fastapi-panel 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. fastapi_panel-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +28 -0
  2. fastapi_panel-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +18 -0
  3. fastapi_panel-0.1.0/.github/dependabot.yml +12 -0
  4. fastapi_panel-0.1.0/.github/pull_request_template.md +10 -0
  5. fastapi_panel-0.1.0/.github/workflows/ci.yml +45 -0
  6. fastapi_panel-0.1.0/.github/workflows/publish.yml +21 -0
  7. fastapi_panel-0.1.0/.gitignore +15 -0
  8. fastapi_panel-0.1.0/CHANGELOG.md +29 -0
  9. fastapi_panel-0.1.0/CODE_OF_CONDUCT.md +7 -0
  10. fastapi_panel-0.1.0/CONTRIBUTING.md +56 -0
  11. fastapi_panel-0.1.0/LICENSE +21 -0
  12. fastapi_panel-0.1.0/PKG-INFO +151 -0
  13. fastapi_panel-0.1.0/README.md +118 -0
  14. fastapi_panel-0.1.0/SECURITY.md +13 -0
  15. fastapi_panel-0.1.0/docs/auth-and-permissions.md +94 -0
  16. fastapi_panel-0.1.0/docs/customizing.md +147 -0
  17. fastapi_panel-0.1.0/docs/getting-started.md +119 -0
  18. fastapi_panel-0.1.0/docs/model-admin.md +264 -0
  19. fastapi_panel-0.1.0/docs/screenshots/changelist-dark.png +0 -0
  20. fastapi_panel-0.1.0/docs/screenshots/changelist.png +0 -0
  21. fastapi_panel-0.1.0/docs/screenshots/dashboard.png +0 -0
  22. fastapi_panel-0.1.0/docs/screenshots/delete.png +0 -0
  23. fastapi_panel-0.1.0/docs/screenshots/deleted.png +0 -0
  24. fastapi_panel-0.1.0/docs/screenshots/history.png +0 -0
  25. fastapi_panel-0.1.0/docs/screenshots/import-preview.png +0 -0
  26. fastapi_panel-0.1.0/docs/screenshots/login.png +0 -0
  27. fastapi_panel-0.1.0/docs/screenshots/mobile.png +0 -0
  28. fastapi_panel-0.1.0/docs/screenshots/order-inline.png +0 -0
  29. fastapi_panel-0.1.0/docs/screenshots/product-form.png +0 -0
  30. fastapi_panel-0.1.0/docs/screenshots/related-popup.png +0 -0
  31. fastapi_panel-0.1.0/docs/security.md +57 -0
  32. fastapi_panel-0.1.0/examples/shop/README.md +18 -0
  33. fastapi_panel-0.1.0/examples/shop/admin.py +141 -0
  34. fastapi_panel-0.1.0/examples/shop/main.py +121 -0
  35. fastapi_panel-0.1.0/examples/shop/models.py +132 -0
  36. fastapi_panel-0.1.0/pyproject.toml +76 -0
  37. fastapi_panel-0.1.0/scripts/screenshots.py +251 -0
  38. fastapi_panel-0.1.0/src/fastapi_panel/__init__.py +77 -0
  39. fastapi_panel-0.1.0/src/fastapi_panel/__main__.py +97 -0
  40. fastapi_panel-0.1.0/src/fastapi_panel/auth.py +189 -0
  41. fastapi_panel-0.1.0/src/fastapi_panel/db.py +41 -0
  42. fastapi_panel-0.1.0/src/fastapi_panel/filters.py +215 -0
  43. fastapi_panel-0.1.0/src/fastapi_panel/forms.py +733 -0
  44. fastapi_panel-0.1.0/src/fastapi_panel/imports.py +264 -0
  45. fastapi_panel-0.1.0/src/fastapi_panel/introspect.py +111 -0
  46. fastapi_panel-0.1.0/src/fastapi_panel/log.py +52 -0
  47. fastapi_panel-0.1.0/src/fastapi_panel/options.py +717 -0
  48. fastapi_panel-0.1.0/src/fastapi_panel/py.typed +0 -0
  49. fastapi_panel-0.1.0/src/fastapi_panel/security.py +139 -0
  50. fastapi_panel-0.1.0/src/fastapi_panel/site.py +602 -0
  51. fastapi_panel-0.1.0/src/fastapi_panel/static/panel.css +420 -0
  52. fastapi_panel-0.1.0/src/fastapi_panel/static/panel.js +404 -0
  53. fastapi_panel-0.1.0/src/fastapi_panel/static/theme.js +5 -0
  54. fastapi_panel-0.1.0/src/fastapi_panel/storage.py +38 -0
  55. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/base.html +76 -0
  56. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/delete.html +25 -0
  57. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/error.html +10 -0
  58. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/form.html +113 -0
  59. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/history.html +23 -0
  60. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/import.html +76 -0
  61. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/index.html +43 -0
  62. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/list.html +131 -0
  63. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/login.html +15 -0
  64. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/password.html +17 -0
  65. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/popup_done.html +4 -0
  66. fastapi_panel-0.1.0/src/fastapi_panel/templates/fastapi_panel/widgets.html +74 -0
  67. fastapi_panel-0.1.0/src/fastapi_panel/views.py +710 -0
  68. fastapi_panel-0.1.0/tests/conftest.py +275 -0
  69. fastapi_panel-0.1.0/tests/test_async_and_sqlmodel.py +127 -0
  70. fastapi_panel-0.1.0/tests/test_csv_import.py +224 -0
  71. fastapi_panel-0.1.0/tests/test_extending.py +212 -0
  72. fastapi_panel-0.1.0/tests/test_files.py +288 -0
  73. fastapi_panel-0.1.0/tests/test_panel.py +352 -0
  74. fastapi_panel-0.1.0/tests/test_related_popup.py +66 -0
  75. fastapi_panel-0.1.0/tests/test_security.py +277 -0
  76. fastapi_panel-0.1.0/tests/test_soft_delete.py +189 -0
  77. fastapi_panel-0.1.0/tests/test_timezone_and_cli.py +156 -0
  78. fastapi_panel-0.1.0/uv.lock +1023 -0
