mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
hydro cli
This commit is contained in:
1 parent
9e96c471af
commit
81868cafbb
11 files changed
+243
-160
No files matched your search
+43
-34
@@ -2,46 +2,55 @@
|
||||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const cluster = require('cluster');
|
||||
const fs = require('fs-extra');
|
||||
const { argv } = require('yargs');
|
||||
const hydro = require('../dist/loader');
|
||||
|
||||
fs.ensureDirSync(path.resolve(os.homedir(), '.hydro'));
|
||||
const addonPath = path.resolve(os.homedir(), '.hydro', 'addon.json');
|
||||
if (!fs.existsSync(addonPath)) fs.writeFileSync(addonPath, '[]');
|
||||
let addons = JSON.parse(fs.readFileSync(addonPath).toString());
|
||||
|
||||
try {
|
||||
const ui = argv.ui || '@hydrooj/ui-default';
|
||||
require.resolve(ui);
|
||||
addons.push(ui);
|
||||
} catch (e) {
|
||||
console.error('Please also install @hydrooj/ui-default');
|
||||
}
|
||||
|
||||
if (argv._[0]) {
|
||||
const operation = argv._[0];
|
||||
const arg1 = argv._[1];
|
||||
const arg2 = argv._[2];
|
||||
if (operation === 'addon') {
|
||||
if (arg1 === 'add') addons.push(arg2);
|
||||
else if (arg1 === 'remove') {
|
||||
for (let i = 0; i < addons.length; i++) {
|
||||
if (addons[i] === arg2) {
|
||||
addons.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
addons = Array.from(new Set(addons));
|
||||
console.log('Current Addons: ', addons);
|
||||
fs.writeFileSync(addonPath, JSON.stringify(addons, null, 2));
|
||||
}
|
||||
} else {
|
||||
addons = Array.from(new Set(addons));
|
||||
for (const addon of addons) hydro.addon(addon);
|
||||
if (!cluster.isMaster) {
|
||||
// Forked by hydro
|
||||
hydro.load().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
fs.ensureDirSync(path.resolve(os.homedir(), '.hydro'));
|
||||
const addonPath = path.resolve(os.homedir(), '.hydro', 'addon.json');
|
||||
if (!fs.existsSync(addonPath)) fs.writeFileSync(addonPath, '[]');
|
||||
let addons = JSON.parse(fs.readFileSync(addonPath).toString());
|
||||
|
||||
try {
|
||||
const ui = argv.ui || '@hydrooj/ui-default';
|
||||
require.resolve(ui);
|
||||
addons.push(ui);
|
||||
} catch (e) {
|
||||
console.error('Please also install @hydrooj/ui-default');
|
||||
}
|
||||
|
||||
if (argv._[0] && argv._[0] !== 'cli') {
|
||||
const operation = argv._[0];
|
||||
const arg1 = argv._[1];
|
||||
const arg2 = argv._[2];
|
||||
if (operation === 'addon') {
|
||||
if (arg1 === 'add') addons.push(arg2);
|
||||
else if (arg1 === 'remove') {
|
||||
for (let i = 0; i < addons.length; i++) {
|
||||
if (addons[i] === arg2) {
|
||||
addons.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
addons = Array.from(new Set(addons));
|
||||
console.log('Current Addons: ', addons);
|
||||
fs.writeFileSync(addonPath, JSON.stringify(addons, null, 2));
|
||||
}
|
||||
} else {
|
||||
addons = Array.from(new Set(addons));
|
||||
for (const addon of addons) hydro.addon(addon);
|
||||
(argv._[0] === 'cli' ? hydro.loadCli : hydro.load)().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/* eslint-disable no-eval */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
import { argv } from 'yargs';
|
||||
import {
|
||||
lib, service, model, script,
|
||||
builtinLib, builtinScript, builtinModel,
|
||||
} from './common';
|
||||
|
||||
const COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
const ARR = /=>.*$/mg;
|
||||
function parseParameters(fn: Function) {
|
||||
const code = fn.toString()
|
||||
.replace(COMMENTS, '')
|
||||
.replace(ARR, '');
|
||||
const result = code.slice(code.indexOf('(') + 1, code.indexOf(')'))
|
||||
.match(/([^,]+)/g).map((i) => i.trim());
|
||||
return result === null ? [] : result;
|
||||
}
|
||||
|
||||
async function cli() {
|
||||
const [, modelName, func, ...args] = argv._;
|
||||
if (!global.Hydro.model[modelName]) {
|
||||
return console.error(`Model ${modelName} doesn't exist.`);
|
||||
}
|
||||
if (!global.Hydro.model[modelName][func]) {
|
||||
return console.error(`Function ${func} doesn't exist in model ${modelName}.`);
|
||||
}
|
||||
if (typeof global.Hydro.model[modelName][func] !== 'function') {
|
||||
return console.error(`${func} in model ${modelName} is not a function.`);
|
||||
}
|
||||
const parameterMin = global.Hydro.model[modelName][func].length;
|
||||
const parameters = parseParameters(global.Hydro.model[modelName][func]);
|
||||
const parameterMax = parameters.length;
|
||||
if (args.length > parameterMax) {
|
||||
console.error(`Too many arguments. Max ${parameterMax}`);
|
||||
return console.error(parameters.join(', '));
|
||||
}
|
||||
if (args.length < parameterMin) {
|
||||
console.error(`Too few arguments. Min ${parameterMin}`);
|
||||
return console.error(parameters.join(', '));
|
||||
}
|
||||
let result = global.Hydro.model[modelName][func](...args);
|
||||
if (result instanceof Promise) result = await result;
|
||||
return console.log(result);
|
||||
}
|
||||
|
||||
export async function load(call, args) {
|
||||
let pending = args;
|
||||
const fail = [];
|
||||
const active = [];
|
||||
require('../lib/i18n');
|
||||
require('../utils');
|
||||
require('../error');
|
||||
require('../options');
|
||||
const bus = require('../service/bus');
|
||||
await new Promise((resolve) => {
|
||||
const h = () => {
|
||||
bus.unsubscribe(['system_database_connected'], h);
|
||||
resolve();
|
||||
};
|
||||
bus.subscribe(['system_database_connected'], h);
|
||||
require('../service/db');
|
||||
});
|
||||
for (const i of builtinLib) require(`../lib/${i}`);
|
||||
await lib(pending, fail);
|
||||
require('../service/gridfs');
|
||||
await service(pending, fail);
|
||||
for (const i of builtinModel) require(`../model/${i}`);
|
||||
await model(pending, fail);
|
||||
for (const i in global.Hydro.service) {
|
||||
if (global.Hydro.service[i].postInit) {
|
||||
try {
|
||||
await global.Hydro.service[i].postInit();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const i of builtinScript) require(`../script/${i}`);
|
||||
await script(pending, fail, active);
|
||||
for (const postInit of global.Hydro.postInit) {
|
||||
await postInit();
|
||||
}
|
||||
pending = [];
|
||||
await cli();
|
||||
return { active, fail };
|
||||
}
|
||||
@@ -21,12 +21,11 @@ export async function load(call) {
|
||||
bus.subscribe(['system_database_connected'], h);
|
||||
require('../service/db');
|
||||
});
|
||||
require('../service/monitor');
|
||||
for (const i of builtinModel) require(`../model/${i}`);
|
||||
const modelSystem = require('../model/system');
|
||||
const dbVer = await modelSystem.get('db.ver');
|
||||
if (dbVer !== 1) {
|
||||
const ins = require('../script/install');
|
||||
const ins = require('../script/upgrade0_1');
|
||||
await ins.run({ username: 'Root', password: 'rootroot' });
|
||||
}
|
||||
for (const i in global.Hydro.service) {
|
||||
|
||||
+69
-63
@@ -2,12 +2,14 @@ import os from 'os';
|
||||
import path from 'path';
|
||||
import http from 'http';
|
||||
import fs from 'fs-extra';
|
||||
import Koa from 'koa';
|
||||
import Koa, { Context } from 'koa';
|
||||
import Body from 'koa-body';
|
||||
import Router from 'koa-router';
|
||||
import cache from 'koa-static-cache';
|
||||
import nunjucks from 'nunjucks';
|
||||
import mongodb from 'mongodb';
|
||||
import { argv } from 'yargs';
|
||||
|
||||
const listenPort = argv.port || 8888;
|
||||
|
||||
class Loader {
|
||||
getSource(name: string) { // eslint-disable-line class-methods-use-this
|
||||
@@ -83,7 +85,7 @@ class Loader {
|
||||
|
||||
const env = new nunjucks.Environment(new Loader(), { autoescape: true, trimBlocks: true });
|
||||
|
||||
function render(name) {
|
||||
function render(name: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
env.render(name, {
|
||||
_: (str: string) => str,
|
||||
@@ -94,70 +96,74 @@ function render(name) {
|
||||
});
|
||||
}
|
||||
|
||||
async function get(ctx: Context) {
|
||||
ctx.body = await render('setup.html');
|
||||
ctx.response.type = 'text/html';
|
||||
}
|
||||
|
||||
async function post(ctx: Context) {
|
||||
const {
|
||||
host, port, name, username, password,
|
||||
} = ctx.request.body;
|
||||
let mongourl = 'mongodb://';
|
||||
if (username) mongourl += `${username}:${password}@`;
|
||||
mongourl += `${host}:${port}/${name}`;
|
||||
try {
|
||||
const Database = await mongodb.MongoClient.connect(mongourl, {
|
||||
useNewUrlParser: true, useUnifiedTopology: true,
|
||||
});
|
||||
const db = Database.db(name);
|
||||
await Promise.all([
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.host' },
|
||||
{ $set: { value: ctx.request.host } },
|
||||
{ upsert: true },
|
||||
),
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.hostname' },
|
||||
{ $set: { value: ctx.request.hostname } },
|
||||
{ upsert: true },
|
||||
),
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.url' },
|
||||
{ $set: { value: ctx.request.href } },
|
||||
{ upsert: true },
|
||||
),
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.port' },
|
||||
{ $set: { value: parseInt(listenPort as string, 10) } },
|
||||
{ upsert: true },
|
||||
),
|
||||
]);
|
||||
fs.ensureDirSync(path.resolve(os.homedir(), '.hydro'));
|
||||
fs.writeFileSync(path.resolve(os.homedir(), '.hydro', 'config.json'), JSON.stringify({
|
||||
host, port, name, username, password,
|
||||
}));
|
||||
ctx.body = `<h1>This page will reload in 3 secs.</h1>
|
||||
<script>
|
||||
setTimeout(function (){
|
||||
window.location.href = '/';
|
||||
}, 3000);
|
||||
</script>`;
|
||||
setTimeout(() => process.exit(0), 500);
|
||||
} catch (e) {
|
||||
ctx.body = `Error connecting to database: ${e.message}\n${e.stack}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function load() {
|
||||
const app = new Koa();
|
||||
const server = http.createServer(app.callback());
|
||||
const router = new Router();
|
||||
app.keys = ['Hydro'];
|
||||
app.use(cache(path.join(os.tmpdir(), 'hydro', 'public'), {
|
||||
maxAge: 365 * 24 * 60 * 60,
|
||||
}));
|
||||
app.use(Body());
|
||||
router.get('/', async (ctx) => {
|
||||
ctx.body = await render('setup.html');
|
||||
ctx.response.type = 'text/html';
|
||||
});
|
||||
const listenPort = global.argv.port || 8888;
|
||||
router.post('/', async (ctx) => {
|
||||
const {
|
||||
host, port, name, username, password,
|
||||
} = ctx.request.body;
|
||||
let mongourl = 'mongodb://';
|
||||
if (username) mongourl += `${username}:${password}@`;
|
||||
mongourl += `${host}:${port}/${name}`;
|
||||
try {
|
||||
const Database = await mongodb.MongoClient.connect(mongourl, {
|
||||
useNewUrlParser: true, useUnifiedTopology: true,
|
||||
});
|
||||
const db = Database.db(name);
|
||||
await Promise.all([
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.host' },
|
||||
{ $set: { value: ctx.request.host } },
|
||||
{ upsert: true },
|
||||
),
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.hostname' },
|
||||
{ $set: { value: ctx.request.hostname } },
|
||||
{ upsert: true },
|
||||
),
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.url' },
|
||||
{ $set: { value: ctx.request.href } },
|
||||
{ upsert: true },
|
||||
),
|
||||
db.collection('system').updateOne(
|
||||
{ _id: 'server.port' },
|
||||
{ $set: { value: parseInt(listenPort, 10) } },
|
||||
{ upsert: true },
|
||||
),
|
||||
]);
|
||||
fs.ensureDirSync(path.resolve(os.homedir(), '.hydro'));
|
||||
fs.writeFileSync(path.resolve(os.homedir(), '.hydro', 'config.json'), JSON.stringify({
|
||||
host, port, name, username, password,
|
||||
}));
|
||||
ctx.body = `<h1>This page will reload in 3 secs.</h1>
|
||||
<script>
|
||||
setTimeout(function (){
|
||||
window.location.href = '/';
|
||||
}, 3000);
|
||||
</script>`;
|
||||
setTimeout(() => process.exit(0), 500);
|
||||
} catch (e) {
|
||||
ctx.body = `Error connecting to database: ${e.message}\n${e.stack}`;
|
||||
}
|
||||
});
|
||||
app.use(router.routes()).use(router.allowedMethods());
|
||||
app
|
||||
.use(cache(path.join(os.tmpdir(), 'hydro', 'public'), {
|
||||
maxAge: 365 * 24 * 60 * 60,
|
||||
}))
|
||||
.use(Body())
|
||||
.use((ctx) => {
|
||||
if (ctx.request.method.toLowerCase() === 'post') return post(ctx);
|
||||
return get(ctx);
|
||||
});
|
||||
server.listen(listenPort);
|
||||
console.log('Server listening at:', listenPort);
|
||||
}
|
||||
@@ -300,7 +300,6 @@ declare global {
|
||||
},
|
||||
onDestory: Function[],
|
||||
addons: string[],
|
||||
argv: any,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-20
@@ -2,7 +2,6 @@
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable no-eval */
|
||||
|
||||
import './interface';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
@@ -11,7 +10,6 @@ import fs from 'fs-extra';
|
||||
import { argv } from 'yargs';
|
||||
import AdmZip from 'adm-zip';
|
||||
|
||||
global.argv = argv;
|
||||
global.Hydro = {
|
||||
stat: { reqCount: 0 },
|
||||
handler: {},
|
||||
@@ -35,15 +33,16 @@ global.Hydro = {
|
||||
global.onDestory = [];
|
||||
global.addons = [];
|
||||
|
||||
if (global.argv.debug) {
|
||||
if (argv.debug) {
|
||||
console.log(process.argv);
|
||||
process.env.debug = 'enable';
|
||||
console.log(argv);
|
||||
}
|
||||
|
||||
async function terminate() {
|
||||
for (const task of global.onDestory) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await task();
|
||||
try {
|
||||
for (const task of global.onDestory) await task();
|
||||
} catch (e) {
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -80,9 +79,8 @@ async function stopWorker() {
|
||||
}
|
||||
|
||||
async function startWorker(cnt: number) {
|
||||
const sargv = [`--args=${Buffer.from(JSON.stringify(global.addons)).toString('base64')}`];
|
||||
await fork(['--firstWorker', ...sargv]);
|
||||
for (let i = 1; i < cnt; i++) await fork(sargv);
|
||||
await fork(['--firstWorker']);
|
||||
for (let i = 1; i < cnt; i++) await fork();
|
||||
}
|
||||
|
||||
async function executeCommand(input: string) {
|
||||
@@ -163,6 +161,9 @@ export function addon(addonPath: string) {
|
||||
} else throw new Error(`Addon not found: ${addonPath}`);
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (e) => console.error(e));
|
||||
process.on('SIGINT', terminate);
|
||||
|
||||
export async function load() {
|
||||
while (!global.addons) {
|
||||
await new Promise((resolve) => {
|
||||
@@ -171,11 +172,9 @@ export async function load() {
|
||||
}
|
||||
addon(path.resolve(__dirname, '..'));
|
||||
Error.stackTraceLimit = 50;
|
||||
process.on('unhandledRejection', (e) => console.error(e));
|
||||
process.on('SIGINT', terminate);
|
||||
process.on('message', messageHandler);
|
||||
cluster.on('message', messageHandler);
|
||||
if (cluster.isMaster || global.argv.startAsMaster) {
|
||||
if (cluster.isMaster || argv.startAsMaster) {
|
||||
console.log(`Master ${process.pid} Starting`);
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', (buf) => {
|
||||
@@ -205,12 +204,12 @@ export async function load() {
|
||||
worker.send({ event: 'setAddon', addons: global.addons });
|
||||
});
|
||||
await startWorker(cnt);
|
||||
} else if (global.argv.entry) {
|
||||
console.log(`Worker ${process.pid} Starting as ${global.argv.entry}`);
|
||||
await entry({ entry: global.argv.entry });
|
||||
console.log(`Worker ${process.pid} Started as ${global.argv.entry}`);
|
||||
} else if (argv.entry) {
|
||||
console.log(`Worker ${process.pid} Starting as ${argv.entry}`);
|
||||
await entry({ entry: argv.entry });
|
||||
console.log(`Worker ${process.pid} Started as ${argv.entry}`);
|
||||
} else {
|
||||
if (global.argv.firstWorker) cluster.isFirstWorker = true;
|
||||
if (argv.firstWorker) cluster.isFirstWorker = true;
|
||||
else cluster.isFirstWorker = false;
|
||||
console.log(`Worker ${process.pid} Starting`);
|
||||
await entry({ entry: 'worker' });
|
||||
@@ -219,8 +218,14 @@ export async function load() {
|
||||
if (global.gc) global.gc();
|
||||
}
|
||||
|
||||
if (global.argv.pandora || !module.parent) {
|
||||
load().catch((e) => {
|
||||
export async function loadCli() {
|
||||
await entry({ entry: 'cli' });
|
||||
return terminate();
|
||||
}
|
||||
|
||||
if (argv.pandora || !module.parent) {
|
||||
const func = argv._[0] === 'cli' ? load : loadCli;
|
||||
func().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dictionary } from 'lodash';
|
||||
import { BUILTIN_ROLES, PRIV } from './builtin';
|
||||
import { Udoc, DomainDoc } from '../interface';
|
||||
import { DomainDoc } from '../interface';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('domain');
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { PRIV } from '../model/builtin';
|
||||
import * as user from '../model/user';
|
||||
import * as domain from '../model/domain';
|
||||
import pwhash from '../lib/hash.hydro';
|
||||
|
||||
export const description = 'Install';
|
||||
|
||||
export async function run({ username = '', password = '' } = {}) {
|
||||
const ddoc = await domain.get('system');
|
||||
if (!ddoc) await domain.add('system', 0, 'Hydro', 'Hydro System');
|
||||
if (username && password) {
|
||||
const udoc = await user.getById('system', -1);
|
||||
if (!udoc) {
|
||||
await user.create('root@hydro.local', username, password, -1, '127.0.0.1', PRIV.PRIV_ALL);
|
||||
} else {
|
||||
const salt = String.random();
|
||||
await user.setById(-1, {
|
||||
uname: username,
|
||||
unameLower: username.trim().toLowerCase(),
|
||||
salt,
|
||||
hash: pwhash(password, salt),
|
||||
hashType: 'hydro',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const validate = {
|
||||
$or: [
|
||||
{ username: 'string', password: 'string' },
|
||||
{ username: 'undefined', password: 'undefined' },
|
||||
],
|
||||
};
|
||||
|
||||
global.Hydro.script.install = { run, description, validate };
|
||||
@@ -0,0 +1,12 @@
|
||||
import * as domain from '../model/domain';
|
||||
|
||||
export const description = 'Upgrade database from 0 to 1';
|
||||
|
||||
export async function run() {
|
||||
const ddoc = await domain.get('system');
|
||||
if (!ddoc) await domain.add('system', 0, 'Hydro', 'Hydro System');
|
||||
}
|
||||
|
||||
export const validate = {};
|
||||
|
||||
global.Hydro.script.setSuperadmin = { run, description, validate };
|
||||
@@ -2,7 +2,7 @@
|
||||
import assert from 'assert';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import http, { Server } from 'http';
|
||||
import http from 'http';
|
||||
import moment from 'moment-timezone';
|
||||
import { isSafeInteger, Dictionary } from 'lodash';
|
||||
import { ObjectID } from 'mongodb';
|
||||
@@ -776,8 +776,8 @@ export async function start() {
|
||||
}
|
||||
app.use(router.routes()).use(router.allowedMethods());
|
||||
Route('notfound_handler', '*', Handler);
|
||||
server.listen(global.argv.port || port);
|
||||
console.log('Server listening at: %s', global.argv.port || port);
|
||||
server.listen(argv.port || port);
|
||||
console.log('Server listening at: %s', argv.port || port);
|
||||
}
|
||||
|
||||
global.Hydro.service.server = {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.9.10",
|
||||
"version": "2.9.11",
|
||||
"main": "dist/loader.js",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||
|
||||
Reference in new issue
Block a user