66 lines
2.2 KiB
TypeScript
66 lines
2.2 KiB
TypeScript
import type { FastifyInstance } from 'fastify';
|
|
import { updateProfileSchema } from '@family-wishlist/shared';
|
|
import { ConflictError, NotFoundError, ValidationError } from '../../utils/errors.js';
|
|
import { Prisma } from '@prisma/client';
|
|
import { deleteLocalImageIfAny, saveUploadedAvatar } from '../images/storage.service.js';
|
|
import { usersRegistry } from '../../auth/users.registry.js';
|
|
|
|
const MAX_AVATAR_BYTES = 2 * 1024 * 1024;
|
|
|
|
export default async function profileRoutes(app: FastifyInstance) {
|
|
app.addHook('preHandler', app.authenticate);
|
|
|
|
app.get('/', async (request) => {
|
|
const profile = await app.prisma.user.findUnique({ where: { id: request.user.id } });
|
|
if (!profile) throw new NotFoundError('Profile');
|
|
return profile;
|
|
});
|
|
|
|
app.get('/friend', async (request) => {
|
|
const friend = usersRegistry.all().find((u) => u.id !== request.user.id);
|
|
if (!friend) return null;
|
|
|
|
return app.prisma.user.findUnique({
|
|
where: { id: friend.id },
|
|
select: { slug: true, displayName: true, avatarUrl: true },
|
|
});
|
|
});
|
|
|
|
app.patch('/', async (request) => {
|
|
const body = updateProfileSchema.parse(request.body);
|
|
try {
|
|
const updated = await app.prisma.user.update({
|
|
where: { id: request.user.id },
|
|
data: body,
|
|
});
|
|
return updated;
|
|
} catch (err) {
|
|
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2002') {
|
|
throw new ConflictError('Slug is already taken');
|
|
}
|
|
throw err;
|
|
}
|
|
});
|
|
|
|
app.post('/avatar', async (request) => {
|
|
const current = await app.prisma.user.findUnique({ where: { id: request.user.id } });
|
|
if (!current) throw new NotFoundError('Profile');
|
|
|
|
const data = await request.file();
|
|
if (!data) throw new ValidationError('No file uploaded');
|
|
|
|
const buffer = await data.toBuffer();
|
|
if (buffer.byteLength > MAX_AVATAR_BYTES) {
|
|
throw new ValidationError('Avatar must be 2 MB or less');
|
|
}
|
|
|
|
const { imageUrl } = await saveUploadedAvatar(request.user.id, data.mimetype, buffer);
|
|
await deleteLocalImageIfAny(current.avatarUrl);
|
|
|
|
return app.prisma.user.update({
|
|
where: { id: request.user.id },
|
|
data: { avatarUrl: imageUrl },
|
|
});
|
|
});
|
|
}
|