core: update detailed status

This commit is contained in:
undefined committed 2021-04-03 12:17:27 +08:00
1 parent 51ce9f80d5
commit b9e08c7e93
15 files changed
+142 -99

No files matched your search

+5 -3
View File
@@ -14,7 +14,9 @@ const {
CI, GITHUB_EVENT_NAME, GITHUB_REF,
} = process.env;
if (CI && (GITHUB_REF !== 'refs/heads/master' || GITHUB_EVENT_NAME !== 'push')) {
const tag = GITHUB_REF === 'refs/heads/master' ? 'latest' : GITHUB_REF === 'refs/heads/develop' ? 'dev' : null;
if (CI && (!tag || GITHUB_EVENT_NAME !== 'push')) {
console.log('publish skipped.');
process.exit(0);
}
@@ -35,7 +37,7 @@ if (CI && (GITHUB_REF !== 'refs/heads/master' || GITHUB_EVENT_NAME !== 'push'))
try {
meta = require(`../${name}/package.json`);
if (!meta.private) {
const version = await latest(meta.name);
const version = await latest(meta.name, { version: tag });
if (gt(meta.version, version)) bumpMap[name] = meta.version;
}
} catch (e) {
@@ -48,7 +50,7 @@ if (CI && (GITHUB_REF !== 'refs/heads/master' || GITHUB_EVENT_NAME !== 'push'))
if (Object.keys(bumpMap).length) {
for (const name in bumpMap) {
console.log(`publishing ${name}@${bumpMap[name]} ...`);
await spawnAsync(`yarn publish ${name} --new-version ${bumpMap[name]} --access public`);
await spawnAsync(`yarn publish ${name} --new-version ${bumpMap[name]}${tag === 'dev' ? '-dev' : ''} --access public --tag ${tag}`);
}
}
console.log('Release created successfully.');
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "2.21.0",
"version": "2.21.1",
"bin": "bin/hydrooj.js",
"main": "dist/loader.js",
"typings": "dist/loader.d.ts",
+1 -1
View File
@@ -26,7 +26,7 @@ export const builtinModel = [
export const builtinHandler = [
'home', 'problem', 'record', 'judge', 'user',
'contest', 'training', 'discussion', 'manage', 'import.syzoj',
'misc', 'homework', 'domain', 'remote',
'misc', 'homework', 'domain', 'remote', 'status',
];
export const builtinScript = [
-37
View File
@@ -2,45 +2,10 @@
import { BadRequestError } from '../error';
import { PRIV } from '../model/builtin';
import user from '../model/user';
import db from '../service/db';
import {
Route, Handler, Types, param,
} from '../service/server';
const coll = db.collection('status');
class StatusHandler extends Handler {
async get() {
const stats = await coll.find().sort({ type: 1, updateAt: -1 }).toArray();
for (const i in stats) {
let desc = '';
const online = new Date(stats[i].updateAt).getTime() > new Date().getTime() - 300000;
if (!online) desc = 'Offline';
desc = desc || 'Online';
stats[i].isOnline = online;
stats[i].status = desc;
}
const path = [
['Hydro', 'homepage'],
['status', null],
];
this.response.body = { stats, path };
this.response.template = 'status.html';
}
}
class StatusUpdateHandler extends Handler {
async post(args) {
this.checkPriv(PRIV.PRIV_JUDGE);
args.type = 'judge';
return coll.updateOne(
{ mid: args.mid, type: 'judge' },
{ $set: args },
{ upsert: true },
);
}
}
class CheckInHandler extends Handler {
async prepare() {
const { checkincnt, lastcheckin } = this.user;
@@ -82,8 +47,6 @@ class SwitchLanguageHandler extends Handler {
}
export async function apply() {
Route('status', '/status', StatusHandler);
Route('status_update', '/status/update', StatusUpdateHandler);
Route('check_in', '/checkin', CheckInHandler, PRIV.PRIV_USER_PROFILE);
Route('switch_language', '/language/:lang', SwitchLanguageHandler);
}
+1 -8
View File
@@ -12,14 +12,7 @@ class NotFoundHandler extends Handler {
}
export async function apply() {
Route('notfound', '/:a', NotFoundHandler);
Route('notfound', '/:a/:b', NotFoundHandler);
Route('notfound', '/:a/:b/:c', NotFoundHandler);
Route('notfound', '/:a/:b/:c/:d', NotFoundHandler);
Route('notfound', '/:a/:b/:c/:d/:e', NotFoundHandler);
Route('notfound', '/:a/:b/:c/:d/:e/:f', NotFoundHandler);
Route('notfound', '/:a/:b/:c/:d/:e/:f/:g', NotFoundHandler);
Route('notfound', '/:a/:b/:c/:d/:e/:f/:g/:h', NotFoundHandler);
Route('notfound', '(/.*)+', NotFoundHandler);
}
global.Hydro.handler.notfound = apply;
+81
View File
@@ -0,0 +1,81 @@
import { PRIV } from '../model/builtin';
import RecordModel from '../model/record';
import DomainModel from '../model/domain';
import * as DocumentModel from '../model/document';
import db from '../service/db';
import { Route, Handler } from '../service/server';
import UserModel from '../model/user';
const coll = db.collection('status');
async function getStatus() {
const stats = await coll.find().sort({ type: 1, updateAt: -1 }).toArray();
for (const stat of stats) {
let desc = '';
const online = new Date(stat.updateAt).getTime() > new Date().getTime() - 300000;
if (!online) desc = 'Offline';
desc = desc || 'Online';
stat.isOnline = online;
stat.status = desc;
}
return stats;
}
class StatusHandler extends Handler {
async get() {
const path = [
['Hydro', 'homepage'],
['status', null],
];
const stats = await getStatus();
for (const stat of stats) {
if (!stat.battery.hasBattery) stat.battery = 'No battery';
else stat.battery = `${stat.battery.type} ${stat.battery.model} ${stat.battery.percent}%${stat.battery.isCharging ? ' Charging' : ''}`;
}
this.response.body = { stats, path };
this.response.template = 'status.html';
}
}
class AdminStatusHandler extends Handler {
async get() {
const record = await RecordModel.stat();
const status = await getStatus();
const [domainCount, userCount, problemCount, discussionCount, recordCount] = await Promise.all([
DomainModel.getMulti().count(),
UserModel.getMulti().count(),
DocumentModel.coll.find({ docType: DocumentModel.TYPE_PROBLEM }).count(),
DocumentModel.coll.find({ docType: DocumentModel.TYPE_DISCUSSION }).count(),
RecordModel.coll.find().count(),
]);
this.response.body = {
record,
status,
domainCount,
userCount,
problemCount,
discussionCount,
recordCount,
};
}
}
class StatusUpdateHandler extends Handler {
async post(args) {
this.checkPriv(PRIV.PRIV_JUDGE);
args.type = 'judge';
return coll.updateOne(
{ mid: args.mid, type: 'judge' },
{ $set: args },
{ upsert: true },
);
}
}
export async function apply() {
Route('status', '/status', StatusHandler);
Route('status_admin', '/.status', AdminStatusHandler);
Route('status_update', '/status/update', StatusUpdateHandler);
}
global.Hydro.handler.status = apply;
+1
View File
@@ -565,6 +565,7 @@ declare global {
namespace NodeJS {
interface Global {
Hydro: {
version: Record<string, string>,
model: Model,
handler: Dict<Function>,
script: Dict<Script>,
+24 -30
View File
@@ -1,60 +1,54 @@
import systeminformation from 'systeminformation';
import { size } from './misc';
import systeminformation, { Systeminformation } from 'systeminformation';
const cache = {
mid: '', cpu: '', osinfo: '', flags: '',
};
const cache: any = {};
export async function get() {
const [Cpu, Memory, OsInfo, CurrentLoad, CpuFlags, CpuTemp, Battery] = await Promise.all([
export interface StatusUpdate {
memory: Systeminformation.MemData;
load: Systeminformation.CurrentLoadData;
battery: Systeminformation.BatteryData;
CpuTemp: Systeminformation.CpuTemperatureData;
}
export interface StatusFull extends StatusUpdate {
mid: string;
cpu: Systeminformation.CpuData;
osinfo: Systeminformation.OsData;
}
export async function get(): Promise<StatusFull> {
const [uuid, cpu, memory, osinfo, load, CpuTemp, battery] = await Promise.all([
systeminformation.uuid(),
systeminformation.cpu(),
systeminformation.mem(),
systeminformation.osInfo(),
systeminformation.currentLoad(),
systeminformation.cpuFlags(),
systeminformation.cpuTemperature(),
systeminformation.battery(),
]);
const cpu = `${Cpu.manufacturer} ${Cpu.brand}`;
const memory = `${size(Memory.active)}/${size(Memory.total)}`;
const osinfo = `${OsInfo.distro} ${OsInfo.release} ${OsInfo.codename} ${OsInfo.kernel} ${OsInfo.arch}`;
const load = `${CurrentLoad.avgLoad}`;
const flags = CpuFlags;
let battery;
if (!Battery.hasBattery) battery = 'No battery';
else battery = `${Battery.type} ${Battery.model} ${Battery.percent}%${Battery.isCharging ? ' Charging' : ''}`;
const mid = OsInfo.serial;
const mid = uuid.hardware;
cache.cpu = cpu;
cache.osinfo = osinfo;
cache.flags = flags;
cache.mid = mid;
return {
mid, cpu, memory, osinfo, load, flags, CpuTemp, battery,
mid, cpu, memory, osinfo, load, CpuTemp, battery,
};
}
export async function update(): Promise<[string, any, any]> {
const [Memory, CurrentLoad, CpuTemp, Battery] = await Promise.all([
export async function update(): Promise<[string, StatusUpdate, StatusFull]> {
const [memory, load, CpuTemp, battery] = await Promise.all([
systeminformation.mem(),
systeminformation.currentLoad(),
systeminformation.cpuTemperature(),
systeminformation.battery(),
]);
const {
mid, cpu, osinfo, flags,
} = cache;
const memory = `${size(Memory.active)}/${size(Memory.total)}`;
const load = `${CurrentLoad.avgLoad}`;
let battery;
if (!Battery.hasBattery) battery = 'No battery';
else battery = `${Battery.type} ${Battery.model} ${Battery.percent}%${Battery.isCharging ? ' Charging' : ''}`;
const { mid, cpu, osinfo } = cache;
return [
mid,
{
memory, load, battery, CpuTemp,
},
{
mid, cpu, memory, osinfo, load, flags, battery, CpuTemp,
mid, cpu, memory, osinfo, load, battery, CpuTemp,
},
];
}
+4
View File
@@ -10,6 +10,10 @@ if (versionNum < 14) throw new Error('NodeJS >=v14 required');
if (!global.Hydro) {
global.Hydro = {
version: {
node: process.version,
hydrooj: require('hydrooj/package.json').version,
},
stat: { reqCount: 0 },
handler: {},
// @ts-ignore
+11 -10
View File
@@ -6,6 +6,7 @@ import db from './db';
import * as bus from './bus';
import * as sysinfo from '../lib/sysinfo';
import { Logger } from '../logger';
import type { StatusUpdate } from '../lib/sysinfo';
const coll = db.collection('status');
const logger = new Logger('monitor');
@@ -17,15 +18,13 @@ function crypt(str: string) {
return encrypted;
}
export async function feedback() {
export async function feedback(): Promise<[string, StatusUpdate]> {
const {
system, domain, document, user, record,
} = global.Hydro.model;
const version = require('hydrooj/package.json').version;
const [mid, , inf] = await sysinfo.update();
const [installid, name, url] = system.getMany([
'installid', 'server.name', 'server.url',
]);
const [mid, $update, inf] = await sysinfo.update();
const [installid, name, url] = system.getMany(['installid', 'server.name', 'server.url']);
const [domainCount, userCount, problemCount, discussionCount, recordCount] = await Promise.all([
domain.getMulti().count(),
user.getMulti().count(),
@@ -47,20 +46,22 @@ export async function feedback() {
memory: inf.memory,
osinfo: inf.osinfo,
cpu: inf.cpu,
flags: inf.flags,
}));
superagent.post('https://feedback.undefined.moe/')
.send({ payload })
.catch(() => {
logger.warn('Cannot connect to hydro center.');
});
return [mid, $update];
}
export async function update() {
await feedback();
const [mid, $set] = await sysinfo.update();
$set.updateAt = new Date();
$set.reqCount = global.Hydro.stat.reqCount;
const [mid, $update] = await feedback();
const $set = {
...$update,
updateAt: new Date(),
reqCount: global.Hydro.stat.reqCount,
};
await bus.serial('monitor/update', 'server', $set);
await coll.updateOne(
{ mid, type: 'server' },
@@ -163,6 +163,7 @@ env.addGlobal('static_url', (assetName) => {
});
// eslint-disable-next-line no-eval
env.addGlobal('eval', eval);
env.addGlobal('global', global);
env.addGlobal('typeof', (o) => typeof o);
env.addGlobal('datetimeSpan', misc.datetimeSpan);
env.addGlobal('paginate', misc.paginate);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/ui-default",
"version": "4.5.0",
"version": "4.5.1",
"author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0",
"main": "hydro.js",
+6 -3
View File
@@ -1,15 +1,18 @@
.page--status
.col--id
width: 70px
width: 100px
.col--os
width: 200px
.col--status
width: 100px
width: 130px
.col--memory
width: 150px
.col--request
width: 100px
width: 130px
+mobile()
.col--os
@@ -66,7 +66,7 @@
{%- for html in (model.system.get('ui-default.footer_extra_html')|default('')).split('\n') -%}
<li class="footer__extra-link-item">{{ html|safe }}</li>
{%- endfor -%}
<li class="footer__extra-link-item">Powered by <a href="https://hydro.js.org">Hydro</a></li>
<li class="footer__extra-link-item">Powered by <a href="https://hydro.js.org">Hydro v{{ global.Hydro.version.hydrooj }}</a></li>
</ol>
</div>
</div>
+4 -4
View File
@@ -27,11 +27,11 @@
<tbody>
{%- for doc in stats -%}
<tr>
<td class="col--id">{{ doc._id.toHexString()|truncate(6,True,'') }}</td>
<td class="col--id">{{ doc._id.toHexString()|truncate(8,True,'') }}</td>
<td class="col--status">{% if doc.isOnline %}{{ doc.status }}{% else %}{{ datetimeSpan(doc.updateAt)|safe }}{% endif %}</td>
<td class="col--os">{{ doc.osinfo }}</td>
<td class="col--cpu" data-tooltip="{{ doc.flags }}">{{ doc.cpu }}</td>
<td class="col--memory"{% if doc.stack %} data-tooltip="Stack size: {{ doc.stack }}MB"{% endif %}>{{ doc.memory }}</td>
<td class="col--os" data-tooltip="{{ doc.osinfo.kernel }}">{{ doc.osinfo.distro }} {{ doc.osinfo.release }} {{ doc.osinfo.codename }} {{ doc.osinfo.arch }}</td>
<td class="col--cpu" data-tooltip="{{ doc.cpu.flags }}">{{ doc.cpu.manufacturer }} {{ doc.cpu.brand }}@{{ doc.cpu.speed }}GHz</td>
<td class="col--memory"{% if doc.stack %} data-tooltip="Stack size: {{ doc.stack }}MB"{% endif %}>{{ size(doc.memory.used) }}/{{ size(doc.memory.total) }}</td>
<td class="col--request">{{ doc.reqCount }}</td>
</tr>
{%- endfor -%}