131 lines
3.7 KiB
TypeScript
131 lines
3.7 KiB
TypeScript
|
|
import { zValidator } from '@hono/zod-validator';
|
||
|
|
import { Hono } from 'hono';
|
||
|
|
import { auth } from '../auth';
|
||
|
|
import prisma from '../lib/db';
|
||
|
|
import { handleNotFound } from '../lib/utils';
|
||
|
|
import { authMiddleware } from '../middlewares/auth';
|
||
|
|
import { updateAccountSchema, updatePasswordSchema } from '../validations/account';
|
||
|
|
|
||
|
|
const app = new Hono<{
|
||
|
|
Variables: {
|
||
|
|
user: typeof auth.$Infer.Session.user | null;
|
||
|
|
};
|
||
|
|
}>();
|
||
|
|
|
||
|
|
// Get user account information
|
||
|
|
app.get('/', authMiddleware, async (c) => {
|
||
|
|
const user = c.get('user');
|
||
|
|
|
||
|
|
if (!user) {
|
||
|
|
return c.json({ error: 'Unauthorized' }, 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
return c.json({
|
||
|
|
username: user.username,
|
||
|
|
email: user.email,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Update user account information
|
||
|
|
app.put('/', authMiddleware, zValidator('json', updateAccountSchema), async (c) => {
|
||
|
|
const user = c.get('user');
|
||
|
|
const { username, email } = c.req.valid('json');
|
||
|
|
|
||
|
|
if (!user) {
|
||
|
|
return c.json({ error: 'Unauthorized' }, 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Check if username is taken by another user
|
||
|
|
if (username) {
|
||
|
|
const existingUser = await prisma.user.findUnique({
|
||
|
|
where: { username },
|
||
|
|
select: { id: true },
|
||
|
|
});
|
||
|
|
if (existingUser && existingUser.id !== user.id) {
|
||
|
|
return c.json({ error: 'Username is already taken' }, 409);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if email is taken by another user
|
||
|
|
if (email) {
|
||
|
|
const existingEmail = await prisma.user.findFirst({
|
||
|
|
where: { email },
|
||
|
|
select: { id: true },
|
||
|
|
});
|
||
|
|
if (existingEmail && existingEmail.id !== user.id) {
|
||
|
|
return c.json({ error: 'Email is already taken' }, 409);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const updatedUser = await prisma.user.update({
|
||
|
|
where: { id: user.id },
|
||
|
|
data: {
|
||
|
|
username,
|
||
|
|
email,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
return c.json({
|
||
|
|
username: updatedUser.username,
|
||
|
|
email: updatedUser.email,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to update account:', error);
|
||
|
|
return handleNotFound(error as Error & { code?: string }, c);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Update user password
|
||
|
|
app.put('/password', authMiddleware, zValidator('json', updatePasswordSchema), async (c) => {
|
||
|
|
const user = c.get('user');
|
||
|
|
const { currentPassword, newPassword } = c.req.valid('json');
|
||
|
|
|
||
|
|
if (!user) {
|
||
|
|
return c.json({ error: 'Unauthorized' }, 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Use better-auth's changePassword API
|
||
|
|
const result = await auth.api.changePassword({
|
||
|
|
body: {
|
||
|
|
currentPassword,
|
||
|
|
newPassword,
|
||
|
|
},
|
||
|
|
headers: c.req.raw.headers,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!result) {
|
||
|
|
return c.json({ error: 'Failed to change password' }, 500);
|
||
|
|
}
|
||
|
|
|
||
|
|
return c.json({ message: 'Password updated successfully' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to update password:', error);
|
||
|
|
const message = error instanceof Error ? error.message : 'Failed to update password';
|
||
|
|
return c.json({ error: message }, 500);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Delete user account
|
||
|
|
app.delete('/', authMiddleware, async (c) => {
|
||
|
|
const user = c.get('user');
|
||
|
|
|
||
|
|
if (!user) {
|
||
|
|
return c.json({ error: 'Unauthorized' }, 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
await prisma.user.delete({
|
||
|
|
where: { id: user.id },
|
||
|
|
});
|
||
|
|
|
||
|
|
return c.json({ message: 'Account deleted successfully' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to delete account:', error);
|
||
|
|
return handleNotFound(error as Error & { code?: string }, c);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
export default app;
|