mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core&framework: pass type checks on cordis 4.0.0-alpha.1
This commit is contained in:
1 parent
2c8c03153e
commit
bd63331137
13 files changed
+100
-64
No files matched your search
@@ -0,0 +1,12 @@
|
|||||||
|
*.js text eol=lf
|
||||||
|
*.jsx text eol=lf
|
||||||
|
*.ts text eol=lf
|
||||||
|
*.tsx text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.css text eol=lf
|
||||||
|
*.scss text eol=lf
|
||||||
|
*.less text eol=lf
|
||||||
|
*.html text eol=lf
|
||||||
@@ -1,17 +1,39 @@
|
|||||||
import type { ConnectionHandler, Handler } from './server';
|
import type { ConnectionHandler, Handler, NotFoundHandler } from './server';
|
||||||
import type { Context as CordisContext } from '@cordisjs/core';
|
|
||||||
|
|
||||||
type HandlerEvents<C extends CordisContext> =
|
export interface KnownHandlers {
|
||||||
Record<`handler/${HookType}/${string}`, (thisArg: Handler<C> & Record<string, any>) => VoidReturn>
|
'NotFound': NotFoundHandler;
|
||||||
& Record<`handler/${HookType}`, (thisArg: Handler<C>) => VoidReturn>
|
}
|
||||||
& Record<`handler/register/${string}`, (HandlerClass: new (...args: any[]) => Handler<C>) => VoidReturn>
|
|
||||||
& Record<`connection/${'create' | 'active' | 'close'}`, (thisArg: ConnectionHandler<C>) => VoidReturn>;
|
type MapHandlerEvents<N extends string, H extends Handler<any>> =
|
||||||
|
Record<`handler/${HookType}/${N}`, (thisArg: H) => VoidReturn>
|
||||||
|
& Record<`handler/${HookWithMethod}/${N}/${Methods}`, (thisArg: H) => VoidReturn>;
|
||||||
|
|
||||||
|
type KnownHandlerEvents = {
|
||||||
|
[key in keyof KnownHandlers]: MapHandlerEvents<key, KnownHandlers[key]>
|
||||||
|
}[keyof KnownHandlers];
|
||||||
|
|
||||||
|
type HandlerEvents<C> =
|
||||||
|
Record<`handler/${HookType}`, (thisArg: Handler<C>) => VoidReturn>
|
||||||
|
& Record<`connection/${'create' | 'active' | 'close'}`, (thisArg: any) => VoidReturn>;
|
||||||
|
|
||||||
export type VoidReturn = Promise<any> | any;
|
export type VoidReturn = Promise<any> | any;
|
||||||
export type HookType = 'before-prepare' | 'before' | 'before-operation' | 'after' | 'finish';
|
export type HookType = 'before-prepare' | 'before' | 'before-operation' | 'after' | 'finish';
|
||||||
|
export type Methods = 'get' | 'post' | 'put' | 'delete' | 'patch';
|
||||||
|
export type HookWithMethod = Exclude<HookType, 'before-operation'>;
|
||||||
|
|
||||||
export interface ServerEvents<C extends CordisContext> extends HandlerEvents<C> {
|
export interface ServerEvents<C> extends KnownHandlerEvents, HandlerEvents<C> {
|
||||||
'handler/create': (thisArg: Handler<C>, type: 'ws' | 'http') => VoidReturn
|
'handler/create': (thisArg: Handler<C> | ConnectionHandler<C>, type: 'ws' | 'http') => VoidReturn
|
||||||
|
'handler/create/http': (thisArg: Handler<C>) => VoidReturn
|
||||||
|
'handler/create/ws': (thisArg: ConnectionHandler<C>) => VoidReturn
|
||||||
'handler/init': (thisArg: Handler<C>) => VoidReturn
|
'handler/init': (thisArg: Handler<C>) => VoidReturn
|
||||||
'handler/error': (thisArg: Handler<C>, e: Error) => VoidReturn
|
'handler/error': (thisArg: Handler<C> | ConnectionHandler<C>, e: Error) => VoidReturn
|
||||||
|
[k: `handler/${HookType}/${string}`]: (thisArg: any) => VoidReturn
|
||||||
|
[k: `handler/${HookWithMethod}/${string}/${Methods}`]: (thisArg: any) => VoidReturn
|
||||||
|
[k: `handler/register/${string}`]: (HandlerClass: new (...args: any[]) => any) => VoidReturn
|
||||||
|
[k: `handler/error/${string}`]: (thisArg: Handler<C>, e: Error) => VoidReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@cordisjs/core' {
|
||||||
|
interface Events<C> extends ServerEvents<C> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { HandlerCommon } from './server';
|
import type { HandlerCommon } from './server';
|
||||||
import type { Context } from '@cordisjs/core';
|
|
||||||
|
|
||||||
export default function serializer<T extends Context>(ignoreSerializeFunction = false, h?: HandlerCommon<T>) {
|
export default function serializer<T>(ignoreSerializeFunction = false, h?: HandlerCommon<T>) {
|
||||||
return (k: string, v: any) => {
|
return (k: string, v: any) => {
|
||||||
if (k.startsWith('_') && k !== '_id') return undefined;
|
if (k.startsWith('_') && k !== '_id') return undefined;
|
||||||
if (typeof v === 'bigint') return `BigInt::${v.toString()}`;
|
if (typeof v === 'bigint') return `BigInt::${v.toString()}`;
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ export interface UserModel {
|
|||||||
_id: number;
|
_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HandlerCommon<C extends CordisContext> { }
|
export interface HandlerCommon<C> { } // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
export class HandlerCommon<C extends CordisContext> {
|
export class HandlerCommon<C> {
|
||||||
static [kHandler]: string | boolean = true;
|
static [kHandler]: string | boolean = true;
|
||||||
session: Record<string, any>;
|
session: Record<string, any>;
|
||||||
args: Record<string, any>;
|
args: Record<string, any>;
|
||||||
@@ -143,7 +143,7 @@ export class HandlerCommon<C extends CordisContext> {
|
|||||||
this.request = context.HydroContext.request;
|
this.request = context.HydroContext.request;
|
||||||
this.response = context.HydroContext.response;
|
this.response = context.HydroContext.response;
|
||||||
this.UiContext = context.HydroContext.UiContext;
|
this.UiContext = context.HydroContext.UiContext;
|
||||||
this.ctx = ctx.extend({});
|
this.ctx = (ctx as any).extend({});
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPerm(..._: bigint[]) {
|
checkPerm(..._: bigint[]) {
|
||||||
@@ -184,7 +184,8 @@ export class HandlerCommon<C extends CordisContext> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderHTML(templateName: string, args: Record<string, any>) {
|
renderHTML(templateName: string, args: Record<string, any>) {
|
||||||
const renderers = Object.values(this.ctx.server.renderers).filter((r) => r.accept.includes(templateName) || r.asFallback);
|
const renderers = Object.values((this.ctx as any).server.renderers as Record<string, Renderer>)
|
||||||
|
.filter((r) => r.accept.includes(templateName) || r.asFallback);
|
||||||
const topPrio = renderers.sort((a, b) => b.priority - a.priority)[0];
|
const topPrio = renderers.sort((a, b) => b.priority - a.priority)[0];
|
||||||
const engine = topPrio?.render || (() => JSON.stringify(args, serializer(false, this)));
|
const engine = topPrio?.render || (() => JSON.stringify(args, serializer(false, this)));
|
||||||
return engine(templateName, args, {
|
return engine(templateName, args, {
|
||||||
@@ -196,7 +197,7 @@ export class HandlerCommon<C extends CordisContext> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Handler<C extends CordisContext> extends HandlerCommon<C> {
|
export class Handler<C> extends HandlerCommon<C> {
|
||||||
loginMethods: any;
|
loginMethods: any;
|
||||||
noCheckPermView = false;
|
noCheckPermView = false;
|
||||||
notUsage = false;
|
notUsage = false;
|
||||||
@@ -242,7 +243,7 @@ export class Handler<C extends CordisContext> extends HandlerCommon<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConnectionHandler<C extends CordisContext> extends HandlerCommon<C> {
|
export class ConnectionHandler<C> extends HandlerCommon<C> {
|
||||||
conn: WebSocket;
|
conn: WebSocket;
|
||||||
compression: Shorty;
|
compression: Shorty;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
@@ -279,7 +280,7 @@ export class ConnectionHandler<C extends CordisContext> extends HandlerCommon<C>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotFoundHandler extends Handler<CordisContext> {
|
export class NotFoundHandler extends Handler<CordisContext> {
|
||||||
prepare() { throw new NotFoundError(this.request.path); }
|
prepare() { throw new NotFoundError(this.request.path); }
|
||||||
all() { }
|
all() { }
|
||||||
}
|
}
|
||||||
@@ -477,7 +478,9 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
? `_${ctx.request.body.operation}`.replace(/_([a-z])/gm, (s) => s[1].toUpperCase())
|
? `_${ctx.request.body.operation}`.replace(/_([a-z])/gm, (s) => s[1].toUpperCase())
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
await this.ctx.parallel('handler/create', h, 'http');
|
// FIXME: should pass type check
|
||||||
|
await (this.ctx.parallel as any)('handler/create', h, 'http');
|
||||||
|
await (this.ctx.parallel as any)('handler/create/http', h);
|
||||||
|
|
||||||
if (checker) checker.call(h);
|
if (checker) checker.call(h);
|
||||||
if (method === 'post') {
|
if (method === 'post') {
|
||||||
@@ -513,8 +516,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
const step = steps[current];
|
const step = steps[current];
|
||||||
let control;
|
let control;
|
||||||
if (step.startsWith('log/')) h.args[step.slice(4)] = Date.now();
|
if (step.startsWith('log/')) h.args[step.slice(4)] = Date.now();
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// @ts-ignore
|
||||||
else if (step.startsWith('handler/')) control = await this.ctx.serial(step as any, h);
|
else if (step.startsWith('handler/')) control = await this.ctx.serial(step, h); // eslint-disable-line no-await-in-loop
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
else if (typeof h[step] === 'function') control = await h[step](args);
|
else if (typeof h[step] === 'function') control = await h[step](args);
|
||||||
if (control) {
|
if (control) {
|
||||||
@@ -528,8 +531,9 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
await this.ctx.serial(`handler/error/${name}`, h, e);
|
// FIXME: should pass type check
|
||||||
await this.ctx.serial('handler/error', h, e);
|
await (this.ctx.serial as any)(`handler/error/${name}`, h, e);
|
||||||
|
await (this.ctx.serial as any)('handler/error', h, e);
|
||||||
await h.onerror(e);
|
await h.onerror(e);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
@@ -543,7 +547,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
private async handleWS(ctx: KoaContext, HandlerClass, checker, conn, layer?) {
|
private async handleWS(ctx: KoaContext, HandlerClass, checker, conn, layer?) {
|
||||||
const { args } = ctx.HydroContext;
|
const { args } = ctx.HydroContext;
|
||||||
const h = new HandlerClass(ctx, this.ctx);
|
const h = new HandlerClass(ctx, this.ctx);
|
||||||
await this.ctx.parallel('connection/create', h);
|
// FIXME: should pass type check
|
||||||
|
await (this.ctx.parallel as any)('connection/create', h);
|
||||||
const stream = new PassThrough();
|
const stream = new PassThrough();
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
// By HTTP
|
// By HTTP
|
||||||
@@ -571,7 +576,9 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
h.conn = conn;
|
h.conn = conn;
|
||||||
const disposables = [];
|
const disposables = [];
|
||||||
try {
|
try {
|
||||||
await this.ctx.parallel('handler/create', h, 'ws');
|
// FIXME: should pass type check
|
||||||
|
await (this.ctx.parallel as any)('handler/create', h, 'ws');
|
||||||
|
await (this.ctx.parallel as any)('handler/create/ws', h);
|
||||||
checker.call(h);
|
checker.call(h);
|
||||||
if (args.shorty) h.resetCompression();
|
if (args.shorty) h.resetCompression();
|
||||||
if (h._prepare) await h._prepare(args);
|
if (h._prepare) await h._prepare(args);
|
||||||
@@ -583,7 +590,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
const clean = () => {
|
const clean = () => {
|
||||||
if (closed) return;
|
if (closed) return;
|
||||||
closed = true;
|
closed = true;
|
||||||
this.ctx.emit('connection/close', h);
|
// FIXME: should pass type check
|
||||||
|
(this.ctx.emit as any)('connection/close', h);
|
||||||
if (layer) layer.clients.delete(conn);
|
if (layer) layer.clients.delete(conn);
|
||||||
if (interval) clearInterval(interval);
|
if (interval) clearInterval(interval);
|
||||||
for (const d of disposables) d();
|
for (const d of disposables) d();
|
||||||
@@ -621,7 +629,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else ctx.body = stream;
|
} else ctx.body = stream;
|
||||||
await this.ctx.parallel('connection/active', h);
|
// FIXME: should pass type check
|
||||||
|
await (this.ctx.parallel as any)('connection/active', h as any);
|
||||||
if (layer) {
|
if (layer) {
|
||||||
if (conn.readyState === conn.OPEN) {
|
if (conn.readyState === conn.OPEN) {
|
||||||
conn.on('close', clean);
|
conn.on('close', clean);
|
||||||
@@ -643,11 +652,11 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
this.registry[name] = HandlerClass;
|
this.registry[name] = HandlerClass;
|
||||||
this.registrationCount[name]++;
|
this.registrationCount[name]++;
|
||||||
|
|
||||||
const Checker = (permPrivChecker) => {
|
const Checker = (args) => {
|
||||||
let perm: bigint;
|
let perm: bigint;
|
||||||
let priv: number;
|
let priv: number;
|
||||||
let checker = () => { };
|
let checker = () => { };
|
||||||
for (const item of permPrivChecker) {
|
for (const item of args) {
|
||||||
if (typeof item === 'object') {
|
if (typeof item === 'object') {
|
||||||
if (typeof item.call !== 'undefined') {
|
if (typeof item.call !== 'undefined') {
|
||||||
checker = item;
|
checker = item;
|
||||||
@@ -692,7 +701,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
name: T, callback: (HandlerClass: T extends `${string}ConnectionHandler` ? typeof ConnectionHandler<C> : typeof Handler<C>) => any,
|
name: T, callback: (HandlerClass: T extends `${string}ConnectionHandler` ? typeof ConnectionHandler<C> : typeof Handler<C>) => any,
|
||||||
) {
|
) {
|
||||||
if (this.registry[name]) callback(this.registry[name]);
|
if (this.registry[name]) callback(this.registry[name]);
|
||||||
this.ctx.on(`handler/register/${name}`, callback);
|
// FIXME: should pass type check
|
||||||
|
this.ctx.on(`handler/register/${name}`, callback as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: should be typeof Handler<Context> instead of any
|
// FIXME: should be typeof Handler<Context> instead of any
|
||||||
|
|||||||
+2
-2
@@ -20,8 +20,8 @@
|
|||||||
"build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production",
|
"build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production",
|
||||||
"test": "node -r @hydrooj/register packages/common/tests/subtask.spec.ts && node test/entry.js",
|
"test": "node -r @hydrooj/register packages/common/tests/subtask.spec.ts && node test/entry.js",
|
||||||
"benchmark": "cross-env BENCHMARK=true node test/entry.js",
|
"benchmark": "cross-env BENCHMARK=true node test/entry.js",
|
||||||
"lint": "eslint packages plugins modules --ext .ts,.tsx,.jsx --fix",
|
"lint": "eslint packages plugins modules framework --ext .ts,.tsx,.jsx --fix",
|
||||||
"lint:ci": "eslint packages plugins modules --ext .ts,.tsx,.jsx --max-warnings=0",
|
"lint:ci": "eslint packages plugins modules framework --ext .ts,.tsx,.jsx --max-warnings=0",
|
||||||
"lint:ui": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --fix",
|
"lint:ui": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --fix",
|
||||||
"lint:ui:ci": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --max-warnings=0",
|
"lint:ui:ci": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --max-warnings=0",
|
||||||
"debug": "node --trace-warnings --async-stack-traces --trace-deprecation packages/hydrooj/bin/hydrooj --debug --template",
|
"debug": "node --trace-warnings --async-stack-traces --trace-deprecation packages/hydrooj/bin/hydrooj --debug --template",
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
"@aws-sdk/lib-storage": "3.726.1",
|
"@aws-sdk/lib-storage": "3.726.1",
|
||||||
"@aws-sdk/s3-presigned-post": "3.726.1",
|
"@aws-sdk/s3-presigned-post": "3.726.1",
|
||||||
"@aws-sdk/s3-request-presigner": "3.726.1",
|
"@aws-sdk/s3-request-presigner": "3.726.1",
|
||||||
"@cordisjs/core": "4.0.0-alpha.0",
|
"@cordisjs/core": "4.0.0-alpha.1",
|
||||||
"@cordisjs/logger": "1.0.0-alpha.0",
|
"@cordisjs/logger": "1.0.0-alpha.1",
|
||||||
"@cordisjs/plugin-timer": "1.0.0-alpha.0",
|
"@cordisjs/plugin-timer": "1.0.0-alpha.0",
|
||||||
"@graphql-tools/schema": "^10.0.18",
|
"@graphql-tools/schema": "^10.0.18",
|
||||||
"@hydrooj/common": "workspace:^",
|
"@hydrooj/common": "workspace:^",
|
||||||
|
|||||||
@@ -2,16 +2,15 @@ import * as cordis from '@cordisjs/core';
|
|||||||
import { Logger } from '@cordisjs/logger';
|
import { Logger } from '@cordisjs/logger';
|
||||||
import { TimerService } from '@cordisjs/plugin-timer';
|
import { TimerService } from '@cordisjs/plugin-timer';
|
||||||
import Schema from 'schemastery';
|
import Schema from 'schemastery';
|
||||||
import type { ServerEvents, WebService } from '@hydrooj/framework';
|
|
||||||
import type { DomainDoc, GeoIP, ModuleInterfaces } from './interface';
|
import type { DomainDoc, GeoIP, ModuleInterfaces } from './interface';
|
||||||
import { inject } from './lib/ui';
|
import { inject } from './lib/ui';
|
||||||
import { Loader } from './loader';
|
import { Loader } from './loader';
|
||||||
import type { EventMap } from './service/bus';
|
import type { EventMap } from './service/bus';
|
||||||
import type { CheckService } from './service/check';
|
import type { CheckService } from './service/check';
|
||||||
import type { } from './service/migration';
|
import type { } from './service/migration';
|
||||||
import type { ConnectionHandler, Handler } from './service/server';
|
|
||||||
|
|
||||||
export interface Events<C extends Context = Context> extends cordis.Events<C>, EventMap { }
|
// TODO: this is an broken declaration
|
||||||
|
export { EventMap as Events };
|
||||||
|
|
||||||
function addScript<K>(name: string, description: string, validate: Schema<K>, run: (args: K, report: any) => boolean | Promise<boolean>) {
|
function addScript<K>(name: string, description: string, validate: Schema<K>, run: (args: K, report: any) => boolean | Promise<boolean>) {
|
||||||
if (global.Hydro.script[name]) throw new Error(`duplicate script ${name} registered.`);
|
if (global.Hydro.script[name]) throw new Error(`duplicate script ${name} registered.`);
|
||||||
@@ -30,8 +29,7 @@ export type EffectScope = cordis.EffectScope<Context>;
|
|||||||
export { Disposable, Plugin, ScopeStatus } from '@cordisjs/core';
|
export { Disposable, Plugin, ScopeStatus } from '@cordisjs/core';
|
||||||
|
|
||||||
export interface Context extends cordis.Context {
|
export interface Context extends cordis.Context {
|
||||||
// @ts-ignore
|
[Context.events]: EventMap & cordis.Events<Context>;
|
||||||
[Context.events]: Events<this> & ServerEvents<this>;
|
|
||||||
loader: Loader;
|
loader: Loader;
|
||||||
check: CheckService;
|
check: CheckService;
|
||||||
setImmediate: typeof setImmediate;
|
setImmediate: typeof setImmediate;
|
||||||
@@ -53,7 +51,7 @@ const T = <F extends (...args: any[]) => any>(origFunc: F, disposeFunc?) =>
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ApiMixin extends cordis.Service {
|
export class ApiMixin extends cordis.Service<Context> {
|
||||||
addScript = T(addScript);
|
addScript = T(addScript);
|
||||||
setImmediate = T(setImmediate, clearImmediate);
|
setImmediate = T(setImmediate, clearImmediate);
|
||||||
provideModule = T(provideModule);
|
provideModule = T(provideModule);
|
||||||
|
|||||||
@@ -2,11 +2,9 @@
|
|||||||
import {
|
import {
|
||||||
BSON, Db, Filter, ObjectId, OnlyFieldsOfType,
|
BSON, Db, Filter, ObjectId, OnlyFieldsOfType,
|
||||||
} from 'mongodb';
|
} from 'mongodb';
|
||||||
import type { Handler, ServerEvents } from '@hydrooj/framework';
|
import type { Handler } from '@hydrooj/framework';
|
||||||
import pm2 from '@hydrooj/utils/lib/locate-pm2';
|
import pm2 from '@hydrooj/utils/lib/locate-pm2';
|
||||||
import { Context } from '../context';
|
import { Context } from '../context';
|
||||||
import type { ProblemSolutionHandler } from '../handler/problem';
|
|
||||||
import type { UserRegisterHandler } from '../handler/user';
|
|
||||||
import type {
|
import type {
|
||||||
BaseUserDict, ContestBalloonDoc, DiscussionDoc, DomainDoc, FileInfo,
|
BaseUserDict, ContestBalloonDoc, DiscussionDoc, DomainDoc, FileInfo,
|
||||||
MessageDoc, ProblemDict, ProblemDoc, RecordDoc,
|
MessageDoc, ProblemDict, ProblemDoc, RecordDoc,
|
||||||
@@ -16,14 +14,8 @@ import type { DocType } from '../model/document';
|
|||||||
|
|
||||||
export type Disposable = () => void;
|
export type Disposable = () => void;
|
||||||
export type VoidReturn = Promise<any> | any;
|
export type VoidReturn = Promise<any> | any;
|
||||||
type HookType = 'before-prepare' | 'before' | 'before-operation' | 'after' | 'finish';
|
|
||||||
type MapHandlerEvents<N extends string, H extends Handler<Context>> = Record<`handler/${HookType}/${N}`, (thisArg: H) => VoidReturn>;
|
|
||||||
type KnownHandlerEvents =
|
|
||||||
MapHandlerEvents<'UserRegister', UserRegisterHandler>
|
|
||||||
& MapHandlerEvents<'ProblemSolution', ProblemSolutionHandler>;
|
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
export interface EventMap {
|
||||||
export interface EventMap extends KnownHandlerEvents {
|
|
||||||
'app/listen': () => void
|
'app/listen': () => void
|
||||||
'app/started': () => void
|
'app/started': () => void
|
||||||
'app/ready': () => VoidReturn
|
'app/ready': () => VoidReturn
|
||||||
@@ -38,7 +30,7 @@ export interface EventMap extends KnownHandlerEvents {
|
|||||||
'database/config': () => VoidReturn
|
'database/config': () => VoidReturn
|
||||||
|
|
||||||
'system/setting': (args: Record<string, any>) => VoidReturn
|
'system/setting': (args: Record<string, any>) => VoidReturn
|
||||||
'bus/broadcast': (event: keyof EventMap | keyof ServerEvents<Context>, payload: any, trace?: string) => VoidReturn
|
'bus/broadcast': (event: keyof EventMap, payload: any, trace?: string) => VoidReturn
|
||||||
'monitor/update': (type: 'server' | 'judge', $set: any) => VoidReturn
|
'monitor/update': (type: 'server' | 'judge', $set: any) => VoidReturn
|
||||||
'monitor/collect': (info: any) => VoidReturn
|
'monitor/collect': (info: any) => VoidReturn
|
||||||
'api/update': () => void;
|
'api/update': () => void;
|
||||||
@@ -101,11 +93,6 @@ export interface EventMap extends KnownHandlerEvents {
|
|||||||
'record/change': (rdoc: RecordDoc, $set?: any, $push?: any, body?: any) => void
|
'record/change': (rdoc: RecordDoc, $set?: any, $push?: any, body?: any) => void
|
||||||
'record/judge': (rdoc: RecordDoc, updated: boolean, pdoc?: ProblemDoc) => VoidReturn
|
'record/judge': (rdoc: RecordDoc, updated: boolean, pdoc?: ProblemDoc) => VoidReturn
|
||||||
}
|
}
|
||||||
/* eslint-enable @typescript-eslint/naming-convention */
|
|
||||||
|
|
||||||
declare module '@cordisjs/core' {
|
|
||||||
interface Events extends EventMap { }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function apply(ctx: Context) {
|
export function apply(ctx: Context) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export default class HMR extends Service {
|
|||||||
this.analyzeChanges();
|
this.analyzeChanges();
|
||||||
|
|
||||||
/** plugins pending classification */
|
/** plugins pending classification */
|
||||||
const pending = new Map<string, Plugin.Runtime<Context>>();
|
const pending = new Map<string, Plugin>();
|
||||||
|
|
||||||
/** plugins that should be reloaded */
|
/** plugins that should be reloaded */
|
||||||
const reloads = new Map<Plugin, { filename: string, runtime: Plugin.Runtime<Context> }>();
|
const reloads = new Map<Plugin, { filename: string, runtime: Plugin.Runtime<Context> }>();
|
||||||
@@ -161,15 +161,16 @@ export default class HMR extends Service {
|
|||||||
if (typeof plugin !== 'object' || !plugin || !('apply' in plugin)) continue;
|
if (typeof plugin !== 'object' || !plugin || !('apply' in plugin)) continue;
|
||||||
const runtime = this.ctx.registry.get(plugin);
|
const runtime = this.ctx.registry.get(plugin);
|
||||||
if (!runtime || this.declined.has(filename)) continue;
|
if (!runtime || this.declined.has(filename)) continue;
|
||||||
pending.set(filename, runtime);
|
pending.set(filename, plugin);
|
||||||
if (!plugin || !('sideEffect' in plugin) || !plugin['sideEffect']) this.declined.add(filename);
|
if (!plugin || !('sideEffect' in plugin) || !plugin['sideEffect']) this.declined.add(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [filename, runtime] of pending) {
|
for (const [filename, plugin] of pending) {
|
||||||
// check if it is a dependent of the changed file
|
// check if it is a dependent of the changed file
|
||||||
this.declined.delete(filename);
|
this.declined.delete(filename);
|
||||||
const dependencies = [...loadDependencies(filename, this.declined)];
|
const dependencies = [...loadDependencies(filename, this.declined)];
|
||||||
if (!runtime.plugin['sideEffect']) this.declined.add(filename);
|
const runtime = this.ctx.registry.get(plugin);
|
||||||
|
if (!plugin['sideEffect']) this.declined.add(filename);
|
||||||
|
|
||||||
// we only detect reloads at plugin level
|
// we only detect reloads at plugin level
|
||||||
// a plugin will be reloaded if any of its dependencies are accepted
|
// a plugin will be reloaded if any of its dependencies are accepted
|
||||||
@@ -177,7 +178,7 @@ export default class HMR extends Service {
|
|||||||
for (const dep of dependencies) this.accepted.add(dep);
|
for (const dep of dependencies) this.accepted.add(dep);
|
||||||
|
|
||||||
// prepare for reload
|
// prepare for reload
|
||||||
reloads.set(runtime.plugin, {
|
reloads.set(plugin, {
|
||||||
filename,
|
filename,
|
||||||
runtime,
|
runtime,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const ignoredLimit = `,${argv.options.ignoredLimit},`;
|
|||||||
const logger = new Logger('server');
|
const logger = new Logger('server');
|
||||||
|
|
||||||
declare module '@hydrooj/framework' {
|
declare module '@hydrooj/framework' {
|
||||||
export interface HandlerCommon<C> {
|
export interface HandlerCommon<C> { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
domain: DomainDoc;
|
domain: DomainDoc;
|
||||||
|
|
||||||
paginate<T>(cursor: FindCursor<T>, page: number, key: string): Promise<[docs: T[], numPages: number, count: number]>;
|
paginate<T>(cursor: FindCursor<T>, page: number, key: string): Promise<[docs: T[], numPages: number, count: number]>;
|
||||||
@@ -277,6 +277,8 @@ export async function apply(ctx: Context) {
|
|||||||
h.ctx = h.ctx.extend({
|
h.ctx = h.ctx.extend({
|
||||||
domain: h.domain,
|
domain: h.domain,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
on('handler/create/http', async (h) => {
|
||||||
if (!argv.options.benchmark && !h.notUsage) await h.limitRate('global', 5, 100);
|
if (!argv.options.benchmark && !h.notUsage) await h.limitRate('global', 5, 100);
|
||||||
h.loginMethods = Object.keys(global.Hydro.module.oauth)
|
h.loginMethods = Object.keys(global.Hydro.module.oauth)
|
||||||
.map((key) => ({
|
.map((key) => ({
|
||||||
@@ -287,6 +289,9 @@ export async function apply(ctx: Context) {
|
|||||||
if (!h.noCheckPermView && !h.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) h.checkPerm(PERM.PERM_VIEW);
|
if (!h.noCheckPermView && !h.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) h.checkPerm(PERM.PERM_VIEW);
|
||||||
if (h.context.pendingError) throw h.context.pendingError;
|
if (h.context.pendingError) throw h.context.pendingError;
|
||||||
});
|
});
|
||||||
|
on('handler/create/ws', async (h) => {
|
||||||
|
if (h.context.pendingError) throw h.context.pendingError;
|
||||||
|
});
|
||||||
|
|
||||||
on('app/listen', () => {
|
on('app/listen', () => {
|
||||||
server.listen();
|
server.listen();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
ForbiddenError, fs, ObjectId, parseTimeMS, PERM, ProblemConfig, ProblemModel,
|
ForbiddenError, fs, ObjectId, parseTimeMS, PERM, ProblemConfig, ProblemModel,
|
||||||
STATUS, STATUS_SHORT_TEXTS, STATUS_TEXTS, Time, UserModel,
|
STATUS, STATUS_SHORT_TEXTS, STATUS_TEXTS, Time, UserModel,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
import type { } from 'hydrooj/src/handler/contest';
|
||||||
import { ResolverInput } from './interface';
|
import { ResolverInput } from './interface';
|
||||||
|
|
||||||
interface IpLoginInfo {
|
interface IpLoginInfo {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import path from 'path';
|
|||||||
import {
|
import {
|
||||||
ContestModel, Context, fs, Handler, ObjectId, PERM, Schema, STATUS, Types, UserModel,
|
ContestModel, Context, fs, Handler, ObjectId, PERM, Schema, STATUS, Types, UserModel,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
import type { } from 'hydrooj/src/handler/contest';
|
||||||
|
|
||||||
const file = fs.readFileSync(path.join(__dirname, 'public/assets/board.html'), 'utf8');
|
const file = fs.readFileSync(path.join(__dirname, 'public/assets/board.html'), 'utf8');
|
||||||
const indexJs = file.match(/index-([A-Za-z0-9_-]+)\.js"/)?.[1];
|
const indexJs = file.match(/index-([A-Za-z0-9_-]+)\.js"/)?.[1];
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@blueprintjs/core": "^5.16.6",
|
"@blueprintjs/core": "^5.16.6",
|
||||||
"@cordisjs/core": "4.0.0-alpha.0",
|
"@cordisjs/core": "4.0.0-alpha.1",
|
||||||
"@fontsource/dm-mono": "^5.1.1",
|
"@fontsource/dm-mono": "^5.1.1",
|
||||||
"@fontsource/fira-code": "^5.1.1",
|
"@fontsource/fira-code": "^5.1.1",
|
||||||
"@fontsource/inconsolata": "^5.1.1",
|
"@fontsource/inconsolata": "^5.1.1",
|
||||||
|
|||||||
Reference in new issue
Block a user