feat: implement explicit sticker history cleanup (TASK-WP5-06)
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 3m16s
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 3m16s
This commit is contained in:
@@ -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 } },
|
||||
|
||||
Reference in New Issue
Block a user