mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: user_group: draft
This commit is contained in:
1 parent
07c5793fb8
commit
f699d67019
5 files changed
+96
-52
No files matched your search
@@ -24,7 +24,7 @@ import * as training from '../model/training';
|
|||||||
import user from '../model/user';
|
import user from '../model/user';
|
||||||
import * as bus from '../service/bus';
|
import * as bus from '../service/bus';
|
||||||
import {
|
import {
|
||||||
Connection, ConnectionHandler, Handler, param, Route, Types,
|
Connection, ConnectionHandler, Handler, param, query, Route, Types,
|
||||||
} from '../service/server';
|
} from '../service/server';
|
||||||
|
|
||||||
const { geoip, useragent } = global.Hydro.lib;
|
const { geoip, useragent } = global.Hydro.lib;
|
||||||
@@ -291,28 +291,32 @@ class UserChangemailWithCodeHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class HomeDomainHandler extends Handler {
|
class HomeDomainHandler extends Handler {
|
||||||
async get() {
|
@query('all', Types.Boolean)
|
||||||
let ddocs: DomainDoc[] = [];
|
async get(domainId: string, all: boolean) {
|
||||||
let dudict: Record<string, DomainDoc> = {};
|
let res: DomainDoc[] = [];
|
||||||
if (!this.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) {
|
let dudict: Record<string, any> = {};
|
||||||
|
if (!all) {
|
||||||
dudict = await domain.getDictUserByDomainId(this.user._id);
|
dudict = await domain.getDictUserByDomainId(this.user._id);
|
||||||
const dids = Object.keys(dudict);
|
const dids = Object.keys(dudict);
|
||||||
ddocs = await domain.getMulti({ _id: { $in: dids } }).toArray();
|
res = await domain.getMulti({ _id: { $in: dids } }).toArray();
|
||||||
} else {
|
} else {
|
||||||
const _ddocs = await domain.getMulti().toArray();
|
this.checkPriv(PRIV.PRIV_VIEW_ALL_DOMAIN);
|
||||||
for (const ddoc of _ddocs) {
|
res = await domain.getMulti().toArray();
|
||||||
// eslint-disable-next-line no-await-in-loop
|
await Promise.all(res.map(async (ddoc) => {
|
||||||
const pcount = await ProblemModel.count(ddoc._id, {});
|
dudict[ddoc._id] = await user.getById(domainId, this.user._id);
|
||||||
if (pcount >= 3 || ddoc.owner === this.user._id) {
|
}));
|
||||||
dudict[ddoc._id] = ddoc;
|
|
||||||
ddocs.push(ddoc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const canManage = {};
|
const canManage = {};
|
||||||
for (const ddoc of ddocs) {
|
const ddocs = [];
|
||||||
|
for (const ddoc of res) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const udoc = (await user.getById(ddoc._id, this.user._id))!;
|
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)
|
canManage[ddoc._id] = udoc.hasPerm(PERM.PERM_EDIT_DOMAIN)
|
||||||
|| udoc.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN);
|
|| udoc.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,13 @@ export interface VUdoc {
|
|||||||
loginip: '127.0.0.1';
|
loginip: '127.0.0.1';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GDoc {
|
||||||
|
_id: ObjectID;
|
||||||
|
domainId: string;
|
||||||
|
name: string;
|
||||||
|
uids: number[];
|
||||||
|
}
|
||||||
|
|
||||||
export type ownerInfo = { owner: number, maintainer?: number[] };
|
export type ownerInfo = { owner: number, maintainer?: number[] };
|
||||||
|
|
||||||
export interface User extends Record<string, any> {
|
export interface User extends Record<string, any> {
|
||||||
@@ -326,25 +333,26 @@ export interface TrainingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Tdoc<docType = document['TYPE_CONTEST'] | document['TYPE_TRAINING']> extends Document {
|
export interface Tdoc<docType = document['TYPE_CONTEST'] | document['TYPE_TRAINING']> extends Document {
|
||||||
docId: ObjectID,
|
docId: ObjectID;
|
||||||
docType: docType & number,
|
docType: docType & number;
|
||||||
beginAt: Date,
|
beginAt: Date;
|
||||||
endAt: Date,
|
endAt: Date;
|
||||||
attend: number,
|
attend: number;
|
||||||
title: string,
|
title: string;
|
||||||
content: string,
|
content: string;
|
||||||
rule: string,
|
rule: string;
|
||||||
pids: number[],
|
pids: number[];
|
||||||
rated?: boolean,
|
rated?: boolean;
|
||||||
_code?: string,
|
_code?: string;
|
||||||
|
assign?: string[];
|
||||||
|
|
||||||
// For homework
|
// For homework
|
||||||
penaltySince?: Date,
|
penaltySince?: Date;
|
||||||
penaltyRules?: PenaltyRules,
|
penaltyRules?: PenaltyRules;
|
||||||
|
|
||||||
// For training
|
// For training
|
||||||
description?: string,
|
description?: string;
|
||||||
dag?: TrainingNode[],
|
dag?: TrainingNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TrainingDoc extends Tdoc {
|
export interface TrainingDoc extends Tdoc {
|
||||||
@@ -583,6 +591,7 @@ export interface Collections {
|
|||||||
'problem': ProblemDoc;
|
'problem': ProblemDoc;
|
||||||
'user': Udoc;
|
'user': Udoc;
|
||||||
'vuser': VUdoc;
|
'vuser': VUdoc;
|
||||||
|
'user.group': GDoc;
|
||||||
'check': any;
|
'check': any;
|
||||||
'message': MessageDoc;
|
'message': MessageDoc;
|
||||||
'token': TokenDoc;
|
'token': TokenDoc;
|
||||||
|
|||||||
@@ -225,12 +225,8 @@ class DomainModel {
|
|||||||
@ArgMethod
|
@ArgMethod
|
||||||
static async getDictUserByDomainId(uid: number) {
|
static async getDictUserByDomainId(uid: number) {
|
||||||
const dudocs = await collUser.find({ uid }).toArray();
|
const dudocs = await collUser.find({ uid }).toArray();
|
||||||
const dudict = {};
|
const dudict: Record<string, any> = {};
|
||||||
for (const dudoc of dudocs) {
|
for (const dudoc of dudocs) dudict[dudoc.domainId] = dudoc;
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
dudict[dudoc.domainId] = await DomainModel.get(dudoc.domainId);
|
|
||||||
dudict[dudoc.domainId].role = dudoc.role;
|
|
||||||
}
|
|
||||||
return dudict;
|
return dudict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { escapeRegExp, pick } from 'lodash';
|
import { escapeRegExp, pick } from 'lodash';
|
||||||
import LRU from 'lru-cache';
|
import LRU from 'lru-cache';
|
||||||
import { Collection } from 'mongodb';
|
import { Collection, ObjectID } from 'mongodb';
|
||||||
import { LoginError, UserAlreadyExistError, UserNotFoundError } from '../error';
|
import { LoginError, UserAlreadyExistError, UserNotFoundError } from '../error';
|
||||||
import {
|
import {
|
||||||
FileInfo, Udict, Udoc,
|
FileInfo, GDoc, Udict,
|
||||||
User as _User, VUdoc,
|
Udoc, User as _User, VUdoc,
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
import pwhash from '../lib/hash.hydro';
|
import pwhash from '../lib/hash.hydro';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
@@ -21,6 +21,7 @@ import token from './token';
|
|||||||
const coll: Collection<Udoc> = db.collection('user');
|
const coll: Collection<Udoc> = db.collection('user');
|
||||||
// Virtual user, only for display in contest.
|
// Virtual user, only for display in contest.
|
||||||
const collV: Collection<VUdoc> = db.collection('vuser');
|
const collV: Collection<VUdoc> = db.collection('vuser');
|
||||||
|
const collGroup: Collection<GDoc> = db.collection('user.group');
|
||||||
const logger = new Logger('model/user');
|
const logger = new Logger('model/user');
|
||||||
const cache = new LRU<string, User>({ max: 500, maxAge: 300 * 1000 });
|
const cache = new LRU<string, User>({ 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('@googlemail.com')) data = data.replace('@googlemail.com', '@gmail.com');
|
||||||
if (data.endsWith('@gmail.com')) {
|
if (data.endsWith('@gmail.com')) {
|
||||||
const [prev] = data.split('@');
|
const [prev] = data.split('@');
|
||||||
data = `${prev.replace(/\.\+/g, '')}@gmail.com`;
|
data = `${prev.replace(/[.+]/g, '')}@gmail.com`;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -371,12 +372,46 @@ class UserModel {
|
|||||||
token.delByUid(uid),
|
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(
|
bus.once('app/started', () => Promise.all([
|
||||||
|
db.ensureIndexes(
|
||||||
coll,
|
coll,
|
||||||
{ key: { unameLower: 1 }, name: 'uname', unique: true },
|
{ key: { unameLower: 1 }, name: 'uname', unique: true },
|
||||||
{ key: { mailLower: 1 }, name: 'mail', 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;
|
export default UserModel;
|
||||||
global.Hydro.model.user = UserModel;
|
global.Hydro.model.user = UserModel;
|
||||||
@@ -9,17 +9,19 @@
|
|||||||
{{ tdoc['content'] }}
|
{{ tdoc['content'] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="section__body typo">
|
<div class="section__body typo">
|
||||||
|
{% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||||
|
<blockquote class="typo note">
|
||||||
|
<p>{{ _('Login to join training plan') }}</p>
|
||||||
|
</blockquote>
|
||||||
|
{% elif not tsdoc.enroll %}
|
||||||
|
<blockquote class="typo note">
|
||||||
|
<p>{{ _('page.training_detail.invalid_when_not_enrolled') }}</p>
|
||||||
|
</blockquote>
|
||||||
|
{% endif %}
|
||||||
{{ tdoc.description|default('')|markdown|safe }}
|
{{ tdoc.description|default('')|markdown|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section">
|
<div class="section">
|
||||||
{% if not tsdoc.enroll %}
|
|
||||||
<div class="section__body">
|
|
||||||
<blockquote class="typo warn">
|
|
||||||
<p>{{ _('page.training_detail.invalid_when_not_enrolled') }}</p>
|
|
||||||
</blockquote>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{%- for node in tdoc['dag'] -%}
|
{%- for node in tdoc['dag'] -%}
|
||||||
<div data-heading-extract-to="#menu-item-training_detail" class="training__section {% if nsdict[node['_id']]['isDone'] %}done{% elif nsdict[node['_id']]['isProgress'] %}progress{% elif nsdict[node['_id']]['isOpen'] %}open{% else %}invalid{% endif %} {% if nsdict[node['_id']]['isProgress'] or nsdict[node['_id']]['isOpen'] %}expanded{% else %}collapsed{% endif %}">
|
<div data-heading-extract-to="#menu-item-training_detail" class="training__section {% if nsdict[node['_id']]['isDone'] %}done{% elif nsdict[node['_id']]['isProgress'] %}progress{% elif nsdict[node['_id']]['isOpen'] %}open{% else %}invalid{% endif %} {% if nsdict[node['_id']]['isProgress'] or nsdict[node['_id']]['isOpen'] %}expanded{% else %}collapsed{% endif %}">
|
||||||
<div class="section__header clearfix">
|
<div class="section__header clearfix">
|
||||||
@@ -42,7 +44,6 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if tsdoc.enroll %}
|
|
||||||
<div class="section__body">
|
<div class="section__body">
|
||||||
<ul class="supplementary dot list">
|
<ul class="supplementary dot list">
|
||||||
<li>
|
<li>
|
||||||
@@ -153,7 +154,6 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
<div class="section__body"> <!-- space placeholder --></div>
|
<div class="section__body"> <!-- space placeholder --></div>
|
||||||
|
|||||||
Reference in new issue
Block a user