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/hydro/loader.js

310 lines
9.2 KiB
JavaScript

/* eslint-disable import/no-dynamic-require */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-eval */
const fs = require('fs');
const os = require('os');
const zlib = require('zlib');
const path = require('path');
const yaml = require('js-yaml');
function root(name) {
return path.resolve(process.cwd(), name);
}
function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
} else if (!fs.statSync(dir).isDirectory()) {
fs.unlinkSync(dir);
fs.mkdirSync(dir);
}
}
const active = [];
const fail = [];
try {
// Let webpack pack builtin module together.
// eslint-disable-next-line import/no-unresolved
const f = require('../.build/module.json');
for (const filename in f) {
const m = { ...yaml.safeLoad(zlib.gunzipSync(f[filename])), filename };
active.push(m);
console.log(filename);
}
} catch (e) {
// Builtin module is in the module directory
}
const moduleRoots = [
root('.build/module'),
root('module'),
root(path.resolve(os.homedir(), '.hydro', 'module')),
root('.'),
];
let moduleRoot;
for (const i of moduleRoots) {
if (fs.existsSync(i) && fs.statSync(i).isDirectory()) {
moduleRoot = i;
break;
}
}
async function preload() {
const files = fs.readdirSync(moduleRoot);
for (const file of files) {
if (file.endsWith('.hydro')) {
try {
const f = fs.readFileSync(root(`${moduleRoot}/${file}`));
const m = { ...yaml.safeLoad(zlib.gunzipSync(f)), id: file.split('.')[0] };
active.push(m);
} catch (e) {
console.error(`Module Load Fail: ${file}`);
}
}
}
for (const i of active) {
try {
if (i.os) {
if (!i.os.includes(os.platform().toLowerCase())) throw new Error('Unsupported OS');
}
if (i.file) {
i.files = {};
ensureDir(path.resolve(os.tmpdir(), 'hydro', i.id));
for (const n in i.file) {
if (i.file[n] === null) {
ensureDir(path.resolve(os.tmpdir(), 'hydro', i.id, n));
} else {
const e = path.resolve(os.tmpdir(), 'hydro', i.id, n);
fs.writeFileSync(e, Buffer.from(i.file[n], 'base64'), { mode: 755 });
i.files[n] = e;
}
}
}
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Module Load Fail: ${i.id}`);
console.error(e);
}
}
}
async function handler() {
for (const i of active) {
if (i.handler && !i.fail) {
try {
console.log(`Handler init: ${i.id}`);
console.time(`Handler init: ${i.id}`);
const exports = {};
// eslint-disable-next-line no-unused-vars
const module = { exports, file: i.files || {} };
eval(i.handler);
console.timeEnd(`Handler init: ${i.id}`);
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Handler Load Fail: ${i.id}`);
}
}
}
}
async function locale() {
for (const i of active) {
if (i.locale && !i.fail) {
try {
global.Hydro.lib.i18n(i.locale);
console.log(`Locale init: ${i.id}`);
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Locale Load Fail: ${i.id}`);
}
}
}
}
async function template() {
for (const i of active) {
if (i.template && !i.fail) {
try {
Object.assign(global.Hydro.template, i.template);
console.log(`Template init: ${i.id}`);
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Template Load Fail: ${i.id}`);
}
}
}
}
async function model() {
for (const i of active) {
if (i.model && !i.fail) {
try {
console.log(`Model init: ${i.id}`);
console.time(`Model init: ${i.id}`);
const exports = {};
const module = { exports, file: i.files || {} };
eval(i.model);
if ((module.exports || {}).index) await module.exports.index();
console.timeEnd(`Model init: ${i.id}`);
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Model Load Fail: ${i.id}`);
}
}
}
}
async function lib() {
for (const i of active) {
if (i.lib && !i.fail) {
try {
console.log(`Lib init: ${i.id}`);
console.time(`Lib init: ${i.id}`);
const exports = {};
// eslint-disable-next-line no-unused-vars
const module = { exports, file: i.files || {} };
eval(i.lib);
console.timeEnd(`Lib init: ${i.id}`);
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Lib Load Fail: ${i.id}`);
}
}
}
}
async function service() {
for (const i of active) {
if (i.service && !i.fail) {
try {
console.time(`Service init: ${i.id}`);
const exports = {}; // eslint-disable-line no-unused-vars
const module = { exports };
eval(i.service);
if ((module.exports || {}).init) await module.exports.init();
console.timeEnd(`Service init: ${i.id}`);
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Service Load Fail: ${i.id}`);
console.error(e);
}
}
}
}
async function script() {
for (const i of active) {
if (i.script && !i.fail) {
try {
console.time(`Script init: ${i.id}`);
const exports = {};
const module = { exports }; // eslint-disable-line no-unused-vars
eval(i.script);
console.timeEnd(`Script init: ${i.id}`);
} catch (e) {
i.fail = true;
fail.push(i.id);
console.error(`Script Load Fail: ${i.id}`);
console.error(e);
}
}
}
}
async function install() {
const setup = require('./service/setup');
await setup.setup();
}
async function load() {
ensureDir(path.resolve(os.tmpdir(), 'hydro'));
global.Hydro = {
handler: {},
service: {},
model: {},
script: {},
lib: {},
nodeModules: {
bson: require('bson'),
'js-yaml': require('js-yaml'),
mongodb: require('mongodb'),
},
template: {},
ui: {},
};
await preload();
require('./lib/i18n');
require('./utils');
require('./error');
require('./permission');
await Promise.all([locale(), template()]);
try {
require('./options');
} catch (e) {
await install();
require('./options');
}
const bus = require('./service/bus');
await new Promise((resolve) => {
const h = () => {
console.log('Database connected');
bus.unsubscribe(['system_database_connected'], h);
resolve();
};
bus.subscribe(['system_database_connected'], h);
require('./service/db');
});
const builtinLib = [
'axios', 'download', 'i18n', 'mail', 'markdown',
'md5', 'misc', 'paginate', 'pwhash', 'rank',
'template', 'validator', 'nav',
];
for (const i of builtinLib) require(`./lib/${i}`);
await lib();
require('./service/gridfs');
const server = require('./service/server');
await server.prepare();
await service();
const builtinModel = [
'blacklist', 'builtin', 'contest', 'discussion', 'message',
'opcount', 'problem', 'record', 'setting', 'solution',
'token', 'training', 'user',
];
for (const i of builtinModel) {
const m = require(`./model/${i}`);
if (m.index) await m.index();
}
const system = require('./model/system');
const dbVer = await system.get('db.ver');
if (dbVer !== 1) {
const ins = require('./script/install');
await ins.run();
}
const builtinHandler = [
'home', 'problem', 'record', 'judge', 'user',
'contest', 'training', 'discussion', 'manage', 'import',
];
for (const i of builtinHandler) require(`./handler/${i}`);
await model();
await handler();
for (const i in global.Hydro.handler) {
await global.Hydro.handler[i].apply();
}
const notfound = require('./handler/notfound');
await notfound.apply();
for (const i in global.Hydro.service) {
if (global.Hydro.service[i].postInit) await global.Hydro.service[i].postInit();
}
await script();
await server.start();
}
module.exports = { load, active, fail };