Initial working server + client for modifying config, running commands

This commit is contained in:
Ivan Gromov 2019-10-13 21:28:11 +05:00
parent e5235e1bdc
commit 29a53c5ef6
2 changed files with 174 additions and 0 deletions

103
app/index.html Normal file
View 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
View 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)