tuskdata 0.2.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 (105) hide show
  1. tuskdata-0.2.0/.gitignore +47 -0
  2. tuskdata-0.2.0/LICENSE +21 -0
  3. tuskdata-0.2.0/PKG-INFO +466 -0
  4. tuskdata-0.2.0/README.md +406 -0
  5. tuskdata-0.2.0/pyproject.toml +99 -0
  6. tuskdata-0.2.0/src/tusk/__init__.py +3 -0
  7. tuskdata-0.2.0/src/tusk/admin/__init__.py +42 -0
  8. tuskdata-0.2.0/src/tusk/admin/backup.py +360 -0
  9. tuskdata-0.2.0/src/tusk/admin/extensions.py +148 -0
  10. tuskdata-0.2.0/src/tusk/admin/maintenance.py +216 -0
  11. tuskdata-0.2.0/src/tusk/admin/monitoring.py +522 -0
  12. tuskdata-0.2.0/src/tusk/admin/pitr.py +487 -0
  13. tuskdata-0.2.0/src/tusk/admin/processes.py +87 -0
  14. tuskdata-0.2.0/src/tusk/admin/roles.py +276 -0
  15. tuskdata-0.2.0/src/tusk/admin/settings.py +185 -0
  16. tuskdata-0.2.0/src/tusk/admin/stats.py +73 -0
  17. tuskdata-0.2.0/src/tusk/cli.py +409 -0
  18. tuskdata-0.2.0/src/tusk/cluster/__init__.py +15 -0
  19. tuskdata-0.2.0/src/tusk/cluster/models.py +68 -0
  20. tuskdata-0.2.0/src/tusk/cluster/scheduler.py +391 -0
  21. tuskdata-0.2.0/src/tusk/cluster/worker.py +241 -0
  22. tuskdata-0.2.0/src/tusk/core/__init__.py +62 -0
  23. tuskdata-0.2.0/src/tusk/core/auth.py +888 -0
  24. tuskdata-0.2.0/src/tusk/core/config.py +163 -0
  25. tuskdata-0.2.0/src/tusk/core/connection.py +149 -0
  26. tuskdata-0.2.0/src/tusk/core/deps.py +84 -0
  27. tuskdata-0.2.0/src/tusk/core/files.py +151 -0
  28. tuskdata-0.2.0/src/tusk/core/geo.py +503 -0
  29. tuskdata-0.2.0/src/tusk/core/history.py +261 -0
  30. tuskdata-0.2.0/src/tusk/core/logging.py +33 -0
  31. tuskdata-0.2.0/src/tusk/core/result.py +51 -0
  32. tuskdata-0.2.0/src/tusk/core/scheduled_tasks.py +271 -0
  33. tuskdata-0.2.0/src/tusk/core/scheduler.py +229 -0
  34. tuskdata-0.2.0/src/tusk/core/workspace.py +224 -0
  35. tuskdata-0.2.0/src/tusk/engines/__init__.py +6 -0
  36. tuskdata-0.2.0/src/tusk/engines/duckdb_engine.py +374 -0
  37. tuskdata-0.2.0/src/tusk/engines/polars_engine.py +1044 -0
  38. tuskdata-0.2.0/src/tusk/engines/postgres.py +465 -0
  39. tuskdata-0.2.0/src/tusk/engines/sqlite.py +122 -0
  40. tuskdata-0.2.0/src/tusk/plugins/__init__.py +55 -0
  41. tuskdata-0.2.0/src/tusk/plugins/base.py +222 -0
  42. tuskdata-0.2.0/src/tusk/plugins/config.py +131 -0
  43. tuskdata-0.2.0/src/tusk/plugins/registry.py +167 -0
  44. tuskdata-0.2.0/src/tusk/plugins/storage.py +123 -0
  45. tuskdata-0.2.0/src/tusk/plugins/templates.py +101 -0
  46. tuskdata-0.2.0/src/tusk/studio/__init__.py +1 -0
  47. tuskdata-0.2.0/src/tusk/studio/app.py +167 -0
  48. tuskdata-0.2.0/src/tusk/studio/htmx.py +130 -0
  49. tuskdata-0.2.0/src/tusk/studio/routes/__init__.py +32 -0
  50. tuskdata-0.2.0/src/tusk/studio/routes/admin.py +1103 -0
  51. tuskdata-0.2.0/src/tusk/studio/routes/api.py +540 -0
  52. tuskdata-0.2.0/src/tusk/studio/routes/auth.py +648 -0
  53. tuskdata-0.2.0/src/tusk/studio/routes/base.py +143 -0
  54. tuskdata-0.2.0/src/tusk/studio/routes/cluster.py +492 -0
  55. tuskdata-0.2.0/src/tusk/studio/routes/data.py +958 -0
  56. tuskdata-0.2.0/src/tusk/studio/routes/files.py +385 -0
  57. tuskdata-0.2.0/src/tusk/studio/routes/pages.py +86 -0
  58. tuskdata-0.2.0/src/tusk/studio/routes/scheduler.py +170 -0
  59. tuskdata-0.2.0/src/tusk/studio/routes/settings.py +97 -0
  60. tuskdata-0.2.0/src/tusk/studio/static/data.js +2299 -0
  61. tuskdata-0.2.0/src/tusk/studio/static/studio.js +2833 -0
  62. tuskdata-0.2.0/src/tusk/studio/static/styles.css +379 -0
  63. tuskdata-0.2.0/src/tusk/studio/templates/admin.html +916 -0
  64. tuskdata-0.2.0/src/tusk/studio/templates/base.html +387 -0
  65. tuskdata-0.2.0/src/tusk/studio/templates/cluster.html +307 -0
  66. tuskdata-0.2.0/src/tusk/studio/templates/components/card.html +83 -0
  67. tuskdata-0.2.0/src/tusk/studio/templates/components/feedback.html +239 -0
  68. tuskdata-0.2.0/src/tusk/studio/templates/components/forms.html +166 -0
  69. tuskdata-0.2.0/src/tusk/studio/templates/components/map.html +103 -0
  70. tuskdata-0.2.0/src/tusk/studio/templates/components/table.html +97 -0
  71. tuskdata-0.2.0/src/tusk/studio/templates/data.html +681 -0
  72. tuskdata-0.2.0/src/tusk/studio/templates/index.html +394 -0
  73. tuskdata-0.2.0/src/tusk/studio/templates/login.html +149 -0
  74. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/backups.html +35 -0
  75. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/bloat.html +38 -0
  76. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/extensions.html +30 -0
  77. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/indexes.html +22 -0
  78. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/locks.html +67 -0
  79. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/logs.html +17 -0
  80. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/pitr.html +36 -0
  81. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/processes.html +33 -0
  82. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/replication.html +27 -0
  83. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/roles.html +24 -0
  84. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/settings.html +12 -0
  85. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/slow-queries.html +18 -0
  86. tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/stats.html +26 -0
  87. tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/job-result.html +30 -0
  88. tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/jobs.html +92 -0
  89. tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/status.html +42 -0
  90. tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/workers.html +38 -0
  91. tuskdata-0.2.0/src/tusk/studio/templates/partials/data/duckdb-extensions.html +27 -0
  92. tuskdata-0.2.0/src/tusk/studio/templates/partials/data/file-browser.html +21 -0
  93. tuskdata-0.2.0/src/tusk/studio/templates/partials/data/plugin-datasets.html +18 -0
  94. tuskdata-0.2.0/src/tusk/studio/templates/partials/data/saved-pipelines.html +20 -0
  95. tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/connections-list.html +23 -0
  96. tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/files-list.html +27 -0
  97. tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/history.html +21 -0
  98. tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/results-table.html +35 -0
  99. tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/saved-queries.html +15 -0
  100. tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/schema-tree.html +35 -0
  101. tuskdata-0.2.0/src/tusk/studio/templates/partials/users/audit.html +26 -0
  102. tuskdata-0.2.0/src/tusk/studio/templates/partials/users/groups.html +22 -0
  103. tuskdata-0.2.0/src/tusk/studio/templates/partials/users/list.html +59 -0
  104. tuskdata-0.2.0/src/tusk/studio/templates/profile.html +125 -0
  105. tuskdata-0.2.0/src/tusk/studio/templates/users.html +374 -0
