вложения, канальчики, бим-бим + бам-бам
This commit is contained in:
96
server/routes/attachment.ts
Normal file
96
server/routes/attachment.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox'
|
||||
import * as fs from 'node:fs'
|
||||
import * as path from 'node:path'
|
||||
import { Type } from 'typebox'
|
||||
|
||||
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',
|
||||
response: {
|
||||
200: Type.String({ format: 'uuid', description: 'Attachment UUID' }),
|
||||
},
|
||||
},
|
||||
},
|
||||
async (req, reply) => {
|
||||
const data = await req.file()
|
||||
|
||||
if (!data) {
|
||||
return reply.notAcceptable()
|
||||
}
|
||||
|
||||
const meta = await fastify.prisma.attachment.create({
|
||||
data: {
|
||||
name: data.filename,
|
||||
mimetype: data.mimetype,
|
||||
size: 0,
|
||||
},
|
||||
})
|
||||
|
||||
if (!meta) {
|
||||
return reply.notAcceptable()
|
||||
}
|
||||
|
||||
const filePath = path.join(process.cwd(), 'uploads', meta.id)
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const writeStream = fs.createWriteStream(filePath)
|
||||
data.file.pipe(writeStream)
|
||||
data.file.on('end', resolve)
|
||||
data.file.on('error', reject)
|
||||
})
|
||||
|
||||
return meta.id
|
||||
},
|
||||
)
|
||||
|
||||
fastify.get(
|
||||
'/attachment/:id',
|
||||
{
|
||||
schema: {
|
||||
summary: 'Get attachment',
|
||||
tags: ['Attachment'],
|
||||
operationId: 'attachment.get',
|
||||
params: Type.Object({
|
||||
id: Type.String({ format: 'uuid' }),
|
||||
}),
|
||||
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
|
||||
Reference in New Issue
Block a user