fasthx-admin 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 (27) hide show
  1. fasthx_admin-0.1.0/.gitignore +25 -0
  2. fasthx_admin-0.1.0/PKG-INFO +197 -0
  3. fasthx_admin-0.1.0/README.md +165 -0
  4. fasthx_admin-0.1.0/examples/demo/app.py +579 -0
  5. fasthx_admin-0.1.0/examples/demo/models.py +92 -0
  6. fasthx_admin-0.1.0/pyproject.toml +49 -0
  7. fasthx_admin-0.1.0/src/fasthx_admin/__init__.py +26 -0
  8. fasthx_admin-0.1.0/src/fasthx_admin/auth.py +145 -0
  9. fasthx_admin-0.1.0/src/fasthx_admin/crud.py +588 -0
  10. fasthx_admin-0.1.0/src/fasthx_admin/database.py +57 -0
  11. fasthx_admin-0.1.0/src/fasthx_admin/static/css/style.css +818 -0
  12. fasthx_admin-0.1.0/src/fasthx_admin/static/js/app.js +31 -0
  13. fasthx_admin-0.1.0/src/fasthx_admin/templates/base.html +98 -0
  14. fasthx_admin-0.1.0/src/fasthx_admin/templates/dashboard.html +163 -0
  15. fasthx_admin-0.1.0/src/fasthx_admin/templates/detail.html +32 -0
  16. fasthx_admin-0.1.0/src/fasthx_admin/templates/form.html +57 -0
  17. fasthx_admin-0.1.0/src/fasthx_admin/templates/list.html +89 -0
  18. fasthx_admin-0.1.0/src/fasthx_admin/templates/login.html +219 -0
  19. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/_form_field.html +55 -0
  20. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/_wizard_indicators.html +21 -0
  21. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/dropdown_options.html +4 -0
  22. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/progress_bar.html +25 -0
  23. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/row_actions.html +39 -0
  24. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/status_cell.html +21 -0
  25. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/table_body.html +29 -0
  26. fasthx_admin-0.1.0/src/fasthx_admin/templates/partials/wizard_step.html +163 -0
  27. fasthx_admin-0.1.0/src/fasthx_admin/templates/wizard.html +19 -0
