diff --git a/.drone.yml b/.drone.yml index 9121e57..9c63a1b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,6 +9,17 @@ trigger: steps: - name: api-build image: node:latest + environment: + DB_USERNAME: + from_secret: SECRET_DB_USERNAME + DB_DBHOST: + from_secret: SECRET_DB_DBHOST + DB_DBNAME: + from_secret: SECRET_DB_DBNAME + DB_USERPASSWORD: + from_secret: SECRET_DB_USERPASSWORD + DB_PORT: + from_secret: SECRET_DB_PORT commands: - cd ./API-Project - npm install @@ -16,6 +27,17 @@ steps: - name: test image: node:latest + environment: + DB_USERNAME: + from_secret: SECRET_DB_USERNAME + DB_DBHOST: + from_secret: SECRET_DB_DBHOST + DB_DBNAME: + from_secret: SECRET_DB_DBNAME + DB_USERPASSWORD: + from_secret: SECRET_DB_USERPASSWORD + DB_PORT: + from_secret: SECRET_DB_PORT commands: - cd ./API-Project - npm run test @@ -23,6 +45,17 @@ steps: - name: docker-build-and-push image: plugins/docker + environment: + DB_USERNAME: + from_secret: SECRET_DB_USERNAME + DB_DBHOST: + from_secret: SECRET_DB_DBHOST + DB_DBNAME: + from_secret: SECRET_DB_DBNAME + DB_USERPASSWORD: + from_secret: SECRET_DB_USERPASSWORD + DB_PORT: + from_secret: SECRET_DB_PORT settings: dockerfile: API-Project/Dockerfile context: API-Project @@ -38,11 +71,21 @@ steps: - name: deploy-container image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest environment: - IMAGENAME: hub.codefirst.iut.uca.fr/rayhan.hassou/leftovers_api:latest - CONTAINERNAME: left-over-container - COMMAND: create - OVERWRITE: true - ADMINS: louison.parant,remi.regnault,rayhan.hassou + IMAGENAME: hub.codefirst.iut.uca.fr/rayhan.hassou/leftovers_api:latest + CONTAINERNAME: left-over-container + COMMAND: create + OVERWRITE: true + ADMINS: louison.parant,remi.regnault,rayhan.hassou + DB_USERNAME: + from_secret: SECRET_DB_USERNAME + DB_DBHOST: + from_secret: SECRET_DB_DBHOST + DB_DBNAME: + from_secret: SECRET_DB_DBNAME + DB_USERPASSWORD: + from_secret: SECRET_DB_USERPASSWORD + DB_PORT: + from_secret: SECRET_DB_PORT depends_on: [ docker-build-and-push ] - name: code-analysis diff --git a/API-Project/package-lock.json b/API-Project/package-lock.json index b2f2f82..ffc5c3d 100644 --- a/API-Project/package-lock.json +++ b/API-Project/package-lock.json @@ -12,7 +12,9 @@ "@types/express": "^4.17.21", "@types/morgan": "^1.9.9", "cors": "^2.8.5", + "dotenv": "^16.3.1", "express": "^4.18.2", + "helmet": "^7.1.0", "morgan": "^1.10.0", "nodemon": "^3.0.1", "pg": "^8.11.3", @@ -2260,6 +2262,17 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2774,6 +2787,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", diff --git a/API-Project/package.json b/API-Project/package.json index da89284..fcf171a 100644 --- a/API-Project/package.json +++ b/API-Project/package.json @@ -14,8 +14,10 @@ "dependencies": { "@types/express": "^4.17.21", "@types/morgan": "^1.9.9", + "dotenv": "^16.3.1", "cors": "^2.8.5", "express": "^4.18.2", + "helmet": "^7.1.0", "morgan": "^1.10.0", "nodemon": "^3.0.1", "pg": "^8.11.3", diff --git a/API-Project/src/controllers/ingredients.controller.ts b/API-Project/src/controllers/ingredients.controller.ts index c08ab41..8945ea5 100644 --- a/API-Project/src/controllers/ingredients.controller.ts +++ b/API-Project/src/controllers/ingredients.controller.ts @@ -1,7 +1,5 @@ -import { Request, Response, NextFunction } from "express"; import { Router } from "express"; import { Exceptions } from "../utils/exception"; -import { IIngredient, Ingredient } from "../types/ingredients"; import { IngredientsGateway } from "../gateways/ingredients.gateway"; const IngredientsController = Router() @@ -67,7 +65,6 @@ IngredientsController.get('/:id', async (req, res) => { res.status(404).send('not found') } else { - const ingredient_ingredient = ingredient as Ingredient res.status(200).json(ingredient) } } catch (error) { diff --git a/API-Project/src/controllers/recipes.controller.ts b/API-Project/src/controllers/recipes.controller.ts index d636690..74639a8 100644 --- a/API-Project/src/controllers/recipes.controller.ts +++ b/API-Project/src/controllers/recipes.controller.ts @@ -1,5 +1,4 @@ import { Router } from "express"; -import { Recipe } from "../types/recipes"; import { Exceptions } from "../utils/exception"; import { RecipeGateway } from "../gateways/recipe.gateway"; @@ -33,7 +32,6 @@ RecipesController.get('/:id', async (req, res) => { res.status(404).send('not found') } else { - const ingredient_ingredient = recipe as Recipe res.status(200).json(recipe) } } catch (error) { diff --git a/API-Project/src/controllers/steps.controller.ts b/API-Project/src/controllers/steps.controller.ts index e2bc432..347896a 100644 --- a/API-Project/src/controllers/steps.controller.ts +++ b/API-Project/src/controllers/steps.controller.ts @@ -1,5 +1,4 @@ import { Router } from "express"; -import { Recipe } from "../types/recipes"; import { Exceptions } from "../utils/exception"; import { StepsGateway } from "../gateways/steps.gateway"; @@ -22,8 +21,6 @@ StepsController.get('/:id', async (req, res) => { res.status(404).send('not found') } else { - const steps_steps = steps as string[] - res.status(200).json(steps) } } catch (error) { diff --git a/API-Project/src/database/connection.ts b/API-Project/src/database/connection.ts index 55c586b..7c702d4 100644 --- a/API-Project/src/database/connection.ts +++ b/API-Project/src/database/connection.ts @@ -1,34 +1,20 @@ -import { Client } from "pg" - -const Pool = require('pg').Pool - -export const pool = new Pool({ - user: 'rgregnault', - host: 'localhost', - database: 'leftovers', - password: 'motdepasse', - port: 5432, -}) +import { Pool, PoolClient } from "pg" export class Connection { - public client:Client - clientIsConnected:boolean = false + private pool:Pool constructor() { - this.client = new Client({ - user: 'leftovers_appuser', - host: 'postgresql-leftovers.alwaysdata.net', - database: 'leftovers_recipedb', - password: 'UsrPsswd', - port: 5432, + this.pool = new Pool({ + user: process.env.DB_USERNAME, + host: process.env.DB_DBHOST, + database: process.env.DB_DBNAME, + password: process.env.DB_USERPASSWORD, + port: Number(process.env.DB_PORT), }) } - public async connect() { - if (!this.clientIsConnected) { - await this.client.connect() - this.clientIsConnected = true - } + public async getPoolClient() : Promise { + return await this.pool.connect() } } \ No newline at end of file diff --git a/API-Project/src/gateways/ingredients.gateway.ts b/API-Project/src/gateways/ingredients.gateway.ts index 4856d4e..76c6ba6 100644 --- a/API-Project/src/gateways/ingredients.gateway.ts +++ b/API-Project/src/gateways/ingredients.gateway.ts @@ -9,9 +9,11 @@ export class IngredientsGateway { } async getAll() : Promise { - this.connection.connect() + const client = await this.connection.getPoolClient() - const res = await this.connection.client.query('SELECT * FROM Ingredients ORDER BY id') + const res = await client.query('SELECT * FROM Ingredients ORDER BY id') + + client.release() let ingredients:Ingredient[] = [] @@ -24,14 +26,16 @@ export class IngredientsGateway { } async findOneById(id: number) : Promise { - this.connection.connect() + const client = await this.connection.getPoolClient() const query = { text: 'SELECT * FROM Ingredients WHERE id =$1', values: [id], } - const res = await this.connection.client.query(query) + const res = await client.query(query) + + client.release() if (res.rowCount != 1) { return null @@ -42,16 +46,17 @@ export class IngredientsGateway { return ingredient } - async findIngredientsForRecipe(id: Number): Promise { - this.connection.connect(); + async findIngredientsForRecipe(id: number): Promise { + const client = await this.connection.getPoolClient() const query = { text: 'SELECT i.name, i.id FROM Ingredients i, Composed c WHERE c.idRecipe =$1 AND i.id = c.idIngredient', values: [id], }; - const res = await this.connection.client.query(query); - console.log(res) + const res = await client.query(query); + + client.release() if (res.rowCount === 0) { return null; @@ -61,21 +66,21 @@ export class IngredientsGateway { name: row.name, id: Number(row.id), // Conversion de l'identifiant en nombre })); - console.log(ingredients); return ingredients as Ingredient[]; } async getByLetter(letter: string): Promise { - this.connection.connect(); + const client = await this.connection.getPoolClient() const query = { text: 'SELECT * FROM Ingredients i WHERE LOWER(SUBSTRING(i.name, 1, 1)) = $1', values: [letter.toLowerCase()], }; - const res = await this.connection.client.query(query); - console.log(res) + const res = await client.query(query); + + client.release() if (res.rowCount === 0) { return null; @@ -92,15 +97,16 @@ export class IngredientsGateway { } async filter(prompt: string): Promise { - this.connection.connect(); + const client = await this.connection.getPoolClient() const query = { text: 'SELECT * FROM Ingredients WHERE LOWER(name) LIKE $1', values: [`%${prompt.toLowerCase()}%`], }; - const res = await this.connection.client.query(query); - console.log(res) + const res = await client.query(query); + + client.release() if (res.rowCount === 0) { return null; diff --git a/API-Project/src/gateways/recipe.gateway.ts b/API-Project/src/gateways/recipe.gateway.ts index ee8a4e5..6e99f61 100644 --- a/API-Project/src/gateways/recipe.gateway.ts +++ b/API-Project/src/gateways/recipe.gateway.ts @@ -1,7 +1,5 @@ -import { Ingredient } from "../types/ingredients"; import { Recipe } from "../types/recipes" import { Connection } from "../database/connection" -import { Router } from "express"; import { StepsGateway } from "./steps.gateway"; import { IngredientsGateway } from "./ingredients.gateway"; @@ -17,11 +15,12 @@ export class RecipeGateway { } async getAll() : Promise { - this.connection.connect() - const res = await this.connection.client.query('SELECT * FROM Recipes ORDER BY id'); + const client = await this.connection.getPoolClient() + const res = await client.query('SELECT * FROM Recipes ORDER BY id'); + client.release() const steps: string[] = []; - let recipes:Recipe[] = [] + let recipes: Recipe[] = [] for (let key in res.rows) { const steps = await this.steps_gw.getForRecipes(Number(key)); @@ -30,27 +29,27 @@ export class RecipeGateway { recipes.push(recipe); } - console.log(recipes); - return recipes } - async getById(id: Number) : Promise{ - this.connection.connect() + async getById(id: number) : Promise{ + const client = await this.connection.getPoolClient() const query = { text: 'SELECT * FROM Recipes WHERE id =$1', values: [id], } - const res = await this.connection.client.query(query) + const res = await client.query(query) + + client.release() if (res.rowCount != 1) { return null } - const steps = await this.steps_gw.getForRecipes(id) - const ingredients = await this.ingredient_gw.findIngredientsForRecipe(id) + const steps = await this.steps_gw.getForRecipes(Number(id)) + const ingredients = await this.ingredient_gw.findIngredientsForRecipe(Number(id)) const recipe = new Recipe(Number(res.rows[0].id), res.rows[0].name, res.rows[0].description, diff --git a/API-Project/src/gateways/steps.gateway.ts b/API-Project/src/gateways/steps.gateway.ts index 0371b05..36a065b 100644 --- a/API-Project/src/gateways/steps.gateway.ts +++ b/API-Project/src/gateways/steps.gateway.ts @@ -1,4 +1,3 @@ -import { Recipe } from "../types/recipes" import { Connection } from "../database/connection" export class StepsGateway { @@ -9,15 +8,17 @@ export class StepsGateway { } - async getForRecipes(id: Number): Promise { - this.connection.connect(); + async getForRecipes(id: number): Promise { + const client = await this.connection.getPoolClient() const query = { text: 'SELECT action FROM Steps WHERE idRecipe = $1 ORDER BY numstep', values: [id], }; - const res = await this.connection.client.query(query); + const res = await client.query(query); + + client.release() const steps = res.rows.map(row => row.action); diff --git a/API-Project/src/server.ts b/API-Project/src/server.ts index 08e5f35..69ca29e 100644 --- a/API-Project/src/server.ts +++ b/API-Project/src/server.ts @@ -1,10 +1,15 @@ +require('dotenv').config(); import express from "express"; import cors from "cors"; import { IngredientsController } from "./controllers/ingredients.controller"; import { RecipesController } from "./controllers/recipes.controller"; import { StepsController } from "./controllers/steps.controller"; -const app = express(); + + +let helmet = require("helmet"); +let app = express(); +app.use(helmet.hidePoweredBy()); // Configuration du middleware CORS pour autoriser toutes les origines app.use(cors({ diff --git a/API-Project/src/services b/API-Project/src/services deleted file mode 100644 index e69de29..0000000 diff --git a/API-Project/src/utils/exception.ts b/API-Project/src/utils/exception.ts index 8f9b1f3..05627f5 100644 --- a/API-Project/src/utils/exception.ts +++ b/API-Project/src/utils/exception.ts @@ -14,11 +14,12 @@ class Exception implements ApiException { constructor(readonly error: any, readonly status: number) {} } -/** - * Création d'une 404 - */ -export module Exceptions { + +export namespace Exceptions { + /** + * Création d'une 404 + */ export class NotFoundException extends Exception { constructor(error: any) { super(error, 404)