feat: migrate to Better Auth for authentication, update environment variables, and enhance database schema with accounts and sessions

This commit is contained in:
2025-12-20 11:00:00 +00:00
parent 377836d1fa
commit 7bff6b0f91
13 changed files with 183 additions and 205 deletions

View File

@@ -1,134 +0,0 @@
import { Router } from 'express';
import { and, eq } from 'drizzle-orm';
import { z } from 'zod';
import { db } from '../db/client';
import { users } from '../db/schema';
import { requireAuth } from '../middleware/auth';
import { signAccessToken } from '../utils/jwt';
import { hashPassword, verifyPassword } from '../utils/password';
const router = Router();
const registerSchema = z.object({
email: z.email().trim().toLowerCase(),
password: z.string().min(8),
name: z.string().trim().min(1).max(255),
});
const loginSchema = z.object({
email: z.email().trim().toLowerCase(),
password: z.string().min(1),
});
router.post('/register', async (req, res) => {
const parsed = registerSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ message: 'Invalid request body', errors: parsed.error.flatten() });
return;
}
const { email, password, name } = parsed.data;
const existingUser = await db.query.users.findFirst({
where: eq(users.email, email),
});
if (existingUser) {
res.status(409).json({ message: 'Email already in use' });
return;
}
const passwordHash = await hashPassword(password);
const [newUser] = await db
.insert(users)
.values({
email,
name,
passwordHash,
})
.returning({
id: users.id,
email: users.email,
name: users.name,
createdAt: users.createdAt,
});
if (!newUser) {
res.status(500).json({ message: 'Failed to create user' });
return;
}
const token = signAccessToken({ userId: newUser.id, email: newUser.email });
res.status(201).json({ token, user: newUser });
});
router.post('/login', async (req, res) => {
const parsed = loginSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ message: 'Invalid request body', errors: parsed.error.flatten() });
return;
}
const { email, password } = parsed.data;
const user = await db.query.users.findFirst({
where: eq(users.email, email),
});
if (!user) {
res.status(401).json({ message: 'Invalid email or password' });
return;
}
const isPasswordValid = await verifyPassword(password, user.passwordHash);
if (!isPasswordValid) {
res.status(401).json({ message: 'Invalid email or password' });
return;
}
const token = signAccessToken({ userId: user.id, email: user.email });
res.json({
token,
user: {
id: user.id,
email: user.email,
name: user.name,
createdAt: user.createdAt,
},
});
});
router.get('/me', requireAuth, async (req, res) => {
const authenticatedUser = req.user;
if (!authenticatedUser) {
res.status(401).json({ message: 'Unauthorized' });
return;
}
const user = await db.query.users.findFirst({
where: and(eq(users.id, authenticatedUser.userId), eq(users.email, authenticatedUser.email)),
columns: {
id: true,
email: true,
name: true,
createdAt: true,
},
});
if (!user) {
res.status(404).json({ message: 'User not found' });
return;
}
res.json({ user });
});
export default router;

View File

@@ -46,16 +46,16 @@ router.post('/upload-url', async (req, res) => {
return;
}
const user = req.user;
const authSession = req.auth;
if (!user) {
if (!authSession?.user) {
res.status(401).json({ message: 'Unauthorized' });
return;
}
await ensureMinioBucket();
const objectKey = buildObjectKey(user.userId, parsed.data.fileName, parsed.data.prefix);
const objectKey = buildObjectKey(authSession.user.id, parsed.data.fileName, parsed.data.prefix);
const uploadUrl = await minioClient.presignedPutObject(minioBucket, objectKey, minioPresignedExpirySeconds);
const now = new Date();
const expiresAt = new Date(now.getTime() + minioPresignedExpirySeconds * 1000);
@@ -63,7 +63,7 @@ router.post('/upload-url', async (req, res) => {
const [videoRecord] = await db
.insert(videos)
.values({
userId: user.userId,
userId: authSession.user.id,
objectKey,
bucket: minioBucket,
uploadUrl,