mirror of
https://github.com/trailofbits/algo.git
synced 2025-07-14 01:32:55 +02:00
Initial working server + client for modifying config, running commands
This commit is contained in:
parent
e5235e1bdc
commit
29a53c5ef6
2 changed files with 174 additions and 0 deletions
103
app/index.html
Normal file
103
app/index.html
Normal file
|
@ -0,0 +1,103 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Algo webapp</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
</head>
|
||||
<div class="container" id="app">
|
||||
<h1>1. Set up user list</h1>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"
|
||||
v-for="(user, index) in config.users"
|
||||
:key="user"
|
||||
>
|
||||
{{ user }}
|
||||
<button type="button" class="btn btn-secondary btn-sm float-right" v-on:click="removeUser(index)">Remove</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="my-3 form-group">
|
||||
<label for="id_new_user">Add new user</label>
|
||||
<div class="input-group">
|
||||
|
||||
<input type="text" id="id_new_user" class="form-control" placeholder="username" v-model="newUser">
|
||||
<div class="input-group-append">
|
||||
<button v-on:click="addUser" class="btn btn-outline-primary" type="button" id="button-addon2">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button v-on:click="save" v-bind:disabled="loading" class="btn btn-primary" type="button">Save</button>
|
||||
<span v-if="saveConfigMessage" v-bind:class="{ 'text-success': ok, 'text-danged': !ok }">{{saveConfigMessage}}</span>
|
||||
|
||||
</div>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
var ws = new WebSocket("ws://127.0.0.1:8080/ws");
|
||||
window.ws = ws;
|
||||
ws.onopen = function (event) {
|
||||
init();
|
||||
}
|
||||
function init() {
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
ok: false,
|
||||
config: {},
|
||||
loading: false,
|
||||
newUser: '',
|
||||
saveConfigMessage: ''
|
||||
},
|
||||
methods: {
|
||||
addUser: function () {
|
||||
this.config.users.push(this.newUser);
|
||||
this.newUser = '';
|
||||
},
|
||||
removeUser: function(index) {
|
||||
this.config.users.splice(index, 1);
|
||||
},
|
||||
save: function() {
|
||||
this.loading = true;
|
||||
fetch('/config', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(this.config),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then(r => r.json()).then(result => {
|
||||
if (result.ok) {
|
||||
this.ok = true;
|
||||
this.saveConfigMessage = 'Saved!';
|
||||
setTimeout(() => {
|
||||
this.saveConfigMessage = '';
|
||||
}, 1000);
|
||||
} else {
|
||||
this.ok = false;
|
||||
this.saveConfigMessage = 'Not Saved!';
|
||||
setTimeout(() => {
|
||||
this.saveConfigMessage = '';
|
||||
}, 1000);
|
||||
}
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
load: function() {
|
||||
this.loading = true;
|
||||
fetch('/config').then(r => r.json()).then(config => {
|
||||
this.config = config;
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
app.load();
|
||||
window.app = app;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
71
app/server.py
Normal file
71
app/server.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
import asyncio
|
||||
from os.path import join, dirname
|
||||
|
||||
import aiohttp
|
||||
import yaml
|
||||
from aiohttp import web
|
||||
|
||||
routes = web.RouteTableDef()
|
||||
PROJECT_ROOT = dirname(dirname(__file__))
|
||||
|
||||
|
||||
async def handle_index(_):
|
||||
with open(join(PROJECT_ROOT, 'app', 'index.html'), 'r') as f:
|
||||
return web.Response(body=f.read(), content_type='text/html')
|
||||
|
||||
|
||||
async def websocket_handler(request):
|
||||
ws = web.WebSocketResponse()
|
||||
await ws.prepare(request)
|
||||
|
||||
async for msg in ws:
|
||||
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||
if msg.data == 'close':
|
||||
await ws.close()
|
||||
else:
|
||||
p = await asyncio.create_subprocess_shell(
|
||||
msg.data,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
while True:
|
||||
line = await p.stdout.readline()
|
||||
if not line:
|
||||
break
|
||||
else:
|
||||
await ws.send_str(line.decode('ascii').rstrip())
|
||||
|
||||
elif msg.type == aiohttp.WSMsgType.ERROR:
|
||||
print('ws connection closed with exception %s' % ws.exception())
|
||||
|
||||
print('websocket connection closed')
|
||||
return ws
|
||||
|
||||
|
||||
@routes.view("/config")
|
||||
class UsersView(web.View):
|
||||
async def get(self):
|
||||
with open(join(PROJECT_ROOT, 'config.cfg'), 'r') as f:
|
||||
config = yaml.safe_load(f.read())
|
||||
return web.json_response(config)
|
||||
|
||||
async def post(self):
|
||||
data = await self.request.json()
|
||||
with open(join(PROJECT_ROOT, 'config.cfg'), 'w') as f:
|
||||
try:
|
||||
config = yaml.safe_dump(data)
|
||||
except Exception as e:
|
||||
return web.json_response({'error': {
|
||||
'code': type(e).__name__,
|
||||
'message': e,
|
||||
}}, status=400)
|
||||
else:
|
||||
f.write(config)
|
||||
return web.json_response({'ok': True})
|
||||
|
||||
|
||||
app = web.Application()
|
||||
app.router.add_get('/ws', websocket_handler)
|
||||
app.router.add_get('/', handle_index)
|
||||
app.router.add_routes(routes)
|
||||
web.run_app(app)
|
Loading…
Add table
Reference in a new issue