Skip to content

Commit

Permalink
Feat(File, Route): First Upload!!! Just for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur-Poffo committed Feb 21, 2024
1 parent fd44dfa commit c8c4a85
Show file tree
Hide file tree
Showing 8 changed files with 1,337 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"accesstoken",
"authtoken"
"authtoken",
"originalname"
]
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
"author": "Artur Poffo",
"license": "ISC",
"dependencies": {
"@aws-sdk/client-s3": "^3.515.0",
"@fastify/cookie": "^9.3.1",
"@fastify/cors": "^9.0.1",
"@fastify/jwt": "^8.0.0",
"@prisma/client": "^5.9.1",
"bcryptjs": "^2.4.3",
"dotenv": "^16.3.1",
"fastify": "^4.25.2",
"fastify-multer": "^2.0.3",
"zod": "^3.22.4"
},
"devDependencies": {
Expand All @@ -50,4 +52,4 @@
"vite-tsconfig-paths": "^4.3.1",
"vitest": "^1.2.1"
}
}
}
7 changes: 7 additions & 0 deletions src/infra/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import fastifyCookie from '@fastify/cookie'
import fastifyCors from '@fastify/cors'
import fastifyJwt from '@fastify/jwt'
import fastify from 'fastify'
import multer from 'fastify-multer'
import { readFileSync } from 'fs'
import { ZodError } from 'zod'
import { env } from './env'
import { courseRoutes } from './http/routes/course'
import { fileRoutes } from './http/routes/file'
import { userRoutes } from './http/routes/user'

export const app = fastify()
Expand Down Expand Up @@ -34,10 +36,15 @@ app.register(fastifyJwt, {

app.register(fastifyCookie)

export const upload = multer()

app.register(multer.contentParser)

// API Routes

app.register(userRoutes)
app.register(courseRoutes)
app.register(fileRoutes)

// Custom error handler

Expand Down
45 changes: 45 additions & 0 deletions src/infra/http/controllers/upload-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { InvalidMimeTypeError } from '@/domain/storage/application/use-cases/errors/invalid-mime-type-error'
import { makeUploadFileUseCase } from '@/infra/use-cases/factories/make-upload-file-use-case'
import { type FastifyReply, type FastifyRequest } from 'fastify'

interface MulterRequest extends FastifyRequest {
file?: {
buffer: Buffer
originalname: string
mimetype: string
size: number
}
}

export async function uploadFileController(request: MulterRequest, reply: FastifyReply) {
const uploadFileUseCase = makeUploadFileUseCase()

if (!request.file) {
return reply.status(404).send({
message: 'File required'
})
}

const result = await uploadFileUseCase.exec({
body: request.file.buffer,
fileName: request.file.originalname,
fileType: request.file.mimetype,
size: request.file.size,
storedAt: new Date()
})

if (result.isLeft()) {
const error = result.value

switch (error.constructor) {
case InvalidMimeTypeError:
return reply.status(415).send()
default:
return reply.status(500).send()
}
}

reply.status(201).send({
file: result.value.file
})
}
8 changes: 8 additions & 0 deletions src/infra/http/routes/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { upload } from '@/infra/app'
import { type FastifyInstance } from 'fastify'
import { uploadFileController } from '../controllers/upload-file'
import { verifyJwt } from '../middlewares/verify-jwt'

export async function fileRoutes(app: FastifyInstance) {
app.post('/files', { onRequest: [verifyJwt], preHandler: [upload.single('file')] }, uploadFileController)
}
47 changes: 47 additions & 0 deletions src/infra/storage/r2-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
type UploadParams,
type Uploader
} from '@/domain/storage/application/upload/uploader'

import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { randomUUID } from 'node:crypto'
import { env } from '../env'

export class R2Storage implements Uploader {
private readonly client: S3Client

constructor() {
const accountId = env.CLOUDFLARE_ACCOUNT_ID

this.client = new S3Client({
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
region: 'auto',
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY
}
})
}

async upload({
fileName,
fileType,
body
}: UploadParams): Promise<{ key: string }> {
const uploadId = randomUUID()
const uniqueFileName = `${uploadId}-${fileName}`

await this.client.send(
new PutObjectCommand({
Bucket: env.AWS_BUCKET_NAME,
Key: uniqueFileName,
ContentType: fileType,
Body: body
})
)

return {
key: uniqueFileName
}
}
}
15 changes: 15 additions & 0 deletions src/infra/use-cases/factories/make-upload-file-use-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UploadFileUseCase } from '@/domain/storage/application/use-cases/upload-file'
import { makePrismaFilesRepository } from '@/infra/database/prisma/repositories/factories/make-prisma-files-repository'
import { R2Storage } from '@/infra/storage/r2-storage'

export function makeUploadFileUseCase() {
const prismaFilesRepository = makePrismaFilesRepository()
const R2Uploader = new R2Storage()

const uploadFileUseCase = new UploadFileUseCase(
prismaFilesRepository,
R2Uploader
)

return uploadFileUseCase
}
Loading

0 comments on commit c8c4a85

Please sign in to comment.