Github Oauth,支持自动转换config.ini配置

This commit is contained in:
undefined committed 2020-06-21 13:37:55 +08:00
1 parent 5707f973d7
commit 8476eac3b4
17 files changed
+240 -109

No files matched your search

+4
View File
@@ -55,6 +55,10 @@ async function load() {
await script(pending, fail, active); await script(pending, fail, active);
pending = []; pending = [];
await server.start(); await server.start();
setInterval(() => {
process.send({ event: 'stat', count: global.Hydro.stat.reqCount });
global.Hydro.stat.reqCount = 0;
}, 30 * 1000);
return { active, fail }; return { active, fail };
} }
+9 -6
View File
@@ -1,6 +1,7 @@
const user = require('../model/user'); const user = require('../model/user');
const domain = require('../model/domain'); const domain = require('../model/domain');
const system = require('../model/system'); const system = require('../model/system');
const { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } = require('../model/setting');
const paginate = require('../lib/paginate'); const paginate = require('../lib/paginate');
const { Route, Handler } = require('../service/server'); const { Route, Handler } = require('../service/server');
const { PERM_MANAGE } = require('../permission'); const { PERM_MANAGE } = require('../permission');
@@ -67,14 +68,16 @@ class DomainEditHandler extends ManageHandler {
['domain_edit', null], ['domain_edit', null],
]; ];
this.response.template = 'domain_edit.html'; this.response.template = 'domain_edit.html';
this.response.body = { domain: this.domain, path }; this.response.body = { current: this.domain, settings: DOMAIN_SETTINGS, path };
} }
async post({ async post(args) {
domainId, name, gravatar, bulletin, const $set = {};
}) { for (const key of args) {
await domain.edit(domainId, { name, gravatar, bulletin }); if (DOMAIN_SETTINGS_BY_KEY[key]) $set[key] = args[key];
this.response.redirect = '/domain/dashboard'; }
await domain.edit(args.domainId, $set);
this.response.redirect = this.url('domain_dashboard');
} }
} }
+71 -3
View File
@@ -1,3 +1,4 @@
const axios = require('axios');
const { Route, Handler } = require('../service/server.js'); const { Route, Handler } = require('../service/server.js');
const user = require('../model/user'); const user = require('../model/user');
const token = require('../model/token'); const token = require('../model/token');
@@ -7,7 +8,7 @@ const misc = require('../lib/misc');
const { const {
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError, UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
UserNotFoundError, LoginError, SystemError, UserNotFoundError, LoginError, SystemError,
PermissionError, BlacklistedError, PermissionError, BlacklistedError, UserFacingError,
} = require('../error'); } = require('../error');
class UserLoginHandler extends Handler { class UserLoginHandler extends Handler {
@@ -25,8 +26,7 @@ class UserLoginHandler extends Handler {
if (udoc.ban) throw new BlacklistedError(uname); if (udoc.ban) throw new BlacklistedError(uname);
this.session.uid = udoc._id; this.session.uid = udoc._id;
this.session.rememberme = rememberme; this.session.rememberme = rememberme;
const referer = this.request.headers.referer || '/'; this.response.redirect = this.request.referer.endsWith('/login') ? '/' : this.request.referer;
this.response.redirect = referer.endsWith('/login') ? '/' : referer;
} }
} }
@@ -161,8 +161,76 @@ class UserSearchHandler extends Handler {
} }
} }
class OauthHandler extends Handler {
async get({ type }) {
if (type === 'github') {
const [appid, [state]] = await Promise.all([
system.get('oauth.githubappid'),
token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }),
]);
this.response.redirect = `https://github.com/login/oauth/authorize?client_id=${appid}&state=${state}`;
}
}
}
class OauthCallbackHandler extends Handler {
async get({ type, state, code }) {
if (type === 'github') {
const [appid, secret, url, s] = await Promise.all([
system.get('oauth.githubappid'),
system.get('oauth.githubsecret'),
system.get('server.url'),
token.get(state, token.TYPE_OAUTH),
]);
const res = await axios.post('https://github.com/login/oauth/access_token', {
client_id: appid,
client_secret: secret,
code,
redirect_uri: `${url}oauth/github/callback`,
state,
}, { headers: { accept: 'application/json' } });
if (res.data.error) {
throw new UserFacingError(
res.data.error, res.data.error_description, res.data.error_uri,
);
}
const t = res.data.access_token;
const userInfo = await axios.get('https://api.github.com/user', {
headers: {
Authorization: `token ${t}`,
},
});
const {
email: mail, bio, name, login,
} = userInfo.data;
const udoc = await user.getByEmail('system', mail, true);
if (udoc) {
this.session.uid = udoc._id;
await token.del(s, token.TYPE_OAUTH);
} else {
let uname;
const nudoc = await user.getByUname('system', name, true);
if (!nudoc) uname = name;
else {
const ludoc = await user.getByUname('system', login, true);
if (!ludoc) uname = login;
else uname = String.random(16);
}
const uid = await user.create({
mail, uname, password: String.random(32), regip: this.request.ip,
});
await user.setById(uid, { bio, oauth: 'github' });
this.session.uid = uid;
}
this.response.redirect = s.redirect;
}
}
}
async function apply() { async function apply() {
Route('user_login', '/login', UserLoginHandler); Route('user_login', '/login', UserLoginHandler);
Route('user_oauth', '/oauth/:type', OauthHandler);
Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler);
Route('user_register', '/register', UserRegisterHandler); Route('user_register', '/register', UserRegisterHandler);
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler); Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler);
Route('user_logout', '/logout', UserLogoutHandler); Route('user_logout', '/logout', UserLogoutHandler);
+2 -3
View File
@@ -1,8 +1,7 @@
const ansiup = require('ansi_up'); const { default: AnsiUp } = require('ansi_up');
const md5 = require('./md5'); const md5 = require('./md5');
// eslint-disable-next-line new-cap const AU = new AnsiUp();
const AU = new ansiup.default();
function ansiToHtml(str) { function ansiToHtml(str) {
return AU.ansi_to_html(str); return AU.ansi_to_html(str);
+19 -4
View File
@@ -6,12 +6,27 @@ function convert(ini) {
const res = { cases: [] }; const res = { cases: [] };
for (let i = 1; i <= count; i++) { for (let i = 1; i <= count; i++) {
const [input, output, time, score, memory] = f[i].split('|'); const [input, output, time, score, memory] = f[i].split('|');
const cur = { input, output, score }; const cur = { input, output, score: parseInt(score) };
if (parseInt(time) !== 1) cur.time = `${time}s`; cur.time = `${time}s`;
if (!Number.isNaN(parseInt(memory))) cur.memory = parseInt(memory) / 1024; if (!Number.isNaN(parseInt(memory))) cur.memory = `${Math.floor(parseInt(memory) / 1024)}m`;
else cur.memory = '128m';
res.cases.push(cur); res.cases.push(cur);
} }
return yaml.safeDump(res); if (res.cases.length > 0) {
const { time, score, memory } = res.cases[0];
for (let i = 1; i < res.cases.length; i++) {
if (res.cases[i].time !== time
|| res.cases[i].score !== score
|| res.cases[i].memory !== memory) {
return yaml.safeDump(res);
}
}
}
return yaml.safeDump({
time: res.cases[0].time,
score: res.cases[0].score,
memory: res.cases[0].memory,
});
} }
global.Hydro.lib['testdata.convert.ini'] = module.exports = convert; global.Hydro.lib['testdata.convert.ini'] = module.exports = convert;
+14 -8
View File
@@ -58,24 +58,30 @@ async function executeCommand(input) {
async function messageHandler(worker, msg) { async function messageHandler(worker, msg) {
if (!msg) msg = worker; if (!msg) msg = worker;
if (msg.event && msg.event === 'restart') { if (msg.event) {
console.log('Restarting'); if (msg.event === 'stat') {
await stopWorker(); global.Hydro.stat.reqCount += msg.count;
console.log('Worker stopped'); } else if (msg.event === 'restart') {
await startWorker(msg.count); console.log('Restarting');
} else if (msg.event && msg.event === 'run') { await stopWorker();
await executeCommand(msg.command); console.log('Worker stopped');
await startWorker(msg.count);
} else if (msg.event === 'run') {
await executeCommand(msg.command);
}
} }
} }
async function load() { async function load() {
global.nodeModules = { global.nodeModules = {
'adm-zip': require('adm-zip'),
axios: require('axios'),
bson: require('bson'), bson: require('bson'),
'js-yaml': require('js-yaml'), 'js-yaml': require('js-yaml'),
mongodb: require('mongodb'), mongodb: require('mongodb'),
}; };
global.Hydro = { global.Hydro = {
config: {}, stat: { reqCount: 0 },
handler: {}, handler: {},
service: {}, service: {},
model: {}, model: {},
+2 -1
View File
@@ -1,5 +1,5 @@
const fs = require('fs'); const fs = require('fs');
const { ForbiddenError } = require('../error'); const { ForbiddenError, NotFoundError } = require('../error');
const db = require('../service/db'); const db = require('../service/db');
const gridfs = require('../service/gridfs'); const gridfs = require('../service/gridfs');
const hash = require('../lib/hash.hydro'); const hash = require('../lib/hash.hydro');
@@ -55,6 +55,7 @@ async function dec(_id) {
async function get(_id, secret) { async function get(_id, secret) {
const file = await coll.findOne({ _id }); const file = await coll.findOne({ _id });
if (!file) throw new NotFoundError(_id);
if (typeof secret !== 'undefined') { if (typeof secret !== 'undefined') {
const timestamp = _timestamp(); const timestamp = _timestamp();
if (!(hash(file.secret, timestamp.toString()) === secret)) { if (!(hash(file.secret, timestamp.toString()) === secret)) {
+21 -1
View File
@@ -12,9 +12,11 @@ const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]);
const PREFERENCE_SETTINGS = []; const PREFERENCE_SETTINGS = [];
const ACCOUNT_SETTINGS = []; const ACCOUNT_SETTINGS = [];
const DOMAIN_SETTINGS = [];
const SYSTEM_SETTINGS = []; const SYSTEM_SETTINGS = [];
const SETTINGS = []; const SETTINGS = [];
const SETTINGS_BY_KEY = {}; const SETTINGS_BY_KEY = {};
const DOMAIN_SETTINGS_BY_KEY = {};
const SYSTEM_SETTINGS_BY_KEY = {}; const SYSTEM_SETTINGS_BY_KEY = {};
const Setting = ( const Setting = (
@@ -39,6 +41,12 @@ const AccountSetting = (...settings) => {
SETTINGS_BY_KEY[setting.key] = setting; SETTINGS_BY_KEY[setting.key] = setting;
} }
}; };
const DomainSetting = (...settings) => {
for (const setting of settings) {
DOMAIN_SETTINGS.push(setting);
DOMAIN_SETTINGS_BY_KEY[setting.key] = setting;
}
};
const SystemSetting = (...settings) => { const SystemSetting = (...settings) => {
for (const setting of settings) { for (const setting of settings) {
SYSTEM_SETTINGS.push(setting); SYSTEM_SETTINGS.push(setting);
@@ -47,8 +55,9 @@ const SystemSetting = (...settings) => {
}; };
PreferenceSetting( PreferenceSetting(
// TODO generate by global.Hydro.locales
Setting('setting_display', 'viewLang', builtin.VIEW_LANGS.map((i) => [i.code, i.name]), Setting('setting_display', 'viewLang', builtin.VIEW_LANGS.map((i) => [i.code, i.name]),
options.default_locale, 'select', 'UI Language'), 'zh_CN', 'select', 'UI Language'),
Setting('setting_display', 'timezone', timezones, Setting('setting_display', 'timezone', timezones,
'Asia/Shanghai', 'select', 'Timezone'), 'Asia/Shanghai', 'select', 'Timezone'),
Setting('setting_usage', 'codeLang', builtin.LANG_TEXTS, Setting('setting_usage', 'codeLang', builtin.LANG_TEXTS,
@@ -73,6 +82,12 @@ AccountSetting(
'Choose the background image in your profile page.'), 'Choose the background image in your profile page.'),
); );
DomainSetting(
Setting('setting_domain', 'name', null, 'New domain', 'text', 'name'),
Setting('setting_domain', 'gravatar', null, '', 'text', 'gravatar', 'Will be used as the domain icon.'),
Setting('setting_domain', 'bulletin', null, '', 'markdown', 'Bulletin'),
);
SystemSetting( SystemSetting(
Setting('setting_basic', 'user.register', null, false, 'checkbox', 'Allow register'), Setting('setting_basic', 'user.register', null, false, 'checkbox', 'Allow register'),
Setting('setting_basic', 'user.create_domain', null, true, 'checkbox', 'Allow domain creation'), Setting('setting_basic', 'user.create_domain', null, true, 'checkbox', 'Allow domain creation'),
@@ -87,6 +102,8 @@ SystemSetting(
Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'), Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'),
Setting('setting_server', 'server.url', null, null, 'text', 'Server BaseURL'), Setting('setting_server', 'server.url', null, null, 'text', 'Server BaseURL'),
Setting('setting_server', 'server.port', null, 8888, 'number', 'Server Port'), Setting('setting_server', 'server.port', null, 8888, 'number', 'Server Port'),
Setting('setting_oauth', 'oauth.githubappid', null, null, 'text', 'Github Oauth AppID'),
Setting('setting_oauth', 'oauth.githubsecret', null, null, 'text', 'Github Oauth Secret'),
Setting('setting_constant', 'PROBLEM_PER_PAGE', null, 100, 'number', 'Problems per Page'), Setting('setting_constant', 'PROBLEM_PER_PAGE', null, 100, 'number', 'Problems per Page'),
Setting('setting_constant', 'CONTEST_PER_PAGE', null, 20, 'number', 'Contests per Page'), Setting('setting_constant', 'CONTEST_PER_PAGE', null, 20, 'number', 'Contests per Page'),
Setting('setting_constant', 'DISCUSSION_PER_PAGE', null, 50, 'number', 'Discussion per Page'), Setting('setting_constant', 'DISCUSSION_PER_PAGE', null, 50, 'number', 'Discussion per Page'),
@@ -100,6 +117,7 @@ global.Hydro.model.setting = module.exports = {
Setting, Setting,
PreferenceSetting, PreferenceSetting,
AccountSetting, AccountSetting,
DomainSetting,
SystemSetting, SystemSetting,
PREFERENCE_SETTINGS, PREFERENCE_SETTINGS,
ACCOUNT_SETTINGS, ACCOUNT_SETTINGS,
@@ -107,4 +125,6 @@ global.Hydro.model.setting = module.exports = {
SETTINGS_BY_KEY, SETTINGS_BY_KEY,
SYSTEM_SETTINGS, SYSTEM_SETTINGS,
SYSTEM_SETTINGS_BY_KEY, SYSTEM_SETTINGS_BY_KEY,
DOMAIN_SETTINGS,
DOMAIN_SETTINGS_BY_KEY,
}; };
+1
View File
@@ -97,6 +97,7 @@ global.Hydro.model.token = module.exports = {
TYPE_CSRF_TOKEN: 1, TYPE_CSRF_TOKEN: 1,
TYPE_REGISTER: 2, TYPE_REGISTER: 2,
TYPE_CHANGEMAIL: 3, TYPE_CHANGEMAIL: 3,
TYPE_OAUTH: 4,
ensureIndexes, ensureIndexes,
add, add,
+27 -17
View File
@@ -2,6 +2,7 @@ const builtin = require('./builtin');
const document = require('./document'); const document = require('./document');
const system = require('./system'); const system = require('./system');
const token = require('./token'); const token = require('./token');
const setting = require('./setting');
const { UserNotFoundError, UserAlreadyExistError, LoginError } = require('../error'); const { UserNotFoundError, UserAlreadyExistError, LoginError } = require('../error');
const perm = require('../permission'); const perm = require('../permission');
const pwhash = require('../lib/hash.hydro'); const pwhash = require('../lib/hash.hydro');
@@ -19,14 +20,16 @@ class USER {
this.salt = () => udoc.salt; this.salt = () => udoc.salt;
this.hash = () => udoc.hash; this.hash = () => udoc.hash;
this.priv = udoc.priv; this.priv = udoc.priv;
this.timeZone = udoc.timeZone || 'Asia/Shanghai';
this.viewLang = udoc.viewLang || 'zh_CN'; for (const key in setting.SETTINGS_BY_KEY) {
this.codeLang = udoc.codeLang || 'c'; if (udoc[key]) this[key] = udoc[key];
this.codeTemplate = udoc.codeTemplate || ''; else if (setting.SETTINGS_BY_KEY[key].value) {
this[key] = setting.SETTINGS_BY_KEY[key].value;
}
}
this.regat = udoc.regat; this.regat = udoc.regat;
this.loginat = udoc.loginat; this.loginat = udoc.loginat;
this.bio = udoc.bio || '';
this.gravatar = udoc.gravatar || '';
this.ban = udoc.ban || false; this.ban = udoc.ban || false;
this.nAccept = dudoc.nAccept || 0; this.nAccept = dudoc.nAccept || 0;
this.nSubmit = dudoc.nSubmit || 0; this.nSubmit = dudoc.nSubmit || 0;
@@ -76,10 +79,13 @@ async function getList(domainId, uids) {
return r; return r;
} }
async function getByUname(domainId, uname) { async function getByUname(domainId, uname, ignoreMissing = false) {
const unameLower = uname.trim().toLowerCase(); const unameLower = uname.trim().toLowerCase();
const udoc = await coll.findOne({ unameLower }); const udoc = await coll.findOne({ unameLower });
if (!udoc) throw new UserNotFoundError(uname); if (!udoc) {
if (ignoreMissing) return null;
throw new UserNotFoundError(uname);
}
const dudoc = await getInDomain(domainId, udoc); const dudoc = await getInDomain(domainId, udoc);
return new USER(udoc, dudoc); return new USER(udoc, dudoc);
} }
@@ -97,9 +103,10 @@ async function getByEmail(domainId, mail, ignoreMissing = false) {
function setPassword(uid, password) { function setPassword(uid, password) {
const salt = String.random(); const salt = String.random();
return coll.findOneAndUpdate({ _id: uid }, { return coll.findOneAndUpdate(
$set: { salt, hash: pwhash(password, salt), hashType: 'hydro' }, { _id: uid },
}); { $set: { salt, hash: pwhash(password, salt), hashType: 'hydro' } },
);
} }
function setById(uid, args) { function setById(uid, args) {
@@ -114,11 +121,10 @@ async function changePassword(uid, currentPassword, newPassword) {
const udoc = await getById(uid); const udoc = await getById(uid);
udoc.checkPassword(currentPassword); udoc.checkPassword(currentPassword);
const salt = String.random(); const salt = String.random();
return await coll.findOneAndUpdate({ return await coll.findOneAndUpdate(
_id: udoc._id, { _id: udoc._id },
}, { { $set: { salt, hash: pwhash(newPassword, salt), hashType: 'hydro' } },
$set: { salt, hash: pwhash.hash(newPassword, salt), hashType: 'hydro' }, );
});
} }
async function inc(_id, field, n = 1) { async function inc(_id, field, n = 1) {
@@ -139,11 +145,14 @@ async function incDomain(domainId, uid, field, n = 1) {
return dudoc; return dudoc;
} }
/**
* @returns {Promise<number>} uid
*/
async function create({ async function create({
uid, mail, uname, password, regip = '127.0.0.1', priv = perm.PRIV_NONE, uid, mail, uname, password, regip = '127.0.0.1', priv = perm.PRIV_NONE,
}) { }) {
const salt = String.random(); const salt = String.random();
if (!uid) uid = system.inc('user'); if (!uid) uid = await system.inc('user');
try { try {
await coll.insertOne({ await coll.insertOne({
_id: uid, _id: uid,
@@ -164,6 +173,7 @@ async function create({
} catch (e) { } catch (e) {
throw new UserAlreadyExistError([uid, uname, mail]); throw new UserAlreadyExistError([uid, uname, mail]);
} }
return uid;
} }
function getMulti(params) { function getMulti(params) {
+3 -2
View File
@@ -7,9 +7,10 @@ async function update() {
const [mid, $set] = await sysinfo.update(); const [mid, $set] = await sysinfo.update();
await coll.updateOne( await coll.updateOne(
{ mid, type: 'server' }, { mid, type: 'server' },
{ $set: { ...$set, updateAt: new Date() } }, { $set: { ...$set, updateAt: new Date(), reqCount: global.Hydro.stat.reqCount } },
{ upsert: true }, { upsert: true },
); );
global.Hydro.stat.reqCount = 0;
} }
async function postInit() { async function postInit() {
@@ -19,7 +20,7 @@ async function postInit() {
{ $set: { ...info, updateAt: new Date(), type: 'server' } }, { $set: { ...info, updateAt: new Date(), type: 'server' } },
{ upsert: true }, { upsert: true },
); );
setInterval(update, 3 * 60 * 1000); setInterval(update, 60 * 1000);
} }
global.Hydro.service.monitor = module.exports = { postInit }; global.Hydro.service.monitor = module.exports = { postInit };
+2
View File
@@ -133,6 +133,7 @@ class Handler {
files: ctx.request.files, files: ctx.request.files,
query: ctx.query, query: ctx.query,
path: ctx.path, path: ctx.path,
referer: ctx.request.headers.referer || '/',
}; };
this.response = { this.response = {
body: '', body: '',
@@ -348,6 +349,7 @@ class Handler {
} }
async function handle(ctx, HandlerClass, permission) { async function handle(ctx, HandlerClass, permission) {
global.Hydro.stat.reqCount++;
const h = new HandlerClass(ctx); const h = new HandlerClass(ctx);
try { try {
const method = ctx.method.toLowerCase(); const method = ctx.method.toLowerCase();
+61 -24
View File
@@ -1,9 +1,12 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
const fs = require('fs');
const os = require('os');
const { mongodb } = global.nodeModules; const { mongodb } = global.nodeModules;
const dst = global.Hydro.service.db; const dst = global.Hydro.service.db;
const { file, problem, discussion } = global.Hydro.model; const { file, problem, discussion } = global.Hydro.model;
const { readConfig } = global.Hydro.lib;
// TODO read config file
// TODO output enhancement // TODO output enhancement
const map = {}; const map = {};
@@ -23,8 +26,8 @@ const tasks = {
hashType: doc.hash.split('|')[0] === 'vj4' ? 'hydro' : doc.hash.split('|')[0], hashType: doc.hash.split('|')[0] === 'vj4' ? 'hydro' : doc.hash.split('|')[0],
priv: doc.priv === -1 ? 1 : 0, priv: doc.priv === -1 ? 1 : 0,
gravatar: doc.gravatar, gravatar: doc.gravatar,
email: doc.mail, mail: doc.mail,
emailLower: doc.mail_lower, mailLower: doc.mail_lower,
regat: doc.regat, regat: doc.regat,
regip: doc.regip, regip: doc.regip,
loginat: doc.loginat, loginat: doc.loginat,
@@ -44,6 +47,7 @@ const tasks = {
num_submit: 'nSubmit', num_submit: 'nSubmit',
num_accept: 'nAccept', num_accept: 'nAccept',
difficulty: 'difficulty', difficulty: 'difficulty',
difficulty_admin: null,
pname: 'pid', pname: 'pid',
title: 'title', title: 'title',
content: 'content', content: 'content',
@@ -153,6 +157,7 @@ const tasks = {
field: 'donePids', field: 'donePids',
processer: (pids) => pids.map((id) => pid(id)), processer: (pids) => pids.map((id) => pid(id)),
}, },
rp: null,
}, },
record: async (doc) => { record: async (doc) => {
const testCases = []; const testCases = [];
@@ -297,7 +302,54 @@ async function domainUser(src, report) {
} }
} }
async function fixPid(report) { async function fix(doc) {
await dst.collection('document').updateOne(
{ _id: doc._id },
{ $set: { pid: doc.pid || doc.docId.toString() } },
);
if (doc.data && doc.data.generationTime) {
await file.inc(doc.data);
const r = await file.get(doc.data);
const p = `${os.tmpdir()}/hydro/migrate.vijos.${doc._id}.zip`;
const w = fs.createWriteStream(p);
await new Promise((resolve, reject) => {
w.on('finish', resolve);
w.on('error', reject);
r.pipe(w);
});
const config = await readConfig(p);
await dst.collection('document').updateOne(
{ _id: doc._id },
{ $set: { config } },
);
fs.unlinkSync(p);
} else if (doc.data) {
const pdoc = await problem.get(doc.data.domain, pid(doc.data.pid));
await Promise.all([
file.inc(pdoc.data),
dst.collection('document').updateOne(
{ _id: doc._id },
{ $set: { data: pdoc.data } },
),
]);
const r = await file.get(pdoc.data);
const p = `${os.tmpdir()}/hydro/migrate.vijos.${doc._id}.zip`;
const w = fs.createWriteStream(p);
await new Promise((resolve, reject) => {
w.on('finish', resolve);
w.on('error', reject);
r.pipe(w);
});
const config = await readConfig(p);
await dst.collection('document').updateOne(
{ _id: doc._id },
{ $set: { config } },
);
fs.unlinkSync(p);
}
}
async function fixProblem(report) {
const count = await dst.collection('document').find({ docType: 10 }).count(); const count = await dst.collection('document').find({ docType: 10 }).count();
await report({ progress: 1, message: `Fix pid: ${count}` }); await report({ progress: 1, message: `Fix pid: ${count}` });
const total = Math.floor(count / 50); const total = Math.floor(count / 50);
@@ -305,28 +357,13 @@ async function fixPid(report) {
const docs = await dst.collection('document') const docs = await dst.collection('document')
.find({ docType: 10 }).skip(i * 50).limit(50) .find({ docType: 10 }).skip(i * 50).limit(50)
.toArray(); .toArray();
const t = [];
for (const doc of docs) { for (const doc of docs) {
t.push( try {
dst.collection('document').updateOne( await fix(doc);
{ _id: doc._id }, } catch (e) {
{ $set: { pid: doc.pid || doc.docId.toString() } }, await report({ message: e.toString() });
),
);
if (doc.data && doc.data.generationTime) {
t.push(file.inc(doc.data));
} else if (doc.data) {
const pdoc = await problem.get(doc.data.domain, pid(doc.data.pid));
t.push(
file.inc(pdoc.data),
dst.collection('document').updateOne(
{ _id: doc._id },
{ $set: { data: pdoc.data } },
),
);
} }
} }
await Promise.all(t);
await report({ progress: Math.round(100 * ((i + 1) / (total + 1))) }); await report({ progress: Math.round(100 * ((i + 1) / (total + 1))) });
} }
} }
@@ -437,7 +474,7 @@ async function migrateVijos({
await dst.collection('user').deleteMany({ _id: { $nin: [0, 1] } }); await dst.collection('user').deleteMany({ _id: { $nin: [0, 1] } });
const d = ['user', 'document', 'document.status', 'record', 'file']; const d = ['user', 'document', 'document.status', 'record', 'file'];
for (const i of d) await task(i, src, report); for (const i of d) await task(i, src, report);
await fixPid(report); await fixProblem(report);
await discussionNode(src, report); await discussionNode(src, report);
await domainUser(src, report); await domainUser(src, report);
return true; return true;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.2.1", "version": "2.3.0",
"main": "hydro/loader.js", "main": "hydro/loader.js",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",
"author": "masnn", "author": "masnn",
+1 -37
View File
@@ -3,43 +3,7 @@
<div class="section"> <div class="section">
<div class="section__body"> <div class="section__body">
<form method="post"> <form method="post">
{{ form.form_text({ {% include "partials/setting.html" with context %}
label:'ID',
name:'id',
value:domain._id,
disabled:true,
required:true
}) }}
{{ form.form_text({
label:'Name',
name:'name',
value:domain.name,
autofocus:true,
required:true
}) }}
{{ form.form_text({
label:'Gravatar Email',
help_text:'Will be used as the system icon.',
name:'gravatar',
value:domain.gravatar
}) }}
{{ form.form_textarea({
columns:12,
label:'Bulletin',
name:'bulletin',
value:domain.bulletin,
markdown:true,
required:false
}) }}
<div class="row"><div class="columns">
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
<button type="submit" class="rounded primary button">
{{ _('Update') }}
</button>
<button type="button" class="rounded button" onclick="window.history.go(-1)">
{{ _('Cancel') }}
</button>
</div></div>
</form> </form>
</div> </div>
</div> </div>
+1 -1
View File
@@ -13,7 +13,7 @@
<div class="profile-header__content"> <div class="profile-header__content">
<div class="media"> <div class="media">
<div class="media__left"> <div class="media__left">
<img src="{{ gravatar(udoc.gravatar) }}" width="120" height="120" class="large user-profile-avatar"> <img src="{{ gravatar(udoc.gravatar, 120) }}" width="120" height="120" class="large user-profile-avatar">
</div> </div>
<div class="media__body profile-header__main"> <div class="media__body profile-header__main">
<h1> <h1>
+1 -1
View File
@@ -5098,7 +5098,7 @@ snyk-try-require@1.3.1, snyk-try-require@^1.1.1, snyk-try-require@^1.3.1:
lru-cache "^4.0.0" lru-cache "^4.0.0"
then-fs "^2.0.0" then-fs "^2.0.0"
snyk@1.346.0, snyk@^1.346.0: snyk@^1.346.0:
version "1.346.0" version "1.346.0"
resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.346.0.tgz#c94646a7ca109a329095419542df92d10df9709f" resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.346.0.tgz#c94646a7ca109a329095419542df92d10df9709f"
integrity sha512-uwJDC0CRefppngm1J1M+Sja5CVfdmzLYkxGYp3ZYXrzvd912pgzrdYm8ciUL5a8x1/C+2L0iZ09W68daSuObhA== integrity sha512-uwJDC0CRefppngm1J1M+Sja5CVfdmzLYkxGYp3ZYXrzvd912pgzrdYm8ciUL5a8x1/C+2L0iZ09W68daSuObhA==