gammanexus/server/authRoutes.ts

100 lines
4.0 KiB
TypeScript

// eslint-disable-next-line @typescript-eslint/no-var-requires
const bcrypt = require("bcryptjs");
import { createUser, getUserByEmail, getUserById, updateUser, updateUserPassword } from "./db";
export function requireAuth(req: any, res: any, next: any) {
if (!(req.session as any)?.userId) {
return res.status(401).json({ error: "Not authenticated" });
}
next();
}
export function registerAuthRoutes(app: any) {
// POST /api/auth/register
app.post("/api/auth/register", async (req: any, res: any) => {
const { name, email, password } = req.body;
if (!name || !email || !password || password.length < 6) {
return res.status(400).json({ error: "Name, email, and password (min 6 chars) are required" });
}
const existing = await getUserByEmail(email.toLowerCase());
if (existing) {
return res.status(409).json({ error: "Email already in use" });
}
const hash = await bcrypt.hash(password, 10);
const user = await createUser({ name, email: email.toLowerCase(), passwordHash: hash });
(req.session as any).userId = user.id;
res.status(201).json({ id: user.id, name: user.name, email: user.email });
});
// POST /api/auth/login
app.post("/api/auth/login", async (req: any, res: any) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ error: "Email and password are required" });
}
const user = await getUserByEmail(email.toLowerCase());
if (!user) {
return res.status(401).json({ error: "Invalid email or password" });
}
const valid = await bcrypt.compare(password, user.passwordHash);
if (!valid) {
return res.status(401).json({ error: "Invalid email or password" });
}
(req.session as any).userId = user.id;
res.json({ id: user.id, name: user.name, email: user.email });
});
// POST /api/auth/logout
app.post("/api/auth/logout", (req: any, res: any) => {
req.session.destroy(() => { res.json({ ok: true }); });
});
// GET /api/auth/me
app.get("/api/auth/me", requireAuth, async (req: any, res: any) => {
const user = await getUserById((req.session as any).userId);
if (!user) {
req.session.destroy();
return res.status(401).json({ error: "Not authenticated" });
}
res.json({ id: user.id, name: user.name, email: user.email });
});
// PUT /api/auth/profile
app.put("/api/auth/profile", requireAuth, async (req: any, res: any) => {
const { name, email } = req.body;
const updates: any = {};
if (name) updates.name = name;
if (email) {
const existing = await getUserByEmail(email.toLowerCase());
if (existing && existing.id !== (req.session as any).userId) {
return res.status(409).json({ error: "Email already in use" });
}
updates.email = email.toLowerCase();
}
const user = await updateUser((req.session as any).userId, updates);
res.json({ id: user.id, name: user.name, email: user.email });
});
// PUT /api/auth/password
app.put("/api/auth/password", requireAuth, async (req: any, res: any) => {
const { currentPassword, newPassword } = req.body;
if (!currentPassword || !newPassword || newPassword.length < 6) {
return res.status(400).json({ error: "Current password and new password (min 6 chars) required" });
}
const user = await getUserById((req.session as any).userId);
if (!user) return res.status(401).json({ error: "Not authenticated" });
const valid = await bcrypt.compare(currentPassword, user.passwordHash);
if (!valid) return res.status(401).json({ error: "Current password is incorrect" });
const hash = await bcrypt.hash(newPassword, 10);
await updateUserPassword(user.id, hash);
res.json({ ok: true });
});
// POST /api/auth/request-reset (stub)
app.post("/api/auth/request-reset", async (req: any, res: any) => {
const { email } = req.body;
if (!email) return res.status(400).json({ error: "Email is required" });
res.json({ ok: true, message: "If an account exists, a reset link would be sent" });
});
}