Apply full refactor updates plus pipeline/email UX confirmations
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
This commit is contained in:
@@ -1,111 +1,111 @@
|
||||
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,
|
||||
})
|
||||
}),
|
||||
})
|
||||
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,
|
||||
})
|
||||
}),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user