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.
- tuskdata-0.2.0/.gitignore +47 -0
- tuskdata-0.2.0/LICENSE +21 -0
- tuskdata-0.2.0/PKG-INFO +466 -0
- tuskdata-0.2.0/README.md +406 -0
- tuskdata-0.2.0/pyproject.toml +99 -0
- tuskdata-0.2.0/src/tusk/__init__.py +3 -0
- tuskdata-0.2.0/src/tusk/admin/__init__.py +42 -0
- tuskdata-0.2.0/src/tusk/admin/backup.py +360 -0
- tuskdata-0.2.0/src/tusk/admin/extensions.py +148 -0
- tuskdata-0.2.0/src/tusk/admin/maintenance.py +216 -0
- tuskdata-0.2.0/src/tusk/admin/monitoring.py +522 -0
- tuskdata-0.2.0/src/tusk/admin/pitr.py +487 -0
- tuskdata-0.2.0/src/tusk/admin/processes.py +87 -0
- tuskdata-0.2.0/src/tusk/admin/roles.py +276 -0
- tuskdata-0.2.0/src/tusk/admin/settings.py +185 -0
- tuskdata-0.2.0/src/tusk/admin/stats.py +73 -0
- tuskdata-0.2.0/src/tusk/cli.py +409 -0
- tuskdata-0.2.0/src/tusk/cluster/__init__.py +15 -0
- tuskdata-0.2.0/src/tusk/cluster/models.py +68 -0
- tuskdata-0.2.0/src/tusk/cluster/scheduler.py +391 -0
- tuskdata-0.2.0/src/tusk/cluster/worker.py +241 -0
- tuskdata-0.2.0/src/tusk/core/__init__.py +62 -0
- tuskdata-0.2.0/src/tusk/core/auth.py +888 -0
- tuskdata-0.2.0/src/tusk/core/config.py +163 -0
- tuskdata-0.2.0/src/tusk/core/connection.py +149 -0
- tuskdata-0.2.0/src/tusk/core/deps.py +84 -0
- tuskdata-0.2.0/src/tusk/core/files.py +151 -0
- tuskdata-0.2.0/src/tusk/core/geo.py +503 -0
- tuskdata-0.2.0/src/tusk/core/history.py +261 -0
- tuskdata-0.2.0/src/tusk/core/logging.py +33 -0
- tuskdata-0.2.0/src/tusk/core/result.py +51 -0
- tuskdata-0.2.0/src/tusk/core/scheduled_tasks.py +271 -0
- tuskdata-0.2.0/src/tusk/core/scheduler.py +229 -0
- tuskdata-0.2.0/src/tusk/core/workspace.py +224 -0
- tuskdata-0.2.0/src/tusk/engines/__init__.py +6 -0
- tuskdata-0.2.0/src/tusk/engines/duckdb_engine.py +374 -0
- tuskdata-0.2.0/src/tusk/engines/polars_engine.py +1044 -0
- tuskdata-0.2.0/src/tusk/engines/postgres.py +465 -0
- tuskdata-0.2.0/src/tusk/engines/sqlite.py +122 -0
- tuskdata-0.2.0/src/tusk/plugins/__init__.py +55 -0
- tuskdata-0.2.0/src/tusk/plugins/base.py +222 -0
- tuskdata-0.2.0/src/tusk/plugins/config.py +131 -0
- tuskdata-0.2.0/src/tusk/plugins/registry.py +167 -0
- tuskdata-0.2.0/src/tusk/plugins/storage.py +123 -0
- tuskdata-0.2.0/src/tusk/plugins/templates.py +101 -0
- tuskdata-0.2.0/src/tusk/studio/__init__.py +1 -0
- tuskdata-0.2.0/src/tusk/studio/app.py +167 -0
- tuskdata-0.2.0/src/tusk/studio/htmx.py +130 -0
- tuskdata-0.2.0/src/tusk/studio/routes/__init__.py +32 -0
- tuskdata-0.2.0/src/tusk/studio/routes/admin.py +1103 -0
- tuskdata-0.2.0/src/tusk/studio/routes/api.py +540 -0
- tuskdata-0.2.0/src/tusk/studio/routes/auth.py +648 -0
- tuskdata-0.2.0/src/tusk/studio/routes/base.py +143 -0
- tuskdata-0.2.0/src/tusk/studio/routes/cluster.py +492 -0
- tuskdata-0.2.0/src/tusk/studio/routes/data.py +958 -0
- tuskdata-0.2.0/src/tusk/studio/routes/files.py +385 -0
- tuskdata-0.2.0/src/tusk/studio/routes/pages.py +86 -0
- tuskdata-0.2.0/src/tusk/studio/routes/scheduler.py +170 -0
- tuskdata-0.2.0/src/tusk/studio/routes/settings.py +97 -0
- tuskdata-0.2.0/src/tusk/studio/static/data.js +2299 -0
- tuskdata-0.2.0/src/tusk/studio/static/studio.js +2833 -0
- tuskdata-0.2.0/src/tusk/studio/static/styles.css +379 -0
- tuskdata-0.2.0/src/tusk/studio/templates/admin.html +916 -0
- tuskdata-0.2.0/src/tusk/studio/templates/base.html +387 -0
- tuskdata-0.2.0/src/tusk/studio/templates/cluster.html +307 -0
- tuskdata-0.2.0/src/tusk/studio/templates/components/card.html +83 -0
- tuskdata-0.2.0/src/tusk/studio/templates/components/feedback.html +239 -0
- tuskdata-0.2.0/src/tusk/studio/templates/components/forms.html +166 -0
- tuskdata-0.2.0/src/tusk/studio/templates/components/map.html +103 -0
- tuskdata-0.2.0/src/tusk/studio/templates/components/table.html +97 -0
- tuskdata-0.2.0/src/tusk/studio/templates/data.html +681 -0
- tuskdata-0.2.0/src/tusk/studio/templates/index.html +394 -0
- tuskdata-0.2.0/src/tusk/studio/templates/login.html +149 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/backups.html +35 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/bloat.html +38 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/extensions.html +30 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/indexes.html +22 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/locks.html +67 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/logs.html +17 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/pitr.html +36 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/processes.html +33 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/replication.html +27 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/roles.html +24 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/settings.html +12 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/slow-queries.html +18 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/admin/stats.html +26 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/job-result.html +30 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/jobs.html +92 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/status.html +42 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/cluster/workers.html +38 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/data/duckdb-extensions.html +27 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/data/file-browser.html +21 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/data/plugin-datasets.html +18 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/data/saved-pipelines.html +20 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/connections-list.html +23 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/files-list.html +27 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/history.html +21 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/results-table.html +35 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/saved-queries.html +15 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/studio/schema-tree.html +35 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/users/audit.html +26 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/users/groups.html +22 -0
- tuskdata-0.2.0/src/tusk/studio/templates/partials/users/list.html +59 -0
- tuskdata-0.2.0/src/tusk/studio/templates/profile.html +125 -0
- 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.
|
tuskdata-0.2.0/PKG-INFO
ADDED
|
@@ -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
|