diff --git a/srcs/requirements/nestjs/.dockerignore b/srcs/requirements/nestjs/.dockerignore index f84cb79f..c2e21c2c 100644 --- a/srcs/requirements/nestjs/.dockerignore +++ b/srcs/requirements/nestjs/.dockerignore @@ -9,3 +9,6 @@ !api_back/*.html !api_back/*.lock !api_back/.env +!api_back/src/uploads/avatars/default.png +!api_back/*.jpg +!api_back/*.jpeg diff --git a/srcs/requirements/nestjs/Dockerfile b/srcs/requirements/nestjs/Dockerfile index ef3dcdd6..dd046602 100644 --- a/srcs/requirements/nestjs/Dockerfile +++ b/srcs/requirements/nestjs/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /usr/app COPY api_back/* . COPY api_back/.env .env + RUN npm ci CMD [ "npm", "run", "start:dev" ] diff --git a/srcs/requirements/nestjs/api_back/node_modules/.package-lock.json b/srcs/requirements/nestjs/api_back/node_modules/.package-lock.json index 07e3a581..df63516b 100644 --- a/srcs/requirements/nestjs/api_back/node_modules/.package-lock.json +++ b/srcs/requirements/nestjs/api_back/node_modules/.package-lock.json @@ -2207,6 +2207,15 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "node_modules/@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "16.11.68", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.68.tgz", diff --git a/srcs/requirements/nestjs/api_back/node_modules/@types/multer/LICENSE b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/LICENSE new file mode 100755 index 00000000..9e841e7a --- /dev/null +++ b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/srcs/requirements/nestjs/api_back/node_modules/@types/multer/README.md b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/README.md new file mode 100755 index 00000000..cdc128b8 --- /dev/null +++ b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/README.md @@ -0,0 +1,16 @@ +# Installation +> `npm install --save @types/multer` + +# Summary +This package contains type definitions for multer (https://github.com/expressjs/multer). + +# Details +Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/multer. + +### Additional Details + * Last updated: Wed, 07 Jul 2021 00:01:45 GMT + * Dependencies: [@types/express](https://npmjs.com/package/@types/express) + * Global values: none + +# Credits +These definitions were written by [jt000](https://github.com/jt000), [vilicvane](https://github.com/vilic), [David Broder-Rodgers](https://github.com/DavidBR-SW), [Michael Ledin](https://github.com/mxl), [HyunSeob Lee](https://github.com/hyunseob), [Pierre Tchuente](https://github.com/PierreTchuente), [Oliver Emery](https://github.com/thrymgjol), and [Piotr Błażejewicz](https://github.com/peterblazejewicz). diff --git a/srcs/requirements/nestjs/api_back/node_modules/@types/multer/index.d.ts b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/index.d.ts new file mode 100755 index 00000000..bd91d3ae --- /dev/null +++ b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/index.d.ts @@ -0,0 +1,321 @@ +// Type definitions for multer 1.4 +// Project: https://github.com/expressjs/multer +// Definitions by: jt000 +// vilicvane +// David Broder-Rodgers +// Michael Ledin +// HyunSeob Lee +// Pierre Tchuente +// Oliver Emery +// Piotr Błażejewicz +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +import { Request, RequestHandler } from 'express'; +import { Readable } from 'stream'; + +declare global { + namespace Express { + namespace Multer { + /** Object containing file metadata and access information. */ + interface File { + /** Name of the form field associated with this file. */ + fieldname: string; + /** Name of the file on the uploader's computer. */ + originalname: string; + /** + * Value of the `Content-Transfer-Encoding` header for this file. + * @deprecated since July 2015 + * @see RFC 7578, Section 4.7 + */ + encoding: string; + /** Value of the `Content-Type` header for this file. */ + mimetype: string; + /** Size of the file in bytes. */ + size: number; + /** + * A readable stream of this file. Only available to the `_handleFile` + * callback for custom `StorageEngine`s. + */ + stream: Readable; + /** `DiskStorage` only: Directory to which this file has been uploaded. */ + destination: string; + /** `DiskStorage` only: Name of this file within `destination`. */ + filename: string; + /** `DiskStorage` only: Full path to the uploaded file. */ + path: string; + /** `MemoryStorage` only: A Buffer containing the entire file. */ + buffer: Buffer; + } + } + + interface Request { + /** `Multer.File` object populated by `single()` middleware. */ + file?: Multer.File | undefined; + /** + * Array or dictionary of `Multer.File` object populated by `array()`, + * `fields()`, and `any()` middleware. + */ + files?: { + [fieldname: string]: Multer.File[]; + } | Multer.File[] | undefined; + } + } +} + + /** + * Returns a Multer instance that provides several methods for generating + * middleware that process files uploaded in `multipart/form-data` format. + * + * The `StorageEngine` specified in `storage` will be used to store files. If + * `storage` is not set and `dest` is, files will be stored in `dest` on the + * local file system with random names. If neither are set, files will be stored + * in memory. + * + * In addition to files, all generated middleware process all text fields in + * the request. For each non-file field, the `Request.body` object will be + * populated with an entry mapping the field name to its string value, or array + * of string values if multiple fields share the same name. + */ +declare function multer(options?: multer.Options): multer.Multer; + +declare namespace multer { + /** + * @see {@link https://github.com/expressjs/multer#api} + */ + interface Multer { + /** + * Returns middleware that processes a single file associated with the + * given form field. + * + * The `Request` object will be populated with a `file` object containing + * information about the processed file. + * + * @param fieldName Name of the multipart form field to process. + */ + single(fieldName: string): RequestHandler; + /** + * Returns middleware that processes multiple files sharing the same field + * name. + * + * The `Request` object will be populated with a `files` array containing + * an information object for each processed file. + * + * @param fieldName Shared name of the multipart form fields to process. + * @param maxCount Optional. Maximum number of files to process. (default: Infinity) + * @throws `MulterError('LIMIT_UNEXPECTED_FILE')` if more than `maxCount` files are associated with `fieldName` + */ + array(fieldName: string, maxCount?: number): RequestHandler; + /** + * Returns middleware that processes multiple files associated with the + * given form fields. + * + * The `Request` object will be populated with a `files` object which + * maps each field name to an array of the associated file information + * objects. + * + * @param fields Array of `Field` objects describing multipart form fields to process. + * @throws `MulterError('LIMIT_UNEXPECTED_FILE')` if more than `maxCount` files are associated with `fieldName` for any field. + */ + fields(fields: ReadonlyArray): RequestHandler; + /** + * Returns middleware that processes all files contained in the multipart + * request. + * + * The `Request` object will be populated with a `files` array containing + * an information object for each processed file. + */ + any(): RequestHandler; + /** + * Returns middleware that accepts only non-file multipart form fields. + * + * @throws `MulterError('LIMIT_UNEXPECTED_FILE')` if any file is encountered. + */ + none(): RequestHandler; + } + + /** + * Returns a `StorageEngine` implementation configured to store files on + * the local file system. + * + * A string or function may be specified to determine the destination + * directory, and a function to determine filenames. If no options are set, + * files will be stored in the system's temporary directory with random 32 + * character filenames. + */ + function diskStorage(options: DiskStorageOptions): StorageEngine; + + /** + * Returns a `StorageEngine` implementation configured to store files in + * memory as `Buffer` objects. + */ + function memoryStorage(): StorageEngine; + + type ErrorCode = + | 'LIMIT_PART_COUNT' + | 'LIMIT_FILE_SIZE' + | 'LIMIT_FILE_COUNT' + | 'LIMIT_FIELD_KEY' + | 'LIMIT_FIELD_VALUE' + | 'LIMIT_FIELD_COUNT' + | 'LIMIT_UNEXPECTED_FILE'; + + class MulterError extends Error { + constructor(code: ErrorCode, field?: string); + /** Name of the MulterError constructor. */ + name: string; + /** Identifying error code. */ + code: ErrorCode; + /** Descriptive error message. */ + message: string; + /** Name of the multipart form field associated with this error. */ + field?: string | undefined; + } + + /** + * a function to control which files should be uploaded and which should be skipped + * pass a boolean to indicate if the file should be accepted + * pass an error if something goes wrong + */ + interface FileFilterCallback { + (error: Error): void; + (error: null, acceptFile: boolean): void; + } + + /** Options for initializing a Multer instance. */ + interface Options { + /** + * A `StorageEngine` responsible for processing files uploaded via Multer. + * Takes precedence over `dest`. + */ + storage?: StorageEngine | undefined; + /** + * The destination directory for uploaded files. If `storage` is not set + * and `dest` is, Multer will create a `DiskStorage` instance configured + * to store files at `dest` with random filenames. + * + * Ignored if `storage` is set. + */ + dest?: string | undefined; + /** + * An object specifying various limits on incoming data. This object is + * passed to Busboy directly, and the details of properties can be found + * at https://github.com/mscdex/busboy#busboy-methods. + */ + limits?: { + /** Maximum size of each form field name in bytes. (Default: 100) */ + fieldNameSize?: number | undefined; + /** Maximum size of each form field value in bytes. (Default: 1048576) */ + fieldSize?: number | undefined; + /** Maximum number of non-file form fields. (Default: Infinity) */ + fields?: number | undefined; + /** Maximum size of each file in bytes. (Default: Infinity) */ + fileSize?: number | undefined; + /** Maximum number of file fields. (Default: Infinity) */ + files?: number | undefined; + /** Maximum number of parts (non-file fields + files). (Default: Infinity) */ + parts?: number | undefined; + /** Maximum number of headers. (Default: 2000) */ + headerPairs?: number | undefined; + } | undefined; + /** Preserve the full path of the original filename rather than the basename. (Default: false) */ + preservePath?: boolean | undefined; + /** + * Optional function to control which files are uploaded. This is called + * for every file that is processed. + * + * @param req The Express `Request` object. + * @param file Object containing information about the processed file. + * @param callback a function to control which files should be uploaded and which should be skipped. + */ + fileFilter?( + req: Request, + file: Express.Multer.File, + callback: FileFilterCallback, + ): void; + } + + /** + * Implementations of this interface are responsible for storing files + * encountered by Multer and returning information on how to access them + * once stored. Implementations must also provide a method for removing + * files in the event that an error occurs. + */ + interface StorageEngine { + /** + * Store the file described by `file`, then invoke the callback with + * information about the stored file. + * + * File contents are available as a stream via `file.stream`. Information + * passed to the callback will be merged with `file` for subsequent + * middleware. + * + * @param req The Express `Request` object. + * @param file Object with `stream`, `fieldname`, `originalname`, `encoding`, and `mimetype` defined. + * @param callback Callback to specify file information. + */ + _handleFile( + req: Request, + file: Express.Multer.File, + callback: (error?: any, info?: Partial) => void + ): void; + /** + * Remove the file described by `file`, then invoke the callback with. + * + * `file` contains all the properties available to `_handleFile`, as + * well as those returned by `_handleFile`. + * + * @param req The Express `Request` object. + * @param file Object containing information about the processed file. + * @param callback Callback to indicate completion. + */ + _removeFile( + req: Request, + file: Express.Multer.File, + callback: (error: Error | null) => void + ): void; + } + + interface DiskStorageOptions { + /** + * A string or function that determines the destination path for uploaded + * files. If a string is passed and the directory does not exist, Multer + * attempts to create it recursively. If neither a string or a function + * is passed, the destination defaults to `os.tmpdir()`. + * + * @param req The Express `Request` object. + * @param file Object containing information about the processed file. + * @param callback Callback to determine the destination path. + */ + destination?: string | (( + req: Request, + file: Express.Multer.File, + callback: (error: Error | null, destination: string) => void + ) => void) | undefined; + /** + * A function that determines the name of the uploaded file. If nothing + * is passed, Multer will generate a 32 character pseudorandom hex string + * with no extension. + * + * @param req The Express `Request` object. + * @param file Object containing information about the processed file. + * @param callback Callback to determine the name of the uploaded file. + */ + filename?( + req: Request, + file: Express.Multer.File, + callback: (error: Error | null, filename: string) => void + ): void; + } + + /** + * An object describing a field name and the maximum number of files with + * that field name to accept. + */ + interface Field { + /** The field name. */ + name: string; + /** Optional maximum number of files per field to accept. (Default: Infinity) */ + maxCount?: number | undefined; + } +} + +export = multer; diff --git a/srcs/requirements/nestjs/api_back/node_modules/@types/multer/package.json b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/package.json new file mode 100755 index 00000000..2577d2c8 --- /dev/null +++ b/srcs/requirements/nestjs/api_back/node_modules/@types/multer/package.json @@ -0,0 +1,62 @@ +{ + "name": "@types/multer", + "version": "1.4.7", + "description": "TypeScript definitions for multer", + "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/multer", + "license": "MIT", + "contributors": [ + { + "name": "jt000", + "url": "https://github.com/jt000", + "githubUsername": "jt000" + }, + { + "name": "vilicvane", + "url": "https://github.com/vilic", + "githubUsername": "vilic" + }, + { + "name": "David Broder-Rodgers", + "url": "https://github.com/DavidBR-SW", + "githubUsername": "DavidBR-SW" + }, + { + "name": "Michael Ledin", + "url": "https://github.com/mxl", + "githubUsername": "mxl" + }, + { + "name": "HyunSeob Lee", + "url": "https://github.com/hyunseob", + "githubUsername": "hyunseob" + }, + { + "name": "Pierre Tchuente", + "url": "https://github.com/PierreTchuente", + "githubUsername": "PierreTchuente" + }, + { + "name": "Oliver Emery", + "url": "https://github.com/thrymgjol", + "githubUsername": "thrymgjol" + }, + { + "name": "Piotr Błażejewicz", + "url": "https://github.com/peterblazejewicz", + "githubUsername": "peterblazejewicz" + } + ], + "main": "", + "types": "index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git", + "directory": "types/multer" + }, + "scripts": {}, + "dependencies": { + "@types/express": "*" + }, + "typesPublisherContentHash": "32b2d0cc5d84f278945858a1be2b00bcdd046da7c6e0886d5afd0d1e002ee95a", + "typeScriptVersion": "3.6" +} \ No newline at end of file diff --git a/srcs/requirements/nestjs/api_back/package-lock.json b/srcs/requirements/nestjs/api_back/package-lock.json index 5b0e01a0..3c68b140 100644 --- a/srcs/requirements/nestjs/api_back/package-lock.json +++ b/srcs/requirements/nestjs/api_back/package-lock.json @@ -37,7 +37,8 @@ "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", - "typeorm": "^0.3.10" + "typeorm": "^0.3.10", + "uuid": "^9.0.0" }, "devDependencies": { "@nestjs/cli": "^9.0.0", @@ -45,6 +46,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/multer": "^1.4.7", "@types/node": "^16.0.0", "@types/passport-jwt": "^3.0.7", "@types/passport-local": "^1.0.34", @@ -2268,6 +2270,15 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "node_modules/@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "16.11.68", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.68.tgz", @@ -11422,6 +11433,15 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/node": { "version": "16.11.68", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.68.tgz", diff --git a/srcs/requirements/nestjs/api_back/package.json b/srcs/requirements/nestjs/api_back/package.json index d8b14391..f92f86b3 100644 --- a/srcs/requirements/nestjs/api_back/package.json +++ b/srcs/requirements/nestjs/api_back/package.json @@ -49,7 +49,8 @@ "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", - "typeorm": "^0.3.10" + "typeorm": "^0.3.10", + "uuid": "^9.0.0" }, "devDependencies": { "@nestjs/cli": "^9.0.0", @@ -57,6 +58,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/multer": "^1.4.7", "@types/node": "^16.0.0", "@types/passport-jwt": "^3.0.7", "@types/passport-local": "^1.0.34", diff --git a/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts b/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts index cbe76106..a032478b 100644 --- a/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts @@ -46,6 +46,7 @@ export class AuthenticationController { @UseGuards(AuthenticateGuard) logout(@Req() request, @Res() response, @Next() next) { this.userService.setIsTwoFactorAuthenticatedWhenLogout(request.user.id); + this.userService.updateStatus(request.user.id, 'disconnected'); request.logout(function(err) { if (err) { return next(err); } response.redirect('/'); diff --git a/srcs/requirements/nestjs/api_back/src/auth/42/guards/42guards.ts b/srcs/requirements/nestjs/api_back/src/auth/42/guards/42guards.ts index 82a39eea..8254ec73 100644 --- a/srcs/requirements/nestjs/api_back/src/auth/42/guards/42guards.ts +++ b/srcs/requirements/nestjs/api_back/src/auth/42/guards/42guards.ts @@ -24,7 +24,7 @@ export class AuthenticateGuard implements CanActivate { export class TwoFactorGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); - console.log("TwoFactorGuard : Is User authenticated : " + request.isAuthenticated() + " and has 2FA enabled : " + request.user.isEnabledTwoFactorAuth + " and has 2FA verified : " + request.user.isTwoFactorAuthenticated); + //console.log("TwoFactorGuard : Is User authenticated : " + request.isAuthenticated() + " and has 2FA enabled : " + request.user.isEnabledTwoFactorAuth + " and has 2FA verified : " + request.user.isTwoFactorAuthenticated); return request.isAuthenticated() && (request.user.isEnabledTwoFactorAuth === false || request.user.isTwoFactorAuthenticated === true); } diff --git a/srcs/requirements/nestjs/api_back/src/auth/42/strategy/42strategy.ts b/srcs/requirements/nestjs/api_back/src/auth/42/strategy/42strategy.ts index ad39fb7c..6b43659c 100644 --- a/srcs/requirements/nestjs/api_back/src/auth/42/strategy/42strategy.ts +++ b/srcs/requirements/nestjs/api_back/src/auth/42/strategy/42strategy.ts @@ -5,23 +5,23 @@ import { AuthenticationService } from "../authentication.service"; import { CreateUsersDto } from "src/users/dto/create-users.dto"; @Injectable() -export class FortyTwoStrategy extends PassportStrategy(Strategy, "42") { - constructor(private authenticationService: AuthenticationService) { - super({ - clientID: process.env.FORTYTWO_CLIENT_ID, - clientSecret: process.env.FORTYTWO_CLIENT_SECRET, - callbackURL: process.env.FORTYTWO_CALLBACK_URL, - scope: ["public"], - }); - } + export class FortyTwoStrategy extends PassportStrategy(Strategy, "42") { + constructor(private authenticationService: AuthenticationService) { + super({ + clientID: process.env.FORTYTWO_CLIENT_ID, + clientSecret: process.env.FORTYTWO_CLIENT_SECRET, + callbackURL: process.env.FORTYTWO_CALLBACK_URL, + scope: ["public"], + }); + } - async validate(accessToken: string, refreshToken: string, profile: Profile, callbackURL: string) { - console.log("Validate inside strategy.ts"); - console.log(profile.id, profile.username, profile.phoneNumbers[0].value, profile.emails[0].value, profile.photos[0].value); - const userDTO: CreateUsersDto = { fortyTwoId: profile.id, username: profile.username, email: profile.emails[0].value, image_url: profile.photos[0].value, isEnabledTwoFactorAuth: false }; - const user = await this.authenticationService.validateUser(userDTO); - if (!user) - throw new UnauthorizedException(); - return user; - } + async validate(accessToken: string, refreshToken: string, profile: Profile, callbackURL: string) { + console.log("Validate inside strategy.ts"); + console.log(profile.id, profile.username, profile.phoneNumbers[0].value, profile.emails[0].value, profile.photos[0].value); + const userDTO: CreateUsersDto = { fortyTwoId: profile.id, username: profile.username, email: profile.emails[0].value, image_url: 'default.png', isEnabledTwoFactorAuth: false , status: "connected" }; + const user = await this.authenticationService.validateUser(userDTO); + if (!user) + throw new UnauthorizedException(); + return user; + } } diff --git a/srcs/requirements/nestjs/api_back/src/common/constants/constants.ts b/srcs/requirements/nestjs/api_back/src/common/constants/constants.ts new file mode 100644 index 00000000..1b549ac1 --- /dev/null +++ b/srcs/requirements/nestjs/api_back/src/common/constants/constants.ts @@ -0,0 +1,15 @@ +import { randomUUID } from "crypto"; +import { diskStorage } from "multer"; +import path from "path"; + +export const storageForAvatar = { + storage: diskStorage({ + destination: './uploads/avatars', + filename: (req, file, cb) => { + console.log(file); + const filename : string = file.originalname.split(' ').join('_') + randomUUID(); + const extension : string = path.extname(file.originalname); + cb(null, `${filename}${extension}`); + } + }) +} diff --git a/srcs/requirements/nestjs/api_back/src/uploads/avatars/default.png b/srcs/requirements/nestjs/api_back/src/uploads/avatars/default.png new file mode 100644 index 00000000..1dfcbae0 Binary files /dev/null and b/srcs/requirements/nestjs/api_back/src/uploads/avatars/default.png differ diff --git a/srcs/requirements/nestjs/api_back/src/users/dto/create-users.dto.ts b/srcs/requirements/nestjs/api_back/src/users/dto/create-users.dto.ts index b5c862ea..a2d453e8 100644 --- a/srcs/requirements/nestjs/api_back/src/users/dto/create-users.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/users/dto/create-users.dto.ts @@ -9,6 +9,8 @@ export class CreateUsersDto { readonly email: string; @IsString() readonly image_url: string; + @IsString() + readonly status: string; @IsBoolean() readonly isEnabledTwoFactorAuth: boolean; } diff --git a/srcs/requirements/nestjs/api_back/src/users/dto/update-users.dto.ts b/srcs/requirements/nestjs/api_back/src/users/dto/update-users.dto.ts index 1f4d10cf..53e00fb3 100644 --- a/srcs/requirements/nestjs/api_back/src/users/dto/update-users.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/users/dto/update-users.dto.ts @@ -5,4 +5,4 @@ import { OmitType, PartialType } from "@nestjs/mapped-types"; import { CreateUsersDto } from "./create-users.dto"; -export class UpdateUsersDto extends OmitType(CreateUsersDto, ['fortyTwoId', 'email'] as const){} +export class UpdateUsersDto extends OmitType(CreateUsersDto, ['fortyTwoId', 'email', 'image_url', 'status'] as const){} diff --git a/srcs/requirements/nestjs/api_back/src/users/entities/user.entity.ts b/srcs/requirements/nestjs/api_back/src/users/entities/user.entity.ts index 714b5b15..b0a97c9a 100644 --- a/srcs/requirements/nestjs/api_back/src/users/entities/user.entity.ts +++ b/srcs/requirements/nestjs/api_back/src/users/entities/user.entity.ts @@ -21,14 +21,14 @@ export class User { @IsEmail() email: string; - @Column({ nullable: true }) + @Column() image_url : string; @Column({ nullable: true }) phone: string; - @Column('json', { nullable: true }) - status: [string]; + @Column({ default: 'disconnected' }) + status: string; // @Column() // isFirstConnection: boolean; diff --git a/srcs/requirements/nestjs/api_back/src/users/users.controller.ts b/srcs/requirements/nestjs/api_back/src/users/users.controller.ts index f1c00cf5..2d8828ac 100644 --- a/srcs/requirements/nestjs/api_back/src/users/users.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/users/users.controller.ts @@ -1,15 +1,29 @@ import { - Body, Controller, Delete, Get, HttpCode, - HttpStatus, Param, Patch, Post, Query, Req, UseGuards + Body, Controller, Delete, Get, HttpException, NotFoundException, Patch, Post, Query, Req, Res, UploadedFile, UseGuards, UseInterceptors } from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards'; import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto'; import { ValidationPipe } from 'src/common/validation/validation.pipe'; -import { CreateUsersDto } from './dto/create-users.dto'; import { UpdateUsersDto } from './dto/update-users.dto'; - import { UsersService } from './users.service'; +import { User } from './entities/user.entity'; +import { diskStorage } from 'multer'; +import path from 'path'; +import { randomUUID } from 'crypto'; +import { of } from 'rxjs'; +export const storageForAvatar = { + storage: diskStorage({ + destination: './uploads/avatars', + filename: (req, file, cb) => { + const filename : string = file.originalname.split(' ').join('_') + randomUUID(); + console.log("RES : " ) + const extension : string = path.extname(file.originalname); + cb(null, `${filename}${extension}`); + } + }) +} @Controller('user') export class UsersController { @@ -61,4 +75,28 @@ export class UsersController { remove(@Req() req) { return this.usersService.remove(req.user.id); } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Post('avatar') + @UseInterceptors(FileInterceptor('file', storageForAvatar)) + uploadAvatar(@UploadedFile() file, @Req() request){ + const user : User = request.user; + this.usersService.updateAvatar(user.id, file.filename); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Get('avatar') + getAvatar(@Req() request, @Res() response) { + const promise = this.usersService.getAvatarUrl(request.user.id).then((url) => + { + if (url) + return of(response.sendFile(process.cwd() + '/uploads/avatars/' + url)); + else + throw new NotFoundException('Avatar not found'); + }); + return promise; + } + } diff --git a/srcs/requirements/nestjs/api_back/src/users/users.service.ts b/srcs/requirements/nestjs/api_back/src/users/users.service.ts index a5cbcd59..dbfc5fd4 100644 --- a/srcs/requirements/nestjs/api_back/src/users/users.service.ts +++ b/srcs/requirements/nestjs/api_back/src/users/users.service.ts @@ -1,6 +1,6 @@ import { HttpCode, HttpException, HttpStatus, Injectable, NotFoundException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { NotFoundError } from 'rxjs'; +import { NotFoundError, of } from 'rxjs'; import { User } from './entities/user.entity'; import { ConnectionOptionsReader, Repository } from 'typeorm'; import { CreateUsersDto } from './dto/create-users.dto'; @@ -9,6 +9,7 @@ import { Friendship } from '../friendship/entities/friendship.entity'; import { isNumberString } from 'class-validator'; import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto'; import { PartialUsersDto } from './dto/partial-users.dto'; +import { join } from 'path'; // On va devoir sûrement trouver un moyen plus simple pour passer l'id, sûrement via des pipes // ou des interceptors, mais pour l'instant on va faire comme ça. @@ -65,7 +66,7 @@ export class UsersService { } async update(id: string, updateUserDto: UpdateUsersDto) { - console.log(`Update user ${id} with ${updateUserDto.image_url} + ${updateUserDto.isEnabledTwoFactorAuth}`); + console.log(`Update user ${id} with ${updateUserDto.isEnabledTwoFactorAuth}`); const user = await this.userRepository.preload( {id: +id, ...updateUserDto}); @@ -92,4 +93,22 @@ export class UsersService { async setAuthenticatorSecret(id: number, secret: string) { return this.userRepository.update(id, {secretTwoFactorAuth: secret}); } + + async updateStatus(id: number, status: string) { + return this.userRepository.update(id, {status: status}); + } + + async updateAvatar(id: number, avatar: string) { + return this.userRepository.update(id, {image_url: avatar}); + } + + async getAvatarUrl(id: number) { + const user = await this.userRepository.findOneBy({id: id}); + if (!user) + throw new HttpException(`The user could not be found.`,HttpStatus.NOT_FOUND); + if (!user.image_url) + throw new HttpException(`The user has no avatar.`,HttpStatus.NOT_FOUND); + console.log(user.image_url); + return user.image_url; + } } diff --git a/srcs/requirements/nginx/Dockerfile b/srcs/requirements/nginx/Dockerfile deleted file mode 100644 index 13ad36a0..00000000 --- a/srcs/requirements/nginx/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM nginx:alpine - -COPY ./conf/default.conf /etc/nginx/conf.d/default.conf diff --git a/srcs/requirements/svelte/api_front/src/pages/profil/updateProfil.svelte b/srcs/requirements/svelte/api_front/src/pages/profil/updateProfil.svelte index a37c722c..fec2d259 100644 --- a/srcs/requirements/svelte/api_front/src/pages/profil/updateProfil.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/profil/updateProfil.svelte @@ -1,9 +1,10 @@
+ {#if errors.image_urlSv} + + {/if} {#if errors.username} {/if} - {#if errors.image_url} + {#if uploadAvatarSuccess} {/if} +
+ {#if avatarUser} + + {/if} + +
+ +
+ +
- - - -
- -
-