feat(game): upload editor images to image service

This commit is contained in:
2026-08-06 15:52:54 +00:00
parent 57800d8574
commit e198f70756
9 changed files with 160 additions and 25 deletions
+11 -23
View File
@@ -1,8 +1,6 @@
import { TRPCError } from '@trpc/server';
import { z } from 'zod';
import path from 'path';
import { promises as fs } from 'fs';
import { randomUUID } from 'crypto';
import { randomBytes } from 'crypto';
import sharp, { type WebpOptions } from 'sharp';
import { accessAuthedInputProcedure, authedProcedure, router } from '../../trpc.js';
@@ -36,21 +34,6 @@ const getBoardActor = async (ctx: Parameters<typeof getMyGeneral>[0]) => {
return { general, permission };
};
const normalizeUploadPath = (value: string) => {
if (!value.startsWith('/')) {
return `/${value}`;
}
return value.replace(/\/$/, '');
};
const buildPublicImageUrl = (uploadPublicUrl: string | null, uploadPath: string, filename: string) => {
if (uploadPublicUrl) {
return `${uploadPublicUrl.replace(/\/$/, '')}/${filename}`;
}
const normalizedPath = normalizeUploadPath(uploadPath);
return `${normalizedPath}/${filename}`;
};
const parseDataUrl = (dataUrl: string): Buffer => {
const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
if (match) {
@@ -274,15 +257,20 @@ export const boardRouter = router({
}
}
await fs.mkdir(ctx.uploadDir, { recursive: true });
const filename = `${randomUUID()}.${outputFormat}`;
await fs.writeFile(path.join(ctx.uploadDir, filename), outputBuffer);
if (!ctx.contentImageUpload) {
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: '이미지 저장소가 설정되지 않았습니다.' });
}
const filename = `${randomBytes(16).toString('hex')}.${outputFormat}`;
const uploaded = await ctx.contentImageUpload.upload({
filename,
contentType: outputFormat === 'avif' ? 'image/avif' : 'image/webp',
body: outputBuffer,
});
const outputMeta = await sharp(outputBuffer, { animated: true }).metadata();
const url = buildPublicImageUrl(ctx.uploadPublicUrl, ctx.uploadPath, filename);
return {
url,
url: uploaded.publicUrl,
width: outputMeta.width ?? metadata.width,
height: outputMeta.height ?? metadata.height,
format: outputFormat,