feat: implement TASK-WP1-01 registration transaction

This commit is contained in:
suyx
2026-07-28 15:54:28 +08:00
parent 1bf7e42294
commit f467e7c09f
15 changed files with 2191 additions and 4 deletions
+27
View File
@@ -1,5 +1,7 @@
// Generated from openapi/openapi.json. Do not edit by hand.
import type { RegistrationCompleteResponse, RegistrationCompleteRequest, UserSessionResponse, RegistrationSendResponse, RegistrationSendRequest } from "./types.gen.js";
export interface ClientOptions { baseUrl?: string; fetch?: typeof globalThis.fetch; headers?: HeadersInit; }
export async function checkBrowserSupport(body: {
@@ -43,6 +45,15 @@ export async function checkBrowserSupport(body: {
}>;
}
export async function completeRegistration(body: RegistrationCompleteRequest, options: ClientOptions = {}): Promise<RegistrationCompleteResponse> {
const request = options.fetch ?? globalThis.fetch;
const headers = new Headers(options.headers);
headers.set("Content-Type", "application/json");
const response = await request(`${options.baseUrl ?? ""}/api/v1/auth/register/complete`, { body: JSON.stringify(body), method: "POST", headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json() as Promise<RegistrationCompleteResponse>;
}
export async function getBootstrap(options: ClientOptions = {}): Promise<{
"app_version": string;
"dependencies": Array<{
@@ -85,3 +96,19 @@ export async function getBootstrap(options: ClientOptions = {}): Promise<{
export function getEvents(options: Pick<ClientOptions, "baseUrl"> = {}): string {
return `${options.baseUrl ?? ""}/api/v1/events`;
}
export async function getUserSession(options: ClientOptions = {}): Promise<UserSessionResponse> {
const request = options.fetch ?? globalThis.fetch;
const response = await request(`${options.baseUrl ?? ""}/api/v1/auth/session`, { method: "GET", headers: options.headers ?? {} });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json() as Promise<UserSessionResponse>;
}
export async function sendRegistrationCode(body: RegistrationSendRequest, options: ClientOptions = {}): Promise<RegistrationSendResponse> {
const request = options.fetch ?? globalThis.fetch;
const headers = new Headers(options.headers);
headers.set("Content-Type", "application/json");
const response = await request(`${options.baseUrl ?? ""}/api/v1/auth/register/send`, { body: JSON.stringify(body), method: "POST", headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json() as Promise<RegistrationSendResponse>;
}
+56 -2
View File
@@ -1,5 +1,13 @@
// Generated from openapi/openapi.json. Do not edit by hand.
export type AuthenticatedUser = {
"creator_name": string;
"role": "user";
"social_id": string;
"status": "active";
"user_id": string;
};
export type BootstrapResponse = {
"app_version": string;
"dependencies": Array<{
@@ -47,6 +55,11 @@ export type BrowserUnsupportedReason = "platform_unsupported" | "brand_unsupport
export type CorrelationId = string;
export type CreditSummary = {
"available_balance": number;
"reserved_balance": number;
};
export type ErrorDetails = {
"capacity_status"?: "normal" | "warning" | "critical" | "full" | "unavailable";
"current_task_ref"?: string;
@@ -65,7 +78,7 @@ export type ErrorDetails = {
export type ErrorEnvelope = {
"error": {
"code": "BROWSER_UNSUPPORTED" | "MODEL_CONFIG_VERSION_CONFLICT" | "MODEL_DEFAULT_REPLACEMENT_REQUIRED" | "MODEL_DEFAULT_REPLACEMENT_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_CONFLICT" | "PRIVATE_CONTENT_NOTICE_ACK_REQUIRED" | "ASSET_HISTORY_REFERENCE_CONFLICT" | "ASSET_CLEANUP_CANDIDATE_STALE" | "STORAGE_CAPACITY_EXCEEDED";
"code": "BROWSER_UNSUPPORTED" | "MODEL_CONFIG_VERSION_CONFLICT" | "MODEL_DEFAULT_REPLACEMENT_REQUIRED" | "MODEL_DEFAULT_REPLACEMENT_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_CONFLICT" | "PRIVATE_CONTENT_NOTICE_ACK_REQUIRED" | "ASSET_HISTORY_REFERENCE_CONFLICT" | "ASSET_CLEANUP_CANDIDATE_STALE" | "STORAGE_CAPACITY_EXCEEDED" | "REGISTRATION_REJECTED" | "REGISTRATION_REQUEST_INVALID" | "IDEMPOTENCY_KEY_CONFLICT" | "AUTH_SESSION_INVALID" | "AUTH_SERVICE_UNAVAILABLE";
"correlation_id": string;
"details": {
"capacity_status"?: "normal" | "warning" | "critical" | "full" | "unavailable";
@@ -105,6 +118,38 @@ export type ModelRuntimeSseEvent = {
"runtime_availability_version": number;
};
export type RegistrationCompleteHeaders = {
"idempotency-key": string;
};
export type RegistrationCompleteRequest = {
"creator_name": string;
"privacy_consent_accepted": boolean;
"privacy_notice_version": string;
"registration_id": string;
"social_id": string;
"verification_code": string;
};
export type RegistrationCompleteResponse = {
"credits": CreditSummary;
"session_expires_at": string;
"status": "registered";
"user": AuthenticatedUser;
};
export type RegistrationSendRequest = {
"email": string;
"invite_code": string;
};
export type RegistrationSendResponse = {
"challenge_expires_at": string;
"registration_id": string;
"resend_available_at": string;
"status": "verification_sent";
};
export type SseEvent = {
"entity_ref": string;
"event_id": number;
@@ -125,7 +170,7 @@ export type SseEvent = {
"runtime_availability_version": number;
};
export type StableEngineeringErrorCode = "BROWSER_UNSUPPORTED" | "MODEL_CONFIG_VERSION_CONFLICT" | "MODEL_DEFAULT_REPLACEMENT_REQUIRED" | "MODEL_DEFAULT_REPLACEMENT_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_CONFLICT" | "PRIVATE_CONTENT_NOTICE_ACK_REQUIRED" | "ASSET_HISTORY_REFERENCE_CONFLICT" | "ASSET_CLEANUP_CANDIDATE_STALE" | "STORAGE_CAPACITY_EXCEEDED";
export type StableEngineeringErrorCode = "BROWSER_UNSUPPORTED" | "MODEL_CONFIG_VERSION_CONFLICT" | "MODEL_DEFAULT_REPLACEMENT_REQUIRED" | "MODEL_DEFAULT_REPLACEMENT_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_INVALID" | "MODEL_RECOMMENDATION_PRIORITY_CONFLICT" | "PRIVATE_CONTENT_NOTICE_ACK_REQUIRED" | "ASSET_HISTORY_REFERENCE_CONFLICT" | "ASSET_CLEANUP_CANDIDATE_STALE" | "STORAGE_CAPACITY_EXCEEDED" | "REGISTRATION_REJECTED" | "REGISTRATION_REQUEST_INVALID" | "IDEMPOTENCY_KEY_CONFLICT" | "AUTH_SESSION_INVALID" | "AUTH_SERVICE_UNAVAILABLE";
export type StateSseEvent = {
"entity_ref": string;
@@ -134,3 +179,12 @@ export type StateSseEvent = {
"occurred_at": string;
"state_version": number;
};
export type UserSessionResponse = {
"audience": "user";
"authenticated": true;
"credits": CreditSummary;
"csrf_token": string;
"expires_at": string;
"user": AuthenticatedUser;
};