feat: complete TASK-WP2-04 credit ledger

This commit is contained in:
suyx
2026-08-02 23:27:10 +08:00
parent 9b4860cfc2
commit 4551d73e76
24 changed files with 3002 additions and 6 deletions
+72
View File
@@ -0,0 +1,72 @@
import { Type, type Static } from "@sinclair/typebox";
import { CreditSummarySchema } from "./auth.js";
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$";
export const CreditEntryTypeSchema = Type.Union([
Type.Literal("registration_grant"), Type.Literal("generation_reserve"), Type.Literal("generation_commit"),
Type.Literal("generation_release"), Type.Literal("admin_adjustment"),
], { $id: "CreditEntryType" });
export const CreditEntryStatusSchema = Type.Union([
Type.Literal("succeeded"), Type.Literal("frozen"), Type.Literal("committed"), Type.Literal("released"),
], { $id: "CreditEntryStatus" });
export const CreditReferenceTypeSchema = Type.Union([
Type.Literal("registration"), Type.Literal("generation"), Type.Literal("admin_adjustment"),
], { $id: "CreditReferenceType" });
export const CreditBalanceResponseSchema = Type.Object({
...CreditSummarySchema.properties,
updated_at: Type.String({ pattern: isoTimestampPattern }),
}, { additionalProperties: false, $id: "CreditBalanceResponse" });
export const CreditLedgerEntrySchema = Type.Object({
amount: Type.Integer(),
available_after: Type.Integer(),
available_before: Type.Integer(),
created_at: Type.String({ pattern: isoTimestampPattern }),
entry_id: Type.String({ pattern: uuidPattern }),
entry_type: Type.Ref(CreditEntryTypeSchema),
model_id: Type.Union([Type.String({ maxLength: 160, minLength: 1 }), Type.Null()]),
reason: Type.Union([Type.String({ maxLength: 500, minLength: 1 }), Type.Null()]),
reference_id: Type.Union([Type.String({ pattern: uuidPattern }), Type.Null()]),
reference_type: Type.Union([Type.Ref(CreditReferenceTypeSchema), Type.Null()]),
reserved_after: Type.Integer({ minimum: 0 }),
reserved_before: Type.Integer({ minimum: 0 }),
status: Type.Ref(CreditEntryStatusSchema),
}, { additionalProperties: false, $id: "CreditLedgerEntry" });
export const CreditLedgerQuerySchema = Type.Object({
cursor: Type.Optional(Type.String({ maxLength: 300, minLength: 1, pattern: "^[A-Za-z0-9_-]+$" })),
event_type: Type.Optional(Type.Ref(CreditEntryTypeSchema)),
from: Type.Optional(Type.String({ pattern: isoTimestampPattern })),
limit: Type.Optional(Type.Integer({ maximum: 100, minimum: 1 })),
to: Type.Optional(Type.String({ pattern: isoTimestampPattern })),
}, { additionalProperties: false, $id: "CreditLedgerQuery" });
export const CreditLedgerResponseSchema = Type.Object({
credits: Type.Ref(CreditSummarySchema),
entries: Type.Array(Type.Ref(CreditLedgerEntrySchema), { maxItems: 100 }),
next_cursor: Type.Union([Type.String({ maxLength: 300, minLength: 1 }), Type.Null()]),
updated_at: Type.String({ pattern: isoTimestampPattern }),
}, { additionalProperties: false, $id: "CreditLedgerResponse" });
export const AdminCreditParamsSchema = Type.Object({
userId: Type.String({ pattern: uuidPattern }),
}, { additionalProperties: false, $id: "AdminCreditParams" });
export const CreditAdjustmentHeadersSchema = Type.Object({
"idempotency-key": Type.String({ maxLength: 200, minLength: 32, pattern: "^[A-Za-z0-9_-]+$" }),
"x-csrf-token": Type.String({ maxLength: 64, minLength: 43, pattern: "^[A-Za-z0-9_-]+$" }),
}, { additionalProperties: true, $id: "CreditAdjustmentHeaders" });
export const CreditAdjustmentRequestSchema = Type.Object({
adjustment_id: Type.String({ pattern: uuidPattern }),
amount: Type.Union([Type.Integer({ maximum: -1 }), Type.Integer({ minimum: 1 })]),
reason: Type.String({ maxLength: 500, minLength: 1 }),
}, { additionalProperties: false, $id: "CreditAdjustmentRequest" });
export const CreditAdjustmentResponseSchema = Type.Object({
adjustment_id: Type.String({ pattern: uuidPattern }),
available_balance: Type.Integer(),
reserved_balance: Type.Integer({ minimum: 0 }),
status: Type.Literal("adjusted"),
}, { additionalProperties: false, $id: "CreditAdjustmentResponse" });
export type CreditLedgerQuery = Static<typeof CreditLedgerQuerySchema>;
export type AdminCreditParams = Static<typeof AdminCreditParamsSchema>;
export type CreditAdjustmentHeaders = Static<typeof CreditAdjustmentHeadersSchema>;
export type CreditAdjustmentRequest = Static<typeof CreditAdjustmentRequestSchema>;
+1
View File
@@ -3,6 +3,7 @@ export * from "./api.js";
export * from "./auth.js";
export * from "./bootstrap.js";
export * from "./canvas.js";
export * from "./credits.js";
export * from "./events.js";
export * from "./projects.js";
export * from "./registration-notice.js";