@@ -0,0 +1,47 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+
28
+ # IDE
29
+ .idea/
30
+ .vscode/
31
+ *.swp
32
+ *.swo
33
+
34
+ # OS
35
+ .DS_Store
36
+ Thumbs.db
37
+
38
+ # Testing
39
+ .pytest_cache/
40
+ .coverage
41
+ htmlcov/
42
+
43
+ # Build
44
+ *.whl
45
+
46
+ # Internal docs (not for public release)
47
+ TODO.es.md
tuskdata-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jearel Alcantara
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,466 @@
1
+ Metadata-Version: 2.4
2
+ Name: tuskdata
3
+ Version: 0.2.0
4
+ Summary: Modern data platform - PostgreSQL admin + Analytics engine
5
+ Project-URL: Homepage, https://github.com/tuskdata/tuskdata
6
+ Project-URL: Repository, https://github.com/tuskdata/tuskdata
7
+ Project-URL: Issues, https://github.com/tuskdata/tuskdata/issues
8
+ Author: Jearel Alcantara
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: admin,analytics,database,duckdb,etl,polars,postgresql,sql
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Web Environment
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Database
24
+ Classifier: Topic :: Database :: Database Engines/Servers
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: msgspec>=0.18
28
+ Requires-Dist: structlog>=24.0
29
+ Requires-Dist: tomli-w>=1.0
30
+ Provides-Extra: admin
31
+ Requires-Dist: apscheduler>=3.11.2; extra == 'admin'
32
+ Requires-Dist: psutil>=5.9; extra == 'admin'
33
+ Provides-Extra: all
34
+ Requires-Dist: apscheduler>=3.10; extra == 'all'
35
+ Requires-Dist: apscheduler>=3.11.2; extra == 'all'
36
+ Requires-Dist: datafusion>=51.0; extra == 'all'
37
+ Requires-Dist: duckdb>=1.0; extra == 'all'
38
+ Requires-Dist: granian>=2.0; extra == 'all'
39
+ Requires-Dist: litestar>=2.0; extra == 'all'
40
+ Requires-Dist: minijinja>=2.0; extra == 'all'
41
+ Requires-Dist: polars>=1.0; extra == 'all'
42
+ Requires-Dist: psutil>=5.9; extra == 'all'
43
+ Requires-Dist: psycopg[binary]>=3.0; extra == 'all'
44
+ Requires-Dist: pyarrow>=17.0; extra == 'all'
45
+ Provides-Extra: cluster
46
+ Requires-Dist: datafusion>=51.0; extra == 'cluster'
47
+ Requires-Dist: pyarrow>=17.0; extra == 'cluster'
48
+ Provides-Extra: postgres
49
+ Requires-Dist: psycopg[binary]>=3.0; extra == 'postgres'
50
+ Provides-Extra: studio
51
+ Requires-Dist: apscheduler>=3.10; extra == 'studio'
52
+ Requires-Dist: duckdb>=1.0; extra == 'studio'
53
+ Requires-Dist: granian>=2.0; extra == 'studio'
54
+ Requires-Dist: litestar>=2.0; extra == 'studio'
55
+ Requires-Dist: minijinja>=2.0; extra == 'studio'
56
+ Requires-Dist: polars>=1.0; extra == 'studio'
57
+ Requires-Dist: psycopg[binary]>=3.0; extra == 'studio'
58
+ Requires-Dist: pyarrow>=17.0; extra == 'studio'
59
+ Description-Content-Type: text/markdown
60
+
61
+ # Tusk
62
+
63
+ Modern Data Platform — SQL Client, PostgreSQL Admin, Analytics Engine, ETL Pipeline Builder & Plugin System
64
+
65
+ > **Built with Claude**: This project was developed with [Claude Code](https://claude.ai) (Anthropic's AI assistant).
66
+
67
+ ## Features
68
+
69
+ ### SQL Client (Studio)
70
+ - Multi-connection support (PostgreSQL, SQLite, DuckDB)
71
+ - Tabbed SQL editor with CodeMirror 6 (syntax highlighting, autocomplete)
72
+ - Schema browser with FK/PK indicators
73
+ - Query history and saved queries with folders
74
+ - Results grid with sortable, resizable columns
75
+ - Export to CSV/JSON
76
+ - Keyboard shortcuts (Ctrl+Enter, Ctrl+S, Ctrl+N, etc.)
77
+
78
+ ### PostgreSQL Admin
79
+ - Server statistics dashboard with auto-refresh
80
+ - Active queries monitor with kill button
81
+ - Locks monitor with blocking visualization
82
+ - Backup/Restore with pg_dump/pg_restore
83
+ - Table maintenance (VACUUM, ANALYZE, REINDEX)
84
+ - Extensions manager
85
+ - Roles & users management
86
+ - Database settings viewer
87
+ - Replication status and slow query analysis
88
+
89
+ ### Analytics Engine (DuckDB)
90
+ - In-process DuckDB for analytical queries
91
+ - File browser with Parquet, CSV, JSON, SQLite support
92
+ - Drag & drop file loading
93
+ - Export results to Parquet
94
+ - Engine selector (PostgreSQL/DuckDB)
95
+
96
+ ### Data/ETL (Polars + DuckDB)
97
+ - Visual transform pipeline builder (filter, select, sort, group by, rename, drop nulls, limit, join)
98
+ - Engine selector: Auto/DuckDB/Polars with performance metrics
99
+ - OSM/PBF file support via Polars
100
+ - Auto-generated Polars code
101
+ - Export to CSV/Parquet, import to DuckDB/PostgreSQL
102
+ - Drag & drop file upload
103
+
104
+ ### Geo Integration
105
+ - Auto-detect geometry columns (WKT, GeoJSON, EWKT, Hex WKB)
106
+ - Map visualization with MapLibre GL
107
+ - Points, lines, polygons rendering
108
+ - Feature popups and hover tooltips
109
+ - Export to GeoJSON
110
+
111
+ ### Cluster Mode (Plugin)
112
+ - Distributed query processing with DataFusion
113
+ - Scheduler + Worker architecture via Arrow Flight
114
+ - Job persistence to SQLite with retry support
115
+ - Real-time cluster dashboard
116
+ - Connect to remote schedulers or start local cluster from UI
117
+
118
+ ### User Management
119
+ - Single mode (no auth) and multi-user mode
120
+ - Session-based authentication
121
+ - 24 permissions across 6 categories
122
+ - Default groups (Administrators, Data Engineers, Analysts, Viewers)
123
+ - User management UI and CLI commands
124
+
125
+ ## Installation
126
+
127
+ ```bash
128
+ # Core only
129
+ pip install tuskdata
130
+
131
+ # With PostgreSQL support
132
+ pip install tuskdata[postgres]
133
+
134
+ # With full web UI (recommended)
135
+ pip install tuskdata[studio]
136
+
137
+ # Everything
138
+ pip install tuskdata[all]
139
+ ```
140
+
141
+ Or install from source:
142
+
143
+ ```bash
144
+ git clone https://github.com/tuskdata/tuskdata.git
145
+ cd tuskdata
146
+ pip install -e ".[all]"
147
+ ```
148
+
149
+ ## Quick Start
150
+
151
+ ```bash
152
+ # Start the web studio
153
+ tusk studio
154
+ # Open http://127.0.0.1:8000
155
+
156
+ # Start with options
157
+ tusk studio --host 0.0.0.0 --port 3000
158
+
159
+ # Start cluster (dev mode, 3 workers)
160
+ tusk cluster --workers 3
161
+ ```
162
+
163
+ ## CLI Commands
164
+
165
+ ```bash
166
+ tusk studio [options] # Start the web studio
167
+ tusk config [options] # Manage configuration
168
+ tusk scheduler [options] # Start the cluster scheduler
169
+ tusk worker [options] # Start a cluster worker
170
+ tusk cluster [options] # Start local cluster (dev mode)
171
+ tusk users [subcommand] # User management
172
+ tusk auth [subcommand] # Authentication management
173
+ tusk plugins # List installed plugins
174
+ tusk version # Show version
175
+ ```
176
+
177
+ ## Authentication
178
+
179
+ ### Single Mode (Default)
180
+ No authentication required. All features accessible.
181
+
182
+ ### Multi-User Mode
183
+ ```bash
184
+ tusk auth enable # Enable auth mode
185
+ tusk auth init # Create admin user and default groups
186
+ tusk studio # Start studio (login required)
187
+ ```
188
+
189
+ Default credentials: `admin` / `admin`
190
+
191
+ ```bash
192
+ tusk users list # List all users
193
+ tusk users create john --admin # Create admin user
194
+ tusk users create jane # Create regular user
195
+ tusk users reset-password john # Reset password
196
+ ```
197
+
198
+ ## Plugin System
199
+
200
+ Tusk has an extensible plugin architecture. Plugins can add new pages, API endpoints, CLI commands, datasets, templates, static files, and reusable components.
201
+
202
+ ### Installing Plugins
203
+
204
+ ```bash
205
+ pip install tusk-security # Security analysis plugin
206
+ pip install tusk-cluster # Distributed query plugin
207
+ tusk plugins # List installed plugins
208
+ tusk studio # Plugins auto-register on startup
209
+ ```
210
+
211
+ ### How Plugins Work
212
+
213
+ Plugins are Python packages that register via `pyproject.toml` entry points. On startup, Tusk:
214
+
215
+ 1. **Discovers** plugins via `importlib.metadata.entry_points()`
216
+ 2. **Checks** version compatibility
217
+ 3. **Copies** plugin templates to `templates/plugins/{id}/`
218
+ 4. **Copies** plugin static files to `static/plugins/{id}/`
219
+ 5. **Mounts** plugin routes alongside core routes
220
+ 6. **Calls** `on_startup()` lifecycle hook
221
+
222
+ Each plugin gets:
223
+ - **Sidebar tab** with icon and label
224
+ - **Isolated SQLite storage** at `~/.tusk/plugins/{id}.db`
225
+ - **TOML config file** at `~/.tusk/plugins/{id}.toml`
226
+ - **Template directory** accessible as `plugins/{id}/`
227
+ - **Static files** served at `/static/plugins/{id}/`
228
+ - **Dataset integration** — plugin tables queryable via DuckDB
229
+
230
+ ### Creating a Plugin
231
+
232
+ ```python
233
+ # my_plugin/__init__.py
234
+ from tusk.plugins.base import TuskPlugin
235
+ from pathlib import Path
236
+
237
+ class MyPlugin(TuskPlugin):
238
+ @property
239
+ def name(self) -> str:
240
+ return "tusk-myplugin"
241
+
242
+ @property
243
+ def version(self) -> str:
244
+ return "0.1.0"
245
+
246
+ @property
247
+ def tab_label(self) -> str:
248
+ return "My Plugin"
249
+
250
+ @property
251
+ def tab_icon(self) -> str:
252
+ return "puzzle" # Lucide icon name
253
+
254
+ @property
255
+ def requires_storage(self) -> bool:
256
+ return True
257
+
258
+ def get_templates_path(self) -> Path | None:
259
+ return Path(__file__).parent / "templates"
260
+
261
+ def get_static_path(self) -> Path | None:
262
+ return Path(__file__).parent / "static"
263
+
264
+ def get_route_handlers(self) -> list:
265
+ from .routes import MyPageController, MyAPIController
266
+ return [MyPageController, MyAPIController]
267
+
268
+ def get_datasets(self) -> list[dict]:
269
+ return [{"name": "items", "table": "items", "description": "Plugin items"}]
270
+
271
+ def get_cli_commands(self) -> dict:
272
+ return {"myplugin": self.handle_cli}
273
+
274
+ async def on_startup(self) -> None:
275
+ from .db import init_database
276
+ init_database()
277
+ ```
278
+
279
+ Register in `pyproject.toml`:
280
+ ```toml
281
+ [project.entry-points."tusk.plugins"]
282
+ myplugin = "my_plugin:MyPlugin"
283
+ ```
284
+
285
+ ### Plugin Templates & Components
286
+
287
+ Plugins can use all core UI components via MiniJinja macros:
288
+
289
+ ```html
290
+ {# Plugin template — extends base.html like any core page #}
291
+ {% extends "base.html" %}
292
+ {% from "components/feedback.html" import badge, modal, alert %}
293
+ {% from "components/card.html" import stat_card, info_card %}
294
+ {% from "components/map.html" import map_assets, map_container, carto_dark_style %}
295
+ {% from "components/htmx.html" import htmx_poll, htmx_tabs %}
296
+
297
+ {% block content %}
298
+ {{ stat_card(label="Total Items", value=42, icon="box", color="blue") }}
299
+
300
+ {% call modal(id="create-item", title="Create Item", icon="plus") %}
301
+ <form>...</form>
302
+ {% endcall %}
303
+ {% endblock %}
304
+ ```
305
+
306
+ Plugins can also create their own reusable components:
307
+
308
+ ```html
309
+ {# Plugin-specific component at plugins/bi/components/chart.html #}
310
+ {% macro bar_chart(data, x, y, height="300px") %}
311
+ <div class="chart-container" style="height: {{ height }}">...</div>
312
+ {% endmacro %}
313
+ ```
314
+
315
+ Other templates (core or plugin) can import these:
316
+ ```html
317
+ {% from "plugins/bi/components/chart.html" import bar_chart %}
318
+ {{ bar_chart(data=sales, x="month", y="revenue") }}
319
+ ```
320
+
321
+ ### Plugin Static Files
322
+
323
+ Plugins with `get_static_path()` get their static files served automatically:
324
+
325
+ ```
326
+ my_plugin/
327
+ ├── __init__.py
328
+ ├── static/ # get_static_path() points here
329
+ │ ├── chart.js # Served at /static/plugins/myplugin/chart.js
330
+ │ └── styles.css # Served at /static/plugins/myplugin/styles.css
331
+ └── templates/
332
+ └── dashboard.html
333
+ ```
334
+
335
+ ```html
336
+ {# In plugin template #}
337
+ <script src="/static/plugins/myplugin/chart.js"></script>
338
+ ```
339
+
340
+ ## Component Library
341
+
342
+ Tusk includes a MiniJinja macro library for consistent UI across core pages and plugins.
343
+
344
+ | File | Macros |
345
+ |------|--------|
346
+ | `components/card.html` | `stat_card()`, `info_card()`, `simple_card()`, `loading_card()`, `metric_row()` |
347
+ | `components/table.html` | `data_table()`, `simple_table()`, `key_value_table()` |
348
+ | `components/forms.html` | `text_input()`, `select_input()`, `checkbox()`, `toggle()`, `button()`, `icon_button()`, `form_group()` |
349
+ | `components/feedback.html` | `badge()`, `severity_badge()`, `status_badge()`, `alert()`, `empty_state()`, `modal()`, `confirmation_dialog()`, `loading_spinner()`, `progress_bar()`, `tooltip()` |
350
+ | `components/htmx.html` | `htmx_table()`, `htmx_poll()`, `htmx_tabs()`, `htmx_search()`, `htmx_form()` |
351
+ | `components/map.html` | `map_assets()`, `map_container()`, `map_dark_styles()`, `carto_dark_style()` |
352
+
353
+ ## Architecture
354
+
355
+ ### Project Structure
356
+ ```
357
+ src/tusk/
358
+ ├── cli.py # CLI entry point
359
+ ├── core/ # Core functionality
360
+ │ ├── config.py # Global configuration
361
+ │ ├── connection.py # Connection registry
362
+ │ ├── auth.py # Authentication system
363
+ │ ├── files.py # File scanning
364
+ │ ├── geo.py # GeoJSON/WKT utilities
365
+ │ ├── history.py # Query history
366
+ │ ├── logging.py # Structlog setup
367
+ │ ├── scheduler.py # Task scheduler
368
+ │ └── result.py # QueryResult struct
369
+ ├── engines/ # Query engines
370
+ │ ├── duckdb_engine.py # DuckDB analytics
371
+ │ ├── polars_engine.py # Polars ETL
372
+ │ ├── postgres.py # PostgreSQL
373
+ │ └── sqlite.py # SQLite
374
+ ├── admin/ # PostgreSQL admin modules
375
+ │ ├── stats.py # Server statistics
376
+ │ ├── processes.py # Active queries
377
+ │ ├── backup.py # Backup/restore
378
+ │ ├── extensions.py # Extensions manager
379
+ │ ├── roles.py # Role management
380
+ │ ├── settings.py # Settings viewer
381
+ │ └── maintenance.py # Table maintenance
382
+ ├── plugins/ # Plugin system
383
+ │ ├── base.py # TuskPlugin abstract base class
384
+ │ ├── registry.py # Discovery via entry_points
385
+ │ ├── storage.py # Per-plugin SQLite storage
386
+ │ ├── config.py # Per-plugin TOML config
387
+ │ └── templates.py # Template & static file loader
388
+ └── studio/ # Web UI
389
+ ├── app.py # Litestar application
390
+ ├── routes/ # API & page controllers
391
+ ├── static/ # JS, CSS, plugin statics
392
+ └── templates/ # HTML templates
393
+ ├── base.html # Base layout (loads Alpine, HTMX, Tailwind, Lucide)
394
+ ├── components/ # Reusable MiniJinja macros
395
+ │ ├── card.html
396
+ │ ├── table.html
397
+ │ ├── forms.html
398
+ │ ├── feedback.html
399
+ │ ├── htmx.html
400
+ │ └── map.html
401
+ ├── partials/ # HTMX partial responses
402
+ └── plugins/ # Plugin templates (copied at startup)
403
+ ```
404
+
405
+ ### Tech Stack
406
+
407
+ | Layer | Technology |
408
+ |-------|-----------|
409
+ | Web Framework | Litestar 2.x |
410
+ | Server | Granian |
411
+ | Templates | MiniJinja |
412
+ | Serialization | msgspec (Structs) |
413
+ | CSS | Tailwind CSS |
414
+ | Interactivity | Alpine.js + HTMX |
415
+ | Icons | Lucide |
416
+ | Maps | MapLibre GL 4.1 |
417
+ | DataFrames | Polars |
418
+ | PostgreSQL | psycopg 3.x (async) |
419
+ | Analytics | DuckDB |
420
+ | Distributed | Arrow Flight + DataFusion |
421
+ | Logging | structlog |
422
+
423
+ ## Configuration
424
+
425
+ ```
426
+ ~/.tusk/
427
+ ├── config.toml # Global settings
428
+ ├── connections.toml # Saved connections
429
+ ├── history.db # Query history (SQLite)
430
+ ├── auth.db # Users/groups (multi-user mode)
431
+ ├── backups/ # Database backups
432
+ └── plugins/ # Plugin storage
433
+ ├── security.db # Plugin SQLite databases
434
+ ├── security.toml # Plugin config files
435
+ └── ...
436
+ ```
437
+
438
+ ```bash
439
+ tusk config show
440
+ tusk config set pg_bin_path /usr/local/pgsql/bin
441
+ tusk config set port 3000
442
+ tusk config set auth_mode multi
443
+ ```
444
+
445
+ ## Keyboard Shortcuts
446
+
447
+ | Shortcut | Action |
448
+ |----------|--------|
449
+ | Ctrl+Enter | Execute query |
450
+ | Ctrl+S | Save query |
451
+ | Ctrl+N / Ctrl+T | New tab |
452
+ | Ctrl+W | Close tab |
453
+ | Ctrl+Space | Autocomplete |
454
+ | F5 | Refresh schema |
455
+ | Escape | Cancel query |
456
+
457
+ ## Known Limitations
458
+
459
+ 1. **Cluster Mode**: Requires scheduler/workers to be running. Local cluster spawns subprocesses.
460
+ 2. **Auth System**: Sessions stored in SQLite. Server restart does not invalidate sessions.
461
+ 3. **CDN Dependencies**: Frontend libraries (Tailwind, Alpine, HTMX, MapLibre, Lucide) loaded via CDN. No offline mode yet.
462
+ 4. **Large Files**: Performance may degrade with files larger than 500MB.
463
+
464
+ ## License
465
+
466
+ MIT