1import { z } from "zod";
2import { generateToken } from "~/lib/generateToken";
3import nodemailer from "nodemailer";
4
5import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
6import { db } from "~/server/db";
7import {
8 pendingUsers,
9 sessions,
10 users,
11 userVerificationCode,
12} from "~/server/db/schema";
13import { eq, or } from "drizzle-orm";
14import { TRPCError } from "@trpc/server";
15import { env } from "~/env";
16
17export const authRouter = createTRPCRouter({
18 // hello: publicProcedure
19 // .input(z.object({ text: z.string() }))
20 // .query(({ input }) => {
21 // return {
22 // greeting: `Hello ${input.text}`,
23 // };
24 // }),
25 // create: publicProcedure
26 // .input(z.object({ name: z.string().min(1) }))
27 // .mutation(async ({ ctx, input }) => {
28 // await ctx.db.insert(posts).values({
29 // name: input.name,
30 // });
31 // }),
32 login: publicProcedure
33 .input(
34 z.object({
35 email: z.string(),
36 }),
37 )
38 .mutation(async ({ input, ctx }) => {
39 const queryUser = (
40 await db.select().from(users).where(eq(users.email, input.email))
41 )[0];
42
43 if (!queryUser) {
44 return {
45 message:
46 "A confirmation email has been sent to the provided address if an account exists.",
47 };
48 }
49
50 const token = generateToken(64);
51 await db.insert(userVerificationCode).values({
52 userId: queryUser.id,
53 token,
54 used: false,
55 });
56
57 const mailOptions = {
58 from: `"R3 by @dinkelspiel" <${process.env.GMAIL_EMAIL}>`,
59 to: input.email,
60 subject: "Verify Your R3 Account",
61 text: `To log in to your R3 account, please click the following link: https://r3.keii.dev/api/auth/login?code=${token}. If you did not request this email, you can safely ignore it.`,
62 };
63
64 const transporter = nodemailer.createTransport({
65 service: "Gmail",
66 host: "smtp.gmail.com",
67 port: 465,
68 secure: true,
69 auth: {
70 user: env.GMAIL_EMAIL,
71 pass: env.GMAIL_PASSWORD,
72 },
73 });
74
75 transporter.sendMail(mailOptions);
76
77 return {
78 message:
79 "A confirmation email has been sent to the provided address if an account exists.",
80 };
81 }),
82 signUp: publicProcedure
83 .input(
84 z.object({
85 username: z.string().regex(/^[A-Za-z0-9_]+$/, {
86 message:
87 "Username must only contain letters, numbers, and underscores",
88 }),
89 email: z.string().email(),
90 }),
91 )
92 .mutation(async ({ input, ctx }) => {
93 const queryUser = await db
94 .select()
95 .from(users)
96 .where(
97 or(eq(users.email, input.email), eq(users.username, input.username)),
98 );
99
100 const queryPendingUser = await db
101 .select()
102 .from(pendingUsers)
103 .where(
104 or(
105 eq(pendingUsers.email, input.email),
106 eq(pendingUsers.username, input.username),
107 ),
108 );
109
110 if (queryUser.length !== 0 || queryPendingUser.length !== 0) {
111 throw new TRPCError({
112 code: "BAD_REQUEST",
113 message: "User already exists with this mail or username.",
114 });
115 }
116
117 const insertedRow = await db.insert(pendingUsers).values({
118 username: input.username,
119 email: input.email,
120 token: generateToken(64),
121 });
122
123 const pendingUser = (
124 await db
125 .select()
126 .from(pendingUsers)
127 .where(eq(pendingUsers.id, insertedRow[0].insertId))
128 )[0];
129
130 const mailOptions = {
131 from: `"R3 by @dinkelspiel" <${process.env.GMAIL_EMAIL}>`,
132 to: input.email,
133 subject: "Signup to R3",
134 text: `Create your R3 account by clicking this link: https://r3.keii.dev/api/auth/login?code=${pendingUser?.token}. If this wasn't sent by you then you can simply ignore it.`,
135 };
136
137 const transporter = nodemailer.createTransport({
138 service: "Gmail",
139 host: "smtp.gmail.com",
140 port: 465,
141 secure: true,
142 auth: {
143 user: env.GMAIL_EMAIL,
144 pass: env.GMAIL_PASSWORD,
145 },
146 });
147
148 transporter.sendMail(mailOptions);
149
150 return {
151 message:
152 "A confirmation email has been sent to the provided address for account signup.",
153 };
154 }),
155});