feat: organize uploads by folder + cdn.hotelsync.ru active
- Upload route now accepts ?folder=categories|rooms|hotels|guests
- Files saved to /uploads/{folder}/uuid.ext, served at cdn.hotelsync.ru/{folder}/uuid.ext
- api.upload.photo() accepts optional folder param (default: 'rooms')
- RoomCategoriesPage passes folder='categories' on photo upload
- SSL cert issued and nginx config active for cdn.hotelsync.ru
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,19 +5,28 @@ import { randomUUID } from 'crypto'
|
||||
import { pipeline } from 'stream/promises'
|
||||
|
||||
const UPLOADS_DIR = process.env.UPLOADS_DIR ?? join(process.cwd(), 'uploads')
|
||||
const CDN_HOST = process.env.CDN_HOST ?? 'cdn.hotelsync.ru'
|
||||
|
||||
// Ensure uploads directory exists
|
||||
mkdirSync(UPLOADS_DIR, { recursive: true })
|
||||
const ALLOWED_FOLDERS = ['categories', 'rooms', 'hotels', 'guests'] as const
|
||||
type UploadFolder = typeof ALLOWED_FOLDERS[number]
|
||||
|
||||
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']
|
||||
const MAX_SIZE = 10 * 1024 * 1024 // 10MB
|
||||
|
||||
// Ensure base uploads directory exists
|
||||
mkdirSync(UPLOADS_DIR, { recursive: true })
|
||||
|
||||
const upload: FastifyPluginAsync = async (fastify) => {
|
||||
// POST /api/upload — upload a single image, returns { url }
|
||||
// POST /api/upload?folder=categories — upload a single image, returns { url }
|
||||
fastify.post(
|
||||
'/api/upload',
|
||||
{ onRequest: [fastify.authenticate] },
|
||||
async (request, reply) => {
|
||||
const { folder } = request.query as { folder?: string }
|
||||
const dir: UploadFolder = ALLOWED_FOLDERS.includes(folder as UploadFolder)
|
||||
? (folder as UploadFolder)
|
||||
: 'rooms'
|
||||
|
||||
const data = await request.file({ limits: { fileSize: MAX_SIZE } })
|
||||
if (!data) return reply.code(400).send({ error: 'No file provided' })
|
||||
|
||||
@@ -25,14 +34,16 @@ const upload: FastifyPluginAsync = async (fastify) => {
|
||||
return reply.code(400).send({ error: 'Only images allowed (jpg, png, webp, gif)' })
|
||||
}
|
||||
|
||||
const ext = extname(data.filename) || '.jpg'
|
||||
const subDir = join(UPLOADS_DIR, dir)
|
||||
mkdirSync(subDir, { recursive: true })
|
||||
|
||||
const ext = extname(data.filename) || '.jpg'
|
||||
const filename = `${randomUUID()}${ext}`
|
||||
const filepath = join(UPLOADS_DIR, filename)
|
||||
const filepath = join(subDir, filename)
|
||||
|
||||
await pipeline(data.file, createWriteStream(filepath))
|
||||
|
||||
const cdnHost = process.env.CDN_HOST ?? 'cdn.hotelsync.ru'
|
||||
return { url: `https://${cdnHost}/${filename}` }
|
||||
return { url: `https://${CDN_HOST}/${dir}/${filename}` }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user