Experiment with TUI implementation using illwill
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
# Ignore agents
|
||||
agents/
|
||||
|
||||
|
||||
# Nim
|
||||
nimcache/
|
||||
nimblecache/
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import prologue
|
||||
|
||||
# /agent/register
|
||||
proc agentRegister*(ctx: Context) {.async.} =
|
||||
|
||||
let body: JsonNode = ctx.request.body().parseJson()
|
||||
echo body
|
||||
|
||||
resp jsonResponse(body, Http200)
|
||||
|
||||
|
||||
|
||||
# /agent/{uuid}/tasks
|
||||
proc agentTasks*(ctx: Context) {.async.} =
|
||||
resp "<h1>Agent Tasks</h1>"
|
||||
|
||||
# /agent/{uuid}/results
|
||||
proc agentResults*(ctx: Context) {.async.} =
|
||||
resp "<h1>Agent Results</h1>"
|
||||
@@ -1,52 +0,0 @@
|
||||
import prologue
|
||||
import logging
|
||||
import uuids
|
||||
import strformat
|
||||
import std/asynchttpserver
|
||||
|
||||
proc hello*(ctx: Context) {.async.} =
|
||||
resp "Test"
|
||||
|
||||
# /client/listener
|
||||
proc listenerList*(ctx: Context) {.async.} =
|
||||
|
||||
# JSON Response
|
||||
let response = %*{"message": "Ok"}
|
||||
resp jsonResponse(response)
|
||||
|
||||
# /client/listener/create
|
||||
proc listenerCreate*(ctx: Context) {.async.} =
|
||||
|
||||
# Handle POST parameters (Port, IP)
|
||||
|
||||
# Create listener with random UUID
|
||||
let
|
||||
name: string = $genUUID()
|
||||
listenerSettings = newSettings(
|
||||
appName = name,
|
||||
debug = false,
|
||||
address = "127.0.0.1",
|
||||
port = Port(443),
|
||||
secretKey = name
|
||||
)
|
||||
var listener = newApp(settings=listenerSettings)
|
||||
|
||||
proc listenerHandler(req: NativeRequest): Future[void] {.gcsafe.} =
|
||||
req.respond(Http200, name)
|
||||
|
||||
discard listener.serveAsync(listenerHandler)
|
||||
logging.info(fmt"Listener {name} created.")
|
||||
|
||||
resp fmt"Listener {name} created.<br>Listening on <a href=http://{listenerSettings.address}:{listenerSettings.port}>{listenerSettings.address}:{listenerSettings.port}</a>"
|
||||
|
||||
# /client/listener/{uuid}/delete
|
||||
proc listenerDelete*(ctx: Context) {.async.} =
|
||||
resp "<h2>Listener Deleted</h2>"
|
||||
|
||||
# /client/agent
|
||||
proc agentList*(ctx: Context) {.async.} =
|
||||
resp "<h1>Agent List</h1>"
|
||||
|
||||
# /client/agent/build
|
||||
proc agentCreate*(ctx: Context) {.async.} =
|
||||
resp "<h1>Agent Create</h1>"
|
||||
@@ -1,3 +1,2 @@
|
||||
# Define command line switches in this file
|
||||
--define:usestd
|
||||
# Compiler flags
|
||||
--threads:on
|
||||
@@ -1,9 +0,0 @@
|
||||
import prologue
|
||||
|
||||
# /
|
||||
proc root*(ctx: Context) {.async.} =
|
||||
resp "<h1>Hello, World!</h1>"
|
||||
|
||||
# /auth
|
||||
proc auth*(ctx: Context) {.async.} =
|
||||
resp "<h1>Hello, Auth!</h1>"
|
||||
@@ -1,20 +1,11 @@
|
||||
import prologue
|
||||
import asyncdispatch
|
||||
import ./tui
|
||||
|
||||
import ./urls
|
||||
proc main() =
|
||||
# Initialize TUI
|
||||
initUi()
|
||||
|
||||
let
|
||||
env = loadPrologueEnv(".env")
|
||||
settings = newSettings(
|
||||
appName = env.getOrDefault("appName", "Prologue"),
|
||||
debug = env.getOrDefault("debug", true),
|
||||
port = Port(env.getOrDefault("port", 8080)),
|
||||
secretKey = env.getOrDefault("secretKey", "")
|
||||
)
|
||||
var app = newApp(settings = settings)
|
||||
|
||||
app.addRoute(urls.indexPatterns, "/")
|
||||
app.addRoute(urls.clientPatterns, "/client")
|
||||
app.addRoute(urls.agentPatterns, "/agent")
|
||||
|
||||
waitFor app.runAsync()
|
||||
|
||||
|
||||
main()
|
||||
|
||||
157
server/tui.nim
Normal file
157
server/tui.nim
Normal file
@@ -0,0 +1,157 @@
|
||||
import strformat, strutils, math, times
|
||||
import illwill
|
||||
import os
|
||||
|
||||
type
|
||||
View = enum
|
||||
BaseView
|
||||
AgentView
|
||||
ListenerView
|
||||
LogView
|
||||
LootView
|
||||
|
||||
UserInterface = object
|
||||
tb: TerminalBuffer
|
||||
view: View
|
||||
x, y: tuple[start, center, ending: int]
|
||||
|
||||
|
||||
#[
|
||||
Exit Application
|
||||
]#
|
||||
proc exitUi*() {.noconv.} =
|
||||
illwillDeinit()
|
||||
showCursor()
|
||||
quit(0)
|
||||
|
||||
proc renderListenerView(ui: var UserInterface) =
|
||||
ui.tb.setForegroundColor(fgGreen, bright=false)
|
||||
ui.tb.drawRect(ui.x.start, 3, ui.tb.width-1, ui.tb.height-2)
|
||||
|
||||
proc renderAgentView(ui: var UserInterface) =
|
||||
ui.tb.setForegroundColor(fgRed, bright=true)
|
||||
ui.tb.drawRect(ui.x.start, 3, ui.tb.width-1, ui.tb.height-2)
|
||||
|
||||
|
||||
proc renderBaseView(ui: var UserInterface) =
|
||||
ui.tb.setForegroundColor(fgWhite, bright=false)
|
||||
ui.tb.drawRect(ui.x.start, 3, ui.tb.width-1, ui.tb.height-2)
|
||||
|
||||
ui.tb.setForegroundColor(fgCyan, bright=false)
|
||||
ui.tb.write(ui.x.start, 5, fmt"Width: {ui.tb.width}")
|
||||
ui.tb.write(ui.x.start, 6, fmt"Center: {ui.x.center}")
|
||||
ui.tb.write(ui.x.start, 7, fmt"Height: {ui.tb.height}")
|
||||
|
||||
#[
|
||||
Navigation Menu
|
||||
TODO:
|
||||
~ Refactor using foreach loop over sequence of navbar items
|
||||
~ NavItem type:
|
||||
text: string (pre- and append space automatically)
|
||||
view: View
|
||||
fgColor: ForegroundColor
|
||||
shortcut: Key
|
||||
]#
|
||||
proc renderNav(ui: var UserInterface) =
|
||||
var offset: int = 0
|
||||
|
||||
var baseNav = newBoxBuffer(ui.tb.width, ui.tb.height)
|
||||
baseNav.drawRect(ui.x.start, 0, ui.x.start + len(" Base ") + 1, 2, doubleStyle = (ui.view == BaseView))
|
||||
ui.tb.setForegroundColor(fgWhite, bright=true)
|
||||
ui.tb.write(baseNav)
|
||||
ui.tb.write(ui.x.start + 1, 1, " B", resetStyle, "ase ")
|
||||
|
||||
offset += len(" Base ") + 2
|
||||
|
||||
var listenerNav = newBoxBuffer(ui.tb.width, ui.tb.height)
|
||||
listenerNav.drawRect(ui.x.start + offset, 0, ui.x.start + len(" Listeners ") + offset + 1, 2, doubleStyle = (ui.view == ListenerView))
|
||||
ui.tb.setForegroundColor(fgGreen)
|
||||
ui.tb.write(listenerNav)
|
||||
ui.tb.write(ui.x.start + offset + 1, 1, " L", resetStyle, "isteners ")
|
||||
|
||||
offset += len(" Listeners ") + 2
|
||||
|
||||
var agentNav = newBoxBuffer(ui.tb.width, ui.tb.height)
|
||||
agentNav.drawRect(ui.x.start + offset, 0, ui.x.start+len(" Agents ") + offset + 1, 2, doubleStyle = (ui.view == AgentView))
|
||||
ui.tb.setForegroundColor(fgRed, bright=true)
|
||||
ui.tb.write(agentNav)
|
||||
ui.tb.write(ui.x.start + offset + 1, 1, " A", resetStyle, "gents ")
|
||||
|
||||
proc renderView(ui: var UserInterface) =
|
||||
case ui.view:
|
||||
of ListenerView: ui.renderListenerView()
|
||||
of AgentView: ui.renderAgentView()
|
||||
else: ui.renderBaseView()
|
||||
|
||||
#[
|
||||
Initialize Terminal User Interface
|
||||
]#
|
||||
|
||||
var input: string = "test"
|
||||
|
||||
proc initUi*() =
|
||||
|
||||
var ui = UserInterface()
|
||||
|
||||
illwillInit(fullscreen=true, mouse=false)
|
||||
setControlCHook(exitUi)
|
||||
hideCursor()
|
||||
|
||||
while true:
|
||||
|
||||
let
|
||||
width = terminalWidth()
|
||||
height = terminalHeight()
|
||||
|
||||
# Horizontal positioning
|
||||
ui.x.start = 2
|
||||
ui.x.center = cast[int](math.round(width / 2).toInt) - 10
|
||||
ui.x.ending = width-1
|
||||
|
||||
# Vertical positioning
|
||||
ui.y.start = 4
|
||||
ui.y.center = cast[int](math.round(height / 2).toInt) - 2
|
||||
ui.y.ending = height-1
|
||||
|
||||
# Clear screen
|
||||
ui.tb = newTerminalBuffer(width, height)
|
||||
|
||||
# Header
|
||||
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
ui.tb.write(ui.x.center, 0, "┏┏┓┏┓┏┓┓┏┏┓┏╋")
|
||||
ui.tb.write(ui.x.center, 1, "┗┗┛┛┗┗┫┗┻┗ ┛┗ 0.1")
|
||||
ui.tb.write(ui.x.center, 2, " ┗ @virtualloc")
|
||||
ui.tb.write(ui.x.ending - len(date), 1, date)
|
||||
|
||||
# Navigation
|
||||
ui.renderNav()
|
||||
|
||||
# Handle keyboard events
|
||||
var key: Key = getKey()
|
||||
case key
|
||||
of Key.CtrlC: exitUi()
|
||||
of Key.CtrlL:
|
||||
ui.view = ListenerView
|
||||
of Key.CtrlA:
|
||||
ui.view = AgentView
|
||||
of Key.CtrlB:
|
||||
ui.view = BaseView
|
||||
else:
|
||||
#[
|
||||
TODO:
|
||||
~ Turn this into a textbox widget
|
||||
]#
|
||||
if(ord(key) >= 32 and ord(key) < 127):
|
||||
input &= char(ord(key))
|
||||
if(ord(key) == 127 and len(input) >= 1):
|
||||
input = input[0..len(input)-2]
|
||||
ui.tb.write(10, 10, input)
|
||||
|
||||
discard
|
||||
|
||||
ui.renderView()
|
||||
|
||||
# Footer
|
||||
ui.tb.write(ui.x.start, ui.x.ending, "Close using [CTRL+C]")
|
||||
|
||||
ui.tb.display()
|
||||
@@ -1,28 +0,0 @@
|
||||
import prologue
|
||||
|
||||
import ./[index, client, agent]
|
||||
|
||||
let indexPatterns* = @[
|
||||
pattern("/", index.root, @[HttpGet]),
|
||||
pattern("/auth", index.auth, @[HttpPost])
|
||||
]
|
||||
|
||||
#[
|
||||
Client Interfaces
|
||||
]#
|
||||
let clientPatterns* = @[
|
||||
pattern("/listener/", client.listenerList, @[HttpGet]),
|
||||
pattern("/listener/create", client.listenerCreate, @[HttpPost, HttpGet]),
|
||||
pattern("/listener/{uuid}/delete", client.listenerDelete, @[HttpGet]),
|
||||
pattern("/agent/", client.agentList, @[HttpGet]),
|
||||
pattern("/agent/create", client.agentCreate, @[HttpPost])
|
||||
]
|
||||
|
||||
#[
|
||||
Agent API
|
||||
]#
|
||||
let agentPatterns* = @[
|
||||
pattern("/register", agent.agentRegister, @[HttpPost]),
|
||||
pattern("/{uuid}/tasks", agent.agentTasks, @[HttpGet, HttpPost]),
|
||||
pattern("/{uuid}/results", agent.agentResults, @[HttpGet, HttpPost])
|
||||
]
|
||||
Reference in New Issue
Block a user