feat: add homepage & auth

This commit is contained in:
2024-02-23 19:36:10 +07:00
parent d579c1b7c5
commit 735fa9354b
42 changed files with 981 additions and 70 deletions
+2
View File
@@ -1,8 +1,10 @@
import { router } from "../api/trpc";
import auth from "./auth";
import project from "./project";
import file from "./file";
export const appRouter = router({
auth,
project,
file,
});
+54
View File
@@ -0,0 +1,54 @@
import { z } from "zod";
import { procedure, router } from "../api/trpc";
import db from "../db";
import { TRPCError } from "@trpc/server";
import { and, eq, isNull } from "drizzle-orm";
import { user } from "../db/schema/user";
import { verifyPassword } from "../lib/crypto";
import { createToken } from "../lib/jwt";
const authRouter = router({
login: procedure
.input(
z.object({
email: z.string().email(),
password: z.string().min(1),
})
)
.mutation(async ({ ctx, input }) => {
const userData = await db.query.user.findFirst({
where: and(eq(user.email, input.email), isNull(user.deletedAt)),
});
if (!userData) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Email is not found!",
});
}
if (!(await verifyPassword(userData.password, input.password))) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Invalid password!",
});
}
// set user token
const token = await createToken({ id: userData.id });
ctx.res.cookie("auth-token", token, { httpOnly: true });
return { ...userData, password: undefined };
}),
logout: procedure.mutation(({ ctx }) => {
ctx.res.cookie("auth-token", null, {
httpOnly: true,
expires: new Date(0),
});
return true;
}),
});
export default authRouter;
+73 -11
View File
@@ -9,6 +9,11 @@ import {
import { z } from "zod";
import { TRPCError } from "@trpc/server";
import { uid } from "../lib/utils";
import { insertUserSchema, user } from "../db/schema/user";
import { faker } from "@faker-js/faker";
import { ucwords } from "~/lib/utils";
import { hashPassword } from "../lib/crypto";
import { createToken } from "../lib/jwt";
const projectRouter = router({
getAll: procedure.query(async () => {
@@ -45,19 +50,59 @@ const projectRouter = router({
create: procedure
.input(
insertProjectSchema.pick({
title: true,
})
insertProjectSchema
.pick({
title: true,
})
.merge(
z.object({
forkFromId: z.number().optional(),
user: insertUserSchema
.pick({
name: true,
email: true,
password: true,
})
.optional(),
})
)
)
.mutation(async ({ input }) => {
const data: z.infer<typeof insertProjectSchema> = {
userId: 1,
title: input.title,
slug: uid(),
};
.mutation(async ({ ctx, input }) => {
const title =
input.title.length > 0 ? input.title : ucwords(faker.lorem.words(2));
let userId = 0;
const [result] = await db.insert(project).values(data).returning();
return result;
return db.transaction(async (tx) => {
if (input.user) {
const [usr] = await tx
.insert(user)
.values({
...input.user,
password: await hashPassword(input.user.password),
})
.returning();
userId = usr.id;
// set user token
const token = await createToken({ id: userId });
ctx.res.cookie("auth-token", token, { httpOnly: true });
}
if (!userId) {
throw new Error("Invalid userId!");
}
const data: z.infer<typeof insertProjectSchema> = {
userId,
title,
slug: uid(),
};
const [result] = await tx.insert(project).values(data).returning();
return result;
});
}),
update: procedure
@@ -95,6 +140,23 @@ const projectRouter = router({
return result;
}),
getTemplates: procedure.query(() => {
return [
{
title: "Empty Project",
projectId: 0,
},
{
title: "Vanilla HTML+CSS+JS",
projectId: 1,
},
{
title: "React + Tailwindcss",
projectId: 2,
},
];
}),
});
export default projectRouter;