+nginx.conf; update scratchpad

This commit is contained in:
undefined committed 2020-07-16 15:18:55 +08:00
1 parent 6ad41dc2ae
commit 2d5eef8d08
28 files changed
+206 -230

No files matched your search

+12 -12
View File
@@ -2,7 +2,7 @@
const { isClass } = require('./utils');
interface IHydroError {
new(...args: any[]): HydroError
new(...args: any[]): HydroError
}
const Err = (name: string, ...info: Array<IHydroError | Function | string | number>) => {
@@ -29,19 +29,19 @@ const Err = (name: string, ...info: Array<IHydroError | Function | string | numb
};
export class HydroError extends Error {
params: any[];
params: any[];
code: number;
code: number;
constructor(...params: any[]) {
super();
this.params = params;
}
constructor(...params: any[]) {
super();
this.params = params;
}
// eslint-disable-next-line class-methods-use-this
msg() {
return 'HydroError';
}
// eslint-disable-next-line class-methods-use-this
msg() {
return 'HydroError';
}
}
export const UserFacingError = Err('UserFacingError', HydroError, 'UserFacingError', 400);
@@ -85,7 +85,7 @@ export const CsrfTokenError = Err('CsrfTokenError', ForbiddenError, 'CsrfTokenEr
export const UserNotFoundError = Err('UserNotFoundError', NotFoundError, 'User {0} not found.');
export const NoProblemError = Err('NoProblemError', NotFoundError, 'No problem.');
export const RecordNotFoundError = Err('RecordNotFoundError', NotFoundError, 'Record {0} not found.');
export const ProblemDataNotFoundError = Err('ProblemDataNotFoundError', NotFoundError, 'Data of problem {1} not found.');
export const ProblemDataNotFoundError = Err('ProblemDataNotFoundError', NotFoundError, 'Data of problem {0} not found.');
export const MessageNotFoundError = Err('MessageNotFoundError', NotFoundError, 'Message {0} not found.');
export const DocumentNotFoundError = Err('DocumentNotFoundError', NotFoundError, 'Document {2} not found.');
+6 -1
View File
@@ -18,6 +18,7 @@ import * as system from '../model/system';
import {
Route, Handler, Types, param,
} from '../service/server';
import * as bus from '../service/bus';
const ContestHandler = contest.ContestHandlerMixin(Handler);
@@ -281,7 +282,11 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
lang,
code,
}, true);
await contest.updateStatus(domainId, this.tdoc.docId, this.user._id, rid, this.pdoc.docId);
const [rdoc] = await Promise.all([
record.get(domainId, rid),
contest.updateStatus(domainId, this.tdoc.docId, this.user._id, rid, this.pdoc.docId),
]);
bus.publish('record_change', rdoc);
if (!this.canShowRecord(this.tdoc)) {
this.response.body = { tid };
this.response.redirect = this.url('contest_detail', { tid });
+8 -3
View File
@@ -11,6 +11,7 @@ import { PenaltyRules } from '../interface';
import {
Route, Handler, Types, param,
} from '../service/server';
import * as bus from '../service/bus';
import { PERM } from '../model/builtin';
import * as system from '../model/system';
import * as user from '../model/user';
@@ -175,7 +176,7 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
@param('code', Types.String)
@param('lang', Types.String)
async post(domainId: string, tid: ObjectID, pid: number, code: string, lang: string) {
this.limitRate('add_record', 60, 100);
this.limitRate('add_record', 3600, 100);
const tsdoc = await contest.getStatus(domainId, tid, this.user._id, document.TYPE_HOMEWORK);
if (!tsdoc.attend) throw new HomeworkNotAttendedError(tid);
if (!contest.isOngoing(this.tdoc)) throw new HomeworkNotLiveError(tid);
@@ -183,8 +184,12 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
const rid = await record.add(domainId, {
pid, lang, code, uid: this.user._id, tid, hidden: true, ttype: document.TYPE_HOMEWORK,
}, true);
await contest.updateStatus(domainId, tid, this.user._id,
rid, pid, false, 0, document.TYPE_HOMEWORK);
const [rdoc] = await Promise.all([
record.get(domainId, rid),
contest.updateStatus(domainId, tid, this.user._id,
rid, pid, false, 0, document.TYPE_HOMEWORK),
]);
bus.publish('record_change', rdoc);
this.response.body = { tid, rid };
if (this.canShowRecord(this.tdoc)) this.response.redirect = this.url('record_detail', { rid });
else this.response.redirect = this.url('homework_detail', { tid });
+1
View File
@@ -140,6 +140,7 @@ class UiSettingsHandler extends Handler {
header_background_2x,
});
this.response.type = 'text/css';
this.ctx.set('nolog', '1');
}
}
+18 -10
View File
@@ -19,6 +19,7 @@ import {
import {
NoProblemError, ProblemDataNotFoundError, BadRequestError,
SolutionNotFoundError,
ProblemNotFoundError,
} from '../error';
import { Pdoc, User, Rdoc } from '../interface';
@@ -220,7 +221,11 @@ class ProblemSubmitHandler extends ProblemDetailHandler {
const rid = await record.add(domainId, {
uid: this.user._id, lang, code, pid: this.pdoc.docId,
}, true);
await domain.incUserInDomain(domainId, this.user._id, 'nSubmit');
const [rdoc] = await Promise.all([
record.get(domainId, rid),
domain.incUserInDomain(domainId, this.user._id, 'nSubmit'),
]);
bus.publish('record_change', rdoc);
this.response.body = { rid };
this.response.redirect = this.url('record_detail', { rid });
}
@@ -229,13 +234,14 @@ class ProblemSubmitHandler extends ProblemDetailHandler {
class ProblemPretestHandler extends ProblemDetailHandler {
@param('lang', Types.String)
@param('code', Types.String)
@param('input', Types.String)
async post(domainId: string, lang: string, code: string, input: string) {
this.limitRate('add_record', 60, 100);
@param('input', Types.String, true)
async post(domainId: string, lang: string, code: string, input: string = '') {
this.limitRate('add_record', 3600, 100);
const rid = await record.add(domainId, {
uid: this.user._id, lang, code, pid: this.pdoc.docId, input,
}, true);
await record.judge(domainId, rid);
const rdoc = await record.get(domainId, rid);
bus.publish('record_change', rdoc);
this.response.body = { rid };
}
}
@@ -247,7 +253,9 @@ class ProblemPretestConnectionHandler extends ConnectionHandler {
@param('pid', Types.String)
async prepare(domainId: string, pid: string) {
this.pid = pid;
const pdoc = await problem.get(domainId, pid);
if (!pdoc) throw new ProblemNotFoundError(domainId, pid);
this.pid = pdoc.docId.toString();
this.domainId = domainId;
bus.subscribe(['record_change'], this, 'onRecordChange');
}
@@ -310,11 +318,11 @@ class ProblemSettingsHandler extends ProblemManageHandler {
@param('pid', Types.String, null, parsePid)
@param('hidden', Types.Boolean)
@param('category', Types.String, null, parseCategory)
@param('tag', Types.String, null, parseCategory)
@param('category', Types.String, true, null, parseCategory)
@param('tag', Types.String, true, null, parseCategory)
async postSetting(
domainId: string, pid: string | number, hidden = false,
category: string[], tag: string[],
category: string[] = [], tag: string[] = [],
) {
const pdoc = await problem.get(domainId, pid);
await problem.edit(domainId, pdoc.docId, { hidden, category, tag });
@@ -554,7 +562,7 @@ export async function apply() {
Route('problem_solution_raw', '/p/:pid/solution/:psid/raw', ProblemSolutionRawHandler);
Route('problem_solution_reply_raw', '/p/:pid/solution/:psid/:psrid/raw', ProblemSolutionReplyRawHandler);
Route('problem_create', '/problem/create', ProblemCreateHandler);
Connection('problem_pretest_conn', '/p/:pid/pretest-conn', ProblemPretestConnectionHandler);
Connection('problem_pretest_conn', '/conn/pretest', ProblemPretestConnectionHandler);
}
global.Hydro.handler.problem = apply;
+1 -1
View File
@@ -42,7 +42,7 @@ export interface User extends Dictionary<any> {
priv: number,
regat: Date,
loginat: Date,
perm: bigint,
perm: () => bigint,
role: string,
regip: () => string,
loginip: () => string,
-10
View File
@@ -503,14 +503,6 @@ export const CATEGORIES = {
],
};
export const FOOTER_EXTRA_HTMLS = [];
export const VIEW_LANGS = [
{ code: 'zh_CN', name: '简体中文' },
{ code: 'zh_TW', name: '正體中文' },
{ code: 'en', name: 'English' },
];
export const LANG_TEXTS = {
c: 'C',
cc: 'C++',
@@ -623,8 +615,6 @@ global.Hydro.model.builtin = {
BUILTIN_ROLES,
DEFAULT_NODES,
CATEGORIES,
VIEW_LANGS,
FOOTER_EXTRA_HTMLS,
LANG_TEXTS,
LANG_HIGHLIGHT_ID,
STATUS,
+1 -1
View File
@@ -29,7 +29,7 @@ export async function add(
domainId: string, pid: string = null, title: string, content: string, owner: number,
tag: string[] = [], category: string[] = [], data: ObjectID = null, hidden = false,
) {
const d = await domain.inc(domainId, 'pid_counter', 1);
const d = await domain.inc(domainId, 'pidCounter', 1);
if (!pid) pid = d.pidCounter.toString();
return await document.add(
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
+1 -1
View File
@@ -46,7 +46,7 @@ export interface JudgeTask {
type?: string,
}
export async function add(domainId: string, data: RdocBase, addTask: boolean) {
export async function add(domainId: string, data: RdocBase, addTask: boolean): Promise<ObjectID> {
_.defaults(data, {
status: STATUS.STATUS_WAITING,
type: 'judge',
+11 -9
View File
@@ -13,7 +13,7 @@ for (const country of countries) {
for (const t of tz) tzs.add(t);
}
const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]) as [string, string][];
const langRange: Dictionary<string> = {};
export const langRange: Dictionary<string> = {};
for (const lang in global.Hydro.locales) {
langRange[lang] = global.Hydro.locales[lang].__langname;
@@ -78,8 +78,8 @@ export const SystemSetting = (...settings: _Setting[]) => {
PreferenceSetting(
Setting('setting_display', 'viewLang', langRange, 'zh_CN', 'select', 'UI Language'),
Setting('setting_display', 'timeZone', timezones, 'Asia/Shanghai', 'select', 'Timezone'),
Setting('setting_usage', 'codeLang', builtin.LANG_TEXTS, null, 'select', 'Default Code Language'),
Setting('setting_usage', 'codeTemplate', null, null, 'textarea', 'Default Code Template',
Setting('setting_usage', 'codeLang', builtin.LANG_TEXTS, 'c', 'select', 'Default Code Language'),
Setting('setting_usage', 'codeTemplate', null, '', 'textarea', 'Default Code Template',
'If left blank, the built-in template of the corresponding language will be used.'),
);
@@ -87,7 +87,7 @@ AccountSetting(
Setting('setting_info', 'gravatar', null, null, 'text', 'Gravatar Email',
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.'),
Setting('setting_info', 'qq', null, null, 'text', 'QQ'),
Setting('setting_info', 'gender', builtin.USER_GENDER_RANGE, null, 'select', 'Gender'),
Setting('setting_info', 'gender', builtin.USER_GENDER_RANGE, builtin.USER_GENDER_OTHER, 'select', 'Gender'),
Setting('setting_info', 'bio', null, null, 'markdown', 'Bio'),
Setting('setting_customize', 'backgroundImage', null,
'/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image',
@@ -99,11 +99,11 @@ 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', 'pid_counter', null, 0, 'number', 'Problem ID Counter', 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),
Setting('setting_storage', 'pidCounter', null, 0, 'number', 'Problem ID Counter', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('setting_storage', 'nAccept', null, 0, 'number', 'nAccept', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('setting_storage', 'nSubmit', null, 0, 'number', 'nSubmit', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('setting_storage', 'nLike', null, 0, 'number', 'nLike', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('setting_storage', 'rating', null, 1500, 'number', 'rating', null, FLAG_HIDDEN | FLAG_DISABLED),
);
SystemSetting(
@@ -126,6 +126,7 @@ SystemSetting(
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.name', null, 'Hydro', 'text', 'Site Name'),
Setting('setting_ui', 'ui.footer_extra_html', null, '', 'textarea', 'Footer Extra HTML'),
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'),
@@ -168,6 +169,7 @@ global.Hydro.postInit.push(
);
global.Hydro.model.setting = {
langRange,
Setting,
PreferenceSetting,
AccountSetting,
+3 -3
View File
@@ -43,7 +43,7 @@ export class User implements _User {
loginat: Date;
perm: bigint;
perm: () => bigint;
role: string;
@@ -67,7 +67,7 @@ export class User implements _User {
this.regip = () => udoc.regip;
this.loginat = udoc.loginat;
this.loginip = () => udoc.loginip;
this.perm = dudoc.perm;
this.perm = () => dudoc.perm;
this.role = dudoc.role || 'default';
for (const key in setting.SETTINGS_BY_KEY) {
@@ -86,7 +86,7 @@ export class User implements _User {
}
hasPerm(p: bigint) {
return (this.perm & p) === p;
return (this.perm() & p) === p;
}
hasPriv(p: number) {
+21 -22
View File
@@ -12,7 +12,7 @@ import Body from 'koa-body';
import Router from 'koa-router';
import cache from 'koa-static-cache';
import sockjs from 'sockjs';
import serialize from 'serialize-javascript';
import serialize, { SerializeJSOptions } from 'serialize-javascript';
import parse from 'yargs-parser';
import { lrucache } from '../utils';
import { User, DomainDoc } from '../interface';
@@ -23,6 +23,7 @@ import {
NotFoundError,
} from '../error';
import { render } from '../lib/template';
import hash from '../lib/hash.hydro';
import * as misc from '../lib/misc';
import * as user from '../model/user';
import * as domain from '../model/domain';
@@ -30,10 +31,8 @@ import * as system from '../model/system';
import * as blacklist from '../model/blacklist';
import * as token from '../model/token';
import * as opcount from '../model/opcount';
import hash from '../lib/hash.hydro';
const argv = parse(process.argv.slice(2));
let enableLog = true;
const app = new Koa();
let server;
const router = new Router();
@@ -43,14 +42,6 @@ interface H {
[key: string]: any,
}
interface IHandler {
// @ts-ignore
new(ctx: Koa.Context): Handler;
}
interface IConnectionHandler {
// @ts-ignore
new(conn: sockjs.Connection): ConnectionHandler;
}
type MethodDecorator = (target: any, name: string, obj: any) => any;
type Converter = (value: any) => any;
type Validator = (value: any) => boolean;
@@ -268,7 +259,6 @@ export class Handler {
}
async renderHTML(name: string, context: any): Promise<string> {
if (enableLog) console.time(name);
const UserContext = {
...this.user,
gravatar: misc.gravatar(this.user.gravatar || '', 128),
@@ -281,7 +271,6 @@ export class Handler {
_: this.translate.bind(this),
...context,
});
if (enableLog) console.timeEnd(name);
return res;
}
@@ -406,6 +395,7 @@ export class Handler {
),
]);
this.csrfToken = this.getCsrfToken(this.session._id || String.random(32));
this.UIContext.csrfToken = this.csrfToken;
}
async finish() {
@@ -421,12 +411,23 @@ export class Handler {
}
async renderBody() {
if (this.response.redirect) {
this.response.body = this.response.body || {};
this.response.body.url = this.response.redirect;
}
if (this.response.type) return;
if (this.request.json || this.response.redirect || !this.response.template) {
this.response.body = serialize(this.response.body);
if (
this.request.json || this.response.redirect
|| this.request.query.noTemplate || !this.response.template) {
try {
this.response.body = JSON.stringify(this.response.body);
} catch (e) {
const opt: SerializeJSOptions = { ignoreFunction: true };
if (this.request.query.noTemplate) opt.space = 2;
this.response.body = serialize(this.response.body, opt);
}
this.response.type = 'application/json';
} else if (this.response.body || this.response.template) {
if (this.request.query.noTemplate || this.request.json) return;
const templateName = this.request.query.template || this.response.template;
if (templateName) {
this.response.body = this.response.body || {};
@@ -442,10 +443,6 @@ export class Handler {
this.ctx.response.status = 302;
this.ctx.redirect(this.response.redirect);
} else {
if (this.response.redirect) {
this.response.body = this.response.body || {};
this.response.body.url = this.response.redirect;
}
if (this.response.body != null) {
this.ctx.response.body = this.response.body;
this.ctx.response.status = this.response.status || 200;
@@ -757,8 +754,10 @@ export function Middleware(middleware: Koa.Middleware) {
export async function start() {
const [disableLog, port] = await system.getMany(['server.log', 'server.port']);
if (!disableLog) {
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
} else enableLog = false;
app.use(morgan(':method :url :status :res[content-length] - :response-time ms', {
skip: (req, res) => res.hasHeader('nolog'),
}));
}
app.use(router.routes()).use(router.allowedMethods());
Route('notfound_handler', '*', Handler);
server.listen(argv.port || port);