- Go 61.6%
- Vue 30.8%
- TypeScript 5.2%
- Makefile 1.5%
- Dockerfile 0.4%
- Other 0.4%
|
All checks were successful
release / docker (push) Successful in 12m42s
- refreshServerData: cleanup volatile tables after successful ping
(not before like the old cleanupServerData), preventing duplicate
entries on each collection cycle
- DuckDB: new connection_detail_history table stores per-client
connection details (user, host, app, state, timestamps)
- API: GET /api/servers/{id}/clients returns the most recent
connection per unique (user@host, database) pair
- Frontend: Last Seen Clients table on server detail page showing
when each client was last connected
|
||
|---|---|---|
| .forgejo/workflows | ||
| api | ||
| cmd/seed | ||
| frontend | ||
| internal | ||
| web | ||
| .air.toml | ||
| .dockerignore | ||
| .gitignore | ||
| AGENTS.md | ||
| Design.md | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| Makefile | ||
| README.md | ||
SQL Panel
A web dashboard for monitoring PostgreSQL and MSSQL database servers. Collects server metrics, database inventory, active connections, and historical trends — all accessible via a Vue 3 SPA.
Features
- Server inventory — add, edit, manage monitored database servers with autogenerated passwords and permission SQL snippets
- Live snapshots — OS, version, RAM, CPU, disk, uptime, connection counts for both PG and MSSQL
- Database discovery — sizes, owners, collation, encoding, data vs WAL/log sizes across all servers
- Database objects — tables, indexes, views, procedures, triggers, sequences, foreign keys
- Active connections — detailed connection table (user, host, app, state, duration, last seen) with full parity across PG and MSSQL
- Historical data — size, connections, CPU/memory/disk trends stored in DuckDB with configurable retention and automatic downsampling
- Time-series charts — auto-scaled line charts (bytes in MB/GB, percentages) with timeframe selector (7d/30d/90d/All)
- Databases page — sortable, filterable table of all databases across all servers with JSON/CSV export
- Role-based access — admin (full CRUD) and viewer (read-only) roles; local user management page
- API token authentication — Bearer token auth with
read/writescopes, managed at/admin/tokens - LDAP authentication — bind auth with recursive group membership resolution, auto-provisioning
- Password self-service — users can change their own password
- PostgreSQL collection — extensions, tablespaces, config parameters, replication slots, running queries, pending restart settings, indexes, views, procedures, triggers, sequences, foreign keys
- MSSQL collection — edition, version, availability groups, agent jobs, running queries, config parameters, tables, indexes, views, procedures, triggers, sequences, foreign keys
- On-demand collection — manual collect button per server, auto-collect on server creation
- Embedded frontend — single binary with no separate static file server (Vue 3 + Tailwind v4)
- Swagger API docs — interactive OpenAPI documentation at
/api/swagger/(17 documented endpoints) - Docker support — multi-stage build with Debian base (glibc) for DuckDB compatibility
- Cross-platform builds —
make build-allfor Linux, macOS, Windows on amd64 and arm64 - Dark mode — default-on dark theme with toggle, persisted to localStorage
- Seed data —
make seedpopulates fictional data for development
Quick start
Docker
docker build -t sqlpanel .
docker run -d -p 8080:8080 --name sqlpanel sqlpanel
Default login: admin / admin (override via SP_DEFAULT_ADMIN_PASSWORD).
Local development
Requirements: Go 1.26+, Node 24+, Make.
# Install frontend deps
cd frontend && npm install && cd ..
# Build (Vue → Go)
make build
# Run
make run
Or with live frontend reload:
make dev
This starts Vite on port 3000 (proxying /api to Go on 8080) and the Go server concurrently.
For network-accessible dev (e.g. testing on another device):
make dev-host
Disables authentication and makes the Vite dev server accessible on 0.0.0.0:3000.
Test collectors against real servers
make test-collectors
Starts PostgreSQL and MSSQL Docker containers, runs the collector, and prints the collected data.
Configuration
Configuration is loaded from TOML files (config.toml in the current directory, ~/.config/sqlpanel/, or /etc/sqlpanel/) and environment variables with the SP_ prefix.
| Config key | Env var | Default | Description | |
|---|---|---|---|---|
server.host |
SP_SERVER_HOST |
0.0.0.0 |
HTTP listen address | |
server.port |
SP_SERVER_PORT |
8080 |
HTTP listen port | |
auth.disable |
SP_AUTH_DISABLE |
false |
Disable authentication (dev only) | |
auth.ldap.enabled |
SP_AUTH_LDAP_ENABLED |
false |
Enable LDAP authentication | |
auth.ldap.url |
SP_AUTH_LDAP_URL |
"" |
LDAP server URL (ldap:// or ldaps://) | |
auth.ldap.bind_dn |
SP_AUTH_LDAP_BIND_DN |
"" |
LDAP bind DN for user search | |
auth.ldap.bind_password |
SP_AUTH_LDAP_BIND_PASSWORD |
"" |
LDAP bind password | |
auth.ldap.search_base |
SP_AUTH_LDAP_SEARCH_BASE |
"" |
LDAP search base DN | |
auth.ldap.user_filter |
SP_AUTH_LDAP_USER_FILTER |
(uid=%s) |
LDAP user search filter (%s = username) | |
auth.ldap.admin_group_dn |
SP_AUTH_LDAP_ADMIN_GROUP_DN |
"" |
LDAP group DN that maps to admin role | |
auth.ldap.viewer_group_dn |
SP_AUTH_LDAP_VIEWER_GROUP_DN |
"" |
LDAP group DN that maps to viewer role | |
| — | SP_DEFAULT_ADMIN_PASSWORD |
admin |
Default admin password (first run only) | |
| — | SP_COLLECT_INTERVAL |
5m |
Collector run interval | |
log.format |
SP_LOG_FORMAT |
text |
Log output format (text or json) |
|
log.level |
SP_LOG_LEVEL |
info |
Log level (debug, info, warn, error) |
|
debug |
SP_DEBUG |
false |
Overrides log level to debug |
|
database.path |
SP_DATABASE_PATH |
data/sqlpanel.db |
SQLite database path | |
database.historical_path |
SP_DATABASE_HISTORICAL_PATH |
data/historical.duckdb |
Historical data path | |
retention.detail |
SP_RETENTION_DETAIL |
168h |
Keep all records for this duration | |
retention.hourly |
SP_RETENTION_HOURLY |
720h |
Keep 1/hour until this | |
retention.daily |
SP_RETENTION_DAILY |
8760h |
Keep 1/day until this |
Retention
Historical data is cleaned automatically every hour:
| Period | Behavior |
|---|---|
< detail |
All records kept as-is |
detail → hourly |
1 row per hour per entity, rest deleted |
hourly → daily |
1 row per day per entity, rest deleted |
> daily |
All rows deleted |
API
LDAP authentication
When auth.ldap.enabled is set, users not found in the local database are
authenticated against the LDAP server. On first successful login the user is
provisioned automatically with a role determined by group membership.
Group resolution is recursive — all nested group memberships are resolved to determine the user's final role.
Example config.toml:
[auth.ldap]
enabled = true
url = "ldaps://dc.example.com:636"
bind_dn = "cn=admin,dc=example,dc=com"
bind_password = "secret"
search_base = "dc=example,dc=com"
user_filter = "(sAMAccountName=%s)"
admin_group_dn = "cn=sqlpanel-admins,ou=Groups,dc=example,dc=com"
viewer_group_dn = "cn=sqlpanel-viewers,ou=Groups,dc=example,dc=com"
Full interactive Swagger documentation is available at /api/swagger/ when the
server is running. The spec is generated from Go annotations via swag init
and embedded in the binary.
All endpoints except /api/health and /api/auth/login require authentication.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/swagger/ |
— | Swagger UI (interactive docs) |
GET |
/api/health |
— | Database health check |
POST |
/api/auth/login |
— | Login with username/password |
POST |
/api/auth/logout |
— | Clear session |
GET |
/api/auth/me |
user | Current user info |
PUT |
/api/auth/password |
user | Change own password |
GET |
/api/servers |
user | List all servers |
POST |
/api/servers |
admin | Create server |
GET |
/api/servers/{id} |
user | Get server details |
PUT |
/api/servers/{id} |
admin | Update server |
DELETE |
/api/servers/{id} |
admin | Delete server |
GET |
/api/servers/{id}/databases |
user | List databases for server |
GET |
/api/servers/{id}/overview |
user | Server dashboard (snapshot + databases + connections) |
POST |
/api/servers/{id}/collect |
admin | Trigger data collection now |
GET |
/api/servers/{id}/history/{metric} |
user | Time-series data (database_size, connections, cpu_memory, disk_usage) |
GET |
/api/databases/{id} |
user | Database detail (tables, indexes, views, etc.) |
GET |
/api/admin/users |
admin | List local users |
POST |
/api/admin/users |
admin | Create local user |
PUT |
/api/admin/users/{id} |
admin | Update user password/role |
DELETE |
/api/admin/users/{id} |
admin | Delete user |
GET |
/api/admin/tokens |
admin | List API tokens |
POST |
/api/admin/tokens |
admin | Create API token |
DELETE |
/api/admin/tokens/{id} |
admin | Revoke API token |
Architecture
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Vue 3 SPA │────▶│ Go HTTP API │────▶│ SQLite (DB) │
│ (embedded) │ │ net/http │ │ config + │
│ Tailwind v4 │ │ + custom │ │ collected │
└─────────────┘ │ router │ │ data │
│ │ └──────────────┘
│ Collector │ ┌──────────────┐
│ (bg worker) │────▶│ DuckDB │
│ PG + MSSQL │ │ historical │
└──────────────┘ │ + retention │
└──────────────┘
- Collector — background goroutine that connects to each configured server on an interval, runs discovery queries, and writes results to the SQLite database.
- Retention manager — runs hourly, downsamples DuckDB historical data according to configurable retention periods using
date_trunctime bucketing. - Auth — passwords hashed with bcrypt, sessions stored in SQLite with HttpOnly cookies. API token auth via
Authorization: Bearer sp_.... Admin and viewer roles enforced via middleware. LDAP bind auth with recursive group resolution.
Project structure
main.go # Entry point (swagger annotations)
api/ # HTTP handlers + swagger.json/ui
internal/
auth/ # Login/logout, LDAP auth, token auth, middleware
collector/ # PG + MSSQL data collection + DuckDB history writer
config/ # Viper config loader
database/ # DB connections, embedded migrations (golang-migrate)
migrations/ # Versioned SQL migration files
retention/ # DuckDB historical data downsampling
server/ # HTTP server, custom router, routes
middlewares/ # Request logging middleware
session/ # SQLite-backed session store
cmd/seed/ # Fictional data generator for dev/testing
web/ # Embedded Vue dist (//go:embed)
vue-dist/ # Built frontend (generated)
frontend/ # Vue 3 + TypeScript + Vite + Tailwind v4
src/
api/ # API client + export utilities
components/ # Layout, TimeSeriesChart, TimeframeSelector
composables/ # Theme composable (dark mode)
stores/ # Auth state store
views/ # All page components
router/ # Vue Router config
License
GNU General Public License v2.0. See LICENSE.