feat: complete TASK-WP4-03 text templates

This commit is contained in:
suyx
2026-08-03 11:40:45 +08:00
parent d16802c164
commit dd7a23a281
24 changed files with 1934 additions and 20 deletions
+86
View File
@@ -87,6 +87,12 @@ import {
ProjectStateSaveResponseSchema,
ProjectSummarySchema,
ProjectViewStatusSchema,
RecentAssetItemSchema,
RecentAssetKindSchema,
RecentAssetListResponseSchema,
RecentAssetQuerySchema,
RecentAssetRecordRequestSchema,
RecentAssetRecordResponseSchema,
RegistrationCompleteHeadersSchema,
RegistrationCompleteRequestSchema,
RegistrationCompleteResponseSchema,
@@ -120,6 +126,8 @@ import {
type ProjectRenameRequest,
type ProjectEditableState,
type ProjectStateSaveHeaders,
type RecentAssetQuery,
type RecentAssetRecordRequest,
type RegistrationCompleteRequest,
type RegistrationSendRequest,
} from "@dada/shared-contracts";
@@ -162,6 +170,7 @@ import {
registrationFieldError,
} from "./registration-errors.js";
import type { RegistrationService } from "./registration.js";
import type { RecentAssetService } from "./recent-assets.js";
import { ModelConfigurationError } from "./model-configuration.js";
import type { ModelConfigurationService } from "./model-configuration.js";
@@ -189,6 +198,7 @@ export interface CreateAppOptions {
models?: ModelConfigurationService;
networkBoundary?: NetworkBoundaryOptions;
publicAssets?: PublicAssetResolver;
recentAssets?: RecentAssetService;
projects?: ProjectService;
registration?: RegistrationService;
}
@@ -726,6 +736,12 @@ export async function createApp(options: CreateAppOptions = {}) {
ProjectStateSaveHeadersSchema,
ProjectStateSaveResponseSchema,
ProjectStateConflictResponseSchema,
RecentAssetKindSchema,
RecentAssetItemSchema,
RecentAssetQuerySchema,
RecentAssetListResponseSchema,
RecentAssetRecordRequestSchema,
RecentAssetRecordResponseSchema,
FailedEmptyTrashRequestSchema,
FailedEmptyTrashResponseSchema,
ModelIdSchema,
@@ -810,6 +826,76 @@ export async function createApp(options: CreateAppOptions = {}) {
},
);
app.get(
"/api/v1/assets/recent",
{
attachValidation: true,
schema: {
operationId: "listRecentAssets",
querystring: Type.Ref(RecentAssetQuerySchema),
response: {
200: Type.Ref(RecentAssetListResponseSchema),
400: Type.Null(),
401: Type.Ref(ErrorEnvelopeSchema),
503: Type.Ref(ErrorEnvelopeSchema),
},
tags: ["Assets"],
},
},
async (request, reply) => {
if (request.validationError) return reply.code(400).send(null);
if (!options.registration || !options.recentAssets) {
return reply.code(503).send(createErrorEnvelope({ code: "AUTH_SERVICE_UNAVAILABLE", correlationId: request.id }));
}
const token = cookieValue(headerValue(request.headers.cookie), userSessionCookieName);
const session = token ? options.registration.readUserSession(token) : undefined;
if (!session) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
const query = request.query as RecentAssetQuery;
return { items: options.recentAssets.list(session.userId, query.asset_kind) };
},
);
app.post(
"/api/v1/assets/recent",
{
attachValidation: true,
schema: {
body: Type.Ref(RecentAssetRecordRequestSchema),
headers: Type.Ref(CsrfHeadersSchema),
operationId: "recordRecentAsset",
response: {
200: Type.Ref(RecentAssetRecordResponseSchema),
400: Type.Null(),
401: Type.Ref(ErrorEnvelopeSchema),
503: Type.Ref(ErrorEnvelopeSchema),
},
tags: ["Assets"],
},
},
async (request, reply) => {
if (request.validationError) return reply.code(400).send(null);
if (!options.registration || !options.recentAssets) {
return reply.code(503).send(createErrorEnvelope({ code: "AUTH_SERVICE_UNAVAILABLE", correlationId: request.id }));
}
const token = cookieValue(headerValue(request.headers.cookie), userSessionCookieName);
const csrfToken = headerValue(request.headers["x-csrf-token"]);
if (!token || !csrfToken) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
try {
const owner = options.registration.authorizeUserMutation({ csrfToken, sessionToken: token });
const body = request.body as RecentAssetRecordRequest;
options.recentAssets.recordSuccessfulUse({
assetId: body.asset_id,
assetKind: body.asset_kind,
resourceVersion: body.resource_version,
userId: owner.userId,
});
return { status: "recorded" as const };
} catch (error) {
return registrationFailure(reply, request.id, error);
}
},
);
app.post(
"/api/v1/admin-auth/login/send",
{