feat: implement TASK-WP5-07 preview grant lifecycle
Dada P0-A isolated Windows CI / validate-and-package (push) Successful in 14m18s

This commit is contained in:
suyx
2026-08-04 01:29:29 +08:00
parent eed125c118
commit 2c803454de
3 changed files with 684 additions and 0 deletions
+22
View File
@@ -174,6 +174,7 @@ import {
registrationFieldError,
} from "./registration-errors.js";
import type { RegistrationService } from "./registration.js";
import type { AssetPreviewGrantService } from "./preview-grants.js";
import type { RecentAssetService } from "./recent-assets.js";
import type { AmapAdapter } from "./amap-adapter.js";
import { ModelConfigurationError } from "./model-configuration.js";
@@ -212,6 +213,7 @@ export interface CreateAppOptions {
resourceId: string;
userId: string;
}) => boolean | Promise<boolean>;
previewGrants?: AssetPreviewGrantService;
privateAssetAdminAuthorizer?: (input: {
adminUserId: string;
ownerId: string;
@@ -871,6 +873,13 @@ export async function createApp(options: CreateAppOptions = {}) {
const session = token ? options.registration.readUserSession(token) : undefined;
if (!session) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
const { resourceVersion } = request.params as { resourceVersion: string };
if (options.previewGrants) {
const manifest = options.previewGrants.projectManifest({ releaseVersion: resourceVersion, userId: session.userId });
if (!manifest) return reply.code(404).send();
reply.header("Cache-Control", "private, no-store");
reply.header("Vary", "Cookie");
return manifest;
}
const available = options.assetReleases?.project("internal_preview_asset", resourceVersion);
if (!available || !options.previewAssetAuthorizer) return reply.code(404).send();
const authorizedIds: string[] = [];
@@ -901,6 +910,19 @@ export async function createApp(options: CreateAppOptions = {}) {
const session = token ? options.registration.readUserSession(token) : undefined;
if (!session) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
const { assetId, resourceVersion } = request.params as { assetId: string; resourceVersion: string };
if (options.previewGrants) {
const resource = options.previewGrants.readManifestItem({
manifestItemId: assetId,
releaseVersion: resourceVersion,
userId: session.userId,
});
if (!resource) return reply.code(404).send();
reply.type(resource.mimeType);
reply.header("Cache-Control", "private, no-store");
reply.header("Content-Disposition", "inline");
reply.header("Vary", "Cookie");
return resource.bytes;
}
const authorized = await options.previewAssetAuthorizer?.({ resourceId: assetId, releaseVersion: resourceVersion, userId: session.userId });
const resource = authorized ? options.assetReleases?.read("internal_preview_asset", resourceVersion, assetId) : undefined;
if (!resource) return reply.code(404).send();