Skip to content

Commit

Permalink
Merge pull request #53 from jiro4989/develop
Browse files Browse the repository at this point in the history
いろいろリリース
  • Loading branch information
jiro4989 authored Dec 21, 2019
2 parents 89cfc42 + a2a4b45 commit cd9d40d
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 61 deletions.
25 changes: 24 additions & 1 deletion websh_front/src/index.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import karax / [kbase, vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils, j

type
ResponseResult = object
status: cint
system_message: cstring
stdout: cstring
stderr: cstring
images: seq[cstring]
Expand All @@ -13,6 +15,9 @@ const
baseColor = "grey darken-4"
textColor = "green-text darken-3"
textInputColor = "grey-text lighten-5"
statusOk = cint(0)
statusTimeout = cint(1)
statusSystemError = cint(100)

when defined local:
# ローカル開発用
Expand All @@ -23,17 +28,27 @@ else:

var
inputShell = cstring""
outputStatus = cint(0)
outputSystemMessage = cstring""
outputStdout = cstring""
outputStderr = cstring""
outputImages: seq[cstring]
isProgress: bool
## シェルの実行中表示を切り替えるためのフラグ

proc respCb(httpStatus: int, response: cstring) =
let resp = fromJson[ResponseResult](response)
outputStatus = resp.status
outputSystemMessage = resp.system_message
outputStdout = resp.stdout
outputStderr = resp.stderr
outputImages = resp.images
# シェルの実行中表示 OFF
isProgress = false

proc sendShellButtonOnClick(ev: Event, n: VNode) =
# シェルの実行中表示 ON
isProgress = true
let body = %*{"code": inputShell}
ajaxPost(apiUrl,
headers = @[
Expand All @@ -50,11 +65,19 @@ proc createDom(): VNode =
tdiv(class = &"nav-wrapper {baseColor}"):
a(class = &"brand-logo {textColor}"): text "websh"
tdiv(class = "col s6"):
if isProgress:
tdiv(class = "col s12 m12"):
text "Running ..."
if outputStatus != statusOk:
tdiv(class = "col s12 m12"):
text outputSystemMessage
h3: text "Input"
tdiv(class = "input-field col s12 m6"):
textarea(id = "inputShell", class = &"materialize-textarea {textInputColor}", setFocus = true):
proc onkeydown(ev: Event, n: VNode) =
if cast[KeyboardEvent](ev).keyCode == 13:
let kbEvt = cast[KeyboardEvent](ev)
# Ctrl + Enterで実行
if kbEvt.ctrlKey and kbEvt.keyCode == 13:
sendShellButtonOnClick(ev, n)
proc onkeyup(ev: Event, n: VNode) =
inputShell = $n.value
Expand Down
133 changes: 75 additions & 58 deletions websh_server/src/websh_server.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@ from strformat import `&`
import jester, uuids

type
RespShellgeiJSON* = object
ReqShellgeiJSON* = object
code*: string

proc info(msgs: varargs[string, `$`]) =
const
statusOk = 0
statusTimeout = 1
statusSystemError = 100

proc logging(level: string, msgs: varargs[string, `$`]) =
## **Note:** マルチスレッドだとloggingモジュールがうまく機能しないので仮で実装
var s: string
for msg in msgs:
s.add(msg)
let now = now()
let dt = now.format("yyyy-MM-dd")
let ti = now.format("HH:mm:ss")
echo &"{dt}T{ti}+0900 INFO {s}"
echo &"{dt}T{ti}+0900 {level} {s}"

proc info(msgs: varargs[string, `$`]) =
logging "INFO", msgs

proc error(msgs: varargs[string, `$`]) =
logging "ERROR", msgs

proc readStream(strm: var Stream): string =
defer: strm.close()
Expand All @@ -24,7 +35,7 @@ proc readStream(strm: var Stream): string =
lines.add(line)
result = lines.join("\n")

proc runCommand(command: string, args: openArray[string], timeout: int = 3): (string, string) =
proc runCommand(command: string, args: openArray[string], timeout: int = 3): (string, string, int, string) =
## ``command`` を実行し、標準出力と標準エラー出力を返す。
## timeout は秒を指定する。
var
Expand All @@ -39,73 +50,79 @@ proc runCommand(command: string, args: openArray[string], timeout: int = 3): (st
sleep sleepInterval
elapsedTime += sleepInterval
if timeoutMilSec < elapsedTime:
info &"Over timeout: {timeout} second"
return
let msg = &"timeout: {timeout} second"
info msg
return ("", "", statusTimeout, msg)
block:
var strm = p.outputStream
stdoutStr = strm.readStream()
block:
var strm = p.errorStream
stderrStr = strm.readStream()
result = (stdoutStr, stderrStr)
result = (stdoutStr, stderrStr, statusOk, "")

router myrouter:
post "/shellgei":
# TODO:
# uuidを使ってるけれど、どうせならシェル芸botと同じアルゴリズムでファ
# イルを生成したい
var respJson = request.body().parseJson().to(RespShellgeiJSON)
info respJson
# シェバンを付けないとshとして評価されるため一部の機能がつかえない模様(プロ
# セス置換とか) (#7)
if not respJson.code.startsWith("#!"):
# シェバンがついてないときだけデフォルトbash
respJson.code = "#!/bin/bash\n" & respJson.code
let uuid = $genUUID()
let scriptName = &"{uuid}.sh"
let shellScriptPath = getTempDir() / scriptName
writeFile(shellScriptPath, respJson.code)
try:
# TODO:
# uuidを使ってるけれど、どうせならシェル芸botと同じアルゴリズムでファ
# イルを生成したい
var respJson = request.body().parseJson().to(ReqShellgeiJSON)
info respJson
# シェバンを付けないとshとして評価されるため一部の機能がつかえない模様(プロ
# セス置換とか) (#7)
if not respJson.code.startsWith("#!"):
# シェバンがついてないときだけデフォルトbash
respJson.code = "#!/bin/bash\n" & respJson.code
let uuid = $genUUID()
let scriptName = &"{uuid}.sh"
let shellScriptPath = getTempDir() / scriptName
writeFile(shellScriptPath, respJson.code)

let img = "images"
let imageDir = getCurrentDir() / img / uuid
defer:
removeFile(shellScriptPath)
info &"{shellScriptPath} was removed"
removeDir(imageDir)
info &"{imageDir} was removed"
let img = "images"
let imageDir = getCurrentDir() / img / uuid
defer:
removeFile(shellScriptPath)
info &"{shellScriptPath} was removed"
removeDir(imageDir)
info &"{imageDir} was removed"

# コマンドを実行するDockerイメージ名
createDir(imageDir)
let containerShellScriptPath = &"/tmp/{scriptName}"
let imageName = getEnv("WEBSH_DOCKER_IMAGE", "theoldmoon0602/shellgeibot")
let args = [
"run",
"--rm",
"--net=none",
"-m", "256MB",
"--oom-kill-disable",
"--pids-limit", "1024",
"--name", uuid,
"-v", &"{shellScriptPath}:{containerShellScriptPath}",
"-v", &"{imageDir}:/{img}",
# "-v", "./media:/media:ro",
imageName,
"bash", "-c", &"chmod +x {containerShellScriptPath} && sync && {containerShellScriptPath} | stdbuf -o0 head -c 100K",
]
let timeout = getEnv("WEBSH_REQUEST_TIMEOUT", "3").parseInt
let (stdoutStr, stderrStr) = runCommand("docker", args, timeout)
# コマンドを実行するDockerイメージ名
createDir(imageDir)
let containerShellScriptPath = &"/tmp/{scriptName}"
let imageName = getEnv("WEBSH_DOCKER_IMAGE", "theoldmoon0602/shellgeibot")
let args = [
"run",
"--rm",
"--net=none",
"-m", "256MB",
"--oom-kill-disable",
"--pids-limit", "1024",
"--name", uuid,
"-v", &"{shellScriptPath}:{containerShellScriptPath}",
"-v", &"{imageDir}:/{img}",
# "-v", "./media:/media:ro",
imageName,
"bash", "-c", &"chmod +x {containerShellScriptPath} && sync && {containerShellScriptPath} | stdbuf -o0 head -c 100K",
]
let timeout = getEnv("WEBSH_REQUEST_TIMEOUT", "3").parseInt
let (stdoutStr, stderrStr, status, systemMsg) = runCommand("docker", args, timeout)

# 画像ファイルをbase64に変換
var images: seq[string]
for kind, path in walkDir(imageDir):
if kind != pcFile:
continue
let (dir, name, ext) = splitFile(path)
if ext.toLowerAscii notin [".png", ".jpg", ".jpeg", ".gif"]:
continue
images.add(base64.encode(readFile(path)))
# 画像ファイルをbase64に変換
var images: seq[string]
for kind, path in walkDir(imageDir):
if kind != pcFile:
continue
let (dir, name, ext) = splitFile(path)
if ext.toLowerAscii notin [".png", ".jpg", ".jpeg", ".gif"]:
continue
images.add(base64.encode(readFile(path)))

resp %*{"stdout":stdoutStr, "stderr":stderrStr, "images":images}
resp %*{"status":status, "system_message":systemMsg, "stdout":stdoutStr, "stderr":stderrStr, "images":images}
except:
let msg = getCurrentExceptionMsg()
error msg
resp %*{"status":statusSystemError, "system_message":"System error occured.", "stdout":"", "stderr":"", "images":[]}
get "/ping":
resp %*{"status":"ok"}

Expand Down
8 changes: 6 additions & 2 deletions websh_server/tests/twebsh_server.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ include websh_server

suite "proc runCommand":
test "echo":
let (s, e) = runCommand("echo", ["test"])
let (s, e, status, msg) = runCommand("echo", ["test"])
check s == "test"
check e == ""
check status == statusOk
check msg == ""
test "not found command":
let (s, e) = runCommand("bash", ["-c", "not_found_command"])
let (s, e, status, msg) = runCommand("bash", ["-c", "not_found_command"])
check s == ""
check e != ""
check status == statusOk
check msg == ""

0 comments on commit cd9d40d

Please sign in to comment.