mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
删除用户功能
This commit is contained in:
1 parent
441eeadaf3
commit
39ea30664d
12 files changed
+159
-23
No files matched your search
@@ -26,7 +26,7 @@ const builtinHandler = [
|
|||||||
|
|
||||||
const builtinScript = [
|
const builtinScript = [
|
||||||
'install', 'uninstall', 'rating', 'recalcRating', 'register',
|
'install', 'uninstall', 'rating', 'recalcRating', 'register',
|
||||||
'blacklist', 'setSuperadmin',
|
'blacklist', 'setSuperadmin', 'deleteUser',
|
||||||
];
|
];
|
||||||
|
|
||||||
async function handler(pending, fail) {
|
async function handler(pending, fail) {
|
||||||
|
|||||||
@@ -72,10 +72,13 @@ class DiscussionMainHandler extends Handler {
|
|||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['discussion_main', null],
|
['discussion_main', null],
|
||||||
];
|
];
|
||||||
const vndict = await discussion.getListVnodes(domainId, ddocs, this);
|
const [vndict, vnodes] = await Promise.all([
|
||||||
|
discussion.getListVnodes(domainId, ddocs, this),
|
||||||
|
discussion.getNodes(domainId),
|
||||||
|
]);
|
||||||
this.response.template = 'discussion_main_or_node.html';
|
this.response.template = 'discussion_main_or_node.html';
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
ddocs, dpcount, udict, page, page_name: 'discussion_main', vndict, vnode: {}, path,
|
ddocs, dpcount, udict, page, page_name: 'discussion_main', vndict, vnode: {}, path, vnodes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,13 +126,23 @@ class ProblemCategoryHandler extends ProblemHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ProblemRandomHandler extends ProblemHandler {
|
class ProblemRandomHandler extends ProblemHandler {
|
||||||
async get({ domainId }) {
|
async get({ domainId, category }) {
|
||||||
const q = {};
|
const q = category[0] ? { $and: [] } : {};
|
||||||
|
for (const name of category) {
|
||||||
|
if (name) {
|
||||||
|
q.$and.push({
|
||||||
|
$or: [
|
||||||
|
{ category: { $elemMatch: { $eq: name } } },
|
||||||
|
{ tag: { $elemMatch: { $eq: name } } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||||
const pid = await problem.random(domainId, q);
|
const pid = await problem.random(domainId, q);
|
||||||
if (!pid) throw new NoProblemError();
|
if (!pid) throw new NoProblemError();
|
||||||
this.response.body = { pid };
|
this.response.body = { pid };
|
||||||
this.response.redirect = `/p/${pid}`;
|
this.response.redirect = this.url('problem_detail', { pid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,10 +173,6 @@ class ProblemDetailHandler extends ProblemHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ProblemSubmitHandler extends ProblemDetailHandler {
|
class ProblemSubmitHandler extends ProblemDetailHandler {
|
||||||
async prepare() {
|
|
||||||
this.checkPerm(PERM_SUBMIT_PROBLEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
async get({ domainId, pid }) {
|
async get({ domainId, pid }) {
|
||||||
this.response.template = 'problem_submit.html';
|
this.response.template = 'problem_submit.html';
|
||||||
const rdocs = await record.getUserInProblemMulti(domainId, this.user._id, this.pdoc.docId)
|
const rdocs = await record.getUserInProblemMulti(domainId, this.user._id, this.pdoc.docId)
|
||||||
@@ -471,7 +477,7 @@ async function apply() {
|
|||||||
Route('problem_category', '/p/category/:category', ProblemCategoryHandler);
|
Route('problem_category', '/p/category/:category', ProblemCategoryHandler);
|
||||||
Route('problem_random', '/problem/random', ProblemRandomHandler);
|
Route('problem_random', '/problem/random', ProblemRandomHandler);
|
||||||
Route('problem_detail', '/p/:pid', ProblemDetailHandler);
|
Route('problem_detail', '/p/:pid', ProblemDetailHandler);
|
||||||
Route('problem_submit', '/p/:pid/submit', ProblemSubmitHandler);
|
Route('problem_submit', '/p/:pid/submit', ProblemSubmitHandler, PERM_SUBMIT_PROBLEM);
|
||||||
Route('problem_pretest', '/p/:pid/pretest', ProblemPretestHandler);
|
Route('problem_pretest', '/p/:pid/pretest', ProblemPretestHandler);
|
||||||
Route('problem_settings', '/p/:pid/settings', ProblemSettingsHandler);
|
Route('problem_settings', '/p/:pid/settings', ProblemSettingsHandler);
|
||||||
Route('problem_statistics', '/p/:pid/statistics', ProblemStatisticsHandler);
|
Route('problem_statistics', '/p/:pid/statistics', ProblemStatisticsHandler);
|
||||||
|
|||||||
+12
-1
@@ -68,7 +68,18 @@ class SystemSettingHandler extends SystemHandler {
|
|||||||
if (typeof args[key] === 'object') {
|
if (typeof args[key] === 'object') {
|
||||||
const subtasks = [];
|
const subtasks = [];
|
||||||
for (const sub in args[key]) {
|
for (const sub in args[key]) {
|
||||||
subtasks.push(system.set(`${key}.${sub}`, args[key][sub]));
|
if (setting.SYSTEM_SETTINGS_BY_KEY[`${key}.${sub}`]) {
|
||||||
|
const s = setting.SYSTEM_SETTINGS_BY_KEY[`${key}.${sub}`];
|
||||||
|
if (s.ui === 'checkbox') {
|
||||||
|
if (args[key][sub] === 'on') {
|
||||||
|
subtasks.push(system.set(`${key}.${sub}`, true));
|
||||||
|
} else {
|
||||||
|
subtasks.push(system.set(`${key}.${sub}`, false));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subtasks.push(system.set(`${key}.${sub}`, args[key][sub]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tasks.push(Promise.all(subtasks));
|
tasks.push(Promise.all(subtasks));
|
||||||
} else tasks.push(system.set(key, args[key]));
|
} else tasks.push(system.set(key, args[key]));
|
||||||
|
|||||||
+20
-2
@@ -1,7 +1,9 @@
|
|||||||
const superagent = require('superagent');
|
const superagent = require('superagent');
|
||||||
|
const moment = require('moment-timezone');
|
||||||
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');
|
||||||
|
const task = require('../model/task');
|
||||||
const system = require('../model/system');
|
const system = require('../model/system');
|
||||||
const { sendMail } = require('../lib/mail');
|
const { sendMail } = require('../lib/mail');
|
||||||
const misc = require('../lib/misc');
|
const misc = require('../lib/misc');
|
||||||
@@ -10,6 +12,7 @@ const {
|
|||||||
UserNotFoundError, LoginError, SystemError,
|
UserNotFoundError, LoginError, SystemError,
|
||||||
PermissionError, BlacklistedError, UserFacingError,
|
PermissionError, BlacklistedError, UserFacingError,
|
||||||
} = require('../error');
|
} = require('../error');
|
||||||
|
const { PERM_LOGGEDIN } = require('../permission.js');
|
||||||
|
|
||||||
class UserLoginHandler extends Handler {
|
class UserLoginHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
@@ -112,12 +115,12 @@ class UserLostPassHandler extends Handler {
|
|||||||
if (!await system.get('smtp.user')) throw new SystemError('Cannot send mail');
|
if (!await system.get('smtp.user')) throw new SystemError('Cannot send mail');
|
||||||
const udoc = await user.getByEmail(mail);
|
const udoc = await user.getByEmail(mail);
|
||||||
if (!udoc) throw new UserNotFoundError(mail);
|
if (!udoc) throw new UserNotFoundError(mail);
|
||||||
const tid = await token.add(
|
const [tid] = await token.add(
|
||||||
token.TYPE_LOSTPASS,
|
token.TYPE_LOSTPASS,
|
||||||
await system.get('lostpass_token_expire_seconds'),
|
await system.get('lostpass_token_expire_seconds'),
|
||||||
{ uid: udoc._id },
|
{ uid: udoc._id },
|
||||||
);
|
);
|
||||||
const m = await this.renderHTML('user_lostpass_mail', { url: `/lostpass/${tid}`, uname: udoc.uname });
|
const m = await this.renderHTML('user_lostpass_mail', { url: `lostpass/${tid}`, uname: udoc.uname });
|
||||||
await sendMail(mail, 'Lost Password', 'user_lostpass_mail', m);
|
await sendMail(mail, 'Lost Password', 'user_lostpass_mail', m);
|
||||||
this.response.template = 'user_lostpass_mail_sent.html';
|
this.response.template = 'user_lostpass_mail_sent.html';
|
||||||
}
|
}
|
||||||
@@ -151,6 +154,20 @@ class UserDetailHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UserDeleteHandler extends Handler {
|
||||||
|
async post({ password }) {
|
||||||
|
this.user.checkPassword(password);
|
||||||
|
const tid = await task.add({
|
||||||
|
executeAfter: moment().add(7, 'days').toDate(),
|
||||||
|
type: 'script',
|
||||||
|
id: 'deleteUser',
|
||||||
|
args: { uid: this.user._id },
|
||||||
|
});
|
||||||
|
await user.setById(this.user._id, { del: tid });
|
||||||
|
this.response.template = 'user_delete_pending.html';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class UserSearchHandler extends Handler {
|
class UserSearchHandler extends Handler {
|
||||||
async get({ domainId, q, exactMatch = false }) {
|
async get({ domainId, q, exactMatch = false }) {
|
||||||
let udocs;
|
let udocs;
|
||||||
@@ -307,6 +324,7 @@ async function apply() {
|
|||||||
Route('user_lostpass', '/lostpass', UserLostPassHandler);
|
Route('user_lostpass', '/lostpass', UserLostPassHandler);
|
||||||
Route('user_lostpass_with_code', '/lostpass/:code', UserLostPassWithCodeHandler);
|
Route('user_lostpass_with_code', '/lostpass/:code', UserLostPassWithCodeHandler);
|
||||||
Route('user_search', '/user/search', UserSearchHandler);
|
Route('user_search', '/user/search', UserSearchHandler);
|
||||||
|
Route('user_delete', '/user/delete', UserDeleteHandler, PERM_LOGGEDIN);
|
||||||
Route('user_detail', '/user/:uid', UserDetailHandler);
|
Route('user_detail', '/user/:uid', UserDetailHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ function count(domainId, query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function random(domainId, query) {
|
async function random(domainId, query) {
|
||||||
const cursor = document.getMulti(domainId, query);
|
const cursor = document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
||||||
const pcount = await cursor.count();
|
const pcount = await cursor.count();
|
||||||
if (pcount) {
|
if (pcount) {
|
||||||
const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray();
|
const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
const moment = require('moment-timezone');
|
const moment = require('moment-timezone');
|
||||||
const builtin = require('./builtin');
|
const builtin = require('./builtin');
|
||||||
const options = require('../options');
|
|
||||||
|
|
||||||
const countries = moment.tz.countries();
|
const countries = moment.tz.countries();
|
||||||
const tzs = new Set();
|
const tzs = new Set();
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
const moment = require('moment-timezone');
|
||||||
const db = require('../service/db');
|
const db = require('../service/db');
|
||||||
|
|
||||||
const coll = db.collection('task');
|
const coll = db.collection('task');
|
||||||
|
|
||||||
async function add(task) {
|
async function add(task) {
|
||||||
|
task.executeAfter = task.executeAfter || new Date();
|
||||||
const res = await coll.insertOne(task);
|
const res = await coll.insertOne(task);
|
||||||
return res.insertedId;
|
return res.insertedId;
|
||||||
}
|
}
|
||||||
@@ -20,9 +22,15 @@ function del(_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getFirst(query) {
|
async function getFirst(query) {
|
||||||
|
query.executeAfter = query.executeAfter || { $lte: new Date() };
|
||||||
const res = await coll.find(query).sort('_id', 1).limit(1).toArray();
|
const res = await coll.find(query).sort('_id', 1).limit(1).toArray();
|
||||||
if (res.length) {
|
if (res.length) {
|
||||||
await coll.deleteOne({ _id: res[0]._id });
|
await coll.deleteOne({ _id: res[0]._id });
|
||||||
|
if (res[0].interval) {
|
||||||
|
await coll.insertOne({
|
||||||
|
...res[0], executeAfter: moment().add(...res[0].interval).toDate(),
|
||||||
|
});
|
||||||
|
}
|
||||||
return res[0];
|
return res[0];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
const user = require('../model/user');
|
||||||
|
const document = require('../model/document');
|
||||||
|
const setting = require('../model/setting');
|
||||||
|
const db = require('../service/db');
|
||||||
|
const paginate = require('../lib/paginate');
|
||||||
|
const { STATUS } = require('../model/builtin');
|
||||||
|
|
||||||
|
const collDocument = db.collection('document');
|
||||||
|
const collStatus = db.collection('document.status');
|
||||||
|
const collRecord = db.collection('record');
|
||||||
|
|
||||||
|
const $unset = {
|
||||||
|
regip: '', regat: '', loginat: '', loginip: '',
|
||||||
|
};
|
||||||
|
for (const s of setting.PREFERENCE_SETTINGS) {
|
||||||
|
$unset[s.key] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteRecord(uid) {
|
||||||
|
const [, pcount] = await paginate(
|
||||||
|
collRecord.find(),
|
||||||
|
1,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
for (let i = 1; i <= pcount; i++) {
|
||||||
|
const [rdocs] = await paginate(
|
||||||
|
collRecord.find(),
|
||||||
|
1,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
const tasks = [];
|
||||||
|
for (const rdoc of rdocs) {
|
||||||
|
if (rdoc.pid) {
|
||||||
|
tasks.push(document.inc(rdoc.domainId, document.TYPE_PROBLEM, rdoc.pid, 'nSubmit', -1));
|
||||||
|
}
|
||||||
|
if (rdoc.status === STATUS.STATUS_ACCEPTED) {
|
||||||
|
tasks.push(document.inc(rdoc.domainId, document.TYPE_PROBLEM, rdoc.pid, 'nAccept', -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Promise.all(tasks);
|
||||||
|
}
|
||||||
|
await collRecord.deleteMany({ uid });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteDiscussion(uid) {
|
||||||
|
const [, pcount] = await paginate(
|
||||||
|
collDocument.find({ docType: document.TYPE_DISCUSSION, owner: uid }),
|
||||||
|
1,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
for (let i = 1; i <= pcount; i++) {
|
||||||
|
const [ddocs] = await paginate(
|
||||||
|
collDocument.find({ docType: document.TYPE_DISCUSSION, owner: uid }),
|
||||||
|
1,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
const tasks = [];
|
||||||
|
for (const ddoc of ddocs) {
|
||||||
|
tasks.push(document.deleteMulti(ddoc.domainId, document.TYPE_DISCUSSION_REPLY, {
|
||||||
|
parentId: ddoc.docId,
|
||||||
|
parentType: document.TYPE_DISCUSSION,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
await Promise.all(tasks);
|
||||||
|
}
|
||||||
|
await collDocument.deleteMany({ docType: document.TYPE_DISCUSSION, owner: uid });
|
||||||
|
// TODO delete replies
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run({
|
||||||
|
uid,
|
||||||
|
}) {
|
||||||
|
await deleteRecord(uid);
|
||||||
|
await deleteDiscussion(uid);
|
||||||
|
await collStatus.deleteMany({ uid, docType: document.TYPE_DOMAIN_USER });
|
||||||
|
const uname = String.random(32);
|
||||||
|
await user.setById(uid, {
|
||||||
|
uname,
|
||||||
|
unameLower: uname.toLowerCase(),
|
||||||
|
priv: 0,
|
||||||
|
}, $unset);
|
||||||
|
await user.setPassword(uid, String.random(32));
|
||||||
|
// TODO delete contest, homework, etc
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.script.deleteUser = module.exports = { run };
|
||||||
@@ -217,14 +217,11 @@ class Handler {
|
|||||||
let res = '#';
|
let res = '#';
|
||||||
const args = { ...kwargs };
|
const args = { ...kwargs };
|
||||||
try {
|
try {
|
||||||
delete args.__keywords;
|
|
||||||
if (this.args.domainId !== 'system') {
|
if (this.args.domainId !== 'system') {
|
||||||
name += '_with_domainId';
|
name += '_with_domainId';
|
||||||
args.domainId = args.domainId || this.args.domainId;
|
args.domainId = args.domainId || this.args.domainId;
|
||||||
}
|
}
|
||||||
const { anchor, query } = args;
|
const { anchor, query } = args;
|
||||||
delete args.anchor;
|
|
||||||
delete args.query;
|
|
||||||
if (query) res = router.url(name, args, { query });
|
if (query) res = router.url(name, args, { query });
|
||||||
else res = router.url(name, args);
|
else res = router.url(name, args);
|
||||||
if (anchor) return `${res}#${anchor}`;
|
if (anchor) return `${res}#${anchor}`;
|
||||||
@@ -427,14 +424,11 @@ class ConnectionHandler {
|
|||||||
let res = '#';
|
let res = '#';
|
||||||
const args = { ...kwargs };
|
const args = { ...kwargs };
|
||||||
try {
|
try {
|
||||||
delete args.__keywords;
|
|
||||||
if (this.args.domainId !== 'system') {
|
if (this.args.domainId !== 'system') {
|
||||||
name += '_with_domainId';
|
name += '_with_domainId';
|
||||||
args.domainId = args.domainId || this.args.domainId;
|
args.domainId = args.domainId || this.args.domainId;
|
||||||
}
|
}
|
||||||
const { anchor, query } = args;
|
const { anchor, query } = args;
|
||||||
delete args.anchor;
|
|
||||||
delete args.query;
|
|
||||||
if (query) res = router.url(name, args, { query });
|
if (query) res = router.url(name, args, { query });
|
||||||
else res = router.url(name, args);
|
else res = router.url(name, args);
|
||||||
if (anchor) return `${res}#${anchor}`;
|
if (anchor) return `${res}#${anchor}`;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{% extends "layout/immersive.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="immersive--content immersive--center">
|
||||||
|
<h1>{{ _('Done') }}</h1>
|
||||||
|
<div class="text-center">
|
||||||
|
{{ _('Your account will be deleted in 7 days.') }}
|
||||||
|
{{ _('You can cancel this at user setting page.') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -7,7 +7,6 @@ import responsiveCutoff from 'vj/breakpoints.json';
|
|||||||
import { isAbove } from 'vj/utils/mediaQuery';
|
import { isAbove } from 'vj/utils/mediaQuery';
|
||||||
import Navigation from '.';
|
import Navigation from '.';
|
||||||
|
|
||||||
|
|
||||||
const nav = Navigation.instance;
|
const nav = Navigation.instance;
|
||||||
const { $nav } = nav;
|
const { $nav } = nav;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user