Skip to content

Commit 69b12cb

Browse files
Merge branch 'main' into modal
2 parents c478a2a + 5ef0b49 commit 69b12cb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2284
-327
lines changed

.cursor/rules/general.mdc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
description: General guidelines
3+
globs:
4+
alwaysApply: true
5+
---
6+
This project uses `uv` for dependency management. To add or remove a dependency, use `uv add <packagename>` or `uv remove <packagename>`. To update a dependency to the latest version, use `uv lock --upgrade-package <packagename>` For development dependencies, add the `--group dev` flag to these commands. Dependencies can be installed with `uv sync`.
7+
8+
When building out features, always keep changes atomic and make sure to write and run tests. To run tests, use:
9+
10+
```bash
11+
uv run pytest tests # or the path to a specific test file
12+
```
13+
14+
All code should be rigorously type hinted so as to pass a static type check with `mypy`. To run a `mypy` check, use:
15+
16+
```bash
17+
uv run mypy .
18+
```

.cursor/rules/routers.mdc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
description: Testing FastAPI routes
3+
globs: routers/**/*.py
4+
alwaysApply: false
5+
---
6+
Here are the five most critical patterns to maintain consistency when adding a new router:
7+
8+
1. **Authentication & Dependency Injection**
9+
- Import `get_authenticated_user` from `utils.core.dependencies` and include `user: User = Depends(get_authenticated_user)` in the arguments of routes requiring authentication
10+
- Similarly, use the `get_optional_user` dependency for public routes with potential auth status
11+
12+
2. **Validation Patterns**
13+
- Validate requests with type hints in the route signature
14+
- Use `Annotated[str, Form()]` for complex request validation cases involving form data
15+
- Perform business logic validation checks in the route body, raising a custom HTTPException defined in `exceptions/http_exceptions.py`
16+
- Note that all exceptions will be handled by middleware in `main.py` that renders an error template
17+
18+
3. **Permission System**
19+
- Use `user.has_permission(ValidPermissions.X, resource)` for authorization
20+
- Validate organization membership through role relationships
21+
- Check permissions at both route and template levels via `user_permissions`
22+
23+
4. **Database & Transaction Patterns**
24+
- Inject session via `Depends(get_session)` from `utils/core/dependencies.py`
25+
- Commit after writes and refresh objects where needed
26+
- Use `selectinload` for eager loading relationships
27+
- Follow PRG pattern with RedirectResponse after mutations
28+
29+
5. **Templating**
30+
- Use Jinja templates from the `/templates` directory in GET routes, and always pass `request` and `user` objects as as context

.cursor/rules/routers_tests.mdc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
description:
3+
globs: tests/routers/**/*.py
4+
alwaysApply: false
5+
---
6+
# Setting test expectations regarding HTTP status codes
7+
8+
Since this is a FastAPI web application, test logic for API endpoints often involves checking status codes. Remember, when making a request to an API endpoint, you should specify the `follow_redirects` parameter. With `follow_redirects=False`, the response code will often be `303`; otherwise it will be the response code of the route we've redirected to. We mostly use `follow_redirects=False` so as to test routes in isolation, but there may be test cases where following the redirect is more appropriate.
9+
10+
When checking status codes, think carefully to make sure the expected status code is the most appropriate to the situation.

.cursor/rules/sqlmodel.mdc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
description: Satisfying the type checker when working with SQLModel
3+
globs:
4+
alwaysApply: false
5+
---
6+
Complex SQLModel queries sometimes cause the type checker to choke, even though the queries are valid.
7+
8+
For instance, this error sometimes arises when using `selectinload`:
9+
10+
'error: Argument 1 to "selectinload" has incompatible type "SomeModel"; expected "Literal['*'] | QueryableAttribute[Any]"'
11+
12+
The solution is to explicitly coerce the argument to the appropriate SQLModel type.
13+
14+
E.g., we can resolve the error above by casting the eager-loaded relationship to InstrumentedAttribute:
15+
16+
```python
17+
session.exec(select(SomeOtherModel).options(selectinload(cast(InstrumentedAttribute, SomeOtherModel.some_model))))
18+
```
19+
20+
Similarly, sometimes we get type checker errors when using `delete` or comparison operators like `in_`:
21+
22+
'error: Item "int" of "Optional[int]" has no attribute "in_"'
23+
24+
These can be resolved by wrapping the column in `col` to let the type checker know these are column objects:
25+
26+
```python
27+
session.exec(select(SomeModel).where(col(SomeModel.id).in_([1,2])))
28+
```

