From f699d670192bbef60da9e7814fafe2721ee94365 Mon Sep 17 00:00:00 2001 From: undefined Date: Fri, 7 Jan 2022 18:20:05 +0800 Subject: [PATCH] core: user_group: draft --- packages/hydrooj/src/handler/home.ts | 36 +++++++------ packages/hydrooj/src/interface.ts | 39 ++++++++------ packages/hydrooj/src/model/domain.ts | 8 +-- packages/hydrooj/src/model/user.ts | 53 +++++++++++++++---- .../ui-default/templates/training_detail.html | 18 +++---- 5 files changed, 99 insertions(+), 55 deletions(-) diff --git a/packages/hydrooj/src/handler/home.ts b/packages/hydrooj/src/handler/home.ts index e2efc0bc3..28acf1aa1 100644 --- a/packages/hydrooj/src/handler/home.ts +++ b/packages/hydrooj/src/handler/home.ts @@ -24,7 +24,7 @@ import * as training from '../model/training'; import user from '../model/user'; import * as bus from '../service/bus'; import { - Connection, ConnectionHandler, Handler, param, Route, Types, + Connection, ConnectionHandler, Handler, param, query, Route, Types, } from '../service/server'; const { geoip, useragent } = global.Hydro.lib; @@ -291,28 +291,32 @@ class UserChangemailWithCodeHandler extends Handler { } class HomeDomainHandler extends Handler { - async get() { - let ddocs: DomainDoc[] = []; - let dudict: Record = {}; - if (!this.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) { + @query('all', Types.Boolean) + async get(domainId: string, all: boolean) { + let res: DomainDoc[] = []; + let dudict: Record = {}; + if (!all) { dudict = await domain.getDictUserByDomainId(this.user._id); const dids = Object.keys(dudict); - ddocs = await domain.getMulti({ _id: { $in: dids } }).toArray(); + res = await domain.getMulti({ _id: { $in: dids } }).toArray(); } else { - const _ddocs = await domain.getMulti().toArray(); - for (const ddoc of _ddocs) { - // eslint-disable-next-line no-await-in-loop - const pcount = await ProblemModel.count(ddoc._id, {}); - if (pcount >= 3 || ddoc.owner === this.user._id) { - dudict[ddoc._id] = ddoc; - ddocs.push(ddoc); - } - } + this.checkPriv(PRIV.PRIV_VIEW_ALL_DOMAIN); + res = await domain.getMulti().toArray(); + await Promise.all(res.map(async (ddoc) => { + dudict[ddoc._id] = await user.getById(domainId, this.user._id); + })); } const canManage = {}; - for (const ddoc of ddocs) { + const ddocs = []; + for (const ddoc of res) { // eslint-disable-next-line no-await-in-loop const udoc = (await user.getById(ddoc._id, this.user._id))!; + const dudoc = dudict[ddoc._id]; + if (['default', 'guest'].includes(dudoc.role)) { + delete dudict[ddoc._id]; + continue; + } + ddocs.push(ddoc); canManage[ddoc._id] = udoc.hasPerm(PERM.PERM_EDIT_DOMAIN) || udoc.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN); } diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index aaaa73b94..4bba9c4cc 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -87,6 +87,13 @@ export interface VUdoc { loginip: '127.0.0.1'; } +export interface GDoc { + _id: ObjectID; + domainId: string; + name: string; + uids: number[]; +} + export type ownerInfo = { owner: number, maintainer?: number[] }; export interface User extends Record { @@ -326,25 +333,26 @@ export interface TrainingNode { } export interface Tdoc extends Document { - docId: ObjectID, - docType: docType & number, - beginAt: Date, - endAt: Date, - attend: number, - title: string, - content: string, - rule: string, - pids: number[], - rated?: boolean, - _code?: string, + docId: ObjectID; + docType: docType & number; + beginAt: Date; + endAt: Date; + attend: number; + title: string; + content: string; + rule: string; + pids: number[]; + rated?: boolean; + _code?: string; + assign?: string[]; // For homework - penaltySince?: Date, - penaltyRules?: PenaltyRules, + penaltySince?: Date; + penaltyRules?: PenaltyRules; // For training - description?: string, - dag?: TrainingNode[], + description?: string; + dag?: TrainingNode[]; } export interface TrainingDoc extends Tdoc { @@ -583,6 +591,7 @@ export interface Collections { 'problem': ProblemDoc; 'user': Udoc; 'vuser': VUdoc; + 'user.group': GDoc; 'check': any; 'message': MessageDoc; 'token': TokenDoc; diff --git a/packages/hydrooj/src/model/domain.ts b/packages/hydrooj/src/model/domain.ts index 9d4390193..55902f4c1 100644 --- a/packages/hydrooj/src/model/domain.ts +++ b/packages/hydrooj/src/model/domain.ts @@ -225,12 +225,8 @@ class DomainModel { @ArgMethod static async getDictUserByDomainId(uid: number) { const dudocs = await collUser.find({ uid }).toArray(); - const dudict = {}; - for (const dudoc of dudocs) { - // eslint-disable-next-line no-await-in-loop - dudict[dudoc.domainId] = await DomainModel.get(dudoc.domainId); - dudict[dudoc.domainId].role = dudoc.role; - } + const dudict: Record = {}; + for (const dudoc of dudocs) dudict[dudoc.domainId] = dudoc; return dudict; } diff --git a/packages/hydrooj/src/model/user.ts b/packages/hydrooj/src/model/user.ts index ac447208c..6071dc4eb 100644 --- a/packages/hydrooj/src/model/user.ts +++ b/packages/hydrooj/src/model/user.ts @@ -1,10 +1,10 @@ import { escapeRegExp, pick } from 'lodash'; import LRU from 'lru-cache'; -import { Collection } from 'mongodb'; +import { Collection, ObjectID } from 'mongodb'; import { LoginError, UserAlreadyExistError, UserNotFoundError } from '../error'; import { - FileInfo, Udict, Udoc, - User as _User, VUdoc, + FileInfo, GDoc, Udict, + Udoc, User as _User, VUdoc, } from '../interface'; import pwhash from '../lib/hash.hydro'; import { Logger } from '../logger'; @@ -21,6 +21,7 @@ import token from './token'; const coll: Collection = db.collection('user'); // Virtual user, only for display in contest. const collV: Collection = db.collection('vuser'); +const collGroup: Collection = db.collection('user.group'); const logger = new Logger('model/user'); const cache = new LRU({ max: 500, maxAge: 300 * 1000 }); @@ -144,7 +145,7 @@ function handleMailLower(mail: string) { if (data.endsWith('@googlemail.com')) data = data.replace('@googlemail.com', '@gmail.com'); if (data.endsWith('@gmail.com')) { const [prev] = data.split('@'); - data = `${prev.replace(/\.\+/g, '')}@gmail.com`; + data = `${prev.replace(/[.+]/g, '')}@gmail.com`; } return data; } @@ -371,12 +372,46 @@ class UserModel { token.delByUid(uid), ]); } + + static createGroup(domainId: string, name: string, uids: number[]) { + return collGroup.insertOne({ domainId, name, uids }); + } + + static async listGroup(domainId: string, uid?: number) { + const groups = await collGroup.find(uid ? { domainId, uids: uid } : { domainId }).toArray(); + if (uid) { + groups.push({ + _id: new ObjectID(), domainId, uids: [uid], name: uid.toString(), + }); + } + return groups; + } + + static delGroup(domainId: string, name: string) { + return collGroup.deleteOne({ domainId, name }); + } + + static updateGroup(domainId: string, name: string, uids: number[]) { + return collGroup.updateOne({ domainId, name }, { $set: { uids } }); + } } -bus.once('app/started', () => db.ensureIndexes( - coll, - { key: { unameLower: 1 }, name: 'uname', unique: true }, - { key: { mailLower: 1 }, name: 'mail', unique: true }, -)); +bus.once('app/started', () => Promise.all([ + db.ensureIndexes( + coll, + { key: { unameLower: 1 }, name: 'uname', unique: true }, + { key: { mailLower: 1 }, name: 'mail', unique: true }, + ), + db.ensureIndexes( + collV, + { key: { unameLower: 1 }, name: 'uname', unique: true }, + { key: { mailLower: 1 }, name: 'mail', unique: true }, + ), + db.ensureIndexes( + collGroup, + { key: { domainId: 1, name: 1 }, name: 'name', unique: true }, + { key: { domainId: 1, uids: 1 }, name: 'uid' }, + ), +])); export default UserModel; global.Hydro.model.user = UserModel; diff --git a/packages/ui-default/templates/training_detail.html b/packages/ui-default/templates/training_detail.html index 814406f05..8e4fff4bf 100644 --- a/packages/ui-default/templates/training_detail.html +++ b/packages/ui-default/templates/training_detail.html @@ -9,17 +9,19 @@ {{ tdoc['content'] }}
+ {% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} +
+

{{ _('Login to join training plan') }}

+
+ {% elif not tsdoc.enroll %} +
+

{{ _('page.training_detail.invalid_when_not_enrolled') }}

+
+ {% endif %} {{ tdoc.description|default('')|markdown|safe }}
- {% if not tsdoc.enroll %} -
-
-

{{ _('page.training_detail.invalid_when_not_enrolled') }}

-
-
- {% endif %} {%- for node in tdoc['dag'] -%}
@@ -42,7 +44,6 @@
- {% if tsdoc.enroll %}
  • @@ -153,7 +154,6 @@
- {% endif %} {%- endfor -%}