Files
documenso/packages/lib/server-only/profile/set-avatar-image.ts
2025-06-10 11:49:52 +10:00

150 lines
3.3 KiB
TypeScript

import sharp from 'sharp';
import { prisma } from '@documenso/prisma';
import { ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/organisations';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/teams';
import { AppError } from '../../errors/app-error';
import type { ApiRequestMetadata } from '../../universal/extract-request-metadata';
import { buildOrganisationWhereQuery } from '../../utils/organisations';
import { buildTeamWhereQuery } from '../../utils/teams';
export type SetAvatarImageOptions = {
userId: number;
target:
| {
type: 'user';
}
| {
type: 'team';
teamId: number;
}
| {
type: 'organisation';
organisationId: string;
};
bytes?: string | null;
requestMetadata: ApiRequestMetadata;
};
/**
* Pretty nasty but will do for now.
*/
export const setAvatarImage = async ({
userId,
target,
bytes,
requestMetadata,
}: SetAvatarImageOptions) => {
let oldAvatarImageId: string | null = null;
if (target.type === 'team') {
const team = await prisma.team.findFirst({
where: buildTeamWhereQuery({
teamId: target.teamId,
userId,
roles: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_TEAM'],
}),
});
if (!team) {
throw new AppError('TEAM_NOT_FOUND', {
statusCode: 404,
});
}
oldAvatarImageId = team.avatarImageId;
} else if (target.type === 'organisation') {
const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({
organisationId: target.organisationId,
userId,
roles: ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_ORGANISATION'],
}),
});
if (!organisation) {
throw new AppError('ORGANISATION_NOT_FOUND', {
statusCode: 404,
});
}
oldAvatarImageId = organisation.avatarImageId;
} else {
const user = await prisma.user.findUnique({
where: {
id: userId,
},
include: {
avatarImage: true,
},
});
if (!user) {
throw new AppError('USER_NOT_FOUND', {
statusCode: 404,
});
}
oldAvatarImageId = user.avatarImageId;
}
if (oldAvatarImageId) {
await prisma.avatarImage.delete({
where: {
id: oldAvatarImageId,
},
});
}
let newAvatarImageId: string | null = null;
if (bytes) {
const optimisedBytes = await sharp(Buffer.from(bytes, 'base64'))
.resize(512, 512)
.toFormat('jpeg', { quality: 75 })
.toBuffer();
const avatarImage = await prisma.avatarImage.create({
data: {
bytes: optimisedBytes.toString('base64'),
},
});
newAvatarImageId = avatarImage.id;
}
// TODO: Audit Logs
if (target.type === 'team') {
await prisma.team.update({
where: {
id: target.teamId,
},
data: {
avatarImageId: newAvatarImageId,
},
});
} else if (target.type === 'organisation') {
await prisma.organisation.update({
where: {
id: target.organisationId,
},
data: {
avatarImageId: newAvatarImageId,
},
});
} else {
await prisma.user.update({
where: {
id: userId,
},
data: {
avatarImageId: newAvatarImageId,
},
});
}
return newAvatarImageId;
};