mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
core&ui: drop graphql apis
This commit is contained in:
1 parent
0f7961d675
commit
6e16d03f67
52 files changed
+485
-583
No files matched your search
@@ -742,33 +742,6 @@ export async function apply(ctx: Context) {
|
||||
}
|
||||
await Promise.all(tasks);
|
||||
});
|
||||
ctx.inject(['api'], ({ api }) => {
|
||||
api.value('Contest', [
|
||||
['_id', 'ObjectID!'],
|
||||
['domainId', 'String!'],
|
||||
['docId', 'ObjectID!'],
|
||||
['owner', 'Int!'],
|
||||
['beginAt', 'Date!'],
|
||||
['title', 'String!'],
|
||||
['content', 'String!'],
|
||||
['beginAt', 'Date!'],
|
||||
['endAt', 'Date!'],
|
||||
['attend', 'Int!'],
|
||||
['pids', '[Int]!'],
|
||||
['rated', 'Boolean!'],
|
||||
]);
|
||||
api.resolver(
|
||||
'Query', 'contest(id: ObjectID!)', 'Contest',
|
||||
async (arg, c) => {
|
||||
c.checkPerm(PERM.PERM_VIEW);
|
||||
arg.id = new ObjectId(arg.id);
|
||||
c.tdoc = await contest.get(c.args.domainId, new ObjectId(arg.id));
|
||||
if (!c.tdoc) throw new ContestNotFoundError(c.args.domainId, arg.id);
|
||||
return c.tdoc;
|
||||
},
|
||||
'Get a contest by ID',
|
||||
);
|
||||
});
|
||||
ctx.plugin(ScoreboardService);
|
||||
ctx.inject(['scoreboard'], ({ Route, scoreboard }) => {
|
||||
Route('contest_scoreboard', '/contest/:tid/scoreboard', ContestScoreboardHandler, PERM.PERM_VIEW_CONTEST_SCOREBOARD);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { load } from 'js-yaml';
|
||||
import { Dictionary } from 'lodash';
|
||||
import moment from 'moment-timezone';
|
||||
import Schema from 'schemastery';
|
||||
import type { Context } from '../context';
|
||||
import {
|
||||
CannotDeleteSystemDomainError, DomainJoinAlreadyMemberError, DomainJoinForbiddenError, ForbiddenError,
|
||||
@@ -17,7 +18,7 @@ import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
|
||||
import * as system from '../model/system';
|
||||
import user from '../model/user';
|
||||
import {
|
||||
Handler, param, post, query, requireSudo, Types,
|
||||
Handler, Mutation, param, post, Query, query, requireSudo, Types,
|
||||
} from '../service/server';
|
||||
import { log2 } from '../utils';
|
||||
|
||||
@@ -412,6 +413,42 @@ class DomainSearchHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
export const DomainApi = {
|
||||
domain: Query(
|
||||
Schema.object({
|
||||
id: Schema.string(),
|
||||
}),
|
||||
async (ctx, args) => {
|
||||
const ddoc = args.id ? await domain.get(args.id) : ctx.domain;
|
||||
if (!ddoc) return null;
|
||||
const udoc = await user.getById(ddoc._id, ctx.user._id);
|
||||
if (!udoc.hasPerm(PERM.PERM_VIEW) && !udoc.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) return null;
|
||||
return ddoc;
|
||||
},
|
||||
),
|
||||
'domain.group': Mutation(
|
||||
Schema.object({
|
||||
name: Schema.string().required(),
|
||||
uids: Schema.array(Schema.number()),
|
||||
}),
|
||||
async (ctx, args) => {
|
||||
if (!ctx.user.hasPerm(PERM.PERM_EDIT_DOMAIN)) throw new PermissionError(PERM.PERM_EDIT_DOMAIN);
|
||||
if (args.uids) {
|
||||
const res = await user.updateGroup(ctx.domain._id, args.name, args.uids);
|
||||
return res.upsertedCount > 0;
|
||||
}
|
||||
const res = await user.delGroup(ctx.domain._id, args.name);
|
||||
return res.deletedCount > 0;
|
||||
},
|
||||
),
|
||||
} as const;
|
||||
|
||||
declare module '@hydrooj/framework' {
|
||||
interface Apis {
|
||||
domain: typeof DomainApi;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
ctx.Route('ranking', '/ranking', DomainRankHandler, PERM.PERM_VIEW_RANKING);
|
||||
ctx.Route('domain_dashboard', '/domain/dashboard', DomainDashboardHandler);
|
||||
@@ -424,34 +461,6 @@ export async function apply(ctx: Context) {
|
||||
ctx.Route('domain_join', '/domain/join', DomainJoinHandler, PRIV.PRIV_USER_PROFILE);
|
||||
ctx.Route('domain_search', '/domain/search', DomainSearchHandler, PRIV.PRIV_USER_PROFILE);
|
||||
ctx.inject(['api'], ({ api }) => {
|
||||
api.value('GroupInfo', [
|
||||
['name', 'String!'],
|
||||
['uids', '[Int]!'],
|
||||
]);
|
||||
api.resolver('Query', 'domain(id: String)', 'Domain', async (args, c) => {
|
||||
const ddoc = args.id ? await domain.get(args.id) : c.domain;
|
||||
if (!ddoc) return null;
|
||||
const udoc = await user.getById(ddoc._id, c.user._id);
|
||||
if (!udoc.hasPerm(PERM.PERM_VIEW) && !udoc.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) return null;
|
||||
c.udoc = udoc;
|
||||
return ddoc;
|
||||
});
|
||||
api.resolver('Domain', 'manage', 'DomainManage', async (args, c) => {
|
||||
if (!c.udoc.hasPerm(PERM.PERM_EDIT_DOMAIN)) throw new PermissionError(PERM.PERM_EDIT_DOMAIN);
|
||||
return c.parent;
|
||||
});
|
||||
api.resolver('DomainManage', 'group', 'DomainGroup', (args, c) => c.parent);
|
||||
api.resolver(
|
||||
'DomainGroup', 'list(uid: Int)', '[GroupInfo]',
|
||||
(args, c) => user.listGroup(c.parent._id, args.uid),
|
||||
);
|
||||
api.resolver(
|
||||
'DomainGroup', 'update(name: String!, uids: [Int]!)', 'Boolean',
|
||||
async (args, c) => !!(await user.updateGroup(c.parent._id, args.name, args.uids)).upsertedCount,
|
||||
);
|
||||
api.resolver(
|
||||
'DomainGroup', 'del(name: String!)', 'Boolean',
|
||||
async (args, c) => !!(await user.delGroup(c.parent._id, args.name)).deletedCount,
|
||||
);
|
||||
api.provide(DomainApi);
|
||||
});
|
||||
}
|
||||
@@ -321,9 +321,12 @@ const allPriv = Math.sum(Object.values(Priv));
|
||||
|
||||
class SystemUserPrivHandler extends SystemHandler {
|
||||
@requireSudo
|
||||
async get() {
|
||||
@param('extraIgnore', Types.NumericArray, true)
|
||||
async get({ }, extraIgnore: number[] = []) {
|
||||
const defaultPriv = system.get('default.priv');
|
||||
const udocs = await user.getMulti({ _id: { $gte: -1000, $ne: 1 }, priv: { $nin: [0, defaultPriv] } }).limit(1000).sort({ _id: 1 }).toArray();
|
||||
const udocs = await user.getMulti({
|
||||
_id: { $gte: -1000, $ne: 1 }, priv: { $nin: [0, defaultPriv, ...extraIgnore] },
|
||||
}).limit(1000).sort({ _id: 1 }).toArray();
|
||||
const banudocs = await user.getMulti({ _id: { $gte: -1000, $ne: 1 }, priv: 0 }).limit(1000).sort({ _id: 1 }).toArray();
|
||||
this.response.body = {
|
||||
udocs: [...udocs, ...banudocs],
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import { Filter, ObjectId } from 'mongodb';
|
||||
import { nanoid } from 'nanoid';
|
||||
import sanitize from 'sanitize-filename';
|
||||
import Schema from 'schemastery';
|
||||
import parser from '@hydrooj/utils/lib/search';
|
||||
import { sortFiles, streamToBuffer } from '@hydrooj/utils/lib/utils';
|
||||
import type { Context } from '../context';
|
||||
@@ -34,7 +35,7 @@ import storage from '../model/storage';
|
||||
import * as system from '../model/system';
|
||||
import user from '../model/user';
|
||||
import {
|
||||
Handler, param, post, query, route, Types,
|
||||
Handler, param, post, Query, query, route, Types,
|
||||
} from '../service/server';
|
||||
import { ContestDetailBaseHandler } from './contest';
|
||||
|
||||
@@ -1021,6 +1022,38 @@ export class ProblemCreateHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
export const ProblemApi = {
|
||||
problem: Query(
|
||||
Schema.object({
|
||||
id: Schema.union([Schema.number().step(1), Schema.string()]).required(),
|
||||
domainId: Schema.string().required(),
|
||||
}),
|
||||
async (ctx, args) => {
|
||||
const pdoc = await problem.get(args.domainId, args.id);
|
||||
if (!pdoc) return null;
|
||||
if (pdoc.hidden) ctx.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||
return pdoc;
|
||||
},
|
||||
),
|
||||
problems: Query(
|
||||
Schema.object({
|
||||
ids: Schema.array(Schema.number().step(1)).required(),
|
||||
domainId: Schema.string().required(),
|
||||
}),
|
||||
async (ctx, args) => {
|
||||
const pdocs = await problem.getList(args.domainId, args.ids, ctx.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN) || ctx.user._id,
|
||||
undefined, undefined, true);
|
||||
return Object.keys(pdocs).map((id) => pdocs[+id]);
|
||||
},
|
||||
),
|
||||
} as const;
|
||||
|
||||
declare module '@hydrooj/framework' {
|
||||
interface Apis {
|
||||
problem: typeof ProblemApi;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
ctx.Route('problem_main', '/p', ProblemMainHandler, PERM.PERM_VIEW_PROBLEM);
|
||||
ctx.Route('problem_random', '/problem/random', ProblemRandomHandler, PERM.PERM_VIEW_PROBLEM);
|
||||
@@ -1038,64 +1071,6 @@ export async function apply(ctx: Context) {
|
||||
ctx.Route('problem_statistics', '/p/:pid/stat', ProblemStatisticsHandler, PERM.PERM_VIEW_PROBLEM);
|
||||
ctx.Route('problem_create', '/problem/create', ProblemCreateHandler, PERM.PERM_CREATE_PROBLEM);
|
||||
ctx.inject(['api'], ({ api }) => {
|
||||
api.value('FileInfo', [
|
||||
['_id', 'String!'],
|
||||
['name', 'String!'],
|
||||
['size', 'Int'],
|
||||
['lastModified', 'Date'],
|
||||
]);
|
||||
api.value('Problem', [
|
||||
['_id', 'ObjectID!'],
|
||||
['owner', 'Int!'],
|
||||
['domainId', 'String!'],
|
||||
['docId', 'Int!'],
|
||||
['docType', 'Int!'],
|
||||
['pid', 'String'],
|
||||
['title', 'String!'],
|
||||
['content', 'String!'],
|
||||
['data', '[FileInfo]'],
|
||||
['additional_file', '[FileInfo]'],
|
||||
['nSubmit', 'Int'],
|
||||
['nAccept', 'Int'],
|
||||
['difficulty', 'Int'],
|
||||
['tag', '[String]'],
|
||||
['hidden', 'Boolean'],
|
||||
]);
|
||||
api.resolver(
|
||||
'Query', 'problem(id: Int, pid: String)', 'Problem',
|
||||
async (arg, c) => {
|
||||
c.checkPerm(PERM.PERM_VIEW);
|
||||
const pdoc = await problem.get(c.args.domainId, arg.pid || arg.id);
|
||||
if (!pdoc) return null;
|
||||
if (pdoc.hidden) c.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||
c.pdoc = pdoc;
|
||||
return pdoc;
|
||||
},
|
||||
);
|
||||
api.resolver('Query', 'problems(ids: [Int])', '[Problem]', async (arg, c) => {
|
||||
c.checkPerm(PERM.PERM_VIEW);
|
||||
const res = await problem.getList(c.args.domainId, arg.ids, c.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN) || c.user._id,
|
||||
undefined, undefined, true);
|
||||
return Object.keys(res).map((id) => res[+id]);
|
||||
}, 'Get a list of problem by ids');
|
||||
api.resolver(
|
||||
'Problem', 'manage', 'ProblemManage',
|
||||
(arg, c) => {
|
||||
if (!c.user.own(c.pdoc, PERM.PERM_EDIT_PROBLEM_SELF)) c.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
||||
return {};
|
||||
},
|
||||
);
|
||||
api.resolver(
|
||||
'ProblemManage', 'delete', 'Boolean!',
|
||||
async (arg, c) => {
|
||||
const tdocs = await contest.getRelated(c.args.domainId, c.pdoc.docId);
|
||||
if (tdocs.length) throw new ProblemAlreadyUsedByContestError(c.pdoc.docId, tdocs[0]._id);
|
||||
return problem.del(c.pdoc.domainId, c.pdoc.docId);
|
||||
},
|
||||
);
|
||||
api.resolver(
|
||||
'ProblemManage', 'edit(title: String, content: String, tag: [String], hidden: Boolean)', 'Problem!',
|
||||
(arg, c) => problem.edit(c.args.domainId, c.pdoc.docId, arg),
|
||||
);
|
||||
api.provide(ProblemApi);
|
||||
});
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { generateAuthenticationOptions, verifyAuthenticationResponse } from '@si
|
||||
import { isoBase64URL } from '@simplewebauthn/server/helpers';
|
||||
import moment from 'moment-timezone';
|
||||
import type { Binary } from 'mongodb';
|
||||
import Schema from 'schemastery';
|
||||
import type { Context } from '../context';
|
||||
import {
|
||||
AuthOperationError, BadRequestError, BlacklistedError, BuiltinLoginError,
|
||||
@@ -25,7 +26,7 @@ import * as system from '../model/system';
|
||||
import token from '../model/token';
|
||||
import user, { deleteUserCache } from '../model/user';
|
||||
import {
|
||||
Handler, param, post, Types,
|
||||
Handler, param, post, Query, Types,
|
||||
} from '../service/server';
|
||||
|
||||
async function successfulAuth(this: Handler, udoc: User) {
|
||||
@@ -515,6 +516,67 @@ class ContestModeHandler extends Handler {
|
||||
|
||||
export const inject = ['oauth'];
|
||||
|
||||
const UserApi = {
|
||||
user: Query(Schema.object({
|
||||
id: Schema.number().step(1),
|
||||
uname: Schema.string(),
|
||||
mail: Schema.string(),
|
||||
domainId: Schema.string().required(),
|
||||
}), (c, arg) => {
|
||||
if (arg.id) return user.getById(arg.domainId, arg.id);
|
||||
if (arg.mail) return user.getByEmail(arg.domainId, arg.mail);
|
||||
if (arg.uname) return user.getByUname(arg.domainId, arg.uname);
|
||||
return c.user;
|
||||
}),
|
||||
users: Query(Schema.object({
|
||||
ids: Schema.array(Schema.number().step(1)),
|
||||
auto: Schema.array(Schema.string()),
|
||||
search: Schema.string(),
|
||||
limit: Schema.number().step(1),
|
||||
exact: Schema.boolean(),
|
||||
}), async (c, arg) => {
|
||||
const auto = (arg.ids?.length && arg.ids) || arg.auto || [];
|
||||
if (auto.length) {
|
||||
const maybeId = auto.filter((i) => !Number.isNaN(+i));
|
||||
const result = [];
|
||||
if (maybeId.length) {
|
||||
const udocs = await user.getList(arg.domainId, maybeId.map((i) => +i));
|
||||
for (const i in udocs) udocs[i].avatarUrl = avatar(udocs[i].avatar);
|
||||
result.push(...Object.values(udocs));
|
||||
}
|
||||
const notFound = auto.filter((i) => !result.find((j) => j._id === +i));
|
||||
if (notFound.length > 50) return result; // reject if too many
|
||||
for (const i of notFound) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const udoc = await user.getByUname(arg.domainId, i.toString()) || await user.getByEmail(arg.domainId, i.toString());
|
||||
if (udoc) result.push(udoc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (!arg.search) return [];
|
||||
const udoc = await user.getById(arg.domainId, +arg.search)
|
||||
|| await user.getByUname(arg.domainId, arg.search)
|
||||
|| await user.getByEmail(arg.domainId, arg.search);
|
||||
const udocs: User[] = arg.exact
|
||||
? []
|
||||
: await user.getPrefixList(arg.domainId, arg.search, Math.min(arg.limit || 10, 10));
|
||||
if (udoc && !udocs.find((i) => i._id === udoc._id)) {
|
||||
udocs.pop();
|
||||
udocs.unshift(udoc);
|
||||
}
|
||||
for (const i in udocs) {
|
||||
udocs[i].avatarUrl = avatar(udocs[i].avatar);
|
||||
}
|
||||
return udocs;
|
||||
}),
|
||||
} as const;
|
||||
|
||||
declare module '@hydrooj/framework' {
|
||||
interface Apis {
|
||||
user: typeof UserApi;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
ctx.Route('user_login', '/login', UserLoginHandler);
|
||||
ctx.Route('user_oauth', '/oauth/:type', OauthHandler);
|
||||
@@ -544,60 +606,6 @@ export async function apply(ctx: Context) {
|
||||
},
|
||||
});
|
||||
ctx.inject(['api'], ({ api }) => {
|
||||
api.value('User', [
|
||||
['_id', 'Int!'],
|
||||
['uname', 'String!'],
|
||||
['mail', 'String!'],
|
||||
['perm', 'String'],
|
||||
['role', 'String'],
|
||||
['loginat', 'Date'],
|
||||
['regat', 'Date!'],
|
||||
['priv', 'Int!', 'User Privilege'],
|
||||
['avatarUrl', 'String'],
|
||||
['displayName', 'String @if(perm: "PERM_VIEW_DISPLAYNAME")'],
|
||||
['rpInfo', 'JSONObject'],
|
||||
]);
|
||||
api.resolver('Query', 'user(id: Int, uname: String, mail: String)', 'User', (arg, c) => {
|
||||
if (arg.id) return user.getById(c.args.domainId, arg.id);
|
||||
if (arg.mail) return user.getByEmail(c.args.domainId, arg.mail);
|
||||
if (arg.uname) return user.getByUname(c.args.domainId, arg.uname);
|
||||
return c.user;
|
||||
}, `Get a user by id, uname, or mail.
|
||||
Returns current user if no argument is provided.`);
|
||||
api.resolver('Query', 'users(ids: [Int], auto: [String], search: String, limit: Int, exact: Boolean)', '[User]', async (arg, c) => {
|
||||
const auto = arg.ids || arg.auto || [];
|
||||
if (auto.length) {
|
||||
const maybeId = auto.filter((i) => !Number.isNaN(+i));
|
||||
const result = [];
|
||||
if (maybeId.length) {
|
||||
const udocs = await user.getList(c.args.domainId, maybeId.map((i) => +i));
|
||||
for (const i in udocs) udocs[i].avatarUrl = avatar(udocs[i].avatar);
|
||||
result.push(...Object.values(udocs));
|
||||
}
|
||||
const notFound = auto.filter((i) => !result.find((j) => j._id === +i));
|
||||
if (notFound.length > 50) return result; // reject if too many
|
||||
for (const i of notFound) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const udoc = await user.getByUname(c.args.domainId, i) || await user.getByEmail(c.args.domainId, i);
|
||||
if (udoc) result.push(udoc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (!arg.search) return [];
|
||||
const udoc = await user.getById(c.args.domainId, +arg.search)
|
||||
|| await user.getByUname(c.args.domainId, arg.search)
|
||||
|| await user.getByEmail(c.args.domainId, arg.search);
|
||||
const udocs: User[] = arg.exact
|
||||
? []
|
||||
: await user.getPrefixList(c.args.domainId, arg.search, Math.min(arg.limit || 10, 10));
|
||||
if (udoc && !udocs.find((i) => i._id === udoc._id)) {
|
||||
udocs.pop();
|
||||
udocs.unshift(udoc);
|
||||
}
|
||||
for (const i in udocs) {
|
||||
udocs[i].avatarUrl = avatar(udocs[i].avatar);
|
||||
}
|
||||
return udocs;
|
||||
}, 'Get a list of user by ids, or search users with the prefix.');
|
||||
api.provide(UserApi);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user