From a22112dc7794b2b2824625880ef5b571770e2a98 Mon Sep 17 00:00:00 2001 From: M1 Date: Mon, 16 Mar 2026 13:37:20 +0400 Subject: [PATCH] refactor: merge auth into account prefix (/account/register, /account/email) --- apps/web/src/dashboard/index.html | 2 +- apps/web/src/index.ts | 3 +- apps/web/src/routes/auth.ts | 46 ++++++++++++++----------------- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/apps/web/src/dashboard/index.html b/apps/web/src/dashboard/index.html index d45998d..9e0c76e 100644 --- a/apps/web/src/dashboard/index.html +++ b/apps/web/src/dashboard/index.html @@ -119,7 +119,7 @@ document.getElementById('register-btn').addEventListener('click', async () => { setLoading('register-btn', true, 'Creating...'); try { - const res = await fetch(`${API}/auth/register`, { + const res = await fetch(`${API}/account/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), diff --git a/apps/web/src/index.ts b/apps/web/src/index.ts index 46c067b..2d3d1cd 100644 --- a/apps/web/src/index.ts +++ b/apps/web/src/index.ts @@ -3,7 +3,7 @@ import { cors } from "@elysiajs/cors"; import { swagger } from "@elysiajs/swagger"; import { checks } from "./routes/checks"; import { monitors } from "./routes/monitors"; -import { auth, account } from "./routes/auth"; +import { account } from "./routes/auth"; import { internal } from "./routes/internal"; import { dashboard } from "./routes/dashboard"; import { migrate } from "./db"; @@ -15,7 +15,6 @@ const app = new Elysia() .use(swagger({ path: "/docs", documentation: { info: { title: "PingQL API", version: "0.1.0" } } })) .get("/", () => ({ name: "PingQL", version: "0.1.0", docs: "/docs", dashboard: "/dashboard" }), { detail: { hide: true } }) .use(dashboard) - .use(auth) .use(account) .use(monitors) .use(checks) diff --git a/apps/web/src/routes/auth.ts b/apps/web/src/routes/auth.ts index 71d7906..0ecb82d 100644 --- a/apps/web/src/routes/auth.ts +++ b/apps/web/src/routes/auth.ts @@ -2,14 +2,12 @@ import { Elysia, t } from "elysia"; import { randomBytes, createHash } from "crypto"; import sql from "../db"; -// Generate a memorable 16-digit account key: XXXX-XXXX-XXXX-XXXX function generateAccountKey(): string { const bytes = randomBytes(8); const hex = bytes.toString("hex").toUpperCase(); return `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`; } -// Middleware: validate account key from Authorization header export function requireAuth(app: Elysia) { return app.derive(async ({ headers, error }) => { const key = headers["authorization"]?.replace("Bearer ", "").trim(); @@ -23,8 +21,28 @@ export function requireAuth(app: Elysia) { } export const account = new Elysia({ prefix: "/account" }) + // Create account + .post("/register", async ({ body }) => { + const key = generateAccountKey(); + const emailHash = body.email + ? createHash("sha256").update(body.email.toLowerCase().trim()).digest("hex") + : null; + + await sql`INSERT INTO accounts (id, email_hash) VALUES (${key}, ${emailHash})`; + + return { + key, + ...(body.email ? { email_registered: true } : { email_registered: false }), + }; + }, { + body: t.Object({ + email: t.Optional(t.String({ format: "email", description: "Optional. Used for recovery and alerts." })), + }), + detail: { summary: "Create account", tags: ["account"] }, + }) + + // Update email .use(requireAuth) - // Update email (post-registration or settings) .post("/email", async ({ accountId, body }) => { const emailHash = body.email ? createHash("sha256").update(body.email.toLowerCase().trim()).digest("hex") @@ -37,25 +55,3 @@ export const account = new Elysia({ prefix: "/account" }) }), detail: { summary: "Update account email", tags: ["account"] }, }); - -export const auth = new Elysia({ prefix: "/auth" }) - // Create a new account — no email required - .post("/register", async ({ body }) => { - const key = generateAccountKey(); - const emailHash = body.email - ? createHash("sha256").update(body.email.toLowerCase().trim()).digest("hex") - : null; - - await sql`INSERT INTO accounts (id, email_hash) VALUES (${key}, ${emailHash})`; - - return { - key, - message: "Save this key — it's your only credential. We don't store it.", - ...(body.email ? { email_registered: true } : { email_registered: false }), - }; - }, { - body: t.Object({ - email: t.Optional(t.String({ format: "email", description: "Optional. Only used for account recovery." })), - }), - detail: { summary: "Create account", tags: ["auth"] }, - });