@@ -0,0 +1,28 @@
1
+ name: Bug report
2
+ description: Something doesn't work as documented
3
+ labels: [bug]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: For security issues, please use a [private advisory](https://github.com/zaman-labs/fastapi-panel/security/advisories/new) instead.
8
+ - type: textarea
9
+ id: what
10
+ attributes:
11
+ label: What happened, and what did you expect?
12
+ validations:
13
+ required: true
14
+ - type: textarea
15
+ id: reproduce
16
+ attributes:
17
+ label: How to reproduce
18
+ description: A small model and ModelAdmin help the most.
19
+ render: python
20
+ validations:
21
+ required: true
22
+ - type: input
23
+ id: versions
24
+ attributes:
25
+ label: Versions
26
+ placeholder: fastapi-panel 0.1.0, SQLAlchemy 2.0, FastAPI 0.115, Python 3.12, PostgreSQL 16
27
+ validations:
28
+ required: true
@@ -0,0 +1,18 @@
1
+ name: Feature request
2
+ description: Suggest something new
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What are you trying to do?
9
+ description: Describe the problem, not only the solution.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: solution
14
+ attributes:
15
+ label: What would you like to happen?
16
+ description: If another admin tool has this, tell us how it works there.
17
+ validations:
18
+ required: true
@@ -0,0 +1,12 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ target-branch: develop
6
+ schedule:
7
+ interval: monthly
8
+ - package-ecosystem: uv
9
+ directory: /
10
+ target-branch: develop
11
+ schedule:
12
+ interval: monthly
@@ -0,0 +1,10 @@
1
+ ## What and why
2
+
3
+ <!-- What does this change, and what problem does it solve? Link the issue if there is one. -->
4
+
5
+ ## Checklist
6
+
7
+ - [ ] Targets the `develop` branch
8
+ - [ ] Tests added or updated
9
+ - [ ] `uv run ruff format . && uv run ruff check . && uv run pytest` passes
10
+ - [ ] Docs and `CHANGELOG.md` updated if users will notice
@@ -0,0 +1,45 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ - run: uv sync
19
+ - run: uv run ruff format --check .
20
+ - run: uv run ruff check .
21
+
22
+ test:
23
+ runs-on: ubuntu-latest
24
+ strategy:
25
+ fail-fast: false
26
+ matrix:
27
+ python: ["3.10", "3.11", "3.12", "3.13"]
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: astral-sh/setup-uv@v5
31
+ with:
32
+ python-version: ${{ matrix.python }}
33
+ - run: uv sync
34
+ - run: uv run pytest
35
+
36
+ build:
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: astral-sh/setup-uv@v5
41
+ - run: uv build
42
+ - name: Templates and static files are in the wheel
43
+ run: |
44
+ unzip -l dist/*.whl | grep -q 'templates/fastapi_panel/base.html'
45
+ unzip -l dist/*.whl | grep -q 'static/panel.css'
@@ -0,0 +1,21 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+ environment: pypi
15
+ permissions:
16
+ id-token: write
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v5
20
+ - run: uv build
21
+ - run: uv publish
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .coverage
10
+ htmlcov/
11
+ *.db
12
+ .DS_Store
13
+ .idea/
14
+ .vscode/
15
+ media/
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes are listed here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and versions follow [Semantic Versioning](https://semver.org/).
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.0] - 2026-11-04
8
+
9
+ First release.
10
+
11
+ ### Added
12
+
13
+ - `AdminSite`, `ModelAdmin`, `register` and `autodiscover`, with startup checks for configuration mistakes
14
+ - List pages with sorting, search, filters, date drill-down, pagination, list editing, bulk actions and CSV export
15
+ - Forms generated from SQLAlchemy models, with fieldsets, read-only fields, validation hooks and Pydantic schemas
16
+ - Foreign key dropdowns and autocomplete, many-to-many pickers, tabular and stacked inlines
17
+ - `ModelAuth`, `StaticAuth`, and an `AuthBackend` base class for your own
18
+ - Per-model, per-action and row-level permissions
19
+ - Audit log and per-record history
20
+ - CSRF protection, signed sessions, login throttling, a strict Content-Security-Policy
21
+ - Sync and async engines, SQLModel, composite primary keys
22
+ - Dark mode and a responsive layout
23
+ - `FileField`, `ImageField`, `Storage` and `LocalStorage` for uploads, with previews, thumbnails and login-protected links
24
+ - A **+** button next to relation fields, to create a related record in a dialog and select it
25
+ - CSV import with `csv_import`: a preview first, updates through a primary key column, all or nothing
26
+ - `soft_delete_field`, with a Deleted page to restore rows or remove them for good
27
+ - `python -m fastapi_panel createuser`
28
+ - `AdminSite(timezone=...)` and `naive_utc`
29
+ - Collapsible sidebar groups
@@ -0,0 +1,7 @@
1
+ # Code of Conduct
2
+
3
+ This project follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
4
+
5
+ In short: be respectful, assume good intent, and keep criticism about the work. Harassment and personal attacks aren't tolerated in any project space.
6
+
7
+ To report unacceptable behavior, contact the maintainers privately through the [zaman-labs organization](https://github.com/zaman-labs). Reports are handled in confidence.
@@ -0,0 +1,56 @@
1
+ # Contributing
2
+
3
+ Bug reports, ideas and pull requests are all welcome. For anything big, open an issue first so we can agree on the approach.
4
+
5
+ ## Setup
6
+
7
+ You need [uv](https://docs.astral.sh/uv/).
8
+
9
+ ```bash
10
+ git clone https://github.com/zaman-labs/fastapi-panel
11
+ cd fastapi-panel
12
+ uv sync
13
+ uv run pytest
14
+ ```
15
+
16
+ Before pushing:
17
+
18
+ ```bash
19
+ uv run ruff format .
20
+ uv run ruff check .
21
+ uv run pytest
22
+ ```
23
+
24
+ To see your changes in a browser, run the [example shop](examples/shop). To check the interface end to end and refresh the screenshots, keep the example running on port 8765 and run:
25
+
26
+ ```bash
27
+ uv run --with playwright python scripts/screenshots.py
28
+ ```
29
+
30
+ ## Branches
31
+
32
+ - `main` is what's released, and always stable.
33
+ - `develop` is where work comes together. Open pull requests against it.
34
+ - Name your branch `feature/<name>`, `fix/<name>` or `docs/<name>`.
35
+
36
+ To release, merge `develop` into `main`, bump the version in `src/fastapi_panel/__init__.py`, update `CHANGELOG.md`, and publish a GitHub release tagged `vX.Y.Z`. That triggers the upload to PyPI.
37
+
38
+ ## Commits
39
+
40
+ Use [Conventional Commits](https://www.conventionalcommits.org/): `feat: add radio fields`, `fix: keep filters after saving`, `docs: explain inlines`. Keep each commit about one thing.
41
+
42
+ ## What we look for
43
+
44
+ - **Small and readable.** This library should stay easy to read in an afternoon. Prefer fewer options and plain code over flexibility nobody needs yet.
45
+ - **Consistent.** New options follow the naming of the existing ones.
46
+ - **Tested.** New behavior and bug fixes come with a test. Security behavior always does.
47
+ - **Secure by default.** No inline scripts or styles, and no new way to write a field that isn't on the form. Anything that touches login, sessions or permissions gets extra attention.
48
+ - **No new dependencies** unless there's really no other way.
49
+
50
+ ## Reporting security issues
51
+
52
+ Please don't open a public issue. See [SECURITY.md](SECURITY.md).
53
+
54
+ ## Code of conduct
55
+
56
+ Everyone taking part agrees to our [Code of Conduct](CODE_OF_CONDUCT.md).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zaman Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.5
2
+ Name: fastapi-panel
3
+ Version: 0.1.0
4
+ Summary: An admin panel for FastAPI and SQLAlchemy. Register a model, get a secure back office.
5
+ Project-URL: Homepage, https://github.com/zaman-labs/fastapi-panel
6
+ Project-URL: Documentation, https://github.com/zaman-labs/fastapi-panel/tree/main/docs
7
+ Project-URL: Changelog, https://github.com/zaman-labs/fastapi-panel/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/zaman-labs/fastapi-panel/issues
9
+ Author: Zaman Labs
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: admin,admin-panel,backoffice,crud,fastapi,sqlalchemy,sqlmodel
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Framework :: AsyncIO
16
+ Classifier: Framework :: FastAPI
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Requires-Dist: fastapi>=0.110
28
+ Requires-Dist: jinja2>=3.1
29
+ Requires-Dist: python-multipart>=0.0.18
30
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.20
31
+ Requires-Dist: starlette>=0.40
32
+ Description-Content-Type: text/markdown
33
+
34
+ <h1 align="center">FastAPI Panel</h1>
35
+
36
+ <p align="center">
37
+ <strong>A modular admin panel for FastAPI.</strong><br>
38
+ Register a SQLAlchemy model, get a secure back office.
39
+ </p>
40
+
41
+ <p align="center">
42
+ <a href="https://github.com/zaman-labs/fastapi-panel/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/zaman-labs/fastapi-panel/actions/workflows/ci.yml/badge.svg"></a>
43
+ <a href="https://pypi.org/project/fastapi-panel/"><img alt="PyPI" src="https://img.shields.io/pypi/v/fastapi-panel"></a>
44
+ <img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-blue">
45
+ <a href="https://github.com/zaman-labs/fastapi-panel/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-green"></a>
46
+ </p>
47
+
48
+ ![The product list, with search, filters, bulk actions and inline editing](https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/changelist.png)
49
+
50
+ If you know `ModelAdmin`, you already know this library: `list_display`, `search_fields`, `list_filter`, `fieldsets`, `inlines`, actions and the rest work the way you expect. There is no frontend build, no Node and no extra service. It's server-rendered pages inside your FastAPI app.
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ pip install fastapi-panel
56
+ ```
57
+
58
+ ## Quick start
59
+
60
+ ```python
61
+ from fastapi import FastAPI
62
+ from fastapi_panel import ModelAdmin, ModelAuth, register, site
63
+ from sqlalchemy.ext.asyncio import create_async_engine
64
+
65
+ from myapp.models import Product, User
66
+
67
+ engine = create_async_engine("postgresql+asyncpg://...")
68
+ app = FastAPI()
69
+
70
+
71
+ @register(Product)
72
+ class ProductAdmin(ModelAdmin):
73
+ list_display = ("name", "category", "price", "is_published")
74
+ list_filter = ("is_published", "category")
75
+ search_fields = ("name", "category__name")
76
+ ordering = ("-created_at",)
77
+
78
+
79
+ site.mount(
80
+ app,
81
+ engine=engine,
82
+ auth=ModelAuth(User, superuser_field="is_superuser"),
83
+ secret_key=settings.PANEL_SECRET_KEY,
84
+ )
85
+ ```
86
+
87
+ Open `/admin`, log in, and you have a list page, add and edit forms, delete confirmation, bulk actions, CSV export and a change history. See the [getting started guide](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/getting-started.md) for creating your first user with `python -m fastapi_panel createuser` and setting up migrations.
88
+
89
+ Want to try it first? The [example shop](https://github.com/zaman-labs/fastapi-panel/tree/main/examples/shop) runs in one command.
90
+
91
+ ## What you get
92
+
93
+ | | |
94
+ |---|---|
95
+ | **Change lists** | Columns, computed columns, sorting, search across relations, sidebar filters, date drill-down, pagination, editing in the list |
96
+ | **Forms** | Built from your models, with fieldsets, read-only fields, prefilled slugs, validation hooks, Pydantic schemas, foreign key autocomplete, many-to-many pickers, and a **+** button to create a related record on the spot |
97
+ | **Files and images** | Uploads with previews and thumbnails, stored on disk or anywhere else through a small storage class |
98
+ | **Import and restore** | CSV import with a preview before anything is saved, and a Deleted page to restore soft-deleted records |
99
+ | **Inlines** | Tabular and stacked, with add, edit and delete on the parent's page |
100
+ | **Actions** | Your own bulk actions, plus delete with a summary of what cascades, CSV export and "select all across pages" |
101
+ | **Permissions** | Per model and per action, row-level through `get_queryset` and `has_*_permission` |
102
+ | **Audit log** | Who changed which fields and when, shown on each record's history page |
103
+ | **Any SQLAlchemy setup** | Sync and async engines, SQLModel, composite primary keys |
104
+ | **Timezones** | Show and enter times in your site's timezone, while the database stays in UTC |
105
+ | **Interface** | Dark mode, works on a phone, keyboard friendly, no external assets |
106
+
107
+ <table>
108
+ <tr>
109
+ <td><img alt="An order with its items edited inline" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/order-inline.png"></td>
110
+ <td><img alt="A product form with fieldsets and a many-to-many picker" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/product-form.png"></td>
111
+ </tr>
112
+ <tr>
113
+ <td><img alt="The dashboard with recent actions" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/dashboard.png"></td>
114
+ <td><img alt="The product list in dark mode" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/changelist-dark.png"></td>
115
+ </tr>
116
+ <tr>
117
+ <td><img alt="Adding a category in a dialog from the product form" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/related-popup.png"></td>
118
+ <td><img alt="A CSV import preview before anything is saved" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/import-preview.png"></td>
119
+ </tr>
120
+ </table>
121
+
122
+ ## Security
123
+
124
+ An admin panel can reach all your data, so these are on by default and have no off switch to forget:
125
+
126
+ - CSRF tokens and `Origin` checks on every POST
127
+ - Signed `HttpOnly`, `SameSite=Lax` session cookies, `Secure` on HTTPS, ended by a password change or by deactivating the user
128
+ - Login throttling, no way to tell a wrong username from a wrong password, scrypt password hashing
129
+ - A strict Content-Security-Policy with no inline scripts or styles, `X-Frame-Options: DENY`, `no-store` caching
130
+ - Columns named like `password`, `token` or `secret` stay hidden unless you ask for them, and the audit log stores field names, never values
131
+ - Only fields on the form can be written, and sort and filter parameters are allowlisted
132
+ - Spreadsheet formulas are neutralised in CSV exports
133
+ - Uploads get random names, images are checked against their contents, and files are only served behind login
134
+
135
+ The panel refuses to start without authentication or with a short secret key. Read [the security guide](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/security.md) for what's left to you.
136
+
137
+ ## Documentation
138
+
139
+ - [Getting started](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/getting-started.md)
140
+ - [ModelAdmin reference](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/model-admin.md): lists, forms, inlines, actions, filters
141
+ - [Authentication and permissions](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/auth-and-permissions.md)
142
+ - [Modular projects and customizing](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/customizing.md): autodiscovery, multiple sites, custom pages, templates, themes
143
+ - [Security](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/security.md)
144
+
145
+ ## Roadmap
146
+
147
+ Translations, a saved-filters UI, password reset by email, two-factor login, and a Redis option for login throttling across workers. Ideas and pull requests are welcome, see [CONTRIBUTING](https://github.com/zaman-labs/fastapi-panel/blob/main/CONTRIBUTING.md).
148
+
149
+ ## License
150
+
151
+ [MIT](https://github.com/zaman-labs/fastapi-panel/blob/main/LICENSE)
@@ -0,0 +1,118 @@
1
+ <h1 align="center">FastAPI Panel</h1>
2
+
3
+ <p align="center">
4
+ <strong>A modular admin panel for FastAPI.</strong><br>
5
+ Register a SQLAlchemy model, get a secure back office.
6
+ </p>
7
+
8
+ <p align="center">
9
+ <a href="https://github.com/zaman-labs/fastapi-panel/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/zaman-labs/fastapi-panel/actions/workflows/ci.yml/badge.svg"></a>
10
+ <a href="https://pypi.org/project/fastapi-panel/"><img alt="PyPI" src="https://img.shields.io/pypi/v/fastapi-panel"></a>
11
+ <img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-blue">
12
+ <a href="https://github.com/zaman-labs/fastapi-panel/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-green"></a>
13
+ </p>
14
+
15
+ ![The product list, with search, filters, bulk actions and inline editing](https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/changelist.png)
16
+
17
+ If you know `ModelAdmin`, you already know this library: `list_display`, `search_fields`, `list_filter`, `fieldsets`, `inlines`, actions and the rest work the way you expect. There is no frontend build, no Node and no extra service. It's server-rendered pages inside your FastAPI app.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install fastapi-panel
23
+ ```
24
+
25
+ ## Quick start
26
+
27
+ ```python
28
+ from fastapi import FastAPI
29
+ from fastapi_panel import ModelAdmin, ModelAuth, register, site
30
+ from sqlalchemy.ext.asyncio import create_async_engine
31
+
32
+ from myapp.models import Product, User
33
+
34
+ engine = create_async_engine("postgresql+asyncpg://...")
35
+ app = FastAPI()
36
+
37
+
38
+ @register(Product)
39
+ class ProductAdmin(ModelAdmin):
40
+ list_display = ("name", "category", "price", "is_published")
41
+ list_filter = ("is_published", "category")
42
+ search_fields = ("name", "category__name")
43
+ ordering = ("-created_at",)
44
+
45
+
46
+ site.mount(
47
+ app,
48
+ engine=engine,
49
+ auth=ModelAuth(User, superuser_field="is_superuser"),
50
+ secret_key=settings.PANEL_SECRET_KEY,
51
+ )
52
+ ```
53
+
54
+ Open `/admin`, log in, and you have a list page, add and edit forms, delete confirmation, bulk actions, CSV export and a change history. See the [getting started guide](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/getting-started.md) for creating your first user with `python -m fastapi_panel createuser` and setting up migrations.
55
+
56
+ Want to try it first? The [example shop](https://github.com/zaman-labs/fastapi-panel/tree/main/examples/shop) runs in one command.
57
+
58
+ ## What you get
59
+
60
+ | | |
61
+ |---|---|
62
+ | **Change lists** | Columns, computed columns, sorting, search across relations, sidebar filters, date drill-down, pagination, editing in the list |
63
+ | **Forms** | Built from your models, with fieldsets, read-only fields, prefilled slugs, validation hooks, Pydantic schemas, foreign key autocomplete, many-to-many pickers, and a **+** button to create a related record on the spot |
64
+ | **Files and images** | Uploads with previews and thumbnails, stored on disk or anywhere else through a small storage class |
65
+ | **Import and restore** | CSV import with a preview before anything is saved, and a Deleted page to restore soft-deleted records |
66
+ | **Inlines** | Tabular and stacked, with add, edit and delete on the parent's page |
67
+ | **Actions** | Your own bulk actions, plus delete with a summary of what cascades, CSV export and "select all across pages" |
68
+ | **Permissions** | Per model and per action, row-level through `get_queryset` and `has_*_permission` |
69
+ | **Audit log** | Who changed which fields and when, shown on each record's history page |
70
+ | **Any SQLAlchemy setup** | Sync and async engines, SQLModel, composite primary keys |
71
+ | **Timezones** | Show and enter times in your site's timezone, while the database stays in UTC |
72
+ | **Interface** | Dark mode, works on a phone, keyboard friendly, no external assets |
73
+
74
+ <table>
75
+ <tr>
76
+ <td><img alt="An order with its items edited inline" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/order-inline.png"></td>
77
+ <td><img alt="A product form with fieldsets and a many-to-many picker" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/product-form.png"></td>
78
+ </tr>
79
+ <tr>
80
+ <td><img alt="The dashboard with recent actions" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/dashboard.png"></td>
81
+ <td><img alt="The product list in dark mode" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/changelist-dark.png"></td>
82
+ </tr>
83
+ <tr>
84
+ <td><img alt="Adding a category in a dialog from the product form" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/related-popup.png"></td>
85
+ <td><img alt="A CSV import preview before anything is saved" src="https://raw.githubusercontent.com/zaman-labs/fastapi-panel/main/docs/screenshots/import-preview.png"></td>
86
+ </tr>
87
+ </table>
88
+
89
+ ## Security
90
+
91
+ An admin panel can reach all your data, so these are on by default and have no off switch to forget:
92
+
93
+ - CSRF tokens and `Origin` checks on every POST
94
+ - Signed `HttpOnly`, `SameSite=Lax` session cookies, `Secure` on HTTPS, ended by a password change or by deactivating the user
95
+ - Login throttling, no way to tell a wrong username from a wrong password, scrypt password hashing
96
+ - A strict Content-Security-Policy with no inline scripts or styles, `X-Frame-Options: DENY`, `no-store` caching
97
+ - Columns named like `password`, `token` or `secret` stay hidden unless you ask for them, and the audit log stores field names, never values
98
+ - Only fields on the form can be written, and sort and filter parameters are allowlisted
99
+ - Spreadsheet formulas are neutralised in CSV exports
100
+ - Uploads get random names, images are checked against their contents, and files are only served behind login
101
+
102
+ The panel refuses to start without authentication or with a short secret key. Read [the security guide](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/security.md) for what's left to you.
103
+
104
+ ## Documentation
105
+
106
+ - [Getting started](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/getting-started.md)
107
+ - [ModelAdmin reference](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/model-admin.md): lists, forms, inlines, actions, filters
108
+ - [Authentication and permissions](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/auth-and-permissions.md)
109
+ - [Modular projects and customizing](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/customizing.md): autodiscovery, multiple sites, custom pages, templates, themes
110
+ - [Security](https://github.com/zaman-labs/fastapi-panel/blob/main/docs/security.md)
111
+
112
+ ## Roadmap
113
+
114
+ Translations, a saved-filters UI, password reset by email, two-factor login, and a Redis option for login throttling across workers. Ideas and pull requests are welcome, see [CONTRIBUTING](https://github.com/zaman-labs/fastapi-panel/blob/main/CONTRIBUTING.md).
115
+
116
+ ## License
117
+
118
+ [MIT](https://github.com/zaman-labs/fastapi-panel/blob/main/LICENSE)
@@ -0,0 +1,13 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes go into the latest release.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Please report it privately through [GitHub's advisory form](https://github.com/zaman-labs/fastapi-panel/security/advisories/new), not in a public issue.
10
+
11
+ Include what you found, how to reproduce it, and the impact you expect. You'll get a reply within a week, and we'll keep you updated until there's a fix. If you'd like credit, we'll name you in the advisory.
12
+
13
+ For how the panel protects itself and what's left to you, see the [security guide](docs/security.md).
@@ -0,0 +1,94 @@
1
+ # Authentication and permissions
2
+
3
+ `site.mount` requires an `auth` backend. There's no anonymous mode.
4
+
5
+ ## ModelAuth
6
+
7
+ ```python
8
+ ModelAuth(
9
+ User,
10
+ username_field="email",
11
+ password_field="hashed_password",
12
+ active_field="is_active", # None to skip
13
+ staff_field="is_staff", # None to skip
14
+ superuser_field="is_superuser",
15
+ permissions_field="permissions",
16
+ )
17
+ ```
18
+
19
+ `active_field` and `staff_field` must exist on the model or be passed as `None`. That's deliberate. If your `users` table also holds customers, a missing staff check would let all of them log in, so the panel refuses to start until you decide.
20
+
21
+ ## Permissions
22
+
23
+ Permissions are strings in the form `<slug>.<action>`, where the action is `view`, `add`, `change` or `delete`.
24
+
25
+ | Configuration | Who can do what |
26
+ |---|---|
27
+ | No `superuser_field`, no `permissions_field` | Everyone who can log in can do everything |
28
+ | `superuser_field` only | Superusers can do everything, other staff can do nothing |
29
+ | `permissions_field` | Superusers can do everything, others get what's in the list |
30
+
31
+ `permissions_field` points at something iterable on the user, usually a JSON column:
32
+
33
+ ```python
34
+ ["product.view", "product.change", "order.*"]
35
+ ```
36
+
37
+ `order.*` covers all actions on orders and `*` covers everything. Having `change` implies `view`.
38
+
39
+ For roles or groups, subclass the backend. Load what you need in `get_user`, because the database session is closed by the time `has_perm` runs:
40
+
41
+ ```python
42
+ class RoleAuth(ModelAuth):
43
+ def get_user(self, session, user_id):
44
+ user = super().get_user(session, user_id)
45
+ if user is not None:
46
+ user.granted = {p.code for role in user.roles for p in role.permissions}
47
+ return user
48
+
49
+ def has_perm(self, user, perm):
50
+ return user.is_superuser or perm in user.granted
51
+ ```
52
+
53
+ ## Per-model and per-row rules
54
+
55
+ ```python
56
+ class InvoiceAdmin(ModelAdmin):
57
+ def get_queryset(self, request):
58
+ user = request.state.user
59
+ stmt = super().get_queryset(request)
60
+ return stmt if user.is_superuser else stmt.where(Invoice.team_id == user.team_id)
61
+
62
+ def has_delete_permission(self, request, obj=None):
63
+ return obj is None or obj.status == "draft"
64
+ ```
65
+
66
+ `get_queryset` is the safest place for row-level rules. Lists, edit pages, deletes, history, bulk actions, autocomplete and foreign key validation all go through it, and a row outside it returns 404.
67
+
68
+ Inline rows follow the parent's change permission by default. Override `has_add_permission`, `has_change_permission` and `has_delete_permission` on the inline to change that.
69
+
70
+ The current user is `request.state.user` in every hook and view.
71
+
72
+ ## Your own backend
73
+
74
+ Single sign-on, LDAP or anything else: subclass `AuthBackend`.
75
+
76
+ ```python
77
+ from fastapi_panel import AuthBackend
78
+
79
+
80
+ class LdapAuth(AuthBackend):
81
+ def find_user(self, session, username): ...
82
+ def get_user(self, session, user_id): ...
83
+ def check_password(self, user, password): ... # user may be None. Runs in a worker thread
84
+ def user_id(self, user): ...
85
+ def is_allowed(self, user): ...
86
+ def has_perm(self, user, perm): ...
87
+ def session_hash(self, user): ... # sessions end when this value changes
88
+ ```
89
+
90
+ ## Sessions
91
+
92
+ Sessions live in a signed cookie, so there's no session table. A session ends when it expires (`session_age`, 12 hours by default), on logout, when the password changes, or when the user is deactivated. The last two apply on the very next request.
93
+
94
+ Logout clears the cookie in that browser. A stolen cookie stays valid until one of the other conditions is met, so changing the password is how you revoke every session of one user, and changing `secret_key` revokes everyone's.