core: oplog: better logging

This commit is contained in:
undefined committed 2021-12-21 21:34:49 +08:00
1 parent 92c1a41585
commit ead0d54a55
8 files changed
+74 -60

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "3.2.27", "version": "3.2.28",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "src/loader", "main": "src/loader",
"module": "src/loader", "module": "src/loader",
+8 -8
View File
@@ -100,10 +100,10 @@ class BlogEditHandler extends BlogHandler {
@param('content', Types.Content) @param('content', Types.Content)
async postUpdate(domainId: string, did: ObjectID, title: string, content: string) { async postUpdate(domainId: string, did: ObjectID, title: string, content: string) {
if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM); if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
await blog.edit(did, title, content); await Promise.all([
await oplog.add({ blog.edit(did, title, content),
...this.ddoc, operator: this.user._id, type: 'edit', oplog.log(this, 'blog.edit', this.ddoc),
}); ]);
this.response.body = { did }; this.response.body = { did };
this.response.redirect = this.url('blog_detail', { uid: this.user._id, did }); this.response.redirect = this.url('blog_detail', { uid: this.user._id, did });
} }
@@ -111,10 +111,10 @@ class BlogEditHandler extends BlogHandler {
@param('did', Types.ObjectID) @param('did', Types.ObjectID)
async postDelete(domainId: string, did: ObjectID) { async postDelete(domainId: string, did: ObjectID) {
if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM); if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
await blog.del(did); await Promise.all([
await oplog.add({ blog.del(did),
...this.ddoc, operator: this.user._id, operateIp: this.request.ip, type: 'delete', oplog.log(this, 'blog.delete', this.ddoc),
}); ]);
this.response.redirect = this.url('blog_main', { uid: this.ddoc.owner }); this.response.redirect = this.url('blog_main', { uid: this.ddoc.owner });
} }
} }
+24 -25
View File
@@ -251,10 +251,10 @@ class DiscussionDetailHandler extends DiscussionHandler {
async postEditReply(domainId: string, drid: ObjectID, content: string) { async postEditReply(domainId: string, drid: ObjectID, content: string) {
this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
if (!this.user.own(this.drdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); if (!this.user.own(this.drdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
await discussion.editReply(domainId, drid, content); await Promise.all([
await oplog.add({ discussion.editReply(domainId, drid, content),
...this.drdoc, operator: this.user._id, operateIp: this.request.ip, type: 'edit', oplog.log(this, 'discussion.reply.edit', this.drdoc),
}); ]);
this.back(); this.back();
} }
@@ -266,10 +266,10 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY); this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY);
} else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF); } else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF);
} }
await discussion.delReply(domainId, drid); await Promise.all([
await oplog.add({ discussion.delReply(domainId, drid),
...this.drdoc, operator: this.user._id, operateIp: this.request.ip, type: 'delete', oplog.log(this, 'discussion.reply.delete', this.drdoc),
}); ]);
this.back(); this.back();
} }
@@ -279,11 +279,10 @@ class DiscussionDetailHandler extends DiscussionHandler {
async postEditTailReply(domainId: string, drid: ObjectID, drrid: ObjectID, content: string) { async postEditTailReply(domainId: string, drid: ObjectID, drrid: ObjectID, content: string) {
this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
if (!this.user.own(this.drrdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); if (!this.user.own(this.drrdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
await discussion.editTailReply(domainId, drid, drrid, content); await Promise.all([
// TODO: history? discussion.editTailReply(domainId, drid, drrid, content),
await oplog.add({ oplog.log(this, 'discussion.tailReply.edit', this.drrdoc),
...this.drrdoc, operator: this.user._id, operateIp: this.request.ip, type: 'edit', ]);
});
this.back(); this.back();
} }
@@ -291,10 +290,10 @@ class DiscussionDetailHandler extends DiscussionHandler {
@param('drrid', Types.ObjectID) @param('drrid', Types.ObjectID)
async postDeleteTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) { async postDeleteTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) {
if (!this.user.own(this.drrdoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY); if (!this.user.own(this.drrdoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY);
await discussion.delTailReply(domainId, drid, drrid); await Promise.all([
await oplog.add({ discussion.delTailReply(domainId, drid, drrid),
...this.drrdoc, operator: this.user._id, operateIp: this.request.ip, type: 'delete', oplog.log(this, 'discussion.tailReply.delete', this.drrdoc),
}); ]);
this.back(); this.back();
} }
@@ -346,10 +345,10 @@ class DiscussionEditHandler extends DiscussionHandler {
else this.checkPerm(PERM.PERM_EDIT_DISCUSSION_SELF); else this.checkPerm(PERM.PERM_EDIT_DISCUSSION_SELF);
if (!this.user.hasPerm(PERM.PERM_HIGHLIGHT_DISCUSSION)) highlight = this.ddoc.highlight; if (!this.user.hasPerm(PERM.PERM_HIGHLIGHT_DISCUSSION)) highlight = this.ddoc.highlight;
if (!this.user.hasPerm(PERM.PERM_PIN_DISCUSSION)) pin = this.ddoc.pin; if (!this.user.hasPerm(PERM.PERM_PIN_DISCUSSION)) pin = this.ddoc.pin;
await discussion.edit(domainId, did, title, content, highlight, pin); await Promise.all([
await oplog.add({ discussion.edit(domainId, did, title, content, highlight, pin),
...this.ddoc, operator: this.user._id, type: 'edit', oplog.log(this, 'discussion.edit', this.ddoc),
}); ]);
this.response.body = { did }; this.response.body = { did };
this.response.redirect = this.url('discussion_detail', { did }); this.response.redirect = this.url('discussion_detail', { did });
} }
@@ -358,10 +357,10 @@ class DiscussionEditHandler extends DiscussionHandler {
async postDelete(domainId: string, did: ObjectID) { async postDelete(domainId: string, did: ObjectID) {
if (!this.user.own(this.ddoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION); if (!this.user.own(this.ddoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION);
else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF); else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF);
await discussion.del(domainId, did); await Promise.all([
await oplog.add({ oplog.log(this, 'discussion.delete', this.ddoc),
...this.ddoc, operator: this.user._id, operateIp: this.request.ip, type: 'delete', discussion.del(domainId, did),
}); ]);
this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId }; this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId };
this.response.redirect = this.url('discussion_node', { this.response.redirect = this.url('discussion_node', {
type: discussion.typeDisplay[this.ddoc.parentType], type: discussion.typeDisplay[this.ddoc.parentType],
+13 -3
View File
@@ -11,6 +11,7 @@ import paginate from '../lib/paginate';
import { PERM, PERMS_BY_FAMILY, PRIV } from '../model/builtin'; import { PERM, PERMS_BY_FAMILY, PRIV } from '../model/builtin';
import * as discussion from '../model/discussion'; import * as discussion from '../model/discussion';
import domain from '../model/domain'; import domain from '../model/domain';
import * as oplog from '../model/oplog';
import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting'; import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
import * as system from '../model/system'; import * as system from '../model/system';
import user from '../model/user'; import user from '../model/user';
@@ -114,14 +115,20 @@ class DomainUserHandler extends ManageHandler {
@post('uid', Types.Int) @post('uid', Types.Int)
@post('role', Types.Name) @post('role', Types.Name)
async postSetUser(domainId: string, uid: number, role: string) { async postSetUser(domainId: string, uid: number, role: string) {
await domain.setUserRole(domainId, uid, role); await Promise.all([
domain.setUserRole(domainId, uid, role),
oplog.log(this, 'domain.setRole', { uid, role }),
]);
this.back(); this.back();
} }
@param('uid', Types.NumericArray) @param('uid', Types.NumericArray)
@param('role', Types.Name) @param('role', Types.Name)
async postSetUsers(domainId: string, uid: number[], role: string) { async postSetUsers(domainId: string, uid: number[], role: string) {
await domain.setUserRole(domainId, uid, role); await Promise.all([
domain.setUserRole(domainId, uid, role),
oplog.log(this, 'domain.setRole', { uid, role }),
]);
this.back(); this.back();
} }
} }
@@ -242,7 +249,10 @@ class DomainJoinHandler extends Handler {
throw new InvalidJoinInvitationCodeError(this.domain._id); throw new InvalidJoinInvitationCodeError(this.domain._id);
} }
} }
await domain.setUserRole(this.domain._id, this.user._id, this.joinSettings.role); await Promise.all([
domain.setUserRole(this.domain._id, this.user._id, this.joinSettings.role),
oplog.log(this, 'domain.join', {}),
]);
this.response.redirect = this.url('homepage', { query: { notification: 'Successfully joined domain.' } }); this.response.redirect = this.url('homepage', { query: { notification: 'Successfully joined domain.' } });
} }
} }
+1 -7
View File
@@ -91,14 +91,8 @@ export class FSDownloadHandler extends Handler {
this.response.addHeader('Cache-Control', 'public'); this.response.addHeader('Cache-Control', 'public');
const target = `user/${uid}/${filename}`; const target = `user/${uid}/${filename}`;
const file = await storage.getMeta(target); const file = await storage.getMeta(target);
await oplog.add({ await oplog.log(this, 'download.file.user', {
type: 'download',
time: new Date(),
uid: this.user._id,
ip: this.request.ip,
fileType: 'user',
target, target,
referer: this.request.referer,
size: file?.size || 0, size: file?.size || 0,
}); });
if (!file) { if (!file) {
+2 -14
View File
@@ -514,14 +514,8 @@ export class ProblemFilesHandler extends ProblemDetailHandler {
?.filter((i) => files.has(i.name)) ?.filter((i) => files.has(i.name))
?.map((i) => i.size), ?.map((i) => i.size),
) || 0; ) || 0;
await oplog.add({ await oplog.log(this, 'download.problem.bulk', {
type: 'bulkDownload',
time: new Date(),
uid: this.user._id,
ip: this.request.ip,
fileType: 'problem',
target: Array.from(files).map((file) => `problem/${this.pdoc.domainId}/${this.pdoc.docId}/${type}/${file}`), target: Array.from(files).map((file) => `problem/${this.pdoc.domainId}/${this.pdoc.docId}/${type}/${file}`),
referer: this.request.referer,
size, size,
}); });
for (const file of files) { for (const file of files) {
@@ -618,14 +612,8 @@ export class ProblemFileDownloadHandler extends ProblemDetailHandler {
} }
const target = `problem/${this.pdoc.domainId}/${this.pdoc.docId}/${type}/${filename}`; const target = `problem/${this.pdoc.domainId}/${this.pdoc.docId}/${type}/${filename}`;
const file = await storage.getMeta(target); const file = await storage.getMeta(target);
await oplog.add({ await oplog.log(this, 'download.problem.single', {
type: 'download',
time: new Date(),
uid: this.user._id,
ip: this.request.ip,
fileType: 'problem',
target, target,
referer: this.request.referer,
size: file?.size || 0, size: file?.size || 0,
}); });
if (!file) { if (!file) {
+5 -1
View File
@@ -14,6 +14,7 @@ import BlackListModel from '../model/blacklist';
import { PERM, PRIV, STATUS } from '../model/builtin'; import { PERM, PRIV, STATUS } from '../model/builtin';
import domain from '../model/domain'; import domain from '../model/domain';
import oauth from '../model/oauth'; import oauth from '../model/oauth';
import * as oplog from '../model/oplog';
import problem, { ProblemDoc } from '../model/problem'; import problem, { ProblemDoc } from '../model/problem';
import * as system from '../model/system'; import * as system from '../model/system';
import task from '../model/task'; import task from '../model/task';
@@ -118,7 +119,10 @@ class UserLoginHandler extends Handler {
let udoc = await user.getByEmail(domainId, uname); let udoc = await user.getByEmail(domainId, uname);
if (!udoc) udoc = await user.getByUname(domainId, uname); if (!udoc) udoc = await user.getByUname(domainId, uname);
if (!udoc) throw new UserNotFoundError(uname); if (!udoc) throw new UserNotFoundError(uname);
await this.limitRate('user_login', 60, 5); await Promise.all([
this.limitRate('user_login', 60, 5),
oplog.log(this, 'user.login', { redirect }),
]);
if (udoc._tfa && !verifyToken(udoc._tfa, tfa)) throw new InvalidTokenError('2FA token invalid.'); if (udoc._tfa && !verifyToken(udoc._tfa, tfa)) throw new InvalidTokenError('2FA token invalid.');
udoc.checkPassword(password); udoc.checkPassword(password);
await user.setById(udoc._id, { loginat: new Date(), loginip: this.request.ip }); await user.setById(udoc._id, { loginat: new Date(), loginip: this.request.ip });
+20 -1
View File
@@ -1,6 +1,7 @@
import { ObjectID } from 'mongodb'; import { ObjectID } from 'mongodb';
import { OplogDoc } from '../interface'; import { OplogDoc } from '../interface';
import db from '../service/db'; import db from '../service/db';
import type { Handler } from '../service/server';
export const coll = db.collection('oplog'); export const coll = db.collection('oplog');
@@ -14,8 +15,26 @@ export async function add(data: Partial<OplogDoc> & { type: string }): Promise<O
return res.insertedId; return res.insertedId;
} }
export async function log<T extends Handler>(handler: T, type: string, data: any) {
const res = await coll.insertOne({
...data,
_id: new ObjectID(),
type,
time: new Date(),
domainId: handler.domainId,
ua: handler.request.headers?.['user-agent'],
referer: handler.request.headers?.referer,
args: handler.args,
operator: handler.user?.id,
operateIp: handler.request.ip,
});
return res.insertedId;
}
export async function get(id: ObjectID): Promise<OplogDoc | null> { export async function get(id: ObjectID): Promise<OplogDoc | null> {
return await coll.findOne({ _id: id }); return await coll.findOne({ _id: id });
} }
global.Hydro.model.oplog = { coll, add, get }; global.Hydro.model.oplog = {
coll, add, get, log,
};