Skip to content

Merging #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
c8cb134
Minor change to Architecture header and alt text
chriscarrollsmith Nov 19, 2024
e974d00
Make features display as bullet points
chriscarrollsmith Nov 19, 2024
da6f2b8
Minor changes to README
chriscarrollsmith Nov 19, 2024
ed2ab81
Minor
chriscarrollsmith Nov 19, 2024
1d4385b
Merge branch 'main' into 28-populate-customization-page-on-documentat…
chriscarrollsmith Nov 19, 2024
dde68d9
Update user.py
AkanshuS Nov 20, 2024
60468fe
Update user.py
AkanshuS Nov 20, 2024
8a62883
Logs Out
AkanshuS Nov 22, 2024
acec7d0
Added notes on error handling patterns to architecture section of docs
chriscarrollsmith Nov 23, 2024
ae0f9f6
Merge pull request #42 from Promptly-Technologies-LLC/8-try-using-fas…
chriscarrollsmith Nov 23, 2024
78dd03b
Styled table in documentation for better borders and column widths
chriscarrollsmith Nov 23, 2024
c436e54
Merge pull request #43 from Promptly-Technologies-LLC/8-try-using-fas…
chriscarrollsmith Nov 23, 2024
18d57ae
Bump tornado from 6.4.1 to 6.4.2
dependabot[bot] Nov 23, 2024
bebe165
Merge pull request #44 from Promptly-Technologies-LLC/dependabot/pip/…
chriscarrollsmith Nov 23, 2024
3356735
Merge latest changes from main
chriscarrollsmith Nov 23, 2024
f3ce82d
Added customization.qmd content on project design patterns
chriscarrollsmith Nov 24, 2024
110c25a
Added note on client-side form validation
chriscarrollsmith Nov 24, 2024
8bac936
Database schema ERD
chriscarrollsmith Nov 24, 2024
2259f11
Merge pull request #47 from Promptly-Technologies-LLC/28-populate-cus…
chriscarrollsmith Nov 24, 2024
9beee0e
Customization adjustments
chriscarrollsmith Nov 24, 2024
4ea6509
Don't return users with deleted column set to True
chriscarrollsmith Nov 24, 2024
30551b7
Use a mock to send email in unit test
chriscarrollsmith Nov 24, 2024
e058660
Revert "Use a mock to send email in unit test"
chriscarrollsmith Nov 24, 2024
b603e73
Revert "Don't return users with deleted column set to True"
chriscarrollsmith Nov 24, 2024
fbcb550
Remove deleted attributes in the database models and actually delete …
chriscarrollsmith Nov 24, 2024
99a18ad
Merge branch 'main' of https://github.com/Promptly-Technologies-LLC/f…
chriscarrollsmith Nov 24, 2024
5805368
Merge branch 'main' into 11-fix-delete-account-flow
chriscarrollsmith Nov 24, 2024
9d8c5b3
Added tests for a couple read routes in main.py
chriscarrollsmith Nov 25, 2024
4ed648a
Added request models for user profile update
chriscarrollsmith Nov 25, 2024
7ea6d51
Fixed a SQLAlchemy model problem where cascade was going the wrong way
chriscarrollsmith Nov 25, 2024
3cc67dd
Redirect unauthed users with 303 rather than 307 so POST requests are…
chriscarrollsmith Nov 25, 2024
bf63caf
New authentication error handler in main.py
chriscarrollsmith Nov 25, 2024
2fe1570
Fix misnamed endpoint in profile.html template
chriscarrollsmith Nov 25, 2024
a6fd524
New test fixtures for authed and unauthed clients, tests for user end…
chriscarrollsmith Nov 25, 2024
26073b4
Fixed a type lint error and updated pytest docs
chriscarrollsmith Nov 25, 2024
e1ee3d7
Adjusted password strength regex to allow forward slashes
chriscarrollsmith Nov 25, 2024
b165e0a
Document that we need to use -v flag when running docker compose down
chriscarrollsmith Nov 25, 2024
ac25378
Added llms.txt generation script in index.qmd
chriscarrollsmith Nov 26, 2024
4ab25bb
Merge pull request #49 from Promptly-Technologies-LLC/45-implement-ll…
chriscarrollsmith Nov 26, 2024
5522fac
Merged changes from remote
chriscarrollsmith Nov 26, 2024
476f69c
Reorganize database schema to allow 3-way user-org-role relationship
chriscarrollsmith Nov 27, 2024
5225799
Perform org operations only if the user has permissions
chriscarrollsmith Nov 27, 2024
11320b3
Moved role.py validation logic to request models and used custom HTTP…
chriscarrollsmith Nov 27, 2024
636bd45
Added authenticated user dependencies
chriscarrollsmith Nov 27, 2024
49b4468
Merge pull request #48 from Promptly-Technologies-LLC/11-fix-delete-a…
chriscarrollsmith Nov 28, 2024
a8bee02
Replaced role and organization endpoints with helper functions to ret…
chriscarrollsmith Nov 28, 2024
4542f23
Added preliminary organizations.html component, moved some helpers to…
chriscarrollsmith Nov 28, 2024
2bd854e
Merge branch 'main' of https://github.com/Promptly-Technologies-LLC/f…
chriscarrollsmith Nov 28, 2024
ad71d96
Merge branch 'main' into 1-finish-implementing-roleorg-system
chriscarrollsmith Nov 28, 2024
eb58d4d
Default permissions will be global, but default roles will be organiz…
chriscarrollsmith Nov 28, 2024
f99af68
Refactored db.py
chriscarrollsmith Nov 28, 2024
b959ff0
Passing tests for utils/db.py helpers
chriscarrollsmith Nov 28, 2024
890e7cc
Test db setup helpers
chriscarrollsmith Nov 28, 2024
91bc397
Correct association table relationships to remove overlaps and silenc…
chriscarrollsmith Nov 29, 2024
a373df7
Eagerly load roles, orgs, and permissions with user in endpoints that…
chriscarrollsmith Nov 29, 2024
747352b
Org creation now works correctly! :-O
chriscarrollsmith Nov 29, 2024
3f13345
Added page for managing an organization
chriscarrollsmith Nov 30, 2024
2587dcf
Tests of cascade delete behaviors pass
chriscarrollsmith Nov 30, 2024
c2d63c6
Moved password hash to a separate database model, resolved some mypy …
chriscarrollsmith Dec 1, 2024
6b48566
Massively imporved/fixed role.py
chriscarrollsmith Dec 1, 2024
5ba8044
Use POST for all routes (since HTML forms only support GET and POST)
chriscarrollsmith Dec 1, 2024
4224b22
Re-render database model diagram and update LLMs.txt
chriscarrollsmith Dec 1, 2024
0ed821a
Merge pull request #53 from Promptly-Technologies-LLC/1-finish-implem…
chriscarrollsmith Dec 1, 2024
d03f117
Added cursorrules as a static asset in addition to full docs
chriscarrollsmith Dec 1, 2024
c62df42
Merge pull request #58 from Promptly-Technologies-LLC/45-implement-ll…
chriscarrollsmith Dec 1, 2024
722240b
copy LLMs.txt and documentation.txt into website output dir
chriscarrollsmith Dec 1, 2024
47bcdd1
Correct file paths for serving and rendering static text files
chriscarrollsmith Dec 2, 2024
38d9852
Abbreviated project docs in llms.txt
chriscarrollsmith Dec 2, 2024
67bf68d
Re-render of documentation.txt
chriscarrollsmith Dec 2, 2024
7fcedf9
Minor typo/wording fixes
chriscarrollsmith Dec 2, 2024
2ff7be3
Documented VSCode configuration
chriscarrollsmith Dec 2, 2024
47ea2bc
Bump pyjwt from 2.10.0 to 2.10.1
dependabot[bot] Dec 2, 2024
5772499
Used Jinja2 templates to generate the reset email
chriscarrollsmith Dec 2, 2024
ef35a9c
Merge pull request #60 from Promptly-Technologies-LLC/dependabot/pip/…
chriscarrollsmith Dec 2, 2024
010170c
Add a dummy resend key to Github workflow so tests will pass
chriscarrollsmith Dec 2, 2024
e763843
Corrected URL encoding in the email template and updated unit tests a…
chriscarrollsmith Dec 2, 2024
0d01c92
Merge pull request #61 from Promptly-Technologies-LLC/25-use-a-templa…
chriscarrollsmith Dec 2, 2024
0102582
Avatar upload + dedicated endpoint for serving binary images from dat…
chriscarrollsmith Dec 3, 2024
c618c6a
Added note on psycopg2 installation error
chriscarrollsmith Dec 5, 2024
90626a2
Generated tests for organization and role using pytest.
AkanshuSrivastav Dec 13, 2024
4d8a304
Merge pull request #63 from Promptly-Technologies-LLC/main
AkanshuS Dec 13, 2024
56b4b3c
Adjusted test suite and fixed list access mistake in read_organization
chriscarrollsmith Dec 13, 2024
0679810
Basic tests of create_role endpoint
chriscarrollsmith Dec 13, 2024
8a3ff74
Fixed type lint error
chriscarrollsmith Dec 15, 2024
fea83dd
Merge pull request #65 from Promptly-Technologies-LLC/21-extend-the-t…
chriscarrollsmith Dec 15, 2024
fa3fd95
Migrated from poetry to uv for dep mgmt
chriscarrollsmith Dec 15, 2024
dcddb9d
Merge pull request #66 from Promptly-Technologies-LLC/64-switch-from-…
chriscarrollsmith Dec 15, 2024
474ac15
Merge branch 'main' into 12-allow-upload-of-avatar-images
chriscarrollsmith Dec 15, 2024
f947dc9
Remove URL upload of avatar
chriscarrollsmith Dec 15, 2024
812560a
Merge branch 'main' into 12-allow-upload-of-avatar-images
chriscarrollsmith Dec 15, 2024
deed7a3
Updated data model documentation
chriscarrollsmith Dec 15, 2024
6e6b13b
Merge pull request #62 from Promptly-Technologies-LLC/12-allow-upload…
chriscarrollsmith Dec 15, 2024
a22d44c
First pass at user profile image validation
chriscarrollsmith Dec 15, 2024
b453116
Added server-side image validation and tests
chriscarrollsmith Dec 16, 2024
96ecbcd
Display info about image constraints on frontend
chriscarrollsmith Dec 16, 2024
8173db2
Client-side validation of uploaded profile image
chriscarrollsmith Dec 16, 2024
7e315a6
Merge pull request #69 from Promptly-Technologies-LLC/68-validation-f…
chriscarrollsmith Dec 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ jobs:
fail-fast: false
matrix:
python-version: ["3.12"]
poetry-version: [latest]
os: [ubuntu-latest]

