优化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

+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,
};