From dcc6a27bbc67af39a7edd68c2c3b197f03f9b48d Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 6 May 2021 18:12:35 +0800 Subject: [PATCH] core: sort for ProblemFiles --- packages/hydrooj/package.json | 4 ++-- packages/hydrooj/src/handler/problem.ts | 11 ++++++++--- packages/hydrooj/src/interface.ts | 1 + packages/hydrooj/src/model/setting.ts | 1 + packages/utils/lib/utils.ts | 24 ++++++++++++++++++++++++ packages/utils/package.json | 2 +- 6 files changed, 37 insertions(+), 6 deletions(-) diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index ee1b77691..6b906d476 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.24.15", + "version": "2.24.16", "bin": "bin/hydrooj.js", "main": "dist/loader.js", "typings": "dist/loader.d.ts", @@ -11,7 +11,7 @@ "node": ">=14" }, "dependencies": { - "@hydrooj/utils": "^1.0.7", + "@hydrooj/utils": "^1.0.11", "adm-zip": "^0.5.5", "ansi_up": "^5.0.1", "cookies": "^0.8.0", diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 1a63fa596..778d5ac79 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -1,9 +1,11 @@ import { isSafeInteger, flatten } from 'lodash'; import { FilterQuery, ObjectID } from 'mongodb'; import AdmZip from 'adm-zip'; +import { sortFiles } from '@hydrooj/utils/lib/utils'; import { NoProblemError, PermissionError, ValidationError, SolutionNotFoundError, ProblemNotFoundError, BadRequestError, + ForbiddenError, } from '../error'; import { Pdoc, User, Rdoc, PathComponent, ProblemStatusDoc, @@ -380,8 +382,8 @@ export class ProblemFilesHandler extends ProblemDetailHandler { @param('additional_file', Types.Boolean) async get(domainId: string, getTestdata = true, getAdditionalFile = true) { const canReadData = !this.user.own(this.pdoc) || this.user.hasPerm(PERM.PERM_READ_PROBLEM_DATA); - this.response.body.testdata = (getTestdata && canReadData) ? this.pdoc.data : []; - this.response.body.additional_file = (getAdditionalFile ? this.pdoc.additional_file : []); + this.response.body.testdata = (getTestdata && canReadData) ? sortFiles(this.pdoc.data) : []; + this.response.body.additional_file = getAdditionalFile ? sortFiles(this.pdoc.additional_file) : []; this.response.template = 'problem_files.html'; } @@ -405,9 +407,12 @@ export class ProblemFilesHandler extends ProblemDetailHandler { @post('filename', Types.Name, true) @post('type', Types.Range(['testdata', 'additional_file']), true) async postUploadFile(domainId: string, filename: string, type = 'testdata') { + if (this.pdoc.data.length + this.pdoc.additional_file.length >= system.get('limit.problem_files_max')) { + throw new ForbiddenError('File limit exceeded.'); + } if (!this.request.files.file) throw new ValidationError('file'); if (!filename) filename = this.request.files.file.name || String.random(16); - if (filename.includes('/')) throw new ValidationError('filename', 'Bad filename'); + if (filename.includes('/') || filename.includes('..')) throw new ValidationError('filename', 'Bad filename'); if (!this.user.own(this.pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); if (filename.endsWith('.zip')) { const zip = new AdmZip(this.request.files.file.path); diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index 0cd1a7977..3c4b004e8 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -35,6 +35,7 @@ export interface SystemKeys { 'server.worker': number, 'server.port': number, 'server.language': string, + 'limit.problem_files_max': number, 'problem.categories': string, 'session.keys': string[], 'session.secure': boolean, diff --git a/packages/hydrooj/src/model/setting.ts b/packages/hydrooj/src/model/setting.ts index 2d65a7201..0a1135ecb 100644 --- a/packages/hydrooj/src/model/setting.ts +++ b/packages/hydrooj/src/model/setting.ts @@ -162,6 +162,7 @@ SystemSetting( Setting('setting_server', 'server.xhost', null, 'text', 'server.xhost', 'Hostname Header'), Setting('setting_server', 'server.language', 'zh_CN', langRange, 'server.language', 'Default display language'), Setting('setting_server', 'server.login', true, 'boolean', 'server.login', 'Allow builtin-login'), + Setting('setting_limits', 'limit.problem_files_max', 100, 'number', 'limit.problem_files_max', 'Max files per problem'), Setting('setting_basic', 'default.priv', builtin.PRIV.PRIV_DEFAULT, 'number', 'default.priv', 'Default Privilege'), Setting('setting_basic', 'problem.categories', builtin.CATEGORIES, 'yaml', 'problem.categories', 'Problem Categories'), Setting('setting_basic', 'lang.texts', builtin.LANG_TEXTS, 'yaml', 'lang.texts', 'LANG_TEXTS'), diff --git a/packages/utils/lib/utils.ts b/packages/utils/lib/utils.ts index 79d734b37..82ad0b59a 100644 --- a/packages/utils/lib/utils.ts +++ b/packages/utils/lib/utils.ts @@ -390,3 +390,27 @@ export function CallableInstance(property = '__call__') { } CallableInstance.prototype = Object.create(Function.prototype); + +const fSortR = /[^\d]+|\d+/g; +export function sortFiles(files: { _id: string }[] | string[]) { + const isString = typeof files[0] === 'string'; + const result = files + .map((i) => (isString ? { name: i, weights: i.match(fSortR) } : { ...i, weights: i.match(fSortR) })) + .sort((a, b) => { + let pos = 0; + const weightsA = a.weights; + const weightsB = b.weights; + let weightA = weightsA[pos]; + let weightB = weightsB[pos]; + while (weightA && weightB) { + const v = weightA - weightB; + if (!Number.isNaN(v) && v !== 0) return v; + if (weightA !== weightB) return weightA > weightB ? 1 : -1; + pos += 1; + weightA = weightsA[pos]; + weightB = weightsB[pos]; + } + return weightA ? 1 : -1; + }); + return isString ? result.map((x) => x.name) : result; +} diff --git a/packages/utils/package.json b/packages/utils/package.json index fb1f77c5b..f47d6022e 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/utils", - "version": "1.0.10", + "version": "1.0.11", "description": "hydrooj utils", "main": "package.json", "repository": "https://github.com/hydro-dev/Hydro.git",