优化domain.user存储结构

This commit is contained in:
undefined committed 2020-07-13 22:33:52 +08:00
1 parent 3eb593d134
commit a93da2badb
17 files changed
+220 -239

No files matched your search

+14 -43
View File
@@ -1,20 +1,18 @@
import { RoleAlreadyExistError, ValidationError, PermissionError } from '../error';
import { RoleAlreadyExistError, ValidationError } from '../error';
import * as user from '../model/user';
import * as domain from '../model/domain';
import * as system from '../model/system';
import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
import { PERM, PERMS_BY_FAMILY } from '../model/builtin';
import paginate from '../lib/paginate';
import {
Route, Handler, Types, param,
} from '../service/server';
import { isTitle, isUname } from '../lib/validator';
class DomainRankHandler extends Handler {
@param('page', Types.UnsignedInt, true)
async get(domainId: string, page = 1) {
const [dudocs, upcount, ucount] = await paginate(
user.getMultiInDomain(domainId).sort({ rating: -1 }),
domain.getMultiInDomain(domainId).sort({ rating: -1 }),
page,
100,
);
@@ -34,31 +32,6 @@ class DomainRankHandler extends Handler {
}
}
class DomainCreateHandler extends Handler {
async prepare() {
if (this.user.priv !== 1) {
if (!await system.get('user.create_domain')) throw new PermissionError('domain_create');
}
}
async get() {
const path = [
['Hydro', 'homepage'],
['domain_create', null],
];
this.response.body = { path };
this.response.template = 'domain_create.html';
}
@param('id', Types.String, isUname)
@param('name', Types.String, isTitle)
async post(domainId: string, id: string, name: string) {
await domain.add(id, this.user._id, name);
this.response.body = { domainId: id };
this.response.redirect = this.url('homepage', { domainId: id });
}
}
class ManageHandler extends Handler {
domain: any;
@@ -105,13 +78,13 @@ class DomainUserHandler extends ManageHandler {
async get({ domainId }) {
const rudocs = {};
const [dudocs, roles] = await Promise.all([
user.getMultiInDomain(domainId, {
domain.getMultiInDomain(domainId, {
$and: [
{ role: { $ne: 'default' } },
{ role: { $ne: null } },
],
}).toArray(),
user.getRoles(domainId),
domain.getRoles(domainId),
]);
const uids = dudocs.map((dudoc) => dudoc.uid);
const udict = await user.getList(domainId, uids);
@@ -133,14 +106,14 @@ class DomainUserHandler extends ManageHandler {
}
async postSetUser({ domainId, uid, role }) {
await user.setRole(domainId, uid, role);
await domain.setUserRole(domainId, uid, role);
this.back();
}
}
class DomainPermissionHandler extends ManageHandler {
async get({ domainId }) {
const roles = await user.getRoles(domainId);
const roles = await domain.getRoles(domainId);
const path = [
['Hydro', 'homepage'],
['domain', null],
@@ -159,14 +132,14 @@ class DomainPermissionHandler extends ManageHandler {
roles[role] = this.request.body[role].join('');
}
}
await user.setRoles(domainId, roles);
await domain.setRoles(domainId, roles);
this.back();
}
}
class DomainRoleHandler extends ManageHandler {
async get({ domainId }) {
const roles = await user.getRoles(domainId);
const roles = await domain.getRoles(domainId);
const path = [
['Hydro', 'homepage'],
['domain', null],
@@ -177,12 +150,11 @@ class DomainRoleHandler extends ManageHandler {
}
async postAdd({ domainId, role }) {
const [r, u] = await Promise.all([
user.getRole(domainId, role),
user.getRole(domainId, 'default'),
]);
if (r) throw new RoleAlreadyExistError(role);
await user.addRole(domainId, role, u.perm);
const roles = await domain.getRoles(this.domain);
const rdict: any = {};
for (const r of roles) rdict[r._id] = r.perm;
if (rdict[role]) throw new RoleAlreadyExistError(role);
await domain.addRole(domainId, role, rdict.default.perm);
this.back();
}
@@ -192,14 +164,13 @@ class DomainRoleHandler extends ManageHandler {
throw new ValidationError('role');
}
}
await user.deleteRoles(domainId, roles);
await domain.deleteRoles(domainId, roles);
this.back();
}
}
export async function apply() {
Route('ranking', '/ranking', DomainRankHandler);
Route('domain_create', '/domain/create', DomainCreateHandler);
Route('domain_dashboard', '/domain/dashboard', DomainDashboardHandler);
Route('domain_edit', '/domain/edit', DomainEditHandler);
Route('domain_user', '/domain/user', DomainUserHandler);
+43 -3
View File
@@ -24,7 +24,9 @@ import * as discussion from '../model/discussion';
import * as token from '../model/token';
import * as training from '../model/training';
import { PERM, PRIV } from '../model/builtin';
import { isContent, isPassword, isEmail } from '../lib/validator';
import {
isContent, isPassword, isEmail, isTitle,
} from '../lib/validator';
const { geoip, useragent } = global.Hydro.lib;
@@ -133,7 +135,7 @@ class HomeSecurityHandler extends Handler {
['home_security', null],
];
this.response.template = 'home_security.html';
this.response.body = { sessions, geoipProvider: (geoip || {}).provider, path };
this.response.body = { sessions, geoipProvider: geoip?.provider, path };
if (useragent) this.response.body.icon = useragent.icon;
}
@@ -210,7 +212,8 @@ class HomeSettingsHandler extends Handler {
async post(args: any) {
const $set = {};
for (const key in args) {
if (setting.SETTINGS_BY_KEY[key] && !setting.SETTINGS_BY_KEY[key].disabled) {
if (setting.SETTINGS_BY_KEY[key]
&& !(setting.SETTINGS_BY_KEY[key].flag & setting.FLAG_DISABLED)) {
$set[key] = args[key];
}
}
@@ -236,6 +239,41 @@ class UserChangemailWithCodeHandler extends Handler {
}
}
class HomeDomainHandler extends Handler {
async get() {
const dudict = await domain.getDictUserByDomainId(this.user._id);
const dids = Object.keys(dudict);
const ddocs = await domain.getMulti({ _id: { $in: dids } }).toArray();
const canManage = {};
for (const ddoc of ddocs) {
// eslint-disable-next-line no-await-in-loop
const udoc = await user.getById(ddoc._id, this.user._id);
canManage[ddoc._id] = udoc.hasPerm(PERM.PERM_EDIT_DOMAIN)
|| udoc.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN);
}
this.response.template = 'home_domain.html';
this.response.body = { ddocs, dudict, canManage };
}
}
class HomeDomainCreateHandler extends Handler {
async get() {
this.response.body = 'home_domain_create.html';
}
@param('id', Types.String)
@param('name', Types.String, isTitle)
@param('bulletin', Types.String, isContent)
@param('gravatar', Types.String, isEmail, true)
async post(_: string, id: string, name: string, bulletin: string, gravatar: string) {
gravatar = gravatar || this.user.gravatar || this.user.mail || 'guest@hydro.local';
const domainId = await domain.add(id, this.user._id, name, bulletin);
await domain.edit(domainId, { gravatar });
this.response.redirect = this.url('domain_manage', { domainId });
this.response.body = { domainId };
}
}
class HomeMessagesHandler extends Handler {
async get() {
// TODO(iceboy): projection, pagination.
@@ -324,6 +362,8 @@ async function apply() {
Route('home_security', '/home/security', HomeSecurityHandler, PRIV.PRIV_USER_PROFILE);
Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PRIV.PRIV_USER_PROFILE);
Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PRIV.PRIV_USER_PROFILE);
Route('home_domain', '/home/domain', HomeDomainHandler, PRIV.PRIV_USER_PROFILE);
Route('home_domain_create', '/home/domain/create', HomeDomainCreateHandler, PRIV.PRIV_CREATE_DOMAIN);
Route('home_messages', '/home/messages', HomeMessagesHandler, PRIV.PRIV_USER_PROFILE);
Route('home_file', '/home/file', HomeFileHandler, PRIV.PRIV_USER_PROFILE);
Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler, PRIV.PRIV_USER_PROFILE);
+2 -2
View File
@@ -5,7 +5,7 @@ import * as record from '../model/record';
import * as problem from '../model/problem';
import * as builtin from '../model/builtin';
import * as contest from '../model/contest';
import * as user from '../model/user';
import * as domain from '../model/domain';
import * as task from '../model/task';
import * as bus from '../service/bus';
import {
@@ -29,7 +29,7 @@ async function _postJudge(rdoc) {
if (accept && !rdoc.rejudged) {
tasks.push(
problem.inc(rdoc.domainId, rdoc.pid, 'nAccept', 1),
user.incDomain(rdoc.domainId, rdoc.uid, 'nAccept', 1),
domain.incUserInDomain(rdoc.domainId, rdoc.uid, 'nAccept', 1),
);
}
}
+2 -1
View File
@@ -7,6 +7,7 @@ import {
import * as file from '../model/file';
import * as problem from '../model/problem';
import * as record from '../model/record';
import * as domain from '../model/domain';
import * as user from '../model/user';
import * as solution from '../model/solution';
import * as system from '../model/system';
@@ -219,7 +220,7 @@ class ProblemSubmitHandler extends ProblemDetailHandler {
const rid = await record.add(domainId, {
uid: this.user._id, lang, code, pid: this.pdoc.docId,
}, true);
await user.incDomain(domainId, this.user._id, 'nSubmit');
await domain.incUserInDomain(domainId, this.user._id, 'nSubmit');
this.response.body = { rid };
this.response.redirect = this.url('record_detail', { rid });
}
+1 -1
View File
@@ -36,7 +36,7 @@ String.prototype.rawformat = function rawFormat(object) {
};
String.prototype.translate = function translate(...languages: string[]) {
if (languages.length == 0) languages = ['zh_CN'];
if (languages.length === 0) languages = ['zh_CN'];
for (const language of languages) {
if (locales[language]) {
if (locales[language][this] !== undefined) return locales[language][this];
+4 -4
View File
@@ -85,7 +85,7 @@ export const PERM = {
PERM_ADMIN: '',
};
export const Permission = (family, key, desc) => ({ family, key, desc });
export const Permission = (family: string, key: string, desc: string) => ({ family, key, desc });
export const PERMS = [
Permission('perm_general', PERM.PERM_VIEW, 'View this domain'),
@@ -298,9 +298,9 @@ export const BUILTIN_USERS = [
];
export const BUILTIN_ROLES = {
guest: { perm: PERM.PERM_BASIC },
default: { perm: PERM.PERM_DEFAULT },
admin: { perm: PERM.PERM_ADMIN },
guest: PERM.PERM_BASIC,
default: PERM.PERM_DEFAULT,
admin: PERM.PERM_ADMIN,
};
export const DEFAULT_NODES = {
+7 -4
View File
@@ -7,7 +7,6 @@ type DocID = ObjectID | string | number;
const coll = db.collection('document');
const collStatus = db.collection('document.status');
export const TYPE_DOMAIN_USER = 0;
export const TYPE_PROBLEM = 10;
export const TYPE_PROBLEM_SOLUTION = 11;
export const TYPE_PROBLEM_LIST = 12;
@@ -52,14 +51,14 @@ export async function set(domainId: string, docType: number, docId: DocID, args:
const res = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{ $set: args },
{ returnOriginal: false },
{ returnOriginal: false, upsert: true },
);
return res.value;
}
export function deleteOne(domainId: string, docType: number, docId: DocID) {
return Promise.all([
coll.deleteMany({ domainId, docType, docId }),
collStatus.deleteMany({ domainId, docType, docId }),
coll.deleteOne({ domainId, docType, docId }),
]);
}
@@ -207,6 +206,10 @@ export function getMultiStatus(domainId: string, docType: number, args: any) {
return collStatus.find({ ...args, docType, domainId });
}
export function getMultiStatusWithoutDomain(docType: number, args: any) {
return collStatus.find({ ...args, docType });
}
export async function setStatus(
domainId: string, docType: number, docId: DocID, uid: number, args: any,
) {
@@ -388,6 +391,7 @@ global.Hydro.model.document = {
get,
getMulti,
getMultiStatus,
getMultiStatusWithoutDomain,
getStatus,
getSub,
inc,
@@ -404,7 +408,6 @@ global.Hydro.model.document = {
setMultiStatus,
setSub,
TYPE_DOMAIN_USER,
TYPE_CONTEST,
TYPE_DISCUSSION,
TYPE_DISCUSSION_NODE,
+110 -32
View File
@@ -1,39 +1,18 @@
import * as builtin from './builtin';
import * as document from './document';
import { BUILTIN_ROLES, PRIV } from './builtin';
import { Udoc } from '../interface';
import * as db from '../service/db';
import * as validator from '../lib/validator';
const coll = db.collection('domain');
const collUser = db.collection('domain.user');
export function add(domainId: string, owner: number, name: string, isEnsure = false) {
const tasks = [];
if (isEnsure) {
tasks.push(
coll.updateOne(
{ _id: domainId },
{ $set: { owner }, $setOnInsert: { name, bulletin: '' } },
{ upsert: true },
),
);
} else {
tasks.push(
coll.insertOne({
_id: domainId, owner, name, bulletin: '',
}),
);
}
for (const id in builtin.BUILTIN_ROLES) {
tasks.push(
document.add(
domainId, builtin.BUILTIN_ROLES[id].perm, owner,
document.TYPE_DOMAIN_USER, id,
),
);
}
return Promise.all(tasks);
export async function add(domainId: string, owner: number, name: string, bulletin: string) {
await coll.insertOne({
_id: domainId, owner, name, bulletin, roles: BUILTIN_ROLES,
});
return domainId;
}
export async function get(domainId: string) {
export function get(domainId: string) {
return coll.findOne({ _id: domainId });
}
@@ -47,8 +26,6 @@ export function getMulti(query: any = {}) {
}
export function edit(domainId: string, $set: any) {
if ($set.title) validator.checkTitle($set.title);
if ($set.content) validator.checkContent($set.content);
return coll.updateOne({ _id: domainId }, { $set });
}
@@ -68,7 +45,98 @@ export async function getList(domainIds: string[]) {
return r;
}
export function setUserRole(domainId: string, uid: number, role: string) {
return collUser.updateOne({ uid, domainId }, { role }, { upsert: true });
}
export async function getRoles(arg: string | any) {
let ddoc;
if (typeof arg === 'string') ddoc = get(arg);
else ddoc = arg;
const roles = [];
const r = [];
for (const role in ddoc.roles) {
roles.push({ _id: role, perm: ddoc.roles[role] });
r.push(role);
}
for (const role in BUILTIN_ROLES) {
if (!r.includes(role)) {
roles.push({ _id: role, perm: BUILTIN_ROLES[role] });
}
}
return roles;
}
export async function setRoles(domainId: string, roles: any) {
const current = await get(domainId);
for (const role in roles) {
current.roles[role] = roles[role];
}
return await coll.updateOne({ _id: domainId }, { $set: { roles: current } });
}
export async function addRole(domainId: string, name: string, permission: string) {
const current = await get(domainId);
current.roles[name] = permission;
return await coll.updateOne({ _id: domainId }, { $set: { roles: current } });
}
export async function deleteRoles(domainId: string, roles: string[]) {
const current = await get(domainId);
for (const role of roles) delete current.roles[role];
await Promise.all([
coll.updateOne({ _id: domainId }, { $set: current }),
collUser.updateMany({ domainId, role: { $in: roles } }, { $set: { role: 'default' } }),
]);
}
export async function getDomainUser(domainId: string, udoc: Udoc) {
let dudoc = await collUser.findOne({ domainId, uid: udoc._id });
dudoc = dudoc || {};
if (udoc._id === 1) dudoc.role = 'guest';
if (udoc.priv & PRIV.PRIV_MANAGE_ALL_DOMAIN) dudoc.role = 'admin';
dudoc.role = dudoc.role || 'default';
const ddoc = await get(domainId);
dudoc.perm = ddoc.roles[dudoc.role] || BUILTIN_ROLES[dudoc.role];
return dudoc;
}
export function setMultiUserInDomain(domainId: string, query: any, params: any) {
return collUser.updateMany({ domainId, ...query }, { $set: params });
}
export function getMultiInDomain(domainId: string, query: any = {}) {
return collUser.find({ domainId, ...query });
}
export function setUserInDomain(domainId: string, uid: number, params: any) {
return collUser.updateOne({ domainId, uid }, { $set: params });
}
export async function incUserInDomain(domainId: string, uid: number, field: string, n = 1) {
// @ts-ignore
const dudoc = await getDomainUser(domainId, { _id: uid });
dudoc[field] = dudoc[field] + n || n;
await setUserInDomain(domainId, uid, { [field]: dudoc[field] });
return dudoc;
}
export async function getDictUserByDomainId(uid: number) {
const dudocs = await collUser.find({ uid }).toArray();
const ddocs = await coll.find({ owner: uid }).toArray();
const dudict = {};
for (const dudoc of dudocs) {
// eslint-disable-next-line no-await-in-loop
dudict[dudoc.domainId] = await get(dudoc.domainId);
}
for (const ddoc of ddocs) {
dudict[ddoc._id] = ddoc;
}
return dudict;
}
global.Hydro.model.domain = {
getRoles,
add,
inc,
get,
@@ -76,4 +144,14 @@ global.Hydro.model.domain = {
edit,
getMulti,
getList,
setRoles,
addRole,
deleteRoles,
setUserRole,
getDomainUser,
setMultiUserInDomain,
setUserInDomain,
incUserInDomain,
getMultiInDomain,
getDictUserByDomainId,
};
+23 -12
View File
@@ -1,7 +1,9 @@
/* eslint-disable no-await-in-loop */
import moment from 'moment-timezone';
import * as builtin from './builtin';
import { Setting as _Setting } from '../interface';
import { Setting as _Setting, Dict } from '../interface';
type SettingDict = Dict<_Setting>;
const countries = moment.tz.countries();
const tzs: Set<string> = new Set();
@@ -10,6 +12,11 @@ for (const country of countries) {
for (const t of tz) tzs.add(t);
}
const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]);
const langRange: Dict<string> = {};
for (const lang in global.Hydro.locales) {
langRange[lang] = global.Hydro.locales[lang].__langname;
}
export const FLAG_HIDDEN = 1;
export const FLAG_DISABLED = 2;
@@ -21,13 +28,13 @@ export const DOMAIN_SETTINGS: _Setting[] = [];
export const DOMAIN_USER_SETTINGS: _Setting[] = [];
export const SYSTEM_SETTINGS: _Setting[] = [];
export const SETTINGS: _Setting[] = [];
export const SETTINGS_BY_KEY = {};
export const DOMAIN_USER_SETTINGS_BY_KEY = {};
export const DOMAIN_SETTINGS_BY_KEY = {};
export const SYSTEM_SETTINGS_BY_KEY = {};
export const SETTINGS_BY_KEY: SettingDict = {};
export const DOMAIN_USER_SETTINGS_BY_KEY: SettingDict = {};
export const DOMAIN_SETTINGS_BY_KEY: SettingDict = {};
export const SYSTEM_SETTINGS_BY_KEY: SettingDict = {};
export const Setting = (
family: string, key: string, range: Array<[string, string]> | { [key: string]: string } = null,
family: string, key: string, range: Array<[string, string]> | Dict<string> = null,
value: any = null, type = 'text', name = '',
desc = '', flag = 0,
): _Setting => ({
@@ -68,8 +75,7 @@ export const SystemSetting = (...settings: _Setting[]) => {
};
PreferenceSetting(
// TODO generate by global.Hydro.locales
Setting('setting_display', 'viewLang', builtin.VIEW_LANGS.map((i) => [i.code, i.name]),
Setting('setting_display', 'viewLang', langRange,
'zh_CN', 'select', 'UI Language'),
Setting('setting_display', 'timeZone', timezones as [string, string][],
'Asia/Shanghai', 'select', 'Timezone'),
@@ -99,10 +105,10 @@ 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'),
Setting('storage', 'nAccept', null, 0, 'number', 'nAccept', null, FLAG_HIDDEN & FLAG_DISABLED),
Setting('storage', 'nSubmit', null, 0, 'number', 'nSubmit', null, FLAG_HIDDEN & FLAG_DISABLED),
Setting('storage', 'nLike', null, 0, 'number', 'nLike', null, FLAG_HIDDEN & FLAG_DISABLED),
Setting('storage', 'rating', null, 1500, 'number', 'rating', null, FLAG_HIDDEN & FLAG_DISABLED),
Setting('storage', 'nAccept', null, 0, 'number', 'nAccept', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('storage', 'nSubmit', null, 0, 'number', 'nSubmit', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('storage', 'nLike', null, 0, 'number', 'nLike', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('storage', 'rating', null, 1500, 'number', 'rating', null, FLAG_HIDDEN | FLAG_DISABLED),
);
SystemSetting(
@@ -112,6 +118,7 @@ SystemSetting(
Setting('setting_smtp', 'smtp.port', null, 465, 'number', 'SMTP Server Port'),
Setting('setting_smtp', 'smtp.from', null, null, 'text', 'Mail From'),
Setting('setting_smtp', 'smtp.secure', null, false, 'boolean', 'SSL'),
Setting('setting_db', 'db.ver', null, 1, 'number', 'Database version', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_server', 'server.worker', null, 1, 'number', 'Server Workers Number'),
Setting('setting_server', 'server.hostname', null, null, 'text', 'Server Hostname'),
Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'),
@@ -119,6 +126,10 @@ SystemSetting(
Setting('setting_server', 'server.port', null, 8888, 'number', 'Server Port'),
Setting('setting_server', 'server.xff', null, null, 'text', 'IP Header'),
Setting('setting_server', 'server.log', null, false, 'boolean', 'Disable Access Log'),
Setting('setting_session', 'session.keys', null, [String.random(32)], 'text', 'session.keys', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_session', 'session.secure', null, false, 'boolean', 'session.secure'),
Setting('setting_session', 'session.saved_expire_seconds', null, 3600 * 24 * 30, 'number', 'Saved session expire seconds'),
Setting('setting_session', 'session.unsaved_expire_seconds', null, 3600 * 3, 'number', 'Unsaved session expire seconds'),
Setting('setting_ui', 'ui.header_logo', null, '/components/header/header-logo.png', 'text', 'Header Logo'),
Setting('setting_ui', 'ui.header_logo_2x', null, '/components/header/header-logo@2x.png', 'text', 'Header Logo@2x'),
Setting('setting_ui', 'ui.header_background', null, '/components/header/header-background.png', 'text', 'Header Background'),
+5 -91
View File
@@ -1,8 +1,8 @@
import * as document from './document';
import * as system from './system';
import * as token from './token';
import * as setting from './setting';
import { BUILTIN_ROLES, BUILTIN_USERS, PRIV } from './builtin';
import * as domain from './domain';
import { BUILTIN_USERS, PRIV } from './builtin';
import { UserNotFoundError, UserAlreadyExistError, LoginError } from '../error';
import pwhash from '../lib/hash.hydro';
import * as db from '../service/db';
@@ -100,22 +100,12 @@ export class User implements Udoc {
}
}
export async function getInDomain(domainId: string, udoc: User) {
let dudoc = await document.getStatus(domainId, document.TYPE_DOMAIN_USER, 0, udoc._id);
dudoc = dudoc || {};
if (udoc._id === 1) dudoc.role = 'guest';
if (udoc.priv & PRIV.PRIV_MANAGE_ALL_DOMAIN) dudoc.role = 'admin';
const p = await document.get(domainId, document.TYPE_DOMAIN_USER, dudoc.role || 'default');
dudoc.perm = p ? p.content : BUILTIN_ROLES[dudoc.role || 'default'].perm;
return dudoc;
}
export async function getById(domainId: string, _id: number): Promise<Udoc | null> {
const udoc = _id === 0 || _id === 1
? BUILTIN_USERS[_id]
: await coll.findOne({ _id });
if (!udoc) return null;
const dudoc = await getInDomain(domainId, udoc);
const dudoc = await domain.getDomainUser(domainId, udoc);
return new User(udoc, dudoc);
}
@@ -140,7 +130,7 @@ export async function getByUname(
if (ignoreMissing) return null;
throw new UserNotFoundError(uname);
}
const dudoc = await getInDomain(domainId, udoc);
const dudoc = await domain.getDomainUser(domainId, udoc);
return new User(udoc, dudoc);
}
@@ -152,7 +142,7 @@ export async function getByEmail(domainId: string, mail: string): Promise<Udoc |
? BUILTIN_USERS[1]
: await coll.findOne({ mailLower });
if (!udoc) return null;
const dudoc = await getInDomain(domainId, udoc);
const dudoc = await domain.getDomainUser(domainId, udoc);
return new User(udoc, dudoc);
}
@@ -186,18 +176,6 @@ export async function inc(_id: number, field: string, n = 1) {
return udoc;
}
export function setInDomain(domainId: string, uid: number, params: any) {
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, params);
}
export async function incDomain(domainId: string, uid: number, field: string, n = 1) {
// @ts-ignore
const dudoc = await getInDomain(domainId, { _id: uid });
dudoc[field] = dudoc[field] + n || n;
await setInDomain(domainId, uid, { [field]: dudoc[field] });
return dudoc;
}
export async function create({
uid = null, mail, uname, password, regip = '127.0.0.1', priv = PRIV.PRIV_DEFAULT,
}) {
@@ -230,10 +208,6 @@ export function getMulti(params) {
return coll.find(params);
}
export function setMultiInDomain(domainId: string, query: any, params: any) {
return document.setMultiStatus(domainId, document.TYPE_DOMAIN_USER, query, params);
}
export async function getPrefixList(prefix: string, limit = 50) {
prefix = prefix.toLowerCase();
const $regex = new RegExp(`\\A\\Q${prefix}\\E`, 'gmi');
@@ -245,55 +219,6 @@ export function setPriv(uid: number, priv: number) {
return coll.findOneAndUpdate({ _id: uid }, { $set: priv }, { returnOriginal: false });
}
export function setRole(domainId: string, uid: number, role: string) {
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, { role });
}
export function setRoles(domainId: string, roles: any) {
const tasks = [];
for (const role in roles) {
tasks.push(document.set(
domainId, document.TYPE_DOMAIN_USER, role, { content: roles[role] },
));
}
return Promise.all(tasks);
}
export async function getRoles(domainId: string) {
const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_USER).sort('_id', 1).toArray();
const roles = [];
const r = [];
for (const doc of docs) {
roles.push({ _id: doc.docId, perm: doc.content });
r.push(doc.docId);
}
for (const role in BUILTIN_ROLES) {
if (!r.includes(role)) {
roles.push({ _id: role, perm: BUILTIN_ROLES[role].perm });
}
}
return roles;
}
export function getRole(domainId: string, name: string) {
return document.get(domainId, document.TYPE_DOMAIN_USER, name);
}
export function getMultiInDomain(domainId: string, query: any = {}) {
return document.getMultiStatus(domainId, document.TYPE_DOMAIN_USER, query);
}
export function addRole(domainId: string, name: string, permission: string) {
return document.add(domainId, permission, 1, document.TYPE_DOMAIN_USER, name);
}
export function deleteRoles(domainId: string, roles) {
return Promise.all([
document.deleteMulti(domainId, document.TYPE_DOMAIN_USER, { docId: { $in: roles } }),
document.deleteMultiStatus(domainId, document.TYPE_DOMAIN_USER, { role: { $in: roles } }),
]);
}
export function ban(uid: number) {
return Promise.all([
setPriv(uid, PRIV.PRIV_NONE),
@@ -317,23 +242,12 @@ global.Hydro.model.user = {
getByUname,
getMulti,
inc,
incDomain,
setById,
setEmail,
setPassword,
setInDomain,
setMultiInDomain,
getMultiInDomain,
getPrefixList,
setPriv,
setRole,
setRoles,
getRole,
getList,
getRoles,
getInDomain,
addRole,
deleteRoles,
ban,
ensureIndexes,
};
+2 -2
View File
@@ -9,7 +9,7 @@ import { STATUS, PRIV } from '../model/builtin';
export const description = 'Delete a user';
const collDocument = db.collection('document');
const collStatus = db.collection('document.status');
const collDomainUser = db.collection('domain.user');
const collRecord = db.collection('record');
const $unset = {
@@ -75,7 +75,7 @@ export async function run({
}) {
await deleteRecord(uid);
await deleteDiscussion(uid);
await collStatus.deleteMany({ uid, docType: document.TYPE_DOMAIN_USER });
await collDomainUser.deleteMany({ uid });
const uname = String.random(32);
await user.setById(uid, {
uname,
+1 -37
View File
@@ -1,5 +1,4 @@
import { PRIV } from '../model/builtin';
import * as system from '../model/system';
import * as user from '../model/user';
import * as domain from '../model/domain';
import pwhash from '../lib/hash.hydro';
@@ -7,42 +6,7 @@ import pwhash from '../lib/hash.hydro';
export const description = 'Install';
export async function run({ username = '', password = '' } = {}) {
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,
HOMEWORK_ON_MAIN: 5,
CONTESTS_ON_MAIN: 5,
TRAININGS_ON_MAIN: 5,
DISCUSSIONS_ON_MAIN: 20,
'smtp.host': '',
'smtp.port': 465,
'smtp.from': '',
'smtp.user': '',
'smtp.pass': '',
'smtp.secure': false,
'db.ver': 1,
'server.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.get(key).then((value) => {
if (!value) return system.set(key, def[key]);
return Promise.resolve();
}));
}
await Promise.all(tasks);
await domain.add('system', 0, 'Hydro', true);
await domain.add('system', 0, 'Hydro', 'Hydro System');
if (username && password) {
const udoc = await user.getById('system', -1);
if (!udoc) {
+2 -3
View File
@@ -1,7 +1,6 @@
/* eslint-disable no-await-in-loop */
import * as domain from '../model/domain';
import * as contest from '../model/contest';
import * as user from '../model/user';
import rating from '../lib/rating';
import { STATUS } from '../model/builtin';
@@ -27,7 +26,7 @@ export async function run({ domainId, isSub = false }, report) {
}
return true;
}
await user.setMultiInDomain(domainId, {}, { rating: 1500 });
await domain.setMultiUserInDomain(domainId, {}, { rating: 1500 });
const contests = await contest.getMulti(domainId, { rated: true }).sort('endAt', -1).toArray();
await report({ message: `Found ${contests.length} contests in ${domainId}` });
const udict = {};
@@ -60,7 +59,7 @@ export async function run({ domainId, isSub = false }, report) {
}
const tasks = [];
for (const uid in udict) {
tasks.push(user.setInDomain(domainId, parseInt(uid, 10), { rating: udict[uid] }));
tasks.push(domain.setUserInDomain(domainId, parseInt(uid, 10), { rating: udict[uid] }));
}
await Promise.all(tasks);
return true;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "2.7.4",
"version": "2.7.5",
"main": "hydro/loader.ts",
"repository": "https://github.com/hydro-dev/Hydro.git",
"author": "masnn",
+1 -1
View File
@@ -3,7 +3,7 @@
{% block manage_content %}
<script>
var Context = {
'socketUrl': "/manage/check-conn?token={{ handler.csrfToken }}"
'socketUrl': "/manage/check-conn?token={{ handler.UIContext.token }}"
};
</script>
<div class="section">
+1 -1
View File
@@ -3,7 +3,7 @@
{% block content %}
<script>
var Context = {
'socketUrl': "/record-detail-conn?rid={{ rdoc._id }}&token={{ handler.csrfToken }}"
'socketUrl': "/record-detail-conn?rid={{ rdoc._id }}&token={{ handler.UIContext.token }}"
};
</script>
<div class="row">
+1 -1
View File
@@ -2,7 +2,7 @@
{% block content %}
<script>
var Context = {
'socketUrl': "/record-conn?token={{ handler.csrfToken }}",
'socketUrl': "/record-conn?token={{ handler.UIContext.token }}",
'rids': [
{% for rdoc in rdocs %}"{{ rdoc._id.toString() }}",{% endfor %}
]