diff --git a/.gitignore b/.gitignore index 5133896..b155443 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ .devcontainer __pycache__ *.pyc -.env \ No newline at end of file +.env +/.quarto/ +_docs/ +.pytest_cache/ +.mypy_cache/ \ No newline at end of file diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 7efd69c..c204737 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,55 @@ -# README +# FastAPI, Jinja2, PostgreSQL Webapp Template +![Screenshot of homepage](docs/static/Screenshot.png) -## FastAPI, Jinja2, PostgreSQL Webapp +## Documentation -![Screenshot of homepage](static/Screenshot.png) +This README provides a high-level overview. See the **[full documentation website](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/)** for more detailed information on installation, features, architecture, conventions and code style, customization, and deployment to cloud platforms. -This project is still under development. +## Features -### Architecture +*FastAPI, Jinja2, PostgreSQL Webapp Template* combines three of the most lightweight and performant open-source web development frameworks in existence into a customizable webapp template with: -This application uses a Post-Redirect-Get (PRG) pattern. The user -submits a form, which sends a POST request to a FastAPI endpoint on the -server. The database is updated, and the user is redirected to a GET -endpoint, which fetches the updated data and re-renders the Jinja2 page -template with the new data. +- Pure Python backend +- Low-Javascript frontend +- Powerful, easy-to-manage database layer -![Webapp Flow](static/webapp_flow.png) +The template also includes full-featured secure auth with: -The advantage of the PRG pattern is that it is very straightforward to -implement and keeps most of the rendering logic on the server side. The -disadvantage is that it requires an extra round trip to the database to -fetch the updated data, and re-rendering the entire page template may be -less efficient than a partial page update on the client side. +- Token-based authentication +- Password recovery flow +- Role-based access control system -### Install development dependencies in a VSCode Dev Container +The design philosophy of the template is to prefer low-level, best-in-class open-source frameworks that offer flexibility, scalability, and performance without vendor-lock-in. You'll find the template amazingly easy not only to understand and customize, but also to deploy to any major cloud hosting platform. -If you use VSCode with Docker to develop in a container, the following -VSCode Dev Container configuration will install all dependencies: +## Tech stack -``` json -{ - "name": "Python 3", - "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", - "postCreateCommand": "sudo apt update && sudo apt install -y python3-dev libpq-dev graphviz && pipx install poetry && poetry install && poetry shell", - "features": { - "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}, - "ghcr.io/rocker-org/devcontainer-features/quarto-cli:1": {} - } -} -``` +**Core frameworks:** +- [FastAPI](https://fastapi.tiangolo.com/): scalable, high-performance, type-annotated Python web backend framework +- [PostgreSQL](https://www.postgresql.org/): the world's most advanced open-source database engine +- [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/): frontend HTML templating engine +- [SQLModel](https://sqlmodel.tiangolo.com/): easy-to-use Python ORM + +**Additional technologies:** +- [Poetry](https://python-poetry.org/): Python dependency manager +- [Pytest](https://docs.pytest.org/en/7.4.x/): testing framework +- [Docker](https://www.docker.com/): development containerization +- [Github Actions](https://docs.github.com/en/actions): CI/CD pipeline +- [Quarto](https://quarto.org/docs/): simple documentation website renderer +- [MyPy](https://mypy.readthedocs.io/en/stable/): static type checker for Python +- [Bootstrap](https://getbootstrap.com/): HTML/CSS styler +- [Resend](https://resend.com/): zero- or low-cost email service used for password recovery -Simply create a `.devcontainer` folder in the root of the project and -add a `devcontainer.json` file in the folder with the above content. -VSCode may prompt you to install the Dev Container extension if you -haven’t already, and/or to open the project in a container. If not, you -can manually select “Dev Containers: Reopen in Container” from View \> -Command Palette. +## Quickstart -### Install development dependencies manually +For comprehensive installation instructions, see the [documentation website](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/). -#### Python and Docker +### Python and Docker - [Python 3.12 or higher](https://www.python.org/downloads/) - [Docker and Docker Compose](https://docs.docker.com/get-docker/) -#### PostgreSQL headers +### PostgreSQL headers For Ubuntu/Debian: @@ -72,28 +67,7 @@ For Windows: - No installation required -#### Quarto CLI and Graphviz - -- [Quarto CLI](https://quarto.org/docs/get-started/) - -For macOS: - -``` bash -brew install graphviz -``` - -For Ubuntu/Debian: - -``` bash -sudo apt update && sudo apt install -y graphviz -``` - -For Windows: - -- Download and install from - [Graphviz.org](https://graphviz.org/download/#windows) - -#### Python dependencies +### Python dependencies 1. Install Poetry @@ -113,22 +87,17 @@ poetry install poetry shell ``` -(Note: You will need to activate the shell every time you open a new -terminal session. Alternatively, you can use the `poetry run` prefix -before other commands to run them without activating the shell.) +(Note: You will need to activate the shell every time you open a new terminal session. Alternatively, you can use the `poetry run` prefix before other commands to run them without activating the shell.) ### Set environment variables Copy .env.example to .env with `cp .env.example .env`. -Generate a 256 bit secret key with `openssl rand -base64 32` and paste -it into the .env file. +Generate a 256 bit secret key with `openssl rand -base64 32` and paste it into the .env file. Set your desired database name, username, and password in the .env file. -To use password recovery, register a [Resend](https://resend.com/) -account, verify a domain, get an API key, and paste the API key into the -.env file. +To use password recovery, register a [Resend](https://resend.com/) account, verify a domain, get an API key, and paste the API key into the .env file. ### Start development database @@ -138,8 +107,7 @@ docker compose up -d ### Run the development server -Make sure the development database is running and tables and default -permissions/roles are created first. +Make sure the development database is running and tables and default permissions/roles are created first. ``` bash uvicorn main:app --host 0.0.0.0 --port 8000 --reload @@ -153,22 +121,10 @@ Navigate to http://localhost:8000/ mypy . ``` -### Render the README - -When updating the documentation, remember to make changes in the -README.qmd file, not the README.md file. Then run the following command -to render the README.md file: - -``` bash -quarto render README.qmd -``` - ### Contributing -Fork the repository, create a new branch, make your changes, and submit -a pull request. +Your contributions are welcome! See the [issues page](https://github.com/promptly-technologies-llc/fastapi-jinja2-postgres-webapp/issues) for ideas. Fork the repository, create a new branch, make your changes, and submit a pull request. ### License -This project is licensed under the MIT License. See the LICENSE file for -more details. +This project is licensed under the MIT License. See the LICENSE file for more details. diff --git a/README.qmd b/README.qmd deleted file mode 100644 index aa9408f..0000000 --- a/README.qmd +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: "README" -format: gfm ---- - -## FastAPI, Jinja2, PostgreSQL Webapp - -![Screenshot of homepage](static/Screenshot.png) - -This project is still under development. - -### Architecture - -This application uses a Post-Redirect-Get (PRG) pattern. The user submits a form, which sends a POST request to a FastAPI endpoint on the server. The database is updated, and the user is redirected to a GET endpoint, which fetches the updated data and re-renders the Jinja2 page template with the new data. - -``` {python} -#| echo: false -#| include: false -from graphviz import Digraph - -dot = Digraph() - -dot.node('A', 'User submits form') -dot.node('B', 'HTML/JS form validation') -dot.node('C', 'Convert to Pydantic model') -dot.node('D', 'Optional custom validation') -dot.node('E', 'Update database') -dot.node('F', 'Middleware error handler') -dot.node('G', 'Render error template') -dot.node('H', 'Redirect to GET endpoint') -dot.node('I', 'Fetch updated data') -dot.node('J', 'Re-render Jinja2 page template') - -dot.edge('A', 'B') -dot.edge('B', 'A') -dot.edge('B', 'C', label='POST Request to FastAPI endpoint') -dot.edge('C', 'D') -dot.edge('C', 'F', label='RequestValidationError') -dot.edge('D', 'E', label='Valid data') -dot.edge('D', 'F', label='Custom Validation Error') -dot.edge('E', 'H', label='Data updated') -dot.edge('H', 'I') -dot.edge('I', 'J') -dot.edge('F', 'G') - -dot.render('static/webapp_flow', format='png', cleanup=True) -``` - -![Webapp Flow](static/webapp_flow.png) - -The advantage of the PRG pattern is that it is very straightforward to implement and keeps most of the rendering logic on the server side. The disadvantage is that it requires an extra round trip to the database to fetch the updated data, and re-rendering the entire page template may be less efficient than a partial page update on the client side. - -### Install development dependencies in a VSCode Dev Container - -If you use VSCode with Docker to develop in a container, the following VSCode Dev Container configuration will install all dependencies: - -``` json -{ - "name": "Python 3", - "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", - "postCreateCommand": "sudo apt update && sudo apt install -y python3-dev libpq-dev graphviz && pipx install poetry && poetry install && poetry shell", - "features": { - "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}, - "ghcr.io/rocker-org/devcontainer-features/quarto-cli:1": {} - } -} -``` - -Simply create a `.devcontainer` folder in the root of the project and add a `devcontainer.json` file in the folder with the above content. VSCode may prompt you to install the Dev Container extension if you haven't already, and/or to open the project in a container. If not, you can manually select "Dev Containers: Reopen in Container" from View > Command Palette. - -### Install development dependencies manually - -#### Python and Docker - -- [Python 3.12 or higher](https://www.python.org/downloads/) -- [Docker and Docker Compose](https://docs.docker.com/get-docker/) - -#### PostgreSQL headers - -For Ubuntu/Debian: - -``` bash -sudo apt update && sudo apt install -y python3-dev libpq-dev -``` - -For macOS: - -``` bash -brew install postgresql -``` - -For Windows: - -- No installation required - -#### Quarto CLI and Graphviz - -- [Quarto CLI](https://quarto.org/docs/get-started/) - - -For macOS: - -``` bash -brew install graphviz -``` - -For Ubuntu/Debian: - -``` bash -sudo apt update && sudo apt install -y graphviz -``` - -For Windows: - -- Download and install from [Graphviz.org](https://graphviz.org/download/#windows) - -#### Python dependencies - -1. Install Poetry - -``` bash -pipx install poetry -``` - -2. Install project dependencies - -``` bash -poetry install -``` - -3. Activate shell - -``` bash -poetry shell -``` - -(Note: You will need to activate the shell every time you open a new terminal session. Alternatively, you can use the `poetry run` prefix before other commands to run them without activating the shell.) - -### Set environment variables - -Copy .env.example to .env with `cp .env.example .env`. - -Generate a 256 bit secret key with `openssl rand -base64 32` and paste it into the .env file. - -Set your desired database name, username, and password in the .env file. - -To use password recovery, register a [Resend](https://resend.com/) account, verify a domain, get an API key, and paste the API key into the .env file. - -### Start development database - -``` bash -docker compose up -d -``` - -### Run the development server - -Make sure the development database is running and tables and default permissions/roles are created first. - -``` bash -uvicorn main:app --host 0.0.0.0 --port 8000 --reload -``` - -Navigate to http://localhost:8000/ - -### Lint types with mypy - -``` bash -mypy . -``` - -### Render the README - -When updating the documentation, remember to make changes in the README.qmd file, not the README.md file. Then run the following command to render the README.md file: - -``` bash -quarto render README.qmd -``` - -### Contributing - -Fork the repository, create a new branch, make your changes, and submit a pull request. - -### License - -This project is licensed under the MIT License. See the LICENSE file for more details. diff --git a/_quarto.yml b/_quarto.yml new file mode 100644 index 0000000..b9083ff --- /dev/null +++ b/_quarto.yml @@ -0,0 +1,27 @@ +project: + type: website + output-dir: _docs + +website: + title: "FastAPI Webapp Template" + navbar: + left: + - href: docs/index.qmd + text: Home + - href: docs/architecture.qmd + text: Architecture + - href: docs/installation.qmd + text: Installation + - href: docs/authentication.qmd + text: Authentication + - href: docs/customization.qmd + text: Customization + - href: docs/deployment.qmd + text: Deployment + - href: docs/contributing.qmd + text: Contributing +format: + html: + theme: cosmo + css: docs/static/styles.css + toc: true diff --git a/docs/architecture.qmd b/docs/architecture.qmd new file mode 100644 index 0000000..3849176 --- /dev/null +++ b/docs/architecture.qmd @@ -0,0 +1,44 @@ +--- +title: "Architecture" +--- + +## Architecture + +This application uses a Post-Redirect-Get (PRG) pattern. The user submits a form, which sends a POST request to a FastAPI endpoint on the server. The database is updated, and the user is redirected to a GET endpoint, which fetches the updated data and re-renders the Jinja2 page template with the new data. + +``` {python} +#| echo: false +#| include: false +from graphviz import Digraph + +dot = Digraph() + +dot.node('A', 'User submits form') +dot.node('B', 'HTML/JS form validation') +dot.node('C', 'Convert to Pydantic model') +dot.node('D', 'Optional custom validation') +dot.node('E', 'Update database') +dot.node('F', 'Middleware error handler') +dot.node('G', 'Render error template') +dot.node('H', 'Redirect to GET endpoint') +dot.node('I', 'Fetch updated data') +dot.node('J', 'Re-render Jinja2 page template') + +dot.edge('A', 'B') +dot.edge('B', 'A') +dot.edge('B', 'C', label='POST Request to FastAPI endpoint') +dot.edge('C', 'D') +dot.edge('C', 'F', label='RequestValidationError') +dot.edge('D', 'E', label='Valid data') +dot.edge('D', 'F', label='Custom Validation Error') +dot.edge('E', 'H', label='Data updated') +dot.edge('H', 'I') +dot.edge('I', 'J') +dot.edge('F', 'G') + +dot.render('static/webapp_flow', format='png', cleanup=True) +``` + +![Webapp Flow](static/webapp_flow.png) + +The advantage of the PRG pattern is that it is very straightforward to implement and keeps most of the rendering logic on the server side. The disadvantage is that it requires an extra round trip to the database to fetch the updated data, and re-rendering the entire page template may be less efficient than a partial page update on the client side. \ No newline at end of file diff --git a/docs/authentication.qmd b/docs/authentication.qmd new file mode 100644 index 0000000..6468023 --- /dev/null +++ b/docs/authentication.qmd @@ -0,0 +1,129 @@ +--- +title: "Authentication" +--- + +## Authentication Flow + +This template implements a comprehensive authentication system with security best practices: + +1. **Token Security**: + - JWT-based with separate access/refresh tokens + - Strict expiry times (30 min access, 30 day refresh) + - Token type validation + - HTTP-only cookies + - Secure flag enabled + - SameSite=strict restriction + +2. **Password Security**: + - Strong password requirements enforced + - Bcrypt hashing with random salt + - Password reset tokens are single-use + - Reset tokens have expiration + +3. **Cookie Security**: + - HTTP-only prevents JavaScript access + - Secure flag ensures HTTPS only + - Strict SameSite prevents CSRF + +4. **Error Handling**: + - Validation errors properly handled + - Security-related errors don't leak information + - Comprehensive error logging + +The diagrams below show the main authentication flows and security measures. + +### Registration and Login Flow + +``` {python} +#| echo: false +#| include: false +from graphviz import Digraph + +# Create graph for registration/login +auth = Digraph(name='auth_flow') +auth.attr(rankdir='TB') +auth.attr('node', shape='box', style='rounded') + +# Client-side nodes +with auth.subgraph(name='cluster_client') as client: + client.attr(label='Client') + client.node('register_form', 'Submit registration') + client.node('login_form', 'Submit login') + client.node('store_cookies', 'Store secure cookies') + +# Server-side nodes +with auth.subgraph(name='cluster_server') as server: + server.attr(label='Server') + # Registration path + server.node('validate_register', 'Validate registration data') + server.node('hash_new', 'Hash new password') + server.node('store_user', 'Store user in database') + + # Login path + server.node('validate_login', 'Validate login data') + server.node('verify_password', 'Verify password hash') + server.node('fetch_user', 'Fetch user from database') + + # Common path + server.node('generate_tokens', 'Generate JWT tokens') + +# Registration path +auth.edge('register_form', 'validate_register', 'POST /register') +auth.edge('validate_register', 'hash_new') +auth.edge('hash_new', 'store_user') +auth.edge('store_user', 'generate_tokens', 'Success') + +# Login path +auth.edge('login_form', 'validate_login', 'POST /login') +auth.edge('validate_login', 'fetch_user') +auth.edge('fetch_user', 'verify_password') +auth.edge('verify_password', 'generate_tokens', 'Success') + +# Common path +auth.edge('generate_tokens', 'store_cookies', 'Set-Cookie') + +auth.render('static/auth_flow', format='png', cleanup=True) +``` + +![Registration and Login Flow](static/auth_flow.png) + +### Password Reset Flow + +``` {python} +#| echo: false +#| include: false +from graphviz import Digraph + +# Create graph for password reset +reset = Digraph(name='reset_flow') +reset.attr(rankdir='TB') +reset.attr('node', shape='box', style='rounded') + +# Client-side nodes +reset.attr(label='Client') +reset.node('forgot', 'User submits forgot password form') +reset.node('reset', 'User submits reset password form') +reset.node('email_client', 'User clicks reset link') + +# Server-side nodes +reset.attr(label='Server') +reset.node('validate', 'Validation') +reset.node('token_gen', 'Generate reset token') +reset.node('hash', 'Hash password') +reset.node('email_server', 'Send email with Resend') +reset.node('db', 'Database', shape='cylinder') + +# Add edges with labels +reset.edge('forgot', 'token_gen', 'POST') +reset.edge('token_gen', 'db', 'Store') +reset.edge('token_gen', 'email_server', 'Add email/token as URL parameter') +reset.edge('email_server', 'email_client') +reset.edge('email_client', 'reset', 'Set email/token as form input') +reset.edge('reset', 'validate', 'POST') +reset.edge('validate', 'hash') +reset.edge('hash', 'db', 'Update') + +reset.render('static/reset_flow', format='png', cleanup=True) +``` + +![Password Reset Flow](static/reset_flow.png) diff --git a/docs/contributing.qmd b/docs/contributing.qmd new file mode 100644 index 0000000..b0e41ae --- /dev/null +++ b/docs/contributing.qmd @@ -0,0 +1,33 @@ +--- +title: "Contributing" +--- + +## Contributors + +Fork the repository, create a new branch, make your changes, and submit a pull request. + +### Render the documentation + +The documentation is rendered with [Quarto](https://quarto.org/docs/). Make changes to the `.qmd` files in the `docs` folder. Then run the following command to render: + +``` bash +quarto render +``` + +## Maintainers + +### Increment the version + +Run the following command to increment the version: + +``` bash +poetry version patch minor +``` + +### Publish the documentation + +To publish the documentation to GitHub Pages, run the following command: + +``` bash +quarto publish gh-pages +``` diff --git a/docs/customization.qmd b/docs/customization.qmd new file mode 100644 index 0000000..2e42344 --- /dev/null +++ b/docs/customization.qmd @@ -0,0 +1,5 @@ +--- +title: "Customization" +--- + +## Under construction \ No newline at end of file diff --git a/docs/deployment.qmd b/docs/deployment.qmd new file mode 100644 index 0000000..6233dd9 --- /dev/null +++ b/docs/deployment.qmd @@ -0,0 +1,5 @@ +--- +title: "Deployment" +--- + +## Under construction \ No newline at end of file diff --git a/docs/index.qmd b/docs/index.qmd new file mode 100644 index 0000000..2974b31 --- /dev/null +++ b/docs/index.qmd @@ -0,0 +1,45 @@ +--- +title: "FastAPI, Jinja2, PostgreSQL Webapp Template" +--- + +![Screenshot of homepage](static/Screenshot.png) + +## Features + +This template combines three of the most lightweight and performant open-source web development frameworks into a customizable webapp template with: + +- Pure Python backend +- Low-Javascript frontend +- Powerful, easy-to-manage database layer + +The template also includes full-featured secure auth with: + +- Token-based authentication +- Password recovery flow +- Role-based access control system + +## Design Philosophy + +The design philosophy of the template is to prefer low-level, best-in-class open-source frameworks that offer flexibility, scalability, and performance without vendor-lock-in. You'll find the template amazingly easy not only to understand and customize, but also to deploy to any major cloud hosting platform. + +## Tech Stack + +**Core frameworks:** +- [FastAPI](https://fastapi.tiangolo.com/): scalable, high-performance, type-annotated Python web backend framework +- [PostgreSQL](https://www.postgresql.org/): the world's most advanced open-source database engine +- [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/): frontend HTML templating engine +- [SQLModel](https://sqlmodel.tiangolo.com/): easy-to-use Python ORM + +**Additional technologies:** +- [Poetry](https://python-poetry.org/): Python dependency manager +- [Pytest](https://docs.pytest.org/en/7.4.x/): testing framework +- [Docker](https://www.docker.com/): development containerization +- [Github Actions](https://docs.github.com/en/actions): CI/CD pipeline +- [Quarto](https://quarto.org/docs/): simple documentation website renderer +- [MyPy](https://mypy.readthedocs.io/en/stable/): static type checker for Python +- [Bootstrap](https://getbootstrap.com/): HTML/CSS styler +- [Resend](https://resend.com/): zero- or low-cost email service used for password recovery + +## License + +This project is developed and maintained by Promptly Technologies, LLC and licensed under the open-source MIT License. See the LICENSE file for more details. diff --git a/docs/installation.qmd b/docs/installation.qmd new file mode 100644 index 0000000..f36bc95 --- /dev/null +++ b/docs/installation.qmd @@ -0,0 +1,121 @@ +--- +title: "Installation" +--- + +## Install development dependencies in a VSCode Dev Container + +If you use VSCode with Docker to develop in a container, the following VSCode Dev Container configuration will install all dependencies: + +``` json +{ + "name": "Python 3", + "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", + "postCreateCommand": "sudo apt update && sudo apt install -y python3-dev libpq-dev graphviz && pipx install poetry && poetry install && poetry shell", + "features": { + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}, + "ghcr.io/rocker-org/devcontainer-features/quarto-cli:1": {} + } +} +``` + +Simply create a `.devcontainer` folder in the root of the project and add a `devcontainer.json` file in the folder with the above content. VSCode may prompt you to install the Dev Container extension if you haven't already, and/or to open the project in a container. If not, you can manually select "Dev Containers: Reopen in Container" from View > Command Palette. + +## Install development dependencies manually + +### Python and Docker + +- [Python 3.12 or higher](https://www.python.org/downloads/) +- [Docker and Docker Compose](https://docs.docker.com/get-docker/) + +### PostgreSQL headers + +For Ubuntu/Debian: + +``` bash +sudo apt update && sudo apt install -y python3-dev libpq-dev +``` + +For macOS: + +``` bash +brew install postgresql +``` + +For Windows: + +- No installation required + +### Quarto CLI and Graphviz + +- [Quarto CLI](https://quarto.org/docs/get-started/) + + +For macOS: + +``` bash +brew install graphviz +``` + +For Ubuntu/Debian: + +``` bash +sudo apt update && sudo apt install -y graphviz +``` + +For Windows: + +- Download and install from [Graphviz.org](https://graphviz.org/download/#windows) + +### Python dependencies + +1. Install Poetry + +``` bash +pipx install poetry +``` + +2. Install project dependencies + +``` bash +poetry install +``` + +3. Activate shell + +``` bash +poetry shell +``` + +(Note: You will need to activate the shell every time you open a new terminal session. Alternatively, you can use the `poetry run` prefix before other commands to run them without activating the shell.) + +## Set environment variables + +Copy .env.example to .env with `cp .env.example .env`. + +Generate a 256 bit secret key with `openssl rand -base64 32` and paste it into the .env file. + +Set your desired database name, username, and password in the .env file. + +To use password recovery, register a [Resend](https://resend.com/) account, verify a domain, get an API key, and paste the API key into the .env file. + +## Start development database + +``` bash +docker compose up -d +``` + +## Run the development server + +Make sure the development database is running and tables and default permissions/roles are created first. + +``` bash +uvicorn main:app --host 0.0.0.0 --port 8000 --reload +``` + +Navigate to http://localhost:8000/ + +## Lint types with mypy + +``` bash +mypy . +``` diff --git a/static/Screenshot.png b/docs/static/Screenshot.png similarity index 100% rename from static/Screenshot.png rename to docs/static/Screenshot.png diff --git a/docs/static/auth_flow.png b/docs/static/auth_flow.png new file mode 100644 index 0000000..0eb77dc Binary files /dev/null and b/docs/static/auth_flow.png differ diff --git a/docs/static/reset_flow.png b/docs/static/reset_flow.png new file mode 100644 index 0000000..a3cf6b3 Binary files /dev/null and b/docs/static/reset_flow.png differ diff --git a/docs/static/styles.css b/docs/static/styles.css new file mode 100644 index 0000000..2ddf50c --- /dev/null +++ b/docs/static/styles.css @@ -0,0 +1 @@ +/* css styles */ diff --git a/static/webapp_flow.png b/docs/static/webapp_flow.png similarity index 100% rename from static/webapp_flow.png rename to docs/static/webapp_flow.png diff --git a/tests/form_submissions.jsonl b/tests/form_submissions.jsonl deleted file mode 100644 index f540c9c..0000000 --- a/tests/form_submissions.jsonl +++ /dev/null @@ -1,4 +0,0 @@ -{"timestamp": "2024-11-11T17:42:32.634039", "path": "/test-validation", "method": "POST", "form_data": {"email": "chris@example.com", "age": "", "name": ""}, "headers": {"host": "localhost:8000", "connection": "keep-alive", "content-length": "36", "cache-control": "max-age=0", "sec-ch-ua": "\"Chromium\";v=\"130\", \"Google Chrome\";v=\"130\", \"Not?A_Brand\";v=\"99\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "origin": "http://localhost:8000", "content-type": "application/x-www-form-urlencoded", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "sec-fetch-site": "same-origin", "sec-fetch-mode": "navigate", "sec-fetch-user": "?1", "sec-fetch-dest": "document", "referer": "http://localhost:8000/test-validation", "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "en-US,en;q=0.9,fr;q=0.8", "cookie": "__stripe_mid=559b6231-5ab5-4bec-9100-81bff721873438bf0f; csrftoken=ikfDDZkDSqCCXeKjXzIlIzcvhXzimMj2; ajs_anonymous_id=48597e31-0e27-4b5d-bc9a-e669c0d36a0b; KB_RM=cc79b71c526e2fb03d51a6df5e3eecf95bd2174d6adc6e5e57745191f56e803b%7C295a10baa287d3d2d560c46d51309686a72d120f40f06e79a59056a6cfd6"}, "client": "127.0.0.1"} -{"timestamp": "2024-11-11T17:44:50.701282", "path": "/test-validation", "method": "POST", "form_data": {"email": "chris@example.com", "age": "5", "name": "Chris"}, "headers": {"host": "localhost:8000", "connection": "keep-alive", "content-length": "42", "cache-control": "max-age=0", "sec-ch-ua": "\"Chromium\";v=\"130\", \"Google Chrome\";v=\"130\", \"Not?A_Brand\";v=\"99\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "origin": "http://localhost:8000", "content-type": "application/x-www-form-urlencoded", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "sec-fetch-site": "same-origin", "sec-fetch-mode": "navigate", "sec-fetch-user": "?1", "sec-fetch-dest": "document", "referer": "http://localhost:8000/test-validation", "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "en-US,en;q=0.9,fr;q=0.8", "cookie": "__stripe_mid=559b6231-5ab5-4bec-9100-81bff721873438bf0f; csrftoken=ikfDDZkDSqCCXeKjXzIlIzcvhXzimMj2; ajs_anonymous_id=48597e31-0e27-4b5d-bc9a-e669c0d36a0b; KB_RM=cc79b71c526e2fb03d51a6df5e3eecf95bd2174d6adc6e5e57745191f56e803b%7C295a10baa287d3d2d560c46d51309686a72d120f40f06e79a59056a6cfd6"}, "client": "127.0.0.1"} -{"timestamp": "2024-11-11T17:45:02.822269", "path": "/test-validation", "method": "POST", "form_data": {"email": "not-an-email", "age": "5", "name": "Chris"}, "headers": {"host": "localhost:8000", "connection": "keep-alive", "content-length": "35", "cache-control": "max-age=0", "sec-ch-ua": "\"Chromium\";v=\"130\", \"Google Chrome\";v=\"130\", \"Not?A_Brand\";v=\"99\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "origin": "http://localhost:8000", "content-type": "application/x-www-form-urlencoded", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "sec-fetch-site": "same-origin", "sec-fetch-mode": "navigate", "sec-fetch-user": "?1", "sec-fetch-dest": "document", "referer": "http://localhost:8000/test-validation", "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "en-US,en;q=0.9,fr;q=0.8", "cookie": "__stripe_mid=559b6231-5ab5-4bec-9100-81bff721873438bf0f; csrftoken=ikfDDZkDSqCCXeKjXzIlIzcvhXzimMj2; ajs_anonymous_id=48597e31-0e27-4b5d-bc9a-e669c0d36a0b; KB_RM=cc79b71c526e2fb03d51a6df5e3eecf95bd2174d6adc6e5e57745191f56e803b%7C295a10baa287d3d2d560c46d51309686a72d120f40f06e79a59056a6cfd6"}, "client": "127.0.0.1"} -{"timestamp": "2024-11-11T17:45:50.523678", "path": "/test-validation", "method": "POST", "form_data": {"email": "chris@example.com", "age": "5", "name": ""}, "headers": {"host": "localhost:8000", "connection": "keep-alive", "content-length": "37", "cache-control": "max-age=0", "sec-ch-ua": "\"Chromium\";v=\"130\", \"Google Chrome\";v=\"130\", \"Not?A_Brand\";v=\"99\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "origin": "http://localhost:8000", "content-type": "application/x-www-form-urlencoded", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "sec-fetch-site": "same-origin", "sec-fetch-mode": "navigate", "sec-fetch-user": "?1", "sec-fetch-dest": "document", "referer": "http://localhost:8000/test-validation", "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "en-US,en;q=0.9,fr;q=0.8", "cookie": "__stripe_mid=559b6231-5ab5-4bec-9100-81bff721873438bf0f; csrftoken=ikfDDZkDSqCCXeKjXzIlIzcvhXzimMj2; ajs_anonymous_id=48597e31-0e27-4b5d-bc9a-e669c0d36a0b; KB_RM=cc79b71c526e2fb03d51a6df5e3eecf95bd2174d6adc6e5e57745191f56e803b%7C295a10baa287d3d2d560c46d51309686a72d120f40f06e79a59056a6cfd6"}, "client": "127.0.0.1"}