feat: We now have daily backups, and somewhat sane environment variable configuration now. No more need to hack around the docker-compose file.
Some checks are pending
CI/CD / build-and-test (push) Waiting to run

This commit is contained in:
Felipe Cotti 2024-12-05 00:46:55 -03:00
parent 2aaad4cf96
commit 8f44b25d44
6 changed files with 73 additions and 44 deletions

3
.gitignore vendored
View file

@ -11,4 +11,5 @@ artifacts/
*.sln.ide *.sln.ide
build/docker-compose.yml build/docker-compose.yml
build/mongodb/mongodata/ build/mongodb/mongodata/
src/Guestbooky/.vs/ src/Guestbooky/.vs/
/build/.env

View file

@ -34,6 +34,7 @@
- [🚀 Deployment ](#-deployment-) - [🚀 Deployment ](#-deployment-)
- [Backend](#backend-1) - [Backend](#backend-1)
- [Admin Panel](#admin-panel-1) - [Admin Panel](#admin-panel-1)
- [Backup](#backup)
- [⛏️ Built Using ](#-built-using-) - [⛏️ Built Using ](#-built-using-)
- [✍️ Authors ](#-authors-) - [✍️ Authors ](#-authors-)
@ -65,20 +66,7 @@ For running it locally:
- A Cloudflare turnstile secret key for the captcha - A Cloudflare turnstile secret key for the captcha
- Not forgetting to set up environment variables - 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. There's a `.env.template` file with all environment variables used throughout the compose file.
```
- 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
```
> [!IMPORTANT] > [!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. 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.| |**CLOUDFLARE_SECRET**|The turnstile secret, used in the server portion of the captcha check.|
|**MONGODB_\***|Related to the connection to MongoDB. Yeah.| |**MONGODB_\***|Related to the connection to MongoDB. Yeah.|
|**LOG_\***|Logging.| |**LOG_\***|Logging.|
|**GUESTBOOKY_**|Related to accessing the main document collection, which uses its own user.|
> [!TIP] > [!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 ### Admin panel
@ -112,12 +100,16 @@ For development, it should be enough to run `vite` in Guestbooky-admin's `src` f
### Backend ### 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 ### 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. 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 <a name = "built_using"></a> ## ⛏️ Built Using <a name = "built_using"></a>
- [MongoDB](https://www.mongodb.com/) - Database - [MongoDB](https://www.mongodb.com/) - Database

View file

@ -5,8 +5,8 @@ services:
container_name: mongo container_name: mongo
restart: always restart: always
environment: environment:
MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
MONGO_INITDB_ROOT_PASSWORD: mongo MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
volumes: volumes:
- ./mongodb/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro - ./mongodb/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
- ./mongodb/mongod.conf:/etc/mongod.conf:ro - ./mongodb/mongod.conf:/etc/mongod.conf:ro
@ -19,15 +19,15 @@ services:
image: mongo-express image: mongo-express
container_name: mongo-express container_name: mongo-express
restart: always restart: always
ports:
- 8082:8081
environment: environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_INITDB_ROOT_USERNAME}
ME_CONFIG_MONGODB_ADMINPASSWORD: mongo ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
ME_CONFIG_MONGODB_URL: mongodb://root:mongo@mongo:27017/ ME_CONFIG_MONGODB_URL: mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017/
ME_CONFIG_BASICAUTH: false ME_CONFIG_BASICAUTH: "true"
depends_on: depends_on:
- mongo - mongo
ports:
- "8082:8081"
volumes: volumes:
mongodata: mongodata:

View file

@ -6,17 +6,17 @@ services:
ports: ports:
- "8080:8080" - "8080:8080"
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production ASPNETCORE_ENVIRONMENT: Production
- CORS_ORIGINS=https://guestbooky.example.com CORS_ORIGINS: ${CORS_ORIGINS}
- ACCESS_USERNAME=user ACCESS_USERNAME: ${ACCESS_USERNAME}
- ACCESS_PASSWORD=pass ACCESS_PASSWORD: ${ACCESS_PASSWORD}
- ACCESS_TOKENKEY=youbetterbesureyouareusingatokenkey ACCESS_TOKENKEY: ${ACCESS_TOKENKEY}
- ACCESS_ISSUER=https://guestbooky.example.com/api ACCESS_ISSUER: ${ACCESS_ISSUER}
- ACCESS_AUDIENCE=https://guestbooky.example.com ACCESS_AUDIENCE: ${ACCESS_AUDIENCE}
- CLOUDFLARE_SECRET=0x000000000000000000000000000000000 CLOUDFLARE_SECRET: ${CLOUDFLARE_SECRET}
- MONGODB_CONNECTIONSTRING=mongodb://mongouser:mongopass@mongo:27017/Guestbooky MONGODB_CONNECTIONSTRING: mongodb://${GUESTBOOKY_USER}:${GUESTBOOKY_PASSWORD}@mongo:27017/${GUESTBOOKY_DB_NAME}
- MONGODB_DATABASENAME=Guestbooky MONGODB_DATABASENAME: ${GUESTBOOKY_DB_NAME}
- LOG_LEVEL=Debug LOG_LEVEL: Debug
depends_on: depends_on:
- mongo - mongo
restart: unless-stopped restart: unless-stopped
@ -28,8 +28,11 @@ services:
container_name: mongo container_name: mongo
restart: always restart: always
environment: environment:
MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
MONGO_INITDB_ROOT_PASSWORD: mongo MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
GUESTBOOKY_DB_NAME: ${GUESTBOOKY_DB_NAME}
GUESTBOOKY_USER: ${GUESTBOOKY_USER}
GUESTBOOKY_PASSWORD: ${GUESTBOOKY_PASSWORD}
volumes: volumes:
- ./mongodb/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro - ./mongodb/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
- ./mongodb/mongod.conf:/etc/mongod.conf:ro - ./mongodb/mongod.conf:/etc/mongod.conf:ro
@ -38,6 +41,37 @@ services:
networks: networks:
- guestbooky - 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: volumes:
mongodata: mongodata:

View file

@ -1,13 +1,13 @@
db = db.getSiblingDB('Guestbooky'); db = db.getSiblingDB(process.env.GUESTBOOKY_DB_NAME);
db.createUser( db.createUser(
{ {
user: "guestbookyuser", user: process.env.GUESTBOOKY_USER,
pwd: "guestbookypassword", pwd: process.env.GUESTBOOKY_PASSWORD,
roles: [ roles: [
{ {
role: "readWrite", role: "readWrite",
db: "Guestbooky" db: process.env.GUESTBOOKY_DB_NAME
} }
] ]
} }

View file

@ -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<T>/Result<T>/ErrorOr<T>` type. - 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<T>/Result<T>/ErrorOr<T>` 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. - 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. - 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.