runs-on: ${{ matrix.os }}
Expand All @@ -35,15 +34,16 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install and configure Poetry
uses: snok/install-poetry@v1

- name: Install project
run: poetry install
run: uv sync --all-extras --dev

- name: Set env variables for pytest
run: |
Expand All @@ -54,6 +54,7 @@ jobs:
echo "DB_NAME=test_db" >> $GITHUB_ENV
echo "SECRET_KEY=$(openssl rand -base64 32)" >> $GITHUB_ENV
echo "BASE_URL=http://localhost:8000" >> $GITHUB_ENV
echo "RESEND_API_KEY=resend_api_key" >> $GITHUB_ENV

- name: Verify environment variables
run: |
Expand All @@ -63,10 +64,11 @@ jobs:
[ -n "$DB_HOST" ] && \
[ -n "$DB_PORT" ] && \
[ -n "$DB_NAME" ] && \
[ -n "$SECRET_KEY" ]
[ -n "$SECRET_KEY" ] && \
[ -n "$RESEND_API_KEY" ]

- name: Run type checking with mypy
run: poetry run mypy .
run: uv run mypy .

- name: Run tests with pytest
run: poetry run pytest -s tests/
run: uv run pytest tests/
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ __pycache__
/.quarto/
_docs/
.pytest_cache/
.mypy_cache/
.mypy_cache/
.cursorrules
85 changes: 61 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ to deploy to any major cloud hosting platform.

