{"slug": "i-built-an-open-source-admin-panel-builder-for-existing-databases", "title": "I built an open-source admin panel builder for existing databases", "summary": "DBoard, an open-source admin panel builder created by developer Haimanot25, uses AI to generate admin panels, dashboards, forms, and queries from natural language prompts for existing databases, supporting PostgreSQL, MySQL, MongoDB, SQLite, Supabase, and SQL Server. The tool, available on GitHub, offers zero-code CRUD interfaces, AI-powered dashboards, a SQL console, schema diff, database monitoring, and self-hosting via Docker, with features like CSRF protection, AES-256-GCM encryption, and audit logs.", "body_md": "**Build admin panels and dashboards for any database — zero code, AI-powered.**\n\nDBoard connects to your existing database and uses AI to instantly generate admin panels, dashboards, forms, and queries from a simple natural language prompt. No coding required.\n\nConnect PostgreSQL, MySQL, MongoDB, SQLite, or Supabase — describe what you want in plain English — and DBoard builds it. Edit rows with validated forms, visualize data with auto-generated charts, run queries with a built-in SQL console, and share dashboards with your team.\n\n[\n](/Haimanot25/dboard/blob/main/public/ss/screenshot-dashboard.png)*Dashboard with charts, metrics, and real-time data*\n\n[\n](/Haimanot25/dboard/blob/main/public/ss/screenshot-query-editor.png)*SQL query editor with syntax highlighting and saved queries*\n\n[\n](/Haimanot25/dboard/blob/main/public/ss/screenshot-db-monitor.png)*Database monitoring with live metrics and table inventory*\n\n[\n](/Haimanot25/dboard/blob/main/public/ss/screenshot-schema-diff.png)*Schema comparison between two databases*\n\n**Zero-Code Admin Panels**— Generate full CRUD interfaces from a natural language prompt — no templates, no configuration** AI-Powered Dashboards**— \"Show me monthly revenue by product category\" → instant chart with no SQL needed** Multi-Database**— Connect PostgreSQL, MySQL, SQLite, MongoDB, Supabase, or SQL Server through one unified interface** Database-First**— Works with your existing database. No data migration, no vendor lock-in, no schema redesign** SQL Console**— Write and execute queries with syntax highlighting, saved queries, history, and AI-powered generation** Data Grid**— Browse, search, filter, sort, inline edit, bulk delete, and import/export (CSV, JSON, Excel, PDF)** Schema Diff**— Compare schemas across two databases and visualize differences instantly** DB Monitor**— Real-time health checks, table inventory, row counts, and performance metrics** Sharing & Collaboration**— Share connections and dashboards with read/write/admin permissions** Webhooks**— Send real-time notifications to Slack, Discord, PagerDuty, or custom HTTP endpoints** API Keys**— Generate scoped API keys for programmatic access to your databases** Secure**— CSRF protection, SSRF guard, AES-256-GCM encryption at rest, rate limiting, audit logs** Self-Hosted**— Your data never leaves your server. Deploy with Docker in one command** Dark Mode**— Eye-friendly dark theme with 6 built-in color presets and full customization\n\n| Database | Status | Default Port | Adapter | Notes |\n|---|---|---|---|---|\n| PostgreSQL | ✅ | 5432 | SQL (Knex) | Full support including pg_stat views |\n| MySQL | ✅ | 3306 | SQL (Knex) | Full support |\n| SQLite | ✅ | — | SQL (Knex) | File-based, no network config |\n| MongoDB | ✅ | 27017 | Native | Document introspection, aggregation |\n| Supabase | ✅ | — | REST API | Uses Supabase JS client |\n| SQL Server | ✅ | 1433 | SQL (Knex) | Full support via mssql driver |\n\n- Node.js 20+ (recommended: use\n[nvm](https://github.com/nvm-sh/nvm)) - npm, yarn, or pnpm\n- Git\n\n```\n# 1. Clone the repository\ngit clone https://github.com/Haimanot25/dboard.git\ncd dboard\n\n# 2. Install dependencies\nnpm install\n\n# 3. Copy environment file\ncp .env.example .env\n\n# 4. Generate secrets (paste output into .env)\nopenssl rand -base64 32   # Use for NEXTAUTH_SECRET\nopenssl rand -base64 32   # Use for ENCRYPTION_KEY\n\n# 5. Initialize the database\nnpx prisma generate\nnpx prisma db push\n\n# 6. Start the development server\nnpm run dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) and register your first account.\n\n```\n# Set secrets in your shell\nexport NEXTAUTH_SECRET=$(openssl rand -base64 32)\nexport ENCRYPTION_KEY=$(openssl rand -base64 32)\n\n# Start with Docker Compose\ndocker compose up -d\n```\n\nOpen [http://localhost:3000](http://localhost:3000).\n\n| Variable | Description | Required | Default |\n|---|---|---|---|\n`DATABASE_URL` |\nSQLite URL for Prisma (application metadata) | Yes | `file:./dev.db` |\n`NEXTAUTH_SECRET` |\nSecret for NextAuth.js session encryption | Yes | — |\n`NEXTAUTH_URL` |\nCanonical URL for NextAuth.js | Yes | `http://localhost:3000` |\n`ENCRYPTION_KEY` |\nKey for encrypting database passwords and API keys (AES-256-GCM) | Yes | — |\n`ALLOW_PRIVATE_DB_HOSTS` |\nSet to `1` to allow connections to localhost/private IPs (dev only) |\nNo | `0` |\n\nProduction:Generate strong secrets with`openssl rand -base64 32`\n\n. Never use default or placeholder values.\n\n| Layer | Technology |\n|---|---|\n| Framework | Next.js 14 (App Router) |\n| Language | TypeScript 5 (strict mode) |\n| Database ORM | Prisma 5 |\n| Authentication | NextAuth.js 4 (Credentials + JWT) |\n| UI Components | shadcn/ui (Radix UI + Tailwind CSS) |\n| State Management | TanStack React Query 5 |\n| Table Grid | TanStack React Table 8 |\n| Drag & Drop | @dnd-kit |\n| Command Palette | cmdk |\n| Charts | Custom SVG (Bar, Line, Pie, Sparkline, Heatmap) |\n| Testing | Vitest (unit) + Playwright (E2E) |\n| Linting | ESLint + eslint-plugin-security |\n| Dead Code Detection | Knip |\n\n```\ndboard/\n├── .github/\n│   └── workflows/ci.yml       # CI/CD pipeline (7 jobs)\n├── docs/                       # Screenshots and documentation assets\n├── e2e/                        # Playwright E2E tests\n│   ├── auth.spec.ts\n│   ├── connections.spec.ts\n│   ├── dashboard.spec.ts\n│   ├── data-browse.spec.ts\n│   └── query.spec.ts\n├── prisma/\n│   ├── schema.prisma           # Database schema (16 models)\n│   └── seed.ts                 # Database seed script\n├── public/                     # Static assets (logos, favicon)\n├── scripts/\n│   └── seed-demo-db.sql        # Demo database seed SQL\n├── src/\n│   ├── app/\n│   │   ├── (auth)/             # Auth route group\n│   │   │   ├── login/\n│   │   │   └── layout.tsx\n│   │   ├── (dashboard)/        # Dashboard route group\n│   │   │   ├── connections/    # Connection management\n│   │   │   │   ├── page.tsx    # List connections\n│   │   │   │   └── [id]/       # Connection detail\n│   │   │   │       ├── tables/ # Browse tables\n│   │   │   │       ├── query/  # SQL editor\n│   │   │   │       ├── schema/ # Schema viewer\n│   │   │   │       ├── settings/# Connection settings\n│   │   │   │       └── edit/   # Edit connection\n│   │   │   ├── dashboards/     # Dashboard management\n│   │   │   │   ├── page.tsx    # List dashboards\n│   │   │   │   └── [id]/       # Dashboard detail\n│   │   │   ├── admin-pages/    # Admin page management\n│   │   │   ├── settings/       # Application settings\n│   │   │   │   ├── ai/         # AI provider config\n│   │   │   │   ├── profile/    # User profile\n│   │   │   │   ├── theme/      # Theme editor\n│   │   │   │   ├── plugins/    # Plugin manager\n│   │   │   │   ├── audit-logs/ # Audit log viewer\n│   │   │   │   ├── notifications/\n│   │   │   │   ├── templates/  # Dashboard templates\n│   │   │   │   └── backup/     # Backup & restore\n│   │   │   ├── db-monitor/     # Database monitoring\n│   │   │   ├── ai/             # AI generation page\n│   │   │   ├── schema-diff/    # Schema comparison\n│   │   │   └── profile/        # User profile page\n│   │   ├── api/                # API routes (80 endpoints)\n│   │   │   ├── auth/           # Authentication\n│   │   │   ├── connections/    # Connection CRUD + health + info\n│   │   │   ├── dashboards/     # Dashboard CRUD + charts + shares\n│   │   │   ├── data/           # Table data CRUD + bulk operations\n│   │   │   ├── query/          # SQL execution + saved + history + AI\n│   │   │   ├── schema/         # Introspection + diff + config\n│   │   │   ├── ai/             # AI generation + providers\n│   │   │   ├── settings/       # Profile + notifications\n│   │   │   ├── admin-pages/    # Admin page CRUD\n│   │   │   ├── views/          # Saved views\n│   │   │   ├── favorites/      # Favorite toggle\n│   │   │   ├── shares/         # Connection sharing\n│   │   │   ├── webhooks/       # Webhook CRUD + actions\n│   │   │   ├── api-keys/       # API key CRUD\n│   │   │   ├── activity/       # Activity feed\n│   │   │   ├── audit-logs/     # Audit log viewer\n│   │   │   ├── alerts/         # Alert management\n│   │   │   ├── plugins/        # Plugin registry\n│   │   │   ├── theme/          # Theme presets\n│   │   │   ├── csrf/           # CSRF token\n│   │   │   ├── export/         # Data export\n│   │   │   ├── import/         # Data import\n│   │   │   └── dashboard-templates/\n│   │   ├── layout.tsx          # Root layout\n│   │   ├── providers.tsx       # React Query + Theme providers\n│   │   └── globals.css         # Global styles\n│   ├── components/\n│   │   ├── layout/             # Layout components\n│   │   │   ├── DashboardShell.tsx\n│   │   │   ├── Sidebar.tsx\n│   │   │   └── TopNav.tsx\n│   │   ├── ui/                 # shadcn/ui primitives\n│   │   ├── shared/             # Shared components\n│   │   ├── dashboard/          # Dashboard components\n│   │   ├── data-grid/          # Data grid components\n│   │   ├── connections/        # Connection components\n│   │   ├── query/              # Query editor components\n│   │   ├── schema/             # Schema components\n│   │   ├── settings/           # Settings components\n│   │   ├── ai/                 # AI components\n│   │   └── db-monitor/         # Database monitoring components\n│   ├── hooks/                  # 20 custom React hooks\n│   ├── lib/\n│   │   ├── ai/                 # AI providers and generation\n│   │   ├── db/\n│   │   │   ├── drivers/        # Database adapters\n│   │   │   ├── encryption.ts   # AES-256-GCM encryption\n│   │   │   ├── ssrf-guard.ts   # SSRF protection\n│   │   │   └── plugins/        # Adapter plugin system\n│   │   ├── theme/              # Theme system\n│   │   ├── widgets/            # Widget plugin registry\n│   │   ├── webhooks/           # Webhook plugin registry\n│   │   ├── schema/             # Schema caching\n│   │   ├── crud/               # Query builder\n│   │   ├── auth.ts             # Authentication config\n│   │   ├── auth-helpers.ts     # Auth utilities\n│   │   ├── csrf.ts             # CSRF protection\n│   │   ├── permissions.ts      # Permission system\n│   │   ├── api-keys.ts         # API key management\n│   │   ├── audit.ts            # Audit logging\n│   │   ├── rate-limit.ts       # Rate limiting\n│   │   ├── with-rate-limit.ts  # Rate limit middleware\n│   │   ├── login-rate-limit.ts # Login rate limiting\n│   │   ├── sql-guard.ts        # SQL write detection\n│   │   ├── prisma.ts           # Prisma client\n│   │   └── utils.ts            # Utility functions\n│   ├── types/                  # TypeScript type definitions\n│   └── generated/              # Prisma generated client\n├── tests/                      # Unit/integration tests\n├── .env.example                # Environment template\n├── .eslintrc.json              # ESLint config\n├── .gitignore                  # Git ignore rules\n├── .gitleaks.toml              # Secret scanning config\n├── components.json             # shadcn/ui config\n├── docker-compose.yml          # Docker Compose config\n├── Dockerfile                  # Multi-stage Docker build\n├── knip.json                   # Dead code detection config\n├── next.config.mjs             # Next.js config + security headers\n├── package.json                # Dependencies and scripts\n├── playwright.config.ts        # E2E test config\n├── postcss.config.mjs          # PostCSS config\n├── tailwind.config.ts          # Tailwind CSS config\n├── tsconfig.json               # TypeScript config\n├── vitest.config.ts            # Vitest test config\n├── CONTRIBUTING.md             # Contributing guidelines\n├── CODE_OF_CONDUCT.md          # Code of conduct\n├── LICENSE                     # MIT License\n├── SECURITY.md                 # Security policy\n├── CHANGELOG.md                # Version history\n└── README.md                   # This file\n┌─────────────────────────────────────────────────────────────┐\n│                       DashboardShell                         │\n│  ┌──────────┐  ┌──────────────────────────────────────────┐ │\n│  │          │  │              TopNav                        │ │\n│  │ Sidebar  │  │  [Menu] [Page Title] [Search⌘K] [🌙] [▼] │ │\n│  │          │  ├──────────────────────────────────────────┤ │\n│  │ [Dash]   │  │                                          │ │\n│  │ [Admin]  │  │              Page Content                 │ │\n│  │ [Settings│  │         (children prop)                   │ │\n│  │ [DB Mon] │  │                                          │ │\n│  │          │  │                                          │ │\n│  │ ──────── │  │                                          │ │\n│  │ ★ Favs   │  │                                          │ │\n│  │          │  │                                          │ │\n│  │ ──────── │  │                                          │ │\n│  │ [User]   │  │                                          │ │\n│  │ [🌙][≡]  │  │                                          │ │\n│  └──────────┘  └──────────────────────────────────────────┘ │\n└─────────────────────────────────────────────────────────────┘\nBrowser Request\n      │\n      ▼\n┌─────────────┐    ┌──────────────┐    ┌──────────────┐\n│  Next.js    │───▶│  Auth Check  │───▶│  CSRF Check  │\n│  Middleware  │    │  (JWT/API Key)│    │  (Origin)    │\n└─────────────┘    └──────────────┘    └──────────────┘\n                                                │\n                                                ▼\n                   ┌──────────────┐    ┌──────────────┐\n                   │  Rate Limit  │───▶│  Route       │\n                   │  (IP+Path)   │    │  Handler     │\n                   └──────────────┘    └──────────────┘\n                                                │\n                                                ▼\n                   ┌──────────────┐    ┌──────────────┐\n                   │  Prisma      │───▶│  SQLite      │\n                   │  (Metadata)  │    │  (App DB)    │\n                   └──────────────┘    └──────────────┘\n                                                │\n                                                ▼\n                   ┌──────────────┐    ┌──────────────┐\n                   │  Adapter     │───▶│  Target DB   │\n                   │  (PG/MySQL/..)   │  (User's DB) │\n                   └──────────────┘    └──────────────┘\n```\n\nDBoard uses SQLite (via Prisma) to store application metadata. The schema contains 16 models:\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| String | `@unique` |\n|\n| name | String? | |\n| password | String? | PBKDF2 hash (salt:hash) |\n| role | String | `@default(\"editor\")` — viewer, editor, admin |\n| createdAt | DateTime | `@default(now())` |\n| updatedAt | DateTime | `@updatedAt` |\n\n**Relations:** connections, dashboards, adminPages, apiKeys, auditLogs, queryHistory, savedViews, alerts, aiProviders, favorites, connectionShares, dashboardShares\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| name | String | Display name |\n| type | String | `@default(\"postgresql\")` — postgresql, mysql, sqlite, mongodb, supabase, mssql |\n| host | String | `@default(\"localhost\")` |\n| port | Int | `@default(5432)` |\n| database | String | Database name |\n| username | String | |\n| encryptedPassword | String? | AES-256-GCM encrypted |\n| ssl | Boolean | `@default(false)` |\n| readOnly | Boolean | `@default(false)` |\n| poolMin | Int | `@default(0)` |\n| poolMax | Int | `@default(10)` |\n| poolIdleTimeout | Int | `@default(30000)` |\n| queryTimeoutMs | Int | `@default(30000)` |\n| userId | String | Owner |\n| createdAt | DateTime | `@default(now())` |\n| updatedAt | DateTime | `@updatedAt` |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| name | String | |\n| description | String? | |\n| userId | String | Owner |\n| createdAt | DateTime | `@default(now())` |\n| updatedAt | DateTime | `@updatedAt` |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| dashboardId | String | |\n| title | String | |\n| type | String | `@default(\"bar\")` — bar, line, pie, table, sparkline, heatmap |\n| connectionId | String | |\n| query | String | SQL query |\n| config | String | `@default(\"{}\")` — JSON config |\n| width | Int | `@default(1)` — grid columns |\n| height | Int | `@default(1)` — grid rows |\n| x | Int | `@default(0)` — grid position |\n| y | Int | `@default(0)` — grid position |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| name | String | |\n| key | String | `@unique` — `dbo_` + 64 hex chars |\n| lastChars | String | Last 8 chars for display |\n| permissions | String | `@default(\"read\")` — read, write, admin |\n| connectionId | String | Scoped to one connection |\n| userId | String | Owner |\n| expiresAt | DateTime? | |\n| lastUsedAt | DateTime? | Fire-and-forget update |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| connectionId | String | |\n| userId | String | |\n| sql | String | |\n| durationMs | Int | `@default(0)` |\n| rowCount | Int | `@default(0)` |\n| error | String? | |\n| saved | Boolean | `@default(false)` |\n| createdAt | DateTime | `@default(now())` |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| connectionId | String | `@unique` |\n| config | String | JSON schema configuration |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| connectionId | String | |\n| userId | String | |\n| action | String | e.g., \"connection.created\", \"row.updated\" |\n| tableName | String? | |\n| recordId | String? | |\n| details | String? | JSON details |\n| ip | String? | |\n| dashboardId | String? |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| name | String | |\n| url | String | Target URL |\n| events | String | `@default(\"row.created,row.updated,row.deleted\")` |\n| connectionId | String | |\n| enabled | Boolean | `@default(true)` |\n| secret | String? | For signature verification |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| connectionId | String | |\n| sharedWithId | String | |\n| sharedById | String | |\n| permission | String | `@default(\"read\")` — read, write, admin |\n`@@unique([connectionId, sharedWithId])` |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| dashboardId | String | |\n| sharedWithId | String | |\n| sharedById | String | |\n| permission | String | `@default(\"read\")` |\n`@@unique([dashboardId, sharedWithId])` |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| name | String | |\n| connectionId | String | |\n| tableName | String | |\n| config | String | JSON view configuration |\n| userId | String |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| userId | String | |\n| name | String | `@default(\"chatgpt\")` |\n| displayName | String | `@default(\"ChatGPT / OpenAI\")` |\n| encryptedApiKey | String? | AES-256-GCM encrypted |\n| baseUrl | String? | |\n| isEnabled | Boolean | `@default(true)` |\n| sortOrder | Int | `@default(0)` |\n`@@unique([userId, name])` |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| providerId | String | |\n| modelId | String | |\n| displayName | String | |\n| isDefault | Boolean | `@default(false)` |\n| sortOrder | Int | `@default(0)` |\n`@@unique([providerId, modelId])` |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| name | String | |\n| connectionId | String | |\n| tableName | String | |\n| condition | String | Alert condition |\n| enabled | Boolean | `@default(true)` |\n| webhookUrl | String? | |\n| String? | ||\n| lastTriggeredAt | DateTime? | |\n| userId | String |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| name | String | |\n| description | String? | |\n| userId | String | |\n| connectionId | String | |\n| config | String | `@default(\"{}\")` — JSON page config |\n\n| Field | Type | Attributes |\n|---|---|---|\n| id | String | `@id @default(cuid())` |\n| userId | String | |\n| kind | String | \"dashboard\" or \"adminPage\" |\n| targetId | String | |\n`@@unique([userId, kind, targetId])` |\n\nDBoard implements multiple layers of security to protect your data:\n\n**File:** `src/lib/db/ssrf-guard.ts`\n\nAll outbound database and webhook connections are validated against Server-Side Request Forgery:\n\n**Private IP blocking**— Blocks RFC 1918 addresses (10.x, 172.16-31.x, 192.168.x), loopback (127.x), link-local (169.254.x), and IPv6 ULA/link-local**DNS validation**— Resolves hostnames before connecting; blocks connections if any resolved address is private** DNS rebinding protection**— Validates resolved addresses match expectations** Protocol enforcement**— Only HTTP/HTTPS allowed for webhooks\n\n**Configuration:** Set `ALLOW_PRIVATE_DB_HOSTS=1`\n\nin `.env`\n\nto allow localhost connections (development only).\n\n**File:** `src/lib/db/encryption.ts`\n\n| Property | Value |\n|---|---|\n| Algorithm | AES-256-GCM (authenticated encryption) |\n| Key Derivation | `crypto.scryptSync()` with salt `\"dboard-v1\"` |\n| IV | 16 random bytes per encryption |\n| Output Format | `hex(iv):hex(authTag):hex(ciphertext)` |\n\nUsed to encrypt:\n\n- Database passwords (\n`Connection.encryptedPassword`\n\n) - AI provider API keys (\n`AiProvider.encryptedApiKey`\n\n)\n\n**File:** `src/lib/csrf.ts`\n\nAll state-changing endpoints (POST/PUT/DELETE) validate that the `Origin`\n\nheader matches the `Host`\n\nheader. Skipped for API key authentication (non-browser clients).\n\n**File:** `src/lib/auth.ts`\n\n| Property | Value |\n|---|---|\n| Password Hashing | PBKDF2 with SHA-512 |\n| Iterations | 600,000 |\n| Salt | 16 random bytes per password |\n| Comparison | `crypto.timingSafeEqual()` (timing-attack resistant) |\n| Session Strategy | JWT with 24-hour max age |\n| Custom Login Page | `/login` |\n\n**Login Rate Limiting** (`src/lib/login-rate-limit.ts`\n\n):\n\n| Track | Max Attempts | Window | Lockout |\n|---|---|---|---|\n| Per-account (email) | 5 | 15 min | 15 min |\n| Per-IP | 30 | 15 min | 15 min |\n\n**General Rate Limiting** (`src/lib/with-rate-limit.ts`\n\n):\n\n- Default: 100 requests per 60-second window per IP+path\n- Configurable per-route (5–30 requests per 60 seconds)\n- Returns\n`429 Too Many Requests`\n\nwith`Retry-After`\n\nheader\n\n**File:** `src/lib/sql-guard.ts`\n\nProtects read-only connections from write operations:\n\n— Detects INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE, GRANT, REVOKE, and 15+ other write keywords`isWriteQuery(sql)`\n\n— Validates query starts with SELECT, WITH, SHOW, DESCRIBE, EXPLAIN, or PRAGMA`isReadQuery(sql)`\n\n**Literal stripping**— Removes string literals before checking to prevent false positives from data values\n\n**File:** `src/lib/permissions.ts`\n\n| Role | Hierarchy | Capabilities |\n|---|---|---|\n| viewer | 1 | Read-only access to connection data |\n| editor | 2 | Read + write access (default for new users) |\n| admin | 3 | Full access including sharing and deletion |\n\n**Connection access logic:**\n\n- Admin role can access everything\n- Owner (created the connection) gets access; write blocked if\n`readOnly`\n\nis set - Shared access via\n`ConnectionShare`\n\nwith read/write/admin permission levels\n\n**File:** `src/lib/api-keys.ts`\n\n**Format:**`dbo_`\n\nprefix + 64 hex characters (32 random bytes)**Scope:** Each key is scoped to a single connection**Permissions:**`read`\n\n(1) <`write`\n\n(2) <`admin`\n\n(3)**Authentication:**`X-API-Key`\n\nheader or`Authorization: Bearer dbo_*`\n\n**CSRF:** Skipped for API key requests (non-browser clients)**Expiry:** Optional expiration date; checked on every request**Tracking:**`lastUsedAt`\n\nupdated on each use\n\n| Provider | API Format | Base URL | Auth | Free Tier |\n|---|---|---|---|---|\n| ChatGPT / OpenAI | OpenAI Chat | `https://api.openai.com/v1` |\nBearer token | No |\n| Groq | OpenAI Chat | `https://api.groq.com/openai/v1` |\nBearer token | Yes |\n| Google Gemini | Gemini Content | `https://generativelanguage.googleapis.com/v1beta` |\nQuery key | Yes |\n| Ollama | OpenAI Chat | `http://localhost:11434` |\nNone | Yes (local) |\n| OpenRouter | OpenAI Chat | `https://router.ai/api/v1` |\nBearer token | Varies |\n\n| Provider | Models |\n|---|---|\n| OpenAI | GPT-4o, GPT-4o Mini, GPT-4 Turbo, o3-mini |\n| Groq | GPT-OSS 120B, Llama 3 70B, Llama 3 8B, Mixtral 8x7B, Gemma 2 9B |\n| Gemini | Gemini 2.0 Flash, Gemini 2.0 Flash Lite, Gemini 2.5 Pro |\n| Ollama | Llama 3, Llama 3.1, Mistral, CodeLlama |\n| OpenRouter | GPT-4o Mini, GPT-4o, Claude 3.5 Sonnet, Llama 3.3 70B, DeepSeek Chat |\n\n| Type | Description | Output |\n|---|---|---|\n`query` |\nGenerate SQL from natural language | Raw SQL string |\n`dashboard` |\nGenerate a multi-chart dashboard | Dashboard + chart configs |\n`panel` |\nGenerate a CRUD admin panel | Panel config with columns, filters, actions |\n`form` |\nGenerate form fields from schema | Form field configuration |\n\n- Navigate to\n**Settings → AI** - Select a provider (e.g., Groq for free usage)\n- Enter your API key (encrypted at rest with AES-256-GCM)\n- Set a default model\n- Use in the SQL editor (AI tab) or AI generation page\n\nDBoard supports three types of plugins:\n\nConnect to databases beyond the built-in six. Each adapter must implement the `DatabaseAdapter`\n\ninterface:\n\n```\ninterface DatabaseAdapter {\n  connect(config: ConnectionConfig): Promise<void>;\n  disconnect(): Promise<void>;\n  test(config: ConnectionConfig): Promise<boolean>;\n  introspect(): Promise<SchemaResult>;\n  executeRaw(query: string, params?: unknown[]): Promise<QueryResult>;\n  list(table: string, options: ListOptions, columns: ColumnMeta[]): Promise<PaginatedResult>;\n  get(table: string, pkValue: string, pkColumn: string): Promise<Record<string, unknown> | null>;\n  create(table: string, data: Record<string, unknown>, columns: ColumnMeta[]): Promise<Record<string, unknown>>;\n  update(table: string, pkValue: string, pkColumn: string, data: Record<string, unknown>, columns: ColumnMeta[]): Promise<Record<string, unknown>>;\n  delete(table: string, pkValue: string, pkColumn: string): Promise<void>;\n  bulkDelete(table: string, pkValues: string[], pkColumn: string): Promise<void>;\n}\n```\n\n**NPM naming:** `@dboard/adapter-<name>`\n\nor `dboard-adapter-<name>`\n\n**Built-in adapters:** PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, Supabase\n\n**External adapter examples:** ClickHouse, DynamoDB, Firestore, Redis, Cassandra\n\nCreate custom chart and visualization types for dashboards:\n\n```\ninterface WidgetDefinition {\n  id: string;\n  name: string;\n  description: string;\n  category: \"chart\" | \"visualization\" | \"table\" | \"custom\";\n  icon: string;\n  renderer: ComponentType<WidgetRendererProps>;\n  defaultConfig?: Record<string, unknown>;\n  configSchema?: WidgetConfigField[];\n}\n```\n\n**NPM naming:** `@dboard/widget-<name>`\n\nor `dboard-widget-<name>`\n\n**Built-in widgets:** Bar Chart, Pie Chart, Line Chart, Data Table, Sparkline, Heatmap\n\n**External widget examples:** Geo Map, Network Graph, Gauge, Treemap\n\nCreate custom notification targets for data change events:\n\n```\ninterface WebhookAction {\n  id: string;\n  name: string;\n  description: string;\n  icon: string;\n  configFields: WebhookConfigField[];\n  deliver(url: string, payload: WebhookPayload, config: Record<string, unknown>): Promise<WebhookDeliveryResult>;\n}\n```\n\n**NPM naming:** `@dboard/webhook-<name>`\n\nor `dboard-webhook-<name>`\n\n**Built-in actions:** Slack, Discord, PagerDuty, Custom HTTP\n\n**External webhook examples:** Email, Jira, Microsoft Teams\n\nDBoard supports two authentication methods:\n\n**Session-based (browser):**\n\n```\n# Login via NextAuth to get session cookie\ncurl -X POST http://localhost:3000/api/auth/callback/credentials \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"email=user@example.com&password=secret\" \\\n  -c cookies.txt\n```\n\n**API key (programmatic):**\n\n```\n# Using X-API-Key header\ncurl -H \"X-API-Key: dbo_your_api_key_here\" http://localhost:3000/api/data/conn_123/users\n\n# Using Authorization header\ncurl -H \"Authorization: Bearer dbo_your_api_key_here\" http://localhost:3000/api/data/conn_123/users\n```\n\n**Content-Type:**`application/json`\n\n(unless noted otherwise)**Error format:**`{ \"error\": \"Error message\" }`\n\n**Success format:** Resource object or`{ \"success\": true }`\n\n**Rate limit headers:**`X-RateLimit-Remaining`\n\n,`Retry-After`\n\n(on 429)\n\nAll rate limits are per IP + path, per 60-second window:\n\n| Limit | Endpoints |\n|---|---|\n| 5 req | Register, AI reset |\n| 10 req | Connection test, API key create, schema diff, export, import, dashboard duplicate |\n| 15 req | AI generate |\n| 20 req | Connection create/update/delete, admin pages, shares, webhooks, charts, alerts, settings |\n| 30 req | Data CRUD, query execute, dashboard CRUD, views, favorites, activity, API keys |\n| 100 req | Default (GET-heavy routes) |\n\nRegister a new user account.\n\n**Rate Limit:** 5 req/hour per IP\n\n```\ncurl -X POST http://localhost:3000/api/auth/register \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"email\":\"user@example.com\",\"name\":\"Alice\",\"password\":\"securepass\"}'\n```\n\n**Response (200):**\n\n```\n{ \"id\": \"clx123...\", \"email\": \"user@example.com\", \"name\": \"Alice\" }\n```\n\nList all connections for the current user.\n\n```\ncurl -b cookies.txt http://localhost:3000/api/connections\n```\n\n**Response (200):**\n\n```\n[\n  {\n    \"id\": \"clx123...\",\n    \"name\": \"My PostgreSQL\",\n    \"type\": \"postgresql\",\n    \"host\": \"localhost\",\n    \"port\": 5432,\n    \"database\": \"mydb\",\n    \"username\": \"postgres\",\n    \"ssl\": false,\n    \"readOnly\": false,\n    \"createdAt\": \"2026-08-02T00:00:00.000Z\"\n  }\n]\n```\n\nCreate a new database connection.\n\n**CSRF Required | Rate Limit:** 20 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/connections \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Production DB\",\"type\":\"postgresql\",\"host\":\"db.example.com\",\"port\":5432,\"database\":\"prod\",\"username\":\"admin\",\"password\":\"secret\",\"ssl\":true}'\n```\n\n**Request Body:**\n\n| Field | Type | Required | Default | Description |\n|---|---|---|---|---|\n| name | string | Yes | — | Display name |\n| type | string | No | `\"postgresql\"` |\nDatabase type |\n| host | string | No | `\"localhost\"` |\nHostname or IP |\n| port | number | No | 5432 | Port number |\n| database | string | Yes | — | Database name |\n| username | string | No | `\"\"` |\nUsername |\n| password | string | No | — | Password (encrypted at rest) |\n| ssl | boolean | No | `false` |\nUse SSL connection |\n| readOnly | boolean | No | `false` |\nRead-only mode |\n\n**Response (200):** Connection object (without password)\n\nTest a connection without saving it.\n\n**CSRF Required | Rate Limit:** 10 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/connections/test \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"type\":\"postgresql\",\"host\":\"localhost\",\"port\":5432,\"database\":\"test\",\"username\":\"postgres\",\"password\":\"secret\"}'\n```\n\n**Response (200):** `{ \"success\": true, \"message\": \"Connection successful\" }`\n\nGet a single connection by ID.\n\n```\ncurl -b cookies.txt http://localhost:3000/api/connections/clx123...\n```\n\nUpdate an existing connection.\n\n**CSRF Required | Rate Limit:** 30 req/60s\n\n```\ncurl -X PUT http://localhost:3000/api/connections/clx123... \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Updated Name\",\"ssl\":true}'\n```\n\nDelete a connection.\n\n**CSRF Required | Rate Limit:** 30 req/60s\n\n```\ncurl -X DELETE http://localhost:3000/api/connections/clx123... -b cookies.txt\n```\n\nHealth-check a connection (measures latency).\n\n```\ncurl -b cookies.txt http://localhost:3000/api/connections/clx123.../health\n```\n\n**Response (200):** `{ \"status\": \"online\", \"latencyMs\": 12 }`\n\n**Response (500):** `{ \"status\": \"offline\", \"error\": \"...\", \"latencyMs\": null }`\n\nGet detailed database metadata (version, size, tables, row counts, cache hit ratio, uptime).\n\n```\ncurl -b cookies.txt http://localhost:3000/api/connections/clx123.../info\n```\n\n**Response (200):**\n\n```\n{\n  \"connectionId\": \"clx123...\",\n  \"status\": \"online\",\n  \"version\": \"PostgreSQL 16.2\",\n  \"databaseSize\": \"15 MB\",\n  \"tableCount\": 12,\n  \"totalRecords\": 45230,\n  \"activeConnections\": 3,\n  \"cacheHitRatio\": 99.8,\n  \"uptime\": \"14 days\",\n  \"tables\": [\n    { \"name\": \"users\", \"type\": \"table\", \"rowCount\": 1500, \"totalSize\": \"240 kB\", \"indexCount\": 3 }\n  ]\n}\n```\n\nList all dashboards for the current user.\n\n```\ncurl -b cookies.txt http://localhost:3000/api/dashboards\n```\n\nCreate a new dashboard.\n\n**CSRF Required | Rate Limit:** 30 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/dashboards \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Sales Dashboard\",\"description\":\"Revenue tracking\"}'\n```\n\nGet a dashboard with its charts.\n\n```\ncurl -b cookies.txt http://localhost:3000/api/dashboards/clx456...\n```\n\nDelete a dashboard.\n\n**CSRF Required | Rate Limit:** 20 req/60s\n\n```\ncurl -X DELETE http://localhost:3000/api/dashboards/clx456... -b cookies.txt\n```\n\nAdd a chart to a dashboard.\n\n**CSRF Required | Rate Limit:** 20 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/dashboards/clx456.../charts \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\":\"User Count\",\"type\":\"bar\",\"connectionId\":\"clx123...\",\"query\":\"SELECT count(*) FROM users\"}'\n```\n\nDelete a chart from a dashboard.\n\nUpdate a chart in a dashboard.\n\nDuplicate a dashboard with all its charts.\n\n**CSRF Required | Rate Limit:** 10 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/dashboards/clx456.../duplicate -b cookies.txt\n```\n\nList all shares for a dashboard.\n\nShare a dashboard with another user.\n\n**CSRF Required | Rate Limit:** 30 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/dashboards/clx456.../shares \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sharedWithEmail\":\"bob@example.com\",\"permission\":\"read\"}'\n```\n\nRemove a dashboard share.\n\nList rows from a table with pagination, sorting, and search.\n\n```\ncurl -b cookies.txt \"http://localhost:3000/api/data/clx123.../users?page=1&pageSize=20&sortBy=created_at&sortDir=desc&search=alice\"\n```\n\n**Query Parameters:**\n\n| Param | Type | Default | Description |\n|---|---|---|---|\n| page | number | 1 | Page number |\n| pageSize | number | 20 | Rows per page (max 100) |\n| sortBy | string | — | Column to sort by |\n| sortDir | string | `\"asc\"` |\nSort direction: asc, desc |\n| search | string | — | Search across string columns |\n| * | string | — | Custom column filters (column=value) |\n\n**Response (200):**\n\n```\n{\n  \"data\": [{ \"id\": 1, \"name\": \"Alice\", \"email\": \"alice@example.com\" }],\n  \"columns\": [{ \"name\": \"id\", \"dataType\": \"integer\", \"isPrimaryKey\": true }],\n  \"tableName\": \"users\",\n  \"isView\": false,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"total\": 150,\n  \"totalPages\": 8\n}\n```\n\nInsert a new row.\n\n**CSRF Required (skipped for API key) | Rate Limit:** 30 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/data/clx123.../users \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Bob\",\"email\":\"bob@example.com\"}'\n```\n\nGet a single row by primary key.\n\n```\ncurl -b cookies.txt http://localhost:3000/api/data/clx123.../users/42\n```\n\nUpdate a row by primary key.\n\n**CSRF Required (skipped for API key) | Rate Limit:** 30 req/60s\n\n```\ncurl -X PUT http://localhost:3000/api/data/clx123.../users/42 \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Bob Updated\"}'\n```\n\nDelete a row by primary key.\n\n**CSRF Required (skipped for API key) | Rate Limit:** 30 req/60s\n\n```\ncurl -X DELETE http://localhost:3000/api/data/clx123.../users/42 -b cookies.txt\n```\n\nBulk delete rows by IDs (max 1000).\n\n**CSRF Required (skipped for API key) | Rate Limit:** 30 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/data/clx123.../users/bulk-delete \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ids\":[1,2,3,4,5]}'\n```\n\n**Response (200):** `{ \"success\": true, \"deleted\": 5 }`\n\nExecute a raw SQL query.\n\n**CSRF Required (skipped for API key)**\n\n```\ncurl -X POST http://localhost:3000/api/query/clx123... \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sql\":\"SELECT * FROM users WHERE created_at > '\\''2026-01-01'\\''\"}'\n```\n\n**Request Body:**\n\n| Field | Type | Required | Description |\n|---|---|---|---|\n| sql | string | Yes | SQL query to execute |\n| dateFrom | string | No | Start date for date-range injection |\n| dateTo | string | No | End date for date-range injection |\n\n**Response (200):**\n\n```\n{\n  \"columns\": [\"id\", \"name\", \"email\"],\n  \"data\": [{ \"id\": 1, \"name\": \"Alice\", \"email\": \"alice@example.com\" }],\n  \"rowCount\": 1,\n  \"totalRows\": 1,\n  \"truncated\": false,\n  \"durationMs\": 12,\n  \"isReadQuery\": true\n}\n```\n\nList saved queries for a connection (max 50).\n\n```\ncurl -b cookies.txt http://localhost:3000/api/query/clx123.../saved\n```\n\nSave a query.\n\n```\ncurl -X POST http://localhost:3000/api/query/clx123.../saved \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sql\":\"SELECT count(*) FROM users\"}'\n```\n\nDelete a saved query.\n\nList query execution history (max 200).\n\n```\ncurl -b cookies.txt \"http://localhost:3000/api/query/clx123.../history?limit=20\"\n```\n\nGenerate SQL from natural language using AI.\n\n**CSRF Required**\n\n```\ncurl -X POST http://localhost:3000/api/query/clx123.../ai \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"prompt\":\"Show me all users who signed up this week\",\"modelId\":\"gpt-4o-mini\"}'\n```\n\n**Response (200):** `{ \"sql\": \"SELECT * FROM users WHERE created_at >= date('now', '-7 days')\" }`\n\nIntrospect a connection's database schema (cached for 60 seconds).\n\n```\ncurl -X POST http://localhost:3000/api/schema/introspect \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"connectionId\":\"clx123...\"}'\n```\n\nCompare schemas between two connections.\n\n**CSRF Required | Rate Limit:** 10 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/schema/diff \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sourceConnectionId\":\"clx123...\",\"targetConnectionId\":\"clx456...\"}'\n```\n\n**Response (200):**\n\n```\n{\n  \"onlyInSource\": [\"legacy_users\"],\n  \"onlyInTarget\": [\"new_table\"],\n  \"columnDiffs\": [{ \"table\": \"users\", \"added\": [\"avatar\"], \"removed\": [] }],\n  \"sourceTableCount\": 12,\n  \"targetTableCount\": 11\n}\n```\n\nSave schema configuration (column-level metadata).\n\nGet schema configuration for a connection.\n\nGenerate structured content from a prompt.\n\n**CSRF Required | Rate Limit:** 15 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/ai/generate \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"prompt\":\"Create a user management panel\",\"connectionId\":\"clx123...\",\"type\":\"panel\",\"modelId\":\"gpt-4o-mini\"}'\n```\n\n**Types:** `panel`\n\n, `dashboard`\n\n, `form`\n\n, `query`\n\nTest an AI provider connection.\n\n**CSRF Required | Rate Limit:** 10 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/ai/test \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"providerId\":\"chatgpt\",\"modelId\":\"gpt-4o-mini\"}'\n```\n\nList all AI providers and models (API keys masked).\n\n```\ncurl -b cookies.txt http://localhost:3000/api/ai/providers\n```\n\nUpdate an AI provider (API key, base URL, enabled status).\n\nReset all AI providers to defaults. **Rate Limit:** 5 req/60s\n\nSet the global default AI model.\n\nUpdate user profile (name).\n\n```\ncurl -X PUT http://localhost:3000/api/settings/profile \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Alice Smith\"}'\n```\n\nGet notification preferences.\n\nUpdate notification preferences.\n\nList all admin pages.\n\nCreate a new admin page.\n\n```\ncurl -X POST http://localhost:3000/api/admin-pages \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"User Management\",\"connectionId\":\"clx123...\",\"config\":{}}'\n```\n\nGet a single admin page.\n\nUpdate an admin page.\n\nDelete an admin page.\n\nList saved views for a connection.\n\nCreate a saved view.\n\n```\ncurl -X POST http://localhost:3000/api/views/clx123... \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Active Users\",\"tableName\":\"users\",\"config\":{\"filters\":{\"status\":\"active\"}}}'\n```\n\nDelete a saved view.\n\nList all favorites for the current user.\n\nToggle a favorite.\n\n```\ncurl -X POST http://localhost:3000/api/favorites \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"kind\":\"dashboard\",\"targetId\":\"clx456...\"}'\n```\n\n**Response (200):** `{ \"favorited\": true }`\n\nor `{ \"favorited\": false }`\n\nList connection shares. Optional: `?connectionId=xxx`\n\nShare a connection with another user.\n\n**CSRF Required | Rate Limit:** 30 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/shares \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"connectionId\":\"clx123...\",\"sharedWithEmail\":\"bob@example.com\",\"permission\":\"read\"}'\n```\n\nRemove a connection share.\n\nList webhooks. Optional: `?connectionId=xxx`\n\nCreate a webhook.\n\n**CSRF Required | Rate Limit:** 30 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/webhooks \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Slack Notify\",\"url\":\"https://hooks.slack.com/...\",\"events\":\"row.created,row.updated\",\"connectionId\":\"clx123...\"}'\n```\n\nUpdate a webhook.\n\nDelete a webhook.\n\nList available webhook action types (no auth required).\n\nList all API keys. Optional: `?connectionId=xxx`\n\n```\ncurl -b cookies.txt http://localhost:3000/api/api-keys\n```\n\nCreate a new API key (shown only once).\n\n**CSRF Required | Rate Limit:** 10 req/60s\n\n```\ncurl -X POST http://localhost:3000/api/api-keys \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"CI Pipeline\",\"connectionId\":\"clx123...\",\"permissions\":\"read\",\"expiresInDays\":90}'\n```\n\n**Response (200):**\n\n```\n{\n  \"id\": \"clx789...\",\n  \"name\": \"CI Pipeline\",\n  \"key\": \"dbo_a1b2c3d4e5f6...\",\n  \"lastChars\": \"...f6g7h8i9\",\n  \"permissions\": \"read\",\n  \"expiresAt\": \"2026-11-01T00:00:00.000Z\"\n}\n```\n\nNote:The full API key is shown only in this response. Store it securely.\n\nDelete an API key.\n\nList the current user's activity feed (max 200).\n\n```\ncurl -b cookies.txt \"http://localhost:3000/api/activity?limit=20\"\n```\n\nList audit logs. Optional connection filter.\n\n```\ncurl -b cookies.txt \"http://localhost:3000/api/audit-logs?limit=50\"\n```\n\nList alerts for a connection.\n\nCreate an alert.\n\n```\ncurl -X POST http://localhost:3000/api/alerts/clx123... \\\n  -b cookies.txt \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"High Row Count\",\"tableName\":\"logs\",\"condition\":\"row_count > 1000000\",\"webhookUrl\":\"https://hooks.slack.com/...\"}'\n```\n\nEnable or disable an alert.\n\nDelete an alert.\n\nList all registered plugins (no auth required).\n\nList available theme presets (no auth required).\n\nGenerate a CSRF token (sets cookie).\n\nExport table data. Formats: `json`\n\n, `csv`\n\n, `jsonl`\n\n, `xlsx`\n\n, `pdf`\n\n.\n\n```\ncurl -b cookies.txt \"http://localhost:3000/api/export/clx123.../users?format=csv\" --output users.csv\n```\n\nImport data from file (CSV, JSON, JSONL). Max 10MB, max 10,000 rows.\n\n```\ncurl -X POST http://localhost:3000/api/import/clx123.../users \\\n  -b cookies.txt \\\n  -F \"file=@users.csv\" \\\n  -F \"format=csv\"\n```\n\n**Response (200):** `{ \"imported\": 500, \"total\": 500, \"truncated\": false }`\n\nList built-in dashboard templates (Database Overview, Table Health, Query Performance, Sales Dashboard, User Analytics).\n\n```\n# Build the image\ndocker build -t dboard .\n\n# Run with environment variables\ndocker run -p 3000:3000 \\\n  -e NEXTAUTH_SECRET=$(openssl rand -base64 32) \\\n  -e ENCRYPTION_KEY=$(openssl rand -base64 32) \\\n  -e DATABASE_URL=file:/data/dboard.db \\\n  -v dboard-data:/data \\\n  dboard\n# Set secrets\nexport NEXTAUTH_SECRET=$(openssl rand -base64 32)\nexport ENCRYPTION_KEY=$(openssl rand -base64 32)\n\n# Start\ndocker compose up -d\n\n# View logs\ndocker compose logs -f\n\n# Stop\ndocker compose down\n# Install Vercel CLI\nnpm i -g vercel\n\n# Deploy\nvercel\n\n# Set environment variables in Vercel dashboard:\n# NEXTAUTH_SECRET, ENCRYPTION_KEY, DATABASE_URL\n```\n\nNote:For Vercel, use an external database (PostgreSQL) instead of SQLite for`DATABASE_URL`\n\n.\n\n```\n# Build for production\nnpm run build\n\n# Start with PM2\npm2 start npm --name \"dboard\" -- start\n\n# Save PM2 config\npm2 save\npm2 startup\n```\n\n| Platform | DATABASE_URL | Notes |\n|---|---|---|\n| Local dev | `file:./dev.db` |\nSQLite, zero config |\n| Docker | `file:/data/dboard.db` |\nPersistent via volume |\n| Vercel | `postgresql://...` |\nExternal PostgreSQL required |\n| Self-hosted | `file:./data/dboard.db` or `postgresql://...` |\nSQLite or external DB |\n\n| Script | Command | Description |\n|---|---|---|\n`dev` |\n`npm run dev` |\nStart development server |\n`build` |\n`npm run build` |\nProduction build |\n`start` |\n`npm run start` |\nStart production server |\n`lint` |\n`npm run lint` |\nRun ESLint |\n`test` |\n`npm run test` |\nRun unit tests (Vitest) |\n`test:watch` |\n`npm run test:watch` |\nRun tests in watch mode |\n`test:coverage` |\n`npm run test:coverage` |\nGenerate coverage report |\n`test:e2e` |\n`npm run test:e2e` |\nRun E2E tests (Playwright) |\n`test:e2e:ui` |\n`npm run test:e2e:ui` |\nPlaywright UI mode |\n`lint:security` |\n`npm run lint:security` |\nSecurity-focused linting |\n`lint:dead` |\n`npm run lint:dead` |\nDead code detection (Knip) |\n`audit:deps` |\n`npm run audit:deps` |\nDependency vulnerability audit |\n`format` |\n`npm run format` |\nFormat code with Prettier |\n`format:check` |\n`npm run format:check` |\nCheck formatting |\n`seed` |\n`npm run seed` |\nSeed the database |\n`prisma:generate` |\n`npx prisma generate` |\nRegenerate Prisma client |\n`prisma:push` |\n`npx prisma db push` |\nPush schema changes |\n\n**Unit tests (Vitest):**\n\n```\nnpm run test              # Run all tests\nnpm run test:watch        # Watch mode\nnpm run test:coverage     # Coverage report (70% threshold)\n```\n\n**E2E tests (Playwright):**\n\n```\nnpm run test:e2e          # Run all E2E tests\nnpm run test:e2e:ui       # Interactive UI mode\n```\n\n**Coverage thresholds:** 70% lines, functions, branches, and statements.\n\n```\nnpm run lint              # ESLint (includes security rules)\nnpm run lint:dead         # Dead code detection\nnpm run lint:security     # Security-focused linting\nnpm run format:check      # Prettier formatting check\n```\n\nThe GitHub Actions workflow (`.github/workflows/ci.yml`\n\n) runs 7 jobs:\n\n**Security Scan**—`npm audit`\n\n+`eslint-plugin-security`\n\n**Lint**—`next lint`\n\n+`prettier --check`\n\n**Type Check**—`tsc --noEmit`\n\n**Dead Code Detection**—`knip`\n\n**Unit Tests**—`vitest`\n\nwith coverage**E2E Tests**— Playwright (Chromium)** Build**—`next build`\n\nWe welcome contributions! Please see [CONTRIBUTING.md](/Haimanot25/dboard/blob/main/CONTRIBUTING.md) for guidelines.\n\n- Fork the repository\n- Create a feature branch:\n`git checkout -b feature/my-feature`\n\n- Make your changes\n- Run tests:\n`npm run test`\n\n- Run linting:\n`npm run lint`\n\n- Commit your changes\n- Push to your fork and submit a Pull Request\n\nLook for issues labeled `good first issue`\n\nin the GitHub issue tracker.\n\nAll project documentation is maintained in the repository root:\n\n| Document | Description |\n|---|---|\n|\n\n**This file**— full project documentation, API reference, and setup guide[CONTRIBUTING.md](/Haimanot25/dboard/blob/main/CONTRIBUTING.md)[CODE_OF_CONDUCT.md](/Haimanot25/dboard/blob/main/CODE_OF_CONDUCT.md)[SECURITY.md](/Haimanot25/dboard/blob/main/SECURITY.md)[CHANGELOG.md](/Haimanot25/dboard/blob/main/CHANGELOG.md)[LICENSE](/Haimanot25/dboard/blob/main/LICENSE)[AUDIT-REPORT.md](/Haimanot25/dboard/blob/main/AUDIT-REPORT.md)**New here?** Start with[Quick Start](#quick-start)above**API integration?** See[API Reference](#api-reference)with curl examples for all 80 endpoints**Contributing?** Read[CONTRIBUTING.md](/Haimanot25/dboard/blob/main/CONTRIBUTING.md)for the development workflow**Found a bug?**[Open an issue](https://github.com/Haimanot25/dboard/issues)** Security concern?**Follow the process in[SECURITY.md](/Haimanot25/dboard/blob/main/SECURITY.md)\n\nThis project is licensed under the MIT License — see the [LICENSE](/Haimanot25/dboard/blob/main/LICENSE) file for details.\n\n**Issues:**[GitHub Issues](https://github.com/Haimanot25/dboard/issues)** Security:**See[SECURITY.md](/Haimanot25/dboard/blob/main/SECURITY.md)for vulnerability reporting** Contributing:**See[CONTRIBUTING.md](/Haimanot25/dboard/blob/main/CONTRIBUTING.md)", "url": "https://wpnews.pro/news/i-built-an-open-source-admin-panel-builder-for-existing-databases", "canonical_source": "https://github.com/Haimanot25/dboard", "published_at": "2026-08-04 06:37:45+00:00", "updated_at": "2026-08-04 06:52:28.176006+00:00", "lang": "en", "topics": ["ai-products", "developer-tools", "generative-ai"], "entities": ["DBoard", "Haimanot25", "PostgreSQL", "MySQL", "MongoDB", "SQLite", "Supabase", "SQL Server"], "alternates": {"html": "https://wpnews.pro/news/i-built-an-open-source-admin-panel-builder-for-existing-databases", "markdown": "https://wpnews.pro/news/i-built-an-open-source-admin-panel-builder-for-existing-databases.md", "text": "https://wpnews.pro/news/i-built-an-open-source-admin-panel-builder-for-existing-databases.txt", "jsonld": "https://wpnews.pro/news/i-built-an-open-source-admin-panel-builder-for-existing-databases.jsonld"}}