交互式安装

pull/1/head
undefined 4 years ago
parent 6134a16f8c
commit 27cfe81e31

1
.gitignore vendored

@ -1,5 +1,6 @@
node_modules/
config.yaml
config.json
.build/
*.build
.uibuild/

@ -23,8 +23,6 @@ const build = async (type) => {
mode: type,
entry: {
development: root('hydro/development.js'),
install: root('hydro/install.js'),
uninstall: root('hydro/uninstall.js'),
},
output: {
filename: '[name].js',

@ -3,12 +3,12 @@ const {
ContestNotAttendedError,
} = require('../error');
const { PERM_CREATE_CONTEST, PERM_EDIT_CONTEST } = require('../permission');
const { constants } = require('../options');
const paginate = require('../lib/paginate');
const contest = require('../model/contest');
const problem = require('../model/problem');
const record = require('../model/record');
const user = require('../model/user');
const system = require('../model/system');
const { Route, Handler } = require('../service/server');
const ContestHandler = contest.ContestHandlerMixin(Handler);
@ -27,7 +27,7 @@ class ContestListHandler extends ContestHandler {
qs = 'rule={0}'.format(rule);
}
// eslint-disable-next-line prefer-const
[tdocs, tpcount] = await paginate(tdocs, page, constants.CONTEST_PER_PAGE);
[tdocs, tpcount] = await paginate(tdocs, page, await system.get('CONTEST_PER_PAGE'));
const tids = [];
for (const tdoc of tdocs) tids.push(tdoc._id);
const tsdict = await contest.getListStatus(this.user._id, tids);
@ -51,12 +51,14 @@ class ContestDetailHandler extends ContestHandler {
attended;
if (tsdoc) {
attended = tsdoc.attend === 1;
for (const pdetail of tsdoc.journal || []) { psdict[pdetail.pid] = pdetail; }
for (const pdetail of tsdoc.journal || []) psdict[pdetail.pid] = pdetail;
if (this.canShowRecord(this.tdoc)) {
const q = [];
for (const i in psdict) q.push(psdict[i].rid);
rdict = await record.getList(q);
} else { for (const i in psdict) rdict[psdict[i].rid] = { _id: psdict[i].rid }; }
} else {
for (const i in psdict) rdict[psdict[i].rid] = { _id: psdict[i].rid };
}
} else attended = false;
const udict = await user.getList([this.tdoc.owner]);
const path = [

@ -1,6 +1,7 @@
const paginate = require('../lib/paginate');
const problem = require('../model/problem');
const contest = require('../model/contest');
const system = require('../model/system');
const user = require('../model/user');
const discussion = require('../model/discussion');
const {
@ -9,7 +10,6 @@ const {
const {
DiscussionNodeNotFoundError, DiscussionNotFoundError, DocumentNotFoundError,
} = require('../error');
const { constants } = require('../options');
const {
PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY,
PERM_VIEW_PROBLEM_HIDDEN, PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY,
@ -65,7 +65,7 @@ class DiscussionMainHandler extends DiscussionHandler {
const [ddocs, dpcount] = await paginate(
discussion.getMulti(),
page,
constants.DISCUSSION_PER_PAGE,
await system.get('DISCUSSION_PER_PAGE'),
);
const udict = await user.getList(ddocs.map((ddoc) => ddoc.owner));
const path = [
@ -84,7 +84,7 @@ class DiscussionNodeHandler extends DiscussionHandler {
const [ddocs, dpcount] = await paginate(
discussion.getMulti({ type, docId }),
page,
constants.DISCUSSION_PER_PAGE,
await system.get('DISCUSSION_PER_PAGE'),
);
const udict = await user.getList(ddocs.map((ddoc) => ddoc.owner));
const path = [
@ -142,7 +142,7 @@ class DiscussionDetailHandler extends DiscussionHandler {
const [drdocs, pcount, drcount] = await paginate(
discussion.getMultiReply(did),
page,
constants.REPLY_PER_PAGE,
await system.get('REPLY_PER_PAGE'),
);
const uids = drdocs.map((drdoc) => drdoc.owner);
uids.push(this.ddoc.owner);

@ -2,7 +2,6 @@ const {
VerifyPasswordError, UserAlreadyExistError, InvalidTokenError,
NotFoundError, MessageNotFoundError,
} = require('../error');
const options = require('../options');
const bus = require('../service/bus');
const {
Route, Connection, Handler, ConnectionHandler,
@ -11,6 +10,7 @@ const misc = require('../lib/misc');
const md5 = require('../lib/md5');
const contest = require('../model/contest');
const message = require('../model/message');
const system = require('../model/system');
const user = require('../model/user');
const setting = require('../model/setting');
const discussion = require('../model/discussion');
@ -20,13 +20,12 @@ const {
PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION,
PERM_LOGGEDIN,
} = require('../permission');
const { CONTESTS_ON_MAIN, TRAININGS_ON_MAIN, DISCUSSIONS_ON_MAIN } = require('../options').constants;
class HomeHandler extends Handler {
async contest() {
if (this.user.hasPerm(PERM_VIEW_CONTEST)) {
const tdocs = await contest.getMulti()
.limit(CONTESTS_ON_MAIN)
.limit(await system.get('CONTESTS_ON_MAIN'))
.toArray();
const tsdict = await contest.getListStatus(
this.user._id, tdocs.map((tdoc) => tdoc._id),
@ -40,7 +39,7 @@ class HomeHandler extends Handler {
if (this.user.hasPerm(PERM_VIEW_TRAINING)) {
const tdocs = await training.getMulti()
.sort('_id', 1)
.limit(TRAININGS_ON_MAIN)
.limit(await system.get('TRAININGS_ON_MAIN'))
.toArray();
const tsdict = await training.getListStatus(
this.user._id, tdocs.map((tdoc) => tdoc._id),
@ -53,7 +52,7 @@ class HomeHandler extends Handler {
async discussion() {
if (this.user.hasPerm(PERM_VIEW_DISCUSSION)) {
const ddocs = await discussion.getMulti()
.limit(DISCUSSIONS_ON_MAIN)
.limit(await system.get('DISCUSSIONS_ON_MAIN'))
.toArray();
const vndict = await discussion.getListVnodes(ddocs, this);
return [ddocs, vndict];
@ -106,7 +105,7 @@ class HomeSecurityHandler extends Handler {
if (udoc) throw new UserAlreadyExistError(mail);
const [rid] = await token.add(
token.TYPE_CHANGEMAIL,
options.changemail_token_expire_seconds,
await system.get('changemail_token_expire_seconds'),
{ uid: this.udoc._id, mail },
);
await mail.sendMail(mail, 'Change Email', 'user_changemail_mail.html', {

@ -15,7 +15,6 @@ const {
NoProblemError, ProblemDataNotFoundError, BadRequestError,
SolutionNotFoundError,
} = require('../error');
const { constants } = require('../options');
const {
PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM,
PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM,
@ -38,7 +37,7 @@ class ProblemHandler extends Handler {
const [pdocs, pcount] = await paginate(
problem.getMulti(q).sort({ pid: 1 }),
page,
constants.PROBLEM_PER_PAGE,
await system.get('PROBLEM_PER_PAGE'),
);
if (this.user.hasPerm(PERM_LOGGEDIN)) {
psdict = await problem.getListStatus(this.user._id, pdocs.map((pdoc) => pdoc._id));
@ -252,7 +251,8 @@ class ProblemSolutionHandler extends ProblemDetailHandler {
this.checkPerm(PERM_VIEW_PROBLEM_SOLUTION);
const [psdocs, pcount, pscount] = await paginate(
solution.getMulti(this.pdoc._id),
page, constants.SOLUTION_PER_PAGE,
page,
await system.get('SOLUTION_PER_PAGE'),
);
const uids = [this.pdoc.owner]; const
docids = [];

@ -1,4 +1,3 @@
const { constants } = require('../options');
const {
PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD,
PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN,
@ -6,6 +5,7 @@ const {
const problem = require('../model/problem');
const record = require('../model/record');
const contest = require('../model/contest');
const system = require('../model/system');
const user = require('../model/user');
const bus = require('../service/bus');
const {
@ -16,7 +16,7 @@ class RecordListHandler extends Handler {
async get({ page = 1 }) {
this.response.template = 'record_main.html';
const q = {};
const rdocs = await record.getMany(q, { _id: -1 }, page, constants.RECORD_PER_PAGE);
const rdocs = await record.getMany(q, { _id: -1 }, page, await system.get('RECORD_PER_PAGE'));
const pdict = {};
const udict = {};
const ulist = [];

@ -4,12 +4,12 @@ const {
PERM_LOGGEDIN, PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN,
PERM_CREATE_TRAINING, PERM_EDIT_TRAINING,
} = require('../permission');
const { constants } = require('../options');
const paginate = require('../lib/paginate');
const problem = require('../model/problem');
const builtin = require('../model/builtin');
const training = require('../model/training');
const user = require('../model/user');
const system = require('../model/system');
const { Route, Handler } = require('../service/server');
async function _parseDagJson(dag) {
@ -60,7 +60,7 @@ class TrainingMainHandler extends TrainingHandler {
const [tdocs, tpcount] = await paginate(
training.getMulti().sort('_id', 1),
page,
constants.TRAINING_PER_PAGE,
await system.get('TRAINING_PER_PAGE'),
);
const tids = new Set();
for (const tdoc of tdocs) tids.add(tdoc._id);

@ -4,7 +4,6 @@ const token = require('../model/token');
const system = require('../model/system');
const { sendMail } = require('../lib/mail');
const misc = require('../lib/misc');
const options = require('../options');
const { PERM_REGISTER_USER, PERM_LOGGEDIN } = require('../permission');
const {
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
@ -58,10 +57,10 @@ class UserRegisterHandler extends Handler {
this.limitRate('send_mail', 3600, 30);
const t = await token.add(
token.TYPE_REGISTRATION,
options.registration_token_expire_seconds,
await system.get('registration_token_expire_seconds'),
{ mail },
);
if (options.smtp.user) {
if (await system.get('smtp.user')) {
const m = await this.renderHTML('user_register_mail', { url: `/register/${t}` });
await sendMail(mail, 'Sign Up', 'user_register_mail', m);
this.response.template = 'user_register_mail_sent.html';
@ -100,21 +99,18 @@ class UserRegisterWithCodeHandler extends Handler {
}
class UserLostPassHandler extends Handler {
constructor(ctx) {
if (!options.smtp.user) throw new SystemError('Cannot send mail');
super(ctx);
}
async get() {
if (!await system.get('smtp.user')) throw new SystemError('Cannot send mail');
this.response.template = 'user_lostpass.html';
}
async post({ mail }) {
if (!await system.get('smtp.user')) throw new SystemError('Cannot send mail');
const udoc = await user.getByEmail(mail);
if (!udoc) throw new UserNotFoundError(mail);
const tid = await token.add(
token.TYPE_LOSTPASS,
options.lostpass_token_expire_seconds,
await system.get('lostpass_token_expire_seconds'),
{ uid: udoc._id },
);
const m = await this.renderHTML('user_lostpass_mail', { url: `/lostpass/${tid}`, uname: udoc.uname });
@ -142,16 +138,12 @@ class UserLostPassWithCodeHandler extends Handler {
}
class UserDetailHandler extends Handler {
constructor(ctx) {
super(ctx);
this.response.template = 'user_detail.html';
}
async get({ uid }) {
const isSelfProfile = this.user._id === uid;
const udoc = await user.getById(uid);
if (!udoc) throw new UserNotFoundError(uid);
const sdoc = await token.getMostRecentSessionByUid(uid);
this.response.template = 'user_detail.html';
this.response.body = { isSelfProfile, udoc, sdoc };
}
}

@ -1,66 +0,0 @@
require('./utils');
const Mongo = require('mongodb');
const { defaults } = require('lodash');
const builtin = require('./model/builtin');
const pwhash = require('./lib/pwhash');
const options = require('./options');
const { udoc } = require('./interface');
async function run() {
let mongourl = 'mongodb://';
if (options.db.username) mongourl += `${options.db.username}:${options.db.password}@`;
mongourl += `${options.db.host}:${options.db.port}/${options.db.name}`;
const Database = await Mongo.MongoClient.connect(
mongourl,
{ useNewUrlParser: true, useUnifiedTopology: true },
);
const db = Database.db(options.db.name);
const collUser = db.collection('user');
const collRole = db.collection('role');
const collBlacklist = db.collection('blacklist');
const collToken = db.collection('token');
async function createUser() {
const salt = pwhash.salt();
await collUser.insertMany([
defaults({
_id: 0,
uname: 'Hydro',
unameLower: 'hydro',
mail: 'hydro@hydro',
mailLower: 'hydro@hydro',
role: 'guest',
}, udoc),
defaults({
_id: 1,
mail: 'guest@hydro',
mailLower: 'guest@hydro',
uname: 'Guest',
unameLower: 'guest',
role: 'guest',
}, udoc),
defaults({
_id: -1,
mail: 'root@hydro',
mailLower: 'root@hydro',
uname: 'Root',
unameLower: 'root',
hash: pwhash.hash('rootroot', salt),
salt,
gravatar: 'root@hydro',
role: 'root',
}, udoc),
]);
}
await collRole.insertMany(builtin.BUILTIN_ROLES);
await collBlacklist.createIndex('expireAt', { expireAfterSeconds: 0 });
await collToken.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true });
await collToken.createIndex('expireAt', { expireAfterSeconds: 0 });
await createUser();
console.log('Installed');
process.exit(0);
}
run().catch((e) => {
console.error(e);
process.exit(1);
});

@ -142,6 +142,41 @@ async function service() {
}
}
async function install() {
await Promise.all([lib(), locale(), template()]);
require('./service/setup');
}
async function installDb() {
const system = require('./model/system');
const def = {
PROBLEM_PER_PAGE: 100,
RECORD_PER_PAGE: 100,
SOLUTION_PER_PAGE: 20,
CONTEST_PER_PAGE: 20,
TRAINING_PER_PAGE: 10,
DISCUSSION_PER_PAGE: 50,
REPLY_PER_PAGE: 50,
CONTESTS_ON_MAIN: 5,
TRAININGS_ON_MAIN: 5,
DISCUSSIONS_ON_MAIN: 20,
'db.ver': 1,
'listen.https': false,
'listen.port': 8888,
'session.keys': ['Hydro'],
'session.secure': false,
'session.saved_expire_seconds': 3600 * 24,
'session.unsaved_expire_seconds': 600,
changemail_token_expire_seconds: 3600 * 24,
registration_token_expire_seconds: 600,
};
const tasks = [];
for (const key in def) {
tasks.push(system.set(key, def[key]));
}
await Promise.all(tasks);
}
async function load() {
ensureDir(path.resolve(os.tmpdir(), 'hydro'));
global.Hydro = {
@ -153,12 +188,16 @@ async function load() {
ui: {},
};
await preload();
await template();
require('./lib/i18n');
require('./utils');
require('./error');
require('./permission');
require('./options');
try {
require('./options');
} catch (e) {
await install();
return;
}
const bus = require('./service/bus');
await new Promise((resolve) => {
const h = () => {
@ -175,20 +214,23 @@ async function load() {
'template', 'validator', 'nav',
];
for (const i of builtinLib) require(`./lib/${i}`);
await lib();
await locale();
await Promise.all([lib(), locale(), template()]);
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',
'system', 'token', 'training', 'user',
'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) await installDb();
const builtinHandler = [
'home', 'problem', 'record', 'judge', 'user',
'contest', 'training', 'discussion', 'manage', 'import',
@ -204,7 +246,7 @@ async function load() {
for (const i in global.Hydro.service) {
if (global.Hydro.service[i].postInit) await global.Hydro.service[i].postInit();
}
server.start();
await server.start();
}
module.exports = { load, active };

@ -6,11 +6,19 @@ async function add(ip) {
const expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
}
function get(ip) {
return coll.findOne({ _id: ip });
}
function del(ip) {
return coll.deleteOne({ _id: ip });
}
module.exports = { add, get, del };
function index() {
return coll.createIndex('expireAt', { expireAfterSeconds: 0 });
}
module.exports = {
add, get, del, index,
};

@ -12,7 +12,7 @@ async function update(_id, operation, config) {
return get(_id);
}
async function set(_id, value) {
await coll.findOneAndUpdate({ _id }, { value }, { upsert: true });
await coll.findOneAndUpdate({ _id }, { $set: { value } }, { upsert: true });
return get(_id);
}
/**

@ -3,12 +3,21 @@ const { ValidationError } = require('../error');
const coll = db.collection('token');
function index() {
return Promise.all([
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
coll.createIndex('expireAt', { expireAfterSeconds: 0 }),
]);
}
module.exports = {
TYPE_SESSION: 0,
TYPE_CSRF_TOKEN: 1,
TYPE_REGISTER: 2,
TYPE_CHANGEMAIL: 3,
index,
/**
* Add a token.
* @param {number} tokenType type of the token.

@ -51,14 +51,10 @@ let options = {
},
};
try {
let f = path.resolve(process.cwd(), 'config.yaml');
if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.config', 'hydro', 'config.yaml');
if (!fs.existsSync(f)) f = path.resolve('/config/config.yaml');
const t = yaml.safeLoad(fs.readFileSync(f));
options = defaultsDeep(t, options);
} catch (e) {
console.error('Cannot load config');
}
let f = path.resolve(process.cwd(), 'config.json');
if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.config', 'hydro', 'config.json');
if (!fs.existsSync(f)) f = path.resolve('/config/config.json');
const t = JSON.parse(fs.readFileSync(f));
options = defaultsDeep(t, options);
module.exports = options;

@ -4,50 +4,42 @@ const builtin = require('../model/builtin');
const pwhash = require('../lib/pwhash');
const { udoc } = require('../interface');
const collUser = db.collection('user');
const collRole = db.collection('role');
const collSystem = db.collection('system');
async function run() {
const collUser = db.collection('user');
const collRole = db.collection('role');
const collBlacklist = db.collection('blacklist');
const collToken = db.collection('token');
async function createUser() {
const salt = pwhash.salt();
await collUser.insertMany([
defaults({
_id: 0,
uname: 'Hydro',
unameLower: 'hydro',
mail: 'hydro@hydro',
mailLower: 'hydro@hydro',
role: 'guest',
}, udoc),
defaults({
_id: 1,
mail: 'guest@hydro',
mailLower: 'guest@hydro',
uname: 'Guest',
unameLower: 'guest',
role: 'guest',
}, udoc),
defaults({
_id: -1,
mail: 'root@hydro',
mailLower: 'root@hydro',
uname: 'Root',
unameLower: 'root',
hash: pwhash.hash('rootroot', salt),
salt,
gravatar: 'root@hydro',
role: 'root',
}, udoc),
]);
}
await collUser.createIndex('unameLower', { unique: true });
await collUser.createIndex('mailLower', { sparse: true });
const salt = pwhash.salt();
await collUser.insertMany([
defaults({
_id: 0,
uname: 'Hydro',
unameLower: 'hydro',
mail: 'hydro@hydro',
mailLower: 'hydro@hydro',
role: 'guest',
}, udoc),
defaults({
_id: 1,
mail: 'guest@hydro',
mailLower: 'guest@hydro',
uname: 'Guest',
unameLower: 'guest',
role: 'guest',
}, udoc),
defaults({
_id: -1,
mail: 'root@hydro',
mailLower: 'root@hydro',
uname: 'Root',
unameLower: 'root',
hash: pwhash.hash('rootroot', salt),
salt,
gravatar: 'root@hydro',
role: 'root',
}, udoc),
]);
await collRole.insertMany(builtin.BUILTIN_ROLES);
await collBlacklist.createIndex('expireAt', { expireAfterSeconds: 0 });
await collToken.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true });
await collToken.createIndex('expireAt', { expireAfterSeconds: 0 });
await createUser();
console.log('Installed');
}

@ -18,4 +18,5 @@ Mongo.MongoClient.connect(mongourl, { useNewUrlParser: true, useUnifiedTopology:
global.Hydro.service.db = module.exports = {
collection: (c) => db.collection(c),
dropDatabase: () => db.dropDatabase(),
};

@ -8,10 +8,10 @@ const cache = require('koa-static-cache');
const sockjs = require('sockjs');
const http = require('http');
const https = require('https');
const options = require('../options');
const validator = require('../lib/validator');
const template = require('../lib/template');
const user = require('../model/user');
const system = require('../model/system');
const blacklist = require('../model/blacklist');
const token = require('../model/token');
const opcount = require('../model/opcount');
@ -21,19 +21,24 @@ const {
} = require('../error');
const app = new Koa();
const server = (options.listen.https ? https : http).createServer(app.callback());
app.keys = options.session.keys;
app.use(cache(path.join(process.cwd(), '.uibuild'), {
maxAge: 365 * 24 * 60 * 60,
}));
app.use(Body({
multipart: true,
formidable: {
maxFileSize: 256 * 1024 * 1024,
},
}));
let server;
const router = new Router();
async function prepare() {
const useHttps = await system.get('listen.https');
server = (useHttps ? https : http).createServer(app.callback());
app.keys = await system.get('session.keys');
app.use(cache(path.join(process.cwd(), '.uibuild'), {
maxAge: 365 * 24 * 60 * 60,
}));
app.use(Body({
multipart: true,
formidable: {
maxFileSize: 256 * 1024 * 1024,
},
}));
}
class Handler {
/**
* @param {import('koa').Context} ctx
@ -128,8 +133,8 @@ class Handler {
this.now = new Date();
this._handler.sid = this.request.cookies.get('sid');
this._handler.save = this.request.cookies.get('save');
if (this._handler.save) this._handler.expireSeconds = options.session.saved_expire_seconds;
else this._handler.expireSeconds = options.session.unsaved_expire_seconds;
if (this._handler.save) this._handler.expireSeconds = await system.get('session.saved_expire_seconds');
else this._handler.expireSeconds = await system.get('session.unsaved_expire_seconds');
this.session = this._handler.sid
? await token.update(
this._handler.sid,
@ -213,7 +218,7 @@ class Handler {
},
);
}
const cookie = { secure: options.session.secure };
const cookie = { secure: await system.get('session.secure') };
if (this._handler.save) {
cookie.expires = this.session.expireAt;
cookie.maxAge = this._handler.expireSeconds;
@ -354,6 +359,7 @@ class ConnectionHandler {
if (!this.user) throw new UserNotFoundError(this.session.uid);
}
}
function Connection(prefix, RouteConnHandler) {
const sock = sockjs.createServer({ prefix });
sock.on('connection', async (conn) => {
@ -389,14 +395,14 @@ function Connection(prefix, RouteConnHandler) {
sock.installHandlers(server);
}
function start() {
async function start() {
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
app.use(router.routes()).use(router.allowedMethods());
Route('*', Handler);
server.listen(options.listen.port);
console.log('Server listening at: %s', options.listen.port);
server.listen(await system.get('listen.port'));
console.log('Server listening at: %s', await system.get('listen.port'));
}
global.Hydro.service.server = module.exports = {
Handler, ConnectionHandler, Route, Connection, start,
Handler, ConnectionHandler, Route, Connection, prepare, start,
};

@ -0,0 +1,73 @@
const fs = require('fs');
const path = require('path');
const Koa = require('koa');
const morgan = require('koa-morgan');
const Body = require('koa-body');
const Router = require('koa-router');
const cache = require('koa-static-cache');
const http = require('http');
const nunjucks = require('nunjucks');
function Loader() { }
Loader.prototype.getSource = function getSource(name) {
if (!global.Hydro.template[name]) throw new Error(`Cannot get template ${name}`);
return {
src: global.Hydro.template[name],
path: name,
};
};
class Nunjucks extends nunjucks.Environment {
constructor() {
super(new Loader(), { autoescape: true, trimBlocks: true });
this.addFilter('json', (self) => JSON.stringify(self), false);
this.addFilter('base64_encode', (s) => Buffer.from(s).toString('base64'));
}
}
const env = new Nunjucks();
function render(name) {
return new Promise((resolve, reject) => {
env.render(name, {
typeof: (o) => typeof o,
static_url: (str) => `/${str}`,
handler: { renderTitle: (str) => str },
_: (str) => str,
}, (err, res) => {
if (err) reject(err);
else resolve(res);
});
});
}
const app = new Koa();
const server = http.createServer(app.callback());
const router = new Router();
app.keys = ['Hydro'];
app.use(cache(path.join(process.cwd(), '.uibuild'), {
maxAge: 365 * 24 * 60 * 60,
}));
app.use(Body({
multipart: true,
formidable: {
maxFileSize: 256 * 1024 * 1024,
},
}));
router.get('/', async (ctx) => {
ctx.body = await render('setup.html');
ctx.response.type = 'text/html';
});
router.post('/', async (ctx) => {
const {
host, port, name, username, password,
} = ctx.request.body;
fs.writeFileSync(path.resolve(process.cwd(), 'config.json'), JSON.stringify({
host, port, name, username, password,
}));
ctx.body = await render('setup_done.html');
ctx.response.type = 'text/html';
});
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
app.use(router.routes()).use(router.allowedMethods());
server.listen(8888);
console.log('Server listening at: 8888');

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html data-page="setup" data-layout="immersive" class="layout--immersive page--setup nojs">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="chrome=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<meta name="msapplication-TileColor" content="#579e9a">
<meta name="theme-color" content="#56758f">
<link rel="stylesheet" media="all" href="{{ static_url('vj4.css') }}">
<title>{{ _('Setup') }}</title>
<script>
var _htmlNode = document.documentElement;
_htmlNode.className = _htmlNode.className.replace(' nojs', ' hasjs');
</script>
</head>
<body>
<div class="slideout-panel" id="panel">
<div class="main">
<div class="row"><div class="columns">
<div class="immersive--content immersive--center">
<h1>{{ _('Setup') }}</h1>
<form method="POST">
<div class="row"><div class="columns">
<label class="inverse material textbox">
{{ _('Database Host') }}
<input name="host" type="text" value="127.0.0.1" autofocus>
</label>
</div></div>
<div class="row"><div class="columns">
<label class="inverse material textbox">
{{ _('Database Port') }}
<input name="port" type="number" value="27017" autofocus>
</label>
</div></div>
<div class="row"><div class="columns">
<label class="inverse material textbox">
{{ _('Database Name') }}
<input name="name" type="text" value="hydro" autofocus>
</label>
</div></div>
<div class="row"><div class="columns">
<label class="inverse material textbox">
{{ _('Database Username') }}
<input name="username" type="text" placeholder="{{ _('Leave blank if none') }}" autofocus>
</label>
</div></div>
<div class="row"><div class="columns">
<label class="inverse material textbox">
{{ _('Database Password') }}
<input name="password" type="password" placeholder="{{ _('Leave blank if none') }}" autofocus>
</label>
</div></div>
<div class="row"><div class="columns">
<div class="text-center">
<input type="submit" value="{{ _('Confirm') }}" class="inverse expanded rounded primary button">
</div>
</div></div>
</form>
</div>
</div></div>
</div>
</div>
<script type="text/javascript" src="{{ static_url('vj4.js') }}"></script>
</body>
</html>

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html data-page="setup" data-layout="immersive" class="layout--immersive page--setup nojs">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="chrome=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<meta name="msapplication-TileColor" content="#579e9a">
<meta name="theme-color" content="#56758f">
<link rel="stylesheet" media="all" href="{{ static_url('vj4.css') }}">
<title>{{ _('Setup') }}</title>
<script>
var _htmlNode = document.documentElement;
_htmlNode.className = _htmlNode.className.replace(' nojs', ' hasjs');
</script>
</head>
<body>
<div class="slideout-panel" id="panel">
<div class="main">
<div class="row"><div class="columns">
<div class="immersive--content immersive--center">
<h1>{{ _('Setup') }}</h1>
<div class="text-center">
{{ _('Done. Please restart the program.') }}
</div>
</div>
</div></div>
</div>
</div>
<script type="text/javascript" src="{{ static_url('vj4.js') }}"></script>
</body>
</html>
Loading…
Cancel
Save