ajout de l'upload. Reste un mystérieux problème à régler avec l'extenstion et multer
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@ WORKDIR /usr/app
|
||||
COPY api_back/* .
|
||||
COPY api_back/.env .env
|
||||
|
||||
|
||||
RUN npm ci
|
||||
|
||||
CMD [ "npm", "run", "start:dev" ]
|
||||
|
||||
9
srcs/requirements/nestjs/api_back/node_modules/.package-lock.json
generated
vendored
9
srcs/requirements/nestjs/api_back/node_modules/.package-lock.json
generated
vendored
@@ -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",
|
||||
|
||||
21
srcs/requirements/nestjs/api_back/node_modules/@types/multer/LICENSE
generated
vendored
Executable file
21
srcs/requirements/nestjs/api_back/node_modules/@types/multer/LICENSE
generated
vendored
Executable file
@@ -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
|
||||
16
srcs/requirements/nestjs/api_back/node_modules/@types/multer/README.md
generated
vendored
Executable file
16
srcs/requirements/nestjs/api_back/node_modules/@types/multer/README.md
generated
vendored
Executable file
@@ -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).
|
||||
321
srcs/requirements/nestjs/api_back/node_modules/@types/multer/index.d.ts
generated
vendored
Executable file
321
srcs/requirements/nestjs/api_back/node_modules/@types/multer/index.d.ts
generated
vendored
Executable file
@@ -0,0 +1,321 @@
|
||||
// Type definitions for multer 1.4
|
||||
// Project: https://github.com/expressjs/multer
|
||||
// Definitions 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>
|
||||
// Piotr Błażejewicz <https://github.com/peterblazejewicz>
|
||||
// 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<Field>): 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<Express.Multer.File>) => 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;
|
||||
62
srcs/requirements/nestjs/api_back/node_modules/@types/multer/package.json
generated
vendored
Executable file
62
srcs/requirements/nestjs/api_back/node_modules/@types/multer/package.json
generated
vendored
Executable file
@@ -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"
|
||||
}
|
||||
22
srcs/requirements/nestjs/api_back/package-lock.json
generated
22
srcs/requirements/nestjs/api_back/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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('/');
|
||||
|
||||
@@ -24,7 +24,7 @@ export class AuthenticateGuard implements CanActivate {
|
||||
export class TwoFactorGuard implements CanActivate {
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -9,6 +9,8 @@ export class CreateUsersDto {
|
||||
readonly email: string;
|
||||
@IsString()
|
||||
readonly image_url: string;
|
||||
@IsString()
|
||||
readonly status: string;
|
||||
@IsBoolean()
|
||||
readonly isEnabledTwoFactorAuth: boolean;
|
||||
}
|
||||
|
||||
@@ -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){}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY ./conf/default.conf /etc/nginx/conf.d/default.conf
|
||||
@@ -1,9 +1,10 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let avatarUser, postVar, newAvatar, fileinput;
|
||||
let usernameSv = "";
|
||||
let image_urlSv = "";
|
||||
let uploadAvatarSuccess = false;
|
||||
let gAuth = false;
|
||||
let errors = {usernameSv, image_urlSv};
|
||||
onMount(async () => {
|
||||
@@ -11,11 +12,29 @@
|
||||
then(response => response.json()).
|
||||
then(data => {
|
||||
usernameSv = data.username;
|
||||
image_urlSv = data.image_url;
|
||||
gAuth = data.isEnabledTwoFactorAuth;
|
||||
});
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar", {method: "GET"}).
|
||||
then(response => {return response.blob()}).
|
||||
then(data => {
|
||||
const url = URL.createObjectURL(data);
|
||||
avatarUser = url;
|
||||
});
|
||||
});
|
||||
|
||||
$: uploadAvatar = async() => {
|
||||
const data = new FormData();
|
||||
data.append("file", newAvatar[0]);
|
||||
console.log(data);
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar",
|
||||
{
|
||||
method : 'POST',
|
||||
body : data,
|
||||
})
|
||||
.then(uploadAvatarSuccess = true)
|
||||
.catch(errors.image_urlSv = "Something went wrong." )
|
||||
}
|
||||
|
||||
$: submit = async() => {
|
||||
errors.usernameSv = "";
|
||||
errors.image_urlSv ="";
|
||||
@@ -23,10 +42,6 @@
|
||||
errors.usernameSv = "Username is required";
|
||||
return;
|
||||
}
|
||||
if (image_urlSv === undefined || image_urlSv.trim() === "") {
|
||||
errors.image_urlSv = "image_url is required";
|
||||
return;
|
||||
}
|
||||
console.log(usernameSv);
|
||||
await fetch("http://transcendance:8080/api/v2/user/",
|
||||
{
|
||||
@@ -36,46 +51,59 @@
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"username" : usernameSv,
|
||||
"image_url" : image_urlSv,
|
||||
"isEnabledTwoFactorAuth" : gAuth
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<main class="form-signin w-100 m-auto">
|
||||
<div class="p-20">
|
||||
{#if errors.image_urlSv}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{errors.image_urlSv}
|
||||
</div>
|
||||
{/if}
|
||||
{#if errors.username}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{errors.username}
|
||||
</div>
|
||||
{/if}
|
||||
{#if errors.image_url}
|
||||
{#if uploadAvatarSuccess}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{errors.image_url}
|
||||
You avatar has been successfully uploaded !
|
||||
</div>
|
||||
{/if}
|
||||
<form on:submit|preventDefault={uploadAvatar}>
|
||||
{#if avatarUser}
|
||||
<img class="avatar" src={avatarUser} alt="" />
|
||||
{/if}
|
||||
<input
|
||||
type="text"
|
||||
bind:value={postVar}
|
||||
placeholder={"choose your file"}
|
||||
/>
|
||||
<br />
|
||||
<input
|
||||
type="file"
|
||||
bind:files={newAvatar} />
|
||||
<br />
|
||||
<button type="submit" class="p-2 bg-blue-500 text-white mt-4 px-6">
|
||||
Choose avatar
|
||||
</button>
|
||||
</form>
|
||||
<form on:submit|preventDefault={submit}>
|
||||
<label for="username" class="block text-sm text-gray-600">Username</label>
|
||||
<input id="username" type="text" placeholder=${usernameSv} class="block px-1 py-2 mt-2 border-2 border-gray-100 text-gray-800" bind:value={usernameSv} />
|
||||
|
||||
<label for="image_url" class="block text-sm text-gray-600 mt-4">image_url</label>
|
||||
<input id="image_url" type="image_url" bind:value={image_urlSv} placeholder=${image_urlSv} class="block px-1 py-2 mt-2 border-2 border-gray-100 text-gray-800" />
|
||||
|
||||
<div>
|
||||
<input type="checkbox" bind:checked={gAuth} id="gAuth" name="gAuth">
|
||||
<label for="gAuth">Enable google authenticator</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="p-2 bg-blue-500 text-white mt-4 px-6">
|
||||
Change
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
|
||||
Reference in New Issue
Block a user