mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: problem.import: add delSource option
This commit is contained in:
3 files changed
+28
-13
No files matched your search
@@ -19,7 +19,12 @@ class ProblemImportHydroHandler extends Handler {
|
|||||||
if (preferredPrefix && !/^[a-zA-Z]+$/.test(preferredPrefix)) throw new ValidationError('preferredPrefix');
|
if (preferredPrefix && !/^[a-zA-Z]+$/.test(preferredPrefix)) throw new ValidationError('preferredPrefix');
|
||||||
const promise = problem.import(
|
const promise = problem.import(
|
||||||
domainId, this.request.files.file.filepath,
|
domainId, this.request.files.file.filepath,
|
||||||
{ preferredPrefix, progress: this.progress.bind(this), operator: keepUser ? null : this.user._id },
|
{
|
||||||
|
preferredPrefix,
|
||||||
|
progress: this.progress.bind(this),
|
||||||
|
operator: keepUser ? null : this.user._id,
|
||||||
|
delSource: true,
|
||||||
|
},
|
||||||
).catch((e) => MessageModel.send(1, this.user._id, `Import failed: ${e.message}\n${e.stack}`));
|
).catch((e) => MessageModel.send(1, this.user._id, `Import failed: ${e.message}\n${e.stack}`));
|
||||||
let resolved = false;
|
let resolved = false;
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
|
|||||||
@@ -729,7 +729,7 @@ export class ProblemFilesHandler extends ProblemDetailHandler {
|
|||||||
if ((this.pdoc.data?.length || 0)
|
if ((this.pdoc.data?.length || 0)
|
||||||
+ (this.pdoc.additional_file?.length || 0)
|
+ (this.pdoc.additional_file?.length || 0)
|
||||||
+ files.length
|
+ files.length
|
||||||
>= system.get('limit.problem_files_max')) {
|
>= this.ctx.setting.get('limit.problem_files_max')) {
|
||||||
throw new FileLimitExceededError('count');
|
throw new FileLimitExceededError('count');
|
||||||
}
|
}
|
||||||
const size = Math.sum(
|
const size = Math.sum(
|
||||||
@@ -737,7 +737,7 @@ export class ProblemFilesHandler extends ProblemDetailHandler {
|
|||||||
(this.pdoc.additional_file || []).map((i) => i.size),
|
(this.pdoc.additional_file || []).map((i) => i.size),
|
||||||
files.map((i) => i.size),
|
files.map((i) => i.size),
|
||||||
);
|
);
|
||||||
if (size >= system.get('limit.problem_files_max_size')) {
|
if (size >= this.ctx.setting.get('limit.problem_files_max_size')) {
|
||||||
throw new FileLimitExceededError('size');
|
throw new FileLimitExceededError('size');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import DomainModel from './domain';
|
|||||||
import RecordModel from './record';
|
import RecordModel from './record';
|
||||||
import SolutionModel from './solution';
|
import SolutionModel from './solution';
|
||||||
import storage from './storage';
|
import storage from './storage';
|
||||||
|
import * as SystemModel from './system';
|
||||||
import user from './user';
|
import user from './user';
|
||||||
|
|
||||||
export interface ProblemDoc extends Document { }
|
export interface ProblemDoc extends Document { }
|
||||||
@@ -59,6 +60,7 @@ interface ProblemImportOptions {
|
|||||||
progress?: any;
|
progress?: any;
|
||||||
override?: boolean;
|
override?: boolean;
|
||||||
operator?: number;
|
operator?: number;
|
||||||
|
delSource?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProblemModel {
|
export class ProblemModel {
|
||||||
@@ -448,7 +450,6 @@ export class ProblemModel {
|
|||||||
|
|
||||||
static async import(domainId: string, filepath: string, options: ProblemImportOptions = {}) {
|
static async import(domainId: string, filepath: string, options: ProblemImportOptions = {}) {
|
||||||
let tmpdir = '';
|
let tmpdir = '';
|
||||||
let del = false;
|
|
||||||
if (typeof options !== 'object') {
|
if (typeof options !== 'object') {
|
||||||
logger.warn('ProblemModel.import: options should be an object');
|
logger.warn('ProblemModel.import: options should be an object');
|
||||||
options = {};
|
options = {};
|
||||||
@@ -456,6 +457,7 @@ export class ProblemModel {
|
|||||||
const {
|
const {
|
||||||
preferredPrefix, progress, override = false, operator = 1,
|
preferredPrefix, progress, override = false, operator = 1,
|
||||||
} = options;
|
} = options;
|
||||||
|
let problems: string[];
|
||||||
try {
|
try {
|
||||||
if (filepath.endsWith('.zip')) {
|
if (filepath.endsWith('.zip')) {
|
||||||
tmpdir = path.join(os.tmpdir(), 'hydro', `${Math.random()}.import`);
|
tmpdir = path.join(os.tmpdir(), 'hydro', `${Math.random()}.import`);
|
||||||
@@ -465,7 +467,6 @@ export class ProblemModel {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ValidationError('zip', null, e.message);
|
throw new ValidationError('zip', null, e.message);
|
||||||
}
|
}
|
||||||
del = true;
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
zip.extractAllToAsync(tmpdir, true, (err) => {
|
zip.extractAllToAsync(tmpdir, true, (err) => {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
@@ -477,11 +478,14 @@ export class ProblemModel {
|
|||||||
} else {
|
} else {
|
||||||
throw new ValidationError('file', null, 'Invalid file');
|
throw new ValidationError('file', null, 'Invalid file');
|
||||||
}
|
}
|
||||||
const problems = await fs.readdir(tmpdir, { withFileTypes: true });
|
const files = await fs.readdir(tmpdir, { withFileTypes: true });
|
||||||
for (const p of problems) {
|
problems = files.filter((f) => f.isDirectory()).map((i) => i.name);
|
||||||
|
} catch (e) {
|
||||||
|
if (options.delSource) await fs.remove(tmpdir);
|
||||||
|
}
|
||||||
|
for (const i of problems) {
|
||||||
|
try {
|
||||||
if (process.env.HYDRO_CLI) logger.info(`Importing problem ${p.name}`);
|
if (process.env.HYDRO_CLI) logger.info(`Importing problem ${p.name}`);
|
||||||
const i = p.name;
|
|
||||||
if (!p.isDirectory()) continue;
|
|
||||||
const files = await fs.readdir(path.join(tmpdir, i), { withFileTypes: true });
|
const files = await fs.readdir(path.join(tmpdir, i), { withFileTypes: true });
|
||||||
if (!files.find((f) => f.name === 'problem.yaml')) continue;
|
if (!files.find((f) => f.name === 'problem.yaml')) continue;
|
||||||
const content = fs.readFileSync(path.join(tmpdir, i, 'problem.yaml'), 'utf-8');
|
const content = fs.readFileSync(path.join(tmpdir, i, 'problem.yaml'), 'utf-8');
|
||||||
@@ -516,16 +520,21 @@ export class ProblemModel {
|
|||||||
const overrideContent = findOverrideContent(path.join(tmpdir, i), 'problem');
|
const overrideContent = findOverrideContent(path.join(tmpdir, i), 'problem');
|
||||||
if (pdoc.difficulty && !Number.isSafeInteger(pdoc.difficulty)) delete pdoc.difficulty;
|
if (pdoc.difficulty && !Number.isSafeInteger(pdoc.difficulty)) delete pdoc.difficulty;
|
||||||
if (typeof pdoc.title !== 'string') throw new ValidationError('title', null, 'Invalid title');
|
if (typeof pdoc.title !== 'string') throw new ValidationError('title', null, 'Invalid title');
|
||||||
|
const allFiles = [...await getFiles('testdata'), ...await getFiles('additional_file')];
|
||||||
|
const totalSize = allFiles.map((f) => fs.statSync(f[1]).size).reduce((a, b) => a + b, 0);
|
||||||
|
if (allFiles.length > SystemModel.get('limit.problem_files')) throw new ValidationError('files', null, 'Too many files');
|
||||||
|
if (totalSize > SystemModel.get('limit.problem_files_size')) throw new ValidationError('files', null, 'Files too large');
|
||||||
|
const tag = (pdoc.tag || []).map((t) => t.toString());
|
||||||
const docId = overridePid
|
const docId = overridePid
|
||||||
? (await ProblemModel.edit(domainId, overridePid, {
|
? (await ProblemModel.edit(domainId, overridePid, {
|
||||||
title: pdoc.title.trim(),
|
title: pdoc.title.trim(),
|
||||||
content: overrideContent || pdoc.content || 'No content',
|
content: overrideContent || pdoc.content || 'No content',
|
||||||
tag: pdoc.tag || [],
|
tag,
|
||||||
difficulty: pdoc.difficulty,
|
difficulty: pdoc.difficulty,
|
||||||
})).docId
|
})).docId
|
||||||
: await ProblemModel.add(
|
: await ProblemModel.add(
|
||||||
domainId, pid, pdoc.title.trim(), overrideContent || pdoc.content || 'No content',
|
domainId, pid, pdoc.title.trim(), overrideContent || pdoc.content || 'No content',
|
||||||
operator || pdoc.owner, pdoc.tag || [], { hidden: pdoc.hidden, difficulty: pdoc.difficulty },
|
operator || pdoc.owner, tag, { hidden: pdoc.hidden, difficulty: pdoc.difficulty },
|
||||||
);
|
);
|
||||||
// TODO delete unused file when updating pdoc
|
// TODO delete unused file when updating pdoc
|
||||||
for (const [f, loc] of await getFiles('testdata')) {
|
for (const [f, loc] of await getFiles('testdata')) {
|
||||||
@@ -551,10 +560,11 @@ export class ProblemModel {
|
|||||||
}
|
}
|
||||||
const message = `${overridePid ? 'Updated' : 'Imported'} problem ${pdoc.pid} (${pdoc.title})`;
|
const message = `${overridePid ? 'Updated' : 'Imported'} problem ${pdoc.pid} (${pdoc.title})`;
|
||||||
(process.env.HYDRO_CLI ? logger.info : progress)?.(message);
|
(process.env.HYDRO_CLI ? logger.info : progress)?.(message);
|
||||||
|
} catch (e) {
|
||||||
|
(process.env.HYDRO_CLI ? logger.info : progress)?.(`Error importing problem ${p.name}: ${e.message}`);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
if (del) await fs.remove(tmpdir);
|
|
||||||
}
|
}
|
||||||
|
if (options.delSource) await fs.remove(tmpdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async export(domainId: string) {
|
static async export(domainId: string) {
|
||||||
|
|||||||
Reference in new issue
Block a user