{"id":18821028,"url":"https://github.com/memphis-tools/dummy_fastapi_flask_blog_app","last_synced_at":"2026-01-17T20:30:16.798Z","repository":{"id":232961234,"uuid":"784711327","full_name":"memphis-tools/dummy_fastapi_flask_blog_app","owner":"memphis-tools","description":"Dummy project for learning purposes. A Fastapi - Flask blog application.","archived":false,"fork":false,"pushed_at":"2024-04-15T10:42:12.000Z","size":4459,"stargazers_count":0,"open_issues_count":28,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-16T02:01:19.363Z","etag":null,"topics":["betterstack","certbot","digitalocean","docker","docker-compose","fastapi","flask","gitlab-ci-pipeline","postgresql","python","sqlalchemy","terraform","vault"],"latest_commit_sha":null,"homepage":"https://dummy-ops.dev/front","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/memphis-tools.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-04-10T12:03:48.000Z","updated_at":"2024-04-17T03:47:40.491Z","dependencies_parsed_at":"2024-04-17T03:47:22.979Z","dependency_job_id":"141688d7-291f-44d3-b014-13f1a8eab087","html_url":"https://github.com/memphis-tools/dummy_fastapi_flask_blog_app","commit_stats":null,"previous_names":["memphis-tools/dummy_fastapi_flask_blog_app"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memphis-tools%2Fdummy_fastapi_flask_blog_app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memphis-tools%2Fdummy_fastapi_flask_blog_app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memphis-tools%2Fdummy_fastapi_flask_blog_app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memphis-tools%2Fdummy_fastapi_flask_blog_app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/memphis-tools","download_url":"https://codeload.github.com/memphis-tools/dummy_fastapi_flask_blog_app/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239758831,"owners_count":19692034,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["betterstack","certbot","digitalocean","docker","docker-compose","fastapi","flask","gitlab-ci-pipeline","postgresql","python","sqlalchemy","terraform","vault"],"created_at":"2024-11-08T00:33:14.219Z","updated_at":"2026-01-17T20:30:16.702Z","avatar_url":"https://github.com/memphis-tools.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Screenshot](https://img.shields.io/badge/python-v3.12-blue?logo=python\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/fastapi--blue?logo=fastapi\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/flask--blue?logo=gitlab\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/sqlalchemy--blue?logo=fastapi\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/postgresql-v15-blue?logo=postgresql\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/docker--blue?logo=docker\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/gitlab-ci:cd-blue?logo=gitlab\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/terraform--blue?logo=hashicorp\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/vault--blue?logo=hashicorp\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/celery-5.4.0-blue?logo=celery\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/rabbitmq-4-blue?logo=rabbitmq\u0026logoColor=yellow)\n![Screenshot](https://img.shields.io/badge/digitalocean--black?logo=digitalocean\u0026logoColor=yellow\u0026color=blue)\n![Screenshot](https://img.shields.io/badge/betterstack--blue)\n![Screenshot](https://img.shields.io/badge/coveralls--blue?logo=coveralls\u0026logoColor=yellow)\n[![Coverage Status](https://coveralls.io/repos/gitlab/memphis-tools/dummy_fastapi_flask_blog_app/badge.svg?branch=HEAD)](https://coveralls.io/gitlab/memphis-tools/dummy_fastapi_flask_blog_app?branch=HEAD)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/5aa0869a8f5a4835a74f84ba0088f69e)](https://app.codacy.com/gh/memphis-tools/dummy_fastapi_flask_blog_app/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n![Known Vulnerabilities](https://snyk.io/test/github/memphis-tools/dummy_fastapi_flask_blog_app/badge.svg)\n\n# DUMMY APP FOR LEARNING PURPOSES\n**This is dummy blog application.**\n\nThe project is mirored from \u003ca href=\"https://gitlab.com/memphis-tools/dummy_fastapi_flask_blog_app\"\u003eGITHUB DUMMY FASTAPI FLASK BLOG APP\u003c/a\u003e\n\nApplication is simultaneously served as a Flask and a FastAPI front-end. Postgresql the database and Nginx as reverse proxy.\n\nCertbot and Nginx handle the HTTPS.\n\nCelery and RabbitMQ are set to be able to send mail through Twilio SendGrid, as asynchrinous tasks.\n\nRedis lists Celery's tasks executions.\n\nEach of these services run on Docker. To avoid excessive spending only 1 virtual machine is used.\n\nAlso the Gitlab subscription is the free default one.\n\n**Focus is more on technologies and how bind it for a project. This is the aim.**\n\nDo not pay much attention to the HTML-CSS-JS part.\n\n**Look at GitHub issues to see what has to be done, what should be updated.**\n\nIf the dummy example application is up you will find it at:\n\n  Flask Front: https://dummy-ops.dev\n\n  FastAPI Front: https://dummy-ops.dev/api/v1/docs\n\nA **visitor account** is created for anybody: visiteur / @pplepie94 / visiteur@localhost.fr\n\n**About Vault and sensitive datas**\n\nThe Vault here has a simple jwt authentication method. Idea is just to a avoid to have sensitive datas on Gitlab.\n\nPurpose is that we do not want a project user, be able to echo the vars through the gitlab-ci.yml.\n\nThe SSH_PRIVATE_KEY is protected with a SSH passphrase. So the private key can be display in logs, but not the passphrase.\n\nPOSTGRES_PASSWORD in the Gitlab settings stands for the default Postgresql's Docker image password. This password is only used during unit_tests step.\n\nBy convenience, and because we do not want to use multiple virtual machines (droplets) the Vault is recreated at each new deployment into production.\n\nVault service is used to set sensitive datas as Docker swarm secrets. The service is shutdown before the swarm stack deployment.\n\nSo currently the Docker swarm is run by a \"dummy_user\"; he can exec into containers and so see the docker secrets.\n\nNext step will be to have a Vault service always running so no secrets could be see within containers.\n\nRemember it's just a dummy project.\n\n**A default virtual machine with 1vcpu and 2gb RAM is needed**\n\n## TECHNOLOGIES\nPython 3.12 and later\n\nPostgresql 15 (driver psycopg 3)\n\nGunicorn (for Flask)\n\nUvicorn (for FastAPI)\n\nNginx\n\nDocker (docker compose), DockerHub, Docker Scout\n\nGitlab (the CI/CD chain is engaged throuh the Gitlab repo)\n\nTerraform\n\nCertbot\n\nVault\n\nRabbitMQ\n\nCelery\n\nRedis\n\nTwilio SendGrid\n\nLynis (for the virtual machine, droplet, hardening)\n\nhCaptcha\n\nCodacy\n\nSnyk\n\nTabNine\n\nCloudflare\n\n## HOW TO SET IT, HOW IT WORKS\n\n  Notice: if you use VisualCode remember that you can preview the markdown from VisualCode by running: Ctrl+Shift+V\n\n  Notice: currently the postgres password is the default one.\n\n  There is no use of the create_app design pattern for Flask.\n\n  The FastAPI app is the main one, and executes the database initialization at boot.\n\n  Github Action workflows are used. You must set secrets (tokens which allow the exchanges).\n\n  ![Screenshot](illustrations/github_secrets.png)\n\n  Celery worker use a default configuration.\n\n  Rabbitmq is setup with a default configuration (we just set a specific user, password and vhost).\n\n  Celery uses 2 types of exchanges: fanout and direct.\n\n  The first one is a broadcast, which spread a task execution order.\n\n  The second one is for the celery worker to return message result to the rabbitmq queue.\n\n        rabbitmqctl list_queues -p your_rabbitmq_vhost name messages\n\n        rabbitmqctl list_bindings -p your_rabbitmq_vhost\n\n  Cloudflare even with a free subscription offers a basic protection against bots:\n\n  ![Screenshot](illustrations/cloudflare_security_events.png)\n\n  **Codacy and Snyk** already display updates to be done. You can also see from **Sonarqube** that there's much to be done:\n\n  ![Screenshot](illustrations/sonarqube_stats.png)\n\n  Also **Zed Attack Proxy (ZAP)** raises warnings and alerts:\n\n  ![Screenshot](illustrations/zap_stats.png)\n\n### POSTGRESQL PREQUISITES FOR LOCAL USAGE\n--------------------------\n  - For a local usage (without docker) first be sure service is running (Linux example command):\n\n        sudo systemctl start postgresql\n\n  - Password must match the POSTGRES_PASSWORD defined in the .envrc.* files. So you may have to update it.\n\n        [postgres@sanjurolab ~]$ psql\n        psql (15.1)\n        Type \"help\" for help.\n\n        postgres=# \\password postgres\n        Enter new password for user \"postgres\":\n        Enter it again:\n\n\n### LOCAL EXECUTION PREQUISITES\n-------------------------\n  You will need a SECRET_KEY var in able to run the application.\n\n  To create one, either use python secrets module, or openssl:\n\n    python -c \"import secrets;print(secrets.token_hex())\"\n\n  or\n\n    openssl rand -hex 32\n\n  - For a local docker execution:\n\n    At the project root folder, touch (create) a **\".env\"** file.\n\n    Notice that this file is the one used for local deployment with local-docker-stack.yml.\n\n    The admin user is an application's admin. Not a Postgresql role. The engine use the default postgres user.\n\n    The \"dummy-operator\" must match the one defined in the Gunicorn Dockerfile.\n\n    Set something like this:\n\n        export ADMIN_LOGIN=\"admin\"\n        export ADMIN_PASSWORD=\"@pplepie94\" #notice this is not the real password\n        export ADMIN_EMAIL=\"admin@localhost.fr\" #use a real email\n        export APP_FOLDER=/home/dummy-operator/flask\n        export BETTERSTACK_SOURCE_TOKEN=\"yourBetterstackToken\"\n        export CELERY_BROKER_URL=\"pyamqp://$RABBITMQ_DEFAULT_USER:$RABBITMQ_DEFAULT_PASS@rabbitmq:5672/$RABBITMQ_DEFAULT_VHOST\"\n        export CELERY_RESULT_BACKEND=\"your redis url\"\n        export COVERALLS_REPO_TOKEN=\"yourCoverallsToken\"\n        export FLASK_APP=project/__init__.py\n        export FLASK_DEBUG=1\n        export HCAPTCHA_SITE_KEY=\"yourHcaptchaSiteSecret\"\n        export HCAPTCHA_SITE_SECRET=\"yourHcaptchaSecret\"\n        export PDF_FOLDER_PATH=\"/tmp\"\n        export PDF_FILE_NAME=\"dummy_books\"\n        export POSTGRES_USER=\"postgres\"\n        export POSTGRES_PASSWORD=\"postgres\"\n        export POSTGRES_TEST_DB_NAME=\"test_dummy_blog\"\n        export POSTGRES_PORT=\"5432\"\n        export POSTGRES_HOST=\"db\"\n        export RABBITMQ_DEFAULT_USER=\"your_rabbitmq_user\"\n        export RABBITMQ_DEFAULT_PASS=\"your_rabbitmq_password\"\n        export RABBITMQ_DEFAULT_VHOST=\"your_rabbitmq_vhost\"\n        export SECRET_KEY=\"YourSUperSecretKey123oclock\"\n        export SCOPE=\"development\"\n        export SENDGRID_API_KEY=\"your sendgrid api key\"\n        export SWARM_IP=\"your_ip\"\n        export TEST_USER_PWD=\"@pplepie94\"\n        export TIMEZONE=\"Europe/Paris\"\n\n    Notice:\n\n    POSTGRES_HOST refers to the postgresql service name\n\n    SCOPE: for a local execution you set \"development\".\n\n    Anything else than \"production\" or \"development\" allow tests to be run.\n\n### HOW RUN IT LOCALLY\n----------------------------------------------\n\nClone the repository\n\n    `git clone https://github.com/memphis-tools/dummy_fastapi_flask_blog_app.git`\n\n    `cd dummy_fastapi_flask_blog_app`\n\nYou do not need to create a python virtualenv.\n\n  - Example (for Linux):\n\n      ./local_stack_deployment.sh\n\n      docker service ls\n\n      docker swarm leave --force\n\n  - Flask front-end will be reachable at:\n\n      http://localhost/\n\n  - Swagger docs will then be served at:\n\n      http://localhost/api/v1/docs\n\n    As we run locally, there is a default test database set with some dummies data (see app/packages/utils.py).\n\n    You will be able to login with following (example) credentials: donald / applepie94 / donald@localhost.fr.\n\n    You can set some default variables in \"app/packages/settings.py\".\n\n### HOW TEST IT\n---------------\n  Notice we set a pytest.ini file to define our patterns.\n\n  Tests occure when you run it locally, or during the ci-cd execution.\n\n  Avoid to change tests order (particulary about the session cookie in tests_flask_urls).\n\n  To run test for a local execution (**ensure postgresql service is started, and the .env file created**):\n\n      python -m venv venv\n\n      source venv/bin/activate\n\n      source .env\n\n      pip install -U pip\n\n      pip install -r app/packages/celery_client_and_worker/requirements.txt\n\n      pip install -r app/packages/fastapi/requirements.txt\n\n      pip install -r app/packages/flask_app/requirements.txt\n\n      SCOPE=\"test\"\n\n      POSTGRES_HOST=\"your_local_ipv4\"\n\n      python -m coverage run -m pytest -vs app/\n\n      python -m coverage report\n\n  To create a friendly html report in a cov_html folder:\n\n      python -m coverage html -d cov_html\n\n  90 should be the minimum relevent score.\n\n  To run test during the gilab-ci execution, see .gitlab-ci.yml file.\n\n  You must also pay attention to the following Gitlab's CI/CD settings variables.\n  During gitlab-ci tests, the official postgresql image is used.\n\n      POSTGRES_HOST: postgres\n      POSTGRES_PASSWORD: postgres\n      POSTGRES_PORT: 5432\n      POSTGRES_USER: postgres\n      POSTGRES_DB: test_dummy_blog\n\n### HOW CHECK PEP'S RECOMMENDED SYNTAX\n--------------------------------------\n      pip install black pylint flake8-html bandit\n\n  You can use black to format code:\n\n      black app/packages/\n\n      black app/tests/\n\n  You can check for a flake8's lint:\n\n      flake8 app/ --max-line-length=127 --count --statistics\n\n  You can check the lint score:\n\n      pylint app/\n\n  You can check for common security issues in Python code:\n\n      python -m bandit -r app/\n\n### HOW RUN IT IN PRODUCTION\n----------------------------\n\n  - You must have a virtual machine. Here we used [DigitalOcean](https://www.digitalocean.com/) cloud provider.\n\n    We use HashiCorp Terraform tool to generate the virtual machine (the \"droplet\") on DigitalOcean.\n\n    [Install Hashicorp's terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)\n\n    Notice and adapt the [VirtualMachine /Droplet's definition](https://github.com/memphis-tools/dummy_fastapi_flask_blog_app/tree/development/terraform/debian_with_docker.tf) file.\n\n    These variables are used to create the virtual machine (\"droplet\"), and to setup the Vault.\n\n    Notice the \"terraform/digitalocean_firewall.tf\" file which set the desired rules (8200 is for the Vault).\n\n    You must have a DigitalOcean account, then created a personal access token, and a ssh dedicated key-pair.\n\n    In the following sequence you can change directory into the terraform dir (cd terraform) to avoid the \"-chdir\" usage.\n\n      Copy the variables file and set the ones you need:\n\n      `cp terraform/variables.tf.ORI terraform/variables.tf`\n\n      Export your DigitalOcean personal access token:\n\n      `export DO_PAT=\"dop_v1_2WhatADopSecretIsNotItAsLongItIs\"`\n\n      Initialize terraform:\n\n      `terraform -chdir=terraform init`\n\n      Create terraform plan:\n\n      `terraform -chdir=terraform plan -var \"do_token=${DO_PAT}\" -var \"pvt_key=[path to your private key]\"`\n\n      Execute the plan,\n\n      `terraform -chdir=terraform apply -var \"do_token=${DO_PAT}\" -var \"pvt_key=[path to your private key]\" -auto-approve`\n\n      Watch out the end of execution and because it succeeded, consult the following.\n\n      `terraform -chdir=terraform state list`\n\n      `terraform -chdir=terraform state show digitalocean_droplet.dummy-django-with-docker`\n\n      Notice the public ipv4_address. Source your ssh private key and login through ssh.\n\n      `ssh dummy-user@PublicIpAddress`\n\n      If you need to destroy the droplet:\n\n      `terraform -chdir=terraform plan -destroy -out=terraform.tfplan \\\n          -var \"do_token=${DO_PAT}\" \\\n          -var \"pvt_key=/home/sanjuro/.ssh/terraform/id_rsa\"\n      `\n\n      `terraform -chdir=terraform apply terraform.tfplan`\n\n  - Only a push to the master branch will trigger the deploy step of the gitlab-ci.yml file\n\n  - Do not remove the \"dummy_user_certbot-etc\" volume because certbot will store the https cert in it.\n\n  - For the production docker execution:\n\n    As we use the gitlab-ci we have to set these settings in the Gitlab project.\n\n    Remember to add a blank line at the end of SSH_PRIVATE_KEY declaration.\n\n    The passwords vars are used for the tests steps. They are not the ones in the vault.\n\n        CELERY_RESULT_BACKEND\n        CERTBOT_EMAIL\n        CI_REGISTRY_TOKEN\n        CI_REGISTRY_USER\n        CODACY_PROJECT_TOKEN\n        COVERALLS_REPO_TOKEN\n        HCAPTCHA_SITE_KEY\n        HOST_IP\n        PDF_FILE_NAME\n        PDF_FOLDER_PATH\n        POSTGRES_PASSWORD\n        RABBITMQ_DEFAULT_USER\n        RABBITMQ_DEFAULT_VHOST\n        SSH_PASSPHRASE\n        SSH_PRIVATE_KEY\n        TEST_USER_PWD\n        TIMEZONE\n\n    CERTBOT_EMAIL is set as Protected, Masked, Hidden and Expanded.\n\n    Notice: not all the necessary CI/CD variables are illustrated in the picture below.\n\n    ![Screenshot](illustrations/dummy_fastapi_gitlab_settings.png)\n\n    See logs on Betterstack.\n\n    ![Screenshot](illustrations/betterstack_dummyops_logs.png)\n\n### USEFUL COMMANDS\n------------------------------\n\n  - to see rabbitmq queue (once you get into the container)\n\n      rabbitmqctl list_users\n\n      rabbitmqctl list_vhosts\n\n      rabbitmqctl list_queues -p your_rabbitmq_user name messages\n\n      rabbitmqctl list_bindings -p your_rabbitmq_vhost\n\n  - to see the history of tasks executed by celery workers (once you get into the container)\n\n      redis-cli KEYS *\n\n      redis-cli GET \u003ckey\u003e\n\n      to reset the redis database: redis-cli FLUSHDB\n\n      to remove sepecific task: redis-cli DEL celery-task-meta-\u003ctask_id\u003e\n\n### HARDENING\n\n  You should use the \"lynis\" tool on the virtual machine and run \"lynis audit system\".\n\n  80 is a minimum relevent score.\n\n### USEFUL LINKS\n-----------------\nTo run Flask behind Gunicorn and Nginx i used the following link:\n\nhttps://testdriven.io/blog/dockerizing-flask-with-postgres-gunicorn-and-nginx/\n\nFor Flask i used:\n\nhttps://www.udemy.com/course/100-days-of-code/\n\nhttps://blog.miguelgrinberg.com/category/Flask\n\nhttps://realpython.com/flask-blueprint/\n\nFor Flask image upload i used:\n\nhttps://flask-wtf.readthedocs.io/en/0.15.x/form/\n\nhttps://flask.palletsprojects.com/en/2.3.x/config/\n\nhttps://flask-resize.readthedocs.io/index.html\n\nFor Flask template custom filters i used:\n\nhttps://stackoverflow.com/questions/4830535/how-do-i-format-a-date-in-jinja2\n\nFor Flask hCaptcha i used:\n\nhttps://github.com/KnugiHK/flask-hcaptcha\n\nFor FastAPI, the ci-cd i used:\n\nhttps://fastapi.tiangolo.com/tutorial/security/\n\nhttps://fastapi.tiangolo.com/tutorial/bigger-applications/\n\nhttps://pypi.org/project/pytest-postgresql/\n\nhttps://gitlab.com/gitlab-examples/postgres/-/blob/master/.gitlab-ci.yml?ref_type=heads\n\nhttps://medium.com/metro-platform/continuous-integration-for-python-3-in-gitlab-e1b4446be76b\n\nTo avoid log warning \"AttributeError: module 'bcrypt' has no attribute '__about__'\" i followed theses links:\n\nhttps://github.com/pyca/bcrypt/issues/684\n\nhttps://www.geeksforgeeks.org/hashing-passwords-in-python-with-bcrypt/\n\nBecause of \"CVE-2024-23342 ecdsa may be vulnerable to the Minerva attack\" we do not use python-jose:\n\nhttps://github.com/mpdavis/python-jose/blob/master/jose/jwt.py\n\nSee how we implement the FastAPI security principles in app/packages/fastapi/routes/routes_and_authentication.py\n\nTo use the Hashicorp Vault, and learn how to use it:\n\nhttps://hub.docker.com/r/hashicorp/vault\n\nhttps://testdriven.io/blog/dynamic-secret-generation-with-vault-and-flask/\n\nhttps://testdriven.io/blog/managing-secrets-with-vault-and-consul/\n\nhttps://developer.hashicorp.com/vault/tutorials/auto-unseal/autounseal-transit\n\nhttps://docs.gitlab.com/ee/integration/vault.html\n\nhttps://developer.hashicorp.com/vault/docs/auth/jwt#redirect-uris\n\nhttps://developer.hashicorp.com/vault/tutorials/auth-methods/approle\n\nhttps://support.hashicorp.com/hc/en-us/articles/12406076771347-Vault-JWT-auth-with-static-keys\n\nTo understand Terraform templates usage i used:\n\nhttps://spacelift.io/blog/terraform-templates\n\nTo set the DigitalOcean Firewall through Terraform i used:\n\nhttps://www.digitalocean.com/community/tutorials/how-to-import-existing-digitalocean-assets-into-terraform\n\nhttps://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/firewall\n\nTo set Nginx i used:\n\nhttps://www.ssllabs.com/ssltest/analyze.html\n\nhttps://nginx.org/en/docs/http/ngx_http_ssl_module.html\n\nhttps://nginx.org/en/docs/http/configuring_https_servers.html\n\nhttps://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/\n\nhttps://www.nginx.com/resources/glossary/http2/\n\nhttps://developer.mozilla.org/en-US/docs/Web/HTTP/CSP\n\nFor the matplotlib charts i used:\n\nhttps://matplotlib.org/stable/gallery/user_interfaces/web_application_server_sgskip.html\n\nFor the logo and favicon.ico i used:\n\nhttps://www.canva.com\n\nAbout predefined variables for gitlab-ci:\n\nhttps://docs.gitlab.com/ee/ci/variables/\n\nFor the Docker-Hub registry credentials:\n\nhttps://docs.gitlab.com/ee/user/packages/container_registry/authenticate_with_container_registry.html\n\nFor Celery, RabbitMQ and Redis is used:\n\nhttps://github.com/memphis-tools/dummy_flask_rabbitmq_celery/blob/main/start_application.sh\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmemphis-tools%2Fdummy_fastapi_flask_blog_app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmemphis-tools%2Fdummy_fastapi_flask_blog_app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmemphis-tools%2Fdummy_fastapi_flask_blog_app/lists"}