You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/packages/hydrooj/src/entry/common.ts

240 lines
8.3 KiB
TypeScript

/* eslint-disable no-await-in-loop */
/* eslint-disable no-eval */
import fs from 'fs-extra';
import os from 'os';
import path from 'path';
import yaml from 'js-yaml';
import { Logger } from '../logger';
import * as bus from '../service/bus';
const logger = new Logger('common', true);
export const builtinLib = [
'jwt', 'download', 'i18n', 'mail', 'useragent',
'crypto', 'misc', 'paginate', 'hash.hydro', 'rank',
'validator', 'ui', 'testdataConfig', 'difficulty', 'content',
'avatar',
];
export const builtinModel = [
'builtin', 'document', 'domain', 'blacklist', 'opcount',
'setting', 'token', 'user', 'storage', 'problem',
'record', 'contest', 'message', 'solution', 'training',
'discussion', 'system', 'oplog',
];
export const builtinHandler = [
'home', 'problem', 'record', 'judge', 'user',
'contest', 'training', 'discussion', 'manage', 'import',
'misc', 'homework', 'domain', 'status',
];
export const builtinScript = [
'rating', 'problemStat', 'blacklist', 'deleteUser', 'storageUsage',
];
function getFiles(folder: string, base = ''): string[] {
const files = [];
const f = fs.readdirSync(folder);
for (const i of f) {
if (fs.statSync(path.join(folder, i)).isDirectory()) {
files.push(...getFiles(path.join(folder, i), path.join(base, i)));
} else files.push(path.join(base, i));
}
return files.map((item) => item.replace(/\\/gmi, '/'));
}
export async function handler(pending: string[], fail: string[]) {
for (const i of pending) {
let p = path.resolve(i, 'handler.ts');
if (!fs.existsSync(p)) p = path.resolve(i, 'handler.js');
if (fs.existsSync(p) && !fail.includes(i)) {
try {
logger.info('Handler init: %s', i);
eval('require')(p);
} catch (e) {
fail.push(i);
logger.error('Handler Load Fail: %s', i);
logger.error(e);
}
}
}
await bus.serial('app/load/handler');
}
export async function locale(pending: string[], fail: string[]) {
for (const i of pending) {
let p = path.resolve(i, 'locales');
if (!fs.existsSync(p)) p = path.resolve(i, 'locale');
if (fs.existsSync(p) && fs.statSync(p).isDirectory() && !fail.includes(i)) {
try {
const files = fs.readdirSync(p);
const locales = {};
for (const file of files) {
const content = fs.readFileSync(path.resolve(p, file)).toString();
locales[file.split('.')[0]] = yaml.load(content);
}
global.Hydro.lib.i18n(locales);
logger.info('Locale init: %s', i);
} catch (e) {
fail.push(i);
logger.error('Locale Load Fail: %s', i);
logger.error(e);
}
}
}
await bus.serial('app/load/locale');
}
export async function setting(pending: string[], fail: string[], modelSetting: typeof import('../model/setting')) {
const map = {
system: modelSetting.SystemSetting,
account: modelSetting.AccountSetting,
preference: modelSetting.PreferenceSetting,
domain: modelSetting.DomainSetting,
};
for (const i of pending) {
let p = path.resolve(i, 'setting.yaml');
const t = i.split(path.sep);
const name = t[t.length - 1];
if (!fs.existsSync(p)) p = path.resolve(i, 'settings.yaml');
if (fs.existsSync(p) && !fail.includes(i)) {
try {
const cfg: any = yaml.load(fs.readFileSync(p, 'utf-8'));
for (const key in cfg) {
let val = cfg[key].default || cfg[key].value;
if (typeof val === 'string') {
val = val
.replace(/\$TEMP/g, os.tmpdir())
.replace(/\$HOME/g, os.homedir());
}
const category = cfg[key].category || 'system';
map[category](
modelSetting.Setting(
cfg[key].family || name, category === 'system' ? `${name}.${key}` : key, val, cfg[key].type || 'text',
cfg[key].name || key, cfg[key].desc || '',
),
);
}
logger.info('Config load: %s', i);
} catch (e) {
logger.error('Config Load Fail: %s', i);
logger.error(e);
}
}
}
await bus.serial('app/load/setting');
}
export async function template(pending: string[], fail: string[]) {
for (const i of pending) {
let p = path.resolve(i, 'templates');
if (!fs.existsSync(p)) p = path.resolve(i, 'template');
if (fs.existsSync(p) && fs.statSync(p).isDirectory() && !fail.includes(i)) {
try {
const files = getFiles(p);
for (const file of files) {
if (file.endsWith('.tsx')) global.Hydro.ui.template[file] = eval('require')(path.resolve(p, file));
global.Hydro.ui.template[file] = await fs.readFile(path.resolve(p, file), 'utf-8');
}
logger.info('Template init: %s', i);
} catch (e) {
fail.push(i);
logger.error('Template Load Fail: %s', i);
logger.error(e);
}
}
}
await bus.serial('app/load/template');
}
export async function uistatic(pending: string[], fail: string[]) {
for (const i of pending) {
const p = path.resolve(i, 'public', 'static-manifest.json');
if (fs.existsSync(p) && fs.statSync(p).isFile() && !fail.includes(i)) {
try {
Object.assign(global.Hydro.ui.manifest, eval('require')(p));
} catch (e) {
fail.push(i);
}
}
}
}
export async function model(pending: string[], fail: string[]) {
for (const i of pending) {
let p = path.resolve(i, 'model.ts');
if (!fs.existsSync(p)) p = path.resolve(i, 'model.js');
if (fs.existsSync(p) && !fail.includes(i)) {
try {
logger.info('Model init: %s', i);
eval('require')(p);
} catch (e) {
fail.push(i);
logger.error('Model Load Fail: %s', i);
logger.error(e);
}
}
}
await bus.serial('app/load/model');
}
export async function lib(pending: string[], fail: string[]) {
for (const i of pending) {
let p = path.resolve(i, 'lib.ts');
if (!fs.existsSync(p)) p = path.resolve(i, 'lib.js');
if (fs.existsSync(p) && !fail.includes(i)) {
try {
logger.info('Lib init: %s', i);
eval('require')(p);
} catch (e) {
fail.push(i);
logger.error('Lib Load Fail: %s', i);
logger.error(e);
}
}
}
await bus.serial('app/load/lib');
}
export async function service(pending: string[], fail: string[]) {
for (const i of pending) {
let p = path.resolve(i, 'service.ts');
if (!fs.existsSync(p)) p = path.resolve(i, 'service.js');
if (fs.existsSync(p) && !fail.includes(i)) {
try {
logger.info('Service init: %s', i);
eval('require')(p);
} catch (e) {
fail.push(i);
logger.error('Service Load Fail: %s', i);
logger.error(e);
}
}
}
for (const key in global.Hydro.service) {
const srv = global.Hydro.service[key];
if (!srv.started && srv.start) await srv.start();
}
await bus.serial('app/load/service');
}
export async function script(pending: string[], fail: string[], active: string[]) {
for (const i of pending) {
let p = path.resolve(i, 'script.ts');
if (!fs.existsSync(p)) p = path.resolve(i, 'script.js');
if (await fs.pathExists(p) && !fail.includes(i)) {
try {
logger.info('Script init: %s', i);
eval('require')(p);
} catch (e) {
fail.push(i);
logger.error('Script Load Fail: %s', i);
logger.error(e);
}
}
active.push(i);
}
await bus.serial('app/load/script');
}