feat: implement donation form with payment processing

Add main donate page with TMN and PromptPay flows, donation schema
with retry system, SSE integration, heartbeat endpoint, and
file-to-data-url utility. Remove unused donate client component.
This commit is contained in:
2026-06-07 23:04:51 +00:00 Unverified
parent acb16777d2
commit d34baefa6d
8 changed files with 211 additions and 99 deletions
+8 -3
View File
@@ -88,12 +88,12 @@ export const submissions = artifact.table("submissions", {
name: text().notNull(),
comment: text().notNull(),
char: text()
.notNull()
// .notNull() // For Special Queue
.references(() => characters.name, {
onDelete: "cascade",
onUpdate: "cascade",
}),
queue: serial(),
queue: serial(), // NULL for Spacial Queue
editToken: text().notNull().$defaultFn(uuidv7),
edits: integer().notNull().default(0),
checked: boolean().notNull().default(false),
@@ -348,9 +348,14 @@ export const schDonate = pgSchema("donate");
export const donations = schDonate.table("donations", {
id: text().primaryKey().$defaultFn(uuidv7),
name: text().notNull(),
amount: numeric().notNull(),
amount: numeric({ mode: "number", precision: 7, scale: 2 }).notNull(),
image: bytea(),
message: text(),
created: timestamp("created_at").defaultNow().notNull(),
// The retry system
lastPing: timestamp("last_ping").default(new Date("01-01-2000")).notNull(),
sent: boolean().default(false).notNull(),
uid: text(),
});
//#endregion
+2 -1
View File
@@ -33,9 +33,10 @@ export const sse = sseEndpointMap({
},
donate: {
ping: z.object({
id: z.string(),
name: z.string(),
amount: z.number(),
image: z.string(),
image: z.string().optional(),
message: z.string(),
}),
// roundtrip SSE system check
+5
View File
@@ -47,6 +47,11 @@ export function shortestPrefixes(values: readonly string[]) {
return res;
}
export async function fileToDataUrl(file: File) {
const buffer = Buffer.from(await file.arrayBuffer());
return `data:${file.type};base64,${buffer.toString("base64")}`;
}
export function parseSearchNumber(
param: string | string[] | undefined,
def: number = 0,