All files / src/routes signup.ts

95% Statements 19/20
88.88% Branches 8/9
100% Functions 3/3
95% Lines 19/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62                    7x     19x   19x 1x         18x 18x   1x 1x           17x 17x 1x     16x 16x         16x 16x           16x 16x     16x   16x            
import type { FastifyInstance } from "fastify";
import { eq } from "drizzle-orm";
import { db } from "../db/index.js";
import { users, accounts, memberships } from "../db/schema.js";
import { verifyOtp, OtpError } from "../lib/otp.js";
import { createSession } from "../lib/sessions.js";
import { getUserAccounts } from "../lib/accounts.js";
 
export async function signupRoutes(app: FastifyInstance) {
  // POST /signup — validate OTP, create user + account, return tokens
  app.post<{ Body: { email: string; code: string; accountName: string } }>(
    "/signup",
    async (request, reply) => {
      const { email, code, accountName } = request.body;
 
      if (!email || !code || !accountName) {
        return reply
          .status(400)
          .send({ error: "Email, code, and accountName are required" });
      }
 
      try {
        await verifyOtp(email, code);
      } catch (err) {
        Eif (err instanceof OtpError) {
          return reply.status(400).send({ error: err.message });
        }
        throw err;
      }
 
      // Check that user does not already exist
      const [existingUser] = await db.select().from(users).where(eq(users.email, email));
      if (existingUser) {
        return reply.status(409).send({ error: "User already exists. Please log in." });
      }
 
      const { user, tokens } = await db.transaction(async (tx) => {
        const [created] = await tx
          .insert(users)
          .values({ email, name: email.split("@")[0] })
          .returning();
 
        const [account] = await tx.insert(accounts).values({ name: accountName }).returning();
        await tx.insert(memberships).values({
          userId: created.id,
          accountId: account.id,
          role: "owner",
        });
 
        const t = await createSession(created.id, tx);
        return { user: created, tokens: t };
      });
 
      const userAccounts = await getUserAccounts(user.id);
 
      return reply
        .status(201)
        .send({ ...tokens, user, accounts: userAccounts });
    },
  );
}