From 8f44b25d44dab329b77336311700847624b78c77 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 5 Dec 2024 00:46:55 -0300 Subject: [PATCH] feat: We now have daily backups, and somewhat sane environment variable configuration now. No more need to hack around the docker-compose file. --- .gitignore | 3 +- README.md | 26 +++++--------- build/docker-compose.local.yml | 16 ++++----- build/docker-compose.public.yml | 60 ++++++++++++++++++++++++++------- build/mongodb/mongo-init.js | 8 ++--- docs/comments.md | 4 ++- 6 files changed, 73 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index bb1de54..bd9765e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ artifacts/ *.sln.ide build/docker-compose.yml build/mongodb/mongodata/ -src/Guestbooky/.vs/ \ No newline at end of file +src/Guestbooky/.vs/ +/build/.env diff --git a/README.md b/README.md index 0675eb5..25b0bdc 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ - [🚀 Deployment ](#-deployment-) - [Backend](#backend-1) - [Admin Panel](#admin-panel-1) + - [Backup](#backup) - [⛏️ Built Using ](#️-built-using-) - [✍️ Authors ](#️-authors-) @@ -65,20 +66,7 @@ For running it locally: - A Cloudflare turnstile secret key for the captcha - Not forgetting to set up environment variables -You will be able to see in `build/docker-compose.public.yml` that the application makes heavy usage of them. -``` - - ASPNETCORE_ENVIRONMENT=Production - - CORS_ORIGINS=https://guestbook.example.com,http://localhost:5008,http://localhost:8080 - - ACCESS_USERNAME=user - - ACCESS_PASSWORD=pass - - ACCESS_TOKENKEY=pleaseinsertafairlylargetokenkeyherewillyou - - ACCESS_ISSUER=https://guestbook.example.com/api - - ACCESS_AUDIENCE=https://guestbook.example.com - - CLOUDFLARE_SECRET=0x000000000000000000000000000000000 - - MONGODB_CONNECTIONSTRING=mongodb://mongouser:mongopass@mongo:27017/Guestbooky - - MONGODB_DATABASENAME=Guestbooky - - LOG_LEVEL=Debug -``` +There's a `.env.template` file with all environment variables used throughout the compose file. > [!IMPORTANT] You will need to set them up either by hand or by using your IDE's capabilities. On Visual Studio, that can be done via the Debug Properties of Guestbooky.API. @@ -89,10 +77,10 @@ You will be able to see in `build/docker-compose.public.yml` that the applicatio |**CLOUDFLARE_SECRET**|The turnstile secret, used in the server portion of the captcha check.| |**MONGODB_\***|Related to the connection to MongoDB. Yeah.| |**LOG_\***|Logging.| - +|**GUESTBOOKY_**|Related to accessing the main document collection, which uses its own user.| > [!TIP] -> For local usage of the backend, you can use `docker-compose.local.yml` and edit the fields you need. +> For local usage of the backend, you can use `docker-compose.local.yml` which provides just what you need to run the backend yourself. ### Admin panel @@ -112,12 +100,16 @@ For development, it should be enough to run `vite` in Guestbooky-admin's `src` f ### Backend -Use `docker-compose.public.yml` as a basis. it should create the image for you and start running. +Use `docker-compose.public.yml` as a basis, and remember to have a `.env` file ready. it should create the image for you and start running. ### Admin Panel In order to create a live version, adjust the **API_URL** path in `Guestbooky-admin/src/environment/constants.js`, and execute `vite build`. The application will be prepared and sent to `src/Guestbooky-admin/dist`. Send to your hosting solution and you should be good. +### Backup + +Mongodump is run as a cron job daily. The behavior is customizable in `docker-compose.yml` + ## ⛏️ Built Using - [MongoDB](https://www.mongodb.com/) - Database diff --git a/build/docker-compose.local.yml b/build/docker-compose.local.yml index 63520fd..c5545d6 100644 --- a/build/docker-compose.local.yml +++ b/build/docker-compose.local.yml @@ -5,8 +5,8 @@ services: container_name: mongo restart: always environment: - MONGO_INITDB_ROOT_USERNAME: root - MONGO_INITDB_ROOT_PASSWORD: mongo + MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD} volumes: - ./mongodb/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro - ./mongodb/mongod.conf:/etc/mongod.conf:ro @@ -19,15 +19,15 @@ services: image: mongo-express container_name: mongo-express restart: always - ports: - - 8082:8081 environment: - ME_CONFIG_MONGODB_ADMINUSERNAME: root - ME_CONFIG_MONGODB_ADMINPASSWORD: mongo - ME_CONFIG_MONGODB_URL: mongodb://root:mongo@mongo:27017/ - ME_CONFIG_BASICAUTH: false + ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_INITDB_ROOT_USERNAME} + ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_INITDB_ROOT_PASSWORD} + ME_CONFIG_MONGODB_URL: mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017/ + ME_CONFIG_BASICAUTH: "true" depends_on: - mongo + ports: + - "8082:8081" volumes: mongodata: \ No newline at end of file diff --git a/build/docker-compose.public.yml b/build/docker-compose.public.yml index b2a1c8c..e189d3b 100644 --- a/build/docker-compose.public.yml +++ b/build/docker-compose.public.yml @@ -6,17 +6,17 @@ services: ports: - "8080:8080" environment: - - ASPNETCORE_ENVIRONMENT=Production - - CORS_ORIGINS=https://guestbooky.example.com - - ACCESS_USERNAME=user - - ACCESS_PASSWORD=pass - - ACCESS_TOKENKEY=youbetterbesureyouareusingatokenkey - - ACCESS_ISSUER=https://guestbooky.example.com/api - - ACCESS_AUDIENCE=https://guestbooky.example.com - - CLOUDFLARE_SECRET=0x000000000000000000000000000000000 - - MONGODB_CONNECTIONSTRING=mongodb://mongouser:mongopass@mongo:27017/Guestbooky - - MONGODB_DATABASENAME=Guestbooky - - LOG_LEVEL=Debug + ASPNETCORE_ENVIRONMENT: Production + CORS_ORIGINS: ${CORS_ORIGINS} + ACCESS_USERNAME: ${ACCESS_USERNAME} + ACCESS_PASSWORD: ${ACCESS_PASSWORD} + ACCESS_TOKENKEY: ${ACCESS_TOKENKEY} + ACCESS_ISSUER: ${ACCESS_ISSUER} + ACCESS_AUDIENCE: ${ACCESS_AUDIENCE} + CLOUDFLARE_SECRET: ${CLOUDFLARE_SECRET} + MONGODB_CONNECTIONSTRING: mongodb://${GUESTBOOKY_USER}:${GUESTBOOKY_PASSWORD}@mongo:27017/${GUESTBOOKY_DB_NAME} + MONGODB_DATABASENAME: ${GUESTBOOKY_DB_NAME} + LOG_LEVEL: Debug depends_on: - mongo restart: unless-stopped @@ -28,8 +28,11 @@ services: container_name: mongo restart: always environment: - MONGO_INITDB_ROOT_USERNAME: root - MONGO_INITDB_ROOT_PASSWORD: mongo + MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD} + GUESTBOOKY_DB_NAME: ${GUESTBOOKY_DB_NAME} + GUESTBOOKY_USER: ${GUESTBOOKY_USER} + GUESTBOOKY_PASSWORD: ${GUESTBOOKY_PASSWORD} volumes: - ./mongodb/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro - ./mongodb/mongod.conf:/etc/mongod.conf:ro @@ -38,6 +41,37 @@ services: networks: - guestbooky + cron: + image: mcuadros/ofelia:latest + container_name: ofelia-cron + command: daemon --docker + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./docker-compose.yml:/etc/docker-compose.yml:ro + labels: + ofelia.job-run.backup: "0 0 * * * docker-compose -f /etc/docker-compose.yml run --rm backup-job" + networks: + - guestbooky + env_file: + - .env + + backup-job: + container_name: mongo-backup-job + image: mongo + command: > + bash -c "mongodump + --host mongo + --db ${GUESTBOOKY_DB_NAME} + --username ${GUESTBOOKY_USER} + --password ${GUESTBOOKY_USER} + --authenticationDatabase ${GUESTBOOKY_DB_NAME} + --out /backups/guestbooky_$(date +\%Y-\%m-\%d)" + volumes: + - ./backups:/backups + depends_on: + - mongo + networks: + - guestbooky volumes: mongodata: diff --git a/build/mongodb/mongo-init.js b/build/mongodb/mongo-init.js index 276b0a1..399290c 100644 --- a/build/mongodb/mongo-init.js +++ b/build/mongodb/mongo-init.js @@ -1,13 +1,13 @@ -db = db.getSiblingDB('Guestbooky'); +db = db.getSiblingDB(process.env.GUESTBOOKY_DB_NAME); db.createUser( { - user: "guestbookyuser", - pwd: "guestbookypassword", + user: process.env.GUESTBOOKY_USER, + pwd: process.env.GUESTBOOKY_PASSWORD, roles: [ { role: "readWrite", - db: "Guestbooky" + db: process.env.GUESTBOOKY_DB_NAME } ] } diff --git a/docs/comments.md b/docs/comments.md index 0c358bd..bbafe3d 100644 --- a/docs/comments.md +++ b/docs/comments.md @@ -43,4 +43,6 @@ ___ - There isn't much exception handling, except in the API layer. This is on purpose. Another thing that this project could really use, but is left as an exercise, is using a `Maybe/Result/ErrorOr` type. - Since there is so little that can go wrong with *low-stakes CRUDding*, it is a reasonable trade-off to let the API layer catch and send an internal server error. -- By default, you need to choose between good Cookie-based authentication defaults or REST-friendly authentication via the `Authorization` header. Luckily you can support both with a few small additions - it made more sense to keep *RESTy* as the main method and stick Cookie support to its tail. \ No newline at end of file +- By default, you need to choose between good Cookie-based authentication defaults or REST-friendly authentication via the `Authorization` header. Luckily you can support both with a few small additions - it made more sense to keep *RESTy* as the main method and stick Cookie support to its tail + +- I really, REALLY should have done a daily backup mechanism right from the start. And I should have done the `.env`-based variable configuration from the get-go, too. \ No newline at end of file