Add verified email API key signup
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
export * as schema from "./schema.js";
|
||||
export {
|
||||
jobs, clones, cache, apiKeys,
|
||||
type Job, type NewJob, type Clone, type NewClone, type CacheRow, type ApiKey,
|
||||
jobs, clones, cache, apiKeys, signupTokens,
|
||||
type Job, type NewJob, type Clone, type NewClone, type CacheRow, type ApiKey, type SignupToken, type NewSignupToken,
|
||||
} from "./schema.js";
|
||||
export { createDb, type Db, type DbHandle } from "./client.js";
|
||||
export * as repo from "./repo.js";
|
||||
|
||||
+20
-2
@@ -1,6 +1,6 @@
|
||||
import { and, desc, eq, gt, sql } from "drizzle-orm";
|
||||
import { and, desc, eq, gt, isNull, sql } from "drizzle-orm";
|
||||
import type { Db } from "./client.js";
|
||||
import { jobs, clones, cache, apiKeys, type Job, type NewJob, type Clone, type NewClone, type CacheRow, type ApiKey } from "./schema.js";
|
||||
import { jobs, clones, cache, apiKeys, signupTokens, type Job, type NewJob, type Clone, type NewClone, type CacheRow, type ApiKey, type SignupToken } from "./schema.js";
|
||||
|
||||
// ---- jobs ----
|
||||
|
||||
@@ -93,3 +93,21 @@ export async function createApiKey(db: Db, input: { keyHash: string; label?: str
|
||||
const [row] = await db.insert(apiKeys).values(input).returning();
|
||||
return row!;
|
||||
}
|
||||
|
||||
// ---- signup tokens ----
|
||||
|
||||
export async function createSignupToken(db: Db, input: { email: string; tokenHash: string; expiresAt: Date }): Promise<SignupToken> {
|
||||
const [row] = await db.insert(signupTokens).values(input).returning();
|
||||
return row!;
|
||||
}
|
||||
|
||||
/** Atomically consume a still-fresh signup token. Returns undefined for missing,
|
||||
* expired, or already-used tokens. */
|
||||
export async function consumeSignupToken(db: Db, tokenHash: string): Promise<SignupToken | undefined> {
|
||||
const [row] = await db
|
||||
.update(signupTokens)
|
||||
.set({ consumedAt: new Date() })
|
||||
.where(and(eq(signupTokens.tokenHash, tokenHash), gt(signupTokens.expiresAt, new Date()), isNull(signupTokens.consumedAt)))
|
||||
.returning();
|
||||
return row;
|
||||
}
|
||||
|
||||
@@ -56,9 +56,21 @@ export const apiKeys = pgTable("api_keys", {
|
||||
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
||||
});
|
||||
|
||||
/** One-time email verification tokens for public API-key signup. */
|
||||
export const signupTokens = pgTable("signup_tokens", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
email: text("email").notNull(),
|
||||
tokenHash: text("token_hash").notNull().unique(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
||||
consumedAt: timestamp("consumed_at", { withTimezone: true }),
|
||||
});
|
||||
|
||||
export type Job = typeof jobs.$inferSelect;
|
||||
export type NewJob = typeof jobs.$inferInsert;
|
||||
export type Clone = typeof clones.$inferSelect;
|
||||
export type NewClone = typeof clones.$inferInsert;
|
||||
export type CacheRow = typeof cache.$inferSelect;
|
||||
export type ApiKey = typeof apiKeys.$inferSelect;
|
||||
export type SignupToken = typeof signupTokens.$inferSelect;
|
||||
export type NewSignupToken = typeof signupTokens.$inferInsert;
|
||||
|
||||
Reference in New Issue
Block a user