Commit 409dcdbc9ac9edee1b6c8c582e3f21b6b86b3ece
1 parent
8a09c214f0
Exists in
master
and in
1 other branch
awal webadmin
Showing 17 changed files with 338 additions and 0 deletions Side-by-side Diff
- index.js
- lib/webadmin/index.js
- lib/webadmin/router/config.js
- package.json
- webadmin-statics/assets/img/favicons/android-chrome-192x192.png
- webadmin-statics/assets/img/favicons/apple-touch-icon.png
- webadmin-statics/assets/img/favicons/browserconfig.xml
- webadmin-statics/assets/img/favicons/favicon-16x16.png
- webadmin-statics/assets/img/favicons/favicon-32x32.png
- webadmin-statics/assets/img/favicons/favicon.ico
- webadmin-statics/assets/img/favicons/manifest.json
- webadmin-statics/assets/img/favicons/mstile-150x150.png
- webadmin-statics/assets/img/favicons/safari-pinned-tab.svg
- webadmin-statics/assets/starter-template.css
- webadmin-views/config.include.addmodem.html
- webadmin-views/config.index.html
- webadmin-views/template.starter.html
index.js
lib/webadmin/index.js
1 | 1 | const express = require('express'); |
2 | +const morgan = require('morgan'); | |
3 | +const rfs = require('rotating-file-stream'); | |
4 | +const nunjucks = require('nunjucks'); | |
5 | +const moment = require('moment'); | |
2 | 6 | |
3 | 7 | const config = require('komodo-sdk/config'); |
4 | 8 | const logger = require('komodo-sdk/logger'); |
5 | 9 | |
10 | +const routerConfig = require('./router/config'); | |
6 | 11 | |
7 | 12 | const app = express(); |
8 | 13 | |
14 | +nunjucks.configure('./webadmin-views', { | |
15 | + autoescape: true, | |
16 | + express: app, | |
17 | + noCache: true, | |
18 | +}); | |
19 | + | |
20 | +app.use(express.static('./webadmin-statics')); | |
21 | + | |
22 | +const accessLogStream = rfs.createStream( | |
23 | + (time, index) => { | |
24 | + const ts = time ? `.${moment(time).format('YYYY-MM-DD')}` : ''; | |
25 | + const idx = index ? `.${index}` : ''; | |
26 | + return `apiserver.access_log${ts}${idx}`; | |
27 | + }, | |
28 | + { | |
29 | + interval: '1d', | |
30 | + path: './logs', | |
31 | + }, | |
32 | +); | |
33 | + | |
34 | +app.use(morgan('combined', { stream: accessLogStream })); | |
35 | + | |
36 | +app.use('/config', routerConfig); | |
37 | + | |
9 | 38 | const listenPort = (config.webadmin && config.webadmin.port) || 21923; |
10 | 39 | app.listen(listenPort, () => { |
11 | 40 | logger.info(`WEBADMIN listen on port ${listenPort}`); |
lib/webadmin/router/config.js
... | ... | @@ -0,0 +1,87 @@ |
1 | +const fs = require('fs'); | |
2 | +const express = require('express'); | |
3 | + | |
4 | +const config = require('komodo-sdk/config'); | |
5 | + | |
6 | +const router = express.Router(); | |
7 | +module.exports = router; | |
8 | + | |
9 | +function composeNewModem(name, params) { | |
10 | + return { | |
11 | + name, | |
12 | + outgoing: (params && params.outgoing) || false, | |
13 | + prefix: (params && params.prefix) || [], | |
14 | + }; | |
15 | +} | |
16 | + | |
17 | +async function writeConfigFile() { | |
18 | + try { | |
19 | + await fs.promises.writeFile('config.json', JSON.stringify(config, null, 4)); | |
20 | + } catch (e) { | |
21 | + // | |
22 | + } | |
23 | +} | |
24 | + | |
25 | +function pageMain(req, res) { | |
26 | + res.render('config.index.html', { | |
27 | + config: JSON.stringify(config, null, 4), | |
28 | + modems: config.modems, | |
29 | + baseUrl: req.baseUrl, | |
30 | + }); | |
31 | +} | |
32 | + | |
33 | +async function pageSetOutgoing(req, res) { | |
34 | + const modemName = (req.params.modemName || '').trim(); | |
35 | + if (!modemName) { | |
36 | + res.end('Invalid modem name'); | |
37 | + return; | |
38 | + } | |
39 | + | |
40 | + const idx = (config.modems || []).findIndex((modem) => modem.name === modemName); | |
41 | + if (idx < 0) { | |
42 | + res.end('No modem matched'); | |
43 | + return; | |
44 | + } | |
45 | + | |
46 | + config.modems[idx].outgoing = req.params.newValue === '1' || req.params.newValue === 'true'; | |
47 | + | |
48 | + await writeConfigFile(); | |
49 | + | |
50 | + res.redirect(`${req.baseUrl}`); | |
51 | +} | |
52 | + | |
53 | +async function pageDelPrefix(req, res) { | |
54 | + const modemName = (req.params.modemName || '').trim(); | |
55 | + if (!modemName) { | |
56 | + res.end('Invalid modem name'); | |
57 | + return; | |
58 | + } | |
59 | + | |
60 | + const prefix = (req.params.prefix || '').trim(); | |
61 | + if (!prefix) { | |
62 | + res.end('Invalid prefix'); | |
63 | + return; | |
64 | + } | |
65 | + | |
66 | + const modemIdx = (config.modems || []).findIndex((modem) => modem.name === modemName); | |
67 | + if (modemIdx < 0) { | |
68 | + res.end('No modem matched'); | |
69 | + return; | |
70 | + } | |
71 | + | |
72 | + const prefixIdx = (config.modems[modemIdx].prefix || []).indexOf(prefix); | |
73 | + if (prefixIdx < 0) { | |
74 | + res.end('No prefix matched'); | |
75 | + return; | |
76 | + } | |
77 | + | |
78 | + config.modems[modemIdx].prefix.splice(prefixIdx, 1); | |
79 | + | |
80 | + await writeConfigFile(); | |
81 | + | |
82 | + res.redirect(`${req.baseUrl}`); | |
83 | +} | |
84 | + | |
85 | +router.get('/', pageMain); | |
86 | +router.get('/modem/set-outgoing/:modemName/:newValue', pageSetOutgoing); | |
87 | +router.get('/modem/del-prefix/:modemName/:prefix', pageDelPrefix); |
package.json
webadmin-statics/assets/img/favicons/android-chrome-192x192.png
1.89 KB
webadmin-statics/assets/img/favicons/apple-touch-icon.png
1.7 KB
webadmin-statics/assets/img/favicons/browserconfig.xml
webadmin-statics/assets/img/favicons/favicon-16x16.png
310 Bytes
webadmin-statics/assets/img/favicons/favicon-32x32.png
491 Bytes
webadmin-statics/assets/img/favicons/favicon.ico
No preview for this file type
webadmin-statics/assets/img/favicons/manifest.json
... | ... | @@ -0,0 +1,20 @@ |
1 | +{ | |
2 | + "name": "Bootstrap", | |
3 | + "short_name": "Bootstrap", | |
4 | + "icons": [ | |
5 | + { | |
6 | + "src": "/assets/img/favicons/android-chrome-192x192.png", | |
7 | + "sizes": "192x192", | |
8 | + "type": "image/png" | |
9 | + }, | |
10 | + { | |
11 | + "src": "/assets/img/favicons/android-chrome-512x512.png", | |
12 | + "sizes": "512x512", | |
13 | + "type": "image/png" | |
14 | + } | |
15 | + ], | |
16 | + "start_url": "/?utm_source=a2hs", | |
17 | + "theme_color": "#563d7c", | |
18 | + "background_color": "#563d7c", | |
19 | + "display": "standalone" | |
20 | +} |
webadmin-statics/assets/img/favicons/mstile-150x150.png
1.39 KB
webadmin-statics/assets/img/favicons/safari-pinned-tab.svg
... | ... | @@ -0,0 +1,4 @@ |
1 | +<svg xmlns="http://www.w3.org/2000/svg" width="933.333" height="933.333" version="1" viewBox="0 0 700 700"> | |
2 | + <path d="M104.5.7C89.2 2.6 77.4 6.2 63.8 13.2 41.7 24.5 24.5 41.7 13.2 63.8 7.5 75.1 4.8 82.3 2.3 94 .6 102.1.5 114.3.5 350s.1 247.9 1.8 256c2.5 11.7 5.2 18.9 10.9 30.2 11.3 22.1 28.5 39.3 50.6 50.6 11.3 5.7 18.5 8.4 30.2 10.9 8.1 1.7 20.3 1.8 256 1.8s247.9-.1 256-1.8c11.7-2.5 18.9-5.2 30.2-10.9 22.1-11.3 39.3-28.5 50.6-50.6 5.7-11.3 8.4-18.5 10.9-30.2 1.7-8.1 1.8-20.3 1.8-256s-.1-247.9-1.8-256c-2.5-11.7-5.2-18.9-10.9-30.2C670.9 32.6 642.9 11 607 2.4 599.9.7 587.2.6 353.5.4 218.2.3 106.2.5 104.5.7zm315.3 153.6c47.4 9 75.3 30.5 85.6 65.9 5.1 17.8 5.6 43.1 1.1 60.3-2 7.5-7.9 20.3-12.2 26.4-8 11.3-21.9 22.8-36 30-3.5 1.7-6.3 3.5-6.3 3.9 0 .5 2.3 1.4 5.1 2 2.8.7 8.5 2.6 12.7 4.3 37.2 14.8 58.1 50.4 58.2 99 0 28.6-9 53.9-25.7 71.8-18.8 20.3-45.2 32.9-83.3 39.8-8.5 1.5-20.9 1.7-119.2 2l-109.8.4V151.9l110.8.4c95.5.3 111.8.6 119 2z"/> | |
3 | + <path d="M262 266.5v56.6l65.3-.4c57.2-.3 65.9-.5 70.7-2 15.8-4.8 28.7-14.9 34.4-27.1 4.4-9.2 5.6-15.3 5.6-28.4-.1-25.1-7.5-39.8-24.3-47.7-14.1-6.7-14.2-6.7-86.4-7.2l-65.3-.5v56.7zm0 171.5v64.1l71.3-.3c69.8-.3 71.4-.4 79-2.6 11.4-3.2 19.2-7.7 27.2-15.7 12.1-12 16.8-24.9 16.8-46 0-20.8-5.1-34.3-17.3-45.9-7.9-7.4-15.8-11.6-28.1-14.7-8.1-2.1-10.5-2.2-78.6-2.6l-70.3-.5V438z"/> | |
4 | +</svg> |
webadmin-statics/assets/starter-template.css
webadmin-views/config.include.addmodem.html
webadmin-views/config.index.html
... | ... | @@ -0,0 +1,80 @@ |
1 | +{% extends "template.starter.html" %} | |
2 | + | |
3 | +{% block content %} | |
4 | + | |
5 | +<!-- | |
6 | +<code> | |
7 | +{{ config | nl2br | safe }} | |
8 | +</code> | |
9 | +--> | |
10 | + | |
11 | +{% for modem in modems %} | |
12 | +<div class="card"> | |
13 | + <div class="card-header bg-info"> | |
14 | + <h3>{{ modem.name }}</3> | |
15 | + </div> | |
16 | + <div class="card-body"> | |
17 | + | |
18 | + <dl class="row"> | |
19 | + <dt class="col-sm-3">Outgoing</dt> | |
20 | + <dd class="col-sm-9"> | |
21 | + {% if modem.outgoing %} | |
22 | + {% set newValue = 0 %} | |
23 | + {% else %} | |
24 | + {% set newValue = 1 %} | |
25 | + {% endif %} | |
26 | + <a href="{{ baseUrl }}/modem/set-outgoing/{{ modem.name | urlencode }}/{{ newValue }}" onclick="return window.confirm('Apakah anda yakin ingin mengubah status outgoing modem {{ modem.name }} menjadi {{ not modem.outgoing }}?');"> | |
27 | + {{ modem.outgoing }} | |
28 | + </a> | |
29 | + </dd> | |
30 | + </dl> | |
31 | + | |
32 | + <dl class="row"> | |
33 | + <dt class="col-sm-3">IMSI</dt> | |
34 | + <dd class="col-sm-9"> | |
35 | + {{ modem.imsi or '-' }} | |
36 | + <br> | |
37 | + [<a href="{{ baseUrl }}/modem/set-imsi/{{ modem.name | urlencode }}"> | |
38 | + edit | |
39 | + </a>] | |
40 | + </dd> | |
41 | + </dl> | |
42 | + | |
43 | + <dl class="row"> | |
44 | + <dt class="col-sm-3">Prefix</dt> | |
45 | + <dd class="col-sm-9"> | |
46 | + {% for prefix_item in modem.prefix %} | |
47 | + {{ prefix_item }} | |
48 | + <a href="{{ baseUrl }}/modem/del-prefix/{{ modem.name | urlencode }}/{{ prefix_item | urlencode}}" onclick="return window.confirm('Apakah anda yakin ingin mengapus prefix {{ prefix_item }} dari modem {{ modem.name }}?');"> | |
49 | + x | |
50 | + </a> | |
51 | + <br> | |
52 | + {% endfor %} | |
53 | + | |
54 | + [ | |
55 | + <a href="{{ baseUrl }}/modem/add-prefix/{{ modem.name | urlencode }}"> | |
56 | + tambah | |
57 | + </a> | |
58 | + ] | |
59 | + </dd> | |
60 | + </dl> | |
61 | + | |
62 | + <dl class="row"> | |
63 | + <dt class="col-sm-3">Custom IP</dt> | |
64 | + <dd class="col-sm-9"> | |
65 | + {{ modem.ip or 'none' }} | |
66 | + <br> | |
67 | + [<a href="{{ baseUrl }}/modem/set-ip/{{ modem.name | urlencode }}"> | |
68 | + edit | |
69 | + </a>] | |
70 | + </dd> | |
71 | + </dl> | |
72 | + | |
73 | + </div> | |
74 | +</div> | |
75 | +<br> | |
76 | +{% endfor %} | |
77 | + | |
78 | +{% include "config.include.addmodem.html" %} | |
79 | + | |
80 | +{% endblock %} | |
0 | 81 | \ No newline at end of file |
webadmin-views/template.starter.html
... | ... | @@ -0,0 +1,92 @@ |
1 | + | |
2 | +<!doctype html> | |
3 | +<html lang="en"> | |
4 | + <head> | |
5 | + <meta charset="utf-8"> | |
6 | + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
7 | + <meta name="description" content=""> | |
8 | + <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> | |
9 | + <meta name="generator" content="Jekyll v3.8.6"> | |
10 | + <title>Starter Template ยท Bootstrap</title> | |
11 | + | |
12 | + <!-- Bootstrap core CSS --> | |
13 | + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> | |
14 | + | |
15 | + <!-- Favicons --> | |
16 | + <link rel="apple-touch-icon" href="/assets/img/favicons/apple-touch-icon.png" sizes="180x180"> | |
17 | + <link rel="icon" href="/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png"> | |
18 | + <link rel="icon" href="/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png"> | |
19 | + <link rel="manifest" href="/assets/img/favicons/manifest.json"> | |
20 | + <link rel="mask-icon" href="/assets/img/favicons/safari-pinned-tab.svg" color="#563d7c"> | |
21 | + <link rel="icon" href="/assets/img/favicons/favicon.ico"> | |
22 | + <meta name="msapplication-config" content="/assets/img/favicons/browserconfig.xml"> | |
23 | + <meta name="theme-color" content="#563d7c"> | |
24 | + | |
25 | + | |
26 | + <style> | |
27 | + .bd-placeholder-img { | |
28 | + font-size: 1.125rem; | |
29 | + text-anchor: middle; | |
30 | + -webkit-user-select: none; | |
31 | + -moz-user-select: none; | |
32 | + -ms-user-select: none; | |
33 | + user-select: none; | |
34 | + } | |
35 | + | |
36 | + @media (min-width: 768px) { | |
37 | + .bd-placeholder-img-lg { | |
38 | + font-size: 3.5rem; | |
39 | + } | |
40 | + } | |
41 | + </style> | |
42 | + <!-- Custom styles for this template --> | |
43 | + <link href="/assets/starter-template.css" rel="stylesheet"> | |
44 | + </head> | |
45 | + <body> | |
46 | + <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top"> | |
47 | + <a class="navbar-brand" href="#">Navbar</a> | |
48 | + <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation"> | |
49 | + <span class="navbar-toggler-icon"></span> | |
50 | + </button> | |
51 | + | |
52 | + <div class="collapse navbar-collapse" id="navbarsExampleDefault"> | |
53 | + <ul class="navbar-nav mr-auto"> | |
54 | + <li class="nav-item active"> | |
55 | + <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a> | |
56 | + </li> | |
57 | + <li class="nav-item"> | |
58 | + <a class="nav-link" href="#">Link</a> | |
59 | + </li> | |
60 | + <li class="nav-item"> | |
61 | + <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a> | |
62 | + </li> | |
63 | + <li class="nav-item dropdown"> | |
64 | + <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a> | |
65 | + <div class="dropdown-menu" aria-labelledby="dropdown01"> | |
66 | + <a class="dropdown-item" href="#">Action</a> | |
67 | + <a class="dropdown-item" href="#">Another action</a> | |
68 | + <a class="dropdown-item" href="#">Something else here</a> | |
69 | + </div> | |
70 | + </li> | |
71 | + </ul> | |
72 | + <form class="form-inline my-2 my-lg-0"> | |
73 | + <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search"> | |
74 | + <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button> | |
75 | + </form> | |
76 | + </div> | |
77 | +</nav> | |
78 | + | |
79 | +<main role="main" class="container"> | |
80 | + | |
81 | + <div class="starter-template"> | |
82 | + {% block content %} | |
83 | + | |
84 | + {% endblock %} | |
85 | + </div> | |
86 | + | |
87 | +</main><!-- /.container --> | |
88 | + | |
89 | +<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> | |
90 | +<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> | |
91 | +<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script> | |
92 | +</html> |