feat: enforce api production safety limits

This commit is contained in:
Codex
2026-06-28 21:20:38 +08:00
parent a8e0ac78be
commit 1d873b2e50
8 changed files with 177 additions and 14 deletions

View File

@@ -1,5 +1,6 @@
import { createHash, randomUUID } from 'node:crypto';
import type pg from 'pg';
import { config } from '../../core/config.js';
import { HttpError, type RequestContext } from '../../core/http.js';
import { intParam, readJsonBody, requiredString, stringParam } from '../../core/request.js';
import { query, queryOne, transaction } from '../../core/db.js';
@@ -2308,25 +2309,25 @@ async function runGenericImport<T>(
export async function previewQuestionsImportRoute(ctx: RequestContext) {
const auth = await requireTenantContentEditor(ctx);
const body = await readJsonBody(ctx);
const body = await readJsonBody(ctx, { maxBytes: config.maxImportJsonBodyBytes });
return createQuestionPreviewJob(auth, body);
}
export async function previewVocabularyImportRoute(ctx: RequestContext) {
const auth = await requireTenantContentEditor(ctx);
const body = await readJsonBody(ctx);
const body = await readJsonBody(ctx, { maxBytes: config.maxImportJsonBodyBytes });
return createGenericPreviewJob(auth, body, 'vocabulary', 'vocabulary_unit', createVocabularyNormalizedItems(body));
}
export async function previewHandbookImportRoute(ctx: RequestContext) {
const auth = await requireTenantContentEditor(ctx);
const body = await readJsonBody(ctx);
const body = await readJsonBody(ctx, { maxBytes: config.maxImportJsonBodyBytes });
return createGenericPreviewJob(auth, body, 'handbook', 'handbook_subject', createHandbookNormalizedItems(body));
}
export async function importQuestionsRoute(ctx: RequestContext) {
const auth = await requireTenantContentEditor(ctx);
const body = await readJsonBody(ctx);
const body = await readJsonBody(ctx, { maxBytes: config.maxImportJsonBodyBytes });
const allowPartial = boolValue(body.allowPartial, false);
const jobId = nullableString(body.previewJobId) || nullableString(body.jobId);
@@ -2441,7 +2442,7 @@ export async function importQuestionsRoute(ctx: RequestContext) {
export async function importVocabularyRoute(ctx: RequestContext) {
const auth = await requireTenantContentEditor(ctx);
const body = await readJsonBody(ctx);
const body = await readJsonBody(ctx, { maxBytes: config.maxImportJsonBodyBytes });
return runGenericImport(
auth,
body,
@@ -2453,7 +2454,7 @@ export async function importVocabularyRoute(ctx: RequestContext) {
export async function importHandbookRoute(ctx: RequestContext) {
const auth = await requireTenantContentEditor(ctx);
const body = await readJsonBody(ctx);
const body = await readJsonBody(ctx, { maxBytes: config.maxImportJsonBodyBytes });
return runGenericImport(
auth,
body,