feat: implement explicit sticker history cleanup (TASK-WP5-06)
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 3m16s

This commit is contained in:
suyx
2026-08-04 10:40:11 +08:00
parent b00500512e
commit c2521f7208
7 changed files with 766 additions and 2 deletions
+87
View File
@@ -180,6 +180,7 @@ import { ModelConfigurationError } from "./model-configuration.js";
import type { ModelConfigurationService } from "./model-configuration.js";
import { StickerReleaseError } from "./sticker-release-errors.js";
import type { StickerReleaseService } from "./sticker-releases.js";
import type { ManagedStorage } from "./managed-storage.js";
const defaultBootstrap: BootstrapResponse = {
app_version: "0.0.0",
@@ -209,6 +210,7 @@ export interface CreateAppOptions {
publicAssets?: PublicAssetResolver;
recentAssets?: RecentAssetService;
projects?: ProjectService;
storage?: ManagedStorage;
previewAssetAuthorizer?: (input: {
releaseVersion: string;
resourceId: string;
@@ -382,6 +384,19 @@ function stickerReleaseFailure(reply: FastifyReply, correlationId: string, error
return latestExportFailure(reply, correlationId, error);
}
function assetCleanupFailure(reply: FastifyReply, correlationId: string, error: unknown) {
const code = error instanceof Error ? error.message : "";
if (code === "ASSET_HISTORY_REFERENCE_CONFLICT" || code === "ASSET_CLEANUP_CANDIDATE_STALE") {
return reply.code(409).send(createErrorEnvelope({ code, correlationId }));
}
if (code === "IDEMPOTENCY_KEY_CONFLICT") {
return reply.code(409).send(createErrorEnvelope({ code: "IDEMPOTENCY_KEY_CONFLICT", correlationId }));
}
if (code === "cleanup_candidates_invalid") return reply.code(400).send(null);
if (code === "cleanup_uncommitted") return reply.code(503).send(createErrorEnvelope({ code: "AUTH_SERVICE_UNAVAILABLE", correlationId }));
return reply.code(503).send(createErrorEnvelope({ code: "AUTH_SERVICE_UNAVAILABLE", correlationId }));
}
function generationTaskResponse(task: GenerationTaskView) {
return {
confirmed_credit_cost: task.confirmedCreditCost,
@@ -965,6 +980,78 @@ export async function createApp(options: CreateAppOptions = {}) {
},
);
app.get(
"/api/v1/admin/assets/static-stickers/cleanup/candidates",
{ schema: { hide: true } },
async (request, reply) => {
if (!options.registration || !options.storage) {
return reply.code(503).send(createErrorEnvelope({ code: "AUTH_SERVICE_UNAVAILABLE", correlationId: request.id }));
}
const token = cookieValue(headerValue(request.headers.cookie), adminSessionCookieName);
const session = token ? options.registration.readAdminSession(token) : undefined;
if (!session) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
try {
reply.header("Cache-Control", "private, no-store");
return options.storage.listAssetCleanupCandidates();
} catch (error) {
return assetCleanupFailure(reply, request.id, error);
}
},
);
app.post(
"/api/v1/admin/assets/static-stickers/cleanup/intents",
{ schema: { hide: true } },
async (request, reply) => {
if (!options.registration || !options.storage) {
return reply.code(503).send(createErrorEnvelope({ code: "AUTH_SERVICE_UNAVAILABLE", correlationId: request.id }));
}
const token = cookieValue(headerValue(request.headers.cookie), adminSessionCookieName);
const csrfToken = headerValue(request.headers["x-csrf-token"]);
const idempotencyKey = headerValue(request.headers["idempotency-key"]);
if (!token || !csrfToken) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
if (!idempotencyKey || !/^[A-Za-z0-9_-]{32,200}$/.test(idempotencyKey) || !/^[A-Za-z0-9_-]{43,64}$/.test(csrfToken)) return reply.code(400).send(null);
try {
const admin = options.registration.authorizeAdminMutation({ csrfToken, sessionToken: token });
const body = request.body as { file_ids?: string[]; snapshot_version?: string } | undefined;
return reply.code(201).send(options.storage.createAssetCleanupIntent({
actorId: admin.userId,
fileIds: body?.file_ids ?? [],
idempotencyKey,
snapshotVersion: body?.snapshot_version ?? "",
}));
} catch (error) {
return assetCleanupFailure(reply, request.id, error);
}
},
);
app.post(
"/api/v1/admin/assets/static-stickers/cleanup/intents/:requestId/confirm",
{ schema: { hide: true } },
async (request, reply) => {
if (!options.registration || !options.storage) {
return reply.code(503).send(createErrorEnvelope({ code: "AUTH_SERVICE_UNAVAILABLE", correlationId: request.id }));
}
const token = cookieValue(headerValue(request.headers.cookie), adminSessionCookieName);
const csrfToken = headerValue(request.headers["x-csrf-token"]);
if (!token || !csrfToken) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
if (!/^[A-Za-z0-9_-]{43,64}$/.test(csrfToken)) return reply.code(400).send(null);
try {
const admin = options.registration.authorizeAdminMutation({ csrfToken, sessionToken: token });
const body = request.body as { confirmation_token?: string } | undefined;
const requestId = (request.params as { requestId: string }).requestId;
return reply.send(options.storage.confirmAssetCleanupIntent({
actorId: admin.userId,
confirmationToken: body?.confirmation_token ?? "",
requestId,
}));
} catch (error) {
return assetCleanupFailure(reply, request.id, error);
}
},
);
app.get(
"/api/v1/assets/public/:resourceVersion/manifest",
{ schema: { hide: true } },