mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
keep things simple
This commit is contained in:
3 files changed
+17
-92
No files matched your search
@@ -223,7 +223,7 @@ export interface ProblemConfig {
|
|||||||
hackable?: boolean;
|
hackable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LocalizedContent = ({ id: string, name: string, lang: string } | { from: string, name: string, lang: string })[];
|
export type LocalizedContent = ({ content: string, name: string, lang: string })[];
|
||||||
export type Content = string | LocalizedContent;
|
export type Content = string | LocalizedContent;
|
||||||
|
|
||||||
export interface Document {
|
export interface Document {
|
||||||
@@ -680,11 +680,6 @@ export interface ContestBalloonDoc {
|
|||||||
sentAt?: Date;
|
sentAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContentDoc {
|
|
||||||
_id: string;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module './service/db' {
|
declare module './service/db' {
|
||||||
interface Collections {
|
interface Collections {
|
||||||
'blacklist': BlacklistDoc;
|
'blacklist': BlacklistDoc;
|
||||||
@@ -696,7 +691,6 @@ declare module './service/db' {
|
|||||||
'document.status': StatusDocBase & {
|
'document.status': StatusDocBase & {
|
||||||
[K in keyof DocStatusType]: { docType: K } & DocStatusType[K];
|
[K in keyof DocStatusType]: { docType: K } & DocStatusType[K];
|
||||||
}[keyof DocStatusType];
|
}[keyof DocStatusType];
|
||||||
'content': ContentDoc;
|
|
||||||
'discussion.history': DiscussionHistoryDoc;
|
'discussion.history': DiscussionHistoryDoc;
|
||||||
'user': Udoc;
|
'user': Udoc;
|
||||||
'user.preference': UserPreferenceDoc;
|
'user.preference': UserPreferenceDoc;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ type NormalArrayKeys<O, P = any> = Exclude<ArrayKeys<O, P>, Symbol>;
|
|||||||
|
|
||||||
export const coll = db.collection('document');
|
export const coll = db.collection('document');
|
||||||
export const collStatus = db.collection('document.status');
|
export const collStatus = db.collection('document.status');
|
||||||
export const collContent = db.collection('content');
|
|
||||||
|
|
||||||
export const TYPE_PROBLEM: 10 = 10;
|
export const TYPE_PROBLEM: 10 = 10;
|
||||||
export const TYPE_PROBLEM_SOLUTION: 11 = 11;
|
export const TYPE_PROBLEM_SOLUTION: 11 = 11;
|
||||||
@@ -426,29 +425,6 @@ export async function revSetStatus<T extends keyof DocStatusType>(
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getContent(contentId: string) {
|
|
||||||
return await collContent.findOne({ _id: contentId });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getContents(contentIds: string[]) {
|
|
||||||
return await collContent.find({ _id: { $in: contentIds } }).toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function addContent(content: string) {
|
|
||||||
const id = String.random(32);
|
|
||||||
await collContent.insertOne({ _id: id, content });
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setContent(contentId: string, content: string) {
|
|
||||||
const res = await collContent.updateOne({ _id: contentId }, { $set: { content } });
|
|
||||||
return !!res.matchedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteContent(contentId: string) {
|
|
||||||
return await collContent.deleteOne({ _id: contentId });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function apply(ctx: Context) {
|
export async function apply(ctx: Context) {
|
||||||
ctx.on('domain/delete', (domainId) => Promise.all([
|
ctx.on('domain/delete', (domainId) => Promise.all([
|
||||||
coll.deleteMany({ domainId }),
|
coll.deleteMany({ domainId }),
|
||||||
@@ -487,7 +463,6 @@ export async function apply(ctx: Context) {
|
|||||||
|
|
||||||
global.Hydro.model.document = {
|
global.Hydro.model.document = {
|
||||||
coll,
|
coll,
|
||||||
collContent,
|
|
||||||
collStatus,
|
collStatus,
|
||||||
|
|
||||||
add,
|
add,
|
||||||
@@ -519,11 +494,6 @@ global.Hydro.model.document = {
|
|||||||
setStatus,
|
setStatus,
|
||||||
setMultiStatus,
|
setMultiStatus,
|
||||||
setSub,
|
setSub,
|
||||||
getContent,
|
|
||||||
getContents,
|
|
||||||
addContent,
|
|
||||||
setContent,
|
|
||||||
deleteContent,
|
|
||||||
|
|
||||||
TYPE_CONTEST,
|
TYPE_CONTEST,
|
||||||
TYPE_CONTEST_CLARIFICATION,
|
TYPE_CONTEST_CLARIFICATION,
|
||||||
|
|||||||
@@ -41,19 +41,20 @@ function sortable(source: string, namespaces: Record<string, string>) {
|
|||||||
|
|
||||||
function findOverrideContent(dir: string, base: string) {
|
function findOverrideContent(dir: string, base: string) {
|
||||||
let files = fs.readdirSync(dir);
|
let files = fs.readdirSync(dir);
|
||||||
if (files.includes(`${base}.md`)) return fs.readFileSync(path.join(dir, `${base}.md`), 'utf8');
|
if (files.includes(`${base}.md`)) return [{ name: 'default', content: fs.readFileSync(path.join(dir, `${base}.md`), 'utf8'), lang: 'en' }];
|
||||||
if (files.includes(`${base}.pdf`)) return `@[PDF](file://${base}.pdf)`;
|
if (files.includes(`${base}.pdf`)) return [{ name: 'default', content: `@[PDF](file://${base}.pdf)`, lang: 'en' }];
|
||||||
const languages = {};
|
const c: LocalizedContent = [];
|
||||||
files = files.filter((i) => new RegExp(`^${base}(?:_|.)([a-zA-Z_]+)\\.(md|pdf)$`).test(i));
|
files = files.filter((i) => new RegExp(`^${base}(?:_|.)([a-zA-Z_]+)\\.(md|pdf)$`).test(i));
|
||||||
if (!files.length) return null;
|
if (!files.length) return null;
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const match = file.match(`^${base}(?:_|.)([a-zA-Z_]+)\\.(md|pdf)$`);
|
const match = file.match(`^${base}(?:_|.)([a-zA-Z_]+)\\.(md|pdf)$`);
|
||||||
const lang = match[1];
|
const lang = match[1];
|
||||||
const ext = match[2];
|
const ext = match[2];
|
||||||
if (ext === 'pdf') languages[lang] = `@[PDF](file://${file})`;
|
// TODO: parse markdown header to get name
|
||||||
else languages[lang] = fs.readFileSync(path.join(dir, file), 'utf8');
|
if (ext === 'pdf') c.push({ name: lang, content: `@[PDF](file://${file})`, lang });
|
||||||
|
else c.push({ name: lang, content: fs.readFileSync(path.join(dir, file), 'utf8'), lang });
|
||||||
}
|
}
|
||||||
return JSON.stringify(languages);
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProblemImportOptions {
|
interface ProblemImportOptions {
|
||||||
@@ -138,22 +139,10 @@ export class ProblemModel {
|
|||||||
|
|
||||||
static async add(
|
static async add(
|
||||||
domainId: string, pid: string = '', title: string,
|
domainId: string, pid: string = '', title: string,
|
||||||
content: { content?: string, from?: string, lang: string, name: string }[] | string,
|
content: { content: string, lang: string, name: string }[] | string,
|
||||||
owner: number, tag: string[] = [], meta: ProblemCreateOptions = {},
|
owner: number, tag: string[] = [], meta: ProblemCreateOptions = {},
|
||||||
) {
|
) {
|
||||||
let c: LocalizedContent;
|
const c = typeof content === 'string' ? [{ content, lang: 'en', name: 'default' }] : content;
|
||||||
if (typeof content === 'string') {
|
|
||||||
c.push({ id: await document.addContent(content), name: 'default', lang: 'en' });
|
|
||||||
} else {
|
|
||||||
for (const entry of content) {
|
|
||||||
if (entry.from) {
|
|
||||||
c.push(entry as any);
|
|
||||||
} else {
|
|
||||||
const id = await document.addContent(entry.content);
|
|
||||||
c.push({ id, name: entry.name, lang: entry.lang });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const [doc] = await ProblemModel.getMulti(domainId, {})
|
const [doc] = await ProblemModel.getMulti(domainId, {})
|
||||||
.sort({ docId: -1 }).limit(1).project({ docId: 1 })
|
.sort({ docId: -1 }).limit(1).project({ docId: 1 })
|
||||||
.toArray();
|
.toArray();
|
||||||
@@ -206,11 +195,6 @@ export class ProblemModel {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getContent(contentIds: string[]) {
|
|
||||||
const content = await document.getContents(contentIds);
|
|
||||||
return Object.fromEntries(content.map((i) => [i._id.toString(), i.content]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static getMulti(domainId: string, query: Filter<ProblemDoc>, projection = ProblemModel.PROJECTION_LIST) {
|
static getMulti(domainId: string, query: Filter<ProblemDoc>, projection = ProblemModel.PROJECTION_LIST) {
|
||||||
return document.getMulti(domainId, document.TYPE_PROBLEM, query, projection).sort({ sort: 1 });
|
return document.getMulti(domainId, document.TYPE_PROBLEM, query, projection).sort({ sort: 1 });
|
||||||
}
|
}
|
||||||
@@ -235,10 +219,7 @@ export class ProblemModel {
|
|||||||
return document.getMultiStatus(domainId, document.TYPE_PROBLEM, query);
|
return document.getMultiStatus(domainId, document.TYPE_PROBLEM, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async edit(
|
static async edit(domainId: string, _id: number, $set: Partial<ProblemDoc>): Promise<ProblemDoc> {
|
||||||
domainId: string, _id: number,
|
|
||||||
$set: (Partial<ProblemDoc> & ({ content?: { content: string }[] })),
|
|
||||||
): Promise<ProblemDoc> {
|
|
||||||
const delpid = $set.pid === '';
|
const delpid = $set.pid === '';
|
||||||
const ddoc = await DomainModel.get(domainId);
|
const ddoc = await DomainModel.get(domainId);
|
||||||
if (delpid) {
|
if (delpid) {
|
||||||
@@ -248,23 +229,6 @@ export class ProblemModel {
|
|||||||
$set.sort = sortable($set.pid, ddoc.namespaces);
|
$set.sort = sortable($set.pid, ddoc.namespaces);
|
||||||
}
|
}
|
||||||
await bus.parallel('problem/before-edit', $set);
|
await bus.parallel('problem/before-edit', $set);
|
||||||
if ($set.content) {
|
|
||||||
const update = $set.content instanceof Array ? $set.content : [{ content: $set.content, lang: 'en', name: 'default' }];
|
|
||||||
const existing = await document.get(domainId, document.TYPE_PROBLEM, _id);
|
|
||||||
if (!existing) throw new ProblemNotFoundError(domainId, _id);
|
|
||||||
const contents = existing.content;
|
|
||||||
const final = [];
|
|
||||||
for (const entry of update) {
|
|
||||||
if ('id' in entry && contents.find((i) => i.id === entry.id)) {
|
|
||||||
await document.setContent(entry.id, entry.content);
|
|
||||||
final.push({ id: entry.id, name: entry.name, lang: entry.lang });
|
|
||||||
} else {
|
|
||||||
const cid = await document.addContent(entry.content);
|
|
||||||
final.push({ id: cid, name: entry.name, lang: entry.lang });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$set.content = final;
|
|
||||||
}
|
|
||||||
const result = await document.set(domainId, document.TYPE_PROBLEM, _id, $set, delpid ? { pid: '' } : undefined);
|
const result = await document.set(domainId, document.TYPE_PROBLEM, _id, $set, delpid ? { pid: '' } : undefined);
|
||||||
await bus.emit('problem/edit', result);
|
await bus.emit('problem/edit', result);
|
||||||
return result;
|
return result;
|
||||||
@@ -277,7 +241,7 @@ export class ProblemModel {
|
|||||||
if (pid && (/^[0-9]+$/.test(pid) || await ProblemModel.get(target, pid))) pid = '';
|
if (pid && (/^[0-9]+$/.test(pid) || await ProblemModel.get(target, pid))) pid = '';
|
||||||
if (!pid && original.pid && !await ProblemModel.get(target, original.pid)) pid = original.pid;
|
if (!pid && original.pid && !await ProblemModel.get(target, original.pid)) pid = original.pid;
|
||||||
return await ProblemModel.add(
|
return await ProblemModel.add(
|
||||||
target, pid, original.title, original.content.map((i) => ({ name: i.name, lang: i.lang, from: 'id' in i ? i.id : i.from })),
|
target, pid, original.title, original.content,
|
||||||
original.owner, original.tag, { hidden: original.hidden, reference: { domainId, pid: _id } },
|
original.owner, original.tag, { hidden: original.hidden, reference: { domainId, pid: _id } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -585,8 +549,8 @@ export class ProblemModel {
|
|||||||
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: [{
|
content: overrideContent || [{
|
||||||
id: '', name: 'default', content: overrideContent || pdoc.content.toString() || 'No content', lang: 'en',
|
name: 'default', content: pdoc.content.toString() || 'No content', lang: 'en',
|
||||||
}],
|
}],
|
||||||
tag,
|
tag,
|
||||||
difficulty: pdoc.difficulty,
|
difficulty: pdoc.difficulty,
|
||||||
@@ -663,19 +627,16 @@ export class ProblemModel {
|
|||||||
nAccept: pdoc.nAccept,
|
nAccept: pdoc.nAccept,
|
||||||
difficulty: pdoc.difficulty,
|
difficulty: pdoc.difficulty,
|
||||||
});
|
});
|
||||||
const [c] = await Promise.all([
|
await fs.writeFile(problemYaml, problemYamlContent);
|
||||||
ProblemModel.getContent(pdoc.content.map((i) => ('id' in i ? i.id : i.from))),
|
|
||||||
fs.writeFile(problemYaml, problemYamlContent),
|
|
||||||
]);
|
|
||||||
for (const i of pdoc.content) {
|
for (const i of pdoc.content) {
|
||||||
await fs.writeFile(path.join(problemPath, `problem_${'id' in i ? i.id : i.from}.md`), [
|
await fs.writeFile(path.join(problemPath, `problem_${i.name}_${i.lang}.md`), [
|
||||||
'---',
|
'---',
|
||||||
yaml.dump({
|
yaml.dump({
|
||||||
name: i.name,
|
name: i.name,
|
||||||
lang: i.lang,
|
lang: i.lang,
|
||||||
}),
|
}),
|
||||||
'---',
|
'---',
|
||||||
c['id' in i ? i.id : i.from],
|
i.content,
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
}
|
}
|
||||||
if ((pdoc.data || []).length) {
|
if ((pdoc.data || []).length) {
|
||||||
|
|||||||
Reference in new issue
Block a user