feat: integrate platform backend and application interfaces

This commit is contained in:
Qiufeng
2026-09-02 21:09:47 +08:00
parent cd9ac1af70
commit b16390dd41
648 changed files with 102488 additions and 17737 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"name": "@miragenflow/contracts",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
}
}
+462
View File
@@ -0,0 +1,462 @@
export const CONTRACT_VERSION = "2026-08-20.v1" as const;
export const API_PREFIX = "/api/v1" as const;
export const WS_PATH = "/api/v1/ws/tasks" as const;
export const ErrorCodes = {
Unauthorized: "UNAUTHORIZED",
Forbidden: "FORBIDDEN",
ValidationFailed: "VALIDATION_FAILED",
RateLimited: "RATE_LIMITED",
NotFound: "NOT_FOUND",
Conflict: "CONFLICT",
InsufficientBalance: "INSUFFICIENT_BALANCE",
CapabilityNotEnabled: "CAPABILITY_NOT_ENABLED",
ModelGroupUnavailable: "MODEL_GROUP_UNAVAILABLE",
Internal: "INTERNAL_ERROR",
VerificationRequired: "VERIFICATION_REQUIRED",
VerificationExpired: "VERIFICATION_EXPIRED",
SessionReused: "SESSION_REUSED",
IdempotencyConflict: "IDEMPOTENCY_CONFLICT",
ObjectNotFound: "OBJECT_NOT_FOUND",
ObjectOwnershipDenied: "OBJECT_OWNERSHIP_DENIED",
FileRejected: "FILE_REJECTED",
StorageUnavailable: "STORAGE_UNAVAILABLE",
ApprovalRequired: "APPROVAL_REQUIRED",
VersionConflict: "VERSION_CONFLICT",
AdminMfaRequired: "ADMIN_MFA_REQUIRED",
} as const;
export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
export type ApiError = {
code: ErrorCode;
message: string;
requestId: string;
details?: Record<string, unknown>;
};
export type ApiEnvelope<T> =
| { ok: true; data: T; requestId: string }
| { ok: false; error: ApiError };
export type AuthUser = {
id: string;
username?: string;
email?: string;
phone?: string;
displayName: string;
status: "pending_verification" | "active" | "frozen" | "closing";
mfaRequired: boolean;
mfaEnabled: boolean;
mfaSetupRequired: boolean;
};
export type SessionResponse = {
user: AuthUser;
accessToken: string;
accessTokenExpiresAt: string;
};
export type MfaEnrollment = {
secret: string;
provisioningUri: string;
recoveryCodes: string[];
};
export type UserMfaChallengeResponse =
| { mfaRequired: true; challengeId: string; challengeType: "verify" }
| { mfaRequired: true; challengeId: string; challengeType: "setup"; mfaEnrollment: MfaEnrollment };
export type UserMfaSetupResponse = {
enabled: boolean;
challengeId: string;
mfaEnrollment: MfaEnrollment;
};
export type UserMfaVerifyResponse =
| (SessionResponse & { mfaRequired: false })
| { enabled: true; user: AuthUser };
export type CaptchaAction = "register" | "login" | "verify" | "reset" | "admin-login";
export type CaptchaChallengeResponse = { challengeId: string; question: string; expiresAt: string };
export type ModelCapability = "image" | "text" | "audio" | "utility" | "3d" | "reverse-prompt" | "multi-angle" | "matting" | "split" | "upscale" | "expand";
export type CapabilitySchema = {
capability: ModelCapability;
inputTypes: Array<"prompt" | "image" | "mask" | "audio" | "text">;
maxInputBytes?: number;
parameters?: Record<string, { type: string; required?: boolean; enum?: string[]; min?: number; max?: number }>;
resultTypes: Array<"image" | "text" | "audio" | "archive" | "3d">;
async: boolean;
};
export type PublicResolution = {
id: string;
label: string;
width: number;
height: number;
priceMultiplier: number;
/** Standard image ratios enabled for this billing preset; `auto` is implicit. */
ratios?: string[];
};
export type ReferenceImageInput = {
objectId: string;
seq: number;
name: string;
};
export type ReferenceImageGroup = {
id?: string;
images: ReferenceImageInput[];
};
export type ImageOutputMetadata = {
width?: number;
height?: number;
format?: "png" | "jpeg" | "webp" | string;
size?: string;
/** Public resolution tier selected for this task, when applicable. */
resolution?: "1K" | "2K" | "4K" | string;
revisedPrompt?: string;
usage?: Record<string, number>;
source?: "base64" | "url";
};
export type PublicModelProduct = {
id: string;
publicModelId: string;
name: string;
tier: "basic" | "advanced" | "flagship";
capabilities: ModelCapability[];
resolutions: PublicResolution[];
basePrice: number;
currencyUnit: string;
enabled: boolean;
version?: number;
capabilitiesSchema?: CapabilitySchema[];
publicErrorMessages?: Partial<Record<ErrorCode, string>>;
maxCount?: number;
maxConcurrent?: number;
};
export type InternalModelProduct = PublicModelProduct & {
channelGroupId?: string;
};
export type BalanceSummary = {
available: number;
reserved: number;
unit: { name: string; shortCode: string; icon: string; precision: number; version: number };
/** Reserved for future organization/team balances; V1 always omits it. */
organizationId?: string;
};
/** Organization/team scope is deliberately a nullable contract extension in
* V1. No organization records or sharing endpoints are enabled yet. */
export type OrganizationScope = {
organizationId?: string;
workspaceOwnerId?: string;
objectScope?: "user" | "organization";
};
export type BalanceLedgerEntry = {
id: string;
userId: string;
type: "recharge" | "reserve" | "settle" | "release" | "refund" | "adjustment";
amount: number;
balanceUnitVersion: number;
idempotencyKey: string;
createdAt: string;
referenceId?: string;
bucketId?: string;
taskId?: string;
outputId?: string;
expiresAt?: string;
status?: "posted" | "voided";
};
export type BalanceBucket = OrganizationScope & {
id: string;
userId: string;
source: "recharge" | "plan" | "manual" | "bonus";
remaining: number;
expiresAt?: string;
priority: number;
};
export type BalanceUnitConfig = {
name: string;
shortCode: string;
icon: string;
precision: number;
version: number;
currencyCode: string;
conversionRate: number;
rounding: "floor" | "ceil" | "round";
};
export type MembershipPlan = {
id: string;
code: string;
name: string;
icon?: string;
description: string;
durationDays: number;
purchaseAmount: number;
includedBalance: number;
published: boolean;
allowedModelProductIds: string[];
allowedChannelGroupIds: string[];
maxConcurrent: number;
queuePriority: number;
retentionDays: number;
maxRetentionExtensionDays: number;
version: number;
};
export type TaskType = "image" | "text" | "audio" | "reverse-prompt" | "multi-angle" | "matting" | "split" | "upscale" | "expand" | "3d";
export type TaskStatus = "queued" | "running" | "succeeded" | "partial" | "failed" | "unknown" | "canceled";
export type CreateTaskRequest = OrganizationScope & {
modelProductId: string;
resolutionPresetId?: string;
prompt?: string;
references?: string[];
referenceGroup?: ReferenceImageGroup;
maskObjectId?: string;
count?: number;
canvasProjectId?: string;
params?: Record<string, unknown>;
};
export type GenerationTask = OrganizationScope & {
id: string;
ownerId: string;
taskType: TaskType;
modelProductId: string;
status: TaskStatus;
estimatedCost: number;
reservedCost: number;
createdAt: string;
updatedAt: string;
eventSequence: number;
channelGroupId: string;
routeSnapshotVersion: number;
attempts: GenerationAttempt[];
outputs: Array<{ id: string; mimeType: string; url?: string; status: "succeeded" | "failed"; unitPriceSnapshot?: number; chargedAmount?: number; metadata?: ImageOutputMetadata }>;
publicErrorCode?: ErrorCode;
prompt?: string;
resolutionPresetId?: string;
count?: number;
pricingSnapshot?: { basePrice: number; multiplier: number; unitVersion: number };
modelSnapshot?: { publicModelId: string; version: number };
routeSnapshot?: { groupId: string; version: number; channelIds: string[]; retryBudget?: number; channelRetryBudgets?: Record<string, number> };
planSnapshot?: { planId?: string; version?: number; queuePriority: number; maxConcurrent: number };
leaseExpiresAt?: string;
/** Fences a late worker/provider response after a lease is recovered. */
leaseToken?: string;
providerSubmitted?: boolean;
idempotencyKey?: string;
requestFingerprint?: string;
retentionExpiresAt?: string;
retentionState?: "active" | "archiving" | "archive_failed" | "archived" | "deleted";
retentionExtensionDays?: number;
reserveExpiresAt?: string;
inputSnapshot?: { references: string[]; referenceGroup?: ReferenceImageGroup; maskObjectId?: string; params: Record<string, unknown>; originalPrompt?: string };
};
export type GenerationAttempt = {
id: string;
channelId: string;
sequence: number;
status: "started" | "succeeded" | "failed" | "unknown";
errorCode?: string;
providerRequestId?: string;
platformIdempotencyKey?: string;
startedAt: string;
finishedAt?: string;
leaseExpiresAt?: string;
retryable?: boolean;
reconciliationStatus?: "pending" | "confirmed_success" | "confirmed_failure" | "manual_review";
};
export type PublicGenerationAttempt = {
id: string;
sequence: number;
status: GenerationAttempt["status"];
startedAt: string;
finishedAt?: string;
retryable?: boolean;
reconciliationStatus?: GenerationAttempt["reconciliationStatus"];
};
export type PublicGenerationTask = {
id: string;
taskType: TaskType;
modelProductId: string;
status: TaskStatus;
estimatedCost: number;
reservedCost: number;
createdAt: string;
updatedAt: string;
eventSequence: number;
publicErrorCode?: ErrorCode;
prompt?: string;
resolutionPresetId?: string;
count?: number;
modelSnapshot?: GenerationTask["modelSnapshot"];
retentionExpiresAt?: string;
retentionState?: GenerationTask["retentionState"];
retentionExtensionDays?: number;
inputSnapshot?: GenerationTask["inputSnapshot"];
attempts: PublicGenerationAttempt[];
outputs: GenerationTask["outputs"];
};
export type TaskEvent = {
eventId: string;
sequence: number;
taskId: string;
attemptId?: string;
type: "task.queued" | "task.running" | "task.attempt" | "task.progress" | "task.succeeded" | "task.failed" | "task.canceled" | "task.unknown";
occurredAt: string;
payload: Record<string, unknown>;
};
export type PaymentWebhookRequest = { orderId: string; eventId: string; status: "paid" | "failed" | "refunded"; amount?: number; currency?: string };
export type UploadObject = OrganizationScope & {
id: string;
idempotencyKey?: string;
ownerId: string;
mimeType: string;
size: number;
checksum: string;
status: "pending" | "scanning" | "ready" | "rejected" | "deleted";
stagingKey?: string;
expiresAt: string;
};
export type AssetRecord = OrganizationScope & {
id: string;
ownerId: string;
type: "image" | "text" | "audio" | "archive" | "3d";
objectId?: string;
taskId?: string;
sourceOutputId?: string;
name: string;
tags: string[];
deletedAt?: string;
retentionExpiresAt?: string;
pinned?: boolean;
objectUrl?: string;
mimeType?: string;
};
export type WebDavConfigStatus = {
configured: boolean;
url?: string;
username?: string;
directory?: string;
lastSyncedAt?: string;
state: "unconfigured" | "ready" | "syncing" | "error";
};
export type WsClientMessage =
| { type: "subscribe"; taskId: string; cursor?: number }
| { type: "unsubscribe"; taskId: string }
| { type: "ping" };
export type WsServerMessage =
| { type: "ready"; contractVersion: string; heartbeatIntervalMs: number }
| { type: "pong"; at: string }
| { type: "event"; event: TaskEvent }
| { type: "error"; error: ApiError };
export type HealthResponse = {
status: "ok";
service: "miragenflow-server";
contractVersion: string;
now: string;
dependencies?: { persistence: { adapter: string; status: string; detail?: string }; queue: { adapter: string; status: string; detail?: string } };
};
export type AdminRole = "super_admin" | "operator" | "finance" | "support" | "auditor";
export type AdminScope = "admin:read" | "admin:write" | "admin:finance" | "admin:security" | "admin:audit";
export type AdminUser = {
id: string;
email: string;
roles: AdminRole[];
scopes: AdminScope[];
mfaRequired: boolean;
};
export type AdminSessionResponse = {
admin: AdminUser;
accessToken?: string;
accessTokenExpiresAt?: string;
mfaRequired?: boolean;
challengeId?: string;
};
export type AdminPage<T> = {
items: T[];
total?: number;
nextCursor?: string;
};
export type AdminChannelSummary = {
id: string;
label: string;
enabled: boolean;
priority: number;
health: "healthy" | "degraded" | "open";
version: number;
retryBudget: number;
consecutiveFailures: number;
providerType?: "generic" | "openai-images";
providerName?: string;
providerModelId?: string;
enabledModelIds?: string[];
modelMappings?: Array<{ displayModelId: string; requestModelId: string }>;
resolutionModelMap?: Record<string, string>;
baseUrl?: string;
secretConfigured?: boolean;
};
export type AdminTaskSummary = {
id: string;
ownerId: string;
taskType: TaskType;
modelProductId: string;
status: TaskStatus;
estimatedCost: number;
reservedCost: number;
createdAt: string;
updatedAt: string;
attemptCount: number;
outputCount: number;
publicErrorCode?: ErrorCode;
};
export type AdminAuditEntry = {
id: string;
actorId?: string;
action: string;
category?: string;
actionLabel?: string;
actorName?: string;
actorType?: "admin" | "user" | "system" | "payment" | "unknown";
objectType: string;
objectId?: string;
requestId: string;
createdAt: string;
statusCode?: number;
level?: "normal" | "error";
before?: unknown;
after?: unknown;
};