All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
112 lines
3.0 KiB
TypeScript
112 lines
3.0 KiB
TypeScript
import { z } from 'zod'
|
|
import { router, protectedProcedure } from '../trpc'
|
|
import { generateAvatarKey, type StorageProviderType } from '@/lib/storage'
|
|
import {
|
|
getImageUploadUrl,
|
|
confirmImageUpload,
|
|
getImageUrl,
|
|
deleteImage,
|
|
type ImageUploadConfig,
|
|
} from '../utils/image-upload'
|
|
|
|
type AvatarSelect = {
|
|
profileImageKey: string | null
|
|
profileImageProvider: string | null
|
|
}
|
|
|
|
const avatarConfig: ImageUploadConfig<AvatarSelect> = {
|
|
label: 'avatar',
|
|
generateKey: generateAvatarKey,
|
|
findCurrent: (prisma, entityId) =>
|
|
prisma.user.findUnique({
|
|
where: { id: entityId },
|
|
select: { profileImageKey: true, profileImageProvider: true },
|
|
}),
|
|
getImageKey: (record) => record.profileImageKey,
|
|
getProviderType: (record) =>
|
|
(record.profileImageProvider as StorageProviderType) || 's3',
|
|
setImage: (prisma, entityId, key, providerType) =>
|
|
prisma.user.update({
|
|
where: { id: entityId },
|
|
data: { profileImageKey: key, profileImageProvider: providerType },
|
|
}),
|
|
clearImage: (prisma, entityId) =>
|
|
prisma.user.update({
|
|
where: { id: entityId },
|
|
data: { profileImageKey: null, profileImageProvider: null },
|
|
}),
|
|
auditEntityType: 'User',
|
|
auditFieldName: 'profileImageKey',
|
|
}
|
|
|
|
export const avatarRouter = router({
|
|
/**
|
|
* Get a pre-signed URL for uploading an avatar
|
|
*/
|
|
getUploadUrl: protectedProcedure
|
|
.input(
|
|
z.object({
|
|
fileName: z.string(),
|
|
contentType: z.string(),
|
|
})
|
|
)
|
|
.mutation(async ({ ctx, input }) => {
|
|
return getImageUploadUrl(
|
|
ctx.user.id,
|
|
input.fileName,
|
|
input.contentType,
|
|
generateAvatarKey
|
|
)
|
|
}),
|
|
|
|
/**
|
|
* Confirm avatar upload and update user profile
|
|
*/
|
|
confirmUpload: protectedProcedure
|
|
.input(
|
|
z.object({
|
|
key: z.string(),
|
|
providerType: z.enum(['s3', 'local']),
|
|
})
|
|
)
|
|
.mutation(async ({ ctx, input }) => {
|
|
const userId = ctx.user.id
|
|
|
|
await confirmImageUpload(ctx.prisma, avatarConfig, userId, input.key, input.providerType, {
|
|
userId: ctx.user.id,
|
|
ip: ctx.ip,
|
|
userAgent: ctx.userAgent,
|
|
})
|
|
|
|
// Return the updated user fields to match original API contract
|
|
const user = await ctx.prisma.user.findUnique({
|
|
where: { id: userId },
|
|
select: {
|
|
id: true,
|
|
profileImageKey: true,
|
|
profileImageProvider: true,
|
|
},
|
|
})
|
|
|
|
return user
|
|
}),
|
|
|
|
/**
|
|
* Get the current user's avatar URL
|
|
*/
|
|
getUrl: protectedProcedure.query(async ({ ctx }) => {
|
|
return getImageUrl(ctx.prisma, avatarConfig, ctx.user.id)
|
|
}),
|
|
|
|
/**
|
|
* Delete the current user's avatar
|
|
*/
|
|
delete: protectedProcedure.mutation(async ({ ctx }) => {
|
|
return deleteImage(ctx.prisma, avatarConfig, ctx.user.id, {
|
|
userId: ctx.user.id,
|
|
ip: ctx.ip,
|
|
userAgent: ctx.userAgent,
|
|
})
|
|
}),
|
|
})
|