diff --git a/server/.env.example b/server/.env.example index e815311..b2c97be 100644 --- a/server/.env.example +++ b/server/.env.example @@ -14,10 +14,6 @@ EMAIL_USERNAME=your-email@example.com EMAIL_PASSWORD=your-email-password FROM_EMAIL = your-from-email -CLOUDINARY_CLOUD_NAME = your-cloud-name -CLOUDINARY_API_KEY = your-cloud-api-key -CLOUDINARY_API_SECRET = your-cloud-secret - AWS_ACCESS_KEY = access-key AWS_SECRET_KEY = secret-key AWS_BUCKET_REGION = bucket-region diff --git a/server/package.json b/server/package.json index a8b5559..2efc658 100644 --- a/server/package.json +++ b/server/package.json @@ -15,7 +15,6 @@ "@aws-sdk/client-s3": "^3.363.0", "@aws-sdk/s3-request-presigner": "^3.363.0", "bcrypt": "^5.1.0", - "cloudinary": "^1.37.2", "colors.ts": "^1.0.20", "cookie-parser": "^1.4.6", "cors": "^2.8.5", @@ -33,7 +32,6 @@ "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", - "multer-storage-cloudinary": "^4.0.0", "nodemailer": "^6.9.3", "redis": "^4.6.7", "socket.io": "^4.7.1", diff --git a/server/src/adapters/controllers/authController.ts b/server/src/adapters/controllers/authController.ts index 5a84392..4655f33 100644 --- a/server/src/adapters/controllers/authController.ts +++ b/server/src/adapters/controllers/authController.ts @@ -24,9 +24,13 @@ import { AdminDbInterface } from '@src/app/repositories/adminDbRepository'; import { AdminRepositoryMongoDb } from '@src/frameworks/database/mongodb/repositories/adminRepoMongoDb'; import { RefreshTokenDbInterface } from '@src/app/repositories/refreshTokenDBRepository'; import { RefreshTokenRepositoryMongoDb } from '@src/frameworks/database/mongodb/repositories/refreshTokenRepoMongoDb'; +import { CloudServiceImpl } from '@src/frameworks/services/s3CloudService'; +import { CloudServiceInterface } from '@src/app/services/cloudServiceInterface'; const authController = ( authServiceInterface: AuthServiceInterface, authServiceImpl: AuthService, + cloudServiceInterface:CloudServiceInterface, + CloudServiceImpl:CloudServiceImpl, studentDbRepository: StudentsDbInterface, studentDbRepositoryImpl: StudentRepositoryMongoDB, instructorDbRepository: InstructorDbInterface, @@ -47,6 +51,7 @@ const authController = ( refreshTokenDbRepositoryImpl() ); const authService = authServiceInterface(authServiceImpl()); + const cloudService = cloudServiceInterface(CloudServiceImpl()) const googleAuthService = googleAuthServiceInterface(googleAuthServiceImpl()); //? STUDENT @@ -109,7 +114,8 @@ const authController = ( instructor, files, dbRepositoryInstructor, - authService + authService, + cloudService ); res.status(200).json({ status: 'success', diff --git a/server/src/app/usecases/auth/instructorAuth.ts b/server/src/app/usecases/auth/instructorAuth.ts index c059ea9..d2ac083 100644 --- a/server/src/app/usecases/auth/instructorAuth.ts +++ b/server/src/app/usecases/auth/instructorAuth.ts @@ -8,52 +8,58 @@ import { InstructorDbInterface } from '../../../app/repositories/instructorDbRep import { AuthServiceInterface } from '../../../app/services/authServicesInterface'; import { RefreshTokenDbInterface } from '../../../app/repositories/refreshTokenDBRepository'; import { UploadedFileInterface } from '@src/types/common'; +import { CloudServiceInterface } from '@src/app/services/cloudServiceInterface'; export const instructorRegister = async ( instructor: InstructorInterface, - files: UploadedFileInterface[], + files: Express.Multer.File[], instructorRepository: ReturnType, - authService: ReturnType + authService: ReturnType, + cloudService: ReturnType ) => { - console.log(instructor); - instructor.certificates = []; - instructor.profilePic = { - name: '', - key: '', - url: '' - }; - if (files) { - files.map((file) => { - if (file.originalname === 'profilePic') { - instructor.profilePic.url = file.path; - instructor.profilePic.name=file.originalname - } else { - const certificate = { - name: file.originalname, - url: file.path - }; - instructor.certificates.push(certificate); - } - }); - } - const { password }: { password: string } = instructor; - instructor.email = instructor?.email?.toLowerCase(); - const isEmailAlreadyRegistered = - await instructorRepository.getInstructorByEmail(instructor.email); + console.log(files); + instructor.certificates=[] + // Use object destructuring and default value + const { password = '', email = '' }: InstructorInterface = instructor; + instructor.email = email.toLowerCase(); + + // Check if the email is already registered + const isEmailAlreadyRegistered = await instructorRepository.getInstructorByEmail( + instructor.email + ); + if (isEmailAlreadyRegistered) { throw new AppError( - 'Instructor with same email already exists..!', + 'Instructor with the same email already exists..!', HttpStatusCodes.CONFLICT ); } + + + for (const file of files) { + let uploadedFile; + + if (file.originalname === 'profilePic') { + uploadedFile = await cloudService.upload(file); + instructor.profilePic = uploadedFile; + } else { + uploadedFile = await cloudService.upload(file); + instructor.certificates.push(uploadedFile); + } + } + + // Hash the password if provided if (password) { instructor.password = await authService.hashPassword(password); } console.log(instructor) + + // Add instructor to the repository const response = await instructorRepository.addInstructor(instructor); + return response - ? { status: true, message: 'successfully registered!' } - : { status: false, message: 'failed to register!' }; + ? { status: true, message: 'Successfully registered!' } + : { status: false, message: 'Failed to register!' }; }; export const instructorLogin = async ( diff --git a/server/src/config.ts b/server/src/config.ts index d2edebd..a8c484b 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -26,11 +26,8 @@ const configKeys = { FROM_EMAIL_NODE_MAILER:process.env.FROM_EMAIL as string, - CLOUDINARY_CLOUD_NAME:process.env.CLOUDINARY_CLOUD_NAME as string, - CLOUDINARY_API_KEY:process.env.CLOUDINARY_API_KEY as string, - CLOUDINARY_API_SECRET:process.env.CLOUDINARY_API_SECRET as string, AWS_ACCESS_KEY:process.env.AWS_ACCESS_KEY as string, diff --git a/server/src/frameworks/webserver/middlewares/imageUpload.ts b/server/src/frameworks/webserver/middlewares/imageUpload.ts index e105317..e69de29 100644 --- a/server/src/frameworks/webserver/middlewares/imageUpload.ts +++ b/server/src/frameworks/webserver/middlewares/imageUpload.ts @@ -1,59 +0,0 @@ -import { v2 as cloudinary } from 'cloudinary'; -import multer from 'multer'; -import { RequestHandler } from 'express'; -import { CloudinaryStorage } from 'multer-storage-cloudinary'; -import configKeys from '../../../config'; - -// Cloudinary configuration -cloudinary.config({ - cloud_name: configKeys.CLOUDINARY_CLOUD_NAME, - api_key: configKeys.CLOUDINARY_API_KEY, - api_secret: configKeys.CLOUDINARY_API_SECRET -}); - -// Function to configure Multer for handling single or multiple images -function configureMulter(field: string, limit: number, resourceType: string, allowedFormats: string[]): RequestHandler { - const storageOptions = { - cloudinary: cloudinary, - params: { - resource_type: resourceType, - allowed_formats: allowedFormats, - folder: 'Tutor-Trek' // Specify the folder where the images or videos will be stored in Cloudinary - } - }; - - const storage = new CloudinaryStorage(storageOptions); - return multer({ storage: storage }).array(field, limit); -} - -// Function to configure Multer for handling images -function configureImageMulter(field: string, limit: number): RequestHandler { - const resourceType = 'image'; // Specify the resource type as 'image' for images - const allowedFormats = ['jpg', 'jpeg', 'png']; - - return configureMulter(field, limit, resourceType, allowedFormats); -} - -// Function to configure Multer for handling videos -function configureVideoMulter(field: string, limit: number): RequestHandler { - const resourceType = 'video'; // Specify the resource type as 'video' for videos - const allowedFormats = ['mp4', 'mov']; // Add supported video formats here - - return configureMulter(field, limit, resourceType, allowedFormats); -} - -// Function to configure Multer for handling both image and video uploads -function configureImageAndVideoMulter(field: string, limit: number): RequestHandler { - const resourceType = 'auto'; // Specify the resource type as 'auto' to handle both images and videos - const allowedFormats = ['jpg', 'jpeg', 'png', 'mp4', 'mov']; // Add supported formats for both images and videos - - return configureMulter(field, limit, resourceType, allowedFormats); -} - -export const uploadSingleImage: RequestHandler = configureImageMulter('image', 1); -export const uploadMultipleImages: RequestHandler = configureImageMulter('images', 5); - -export const uploadSingleVideo: RequestHandler = configureVideoMulter('video', 1); -export const uploadMultipleVideos: RequestHandler = configureVideoMulter('videos', 5); - -export const uploadImageAndVideo: RequestHandler = configureImageAndVideoMulter('files', 2); diff --git a/server/src/frameworks/webserver/routes/auth.ts b/server/src/frameworks/webserver/routes/auth.ts index d6ab005..255f7ab 100644 --- a/server/src/frameworks/webserver/routes/auth.ts +++ b/server/src/frameworks/webserver/routes/auth.ts @@ -12,13 +12,17 @@ import { adminDbRepository } from "../../../app/repositories/adminDbRepository"; import { adminRepoMongoDb } from "../../../frameworks/database/mongodb/repositories/adminRepoMongoDb"; import { refreshTokenDbRepository } from "../../../app/repositories/refreshTokenDBRepository"; import { refreshTokenRepositoryMongoDB } from "../../../frameworks/database/mongodb/repositories/refreshTokenRepoMongoDb"; -import { uploadMultipleImages } from "../middlewares/imageUpload"; +import { s3Service } from "../../../frameworks/services/s3CloudService"; +import { cloudServiceInterface } from "../../../app/services/cloudServiceInterface"; +import upload from "../middlewares/multer"; const authRouter = () => { const router = express.Router(); const controller = authController( authServiceInterface, - authService, + authService, + cloudServiceInterface, + s3Service, studentDbRepository, studentRepositoryMongoDB, instructorDbRepository, @@ -36,7 +40,7 @@ const authRouter = () => { router.post("/login-with-google",controller.loginWithGoogle) //* Instructor - router.post("/instructor/instructor-register",uploadMultipleImages, controller.registerInstructor) + router.post("/instructor/instructor-register",upload.array('images'), controller.registerInstructor) router.post("/instructor/instructor-login",controller.loginInstructor) //* Admin diff --git a/server/src/types/instructorInterface.ts b/server/src/types/instructorInterface.ts index 0ab6297..0d51ae0 100644 --- a/server/src/types/instructorInterface.ts +++ b/server/src/types/instructorInterface.ts @@ -18,7 +18,8 @@ export interface InstructorInterface { } export interface Certificate { name:string; - url:string; + url?:string; + key:string } export interface SavedInstructorInterface extends InstructorInterface { _id:string; diff --git a/server/yarn.lock b/server/yarn.lock index 381ed6e..af15535 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -1699,21 +1699,6 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -cloudinary-core@^2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/cloudinary-core/-/cloudinary-core-2.13.0.tgz#b59f90871b6c708c3d0735b9be47ac08181c57fb" - integrity sha512-Nt0Q5I2FtenmJghtC4YZ3MZZbGg1wLm84SsxcuVwZ83OyJqG9CNIGp86CiI6iDv3QobaqBUpOT7vg+HqY5HxEA== - -cloudinary@^1.37.2: - version "1.39.0" - resolved "https://registry.yarnpkg.com/cloudinary/-/cloudinary-1.39.0.tgz#3f4a103ab36daae85705d9240d17b3583978eea2" - integrity sha512-xVCzduGsEgYWe/xnbB6rrx56NCvPtqJGh+G6aTSdattxpuewgFH9S/7JCIatLQcBw2JzkJKAYJ1wdRg/QiRFlw== - dependencies: - cloudinary-core "^2.13.0" - core-js "^3.30.1" - lodash "^4.17.21" - q "^1.5.1" - cluster-key-slot@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" @@ -1801,11 +1786,6 @@ cookie@~0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -core-js@^3.30.1: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.0.tgz#7643d353d899747ab1f8b03d2803b0312a0fb3b6" - integrity sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -2829,11 +2809,6 @@ multer-s3@^3.0.1: html-comment-regex "^1.1.2" run-parallel "^1.1.6" -multer-storage-cloudinary@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/multer-storage-cloudinary/-/multer-storage-cloudinary-4.0.0.tgz#afc9e73c353668c57dda5b73b7bb84bae6635f6f" - integrity sha512-25lm9R6o5dWrHLqLvygNX+kBOxprzpmZdnVKH4+r68WcfCt8XV6xfQaMuAg+kUE5Xmr8mJNA4gE0AcBj9FJyWA== - multer@^1.4.5-lts.1: version "1.4.5-lts.1" resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" @@ -3040,11 +3015,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -q@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== - qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"