.cursor/rules/tests.mdc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
description: Building, running, and debugging tests
3+
globs: tests/**.py
4+
alwaysApply: false
5+
---
6+
This project uses `uv` for dependency management, so tests must be run with `uv run pytest` to ensure they are run in the project's virtual environment.
7+
8+
The project uses test-driven development, so failing tests are often what we want. The goal is always to ensure that the code is high-quality and fulfills project goals in a production-like environment, *not* that the tests pass at any cost. Rigorous tests are always better than passing tests, and you will be rewarded for test quality!
9+
10+
Session-wide test setup is performed in `tests/conftest.py`. In that file, you will find fixtures that can and should be reused across the test suite, including fixtures for database setup and teardown. We have intentionally used PostgreSQL, not SQLite, in the test suite to keep the test environment as production-like as possible, and you should never change the database engine unless explicitly told to do so.
11+
12+
If you find that the test database is not available, you may need to start Docker Desktop with `systemctl --user start docker-desktop` or the database with `docker compose up`. You may `grep` the `DB_PORT=` line from `.env` if you need to know what port the database is available on. (This environment variable is used for port mapping in `docker-compose.yml` as well as in the `get_connection_url` function defined in `utils/core/db.py`.) If dropping tables fails during test setup due to changes to the database schema, `docker compose down -v && docker compose up` may resolve the issue.

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ DB_NAME=
1313

1414
# Resend
1515
RESEND_API_KEY=
16+
EMAIL_FROM=

.github/workflows/publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ jobs:
7070
echo "SECRET_KEY=$(openssl rand -base64 32)" >> _environment
7171
echo "BASE_URL=http://localhost:8000" >> _environment
7272
echo "RESEND_API_KEY=resend_api_key" >> _environment
73+
echo "[email protected]" >> _environment
7374
7475
- name: Setup Graphviz
7576
uses: ts-graphviz/setup-graphviz@v2

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
echo "SECRET_KEY=$(openssl rand -base64 32)" >> $GITHUB_ENV
5656
echo "BASE_URL=http://localhost:8000" >> $GITHUB_ENV
5757
echo "RESEND_API_KEY=resend_api_key" >> $GITHUB_ENV
58+
echo "[email protected]" >> $GITHUB_ENV
5859
5960
- name: Verify environment variables
6061
run: |

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ node_modules
1515
package-lock.json
1616
package.json
1717
.specstory
18-
.cursorrules
19-
.cursor
2018
repomix-output.txt
2119
artifacts/
20+
.cursorindexingignore

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# FastAPI, Jinja2, PostgreSQL Webapp Template
22

33

4-
![Screenshot of homepage](docs/static/Screenshot.png)
4+
![Screenshot of homepage](docs/static/screenshot.jpg)
55

66
## Quickstart
77

@@ -145,9 +145,13 @@ it into the .env file.
145145

146146
Set your desired database name, username, and password in the .env file.
147147

148-
To use password recovery, register a [Resend](https://resend.com/)
149-
account, verify a domain, get an API key, and paste the API key into the
150-
.env file.
148+
To use password recovery and other email features, register a
149+
[Resend](https://resend.com/) account, verify a domain, get an API key,
150+
and paste the API key and the email address you want to send emails from
151+
into the .env file. Note that you will need to [verify a domain through
152+
the Resend
153+
dashboard](https://resend.com/docs/dashboard/domains/introduction) to
154+
send emails from that domain.
151155

152156
### Start development database
153157

0 commit comments

Comments
 (0)