@kotori-bot/kotori-plugin-webui 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -7
- package/lib/index.js +25 -112
- package/lib/routers/api/accounts.js +32 -0
- package/lib/routers/api/config.js +72 -0
- package/lib/routers/api/data.js +88 -0
- package/lib/routers/api/demo.js +8 -0
- package/lib/routers/index.js +15 -0
- package/lib/routers/router.js +25 -0
- package/lib/types/index.js +5 -0
- package/lib/utils/common.js +47 -0
- package/lib/utils/transport.js +34 -0
- package/lib/utils/webui.js +111 -0
- package/lib/ws/index.js +43 -0
- package/package.json +6 -2
- package/LICENSE +0 -674
- package/dist/assets/index-CUrUhEE2.css +0 -1
- package/dist/index.html +0 -14
- package/lib/common.d.ts +0 -9
- package/lib/common.js +0 -31
- package/lib/const.d.ts +0 -2
- package/lib/index.d.ts +0 -7
- /package/lib/{const.js → constant/index.js} +0 -0
package/README.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
# kotori-bot-admin-server
|
|
2
|
-
|
|
3
|
-
Web front end repository: [kotorijs/webui](https://github.com/kotorijs/webui)
|
|
4
|
-
|
|
5
|
-
## Reference
|
|
6
|
-
|
|
7
|
-
- [Kotori Docs](https://kotori.js.org/)
|
|
1
|
+
# kotori-bot-admin-server
|
|
2
|
+
|
|
3
|
+
Web front end repository: [kotorijs/webui](https://github.com/kotorijs/webui)
|
|
4
|
+
|
|
5
|
+
## Reference
|
|
6
|
+
|
|
7
|
+
- [Kotori Docs](https://kotori.js.org/)
|
|
8
|
+
|
|
9
|
+
## TODO
|
|
10
|
+
|
|
11
|
+
- [] Add authorization for websocket server
|
|
12
|
+
- [] Login at the same time
|
|
13
|
+
- [] Console outputting support for cmd adapter
|
package/lib/index.js
CHANGED
|
@@ -1,128 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
4
|
};
|
|
25
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
6
|
exports.main = exports.config = exports.inject = void 0;
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
exports.inject = ['server', 'file'];
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const webui_1 = require("./utils/webui");
|
|
9
|
+
const routers_1 = __importDefault(require("./routers"));
|
|
10
|
+
const ws_1 = __importDefault(require("./ws"));
|
|
11
|
+
const router_1 = __importDefault(require("./routers/router"));
|
|
12
|
+
exports.inject = ['server', 'file', 'cache'];
|
|
13
|
+
var webui_2 = require("./utils/webui");
|
|
14
|
+
Object.defineProperty(exports, "config", { enumerable: true, get: function () { return webui_2.config; } });
|
|
15
|
+
function main(ctx, cfg) {
|
|
16
|
+
/* Starts webui service */
|
|
17
|
+
ctx.service('webui', new webui_1.Webui(ctx, cfg));
|
|
18
|
+
ctx.inject('webui');
|
|
19
|
+
ctx.on('ready', () => {
|
|
20
|
+
(0, ws_1.default)(ctx, ctx.server.wss('/webui'));
|
|
21
|
+
});
|
|
22
|
+
/* Sets up routes */
|
|
41
23
|
const app = ctx.server;
|
|
42
24
|
app.use(app.static(path_1.default.resolve(__dirname, '../dist')));
|
|
43
25
|
app.use(app.json());
|
|
44
26
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
45
27
|
app.all('*', (req, res, next) => {
|
|
46
|
-
|
|
47
|
-
ctx.logger.label(req.method).trace(req.path);
|
|
28
|
+
ctx.logger.label(req.method).trace(req.path);
|
|
48
29
|
res.header('Access-Control-Allow-Origin', '*');
|
|
49
30
|
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
|
50
31
|
res.header('Access-Control-Allow-Methods', '*');
|
|
51
32
|
res.header('Content-Type', 'application/json;charset=utf-8');
|
|
52
|
-
if (
|
|
53
|
-
res.status(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
58
|
-
const router = app.router();
|
|
59
|
-
router.get('/bots', (_, res) => {
|
|
60
|
-
const bots = [];
|
|
61
|
-
ctx[kotori_bot_1.Symbols.bot].forEach((bot) => bot.forEach((api) => bots.push({
|
|
62
|
-
...api.adapter.status,
|
|
63
|
-
platform: api.adapter.platform,
|
|
64
|
-
identity: api.adapter.identity,
|
|
65
|
-
id: api.adapter.selfId,
|
|
66
|
-
lang: api.adapter.config.lang
|
|
67
|
-
})));
|
|
68
|
-
res.status(200).json(bots);
|
|
69
|
-
});
|
|
70
|
-
router.get('/modules', (_, res) => {
|
|
71
|
-
const modules = [];
|
|
72
|
-
ctx[kotori_bot_1.Symbols.modules].forEach((module) => modules.push([module[0].pkg, module[1]]));
|
|
73
|
-
res.status(200).json(modules);
|
|
74
|
-
});
|
|
75
|
-
router.get('/data', (_, res) => {
|
|
76
|
-
res.status(200).json({
|
|
77
|
-
midwares: ctx[kotori_bot_1.Symbols.midware].size,
|
|
78
|
-
commands: ctx[kotori_bot_1.Symbols.command].size,
|
|
79
|
-
regexps: ctx[kotori_bot_1.Symbols.regexp].size,
|
|
80
|
-
bots: ctx[kotori_bot_1.Symbols.bot].size,
|
|
81
|
-
adapters: ctx[kotori_bot_1.Symbols.adapter].size,
|
|
82
|
-
modules: ctx[kotori_bot_1.Symbols.modules].size,
|
|
83
|
-
dir: ctx.baseDir.root,
|
|
84
|
-
pkg: ctx.pkg,
|
|
85
|
-
node: process.version
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
router.get('/info', (_, res) => {
|
|
89
|
-
res.status(200).json({
|
|
90
|
-
cpu: (0, common_1.getCpuRate)(),
|
|
91
|
-
ram: (0, common_1.getRamRate)(),
|
|
92
|
-
version: JSON.parse((0, fs_1.readFileSync)((0, path_1.resolve)(__dirname, '../package.json')).toString()).version
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
router.get('/login', (req, res) => {
|
|
96
|
-
const { user, pwd } = req.query;
|
|
97
|
-
if (user === con.username && pwd === con.password) {
|
|
98
|
-
updateToken();
|
|
99
|
-
res
|
|
100
|
-
.status(200)
|
|
101
|
-
.json({ token: loadToken(), default: con.username === const_1.DEFAULT_USERNAME && con.password === const_1.DEFAULT_PASSWORD });
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
res.status(200).json({});
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
router.get('/config', (req, res) => {
|
|
108
|
-
const { type, id } = req.query;
|
|
109
|
-
if (type === 'module') {
|
|
110
|
-
if (typeof id === 'string') {
|
|
111
|
-
const split = id.split('/');
|
|
112
|
-
const handle = (0, kotori_bot_1.stringRightSplit)(split.length > 1 ? split[1] : split[0], kotori_bot_1.PLUGIN_PREFIX);
|
|
113
|
-
res.status(200).json(handle in ctx.config.plugin ? ctx.config.plugin[handle] : {});
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
res.status(200).json({});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else if (type === 'adapter') {
|
|
120
|
-
res.status(200).json(typeof id === 'string' && id in ctx.config.adapter ? ctx.config.adapter[id] : {});
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
res.status(200).json(ctx.config);
|
|
124
|
-
}
|
|
33
|
+
if (!router_1.default.find((item) => item.path === req.path || req.path.startsWith(item.path)))
|
|
34
|
+
return res.status(404).send();
|
|
35
|
+
if (req.path === '/api/accounts/login' || ctx.webui.checkToken(req.headers.authorization))
|
|
36
|
+
return next();
|
|
37
|
+
return res.status(401);
|
|
125
38
|
});
|
|
126
|
-
app.use('/
|
|
39
|
+
app.use('/', (0, routers_1.default)(ctx, app));
|
|
127
40
|
}
|
|
128
41
|
exports.main = main;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const constant_1 = require("../../constant");
|
|
4
|
+
/* eslint-disable-next-line func-names */
|
|
5
|
+
function default_1(ctx, app) {
|
|
6
|
+
const router = app.router();
|
|
7
|
+
router.post('/login', (req, res) => {
|
|
8
|
+
const { username: user, password: pwd } = req.body;
|
|
9
|
+
const { username, password } = ctx.webui.config;
|
|
10
|
+
const loginStats = ctx.webui.getLoginStats();
|
|
11
|
+
if (user === username && pwd === password) {
|
|
12
|
+
ctx.logger.label('server').trace('Login successful');
|
|
13
|
+
loginStats.success += 1;
|
|
14
|
+
ctx.webui.setLoginStats(loginStats);
|
|
15
|
+
ctx.webui.updateToken();
|
|
16
|
+
return res.json({
|
|
17
|
+
token: ctx.webui.getToken(),
|
|
18
|
+
isDefault: username + password === constant_1.DEFAULT_USERNAME + constant_1.DEFAULT_PASSWORD
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
ctx.logger.label('server').trace('Login failed');
|
|
22
|
+
loginStats.failed += 1;
|
|
23
|
+
ctx.webui.setLoginStats(loginStats);
|
|
24
|
+
return res.status(401).json({ message: 'Invalid username or password' });
|
|
25
|
+
});
|
|
26
|
+
router.post('/logout', (_, res) => {
|
|
27
|
+
ctx.webui.updateToken();
|
|
28
|
+
res.status(204).send();
|
|
29
|
+
});
|
|
30
|
+
return router;
|
|
31
|
+
}
|
|
32
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const kotori_bot_1 = require("kotori-bot");
|
|
4
|
+
/* eslint-disable-next-line func-names */
|
|
5
|
+
function default_1(ctx, app) {
|
|
6
|
+
const getPluginConfig = () => Object.entries(ctx.config.plugin).map(([name, origin]) => ({ name, origin, schema: {} }));
|
|
7
|
+
const getBotConfig = () => Object.entries(ctx.config.adapter).map(([id, origin]) => ({ id, origin, schema: {} }));
|
|
8
|
+
const router = app.router();
|
|
9
|
+
router.get('/plugins/:scope?/:name?', (req, res) => {
|
|
10
|
+
const { scope, name } = req.params;
|
|
11
|
+
if (!scope)
|
|
12
|
+
return res.json(getPluginConfig());
|
|
13
|
+
const pluginName = (0, kotori_bot_1.stringRightSplit)(name ?? scope, kotori_bot_1.PLUGIN_PREFIX);
|
|
14
|
+
const pluginConfig = getPluginConfig().find((plugin) => plugin.name === pluginName);
|
|
15
|
+
return pluginConfig ? res.json(pluginConfig) : res.status(404).json({ message: 'Plugin not found' });
|
|
16
|
+
});
|
|
17
|
+
router.put('/plugins/:scope?/:name?', (req, res) => {
|
|
18
|
+
const { scope, name } = req.params;
|
|
19
|
+
const { body } = req;
|
|
20
|
+
if (!scope || !name)
|
|
21
|
+
return res.status(400).json({ message: 'Invalid plugin scope' });
|
|
22
|
+
if (typeof body !== 'object')
|
|
23
|
+
return res.status(400).json({ message: 'Invalid body' });
|
|
24
|
+
const pluginName = (0, kotori_bot_1.stringRightSplit)(name ?? scope, kotori_bot_1.PLUGIN_PREFIX);
|
|
25
|
+
const pluginConfig = getPluginConfig().find((plugin) => plugin.name === pluginName);
|
|
26
|
+
if (!pluginConfig)
|
|
27
|
+
return res.status(404).json({ message: 'Plugin not found' });
|
|
28
|
+
Object.keys(body).forEach((key) => {
|
|
29
|
+
if (key in pluginConfig)
|
|
30
|
+
pluginConfig[key] = body[key];
|
|
31
|
+
});
|
|
32
|
+
return res.status(204).send();
|
|
33
|
+
});
|
|
34
|
+
router.get('/bots/:name?', (req, res) => {
|
|
35
|
+
const { name } = req.params;
|
|
36
|
+
if (!name)
|
|
37
|
+
return res.json(getBotConfig());
|
|
38
|
+
const botConfig = getBotConfig().find((bot) => bot.id === name);
|
|
39
|
+
return botConfig ? res.json(botConfig) : res.status(404).json({ message: 'Bot not found' });
|
|
40
|
+
});
|
|
41
|
+
router.put('/bots/:name?', (req, res) => {
|
|
42
|
+
const { name } = req.params;
|
|
43
|
+
const { body } = req;
|
|
44
|
+
if (!name)
|
|
45
|
+
return res.status(400).json({ message: 'Invalid bot name' });
|
|
46
|
+
if (typeof body !== 'object')
|
|
47
|
+
return res.status(400).json({ message: 'Invalid body' });
|
|
48
|
+
const botConfig = ctx.config.adapter[name];
|
|
49
|
+
if (!botConfig)
|
|
50
|
+
return res.status(404).json({ message: 'Bot not found' });
|
|
51
|
+
Object.keys(body).forEach((key) => {
|
|
52
|
+
if (key in botConfig)
|
|
53
|
+
botConfig[key] = body[key];
|
|
54
|
+
});
|
|
55
|
+
return res.status(204).send();
|
|
56
|
+
});
|
|
57
|
+
router.get('/global', (_, res) => {
|
|
58
|
+
res.json(ctx.config.global);
|
|
59
|
+
});
|
|
60
|
+
router.put('/global', (req, res) => {
|
|
61
|
+
const { body } = req;
|
|
62
|
+
if (typeof body !== 'object')
|
|
63
|
+
return res.status(400).json({ message: 'Invalid body' });
|
|
64
|
+
Object.keys(body).forEach((key) => {
|
|
65
|
+
if (key in ctx.config.global)
|
|
66
|
+
ctx.config.global[key] = body[key];
|
|
67
|
+
});
|
|
68
|
+
return res.status(204).send();
|
|
69
|
+
});
|
|
70
|
+
return router;
|
|
71
|
+
}
|
|
72
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const os_1 = __importDefault(require("os"));
|
|
7
|
+
const kotori_bot_1 = require("kotori-bot");
|
|
8
|
+
const common_1 = require("../../utils/common");
|
|
9
|
+
/* eslint-disable-next-line func-names */
|
|
10
|
+
function default_1(ctx, app) {
|
|
11
|
+
const getModuleData = () => {
|
|
12
|
+
const list = [];
|
|
13
|
+
ctx[kotori_bot_1.Symbols.modules].forEach((module) => list.push(module[0].pkg));
|
|
14
|
+
return list;
|
|
15
|
+
};
|
|
16
|
+
const getBotData = () => {
|
|
17
|
+
const list = [];
|
|
18
|
+
ctx[kotori_bot_1.Symbols.bot].forEach((bot) => bot.forEach((api) => list.push({
|
|
19
|
+
status: api.adapter.status,
|
|
20
|
+
platform: api.adapter.platform,
|
|
21
|
+
identity: api.adapter.identity,
|
|
22
|
+
id: String(api.adapter.selfId),
|
|
23
|
+
lang: api.adapter.config.lang
|
|
24
|
+
})));
|
|
25
|
+
return list;
|
|
26
|
+
};
|
|
27
|
+
const router = app.router();
|
|
28
|
+
router.get('/modules/:scope?/:name?', (req, res) => {
|
|
29
|
+
const { scope, name } = req.params;
|
|
30
|
+
if (!scope)
|
|
31
|
+
return res.json(getModuleData());
|
|
32
|
+
const moduleName = name ? `${scope}/${name}` : scope;
|
|
33
|
+
const moduleData = getModuleData().find((module) => module.name === moduleName);
|
|
34
|
+
return moduleData ? res.json(moduleData) : res.status(404).json({ message: 'Modules not found' });
|
|
35
|
+
});
|
|
36
|
+
router.get('/bots/:name', (req, res) => {
|
|
37
|
+
const { name } = req.params;
|
|
38
|
+
if (!name)
|
|
39
|
+
return res.json(getBotData());
|
|
40
|
+
const botData = getBotData().find((bot) => bot.identity === name);
|
|
41
|
+
return botData ? res.json(botData) : res.status(404).json({ message: 'Bot not found' });
|
|
42
|
+
});
|
|
43
|
+
router.get('/stats', async (_, res) => {
|
|
44
|
+
const botsStatus = getBotData().map((bot) => bot.status.value === 'online');
|
|
45
|
+
const msgTotal = (0, common_1.calcGrandRecord)(ctx.webui.getMsgTotal().origin);
|
|
46
|
+
const { success: loginSuccess, failed: loginFailed } = ctx.webui.getLoginStats();
|
|
47
|
+
const chats = { received: [], sent: [] };
|
|
48
|
+
[0, 1, 2, 3, 4, 5, 6, 7].forEach((day) => {
|
|
49
|
+
const { received, sent } = (0, common_1.calcGrandRecord)(ctx.webui.getMsgDay(day).origin);
|
|
50
|
+
chats.received.push(received || 0);
|
|
51
|
+
chats.sent.push(sent || 0);
|
|
52
|
+
});
|
|
53
|
+
res.json({
|
|
54
|
+
chats,
|
|
55
|
+
count: {
|
|
56
|
+
midwares: ctx[kotori_bot_1.Symbols.midware].size,
|
|
57
|
+
commands: ctx[kotori_bot_1.Symbols.command].size,
|
|
58
|
+
regexps: ctx[kotori_bot_1.Symbols.regexp].size,
|
|
59
|
+
bots: ctx[kotori_bot_1.Symbols.bot].size,
|
|
60
|
+
adapters: ctx[kotori_bot_1.Symbols.adapter].size,
|
|
61
|
+
modules: ctx[kotori_bot_1.Symbols.modules].size
|
|
62
|
+
},
|
|
63
|
+
system: {
|
|
64
|
+
type: os_1.default.type(),
|
|
65
|
+
arch: os_1.default.arch(),
|
|
66
|
+
uptime: os_1.default.uptime(),
|
|
67
|
+
hostname: os_1.default.hostname(),
|
|
68
|
+
homedir: os_1.default.homedir(),
|
|
69
|
+
node: process.version
|
|
70
|
+
},
|
|
71
|
+
info: [
|
|
72
|
+
{ name: '累计消息收:发量', value: `${msgTotal.received}:${msgTotal.sent}` },
|
|
73
|
+
{ name: '实例在线数/总数', value: `${botsStatus.filter((status) => status).length}/${botsStatus.length}` },
|
|
74
|
+
{ name: '登录成功:失败次数', value: `${loginSuccess}:${loginFailed}` },
|
|
75
|
+
{ name: '内存使用量', value: `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)} MB` }
|
|
76
|
+
]
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
router.get('/status', (_, res) => {
|
|
80
|
+
res.json({
|
|
81
|
+
...ctx.webui.getStats(),
|
|
82
|
+
mode: ctx.options.mode,
|
|
83
|
+
core: ctx.pkg.version
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
return router;
|
|
87
|
+
}
|
|
88
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const router_1 = __importDefault(require("./router"));
|
|
7
|
+
/* eslint-disable-next-line func-names */
|
|
8
|
+
function default_1(ctx, app) {
|
|
9
|
+
const router = app.router();
|
|
10
|
+
router_1.default.forEach((page) => {
|
|
11
|
+
router.use(page.path, page.handler(ctx, app));
|
|
12
|
+
});
|
|
13
|
+
return router;
|
|
14
|
+
}
|
|
15
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const accounts_1 = __importDefault(require("./api/accounts"));
|
|
7
|
+
const config_1 = __importDefault(require("./api/config"));
|
|
8
|
+
const data_1 = __importDefault(require("./api/data"));
|
|
9
|
+
function defineRouter(config) {
|
|
10
|
+
return config;
|
|
11
|
+
}
|
|
12
|
+
exports.default = defineRouter([
|
|
13
|
+
{
|
|
14
|
+
path: '/api/accounts',
|
|
15
|
+
handler: accounts_1.default
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
path: '/api/config',
|
|
19
|
+
handler: config_1.default
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
path: '/api/data',
|
|
23
|
+
handler: data_1.default
|
|
24
|
+
}
|
|
25
|
+
]);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.calcGrandRecord = exports.getDate = exports.generateMessage = exports.getCpuData = exports.getRamData = exports.generateToken = void 0;
|
|
7
|
+
const kotori_bot_1 = require("kotori-bot");
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
function generateToken() {
|
|
10
|
+
return (0, kotori_bot_1.getUuid)().replaceAll('-', '');
|
|
11
|
+
}
|
|
12
|
+
exports.generateToken = generateToken;
|
|
13
|
+
function getRamData() {
|
|
14
|
+
const total = os_1.default.totalmem() / 1024 / 1024 / 1024;
|
|
15
|
+
const unused = os_1.default.freemem() / 1024 / 1024 / 1024;
|
|
16
|
+
const used = total - unused;
|
|
17
|
+
const rate = (used / total) * 100;
|
|
18
|
+
return { total, unused, used, rate };
|
|
19
|
+
}
|
|
20
|
+
exports.getRamData = getRamData;
|
|
21
|
+
function getCpuData() {
|
|
22
|
+
const cpuData = os_1.default.cpus();
|
|
23
|
+
let rate = 0;
|
|
24
|
+
let speed = 0;
|
|
25
|
+
cpuData.forEach((value) => {
|
|
26
|
+
const { times, speed: spd } = value;
|
|
27
|
+
rate += (1 - times.idle / (times.idle + times.user + times.nice + times.sys + times.irq)) * 100;
|
|
28
|
+
speed += spd / cpuData.length;
|
|
29
|
+
});
|
|
30
|
+
return { rate, speed };
|
|
31
|
+
}
|
|
32
|
+
exports.getCpuData = getCpuData;
|
|
33
|
+
function generateMessage(type, data) {
|
|
34
|
+
return JSON.stringify(type === 'error' ? { type, message: data } : { type, data });
|
|
35
|
+
}
|
|
36
|
+
exports.generateMessage = generateMessage;
|
|
37
|
+
function getDate() {
|
|
38
|
+
return `${new Date().getFullYear()}-${new Date().getMonth() + 1}-${new Date().getDay()}`;
|
|
39
|
+
}
|
|
40
|
+
exports.getDate = getDate;
|
|
41
|
+
function calcGrandRecord(data) {
|
|
42
|
+
return Object.values(data).reduce((acc, cur) => ({
|
|
43
|
+
received: acc.received + cur.received,
|
|
44
|
+
sent: acc.sent + cur.sent
|
|
45
|
+
}), { received: 0, sent: 0 });
|
|
46
|
+
}
|
|
47
|
+
exports.calcGrandRecord = calcGrandRecord;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
require("../types");
|
|
27
|
+
const kotori_bot_1 = __importStar(require("kotori-bot"));
|
|
28
|
+
class WebuiTransport extends kotori_bot_1.Transport {
|
|
29
|
+
handle(data) {
|
|
30
|
+
(0, kotori_bot_1.none)(this);
|
|
31
|
+
kotori_bot_1.default.emit('console_output', data);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.default = WebuiTransport;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Webui = exports.config = void 0;
|
|
7
|
+
const date_fns_1 = require("date-fns");
|
|
8
|
+
const kotori_bot_1 = require("kotori-bot");
|
|
9
|
+
const common_1 = require("./common");
|
|
10
|
+
const constant_1 = require("../constant");
|
|
11
|
+
const transport_1 = __importDefault(require("./transport"));
|
|
12
|
+
exports.config = kotori_bot_1.Tsu.Object({
|
|
13
|
+
username: kotori_bot_1.Tsu.String().default(constant_1.DEFAULT_USERNAME),
|
|
14
|
+
password: kotori_bot_1.Tsu.String().default(constant_1.DEFAULT_PASSWORD),
|
|
15
|
+
interval: kotori_bot_1.Tsu.Number().default(5)
|
|
16
|
+
});
|
|
17
|
+
class Webui extends kotori_bot_1.Service {
|
|
18
|
+
timer;
|
|
19
|
+
constructor(ctx, cfg) {
|
|
20
|
+
super(ctx, cfg, 'webui');
|
|
21
|
+
}
|
|
22
|
+
start() {
|
|
23
|
+
/* Add webui transport to logger */
|
|
24
|
+
const logger = this.ctx.get('logger');
|
|
25
|
+
logger.options.transports.push(new transport_1.default({}));
|
|
26
|
+
/* Load records from file to cache */
|
|
27
|
+
const msgTotal = this.ctx.file.load(`${"msg-total" /* KEY.MSG_TOTAL */}.json`, 'json', {});
|
|
28
|
+
const msgDay = this.ctx.file.load(`${"msg-day" /* KEY.MSG_DAY */}${(0, common_1.getDate)()}.json`, 'json', {});
|
|
29
|
+
this.setMsgTotal({ origin: msgTotal });
|
|
30
|
+
this.setMsgDay({ day: (0, common_1.getDate)(), origin: msgDay });
|
|
31
|
+
/* Auto save records from cache to file */
|
|
32
|
+
this.timer = setInterval(() => {
|
|
33
|
+
this.ctx.file.save(`${"msg-total" /* KEY.MSG_TOTAL */}.json`, this.getMsgTotal().origin, 'json');
|
|
34
|
+
const msgDay = this.getMsgDay();
|
|
35
|
+
if (msgDay.day === (0, common_1.getDate)()) {
|
|
36
|
+
this.ctx.file.save(`${"msg-day" /* KEY.MSG_DAY */}${(0, common_1.getDate)()}.json`, msgDay.origin, 'json');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.setMsgDay({ day: (0, common_1.getDate)(), origin: {} });
|
|
40
|
+
}
|
|
41
|
+
}, this.config.interval * 1000);
|
|
42
|
+
/* Update records */
|
|
43
|
+
this.ctx.on('send', (data) => {
|
|
44
|
+
const { identity } = data.api.adapter;
|
|
45
|
+
const { origin: dataTotal } = this.getMsgTotal();
|
|
46
|
+
const { origin: dataDay } = this.getMsgDay();
|
|
47
|
+
if (!(identity in dataTotal))
|
|
48
|
+
dataTotal[identity] = { sent: 0, received: 0 };
|
|
49
|
+
if (!(identity in dataDay))
|
|
50
|
+
dataDay[identity] = { sent: 0, received: 0 };
|
|
51
|
+
dataTotal[identity].sent = (dataTotal[identity].sent || 0) + 1;
|
|
52
|
+
dataDay[identity].sent = (dataDay[identity].sent || 0) + 1;
|
|
53
|
+
});
|
|
54
|
+
this.ctx.midware((next, session) => {
|
|
55
|
+
next();
|
|
56
|
+
const { identity } = session.api.adapter;
|
|
57
|
+
const { origin: dataTotal } = this.getMsgTotal();
|
|
58
|
+
const { origin: dataDay } = this.getMsgDay();
|
|
59
|
+
if (!(identity in dataTotal))
|
|
60
|
+
dataTotal[identity] = { sent: 0, received: 0 };
|
|
61
|
+
if (!(identity in dataDay))
|
|
62
|
+
dataDay[identity] = { sent: 0, received: 0 };
|
|
63
|
+
dataTotal[identity].received = (dataTotal[identity].received || 0) + 1;
|
|
64
|
+
dataDay[identity].received = (dataDay[identity].received || 0) + 1;
|
|
65
|
+
}, 10);
|
|
66
|
+
}
|
|
67
|
+
getToken() {
|
|
68
|
+
return this.ctx.file.load('token', 'txt');
|
|
69
|
+
}
|
|
70
|
+
updateToken() {
|
|
71
|
+
return this.ctx.file.save('token', (0, common_1.generateToken)(), 'txt');
|
|
72
|
+
}
|
|
73
|
+
checkToken(token) {
|
|
74
|
+
return token && token === `Bearer ${this.getToken()}`;
|
|
75
|
+
}
|
|
76
|
+
getStats() {
|
|
77
|
+
(0, kotori_bot_1.none)(this);
|
|
78
|
+
return {
|
|
79
|
+
ram: (0, common_1.getRamData)(),
|
|
80
|
+
cpu: (0, common_1.getCpuData)()
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
getMsgTotal() {
|
|
84
|
+
return this.ctx.cache.get("msg-total" /* KEY.MSG_TOTAL */);
|
|
85
|
+
}
|
|
86
|
+
setMsgTotal(data) {
|
|
87
|
+
this.ctx.cache.set("msg-total" /* KEY.MSG_TOTAL */, data);
|
|
88
|
+
}
|
|
89
|
+
getMsgDay(days = 0) {
|
|
90
|
+
if (!days)
|
|
91
|
+
return this.ctx.cache.get("msg-day" /* KEY.MSG_DAY */);
|
|
92
|
+
const dateString = (0, date_fns_1.format)((0, date_fns_1.addDays)(new Date(), -days), 'yyyy-M-d');
|
|
93
|
+
return {
|
|
94
|
+
day: dateString,
|
|
95
|
+
origin: this.ctx.file.load(`${"msg-day" /* KEY.MSG_DAY */}${dateString}.json`, 'json', {
|
|
96
|
+
sent: 0,
|
|
97
|
+
received: 0
|
|
98
|
+
})
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
setMsgDay(data) {
|
|
102
|
+
this.ctx.cache.set("msg-day" /* KEY.MSG_DAY */, data);
|
|
103
|
+
}
|
|
104
|
+
getLoginStats() {
|
|
105
|
+
return this.ctx.file.load(`${"login-stats" /* KEY.LOGIN_STATS */}.json`, 'json', { success: 0, failed: 0 });
|
|
106
|
+
}
|
|
107
|
+
setLoginStats(stats) {
|
|
108
|
+
this.ctx.file.save(`${"login-stats" /* KEY.LOGIN_STATS */}.json`, stats, 'json');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.Webui = Webui;
|
package/lib/ws/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("../types");
|
|
4
|
+
const common_1 = require("../utils/common");
|
|
5
|
+
function main(ctx, server) {
|
|
6
|
+
server.on('connection', (ws) => {
|
|
7
|
+
// if (!ctx.webui.checkToken(req.headers.authorization)) ws.close(1008, 'Invalid token');
|
|
8
|
+
/* Setup interval to send stats to client */
|
|
9
|
+
const interval = ctx.webui.config.interval * 1000;
|
|
10
|
+
const timer = setInterval(() => {
|
|
11
|
+
if (ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) {
|
|
12
|
+
clearInterval(timer);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
ws.send((0, common_1.generateMessage)('stats', ctx.webui.getStats()));
|
|
16
|
+
}, interval);
|
|
17
|
+
ctx.on('dispose', () => clearInterval(timer));
|
|
18
|
+
/* Listen for messages from client */
|
|
19
|
+
ws.on('message', (message) => {
|
|
20
|
+
let data;
|
|
21
|
+
try {
|
|
22
|
+
data = JSON.parse(message.toString());
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
switch (data.action) {
|
|
28
|
+
case 'command':
|
|
29
|
+
if (!data.command) {
|
|
30
|
+
ws.send((0, common_1.generateMessage)('error', 'No command provided.'));
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
process.stdin.emit('data', data.command);
|
|
34
|
+
ctx.logger.label('server').trace(`Received command from client: ${data.command}`);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
/* Listen for console output from server */
|
|
40
|
+
ctx.on('console_output', (data) => ws.send((0, common_1.generateMessage)('console_output', data)));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
exports.default = main;
|