diff --git a/.env.example b/.env.example index 45276316..e7e4e732 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,8 @@ # ------------------------------------------------ # API - -API_DOMAIN=localhost API_PORT=3200 -API_PORT_PUBLIC=3200 -API_SCHEME=http +POSTGREST_URI=http://postgrest:3000 # ------------------------------------------------ # Web @@ -23,12 +20,6 @@ APP_SCHEME=http CDTN_API_URL=https://cdtn-api.fabrique.social.gouv.fr # CDTN_API_URL=http://localhost:3300 -# ------------------------------------------------ -# Development & test variables - -DEV_DB_PORT=5433 -DEV_POSTGREST_PORT=3001 - ################################################## # PostgreSQL diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml new file mode 100644 index 00000000..6976dfb8 --- /dev/null +++ b/.github/workflows/review.yml @@ -0,0 +1,32 @@ +name: Review + +on: + push: + branches-ignore: + - master + tags-ignore: + - v* + +concurrency: + cancel-in-progress: true + group: review-${{ github.ref }} + +jobs: + register: + name: Register images + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - images: "cdtn-contrib-api" + path: "./packages/api" + - images: "cdtn-contrib-app" + path: "./packages/app" + steps: + - name: Register docker images + uses: SocialGouv/actions/autodevops-build-register@v1 + with: + imagePackage: ${{ matrix.images }} + token: ${{ secrets.GITHUB_TOKEN }} + dockerfile: "${{ matrix.path }}/Dockerfile" diff --git a/.github/workflows/sync-gitlab.yml b/.github/workflows/sync-gitlab.yml new file mode 100644 index 00000000..ba2ed8f9 --- /dev/null +++ b/.github/workflows/sync-gitlab.yml @@ -0,0 +1,15 @@ +name: Gitlab mirroring + +on: + - push + - delete + +jobs: + mirror_gitlab: + name: 🪞 Gitlab + runs-on: ubuntu-latest + steps: + - uses: SocialGouv/actions/mirror-gitlab@master + with: + project: SocialGouv/cdtn/code-du-travail-backoffice + token: ${{ secrets.SOCIALGROOVYBOT_GITLAB_TOKEN }} diff --git a/.kube-workflow/dev/templates/api.configmap.yaml b/.kube-workflow/dev/templates/api.configmap.yaml new file mode 100644 index 00000000..cd4308fa --- /dev/null +++ b/.kube-workflow/dev/templates/api.configmap.yaml @@ -0,0 +1,8 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: api +data: + API_PORT: 80 + NODE_ENV: "production" + POSTGREST_URI: "" diff --git a/.kube-workflow/dev/templates/api.sealed-secret.yaml b/.kube-workflow/dev/templates/api.sealed-secret.yaml new file mode 100644 index 00000000..f3cdfdcc --- /dev/null +++ b/.kube-workflow/dev/templates/api.sealed-secret.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: "bitnami.com/v1alpha1" +kind: "SealedSecret" +metadata: + annotations: &a1 + sealedsecrets.bitnami.com/cluster-wide: "true" + name: api + namespace: null +spec: + encryptedData: + DB_URI: HERE + template: + metadata: + annotations: *a1 + name: api + type: "Opaque" diff --git a/.kube-workflow/dev/templates/postgrest.configmap.yaml b/.kube-workflow/dev/templates/postgrest.configmap.yaml new file mode 100644 index 00000000..449cda6c --- /dev/null +++ b/.kube-workflow/dev/templates/postgrest.configmap.yaml @@ -0,0 +1,7 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: postgrest +data: + PGRST_DB_ANON_ROLE: "anonymous" + PGRST_DB_SCHEMA: "api" diff --git a/.kube-workflow/dev/templates/postgrest.sealed-secret.yaml b/.kube-workflow/dev/templates/postgrest.sealed-secret.yaml new file mode 100644 index 00000000..f97f2012 --- /dev/null +++ b/.kube-workflow/dev/templates/postgrest.sealed-secret.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: "bitnami.com/v1alpha1" +kind: "SealedSecret" +metadata: + annotations: &a1 + sealedsecrets.bitnami.com/cluster-wide: "true" + name: postgrest + namespace: null +spec: + encryptedData: + # It must be at least 32 characters long: + # https://github.com/PostgREST/postgrest/issues/977#issuecomment-329917367 + PGRST_JWT_SECRET: a_test_only_postgrest_jwt_secret + template: + metadata: + annotations: *a1 + name: postgrest + type: "Opaque" diff --git a/.kube-workflow/dev/templates/www.configmap.yaml b/.kube-workflow/dev/templates/www.configmap.yaml new file mode 100644 index 00000000..6726223c --- /dev/null +++ b/.kube-workflow/dev/templates/www.configmap.yaml @@ -0,0 +1,10 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: www +data: + APP_PORT: 80 + CDTN_API_URL: "https://cdtn-api.fabrique.social.gouv.fr" + API_URI_DOCKER: "" + API_URI: "" + NODE_ENV: "production" diff --git a/.kube-workflow/dev/templates/www.sealed-secret.yaml b/.kube-workflow/dev/templates/www.sealed-secret.yaml new file mode 100644 index 00000000..ed70e690 --- /dev/null +++ b/.kube-workflow/dev/templates/www.sealed-secret.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: "bitnami.com/v1alpha1" +kind: "SealedSecret" +metadata: + annotations: &a1 + sealedsecrets.bitnami.com/cluster-wide: "true" + name: www + namespace: null +spec: + encryptedData: + DB_URI: HERE + template: + metadata: + annotations: *a1 + name: www + type: "Opaque" diff --git a/.kube-workflow/dev/values.yaml b/.kube-workflow/dev/values.yaml new file mode 100644 index 00000000..e69de29b diff --git a/.kube-workflow/prod/templates/api.configmap.yaml b/.kube-workflow/prod/templates/api.configmap.yaml new file mode 100644 index 00000000..cd4308fa --- /dev/null +++ b/.kube-workflow/prod/templates/api.configmap.yaml @@ -0,0 +1,8 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: api +data: + API_PORT: 80 + NODE_ENV: "production" + POSTGREST_URI: "" diff --git a/.kube-workflow/prod/templates/api.sealed-secret.yaml b/.kube-workflow/prod/templates/api.sealed-secret.yaml new file mode 100644 index 00000000..f3cdfdcc --- /dev/null +++ b/.kube-workflow/prod/templates/api.sealed-secret.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: "bitnami.com/v1alpha1" +kind: "SealedSecret" +metadata: + annotations: &a1 + sealedsecrets.bitnami.com/cluster-wide: "true" + name: api + namespace: null +spec: + encryptedData: + DB_URI: HERE + template: + metadata: + annotations: *a1 + name: api + type: "Opaque" diff --git a/.kube-workflow/prod/templates/www.configmap.yaml b/.kube-workflow/prod/templates/www.configmap.yaml new file mode 100644 index 00000000..6726223c --- /dev/null +++ b/.kube-workflow/prod/templates/www.configmap.yaml @@ -0,0 +1,10 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: www +data: + APP_PORT: 80 + CDTN_API_URL: "https://cdtn-api.fabrique.social.gouv.fr" + API_URI_DOCKER: "" + API_URI: "" + NODE_ENV: "production" diff --git a/.kube-workflow/prod/templates/www.sealed-secret.yaml b/.kube-workflow/prod/templates/www.sealed-secret.yaml new file mode 100644 index 00000000..ed70e690 --- /dev/null +++ b/.kube-workflow/prod/templates/www.sealed-secret.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: "bitnami.com/v1alpha1" +kind: "SealedSecret" +metadata: + annotations: &a1 + sealedsecrets.bitnami.com/cluster-wide: "true" + name: www + namespace: null +spec: + encryptedData: + DB_URI: HERE + template: + metadata: + annotations: *a1 + name: www + type: "Opaque" diff --git a/.kube-workflow/prod/values.yaml b/.kube-workflow/prod/values.yaml new file mode 100644 index 00000000..e69de29b diff --git a/.kube-workflow/values.yaml b/.kube-workflow/values.yaml new file mode 100644 index 00000000..57d979bc --- /dev/null +++ b/.kube-workflow/values.yaml @@ -0,0 +1,33 @@ +app-api: + probesPath: /healthz + imagePackage: cdtn-contrib-api + envFrom: + - configMapRef: + name: api + - secretRef: + name: api + resources: + limits: + cpu: "1000m" + memory: "560Mi" + requests: + cpu: "5m" + memory: "128Mi" + +app-www: + probesPath: / + imagePackage: cdtn-contrib-app + envFrom: + - configMapRef: + name: www + - secretRef: + name: www + +postgrest: + imagePackage: postgrest/postgrest:v6.0.2 + containerPort: 3000 + envFrom: + - configMapRef: + name: postgrest + - secretRef: + name: postgrest diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index bafda1c7..00000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,33 +0,0 @@ -version: "3" - -services: - db: - ports: - - ${DEV_DB_PORT}:5432 - - postgrest: - ports: - - ${DEV_POSTGREST_PORT}:3000 - - cdtn_api: - image: igabriele/cdtn-api:latest - restart: always - environment: - PORT: 3300 - REDIS_URL: redis://redis:6379 - ports: - - 3300:3300 - depends_on: - - redis - - redis: - image: redis:6 - restart: always - ports: - - 6379:6379 - volumes: - - redis-data:/data - -volumes: - postgre-data: - redis-data: diff --git a/docker-compose.yml b/docker-compose.yml index bad459b8..b013eb90 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,12 +5,10 @@ services: container_name: cdtn_backoffice_db image: postgres:12-alpine restart: always - environment: - # The JWT secret is used whithin the api.login() function: - PGRST_JWT_SECRET: ${PGRST_JWT_SECRET} - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + env_file: + - .env + ports: + - 3300:5432 volumes: - postgre-data:/var/lib/postgresql/data - ./backups:/backups @@ -21,11 +19,10 @@ services: image: postgrest/postgrest:v6.0.2 # image: hughjfchen/hughjfchen:postgrest-aarch64-6.0.2 # for arm64 restart: always + env_file: + - .env environment: - PGRST_DB_ANON_ROLE: ${PGRST_DB_ANON_ROLE} - PGRST_DB_SCHEMA: ${PGRST_DB_SCHEMA} PGRST_DB_URI: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - PGRST_JWT_SECRET: ${PGRST_JWT_SECRET} depends_on: - db @@ -33,14 +30,13 @@ services: container_name: cdtn_backoffice_api build: context: ./packages/api - args: - API_PORT: ${API_PORT} - DB_URI: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - NODE_ENV: ${NODE_ENV} - POSTGREST_URI: http://postgrest:3000 restart: always ports: - ${API_PORT}:${API_PORT} + env_file: + - .env + environment: + DB_URI: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} depends_on: - postgrest @@ -48,18 +44,15 @@ services: container_name: cdtn_backoffice_app build: context: ./packages/app - args: - API_DOMAIN: ${API_DOMAIN} - API_PORT_PUBLIC: ${API_PORT_PUBLIC} - API_SCHEME: ${API_SCHEME} - API_URI_DOCKER: http://api:${API_PORT} - CDTN_API_URL: ${CDTN_API_URL} - DB_URI: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - NODE_ENV: ${NODE_ENV} - APP_PORT: ${APP_PORT} restart: always ports: - ${APP_PORT}:${APP_PORT} + env_file: + - .env + environment: + API_URI_DOCKER: http://api:${API_PORT} + API_URI: http://localhost:${API_PORT} + DB_URI: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} depends_on: - api diff --git a/knexfile.js b/knexfile.js index c8f9e11e..536b27f3 100644 --- a/knexfile.js +++ b/knexfile.js @@ -1,11 +1,4 @@ -let { DB_URI } = process.env; -if (DB_URI === undefined) { - const dotenv = require("dotenv"); - dotenv.config(); - const { DEV_DB_PORT, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_USER } = process.env; - - DB_URI = `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${DEV_DB_PORT}/${POSTGRES_DB}`; -} +const { DB_URI } = process.env; module.exports = { development: { diff --git a/package.json b/package.json index e07fbb4e..b90c85f4 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "db:snapshot:update": "node -r dotenv/config ./scripts/db/updateSnapshot.js", "dev": "yarn dev:docker && yarn dev:packages", "dev:standalone": "yarn dev:docker:standalone && yarn dev:packages", - "dev:docker": "docker-compose down && docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d postgrest", - "dev:docker:standalone": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d cdtn_api postgrest", + "dev:docker": "docker-compose down && docker-compose -f docker-compose.yml up -d postgrest", + "dev:docker:standalone": "docker-compose -f docker-compose.yml up -d cdtn_api postgrest", "dev:packages": "lerna run --parallel --scope \"@socialgouv/code-du-travail-backoffice__api\" --scope \"@socialgouv/code-du-travail-backoffice__app\" dev", "preinstall": "node ./scripts/ci/removeWorkspaces.js && node ./scripts/ci/deleteYarnLock.js", "setup": "yarn setup:env && node -r dotenv/config ./scripts/dev/setup.js", diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index 870121a4..291014d6 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -1,16 +1,7 @@ # API Image +ARG NODE_VERSION=16.13.1-alpine -FROM node:16.13.1-alpine - -ARG API_PORT -ARG DB_URI -ARG NODE_ENV -ARG POSTGREST_URI - -ENV API_PORT=$API_PORT -ENV DB_URI=$DB_URI -ENV NODE_ENV=$NODE_ENV -ENV POSTGREST_URI=$POSTGREST_URI +FROM node:$NODE_VERSION WORKDIR /app diff --git a/packages/api/src/hooks/logAction.js b/packages/api/src/hooks/logAction.js index 63464c6f..c2cae222 100644 --- a/packages/api/src/hooks/logAction.js +++ b/packages/api/src/hooks/logAction.js @@ -7,11 +7,7 @@ const answerWithError = require("../helpers/answerWithError"); const isJsonValid = require("../helpers/isJsonValid"); const isUuidValid = require("../helpers/isUuidValid"); -const { DEV_DB_PORT, NODE_ENV, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_USER } = process.env; -let { DB_URI } = process.env; -if (NODE_ENV !== "production") { - DB_URI = `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${DEV_DB_PORT}/${POSTGRES_DB}`; -} +const { DB_URI } = process.env; const knexClient = knex({ client: "pg", diff --git a/packages/app/Dockerfile b/packages/app/Dockerfile index d65910c5..ffaa3e68 100644 --- a/packages/app/Dockerfile +++ b/packages/app/Dockerfile @@ -1,28 +1,11 @@ -FROM node:16.13.1-alpine +ARG NODE_VERSION=16.13.1-alpine -# Add build-arg from github actions -ARG API_DOMAIN -ARG API_PORT_PUBLIC -ARG API_SCHEME -ARG API_URI_DOCKER -ARG CDTN_API_URL -ARG DB_URI -ARG NODE_ENV -ARG APP_PORT - -ENV API_DOMAIN=$API_DOMAIN -ENV API_PORT_PUBLIC=$API_PORT_PUBLIC -ENV API_SCHEME=$API_SCHEME -ENV CDTN_API_URL=$CDTN_API_URL -ENV DB_URI=$DB_URI -ENV NODE_ENV=$NODE_ENV -ENV APP_PORT=$APP_PORT +FROM node:$NODE_VERSION AS dist WORKDIR /app COPY . . RUN yarn --production --pure-lockfile -RUN yarn build -ENTRYPOINT ["yarn", "start"] +ENTRYPOINT ["yarn", "start:docker"] diff --git a/packages/app/next.config.js b/packages/app/next.config.js index cfd99074..a9517129 100644 --- a/packages/app/next.config.js +++ b/packages/app/next.config.js @@ -1,6 +1,4 @@ -const { API_DOMAIN, API_PORT_PUBLIC, API_SCHEME, API_URI_DOCKER, CDTN_API_URL } = process.env; - -const API_URI = `${API_SCHEME}://${API_DOMAIN}:${API_PORT_PUBLIC}`; +const { API_URI, API_URI_DOCKER, CDTN_API_URL } = process.env; module.exports = { // https://nextjs.org/docs#build-time-configuration diff --git a/packages/app/package.json b/packages/app/package.json index 6574fbfd..a0443aac 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -9,7 +9,8 @@ "build:docker": "node --max-old-space-size=1024 ./node_modules/.bin/next build", "clean": "rimraf ./.next", "dev": "nodemon --exec \"node --inspect -r dotenv/config ./server dotenv_config_path=../../.env\" --watch ./server", - "start": "node ./server" + "start": "node ./server", + "start:docker": "yarn build:docker && yarn start" }, "dependencies": { "@emotion/babel-plugin": "11.1.2", diff --git a/packages/app/server/middlewares/withPostgres.js b/packages/app/server/middlewares/withPostgres.js index b0322b12..24dde9ce 100644 --- a/packages/app/server/middlewares/withPostgres.js +++ b/packages/app/server/middlewares/withPostgres.js @@ -2,11 +2,7 @@ const { Client: PostgresClient } = require("pg"); const reportError = require("../libs/reportError"); -const { DEV_DB_PORT, NODE_ENV, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_USER } = process.env; -let { DB_URI } = process.env; -if (NODE_ENV !== "production") { - DB_URI = `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${DEV_DB_PORT}/${POSTGRES_DB}`; -} +const { DB_URI } = process.env; let postgresClient; let postgresClientIsConnected = false; diff --git a/scripts/db/backup.js b/scripts/db/backup.js index 9d9a62a8..e2a271a7 100644 --- a/scripts/db/backup.js +++ b/scripts/db/backup.js @@ -35,9 +35,7 @@ try { if (isProduction) { run(`docker-compose up -d ${DOCKER_COMPOSE_SERVICE_NAME}`); } else { - run( - `docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`, - ); + run(`docker-compose -f docker-compose.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`); } // https://stackoverflow.com/a/63011266/2736233 run( diff --git a/scripts/db/restore.js b/scripts/db/restore.js index b92f4ccf..39da8e2b 100644 --- a/scripts/db/restore.js +++ b/scripts/db/restore.js @@ -44,9 +44,7 @@ try { if (isProduction) { run(`docker-compose up -d ${DOCKER_COMPOSE_SERVICE_NAME}`); } else { - run( - `docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`, - ); + run(`docker-compose -f docker-compose.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`); } // https://stackoverflow.com/a/63011266/2736233 run( diff --git a/scripts/db/restoreSnapshot.js b/scripts/db/restoreSnapshot.js index 309c9581..ea76a5d9 100644 --- a/scripts/db/restoreSnapshot.js +++ b/scripts/db/restoreSnapshot.js @@ -30,9 +30,7 @@ try { const backupPath = path.join(__dirname, "../../db/snapshot.sql"); run(`docker-compose down -v`); - run( - `docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`, - ); + run(`docker-compose -f docker-compose.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`); // https://stackoverflow.com/a/63011266/2736233 run( `timeout 90s bash -c "until docker exec ${DOCKER_CONTAINER_NAME} pg_isready ; do sleep 5 ; done"`, diff --git a/scripts/db/updateSnapshot.js b/scripts/db/updateSnapshot.js index de46a53e..76d9a1ce 100644 --- a/scripts/db/updateSnapshot.js +++ b/scripts/db/updateSnapshot.js @@ -4,8 +4,8 @@ const knex = require("knex"); const path = require("path"); const shell = require("shelljs"); -const { DEV_DB_PORT, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_USER } = process.env; -const DATABASE_URL = `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${DEV_DB_PORT}/${POSTGRES_DB}`; +const { POSTGRES_PORT, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_USER } = process.env; +const DATABASE_URL = `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${POSTGRES_PORT}/${POSTGRES_DB}`; const DOCKER_COMPOSE_SERVICE_NAME = "db"; const DOCKER_CONTAINER_NAME = "cdtn_backoffice_db"; @@ -30,9 +30,7 @@ function run(command) { const backupPath = path.join(__dirname, `../../backups/${process.argv[2]}.sql`); run(`docker-compose down -v`); - run( - `docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`, - ); + run(`docker-compose -f docker-compose.yml up -d ${DOCKER_COMPOSE_SERVICE_NAME}`); // https://stackoverflow.com/a/63011266/2736233 run( `timeout 90s bash -c "until docker exec ${DOCKER_CONTAINER_NAME} pg_isready ; do sleep 5 ; done"`, diff --git a/scripts/dev/setup.js b/scripts/dev/setup.js index a33ce3bf..115560cb 100644 --- a/scripts/dev/setup.js +++ b/scripts/dev/setup.js @@ -34,7 +34,7 @@ function run(command) { fs.appendFileSync(".env", "\nNODE_ENV=development\n"); run(`lerna link`); run(`docker-compose down --remove-orphans -v`); - run(`docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d db`); + run(`docker-compose -f docker-compose.yml up -d db`); shell.echo(`Waiting for db to be up and ready…`.blue); // https://stackoverflow.com/a/63011266/2736233 run(