@@ -0,0 +1,25 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ *.egg
9
+ .eggs/
10
+ *.db
11
+ *.sqlite3
12
+ .env
13
+ .venv/
14
+ venv/
15
+ env/
16
+ .idea/
17
+ .vscode/
18
+ *.swp
19
+ *.swo
20
+ *~
21
+ .DS_Store
22
+ Thumbs.db
23
+ .pytest_cache/
24
+ .mypy_cache/
25
+ .ruff_cache/
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.4
2
+ Name: fasthx-admin
3
+ Version: 0.1.0
4
+ Summary: FastAPI + HTMX + Jinja2 admin interface framework — a modern replacement for Flask-Admin
5
+ Project-URL: Homepage, https://github.com/talbiston/fasthx-admin
6
+ Project-URL: Repository, https://github.com/talbiston/fasthx-admin
7
+ Author: talbiston
8
+ License-Expression: MIT
9
+ Keywords: admin,crud,fastapi,htmx,jinja2
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Framework :: FastAPI
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: fastapi
22
+ Requires-Dist: itsdangerous
23
+ Requires-Dist: jinja2
24
+ Requires-Dist: python-multipart
25
+ Requires-Dist: requests
26
+ Requires-Dist: sqlalchemy
27
+ Provides-Extra: dev
28
+ Requires-Dist: httpx; extra == 'dev'
29
+ Requires-Dist: pytest; extra == 'dev'
30
+ Requires-Dist: uvicorn[standard]; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # fasthx-admin
34
+
35
+ FastAPI + HTMX + Jinja2 admin interface framework — a modern replacement for Flask-Admin.
36
+
37
+ ## Features
38
+
39
+ - Auto-generated CRUD routes from SQLAlchemy models
40
+ - Dark/light theme with Bootstrap 5.3
41
+ - HTMX-powered interactions (search, sorting, polling, dependent dropdowns)
42
+ - Accordion-grouped form sections
43
+ - Custom column formatters and row actions
44
+ - OIDC/Keycloak authentication (with `AUTH_DISABLED` dev mode)
45
+ - Deploy wizard with real-time progress tracking
46
+ - Responsive sidebar navigation
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install fasthx-admin
52
+ ```
53
+
54
+ For development (includes uvicorn):
55
+
56
+ ```bash
57
+ pip install fasthx-admin[dev]
58
+ ```
59
+
60
+ ## Quick Start
61
+
62
+ ```python
63
+ from contextlib import asynccontextmanager
64
+
65
+ from fastapi import FastAPI
66
+ from starlette.middleware.sessions import SessionMiddleware
67
+
68
+ from fasthx_admin import Admin, CRUDView, Base, init_db
69
+
70
+ # 1. Define your models
71
+ from sqlalchemy import Column, Integer, String
72
+
73
+ class Customer(Base):
74
+ __tablename__ = "customers"
75
+ id = Column(Integer, primary_key=True)
76
+ name = Column(String(100), nullable=False)
77
+ sid = Column(String(50), nullable=False)
78
+
79
+ __admin_category__ = "CRM"
80
+ __admin_icon__ = "building"
81
+ __admin_name__ = "Customers"
82
+
83
+ # 2. Initialise the database
84
+ engine = init_db("sqlite:///./app.db", connect_args={"check_same_thread": False})
85
+
86
+ # 3. Create the app
87
+ @asynccontextmanager
88
+ async def lifespan(app):
89
+ Base.metadata.create_all(bind=engine)
90
+ yield
91
+
92
+ app = FastAPI(lifespan=lifespan)
93
+ app.add_middleware(SessionMiddleware, secret_key="change-me")
94
+
95
+ # 4. Create the admin and register views
96
+ admin = Admin(app, title="My Admin")
97
+
98
+ class CustomerView(CRUDView):
99
+ model = Customer
100
+ column_list = ["id", "name", "sid"]
101
+
102
+ admin.add_view(CustomerView)
103
+ ```
104
+
105
+ Run with auth disabled for development:
106
+
107
+ ```bash
108
+ AUTH_DISABLED=1 uvicorn app:app --reload
109
+ ```
110
+
111
+ ## CRUDView Configuration
112
+
113
+ Subclass `CRUDView` and set class-level attributes:
114
+
115
+ | Attribute | Description |
116
+ |---|---|
117
+ | `model` | SQLAlchemy model class (required) |
118
+ | `name` | URL prefix (defaults to `__tablename__`) |
119
+ | `display_name` | Sidebar label (defaults to model's `__admin_name__`) |
120
+ | `category` | Sidebar group (defaults to model's `__admin_category__`) |
121
+ | `icon` | Bootstrap Icons name (defaults to model's `__admin_icon__`) |
122
+ | `column_list` | Columns to show in the list view |
123
+ | `column_exclude` | Columns to exclude (alternative to `column_list`) |
124
+ | `column_labels` | Display name overrides, e.g. `{"customer_id": "Customer"}` |
125
+ | `column_formatters` | `{col: fn(value, obj) -> html_string}` |
126
+ | `column_searchable` | Columns to search (defaults to all String columns) |
127
+ | `column_sortable` | Columns that can be sorted |
128
+ | `form_columns` | Editable fields (defaults to all except `id`) |
129
+ | `form_sections` | Accordion groups: `{"Section": ["field1", "field2"]}` |
130
+ | `form_widget_overrides` | Per-field HTMX attrs or select choices |
131
+ | `row_actions` | Custom action buttons per row |
132
+ | `htmx_columns` | Auto-polling cells: `{"field": {"url": "...", "trigger": "every 3s"}}` |
133
+ | `page_size` | Records per page (default 20) |
134
+ | `can_create` / `can_edit` / `can_delete` | Permission flags |
135
+
136
+ ## Custom Endpoints
137
+
138
+ Override `setup_endpoints()` on your CRUDView subclass to add custom routes:
139
+
140
+ ```python
141
+ class OrchestratorView(CRUDView):
142
+ model = Orchestrator
143
+
144
+ def setup_endpoints(self):
145
+ @self.router.post(f"/{self.name}/{{item_id}}/build")
146
+ async def build(request: Request, item_id: int, db=Depends(get_db)):
147
+ # custom logic
148
+ return HTMLResponse("", headers={"HX-Redirect": f"/{self.name}"})
149
+ ```
150
+
151
+ ## Admin Class
152
+
153
+ ```python
154
+ admin = Admin(
155
+ app,
156
+ title="My Admin", # Sidebar brand + page titles
157
+ static_url="/static/fasthx-admin", # Where package statics are mounted
158
+ public_pages={"login.html"}, # Pages that skip auth check
159
+ )
160
+ ```
161
+
162
+ The `Admin` instance:
163
+ - Mounts built-in static files (CSS, JS)
164
+ - Sets up Jinja2 templates from the package
165
+ - Wraps template responses with nav context + auth check
166
+ - Provides `admin.templates` for rendering custom pages
167
+
168
+ ## Model Metadata
169
+
170
+ Set these on your SQLAlchemy model classes for automatic sidebar grouping:
171
+
172
+ ```python
173
+ class MyModel(Base):
174
+ __tablename__ = "my_models"
175
+ __admin_category__ = "Section Name" # Sidebar group
176
+ __admin_icon__ = "table" # Bootstrap Icons name
177
+ __admin_name__ = "My Models" # Display label
178
+ ```
179
+
180
+ ## Environment Variables
181
+
182
+ | Variable | Purpose | Default |
183
+ |---|---|---|
184
+ | `AUTH_DISABLED` | Bypass authentication (`1`/`true`/`yes`) | disabled |
185
+ | `SESSION_SECRET` | Session signing key | (set in your app) |
186
+ | `OIDC_SECRETS` | Path to `client_secrets.json` | `./client_secrets.json` |
187
+
188
+ ## Running the Demo
189
+
190
+ ```bash
191
+ cd examples/demo
192
+ pip install -e ../..
193
+ pip install uvicorn[standard]
194
+ AUTH_DISABLED=1 uvicorn app:app --reload
195
+ ```
196
+
197
+ Open http://127.0.0.1:8000
@@ -0,0 +1,165 @@
1
+ # fasthx-admin
2
+
3
+ FastAPI + HTMX + Jinja2 admin interface framework — a modern replacement for Flask-Admin.
4
+
5
+ ## Features
6
+
7
+ - Auto-generated CRUD routes from SQLAlchemy models
8
+ - Dark/light theme with Bootstrap 5.3
9
+ - HTMX-powered interactions (search, sorting, polling, dependent dropdowns)
10
+ - Accordion-grouped form sections
11
+ - Custom column formatters and row actions
12
+ - OIDC/Keycloak authentication (with `AUTH_DISABLED` dev mode)
13
+ - Deploy wizard with real-time progress tracking
14
+ - Responsive sidebar navigation
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install fasthx-admin
20
+ ```
21
+
22
+ For development (includes uvicorn):
23
+
24
+ ```bash
25
+ pip install fasthx-admin[dev]
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ from contextlib import asynccontextmanager
32
+
33
+ from fastapi import FastAPI
34
+ from starlette.middleware.sessions import SessionMiddleware
35
+
36
+ from fasthx_admin import Admin, CRUDView, Base, init_db
37
+
38
+ # 1. Define your models
39
+ from sqlalchemy import Column, Integer, String
40
+
41
+ class Customer(Base):
42
+ __tablename__ = "customers"
43
+ id = Column(Integer, primary_key=True)
44
+ name = Column(String(100), nullable=False)
45
+ sid = Column(String(50), nullable=False)
46
+
47
+ __admin_category__ = "CRM"
48
+ __admin_icon__ = "building"
49
+ __admin_name__ = "Customers"
50
+
51
+ # 2. Initialise the database
52
+ engine = init_db("sqlite:///./app.db", connect_args={"check_same_thread": False})
53
+
54
+ # 3. Create the app
55
+ @asynccontextmanager
56
+ async def lifespan(app):
57
+ Base.metadata.create_all(bind=engine)
58
+ yield
59
+
60
+ app = FastAPI(lifespan=lifespan)
61
+ app.add_middleware(SessionMiddleware, secret_key="change-me")
62
+
63
+ # 4. Create the admin and register views
64
+ admin = Admin(app, title="My Admin")
65
+
66
+ class CustomerView(CRUDView):
67
+ model = Customer
68
+ column_list = ["id", "name", "sid"]
69
+
70
+ admin.add_view(CustomerView)
71
+ ```
72
+
73
+ Run with auth disabled for development:
74
+
75
+ ```bash
76
+ AUTH_DISABLED=1 uvicorn app:app --reload
77
+ ```
78
+
79
+ ## CRUDView Configuration
80
+
81
+ Subclass `CRUDView` and set class-level attributes:
82
+
83
+ | Attribute | Description |
84
+ |---|---|
85
+ | `model` | SQLAlchemy model class (required) |
86
+ | `name` | URL prefix (defaults to `__tablename__`) |
87
+ | `display_name` | Sidebar label (defaults to model's `__admin_name__`) |
88
+ | `category` | Sidebar group (defaults to model's `__admin_category__`) |
89
+ | `icon` | Bootstrap Icons name (defaults to model's `__admin_icon__`) |
90
+ | `column_list` | Columns to show in the list view |
91
+ | `column_exclude` | Columns to exclude (alternative to `column_list`) |
92
+ | `column_labels` | Display name overrides, e.g. `{"customer_id": "Customer"}` |
93
+ | `column_formatters` | `{col: fn(value, obj) -> html_string}` |
94
+ | `column_searchable` | Columns to search (defaults to all String columns) |
95
+ | `column_sortable` | Columns that can be sorted |
96
+ | `form_columns` | Editable fields (defaults to all except `id`) |
97
+ | `form_sections` | Accordion groups: `{"Section": ["field1", "field2"]}` |
98
+ | `form_widget_overrides` | Per-field HTMX attrs or select choices |
99
+ | `row_actions` | Custom action buttons per row |
100
+ | `htmx_columns` | Auto-polling cells: `{"field": {"url": "...", "trigger": "every 3s"}}` |
101
+ | `page_size` | Records per page (default 20) |
102
+ | `can_create` / `can_edit` / `can_delete` | Permission flags |
103
+
104
+ ## Custom Endpoints
105
+
106
+ Override `setup_endpoints()` on your CRUDView subclass to add custom routes:
107
+
108
+ ```python
109
+ class OrchestratorView(CRUDView):
110
+ model = Orchestrator
111
+
112
+ def setup_endpoints(self):
113
+ @self.router.post(f"/{self.name}/{{item_id}}/build")
114
+ async def build(request: Request, item_id: int, db=Depends(get_db)):
115
+ # custom logic
116
+ return HTMLResponse("", headers={"HX-Redirect": f"/{self.name}"})
117
+ ```
118
+
119
+ ## Admin Class
120
+
121
+ ```python
122
+ admin = Admin(
123
+ app,
124
+ title="My Admin", # Sidebar brand + page titles
125
+ static_url="/static/fasthx-admin", # Where package statics are mounted
126
+ public_pages={"login.html"}, # Pages that skip auth check
127
+ )
128
+ ```
129
+
130
+ The `Admin` instance:
131
+ - Mounts built-in static files (CSS, JS)
132
+ - Sets up Jinja2 templates from the package
133
+ - Wraps template responses with nav context + auth check
134
+ - Provides `admin.templates` for rendering custom pages
135
+
136
+ ## Model Metadata
137
+
138
+ Set these on your SQLAlchemy model classes for automatic sidebar grouping:
139
+
140
+ ```python
141
+ class MyModel(Base):
142
+ __tablename__ = "my_models"
143
+ __admin_category__ = "Section Name" # Sidebar group
144
+ __admin_icon__ = "table" # Bootstrap Icons name
145
+ __admin_name__ = "My Models" # Display label
146
+ ```
147
+
148
+ ## Environment Variables
149
+
150
+ | Variable | Purpose | Default |
151
+ |---|---|---|
152
+ | `AUTH_DISABLED` | Bypass authentication (`1`/`true`/`yes`) | disabled |
153
+ | `SESSION_SECRET` | Session signing key | (set in your app) |
154
+ | `OIDC_SECRETS` | Path to `client_secrets.json` | `./client_secrets.json` |
155
+
156
+ ## Running the Demo
157
+
158
+ ```bash
159
+ cd examples/demo
160
+ pip install -e ../..
161
+ pip install uvicorn[standard]
162
+ AUTH_DISABLED=1 uvicorn app:app --reload
163
+ ```
164
+
165
+ Open http://127.0.0.1:8000