mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: api: allow extend
This commit is contained in:
1 parent
97b262f997
commit
9eece289e0
3 files changed
+96
-21
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.33.4",
|
"version": "2.33.5",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "src/loader",
|
"main": "src/loader",
|
||||||
"module": "src/loader",
|
"module": "src/loader",
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"detect-browser": "^5.2.0",
|
"detect-browser": "^5.2.0",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"graphql": "^15.5.1",
|
"graphql": "^15.5.1",
|
||||||
|
"graphql-scalars": "^1.10.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"koa": "^2.13.1",
|
"koa": "^2.13.1",
|
||||||
"koa-body": "^4.2.0",
|
"koa-body": "^4.2.0",
|
||||||
|
|||||||
@@ -1,34 +1,78 @@
|
|||||||
import graphql from 'graphql';
|
import graphql from 'graphql';
|
||||||
import UserModel from '../model/user';
|
import { typeDefs, resolvers } from 'graphql-scalars';
|
||||||
import { Handler, Route } from '../service/server';
|
import { Handler, Route } from '../service/server';
|
||||||
|
import * as bus from '../service/bus';
|
||||||
|
|
||||||
const schemaStr = `\
|
const types: Record<string, Record<string, string>> = {};
|
||||||
type Query {
|
const descriptions: Record<string, Record<string, string>> = {};
|
||||||
user(id: Int, uname: String, mail: String): User
|
const handlers: Record<string, Record<string, any>> = {};
|
||||||
problem(id: Int): String
|
let root: Record<string, any> = {};
|
||||||
|
|
||||||
|
export function registerValue(typeName: string, key: string, value: string, description?: string): void;
|
||||||
|
export function registerValue(typeName: string, vals: [string, string, string?][]): void;
|
||||||
|
export function registerValue(typeName: string, arg1: [string, string, string?][] | string, value?: string, description?: string) {
|
||||||
|
if (typeof arg1 === 'string') arg1 = [[arg1, value!, description]];
|
||||||
|
for (const [k, v, d] of arg1) {
|
||||||
|
if (!types[typeName]) types[typeName] = { [k]: v };
|
||||||
|
else types[typeName][k] = v;
|
||||||
|
if (d) {
|
||||||
|
if (!descriptions[typeName]) descriptions[typeName] = { [k]: d };
|
||||||
|
else descriptions[typeName][k] = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bus.emit('api/update');
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
export function registerResolver(typeName: string, key: string, value: string, func: Function, description?: string) {
|
||||||
_id: Int!
|
registerValue(typeName, key, value, description);
|
||||||
uname: String!
|
if (handlers[typeName]) handlers[typeName][key.split('(')[0].trim()] = func;
|
||||||
mail: String!
|
else handlers[typeName] = { [key.split('(')[0].trim()]: func };
|
||||||
}`;
|
bus.emit('api/update');
|
||||||
|
}
|
||||||
|
|
||||||
const schema = graphql.buildSchema(schemaStr);
|
function setDescription(desc: string) {
|
||||||
const root = {
|
if (desc.includes('\n')) return ['"""', desc, '"""'].join('\n');
|
||||||
user: (arg, ctx) => {
|
return JSON.stringify(desc);
|
||||||
if (arg.id) return UserModel.getById(ctx.domainId, arg.id);
|
}
|
||||||
if (arg.mail) return UserModel.getByEmail(ctx.domainId, arg.mail);
|
|
||||||
if (arg.uname) return UserModel.getByUname(ctx.domainId, arg.uname);
|
function buildSchemaStr() {
|
||||||
return ctx.user;
|
let res = `${typeDefs.join('\n')}\n`;
|
||||||
},
|
for (const key in types) {
|
||||||
problem: async () => { },
|
if (descriptions[key]._description) res += `${setDescription(descriptions[key]._description)}\n`;
|
||||||
};
|
res += `type ${key}{\n`;
|
||||||
|
for (const k in types[key]) {
|
||||||
|
if (descriptions[key][k]) res += ` ${setDescription(descriptions[key][k])}\n`;
|
||||||
|
res += ` ${k}: ${types[key][k]}\n`;
|
||||||
|
}
|
||||||
|
res += '}\n';
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
let schemaStr = buildSchemaStr();
|
||||||
|
let schema = graphql.buildSchema(schemaStr);
|
||||||
|
root = { ...handlers.Query, ...resolvers };
|
||||||
|
|
||||||
|
export function rebuild() {
|
||||||
|
try {
|
||||||
|
const str = buildSchemaStr();
|
||||||
|
schema = graphql.buildSchema(str);
|
||||||
|
schemaStr = str;
|
||||||
|
root = { ...handlers.Query, ...resolvers };
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bus.on('app/started', () => {
|
||||||
|
rebuild();
|
||||||
|
bus.on('api/update', rebuild);
|
||||||
|
});
|
||||||
|
|
||||||
class ApiHandler extends Handler {
|
class ApiHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
const q = decodeURIComponent(this.ctx.request.querystring);
|
const q = decodeURIComponent(this.ctx.request.querystring);
|
||||||
if (q === 'schema') {
|
if (q === 'schema') {
|
||||||
|
this.response.type = 'application/json';
|
||||||
this.response.body = { schema: schemaStr };
|
this.response.body = { schema: schemaStr };
|
||||||
} else if (q) {
|
} else if (q) {
|
||||||
this.response.type = 'application/json';
|
this.response.type = 'application/json';
|
||||||
@@ -38,6 +82,7 @@ class ApiHandler extends Handler {
|
|||||||
|
|
||||||
async post() {
|
async post() {
|
||||||
this.response.type = 'application/json';
|
this.response.type = 'application/json';
|
||||||
|
// FIXME validation for fields like ObjectID doesn't work.
|
||||||
this.response.body = await graphql.graphql(schema, this.args.query, root, this, this.args.variables);
|
this.response.body = await graphql.graphql(schema, this.args.query, root, this, this.args.variables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
|
import { registerResolver, registerValue } from './api';
|
||||||
import {
|
import {
|
||||||
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
|
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
|
||||||
UserNotFoundError, SystemError, BlacklistedError,
|
UserNotFoundError, SystemError, BlacklistedError,
|
||||||
@@ -22,6 +23,34 @@ import { sendMail } from '../lib/mail';
|
|||||||
import paginate from '../lib/paginate';
|
import paginate from '../lib/paginate';
|
||||||
import avatar from '../lib/avatar';
|
import avatar from '../lib/avatar';
|
||||||
|
|
||||||
|
registerValue('User', [
|
||||||
|
['_id', 'Int!'],
|
||||||
|
['uname', 'String!'],
|
||||||
|
['mail', 'String!'],
|
||||||
|
['perm', 'Int'],
|
||||||
|
['scope', 'Int'],
|
||||||
|
['role', 'String'],
|
||||||
|
['loginat', 'Date'],
|
||||||
|
['regat', 'Date'],
|
||||||
|
['priv', 'Int', 'User Privilege'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
registerResolver('Query', 'user(id: Int, uname: String, mail: String)', 'User', (arg, ctx) => {
|
||||||
|
if (arg.id) return user.getById(ctx.domainId, arg.id);
|
||||||
|
if (arg.mail) return user.getByEmail(ctx.domainId, arg.mail);
|
||||||
|
if (arg.uname) return user.getByUname(ctx.domainId, arg.uname);
|
||||||
|
return ctx.user;
|
||||||
|
}, `Get a user by id, uname, or mail.
|
||||||
|
Returns current user if no argument is provided.`);
|
||||||
|
|
||||||
|
registerResolver('Query', 'users(ids: [Int], search: String)', '[User]', async (arg, ctx) => {
|
||||||
|
if (arg.ids) {
|
||||||
|
const res = await user.getList(ctx.domainId, arg.ids);
|
||||||
|
return Object.keys(res).map((id) => res[+id]);
|
||||||
|
}
|
||||||
|
return arg.search ? await user.getPrefixList(ctx.domainId, arg.search) : [];
|
||||||
|
}, 'Get a list of user by ids, or search users with the prefix.');
|
||||||
|
|
||||||
class UserLoginHandler extends Handler {
|
class UserLoginHandler extends Handler {
|
||||||
noCheckPermView = true;
|
noCheckPermView = true;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user