feat: phase 2 databases via DBX (v0.3.0)

- dbx-mcp persistent stdio subprocess in Go backend (internal/dbx),
  lazy start + single retry, tool failures surface as 502
- core.db_connections registry: per-project connections, credentials
  server-side only, managed entries protected (409)
- platform-postgres + platform-dragonfly auto-register per project
- REST proxy: list/create/delete/test, databases, tables, describe,
  schema context, SQL query, redis command
- Databases page: connection list, schema/table browser, column
  inspector, SQL editor with result grid + row detail, redis console
- backend image moved to debian-slim for glibc; pinned dbx release
  with SHA256 verification

Authored By: TDvorak <[email protected]>
This commit is contained in:
Tomas Dvorak committed 2026-09-16 14:54:33 +02:00
1 parent 420502eec1
commit 627bd89d5e
71 files changed
+3685 -49

No files matched your search

@@ -0,0 +1,173 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: db_connections.sql
package db
import (
"context"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
)
const createDBConnection = `-- name: CreateDBConnection :one
INSERT INTO core.db_connections (
project_id,
name,
db_type,
config,
is_managed,
created_by_user_id
) VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, project_id, name, db_type, config, is_managed, created_by_user_id, created_at, updated_at
`
type CreateDBConnectionParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
DbType string `json:"db_type"`
Config []byte `json:"config"`
IsManaged bool `json:"is_managed"`
CreatedByUserID pgtype.UUID `json:"created_by_user_id"`
}
func (q *Queries) CreateDBConnection(ctx context.Context, arg CreateDBConnectionParams) (CoreDbConnection, error) {
row := q.db.QueryRow(ctx, createDBConnection,
arg.ProjectID,
arg.Name,
arg.DbType,
arg.Config,
arg.IsManaged,
arg.CreatedByUserID,
)
var i CoreDbConnection
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.DbType,
&i.Config,
&i.IsManaged,
&i.CreatedByUserID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const deleteDBConnection = `-- name: DeleteDBConnection :one
DELETE FROM core.db_connections
WHERE id = $1
AND project_id = $2
RETURNING id, project_id, name, db_type, config, is_managed, created_by_user_id, created_at, updated_at
`
type DeleteDBConnectionParams struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
}
func (q *Queries) DeleteDBConnection(ctx context.Context, arg DeleteDBConnectionParams) (CoreDbConnection, error) {
row := q.db.QueryRow(ctx, deleteDBConnection, arg.ID, arg.ProjectID)
var i CoreDbConnection
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.DbType,
&i.Config,
&i.IsManaged,
&i.CreatedByUserID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getDBConnectionByID = `-- name: GetDBConnectionByID :one
SELECT id, project_id, name, db_type, config, is_managed, created_by_user_id, created_at, updated_at FROM core.db_connections
WHERE id = $1
`
func (q *Queries) GetDBConnectionByID(ctx context.Context, id uuid.UUID) (CoreDbConnection, error) {
row := q.db.QueryRow(ctx, getDBConnectionByID, id)
var i CoreDbConnection
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.DbType,
&i.Config,
&i.IsManaged,
&i.CreatedByUserID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getDBConnectionByName = `-- name: GetDBConnectionByName :one
SELECT id, project_id, name, db_type, config, is_managed, created_by_user_id, created_at, updated_at FROM core.db_connections
WHERE project_id = $1
AND name = $2
`
type GetDBConnectionByNameParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
}
func (q *Queries) GetDBConnectionByName(ctx context.Context, arg GetDBConnectionByNameParams) (CoreDbConnection, error) {
row := q.db.QueryRow(ctx, getDBConnectionByName, arg.ProjectID, arg.Name)
var i CoreDbConnection
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.DbType,
&i.Config,
&i.IsManaged,
&i.CreatedByUserID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listDBConnections = `-- name: ListDBConnections :many
SELECT id, project_id, name, db_type, config, is_managed, created_by_user_id, created_at, updated_at FROM core.db_connections
WHERE project_id = $1
ORDER BY is_managed DESC, name ASC
`
func (q *Queries) ListDBConnections(ctx context.Context, projectID uuid.UUID) ([]CoreDbConnection, error) {
rows, err := q.db.Query(ctx, listDBConnections, projectID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CoreDbConnection
for rows.Next() {
var i CoreDbConnection
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.DbType,
&i.Config,
&i.IsManaged,
&i.CreatedByUserID,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
@@ -200,6 +200,18 @@ type CoreCollection struct {
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type CoreDbConnection struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
DbType string `json:"db_type"`
Config []byte `json:"config"`
IsManaged bool `json:"is_managed"`
CreatedByUserID pgtype.UUID `json:"created_by_user_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type CoreDocument struct {
ID uuid.UUID `json:"id"`
CollectionID uuid.UUID `json:"collection_id"`
@@ -25,6 +25,7 @@ type Querier interface {
CreateBucket(ctx context.Context, arg CreateBucketParams) (CoreBucket, error)
CreateBucketObject(ctx context.Context, arg CreateBucketObjectParams) (CoreBucketObject, error)
CreateCollection(ctx context.Context, arg CreateCollectionParams) (CoreCollection, error)
CreateDBConnection(ctx context.Context, arg CreateDBConnectionParams) (CoreDbConnection, error)
CreateDocument(ctx context.Context, arg CreateDocumentParams) (CoreDocument, error)
CreateInvitation(ctx context.Context, arg CreateInvitationParams) (CoreProjectInvitation, error)
CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (CoreOrganization, error)
@@ -32,6 +33,7 @@ type Querier interface {
DeleteBucketByID(ctx context.Context, id uuid.UUID) (CoreBucket, error)
DeleteBucketObjectByKey(ctx context.Context, arg DeleteBucketObjectByKeyParams) (CoreBucketObject, error)
DeleteCollection(ctx context.Context, arg DeleteCollectionParams) error
DeleteDBConnection(ctx context.Context, arg DeleteDBConnectionParams) (CoreDbConnection, error)
DeleteDocument(ctx context.Context, arg DeleteDocumentParams) error
DeleteOrganizationByID(ctx context.Context, id uuid.UUID) (CoreOrganization, error)
DeletePendingInvitationByIDForOrganization(ctx context.Context, arg DeletePendingInvitationByIDForOrganizationParams) (CoreProjectInvitation, error)
@@ -42,6 +44,8 @@ type Querier interface {
GetBucketObjectByKey(ctx context.Context, arg GetBucketObjectByKeyParams) (CoreBucketObject, error)
GetCollectionByID(ctx context.Context, id uuid.UUID) (CoreCollection, error)
GetCollectionBySlug(ctx context.Context, arg GetCollectionBySlugParams) (CoreCollection, error)
GetDBConnectionByID(ctx context.Context, id uuid.UUID) (CoreDbConnection, error)
GetDBConnectionByName(ctx context.Context, arg GetDBConnectionByNameParams) (CoreDbConnection, error)
GetDocumentByID(ctx context.Context, arg GetDocumentByIDParams) (CoreDocument, error)
GetInvitationByIDForOrganization(ctx context.Context, arg GetInvitationByIDForOrganizationParams) (CoreProjectInvitation, error)
GetInvitationByTokenHash(ctx context.Context, tokenHash string) (CoreProjectInvitation, error)
@@ -57,6 +61,7 @@ type Querier interface {
ListBucketsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]uuid.UUID, error)
ListBucketsForProject(ctx context.Context, arg ListBucketsForProjectParams) ([]CoreBucket, error)
ListCollections(ctx context.Context, projectID uuid.UUID) ([]CoreCollection, error)
ListDBConnections(ctx context.Context, projectID uuid.UUID) ([]CoreDbConnection, error)
ListDocuments(ctx context.Context, arg ListDocumentsParams) ([]CoreDocument, error)
ListInvitationsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]ListInvitationsForOrganizationRow, error)
ListOrganizationMembers(ctx context.Context, organizationID uuid.UUID) ([]ListOrganizationMembersRow, error)