**Additional technologies:**

- [Poetry](https://python-poetry.org/): Python dependency manager
- [uv](https://docs.astral.sh/uv/): 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
Expand All @@ -72,56 +72,73 @@ to deploy to any major cloud hosting platform.
For comprehensive installation instructions, see the [installation
page](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/installation.html).

### Python and Docker
### uv

- [Python 3.12 or higher](https://www.python.org/downloads/)
- [Docker and Docker Compose](https://docs.docker.com/get-docker/)
MacOS and Linux:

### PostgreSQL headers
``` bash
wget -qO- https://astral.sh/uv/install.sh | sh
```

For Ubuntu/Debian:
Windows:

``` bash
sudo apt update && sudo apt install -y python3-dev libpq-dev
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```

For macOS:
See the [uv installation
docs](https://docs.astral.sh/uv/getting-started/installation/) for more
information.

### Python

Install Python 3.12 or higher from either the official [downloads
page](https://www.python.org/downloads/) or using uv:

``` bash
brew install postgresql
# Installs the latest version
uv python install
```

For Windows:
### Docker and Docker Compose

- No installation required
Install Docker Desktop and Coker Compose for your operating system by
following the [instructions in the
documentation](https://docs.docker.com/compose/install/).

### Python dependencies
### PostgreSQL headers

1. Install Poetry
For Ubuntu/Debian:

``` bash
pipx install poetry
sudo apt update && sudo apt install -y python3-dev libpq-dev
```

2. Install project dependencies
For macOS:

``` bash
poetry install
brew install postgresql
```

3. Activate shell
For Windows:

- No installation required

### Python dependencies

From the root directory, run:

``` bash
poetry shell
uv venv
uv sync
```

(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.)
This will create an in-project virtual environment and install all
dependencies.

### Set environment variables

Copy .env.example to .env with `cp .env.example .env`.
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.
Expand All @@ -134,6 +151,9 @@ account, verify a domain, get an API key, and paste the API key into the

### Start development database

To start the development database, run the following command in your
terminal from the root directory:

``` bash
docker compose up -d
```
Expand All @@ -155,14 +175,31 @@ Navigate to http://localhost:8000/
mypy .
```

### Contributing
## Developing with LLMs

In line with the [llms.txt standard](https://llmstxt.org/), we have
provided a Markdown-formatted prompt—designed to help LLM agents
understand how to work with this template—as a text file:
[llms.txt](docs/static/llms.txt).

One use case for this file, if using the Cursor IDE, is to rename it to
`.cursorrules` and place it in your project directory (see the [Cursor
docs](https://docs.cursor.com/context/rules-for-ai) on this for more
information). Alternatively, you could use it as a custom system prompt
in the web interface for ChatGPT, Claude, or the LLM of your choice.

We have also exposed the full Markdown-formatted project documentation
as a [single text file](docs/static/documentation.txt) for easy
downloading and embedding for RAG workflows.

## Contributing

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
## License

This project is created and maintained by [Promptly Technologies,
LLC](https://promptlytechnologies.com/) and licensed under the MIT
Expand Down
101 changes: 100 additions & 1 deletion docs/architecture.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,103 @@ dot.render('static/data_flow', format='png', cleanup=True)

![Data flow diagram](static/data_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.
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.

## Form validation flow

We've experimented with several approaches to validating form inputs in the FastAPI endpoints.

### Objectives

Ideally, on an invalid input, we would redirect the user back to the form, preserving their inputs and displaying an error message about which input was invalid.

This would keep the error handling consistent with the PRG pattern described in the [Architecture](https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/docs/architecture) section of this documentation.

To keep the code DRY, we'd also like to handle such validation with Pydantic dependencies, Python exceptions, and exception-handling middleware as much as possible.

### Obstacles

One challenge is that if we redirect back to the page with the form, the page is re-rendered with empty form fields.

This can be overcome by passing the inputs from the request as context variables to the template.

But that's a bit clunky, because then we have to support form-specific context variables in every form page and corresponding GET endpoint.

Also, we have to:

1. access the request object (which is not by default available to our middleware), and
2. extract the form inputs (at least one of which is invalid in this error case), and
3. pass the form inputs to the template (which is a bit challenging to do in a DRY way since there are different sets of form inputs for different forms).

Solving these challenges is possible, but gets high-complexity pretty quickly.

### Approaches

The best solution, I think, is to use really robust client-side form validation to prevent invalid inputs from being sent to the server in the first place. That makes it less important what we do on the server side, although we still need to handle the server-side error case as a backup in the event that something slips past our validation on the client side.

Here are some patterns we've considered for server-side error handling:

<style>
.styled-table, .styled-table th, .styled-table td {
border: 1px solid black;
padding: 8px;
border-collapse: collapse;
}

.styled-table th:nth-child(1) { width: 50%; }
.styled-table th:nth-child(2),
.styled-table th:nth-child(3),
.styled-table th:nth-child(4) { width: 15%; }
.styled-table th:nth-child(5) { width: 10%; }
</style>

<table class="styled-table">
<thead>
<tr>
<th>Approach</th>
<th>Returns to same page</th>
<th>Preserves form inputs</th>
<th>Follows PRG pattern</th>
<th>Complexity</th>
</tr>
</thead>
<tbody>
<tr>
<td>Validate with Pydantic dependency, catch and redirect from middleware (with exception message as context) to an error page with "go back" button</td>
<td>No</td>
<td>Yes</td>
<td>Yes</td>
<td>Low</td>
</tr>
<tr>
<td>Validate in FastAPI endpoint function body, redirect to origin page with error message query param</td>
<td>Yes</td>
<td>No</td>
<td>Yes</td>
<td>Medium</td>
</tr>
<tr>
<td>Validate in FastAPI endpoint function body, redirect to origin page with error message query param and form inputs as context so we can re-render page with original form inputs</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>High</td>
</tr>
<tr>
<td>Validate with Pydantic dependency, use session context to get form inputs from request, redirect to origin page from middleware with exception message and form inputs as context so we can re-render page with original form inputs</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>High</td>
</tr>
<tr>
<td>Validate in either Pydantic dependency or function endpoint body and directly return error message or error toast HTML partial in JSON, then mount error toast with HTMX or some simple layout-level Javascript</td>
<td>Yes</td>
<td>Yes</td>
<td>No</td>
<td>Low</td>
</tr>
</tbody>
</table>

Presently this template primarily uses option 1 but also supports option 2. Ultimately, I think option 5 will be preferable; support for that [is planned](https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/issues/5) for a future update or fork of this template.
Loading
Loading