交互式安装
parent
6134a16f8c
commit
27cfe81e31
@ -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);
|
||||
});
|
@ -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…
Reference in New Issue