mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
feat(backups): scheduled database backups via cron tick
- database_services gains backup_schedule + next_backup_at (goose migration, sqlc schema, new queries). - PUT /databases/:id accepts backup_schedule — validated cron expression, empty string disables; invalid returns 400. - Cron scheduler tick now also folds due databases into the existing snapshot pipeline (createBackupProcess). - Databases page backups section gets a schedule row showing current expr and next run. - resolveBackupConfig reports real next_backup/enabled instead of a fake last+24h estimate. - Verified e2e: '* * * * *' produced three consecutive completed archives containing the seeded payload; invalid schedule rejected. Authored By: TDvorak <[email protected]>
This commit is contained in:
1 parent
aeb91b03e9
commit
08d92fb10f
13 files changed
+387
-99
No files matched your search
@@ -2,8 +2,10 @@ package api
|
||||
|
||||
import (
|
||||
"containr/internal/database"
|
||||
"containr/internal/database/sqlcdb"
|
||||
"containr/internal/docker"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -436,6 +438,7 @@ func StartCronScheduler(ctx context.Context, db *database.DB, dockerClient *dock
|
||||
return
|
||||
case <-ticker.C:
|
||||
runDueCronJobs(db, dockerClient)
|
||||
runDueDatabaseBackups(db, dockerClient)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -478,6 +481,51 @@ func runDueCronJobs(db *database.DB, dockerClient *docker.Client) {
|
||||
}
|
||||
}
|
||||
|
||||
// runDueDatabaseBackups snapshots managed databases whose backup_schedule is
|
||||
// due, reusing the same archive pipeline as manual backups.
|
||||
func runDueDatabaseBackups(db *database.DB, dockerClient *docker.Client) {
|
||||
if dockerClient == nil {
|
||||
return
|
||||
}
|
||||
q := sqlcdb.New(db.DB)
|
||||
due, err := q.ListDueDatabaseBackups(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("cron scheduler: failed to list due database backups: %v", err)
|
||||
return
|
||||
}
|
||||
handler := NewDatabaseHandler(db.DB, dockerClient)
|
||||
for _, row := range due {
|
||||
schedule := row.BackupSchedule.String
|
||||
next, err := calculateNextRun(schedule, "UTC")
|
||||
if err != nil {
|
||||
log.Printf("cron scheduler: invalid backup schedule %q on %s: %v", schedule, row.ID, err)
|
||||
continue
|
||||
}
|
||||
now := time.Now()
|
||||
if err := q.SetDatabaseNextBackupAt(context.Background(), sqlcdb.SetDatabaseNextBackupAtParams{
|
||||
NextBackupAt: sql.NullTime{Time: *next, Valid: true},
|
||||
ID: row.ID,
|
||||
}); err != nil {
|
||||
log.Printf("cron scheduler: failed to advance backup schedule on %s: %v", row.ID, err)
|
||||
continue
|
||||
}
|
||||
backupID := generateDatabaseBackupID(row.ID)
|
||||
archivePath := sanitizeBackupArchivePath(managedDatabaseBackupArchivePath(backupID))
|
||||
if err := q.CreateDatabaseBackup(context.Background(), sqlcdb.CreateDatabaseBackupParams{
|
||||
ID: backupID,
|
||||
DatabaseID: row.ID,
|
||||
Size: "pending",
|
||||
Status: "in_progress",
|
||||
BackupPath: sql.NullString{String: archivePath, Valid: true},
|
||||
CreatedAt: sql.NullTime{Time: now, Valid: true},
|
||||
}); err != nil {
|
||||
log.Printf("cron scheduler: failed to record backup for %s: %v", row.ID, err)
|
||||
continue
|
||||
}
|
||||
go handler.createBackupProcess(row.ID, backupID, archivePath)
|
||||
}
|
||||
}
|
||||
|
||||
// findServiceContainer returns the first running container whose name matches
|
||||
// the deployment naming convention containr-<serviceID>-*.
|
||||
func findServiceContainer(ctx context.Context, dockerClient *docker.Client, serviceID string) (string, error) {
|
||||
|
||||
@@ -47,6 +47,8 @@ type DatabaseService struct {
|
||||
Version string `json:"version" db:"version"`
|
||||
Plan string `json:"plan" db:"plan"` // hobby, starter, standard, business
|
||||
Region string `json:"region" db:"region"`
|
||||
BackupSchedule string `json:"backup_schedule,omitempty"`
|
||||
NextBackupAt *time.Time `json:"next_backup_at,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
ConnectionURL string `json:"connection_url"`
|
||||
@@ -102,8 +104,9 @@ type DatabaseCreateRequest struct {
|
||||
|
||||
// DatabaseUpdateRequest represents a request to update a database
|
||||
type DatabaseUpdateRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Plan string `json:"plan,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Plan string `json:"plan,omitempty"`
|
||||
BackupSchedule *string `json:"backup_schedule,omitempty"`
|
||||
}
|
||||
|
||||
// DatabaseActionRequest represents a request to perform database actions
|
||||
@@ -183,7 +186,7 @@ func (h *DatabaseHandler) GetDatabases(c *gin.Context) {
|
||||
db = h.reconcileManagedDatabaseState(c.Request.Context(), db)
|
||||
|
||||
db.Metrics = h.resolveDatabaseMetrics(c.Request.Context(), db)
|
||||
if backupConfig, err := h.resolveBackupConfig(c.Request.Context(), userID, db.ID); err == nil {
|
||||
if backupConfig, err := h.resolveBackupConfig(c.Request.Context(), userID, db); err == nil {
|
||||
db.Backups = backupConfig
|
||||
} else {
|
||||
db.Backups = h.generateMockBackupConfig()
|
||||
@@ -220,7 +223,7 @@ func (h *DatabaseHandler) GetDatabase(c *gin.Context) {
|
||||
db = h.reconcileManagedDatabaseState(c.Request.Context(), db)
|
||||
|
||||
db.Metrics = h.resolveDatabaseMetrics(c.Request.Context(), db)
|
||||
if backupConfig, err := h.resolveBackupConfig(c.Request.Context(), userID, db.ID); err == nil {
|
||||
if backupConfig, err := h.resolveBackupConfig(c.Request.Context(), userID, db); err == nil {
|
||||
db.Backups = backupConfig
|
||||
} else {
|
||||
db.Backups = h.generateMockBackupConfig()
|
||||
@@ -364,40 +367,67 @@ func (h *DatabaseHandler) UpdateDatabase(c *gin.Context) {
|
||||
req.Name = strings.TrimSpace(req.Name)
|
||||
req.Plan = strings.TrimSpace(req.Plan)
|
||||
|
||||
if req.Name == "" && req.Plan == "" {
|
||||
if req.Name == "" && req.Plan == "" && req.BackupSchedule == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "No fields to update"})
|
||||
return
|
||||
}
|
||||
|
||||
now := sql.NullTime{Time: time.Now(), Valid: true}
|
||||
var err error
|
||||
switch {
|
||||
case req.Name != "" && req.Plan != "":
|
||||
err = h.queries.UpdateDatabaseServiceNameAndPlanByIDAndUser(c.Request.Context(), sqlcdb.UpdateDatabaseServiceNameAndPlanByIDAndUserParams{
|
||||
Name: req.Name,
|
||||
Plan: req.Plan,
|
||||
UpdatedAt: now,
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
})
|
||||
case req.Name != "":
|
||||
err = h.queries.UpdateDatabaseServiceNameByIDAndUser(c.Request.Context(), sqlcdb.UpdateDatabaseServiceNameByIDAndUserParams{
|
||||
Name: req.Name,
|
||||
UpdatedAt: now,
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
})
|
||||
default:
|
||||
err = h.queries.UpdateDatabaseServicePlanByIDAndUser(c.Request.Context(), sqlcdb.UpdateDatabaseServicePlanByIDAndUserParams{
|
||||
Plan: req.Plan,
|
||||
UpdatedAt: now,
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
if req.BackupSchedule != nil {
|
||||
var schedule sql.NullString
|
||||
var nextBackup sql.NullTime
|
||||
if expr := strings.TrimSpace(*req.BackupSchedule); expr != "" {
|
||||
next, err := calculateNextRun(expr, "UTC")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid cron schedule"})
|
||||
return
|
||||
}
|
||||
schedule = sql.NullString{String: expr, Valid: true}
|
||||
nextBackup = sql.NullTime{Time: *next, Valid: true}
|
||||
}
|
||||
err := h.queries.SetDatabaseBackupScheduleByIDAndUser(c.Request.Context(), sqlcdb.SetDatabaseBackupScheduleByIDAndUserParams{
|
||||
BackupSchedule: schedule,
|
||||
NextBackupAt: nextBackup,
|
||||
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update backup schedule"})
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update database"})
|
||||
return
|
||||
|
||||
if req.Name != "" || req.Plan != "" {
|
||||
now := sql.NullTime{Time: time.Now(), Valid: true}
|
||||
var err error
|
||||
switch {
|
||||
case req.Name != "" && req.Plan != "":
|
||||
err = h.queries.UpdateDatabaseServiceNameAndPlanByIDAndUser(c.Request.Context(), sqlcdb.UpdateDatabaseServiceNameAndPlanByIDAndUserParams{
|
||||
Name: req.Name,
|
||||
Plan: req.Plan,
|
||||
UpdatedAt: now,
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
})
|
||||
case req.Name != "":
|
||||
err = h.queries.UpdateDatabaseServiceNameByIDAndUser(c.Request.Context(), sqlcdb.UpdateDatabaseServiceNameByIDAndUserParams{
|
||||
Name: req.Name,
|
||||
UpdatedAt: now,
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
})
|
||||
default:
|
||||
err = h.queries.UpdateDatabaseServicePlanByIDAndUser(c.Request.Context(), sqlcdb.UpdateDatabaseServicePlanByIDAndUserParams{
|
||||
Plan: req.Plan,
|
||||
UpdatedAt: now,
|
||||
ID: databaseID,
|
||||
UserID: userID,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update database"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Database updated successfully"})
|
||||
@@ -839,9 +869,9 @@ func roundFloat(value float64, places int) float64 {
|
||||
return math.Round(value*pow) / pow
|
||||
}
|
||||
|
||||
func (h *DatabaseHandler) resolveBackupConfig(ctx context.Context, userID, databaseID string) (DatabaseBackupConfig, error) {
|
||||
func (h *DatabaseHandler) resolveBackupConfig(ctx context.Context, userID string, db DatabaseService) (DatabaseBackupConfig, error) {
|
||||
rows, err := h.queries.ListDatabaseBackupsByDatabaseAndUser(ctx, sqlcdb.ListDatabaseBackupsByDatabaseAndUserParams{
|
||||
DatabaseID: databaseID,
|
||||
DatabaseID: db.ID,
|
||||
UserID: userID,
|
||||
Limit: 20,
|
||||
})
|
||||
@@ -865,17 +895,11 @@ func (h *DatabaseHandler) resolveBackupConfig(ctx context.Context, userID, datab
|
||||
}
|
||||
}
|
||||
|
||||
var nextBackup *time.Time
|
||||
if lastBackup != nil {
|
||||
t := lastBackup.Add(24 * time.Hour)
|
||||
nextBackup = &t
|
||||
}
|
||||
|
||||
return DatabaseBackupConfig{
|
||||
Enabled: true,
|
||||
Enabled: db.BackupSchedule != "",
|
||||
LastBackup: lastBackup,
|
||||
Retention: 30,
|
||||
NextBackup: nextBackup,
|
||||
NextBackup: db.NextBackupAt,
|
||||
Backups: backups,
|
||||
}, nil
|
||||
}
|
||||
@@ -1017,30 +1041,34 @@ func (h *DatabaseHandler) resolveManagedRuntimeStatus(ctx context.Context, db Da
|
||||
func mapDatabaseServiceListRow(row sqlcdb.ListDatabaseServicesByUserRow) DatabaseService {
|
||||
return DatabaseService{
|
||||
ID: row.ID,
|
||||
Name: row.Name,
|
||||
Type: row.Type,
|
||||
Status: row.Status,
|
||||
Version: row.Version,
|
||||
Plan: row.Plan,
|
||||
Region: row.Region,
|
||||
ConnectionURL: databaseNullString(row.ConnectionUrl),
|
||||
CreatedAt: databaseNullTime(row.CreatedAt),
|
||||
UpdatedAt: databaseNullTime(row.UpdatedAt),
|
||||
Name: row.Name,
|
||||
Type: row.Type,
|
||||
Status: row.Status,
|
||||
Version: row.Version,
|
||||
Plan: row.Plan,
|
||||
Region: row.Region,
|
||||
BackupSchedule: databaseNullString(row.BackupSchedule),
|
||||
NextBackupAt: databaseNullTimePtr(row.NextBackupAt),
|
||||
ConnectionURL: databaseNullString(row.ConnectionUrl),
|
||||
CreatedAt: databaseNullTime(row.CreatedAt),
|
||||
UpdatedAt: databaseNullTime(row.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func mapDatabaseServiceGetRow(row sqlcdb.GetDatabaseServiceByIDAndUserRow) DatabaseService {
|
||||
return DatabaseService{
|
||||
ID: row.ID,
|
||||
Name: row.Name,
|
||||
Type: row.Type,
|
||||
Status: row.Status,
|
||||
Version: row.Version,
|
||||
Plan: row.Plan,
|
||||
Region: row.Region,
|
||||
ConnectionURL: databaseNullString(row.ConnectionUrl),
|
||||
CreatedAt: databaseNullTime(row.CreatedAt),
|
||||
UpdatedAt: databaseNullTime(row.UpdatedAt),
|
||||
ID: row.ID,
|
||||
Name: row.Name,
|
||||
Type: row.Type,
|
||||
Status: row.Status,
|
||||
Version: row.Version,
|
||||
Plan: row.Plan,
|
||||
Region: row.Region,
|
||||
BackupSchedule: databaseNullString(row.BackupSchedule),
|
||||
NextBackupAt: databaseNullTimePtr(row.NextBackupAt),
|
||||
ConnectionURL: databaseNullString(row.ConnectionUrl),
|
||||
CreatedAt: databaseNullTime(row.CreatedAt),
|
||||
UpdatedAt: databaseNullTime(row.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1051,6 +1079,14 @@ func databaseNullTime(value sql.NullTime) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func databaseNullTimePtr(value sql.NullTime) *time.Time {
|
||||
if value.Valid {
|
||||
t := value.Time
|
||||
return &t
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func databaseNullString(value sql.NullString) string {
|
||||
if value.Valid {
|
||||
return value.String
|
||||
|
||||
@@ -150,7 +150,7 @@ func (q *Queries) GetDatabaseBackupByIDAndDatabaseAndUser(ctx context.Context, a
|
||||
}
|
||||
|
||||
const getDatabaseServiceByIDAndUser = `-- name: GetDatabaseServiceByIDAndUser :one
|
||||
SELECT id, name, type, status, version, plan, region, connection_url, created_at, updated_at
|
||||
SELECT id, name, type, status, version, plan, region, connection_url, backup_schedule, next_backup_at, created_at, updated_at
|
||||
FROM database_services
|
||||
WHERE id = $1 AND user_id = $2
|
||||
`
|
||||
@@ -161,16 +161,18 @@ type GetDatabaseServiceByIDAndUserParams struct {
|
||||
}
|
||||
|
||||
type GetDatabaseServiceByIDAndUserRow struct {
|
||||
ID string `json:"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"`
|
||||
ConnectionUrl sql.NullString `json:"connection_url"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
ID string `json:"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"`
|
||||
ConnectionUrl sql.NullString `json:"connection_url"`
|
||||
BackupSchedule sql.NullString `json:"backup_schedule"`
|
||||
NextBackupAt sql.NullTime `json:"next_backup_at"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetDatabaseServiceByIDAndUser(ctx context.Context, arg GetDatabaseServiceByIDAndUserParams) (GetDatabaseServiceByIDAndUserRow, error) {
|
||||
@@ -185,6 +187,8 @@ func (q *Queries) GetDatabaseServiceByIDAndUser(ctx context.Context, arg GetData
|
||||
&i.Plan,
|
||||
&i.Region,
|
||||
&i.ConnectionUrl,
|
||||
&i.BackupSchedule,
|
||||
&i.NextBackupAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
@@ -238,23 +242,25 @@ func (q *Queries) ListDatabaseBackupsByDatabaseAndUser(ctx context.Context, arg
|
||||
}
|
||||
|
||||
const listDatabaseServicesByUser = `-- name: ListDatabaseServicesByUser :many
|
||||
SELECT id, name, type, status, version, plan, region, connection_url, created_at, updated_at
|
||||
SELECT id, name, type, status, version, plan, region, connection_url, backup_schedule, next_backup_at, created_at, updated_at
|
||||
FROM database_services
|
||||
WHERE user_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
type ListDatabaseServicesByUserRow struct {
|
||||
ID string `json:"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"`
|
||||
ConnectionUrl sql.NullString `json:"connection_url"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
ID string `json:"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"`
|
||||
ConnectionUrl sql.NullString `json:"connection_url"`
|
||||
BackupSchedule sql.NullString `json:"backup_schedule"`
|
||||
NextBackupAt sql.NullTime `json:"next_backup_at"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListDatabaseServicesByUser(ctx context.Context, userID string) ([]ListDatabaseServicesByUserRow, error) {
|
||||
@@ -275,6 +281,8 @@ func (q *Queries) ListDatabaseServicesByUser(ctx context.Context, userID string)
|
||||
&i.Plan,
|
||||
&i.Region,
|
||||
&i.ConnectionUrl,
|
||||
&i.BackupSchedule,
|
||||
&i.NextBackupAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
@@ -291,6 +299,66 @@ func (q *Queries) ListDatabaseServicesByUser(ctx context.Context, userID string)
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listDueDatabaseBackups = `-- name: ListDueDatabaseBackups :many
|
||||
SELECT id, backup_schedule
|
||||
FROM database_services
|
||||
WHERE backup_schedule IS NOT NULL AND backup_schedule <> ''
|
||||
AND next_backup_at IS NOT NULL AND next_backup_at <= NOW()
|
||||
`
|
||||
|
||||
type ListDueDatabaseBackupsRow struct {
|
||||
ID string `json:"id"`
|
||||
BackupSchedule sql.NullString `json:"backup_schedule"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListDueDatabaseBackups(ctx context.Context) ([]ListDueDatabaseBackupsRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listDueDatabaseBackups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListDueDatabaseBackupsRow{}
|
||||
for rows.Next() {
|
||||
var i ListDueDatabaseBackupsRow
|
||||
if err := rows.Scan(&i.ID, &i.BackupSchedule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const setDatabaseBackupScheduleByIDAndUser = `-- name: SetDatabaseBackupScheduleByIDAndUser :exec
|
||||
UPDATE database_services
|
||||
SET backup_schedule = $1, next_backup_at = $2, updated_at = $3
|
||||
WHERE id = $4 AND user_id = $5
|
||||
`
|
||||
|
||||
type SetDatabaseBackupScheduleByIDAndUserParams struct {
|
||||
BackupSchedule sql.NullString `json:"backup_schedule"`
|
||||
NextBackupAt sql.NullTime `json:"next_backup_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetDatabaseBackupScheduleByIDAndUser(ctx context.Context, arg SetDatabaseBackupScheduleByIDAndUserParams) error {
|
||||
_, err := q.db.ExecContext(ctx, setDatabaseBackupScheduleByIDAndUser,
|
||||
arg.BackupSchedule,
|
||||
arg.NextBackupAt,
|
||||
arg.UpdatedAt,
|
||||
arg.ID,
|
||||
arg.UserID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const setDatabaseBackupStatusByID = `-- name: SetDatabaseBackupStatusByID :exec
|
||||
UPDATE database_backups
|
||||
SET status = $1, size = $2, completed_at = $3
|
||||
@@ -314,6 +382,22 @@ func (q *Queries) SetDatabaseBackupStatusByID(ctx context.Context, arg SetDataba
|
||||
return err
|
||||
}
|
||||
|
||||
const setDatabaseNextBackupAt = `-- name: SetDatabaseNextBackupAt :exec
|
||||
UPDATE database_services
|
||||
SET next_backup_at = $1
|
||||
WHERE id = $2
|
||||
`
|
||||
|
||||
type SetDatabaseNextBackupAtParams struct {
|
||||
NextBackupAt sql.NullTime `json:"next_backup_at"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetDatabaseNextBackupAt(ctx context.Context, arg SetDatabaseNextBackupAtParams) error {
|
||||
_, err := q.db.ExecContext(ctx, setDatabaseNextBackupAt, arg.NextBackupAt, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const setDatabaseServiceStatusAndConnectionByID = `-- name: SetDatabaseServiceStatusAndConnectionByID :exec
|
||||
UPDATE database_services
|
||||
SET status = $1, connection_url = $2, updated_at = $3
|
||||
|
||||
@@ -80,17 +80,19 @@ type DatabaseBackup struct {
|
||||
}
|
||||
|
||||
type DatabaseService 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"`
|
||||
ConnectionUrl sql.NullString `json:"connection_url"`
|
||||
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"`
|
||||
ConnectionUrl sql.NullString `json:"connection_url"`
|
||||
BackupSchedule sql.NullString `json:"backup_schedule"`
|
||||
NextBackupAt sql.NullTime `json:"next_backup_at"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Deployment struct {
|
||||
|
||||
@@ -49,12 +49,15 @@ type Querier interface {
|
||||
ListContainersForAgent(ctx context.Context, nodeAgentID string) ([]ContainerInstance, error)
|
||||
ListDatabaseBackupsByDatabaseAndUser(ctx context.Context, arg ListDatabaseBackupsByDatabaseAndUserParams) ([]DatabaseBackup, error)
|
||||
ListDatabaseServicesByUser(ctx context.Context, userID string) ([]ListDatabaseServicesByUserRow, error)
|
||||
ListDueDatabaseBackups(ctx context.Context) ([]ListDueDatabaseBackupsRow, error)
|
||||
ListPendingCommands(ctx context.Context, nodeAgentID string) ([]AgentCommand, error)
|
||||
ListProjectsWithStatsByUser(ctx context.Context, arg ListProjectsWithStatsByUserParams) ([]ListProjectsWithStatsByUserRow, error)
|
||||
ListServiceTemplates(ctx context.Context) ([]ServiceTemplate, error)
|
||||
ListServiceTemplatesByCategory(ctx context.Context, category string) ([]ServiceTemplate, error)
|
||||
RevokeAgentAuthToken(ctx context.Context, id uuid.UUID) (AgentAuthToken, error)
|
||||
SetDatabaseBackupScheduleByIDAndUser(ctx context.Context, arg SetDatabaseBackupScheduleByIDAndUserParams) error
|
||||
SetDatabaseBackupStatusByID(ctx context.Context, arg SetDatabaseBackupStatusByIDParams) error
|
||||
SetDatabaseNextBackupAt(ctx context.Context, arg SetDatabaseNextBackupAtParams) error
|
||||
SetDatabaseServiceStatusAndConnectionByID(ctx context.Context, arg SetDatabaseServiceStatusAndConnectionByIDParams) error
|
||||
SetDatabaseServiceStatusByID(ctx context.Context, arg SetDatabaseServiceStatusByIDParams) error
|
||||
SetDatabaseServiceStatusByIDAndUser(ctx context.Context, arg SetDatabaseServiceStatusByIDAndUserParams) error
|
||||
|
||||
Reference in new issue
Block a user