Files
chad/server/routes/attachment.ts
2026-05-29 04:28:09 +06:00

112 lines
2.9 KiB
TypeScript

import type { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox'
import * as fs from 'node:fs'
import * as path from 'node:path'
import { Type } from 'typebox'
import { GetAttachmentParamsSchema } from '../plugins/schemas/attachment.ts'
import { TypeboxRef } from '../utils/typebox-ref.ts'
const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
const uploadDir = path.join(process.cwd(), 'uploads')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
fastify.post(
'/attachment/upload',
{
schema: {
summary: 'Upload attachment',
tags: ['Attachment'],
operationId: 'attachment.upload',
description: 'Pass file to multipart/form-data',
consumes: ['multipart/form-data'],
response: {
200: Type.String({ format: 'uuid', description: 'Attachment UUID' }),
},
},
},
async (req, reply) => {
const user = req.user!
const data = await req.file()
if (!data) {
return reply.notAcceptable()
}
const meta = await fastify.prisma.attachment.create({
data: {
name: data.filename,
username: user.username,
mimetype: data.mimetype,
size: 0,
},
})
if (!meta) {
return reply.notAcceptable()
}
const filePath = path.join(process.cwd(), 'uploads', meta.id)
let fileSize = 0
await new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(filePath)
data.file.pipe(writeStream)
data.file.on('data', (chunk) => {
fileSize += chunk.length
})
data.file.on('end', resolve)
data.file.on('error', reject)
})
await fastify.prisma.attachment.update({
where: { id: meta.id },
data: { size: fileSize },
})
// await new Promise(resolve => setTimeout(resolve, Math.random() * 10000))
return meta.id
},
)
fastify.get(
'/attachment/:id',
{
schema: {
summary: 'Get attachment',
tags: ['Attachment'],
operationId: 'attachment.get',
params: TypeboxRef(GetAttachmentParamsSchema),
response: {
200: Type.Any({ description: 'Attachment content' }),
},
},
config: {
skipAuth: true,
},
},
async (req, reply) => {
const meta = await fastify.prisma.attachment.findFirst({
where: { id: req.params.id },
})
if (!meta) {
return reply.notFound('Attachment not found')
}
const filePath = path.join(process.cwd(), 'uploads', meta.id)
reply.type(meta.mimetype)
reply.header('Cache-Control', 'public, max-age=31536000')
reply.header('Content-Disposition', `inline; filename="${meta.name}"`)
return fs.createReadStream(filePath)
},
)
}
export default plugin