mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
feat: public_port for managed databases — opt-in 0.0.0.0 bind
Managed databases have always published their port on 127.0.0.1 only.
public_port opts into binding on all interfaces so the database is
reachable off-host. Toggling via PUT recreates the container — the
named data volume survives.
- create accepts public_port; PUT /databases/{id} toggles it
- external rows reject the flag (no container exists)
- CLI: databases create --public, databases update --public/--public=false
- MCP: databases_update tool; UI: create checkbox + per-row toggle
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
5dc9b5f115
commit
b7131965b1
16 files changed
+235
-45
No files matched your search
@@ -52,7 +52,8 @@ type DatabaseService struct {
|
||||
Region string `json:"region" db:"region"`
|
||||
BackupSchedule string `json:"backup_schedule,omitempty"`
|
||||
NextBackupAt *time.Time `json:"next_backup_at,omitempty"`
|
||||
Provider string `json:"provider"` // managed | external
|
||||
Provider string `json:"provider"` // managed | external
|
||||
PublicPort bool `json:"public_port" db:"public_port"`
|
||||
External *DatabaseExternalRef `json:"external,omitempty"` // connection target for external DBs
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
@@ -117,10 +118,11 @@ type DatabaseSettings struct {
|
||||
|
||||
// DatabaseCreateRequest represents a request to create a new database
|
||||
type DatabaseCreateRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Type string `json:"type" binding:"required"`
|
||||
Plan string `json:"plan" binding:"required,oneof=hobby starter standard business"`
|
||||
Region string `json:"region" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Type string `json:"type" binding:"required"`
|
||||
Plan string `json:"plan" binding:"required,oneof=hobby starter standard business"`
|
||||
Region string `json:"region" binding:"required"`
|
||||
PublicPort bool `json:"public_port"`
|
||||
}
|
||||
|
||||
// DatabaseUpdateRequest represents a request to update a database
|
||||
@@ -128,6 +130,7 @@ type DatabaseUpdateRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Plan string `json:"plan,omitempty"`
|
||||
BackupSchedule *string `json:"backup_schedule,omitempty"`
|
||||
PublicPort *bool `json:"public_port,omitempty"`
|
||||
}
|
||||
|
||||
// DatabaseActionRequest represents a request to perform database actions
|
||||
@@ -291,10 +294,11 @@ func (h *DatabaseHandler) CreateDatabase(c *gin.Context) {
|
||||
}
|
||||
|
||||
databaseID, dbName, dbType, err := h.createManagedDatabase(c.Request.Context(), userID, managedDatabaseCreateRequest{
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Plan: req.Plan,
|
||||
Region: req.Region,
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Plan: req.Plan,
|
||||
Region: req.Region,
|
||||
PublicPort: req.PublicPort,
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
@@ -322,6 +326,7 @@ type managedDatabaseCreateRequest struct {
|
||||
Type string
|
||||
Plan string
|
||||
Region string
|
||||
PublicPort bool
|
||||
RuntimeVariables map[string]string
|
||||
}
|
||||
|
||||
@@ -365,16 +370,17 @@ func (h *DatabaseHandler) createManagedDatabase(ctx context.Context, userID stri
|
||||
version := h.getDefaultVersion(dbType)
|
||||
|
||||
err = h.queries.CreateDatabaseService(ctx, sqlcdb.CreateDatabaseServiceParams{
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
Name: name,
|
||||
Type: dbType,
|
||||
Status: "building",
|
||||
Version: version,
|
||||
Plan: plan,
|
||||
Region: region,
|
||||
CreatedAt: sql.NullTime{Time: now, Valid: true},
|
||||
UpdatedAt: sql.NullTime{Time: now, Valid: true},
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
Name: name,
|
||||
Type: dbType,
|
||||
Status: "building",
|
||||
Version: version,
|
||||
Plan: plan,
|
||||
Region: region,
|
||||
PublicPort: req.PublicPort,
|
||||
CreatedAt: sql.NullTime{Time: now, Valid: true},
|
||||
UpdatedAt: sql.NullTime{Time: now, Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to create database service: %w", err)
|
||||
@@ -411,11 +417,40 @@ func (h *DatabaseHandler) UpdateDatabase(c *gin.Context) {
|
||||
req.Name = strings.TrimSpace(req.Name)
|
||||
req.Plan = strings.TrimSpace(req.Plan)
|
||||
|
||||
if req.Name == "" && req.Plan == "" && req.BackupSchedule == nil {
|
||||
if req.Name == "" && req.Plan == "" && req.BackupSchedule == nil && req.PublicPort == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "No fields to update"})
|
||||
return
|
||||
}
|
||||
|
||||
if req.PublicPort != nil {
|
||||
row, err := h.queries.GetDatabaseServiceByIDAndUser(c.Request.Context(), sqlcdb.GetDatabaseServiceByIDAndUserParams{
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Database not found", "code": "NOT_FOUND"})
|
||||
return
|
||||
}
|
||||
if row.Provider == "external" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "public_port applies to managed databases — external databases are already reachable", "code": "VALIDATION"})
|
||||
return
|
||||
}
|
||||
if err := h.queries.SetDatabaseServicePublicPortByIDAndUser(c.Request.Context(), sqlcdb.SetDatabaseServicePublicPortByIDAndUserParams{
|
||||
PublicPort: *req.PublicPort,
|
||||
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update public port"})
|
||||
return
|
||||
}
|
||||
// Port bindings are baked into the container — recreate it so the
|
||||
// new bind scope takes effect. The data volume survives.
|
||||
if *req.PublicPort != row.PublicPort {
|
||||
go h.reprovisionManagedDatabase(databaseID, row.Name, row.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if req.BackupSchedule != nil {
|
||||
var schedule sql.NullString
|
||||
var nextBackup sql.NullTime
|
||||
@@ -1108,6 +1143,7 @@ func mapDatabaseServiceRow(row sqlcdb.DatabaseService) DatabaseService {
|
||||
BackupSchedule: databaseNullString(row.BackupSchedule),
|
||||
NextBackupAt: databaseNullTimePtr(row.NextBackupAt),
|
||||
Provider: row.Provider,
|
||||
PublicPort: row.PublicPort,
|
||||
ConnectionURL: databaseNullString(row.ConnectionUrl),
|
||||
CreatedAt: databaseNullTime(row.CreatedAt),
|
||||
UpdatedAt: databaseNullTime(row.UpdatedAt),
|
||||
@@ -1288,6 +1324,17 @@ func (h *DatabaseHandler) provisionDatabase(databaseID, databaseName, dbType str
|
||||
h.provisionDatabaseWithVariables(databaseID, databaseName, dbType, nil)
|
||||
}
|
||||
|
||||
// reprovisionManagedDatabase recreates a managed container so changed
|
||||
// runtime settings (public_port) take effect. The data volume persists.
|
||||
func (h *DatabaseHandler) reprovisionManagedDatabase(databaseID, databaseName, dbType string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
if h.dockerClient != nil {
|
||||
_ = h.dockerClient.RemoveContainer(ctx, managedDatabaseContainerName(databaseID), true)
|
||||
}
|
||||
h.provisionDatabase(databaseID, databaseName, dbType)
|
||||
}
|
||||
|
||||
func (h *DatabaseHandler) provisionDatabaseWithVariables(databaseID, databaseName, dbType string, runtimeVariables map[string]string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
@@ -1344,8 +1391,12 @@ func (h *DatabaseHandler) provisionDatabaseRuntime(ctx context.Context, database
|
||||
}
|
||||
}
|
||||
|
||||
hostIP := "127.0.0.1"
|
||||
if row, err := h.queries.GetDatabaseServiceByID(ctx, databaseID); err == nil && row.PublicPort {
|
||||
hostIP = "0.0.0.0"
|
||||
}
|
||||
portBindings := nat.PortMap{
|
||||
plan.Port: []nat.PortBinding{{HostIP: "127.0.0.1", HostPort: ""}},
|
||||
plan.Port: []nat.PortBinding{{HostIP: hostIP, HostPort: ""}},
|
||||
}
|
||||
|
||||
exposedPorts := nat.PortSet{plan.Port: struct{}{}}
|
||||
|
||||
@@ -79,6 +79,9 @@ var dbCreateCmd = &cobra.Command{
|
||||
body[key] = v
|
||||
}
|
||||
}
|
||||
if v, _ := cmd.Flags().GetBool("public"); v {
|
||||
body["public_port"] = true
|
||||
}
|
||||
data, err := c.Do("POST", "/databases", body)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -88,6 +91,39 @@ var dbCreateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var dbUpdateCmd = &cobra.Command{
|
||||
Use: "update <id>",
|
||||
Short: "Update a database (--public/--public=false toggles the exposed host port)",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c, err := client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body := map[string]interface{}{}
|
||||
if cmd.Flags().Changed("public") {
|
||||
v, _ := cmd.Flags().GetBool("public")
|
||||
body["public_port"] = v
|
||||
}
|
||||
if v, _ := cmd.Flags().GetString("name"); v != "" {
|
||||
body["name"] = v
|
||||
}
|
||||
if cmd.Flags().Changed("backup-schedule") {
|
||||
v, _ := cmd.Flags().GetString("backup-schedule")
|
||||
body["backup_schedule"] = v
|
||||
}
|
||||
if len(body) == 0 {
|
||||
return &APIError{Message: "nothing to update — pass --public/--private-style flags, --name, or --backup-schedule", ExitCode: ExitError}
|
||||
}
|
||||
data, err := c.Do("PUT", "/databases/"+args[0], body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
PrintRaw(data)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var dbDeleteCmd = &cobra.Command{
|
||||
Use: "delete <id>",
|
||||
Short: "Delete a database and its data",
|
||||
@@ -278,6 +314,10 @@ func init() {
|
||||
dbCreateCmd.Flags().String("type", "postgres", "postgres|mysql|mariadb|mongodb|redis|dragonfly|clickhouse")
|
||||
dbCreateCmd.Flags().String("version", "", "engine version tag")
|
||||
dbCreateCmd.Flags().String("storage-size", "", "volume size (e.g. 10Gi)")
|
||||
dbCreateCmd.Flags().Bool("public", false, "bind the database port on all interfaces (default: localhost only)")
|
||||
dbUpdateCmd.Flags().Bool("public", false, "bind the database port on all interfaces")
|
||||
dbUpdateCmd.Flags().String("name", "", "rename the database")
|
||||
dbUpdateCmd.Flags().String("backup-schedule", "", "cron expression for automatic backups (empty clears)")
|
||||
for _, cmd := range []*cobra.Command{dbRegisterCmd, dbTestConnCmd} {
|
||||
cmd.Flags().String("type", "postgres", "postgres|mysql|mariadb|mongodb|redis|dragonfly|clickhouse")
|
||||
cmd.Flags().String("host", "", "database host")
|
||||
@@ -287,5 +327,5 @@ func init() {
|
||||
cmd.Flags().String("password", "", "database password (stored encrypted on register)")
|
||||
cmd.Flags().Bool("ssl", false, "require TLS")
|
||||
}
|
||||
DatabasesCmd.AddCommand(dbListCmd, dbGetCmd, dbCreateCmd, dbRegisterCmd, dbTestConnCmd, dbDeleteCmd, dbActionCmd, dbBackupCmd, dbRestoreCmd, dbDownloadCmd)
|
||||
DatabasesCmd.AddCommand(dbListCmd, dbGetCmd, dbCreateCmd, dbUpdateCmd, dbRegisterCmd, dbTestConnCmd, dbDeleteCmd, dbActionCmd, dbBackupCmd, dbRestoreCmd, dbDownloadCmd)
|
||||
}
|
||||
@@ -55,21 +55,22 @@ func (q *Queries) CreateDatabaseBackup(ctx context.Context, arg CreateDatabaseBa
|
||||
}
|
||||
|
||||
const createDatabaseService = `-- name: CreateDatabaseService :exec
|
||||
INSERT INTO database_services (id, user_id, name, type, status, version, plan, region, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
INSERT INTO database_services (id, user_id, name, type, status, version, plan, region, public_port, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
`
|
||||
|
||||
type CreateDatabaseServiceParams struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
Plan string `json:"plan"`
|
||||
Region string `json:"region"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
Plan string `json:"plan"`
|
||||
Region string `json:"region"`
|
||||
PublicPort bool `json:"public_port"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateDatabaseService(ctx context.Context, arg CreateDatabaseServiceParams) error {
|
||||
@@ -82,6 +83,7 @@ func (q *Queries) CreateDatabaseService(ctx context.Context, arg CreateDatabaseS
|
||||
arg.Version,
|
||||
arg.Plan,
|
||||
arg.Region,
|
||||
arg.PublicPort,
|
||||
arg.CreatedAt,
|
||||
arg.UpdatedAt,
|
||||
)
|
||||
@@ -196,7 +198,7 @@ func (q *Queries) GetDatabaseBackupByIDAndDatabaseAndUser(ctx context.Context, a
|
||||
}
|
||||
|
||||
const getDatabaseServiceByID = `-- name: GetDatabaseServiceByID :one
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.created_at, database_services.updated_at
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.public_port, database_services.created_at, database_services.updated_at
|
||||
FROM database_services
|
||||
WHERE id = $1
|
||||
`
|
||||
@@ -223,6 +225,7 @@ func (q *Queries) GetDatabaseServiceByID(ctx context.Context, id string) (Databa
|
||||
&i.ExternalUsername,
|
||||
&i.ExternalPassword,
|
||||
&i.ExternalSsl,
|
||||
&i.PublicPort,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
@@ -230,7 +233,7 @@ func (q *Queries) GetDatabaseServiceByID(ctx context.Context, id string) (Databa
|
||||
}
|
||||
|
||||
const getDatabaseServiceByIDAndUser = `-- name: GetDatabaseServiceByIDAndUser :one
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.created_at, database_services.updated_at
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.public_port, database_services.created_at, database_services.updated_at
|
||||
FROM database_services
|
||||
WHERE id = $1 AND user_id = $2
|
||||
`
|
||||
@@ -262,6 +265,7 @@ func (q *Queries) GetDatabaseServiceByIDAndUser(ctx context.Context, arg GetData
|
||||
&i.ExternalUsername,
|
||||
&i.ExternalPassword,
|
||||
&i.ExternalSsl,
|
||||
&i.PublicPort,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
@@ -282,7 +286,7 @@ func (q *Queries) GetDatabaseServiceOwnerID(ctx context.Context, id string) (str
|
||||
}
|
||||
|
||||
const listAllDatabaseServices = `-- name: ListAllDatabaseServices :many
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.created_at, database_services.updated_at
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.public_port, database_services.created_at, database_services.updated_at
|
||||
FROM database_services
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
@@ -315,6 +319,7 @@ func (q *Queries) ListAllDatabaseServices(ctx context.Context) ([]DatabaseServic
|
||||
&i.ExternalUsername,
|
||||
&i.ExternalPassword,
|
||||
&i.ExternalSsl,
|
||||
&i.PublicPort,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
@@ -378,7 +383,7 @@ func (q *Queries) ListDatabaseBackupsByDatabaseAndUser(ctx context.Context, arg
|
||||
}
|
||||
|
||||
const listDatabaseServicesByUser = `-- name: ListDatabaseServicesByUser :many
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.created_at, database_services.updated_at
|
||||
SELECT database_services.id, database_services.user_id, database_services.name, database_services.type, database_services.status, database_services.version, database_services.plan, database_services.region, database_services.connection_url, database_services.backup_schedule, database_services.next_backup_at, database_services.provider, database_services.external_host, database_services.external_port, database_services.external_name, database_services.external_username, database_services.external_password, database_services.external_ssl, database_services.public_port, database_services.created_at, database_services.updated_at
|
||||
FROM database_services
|
||||
WHERE user_id = $1
|
||||
ORDER BY created_at DESC
|
||||
@@ -412,6 +417,7 @@ func (q *Queries) ListDatabaseServicesByUser(ctx context.Context, userID string)
|
||||
&i.ExternalUsername,
|
||||
&i.ExternalPassword,
|
||||
&i.ExternalSsl,
|
||||
&i.PublicPort,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
@@ -527,6 +533,29 @@ func (q *Queries) SetDatabaseNextBackupAt(ctx context.Context, arg SetDatabaseNe
|
||||
return err
|
||||
}
|
||||
|
||||
const setDatabaseServicePublicPortByIDAndUser = `-- name: SetDatabaseServicePublicPortByIDAndUser :exec
|
||||
UPDATE database_services
|
||||
SET public_port = $1, updated_at = $2
|
||||
WHERE id = $3 AND user_id = $4
|
||||
`
|
||||
|
||||
type SetDatabaseServicePublicPortByIDAndUserParams struct {
|
||||
PublicPort bool `json:"public_port"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetDatabaseServicePublicPortByIDAndUser(ctx context.Context, arg SetDatabaseServicePublicPortByIDAndUserParams) error {
|
||||
_, err := q.db.ExecContext(ctx, setDatabaseServicePublicPortByIDAndUser,
|
||||
arg.PublicPort,
|
||||
arg.UpdatedAt,
|
||||
arg.ID,
|
||||
arg.UserID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const setDatabaseServiceStatusAndConnectionByID = `-- name: SetDatabaseServiceStatusAndConnectionByID :exec
|
||||
UPDATE database_services
|
||||
SET status = $1, connection_url = $2, updated_at = $3
|
||||
|
||||
@@ -98,6 +98,7 @@ type DatabaseService struct {
|
||||
ExternalUsername sql.NullString `json:"external_username"`
|
||||
ExternalPassword string `json:"external_password"`
|
||||
ExternalSsl bool `json:"external_ssl"`
|
||||
PublicPort bool `json:"public_port"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ type Querier interface {
|
||||
SetDatabaseBackupScheduleByIDAndUser(ctx context.Context, arg SetDatabaseBackupScheduleByIDAndUserParams) error
|
||||
SetDatabaseBackupStatusByID(ctx context.Context, arg SetDatabaseBackupStatusByIDParams) error
|
||||
SetDatabaseNextBackupAt(ctx context.Context, arg SetDatabaseNextBackupAtParams) error
|
||||
SetDatabaseServicePublicPortByIDAndUser(ctx context.Context, arg SetDatabaseServicePublicPortByIDAndUserParams) error
|
||||
SetDatabaseServiceStatusAndConnectionByID(ctx context.Context, arg SetDatabaseServiceStatusAndConnectionByIDParams) error
|
||||
SetDatabaseServiceStatusByID(ctx context.Context, arg SetDatabaseServiceStatusByIDParams) error
|
||||
SetDatabaseServiceStatusByIDAndUser(ctx context.Context, arg SetDatabaseServiceStatusByIDAndUserParams) error
|
||||
|
||||
@@ -98,7 +98,8 @@ func init() {
|
||||
// Databases
|
||||
register(spec{"containr_databases_list", "List managed databases", "GET", "/databases", nil, nil, false})
|
||||
register(spec{"containr_databases_get", "Get a database (connection URL included)", "GET", "/databases/{id}", []string{"id"}, nil, false})
|
||||
register(spec{"containr_databases_create", "Provision a managed database. Args: name, type (postgres|mysql|mariadb|mongodb|redis|dragonfly|clickhouse).", "POST", "/databases", nil, nil, false})
|
||||
register(spec{"containr_databases_create", "Provision a managed database. Args: name, type (postgres|mysql|mariadb|mongodb|redis|dragonfly|clickhouse), public_port (bind on all interfaces, default localhost-only).", "POST", "/databases", nil, nil, false})
|
||||
register(spec{"containr_databases_update", "Update a database. Args: id, name, backup_schedule (empty clears), public_port — toggling recreates the container so the new bind scope applies.", "PUT", "/databases/{id}", []string{"id"}, nil, false})
|
||||
register(spec{"containr_databases_register_external", "Register a database hosted elsewhere — probed first, password stored encrypted. Args: name (required), type (required), host (required), port, database, username, password, ssl.", "POST", "/databases/register-external", nil, []string{"name", "type", "host"}, false})
|
||||
register(spec{"containr_databases_test_connection", "Probe a DB connection ad-hoc. Args: type, host (required), port, database, username, password, ssl.", "POST", "/databases/test-connection", nil, []string{"type", "host"}, false})
|
||||
register(spec{"containr_databases_test_connection_by_id", "Re-probe a registered external database with its stored credentials. Args: id.", "POST", "/databases/{id}/test-connection", []string{"id"}, nil, false})
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
ALTER TABLE database_services
|
||||
ADD COLUMN IF NOT EXISTS public_port BOOLEAN NOT NULL DEFAULT false;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
ALTER TABLE database_services
|
||||
DROP COLUMN IF EXISTS public_port;
|
||||
-- +goose StatementEnd
|
||||
@@ -35,8 +35,13 @@ FROM database_services
|
||||
WHERE user_id = sqlc.arg(user_id) AND LOWER(name) = LOWER(sqlc.arg(name));
|
||||
|
||||
-- name: CreateDatabaseService :exec
|
||||
INSERT INTO database_services (id, user_id, name, type, status, version, plan, region, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
|
||||
INSERT INTO database_services (id, user_id, name, type, status, version, plan, region, public_port, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);
|
||||
|
||||
-- name: SetDatabaseServicePublicPortByIDAndUser :exec
|
||||
UPDATE database_services
|
||||
SET public_port = $1, updated_at = $2
|
||||
WHERE id = $3 AND user_id = $4;
|
||||
|
||||
-- name: UpdateDatabaseServiceNameAndPlanByIDAndUser :exec
|
||||
UPDATE database_services
|
||||
|
||||
@@ -146,6 +146,7 @@ CREATE TABLE database_services (
|
||||
external_username VARCHAR(255),
|
||||
external_password TEXT NOT NULL DEFAULT '',
|
||||
external_ssl BOOLEAN NOT NULL DEFAULT false,
|
||||
public_port BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user