Version 1 (currently in development).
For full API documentation, check the API Docs
.
For full project information, check the main README file
.
If you want to contribute to the project, you should start by reading the contribution guidelines and the code of conduct.
The project is open source, so feel free to use parts of the code. However, the full project itself is not meant to be reused. The design, the concept and the project itself are personal and belong to the Send A Hug group.
- Python 3.11+
- Postgres 13
- Download or clone the repo.
- cd into the project directory.
- cd into backend.
- Run
pip install -r requirements.txt -r dev_requirements.txt
to install dependencies. - Run
pre-commit install
to install and initialise pre-commit. - Create a database for the app.
- Set the required environment variables:
- The database URI is built in the config using the following environment variables:
- DB_CREDENTIALS_PATH - The path to the credentials file.
- DATABASE_USERNAME - The username to log into the database if no credentials file is set.
- DATABASE_PASSWORD - The password to log into the database if no credentials file is set.
- PRIVATE_VAPID_KEY - Your private VAPID key (required for push notifications).
- FRONTEND - The frontend URI.
- FIREBASE_CREDENTIALS_FILE - The firebase credentials file's location.
- The database URI is built in the config using the following environment variables:
- Update your database using
alembic upgrade head
- Run Quart with:
export QUART_APP=app.py
quart --debug run
Developers that are part of the main Send A Hug project can use the ./get_secrets.sh
script to download the above secrets from Google Cloud Secrets Manager to their default locations and minimise the need for setting environment variables (requires access to the Send A Hug Google Cloud Project and installation of the gcloud
cli).
Not yet ready for users!
The app contains several files and folders:
- app.py - The main application file. Simply generates and runs the app.
- create_app.py - Contains the app's content. This file contains all endpoints and error handlers.
- filter.py - The system responsible for filtering words. (Work in progress)
- manage.py - The file managing running database migrations when deploying.
- models - The folder containing SQLAlchemy models, as well as all database-related methods.
- auth.py - The file dealing with authentication - getting the Authorization header, verifying the JWT and confirming the user has the required permission.
- tests - The folder containing the backend's test suite.
The site uses several tools to maximise compatibility:
-
Quart - Quart is a Python microframework used to build and run the local server on which the app is running. As Quart is an async reimplementation of Flask, its API is mostly the same as Flask's. For full Quart documentation, try the Quart website.
-
SQLAlchemy - This application uses the SQLAlchemy ORM in order to interact with the database. You can read more about SQLAlchemy (including API documentation) on the SQLAlchemy website.
-
Alembic - This application uses Alembic, a database migration tool made by the SQLAlchemy team, in order to manage model versions. You can read more on the Alembic docs.
-
Quart-CORS - This application uses Quart-CORS in order to enable communication from the frontend. You can read more about it in the Quart-CORS repository.
-
Firebase-Admin - This application uses firebase-admin in order to decode and verify the authenticity of a given JWT (see Contents -> auth.py). You can read more on the Firebase SDK API docs.
-
PyWebPush - This application uses pywebpush in order to handle push notifications. For more information, check their GitHub repo.
-
Coverage - This application uses coverage in order to provide code coverage for testing. For more information, check the Coverage documentation.
The project uses Firebase as a third-party authentication provider. Authentication is done by Firebase, which in turn returns a JSON Web Token containing the user's data and permissions.
Decoding and verifying the token is done by auth.py
, in three stages:
-
The server gets the Authorization header from the request and ensures that it exists and is in the right form. (Done by the
get_auth_header
function.) -
The server uses the Firebase SDK to decode and verify the token. (Done by the
validate_token
function.) -
Once the JWT is decoded and verified, the user's data (including permissions) is fetched from the back-end (done by the
get_current_user
function). Each endpoint that requires authorisation contains a string, which is then compared to the user's permissions. (Done by thecheck_user_permissions
function.)
- There's one exception to this, which is the
POST /users
endpoint. Since at that point the user doesn't exist in the internal database yet, fetching the permissions would fail. This endpoint is the only endpoint that requires authentication but no authorisation. This allows them to create a user, which is then used for all subsequent visits.
Endpoints that require authorisation are marked with the @requires_auth
decorator. The function creating the decorator is written in full in auth.py
.
In case the user's authorisation header is malformed, their JWT is invalid in any way, or they don't have the required permission, the server raises an AuthError. The error handler is defined in full with the rest of the error handlers, in app.py
.
This project utilises Pytest for testing.
Once you've completed the setup for whichever approach you've chosen, run the following commands:
cat init_test_db.sql | sudo -u postgres psql
pytest
Or, if using macOS:
dropdb test_sah && createdb test_sah
pytest
There are no current issues at the time.