A realtime multiplayer version of the boardgame Ricochet Robots
at master 4.4 kB view raw
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});