From 048187653619bb1ce20bba175845ae21e35d92db Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 6 Dec 2020 22:48:50 +0800 Subject: [PATCH] syzoj-ng-api: init (WIP) --- build/build.js | 8 +- packages/syzoj-ng-api/handler.ts | 7 ++ packages/syzoj-ng-api/handlers/problem.ts | 137 ++++++++++++++++++++++ packages/syzoj-ng-api/package.json | 9 ++ 4 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 packages/syzoj-ng-api/handler.ts create mode 100644 packages/syzoj-ng-api/handlers/problem.ts create mode 100644 packages/syzoj-ng-api/package.json diff --git a/build/build.js b/build/build.js index 6a5f44670..3074f4e6c 100644 --- a/build/build.js +++ b/build/build.js @@ -28,9 +28,7 @@ const configSrc = { outDir: 'dist', rootDir: 'src', }, - include: [ - 'src', - ], + include: ['src'], exclude: [ '**/__mocks__', 'bin', @@ -43,9 +41,7 @@ const configFlat = { outDir: '.', rootDir: '.', }, - include: [ - '*.ts', - ], + include: ['**/*.ts'], exclude: [], }; diff --git a/packages/syzoj-ng-api/handler.ts b/packages/syzoj-ng-api/handler.ts new file mode 100644 index 000000000..51a2f9e6c --- /dev/null +++ b/packages/syzoj-ng-api/handler.ts @@ -0,0 +1,7 @@ +import { apply as ApplyProblemHandler } from './handlers/problem'; + +export async function apply() { + await ApplyProblemHandler(); +} + +global.Hydro.handler.syzojApi = apply; diff --git a/packages/syzoj-ng-api/handlers/problem.ts b/packages/syzoj-ng-api/handlers/problem.ts new file mode 100644 index 000000000..c1ccc8a9c --- /dev/null +++ b/packages/syzoj-ng-api/handlers/problem.ts @@ -0,0 +1,137 @@ +import { FilterQuery } from 'mongodb'; +import { Pdoc } from 'hydrooj/dist/interface'; +import * as problem from 'hydrooj/dist/model/problem'; +import * as record from 'hydrooj/dist/model/record'; +import * as user from 'hydrooj/dist/model/user'; +import { PERM, PRIV } from 'hydrooj/dist/model/builtin'; +import { + Route, Handler, Types, post, +} from 'hydrooj/dist/service/server'; + +export class QueryProblemSetAPIHandler extends Handler { + @post('locale', Types.String, true) + @post('keyword', Types.String, true) + @post('ownerId', Types.Int, true) + @post('nonpublic', Types.Boolean, true) + @post('tagsIds', Types.Array, true) + @post('skipCount', Types.UnsignedInt, true) + @post('takeCount', Types.UnsignedInt, true) + async post( + domainId: string, locale: string, $search: string, + owner: number, nonpublic = false, tags = [], + skip = 0, take = 0, + ) { + if ( + (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)) + || (nonpublic && !this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) + ) { + this.response.body = { error: 'PERMISSION_DENIED' }; + return; + } + const query: FilterQuery = {}; + if ($search) query.$text = { $search }; + if (tags?.length) { + query.$and = tags.map(($eq) => ({ + $or: [ + { category: { $elemMatch: { $eq } } }, + { tag: { $elemMatch: { $eq } } }, + ], + })); + } + if (owner) query.owner = owner; + if (!nonpublic) query.hidden = false; + const [raw, count] = await Promise.all([ + problem.getMulti(domainId, query).sort({ pid: 1, docId: 1 }) + .skip(skip).limit(take) + .toArray(), + problem.getMulti(domainId, query).count(), + ]); + const result = []; + for (const doc of raw) { + // eslint-disable-next-line no-await-in-loop + const [rdoc] = await record + .getUserInProblemMulti(domainId, this.user._id, doc.docId) + .sort({ _id: -1 }) + .limit(1) + .toArray(); + let locales = []; + try { + const ct = typeof doc.content === 'string' ? JSON.parse(doc.content) : doc.content; + if (ct instanceof Array) locales = [this.session.viewLang || this.user.viewLang]; + else locales = Object.keys(ct); + } catch (e) { + locales = [this.session.viewLang || this.user.viewLang]; + } + result.push({ + meta: { + id: doc.docId, + displayId: doc.pid, + type: 'Traditional', + isPublic: !doc.hidden, + publicTime: doc._id.getTimestamp(), + ownerId: doc.owner, + locales, + submissionCount: doc.nSubmit, + acceptedSubmissionCount: doc.nAccept, + }, + title: doc.title, + tags: [], // TODO + resultLocale: this.session.viewLang || this.user.viewLang, + submission: rdoc + ? { + id: rdoc._id, + isPublic: !doc.hidden, + codeLanguage: rdoc.lang, + answerSize: rdoc.code.length, + score: rdoc.score, + status: rdoc.status, + submitTime: rdoc._id.getTimestamp(), + timeUsed: rdoc.time, + memoryUsed: rdoc.memory, + } + : null, + }); + } + this.response.body = { + result, + count, + permissions: { + createProblem: this.user.hasPerm(PERM.PERM_CREATE_PROBLEM), + manageTags: this.user.hasPerm(PERM.PERM_EDIT_PROBLEM), + filterByOwner: this.user.hasPerm(PERM.PERM_VIEW_PROBLEM), + filterNonpublic: this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), + }, + }; + if (owner) { + const udoc = await user.getById(domainId, owner); + this.response.body.filterOwner = { + id: udoc._id, + username: udoc.uname, + email: udoc.mail, + nickname: udoc.displayName || udoc.uname, + bio: udoc.bio, + avatar: { + type: 'gravatar', + key: udoc.gravatar, + }, + isAdmin: udoc.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN) || udoc.hasPerm(PERM.PERM_ADMIN), + acceptedProblemCount: udoc.nAccept, + submissionCount: udoc.nSubmit, + rating: udoc.rating, + registrationTime: udoc.regat, + }; + } + if (tags) { + this.response.body.filterTags = tags.map((tag) => ({ + id: tag, + name: tag, + color: '1', + nameLocale: locale, + })); + } + } +} + +export async function apply() { + Route('syzoj_api_problem_queryProblemSet', '/api/problem/queryProblemSet', QueryProblemSetAPIHandler); +} diff --git a/packages/syzoj-ng-api/package.json b/packages/syzoj-ng-api/package.json new file mode 100644 index 000000000..e3b0c3eb8 --- /dev/null +++ b/packages/syzoj-ng-api/package.json @@ -0,0 +1,9 @@ +{ + "name": "@hydrooj/syzoj-ng-api", + "version": "0.0.1", + "description": "A syzoj-ng API interface", + "main": "package.json", + "repository": "https://github.com/hydro-dev/Hydro.git", + "author": "undefined ", + "license": "AGPL-3.0.only" +} \ No newline at end of file