feat: implement TASK-WP1-01 registration transaction
This commit is contained in:
@@ -14,6 +14,11 @@ export const stableEngineeringErrors = {
|
||||
ASSET_HISTORY_REFERENCE_CONFLICT: { httpStatus: 409, messageKey: "ASSET_HISTORY_REFERENCE_CONFLICT" },
|
||||
ASSET_CLEANUP_CANDIDATE_STALE: { httpStatus: 409, messageKey: "ASSET_CLEANUP_CANDIDATE_STALE" },
|
||||
STORAGE_CAPACITY_EXCEEDED: { httpStatus: 507, messageKey: "STORAGE_CAPACITY_EXCEEDED" },
|
||||
REGISTRATION_REJECTED: { httpStatus: 409, messageKey: "auth.registration.rejected" },
|
||||
REGISTRATION_REQUEST_INVALID: { httpStatus: 400, messageKey: "auth.registration.request_invalid" },
|
||||
IDEMPOTENCY_KEY_CONFLICT: { httpStatus: 409, messageKey: "request.idempotency_conflict" },
|
||||
AUTH_SESSION_INVALID: { httpStatus: 401, messageKey: "auth.session.invalid" },
|
||||
AUTH_SERVICE_UNAVAILABLE: { httpStatus: 503, messageKey: "auth.service.unavailable" },
|
||||
} as const;
|
||||
|
||||
export type StableEngineeringErrorCode = keyof typeof stableEngineeringErrors;
|
||||
@@ -45,6 +50,11 @@ export const StableEngineeringErrorCodeSchema = Type.Union(
|
||||
Type.Literal("ASSET_HISTORY_REFERENCE_CONFLICT"),
|
||||
Type.Literal("ASSET_CLEANUP_CANDIDATE_STALE"),
|
||||
Type.Literal("STORAGE_CAPACITY_EXCEEDED"),
|
||||
Type.Literal("REGISTRATION_REJECTED"),
|
||||
Type.Literal("REGISTRATION_REQUEST_INVALID"),
|
||||
Type.Literal("IDEMPOTENCY_KEY_CONFLICT"),
|
||||
Type.Literal("AUTH_SESSION_INVALID"),
|
||||
Type.Literal("AUTH_SERVICE_UNAVAILABLE"),
|
||||
],
|
||||
{ $id: "StableEngineeringErrorCode" },
|
||||
);
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { Type, type Static } from "@sinclair/typebox";
|
||||
|
||||
const uuidPattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$";
|
||||
const isoTimestampPattern = "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$";
|
||||
const emailPattern = "^[^@\\s]{1,128}@[^@\\s]{1,190}$";
|
||||
|
||||
export const RegistrationSendRequestSchema = Type.Object(
|
||||
{
|
||||
email: Type.String({ maxLength: 320, pattern: emailPattern }),
|
||||
invite_code: Type.String({ maxLength: 160, minLength: 8 }),
|
||||
},
|
||||
{ additionalProperties: false, $id: "RegistrationSendRequest" },
|
||||
);
|
||||
|
||||
export const RegistrationSendResponseSchema = Type.Object(
|
||||
{
|
||||
challenge_expires_at: Type.String({ pattern: isoTimestampPattern }),
|
||||
registration_id: Type.String({ pattern: uuidPattern }),
|
||||
resend_available_at: Type.String({ pattern: isoTimestampPattern }),
|
||||
status: Type.Literal("verification_sent"),
|
||||
},
|
||||
{ additionalProperties: false, $id: "RegistrationSendResponse" },
|
||||
);
|
||||
|
||||
export const RegistrationCompleteRequestSchema = Type.Object(
|
||||
{
|
||||
creator_name: Type.String({ maxLength: 80, minLength: 1 }),
|
||||
privacy_consent_accepted: Type.Boolean(),
|
||||
privacy_notice_version: Type.String({ maxLength: 80, minLength: 1 }),
|
||||
registration_id: Type.String({ pattern: uuidPattern }),
|
||||
social_id: Type.String({ maxLength: 80, minLength: 1 }),
|
||||
verification_code: Type.String({ pattern: "^[0-9]{6}$" }),
|
||||
},
|
||||
{ additionalProperties: false, $id: "RegistrationCompleteRequest" },
|
||||
);
|
||||
|
||||
export const RegistrationCompleteHeadersSchema = Type.Object(
|
||||
{
|
||||
"idempotency-key": Type.String({ maxLength: 200, minLength: 32, pattern: "^[A-Za-z0-9_-]+$" }),
|
||||
},
|
||||
{ additionalProperties: true, $id: "RegistrationCompleteHeaders" },
|
||||
);
|
||||
|
||||
export const AuthenticatedUserSchema = Type.Object(
|
||||
{
|
||||
creator_name: Type.String({ maxLength: 80, minLength: 1 }),
|
||||
role: Type.Literal("user"),
|
||||
social_id: Type.String({ maxLength: 80, minLength: 1 }),
|
||||
status: Type.Literal("active"),
|
||||
user_id: Type.String({ pattern: uuidPattern }),
|
||||
},
|
||||
{ additionalProperties: false, $id: "AuthenticatedUser" },
|
||||
);
|
||||
|
||||
export const CreditSummarySchema = Type.Object(
|
||||
{
|
||||
available_balance: Type.Integer(),
|
||||
reserved_balance: Type.Integer({ minimum: 0 }),
|
||||
},
|
||||
{ additionalProperties: false, $id: "CreditSummary" },
|
||||
);
|
||||
|
||||
export const RegistrationCompleteResponseSchema = Type.Object(
|
||||
{
|
||||
credits: Type.Ref(CreditSummarySchema),
|
||||
session_expires_at: Type.String({ pattern: isoTimestampPattern }),
|
||||
status: Type.Literal("registered"),
|
||||
user: Type.Ref(AuthenticatedUserSchema),
|
||||
},
|
||||
{ additionalProperties: false, $id: "RegistrationCompleteResponse" },
|
||||
);
|
||||
|
||||
export const UserSessionResponseSchema = Type.Object(
|
||||
{
|
||||
audience: Type.Literal("user"),
|
||||
authenticated: Type.Literal(true),
|
||||
credits: Type.Ref(CreditSummarySchema),
|
||||
csrf_token: Type.String({ maxLength: 64, minLength: 43, pattern: "^[A-Za-z0-9_-]+$" }),
|
||||
expires_at: Type.String({ pattern: isoTimestampPattern }),
|
||||
user: Type.Ref(AuthenticatedUserSchema),
|
||||
},
|
||||
{ additionalProperties: false, $id: "UserSessionResponse" },
|
||||
);
|
||||
|
||||
export type RegistrationSendRequest = Static<typeof RegistrationSendRequestSchema>;
|
||||
export type RegistrationSendResponse = Static<typeof RegistrationSendResponseSchema>;
|
||||
export type RegistrationCompleteRequest = Static<typeof RegistrationCompleteRequestSchema>;
|
||||
export type RegistrationCompleteResponse = Static<typeof RegistrationCompleteResponseSchema>;
|
||||
export type UserSessionResponse = Static<typeof UserSessionResponseSchema>;
|
||||
@@ -1,4 +1,5 @@
|
||||
export { Type } from "@sinclair/typebox";
|
||||
export * from "./api.js";
|
||||
export * from "./auth.js";
|
||||
export * from "./bootstrap.js";
|
||||
export * from "./events.js";
|
||||
|
||||
Reference in New Issue
Block a user