small fix, don't worry about it

This commit is contained in:
Tomas Dvorak
2026-04-10 12:02:36 +02:00
parent 08bd0c6e5c
commit 08cb5754f3
638 changed files with 57332 additions and 34706 deletions
@@ -0,0 +1,135 @@
package database
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/pressly/goose/v3"
)
const (
defaultLegacyMigrationsDir = "migrations"
defaultGooseMigrationsDir = "migrations_goose"
migrationAdvisoryLockKey = int64(637266846588921720)
)
var migrationLockRetryInterval = 250 * time.Millisecond
// MigrateAll runs legacy app migrations first, then goose-managed migrations.
// This allows a safe transition without breaking existing installations.
func (db *DB) MigrateAll(legacyDir, gooseDir string) error {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
return db.MigrateAllWithLock(ctx, legacyDir, gooseDir)
}
// MigrateAllWithLock runs legacy + goose migrations while holding a PostgreSQL
// advisory lock to prevent concurrent migrators from racing.
func (db *DB) MigrateAllWithLock(ctx context.Context, legacyDir, gooseDir string) error {
if legacyDir == "" {
legacyDir = defaultLegacyMigrationsDir
}
if gooseDir == "" {
gooseDir = defaultGooseMigrationsDir
}
releaseLock, err := db.acquireMigrationLock(ctx)
if err != nil {
return err
}
defer func() {
if unlockErr := releaseLock(); unlockErr != nil {
log.Printf("Warning: failed to release migration advisory lock: %v", unlockErr)
}
}()
if err := db.Migrate(legacyDir); err != nil {
return err
}
if err := db.MigrateGoose(gooseDir); err != nil {
return err
}
return nil
}
func (db *DB) acquireMigrationLock(ctx context.Context) (func() error, error) {
if ctx == nil {
ctx = context.Background()
}
conn, err := db.Conn(ctx)
if err != nil {
return nil, fmt.Errorf("failed to open connection for migration lock: %w", err)
}
for {
var acquired bool
if err := conn.QueryRowContext(ctx, "SELECT pg_try_advisory_lock($1)", migrationAdvisoryLockKey).Scan(&acquired); err != nil {
conn.Close()
return nil, fmt.Errorf("failed to acquire migration advisory lock: %w", err)
}
if acquired {
break
}
select {
case <-ctx.Done():
conn.Close()
return nil, fmt.Errorf("timed out waiting for migration advisory lock: %w", ctx.Err())
case <-time.After(migrationLockRetryInterval):
}
}
log.Println("Acquired migration advisory lock")
return func() error {
defer conn.Close()
var released bool
if err := conn.QueryRowContext(context.Background(), "SELECT pg_advisory_unlock($1)", migrationAdvisoryLockKey).Scan(&released); err != nil {
return fmt.Errorf("failed to release migration advisory lock: %w", err)
}
if !released {
return fmt.Errorf("migration advisory lock was not held by current session")
}
log.Println("Released migration advisory lock")
return nil
}, nil
}
func (db *DB) MigrateGoose(migrationsDir string) error {
if _, err := os.Stat(migrationsDir); err != nil {
if os.IsNotExist(err) {
log.Printf("Goose migrations directory %q not found, skipping goose migrations", migrationsDir)
return nil
}
return fmt.Errorf("failed to access goose migrations directory %q: %w", migrationsDir, err)
}
if err := goose.SetDialect("postgres"); err != nil {
return fmt.Errorf("failed to set goose dialect: %w", err)
}
if err := goose.Up(db.DB, migrationsDir); err != nil {
return fmt.Errorf("failed to run goose migrations from %q: %w", migrationsDir, err)
}
log.Printf("Goose migrations completed successfully from %q", migrationsDir)
return nil
}
func (db *DB) GooseStatus(migrationsDir string) error {
if err := goose.SetDialect("postgres"); err != nil {
return fmt.Errorf("failed to set goose dialect: %w", err)
}
if err := goose.Status(db.DB, migrationsDir); err != nil {
return fmt.Errorf("failed to get goose migration status: %w", err)
}
return nil
}
+187
View File
@@ -0,0 +1,187 @@
package database
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"sort"
)
var managedMigrationPattern = regexp.MustCompile(`^\d{3}_.+\.sql$`)
// Migrate runs all migration files in the migrations directory
func (db *DB) Migrate(migrationsDir string) error {
if err := db.ensureRequiredExtensions(); err != nil {
return err
}
// Create migrations table if it doesn't exist
if err := db.createMigrationsTable(); err != nil {
return fmt.Errorf("failed to create migrations table: %w", err)
}
migrationFiles, err := listManagedMigrationFiles(migrationsDir)
if err != nil {
return err
}
// Run each migration that hasn't been run yet
for _, fileName := range migrationFiles {
if err := db.runMigration(migrationsDir, fileName); err != nil {
return fmt.Errorf("failed to run migration %s: %w", fileName, err)
}
}
log.Println("All migrations completed successfully")
return nil
}
func listManagedMigrationFiles(migrationsDir string) ([]string, error) {
entries, err := os.ReadDir(migrationsDir)
if err != nil {
return nil, fmt.Errorf("failed to read migrations directory: %w", err)
}
migrationFiles := make([]string, 0, len(entries))
for _, entry := range entries {
if entry.IsDir() {
continue
}
fileName := entry.Name()
if !isManagedMigrationFile(fileName) {
log.Printf("Skipping unmanaged migration file: %s", fileName)
continue
}
migrationFiles = append(migrationFiles, fileName)
}
sort.Strings(migrationFiles)
return migrationFiles, nil
}
func isManagedMigrationFile(fileName string) bool {
return managedMigrationPattern.MatchString(fileName)
}
func (db *DB) ensureRequiredExtensions() error {
if _, err := db.Exec(`CREATE EXTENSION IF NOT EXISTS pgcrypto`); err != nil {
return fmt.Errorf("failed to ensure pgcrypto extension required for UUID defaults: %w", err)
}
return nil
}
func (db *DB) createMigrationsTable() error {
query := `
CREATE TABLE IF NOT EXISTS migrations (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) UNIQUE NOT NULL,
executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
)
`
_, err := db.Exec(query)
return err
}
func (db *DB) runMigration(migrationsDir, fileName string) error {
// Check if migration has already been run
var count int
err := db.QueryRow("SELECT COUNT(*) FROM migrations WHERE filename = $1", fileName).Scan(&count)
if err != nil {
return fmt.Errorf("failed to check migration status: %w", err)
}
if count > 0 {
log.Printf("Migration %s already executed, skipping", fileName)
return nil
}
// Read migration file
filePath := filepath.Join(migrationsDir, fileName)
content, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("failed to read migration file %s: %w", fileName, err)
}
// Execute migration in a transaction
tx, err := db.BeginTx(context.Background(), nil)
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
// Execute migration SQL
_, err = tx.Exec(string(content))
if err != nil {
return fmt.Errorf("failed to execute migration %s: %w", fileName, err)
}
// Record that migration was executed
_, err = tx.Exec("INSERT INTO migrations (filename) VALUES ($1)", fileName)
if err != nil {
return fmt.Errorf("failed to record migration %s: %w", fileName, err)
}
// Commit transaction
if err = tx.Commit(); err != nil {
return fmt.Errorf("failed to commit migration %s: %w", fileName, err)
}
log.Printf("Successfully executed migration: %s", fileName)
return nil
}
// SeedData inserts initial data for development
func (db *DB) SeedData() error {
// Check if we already have users
var count int
err := db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
if err != nil {
return fmt.Errorf("failed to check existing users: %w", err)
}
if count > 0 {
log.Println("Database already has data, skipping seed")
return nil
}
// Insert demo user
hashedPassword := "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi" // "password"
_, err = db.Exec(`
INSERT INTO users (email, password_hash, name)
VALUES ($1, $2, $3)
`, "demo@containr.dev", hashedPassword, "Demo User")
if err != nil {
return fmt.Errorf("failed to create demo user: %w", err)
}
// Insert demo project
var projectID string
err = db.QueryRow(`
INSERT INTO projects (name, description, owner_id)
VALUES ($1, $2, (SELECT id FROM users WHERE email = $3))
RETURNING id
`, "Demo Project", "A sample project to showcase Containr features", "demo@containr.dev").Scan(&projectID)
if err != nil {
return fmt.Errorf("failed to create demo project: %w", err)
}
// Insert environments
environments := []string{"production", "preview", "development"}
for _, env := range environments {
_, err = db.Exec(`
INSERT INTO environments (name, project_id)
VALUES ($1, $2)
`, env, projectID)
if err != nil {
return fmt.Errorf("failed to create environment %s: %w", env, err)
}
}
log.Println("Database seeded successfully")
return nil
}
@@ -0,0 +1,63 @@
package database
import (
"os"
"path/filepath"
"reflect"
"testing"
)
func TestIsManagedMigrationFile(t *testing.T) {
cases := []struct {
name string
fileName string
want bool
}{
{name: "valid triple digit migration", fileName: "001_initial_schema.sql", want: true},
{name: "supports higher migration number", fileName: "120_add_table.sql", want: true},
{name: "legacy sqlite migration excluded", fileName: "0001_init.sql", want: false},
{name: "backup file excluded", fileName: "001_initial_schema.sql.bak", want: false},
{name: "non migration sql excluded", fileName: "seed.sql", want: false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := isManagedMigrationFile(tc.fileName)
if got != tc.want {
t.Fatalf("isManagedMigrationFile(%q) = %v, want %v", tc.fileName, got, tc.want)
}
})
}
}
func TestListManagedMigrationFiles(t *testing.T) {
dir := t.TempDir()
files := []string{
"020_add_database_templates.sql",
"001_initial_schema.sql",
"0001_init.sql",
"README.txt",
"021_expand_database_service_types.sql",
}
for _, file := range files {
path := filepath.Join(dir, file)
if err := os.WriteFile(path, []byte("-- test"), 0o644); err != nil {
t.Fatalf("failed to write temp file %s: %v", file, err)
}
}
got, err := listManagedMigrationFiles(dir)
if err != nil {
t.Fatalf("listManagedMigrationFiles returned error: %v", err)
}
want := []string{
"001_initial_schema.sql",
"020_add_database_templates.sql",
"021_expand_database_service_types.sql",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("listManagedMigrationFiles = %v, want %v", got, want)
}
}
+59
View File
@@ -0,0 +1,59 @@
package database
import (
"context"
"database/sql"
"fmt"
"time"
_ "github.com/lib/pq"
)
type DB struct {
*sql.DB
}
type DBConfig struct {
MaxOpenConns int
MaxIdleConns int
ConnMaxLifetime time.Duration
ConnMaxIdleTime time.Duration
}
func NewConnection(databaseURL string) (*DB, error) {
return NewConnectionWithConfig(databaseURL, DBConfig{
MaxOpenConns: 25,
MaxIdleConns: 25,
ConnMaxLifetime: 5 * time.Minute,
ConnMaxIdleTime: 5 * time.Minute,
})
}
func NewConnectionWithConfig(databaseURL string, config DBConfig) (*DB, error) {
db, err := sql.Open("postgres", databaseURL)
if err != nil {
return nil, fmt.Errorf("unable to open database: %w", err)
}
// Configure connection pool
db.SetMaxOpenConns(config.MaxOpenConns)
db.SetMaxIdleConns(config.MaxIdleConns)
db.SetConnMaxLifetime(config.ConnMaxLifetime)
db.SetConnMaxIdleTime(config.ConnMaxIdleTime)
// Test the connection
if err := db.PingContext(context.Background()); err != nil {
return nil, fmt.Errorf("unable to ping database: %w", err)
}
return &DB{DB: db}, nil
}
func (db *DB) Health(ctx context.Context) error {
return db.PingContext(ctx)
}
// Stats returns connection pool statistics for monitoring
func (db *DB) Stats() sql.DBStats {
return db.Stats()
}
+55
View File
@@ -0,0 +1,55 @@
package database
import (
"context"
"fmt"
"strings"
"time"
"github.com/go-redis/redis/v8"
)
type Redis struct {
Client *redis.Client
}
func NewRedis(redisURL string) (*Redis, error) {
if strings.TrimSpace(redisURL) == "" {
return nil, fmt.Errorf("redis URL cannot be empty")
}
opt, err := redis.ParseURL(redisURL)
if err != nil {
return nil, fmt.Errorf("invalid redis URL: %w", err)
}
client := redis.NewClient(opt)
return &Redis{Client: client}, nil
}
func (r *Redis) Close() error {
return r.Client.Close()
}
func (r *Redis) Health(ctx context.Context) error {
_, err := r.Client.Ping(ctx).Result()
return err
}
func (r *Redis) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
return r.Client.Set(ctx, key, value, expiration).Err()
}
func (r *Redis) Get(ctx context.Context, key string) (string, error) {
return r.Client.Get(ctx, key).Result()
}
func (r *Redis) Del(ctx context.Context, keys ...string) error {
return r.Client.Del(ctx, keys...).Err()
}
func (r *Redis) Exists(ctx context.Context, key string) (bool, error) {
result, err := r.Client.Exists(ctx, key).Result()
return result > 0, err
}
@@ -0,0 +1,23 @@
package database
import "testing"
func TestNewRedisRejectsEmptyURL(t *testing.T) {
if _, err := NewRedis(""); err == nil {
t.Fatal("expected error for empty redis URL")
}
}
func TestNewRedisRejectsInvalidURL(t *testing.T) {
if _, err := NewRedis("://bad-redis-url"); err == nil {
t.Fatal("expected error for invalid redis URL")
}
}
func TestNewRedisAcceptsValidURL(t *testing.T) {
r, err := NewRedis("redis://:password@localhost:6379/0")
if err != nil {
t.Fatalf("expected valid redis URL, got error: %v", err)
}
_ = r.Close()
}
@@ -0,0 +1,449 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: databases.sql
package sqlcdb
import (
"context"
"database/sql"
)
const countDatabaseServicesByUserAndName = `-- name: CountDatabaseServicesByUserAndName :one
SELECT COUNT(*)
FROM database_services
WHERE user_id = $1 AND LOWER(name) = LOWER($2)
`
type CountDatabaseServicesByUserAndNameParams struct {
UserID string `json:"user_id"`
Name string `json:"name"`
}
func (q *Queries) CountDatabaseServicesByUserAndName(ctx context.Context, arg CountDatabaseServicesByUserAndNameParams) (int64, error) {
row := q.db.QueryRowContext(ctx, countDatabaseServicesByUserAndName, arg.UserID, arg.Name)
var count int64
err := row.Scan(&count)
return count, err
}
const createDatabaseBackup = `-- name: CreateDatabaseBackup :exec
INSERT INTO database_backups (id, database_id, size, status, backup_path, created_at)
VALUES ($1, $2, $3, $4, $5, $6)
`
type CreateDatabaseBackupParams struct {
ID string `json:"id"`
DatabaseID string `json:"database_id"`
Size string `json:"size"`
Status string `json:"status"`
BackupPath sql.NullString `json:"backup_path"`
CreatedAt sql.NullTime `json:"created_at"`
}
func (q *Queries) CreateDatabaseBackup(ctx context.Context, arg CreateDatabaseBackupParams) error {
_, err := q.db.ExecContext(ctx, createDatabaseBackup,
arg.ID,
arg.DatabaseID,
arg.Size,
arg.Status,
arg.BackupPath,
arg.CreatedAt,
)
return err
}
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)
`
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"`
}
func (q *Queries) CreateDatabaseService(ctx context.Context, arg CreateDatabaseServiceParams) error {
_, err := q.db.ExecContext(ctx, createDatabaseService,
arg.ID,
arg.UserID,
arg.Name,
arg.Type,
arg.Status,
arg.Version,
arg.Plan,
arg.Region,
arg.CreatedAt,
arg.UpdatedAt,
)
return err
}
const databaseServiceExistsByIDAndUser = `-- name: DatabaseServiceExistsByIDAndUser :one
SELECT EXISTS(
SELECT 1 FROM database_services WHERE id = $1 AND user_id = $2
)
`
type DatabaseServiceExistsByIDAndUserParams struct {
ID string `json:"id"`
UserID string `json:"user_id"`
}
func (q *Queries) DatabaseServiceExistsByIDAndUser(ctx context.Context, arg DatabaseServiceExistsByIDAndUserParams) (bool, error) {
row := q.db.QueryRowContext(ctx, databaseServiceExistsByIDAndUser, arg.ID, arg.UserID)
var exists bool
err := row.Scan(&exists)
return exists, err
}
const deleteDatabaseServiceByIDAndUser = `-- name: DeleteDatabaseServiceByIDAndUser :exec
DELETE FROM database_services
WHERE id = $1 AND user_id = $2
`
type DeleteDatabaseServiceByIDAndUserParams struct {
ID string `json:"id"`
UserID string `json:"user_id"`
}
func (q *Queries) DeleteDatabaseServiceByIDAndUser(ctx context.Context, arg DeleteDatabaseServiceByIDAndUserParams) error {
_, err := q.db.ExecContext(ctx, deleteDatabaseServiceByIDAndUser, arg.ID, arg.UserID)
return err
}
const getDatabaseBackupByIDAndDatabaseAndUser = `-- name: GetDatabaseBackupByIDAndDatabaseAndUser :one
SELECT b.id, b.database_id, b.size, b.status, b.backup_path, b.created_at, b.completed_at
FROM database_backups b
JOIN database_services s ON s.id = b.database_id
WHERE b.id = $1 AND b.database_id = $2 AND s.user_id = $3
`
type GetDatabaseBackupByIDAndDatabaseAndUserParams struct {
ID string `json:"id"`
DatabaseID string `json:"database_id"`
UserID string `json:"user_id"`
}
func (q *Queries) GetDatabaseBackupByIDAndDatabaseAndUser(ctx context.Context, arg GetDatabaseBackupByIDAndDatabaseAndUserParams) (DatabaseBackup, error) {
row := q.db.QueryRowContext(ctx, getDatabaseBackupByIDAndDatabaseAndUser, arg.ID, arg.DatabaseID, arg.UserID)
var i DatabaseBackup
err := row.Scan(
&i.ID,
&i.DatabaseID,
&i.Size,
&i.Status,
&i.BackupPath,
&i.CreatedAt,
&i.CompletedAt,
)
return i, err
}
const getDatabaseServiceByIDAndUser = `-- name: GetDatabaseServiceByIDAndUser :one
SELECT id, name, type, status, version, plan, region, connection_url, created_at, updated_at
FROM database_services
WHERE id = $1 AND user_id = $2
`
type GetDatabaseServiceByIDAndUserParams struct {
ID string `json:"id"`
UserID string `json:"user_id"`
}
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"`
}
func (q *Queries) GetDatabaseServiceByIDAndUser(ctx context.Context, arg GetDatabaseServiceByIDAndUserParams) (GetDatabaseServiceByIDAndUserRow, error) {
row := q.db.QueryRowContext(ctx, getDatabaseServiceByIDAndUser, arg.ID, arg.UserID)
var i GetDatabaseServiceByIDAndUserRow
err := row.Scan(
&i.ID,
&i.Name,
&i.Type,
&i.Status,
&i.Version,
&i.Plan,
&i.Region,
&i.ConnectionUrl,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listDatabaseBackupsByDatabaseAndUser = `-- name: ListDatabaseBackupsByDatabaseAndUser :many
SELECT b.id, b.database_id, b.size, b.status, b.backup_path, b.created_at, b.completed_at
FROM database_backups b
JOIN database_services s ON s.id = b.database_id
WHERE b.database_id = $1 AND s.user_id = $2
ORDER BY b.created_at DESC
LIMIT $3
`
type ListDatabaseBackupsByDatabaseAndUserParams struct {
DatabaseID string `json:"database_id"`
UserID string `json:"user_id"`
Limit int32 `json:"limit"`
}
func (q *Queries) ListDatabaseBackupsByDatabaseAndUser(ctx context.Context, arg ListDatabaseBackupsByDatabaseAndUserParams) ([]DatabaseBackup, error) {
rows, err := q.db.QueryContext(ctx, listDatabaseBackupsByDatabaseAndUser, arg.DatabaseID, arg.UserID, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []DatabaseBackup{}
for rows.Next() {
var i DatabaseBackup
if err := rows.Scan(
&i.ID,
&i.DatabaseID,
&i.Size,
&i.Status,
&i.BackupPath,
&i.CreatedAt,
&i.CompletedAt,
); 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 listDatabaseServicesByUser = `-- name: ListDatabaseServicesByUser :many
SELECT id, name, type, status, version, plan, region, connection_url, 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"`
}
func (q *Queries) ListDatabaseServicesByUser(ctx context.Context, userID string) ([]ListDatabaseServicesByUserRow, error) {
rows, err := q.db.QueryContext(ctx, listDatabaseServicesByUser, userID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListDatabaseServicesByUserRow{}
for rows.Next() {
var i ListDatabaseServicesByUserRow
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Type,
&i.Status,
&i.Version,
&i.Plan,
&i.Region,
&i.ConnectionUrl,
&i.CreatedAt,
&i.UpdatedAt,
); 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 setDatabaseBackupStatusByID = `-- name: SetDatabaseBackupStatusByID :exec
UPDATE database_backups
SET status = $1, size = $2, completed_at = $3
WHERE id = $4
`
type SetDatabaseBackupStatusByIDParams struct {
Status string `json:"status"`
Size string `json:"size"`
CompletedAt sql.NullTime `json:"completed_at"`
ID string `json:"id"`
}
func (q *Queries) SetDatabaseBackupStatusByID(ctx context.Context, arg SetDatabaseBackupStatusByIDParams) error {
_, err := q.db.ExecContext(ctx, setDatabaseBackupStatusByID,
arg.Status,
arg.Size,
arg.CompletedAt,
arg.ID,
)
return err
}
const setDatabaseServiceStatusAndConnectionByID = `-- name: SetDatabaseServiceStatusAndConnectionByID :exec
UPDATE database_services
SET status = $1, connection_url = $2, updated_at = $3
WHERE id = $4
`
type SetDatabaseServiceStatusAndConnectionByIDParams struct {
Status string `json:"status"`
ConnectionUrl sql.NullString `json:"connection_url"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID string `json:"id"`
}
func (q *Queries) SetDatabaseServiceStatusAndConnectionByID(ctx context.Context, arg SetDatabaseServiceStatusAndConnectionByIDParams) error {
_, err := q.db.ExecContext(ctx, setDatabaseServiceStatusAndConnectionByID,
arg.Status,
arg.ConnectionUrl,
arg.UpdatedAt,
arg.ID,
)
return err
}
const setDatabaseServiceStatusByID = `-- name: SetDatabaseServiceStatusByID :exec
UPDATE database_services
SET status = $1, updated_at = $2
WHERE id = $3
`
type SetDatabaseServiceStatusByIDParams struct {
Status string `json:"status"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID string `json:"id"`
}
func (q *Queries) SetDatabaseServiceStatusByID(ctx context.Context, arg SetDatabaseServiceStatusByIDParams) error {
_, err := q.db.ExecContext(ctx, setDatabaseServiceStatusByID, arg.Status, arg.UpdatedAt, arg.ID)
return err
}
const setDatabaseServiceStatusByIDAndUser = `-- name: SetDatabaseServiceStatusByIDAndUser :exec
UPDATE database_services
SET status = $1, updated_at = $2
WHERE id = $3 AND user_id = $4
`
type SetDatabaseServiceStatusByIDAndUserParams struct {
Status string `json:"status"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID string `json:"id"`
UserID string `json:"user_id"`
}
func (q *Queries) SetDatabaseServiceStatusByIDAndUser(ctx context.Context, arg SetDatabaseServiceStatusByIDAndUserParams) error {
_, err := q.db.ExecContext(ctx, setDatabaseServiceStatusByIDAndUser,
arg.Status,
arg.UpdatedAt,
arg.ID,
arg.UserID,
)
return err
}
const updateDatabaseServiceNameAndPlanByIDAndUser = `-- name: UpdateDatabaseServiceNameAndPlanByIDAndUser :exec
UPDATE database_services
SET name = $1, plan = $2, updated_at = $3
WHERE id = $4 AND user_id = $5
`
type UpdateDatabaseServiceNameAndPlanByIDAndUserParams struct {
Name string `json:"name"`
Plan string `json:"plan"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID string `json:"id"`
UserID string `json:"user_id"`
}
func (q *Queries) UpdateDatabaseServiceNameAndPlanByIDAndUser(ctx context.Context, arg UpdateDatabaseServiceNameAndPlanByIDAndUserParams) error {
_, err := q.db.ExecContext(ctx, updateDatabaseServiceNameAndPlanByIDAndUser,
arg.Name,
arg.Plan,
arg.UpdatedAt,
arg.ID,
arg.UserID,
)
return err
}
const updateDatabaseServiceNameByIDAndUser = `-- name: UpdateDatabaseServiceNameByIDAndUser :exec
UPDATE database_services
SET name = $1, updated_at = $2
WHERE id = $3 AND user_id = $4
`
type UpdateDatabaseServiceNameByIDAndUserParams struct {
Name string `json:"name"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID string `json:"id"`
UserID string `json:"user_id"`
}
func (q *Queries) UpdateDatabaseServiceNameByIDAndUser(ctx context.Context, arg UpdateDatabaseServiceNameByIDAndUserParams) error {
_, err := q.db.ExecContext(ctx, updateDatabaseServiceNameByIDAndUser,
arg.Name,
arg.UpdatedAt,
arg.ID,
arg.UserID,
)
return err
}
const updateDatabaseServicePlanByIDAndUser = `-- name: UpdateDatabaseServicePlanByIDAndUser :exec
UPDATE database_services
SET plan = $1, updated_at = $2
WHERE id = $3 AND user_id = $4
`
type UpdateDatabaseServicePlanByIDAndUserParams struct {
Plan string `json:"plan"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID string `json:"id"`
UserID string `json:"user_id"`
}
func (q *Queries) UpdateDatabaseServicePlanByIDAndUser(ctx context.Context, arg UpdateDatabaseServicePlanByIDAndUserParams) error {
_, err := q.db.ExecContext(ctx, updateDatabaseServicePlanByIDAndUser,
arg.Plan,
arg.UpdatedAt,
arg.ID,
arg.UserID,
)
return err
}
@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
package sqlcdb
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}
@@ -0,0 +1,108 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
package sqlcdb
import (
"database/sql"
"encoding/json"
"github.com/google/uuid"
"github.com/sqlc-dev/pqtype"
)
type DatabaseBackup struct {
ID string `json:"id"`
DatabaseID string `json:"database_id"`
Size string `json:"size"`
Status string `json:"status"`
BackupPath sql.NullString `json:"backup_path"`
CreatedAt sql.NullTime `json:"created_at"`
CompletedAt sql.NullTime `json:"completed_at"`
}
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"`
}
type Deployment struct {
ID uuid.UUID `json:"id"`
ServiceID uuid.UUID `json:"service_id"`
Status sql.NullString `json:"status"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type Environment struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
ProjectID uuid.UUID `json:"project_id"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type EnvironmentVariable struct {
ID uuid.UUID `json:"id"`
ServiceID uuid.UUID `json:"service_id"`
Key string `json:"key"`
Value string `json:"value"`
IsSecret sql.NullBool `json:"is_secret"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type Project struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Description sql.NullString `json:"description"`
OwnerID uuid.UUID `json:"owner_id"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type ProjectMember struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
UserID uuid.UUID `json:"user_id"`
Role string `json:"role"`
CreatedAt sql.NullTime `json:"created_at"`
}
type Service struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"`
Image sql.NullString `json:"image"`
Command sql.NullString `json:"command"`
Environment sql.NullString `json:"environment"`
Cpu sql.NullString `json:"cpu"`
Memory sql.NullString `json:"memory"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type ServiceTemplate struct {
ID string `json:"id"`
Name string `json:"name"`
Description sql.NullString `json:"description"`
Category string `json:"category"`
Logo sql.NullString `json:"logo"`
Config json.RawMessage `json:"config"`
Variables pqtype.NullRawMessage `json:"variables"`
IsOfficial sql.NullBool `json:"is_official"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
@@ -0,0 +1,283 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: projects.sql
package sqlcdb
import (
"context"
"database/sql"
"github.com/google/uuid"
)
const countProjectsByUser = `-- name: CountProjectsByUser :one
SELECT COUNT(*)::bigint AS total
FROM projects p
WHERE
(p.owner_id = $1 OR EXISTS (
SELECT 1
FROM project_members pm
WHERE pm.project_id = p.id AND pm.user_id = $1
))
AND (
$2::text IS NULL
OR p.name ILIKE ('%' || $2::text || '%')
OR COALESCE(p.description, '') ILIKE ('%' || $2::text || '%')
)
`
type CountProjectsByUserParams struct {
UserID uuid.UUID `json:"user_id"`
Search sql.NullString `json:"search"`
}
func (q *Queries) CountProjectsByUser(ctx context.Context, arg CountProjectsByUserParams) (int64, error) {
row := q.db.QueryRowContext(ctx, countProjectsByUser, arg.UserID, arg.Search)
var total int64
err := row.Scan(&total)
return total, err
}
const createProject = `-- name: CreateProject :one
INSERT INTO projects (name, description, owner_id)
VALUES ($1, $2, $3)
RETURNING id, name, description, owner_id, created_at, updated_at
`
type CreateProjectParams struct {
Name string `json:"name"`
Description sql.NullString `json:"description"`
OwnerID uuid.UUID `json:"owner_id"`
}
func (q *Queries) CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) {
row := q.db.QueryRowContext(ctx, createProject, arg.Name, arg.Description, arg.OwnerID)
var i Project
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.OwnerID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const deleteProjectByID = `-- name: DeleteProjectByID :execrows
DELETE FROM projects
WHERE id = $1
`
func (q *Queries) DeleteProjectByID(ctx context.Context, projectID uuid.UUID) (int64, error) {
result, err := q.db.ExecContext(ctx, deleteProjectByID, projectID)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const getProjectByIDForUser = `-- name: GetProjectByIDForUser :one
SELECT p.id, p.name, p.description, p.owner_id, p.created_at, p.updated_at
FROM projects p
WHERE p.id = $1
AND (
p.owner_id = $2
OR EXISTS (
SELECT 1
FROM project_members pm
WHERE pm.project_id = p.id AND pm.user_id = $2
)
)
`
type GetProjectByIDForUserParams struct {
ProjectID uuid.UUID `json:"project_id"`
UserID uuid.UUID `json:"user_id"`
}
func (q *Queries) GetProjectByIDForUser(ctx context.Context, arg GetProjectByIDForUserParams) (Project, error) {
row := q.db.QueryRowContext(ctx, getProjectByIDForUser, arg.ProjectID, arg.UserID)
var i Project
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.OwnerID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getProjectOwnerByID = `-- name: GetProjectOwnerByID :one
SELECT owner_id
FROM projects
WHERE id = $1
`
func (q *Queries) GetProjectOwnerByID(ctx context.Context, projectID uuid.UUID) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, getProjectOwnerByID, projectID)
var owner_id uuid.UUID
err := row.Scan(&owner_id)
return owner_id, err
}
const getProjectRoleForUser = `-- name: GetProjectRoleForUser :one
SELECT (CASE
WHEN p.owner_id = $1 THEN 'owner'
ELSE COALESCE(pm.role, '')
END)::text AS role
FROM projects p
LEFT JOIN project_members pm ON p.id = pm.project_id AND pm.user_id = $1
WHERE p.id = $2
`
type GetProjectRoleForUserParams struct {
UserID uuid.UUID `json:"user_id"`
ProjectID uuid.UUID `json:"project_id"`
}
func (q *Queries) GetProjectRoleForUser(ctx context.Context, arg GetProjectRoleForUserParams) (string, error) {
row := q.db.QueryRowContext(ctx, getProjectRoleForUser, arg.UserID, arg.ProjectID)
var role string
err := row.Scan(&role)
return role, err
}
const insertProjectEnvironment = `-- name: InsertProjectEnvironment :exec
INSERT INTO environments (name, project_id)
VALUES ($1, $2)
`
type InsertProjectEnvironmentParams struct {
Name string `json:"name"`
ProjectID uuid.UUID `json:"project_id"`
}
func (q *Queries) InsertProjectEnvironment(ctx context.Context, arg InsertProjectEnvironmentParams) error {
_, err := q.db.ExecContext(ctx, insertProjectEnvironment, arg.Name, arg.ProjectID)
return err
}
const listProjectsWithStatsByUser = `-- name: ListProjectsWithStatsByUser :many
SELECT
p.id,
p.name,
p.description,
p.owner_id,
p.created_at,
p.updated_at,
COUNT(DISTINCT s.id)::bigint AS service_count,
COUNT(DISTINCT d.id)::bigint AS deployment_count,
COUNT(DISTINCT CASE WHEN s.status = 'running' THEN s.id END)::bigint AS running_services,
(
SELECT d2.created_at
FROM deployments d2
JOIN services s2 ON s2.id = d2.service_id
WHERE s2.project_id = p.id
ORDER BY d2.created_at DESC
LIMIT 1
) AS last_deployment
FROM projects p
LEFT JOIN services s ON s.project_id = p.id
LEFT JOIN deployments d ON d.service_id = s.id
WHERE
(p.owner_id = $1 OR EXISTS (
SELECT 1
FROM project_members pm
WHERE pm.project_id = p.id AND pm.user_id = $1
))
AND (
$2::text IS NULL
OR p.name ILIKE ('%' || $2::text || '%')
OR COALESCE(p.description, '') ILIKE ('%' || $2::text || '%')
)
GROUP BY p.id, p.name, p.description, p.owner_id, p.created_at, p.updated_at
ORDER BY p.updated_at DESC
LIMIT $4 OFFSET $3
`
type ListProjectsWithStatsByUserParams struct {
UserID uuid.UUID `json:"user_id"`
Search sql.NullString `json:"search"`
OffsetCount int32 `json:"offset_count"`
LimitCount int32 `json:"limit_count"`
}
type ListProjectsWithStatsByUserRow struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Description sql.NullString `json:"description"`
OwnerID uuid.UUID `json:"owner_id"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
ServiceCount int64 `json:"service_count"`
DeploymentCount int64 `json:"deployment_count"`
RunningServices int64 `json:"running_services"`
LastDeployment sql.NullTime `json:"last_deployment"`
}
func (q *Queries) ListProjectsWithStatsByUser(ctx context.Context, arg ListProjectsWithStatsByUserParams) ([]ListProjectsWithStatsByUserRow, error) {
rows, err := q.db.QueryContext(ctx, listProjectsWithStatsByUser,
arg.UserID,
arg.Search,
arg.OffsetCount,
arg.LimitCount,
)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListProjectsWithStatsByUserRow{}
for rows.Next() {
var i ListProjectsWithStatsByUserRow
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.OwnerID,
&i.CreatedAt,
&i.UpdatedAt,
&i.ServiceCount,
&i.DeploymentCount,
&i.RunningServices,
&i.LastDeployment,
); 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 updateProjectByID = `-- name: UpdateProjectByID :execrows
UPDATE projects
SET
name = COALESCE($1, name),
description = COALESCE($2, description),
updated_at = NOW()
WHERE id = $3
`
type UpdateProjectByIDParams struct {
Name sql.NullString `json:"name"`
Description sql.NullString `json:"description"`
ProjectID uuid.UUID `json:"project_id"`
}
func (q *Queries) UpdateProjectByID(ctx context.Context, arg UpdateProjectByIDParams) (int64, error) {
result, err := q.db.ExecContext(ctx, updateProjectByID, arg.Name, arg.Description, arg.ProjectID)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
@@ -0,0 +1,48 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
package sqlcdb
import (
"context"
"github.com/google/uuid"
)
type Querier interface {
CountDatabaseServicesByUserAndName(ctx context.Context, arg CountDatabaseServicesByUserAndNameParams) (int64, error)
CountProjectsByUser(ctx context.Context, arg CountProjectsByUserParams) (int64, error)
CountServicesByProjectAndName(ctx context.Context, arg CountServicesByProjectAndNameParams) (int64, error)
CreateDatabaseBackup(ctx context.Context, arg CreateDatabaseBackupParams) error
CreateDatabaseService(ctx context.Context, arg CreateDatabaseServiceParams) error
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
CreateServiceFromTemplate(ctx context.Context, arg CreateServiceFromTemplateParams) error
DatabaseServiceExistsByIDAndUser(ctx context.Context, arg DatabaseServiceExistsByIDAndUserParams) (bool, error)
DeleteDatabaseServiceByIDAndUser(ctx context.Context, arg DeleteDatabaseServiceByIDAndUserParams) error
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) (int64, error)
GetDatabaseBackupByIDAndDatabaseAndUser(ctx context.Context, arg GetDatabaseBackupByIDAndDatabaseAndUserParams) (DatabaseBackup, error)
GetDatabaseServiceByIDAndUser(ctx context.Context, arg GetDatabaseServiceByIDAndUserParams) (GetDatabaseServiceByIDAndUserRow, error)
GetProjectByIDForUser(ctx context.Context, arg GetProjectByIDForUserParams) (Project, error)
GetProjectOwnerByID(ctx context.Context, projectID uuid.UUID) (uuid.UUID, error)
GetProjectOwnerID(ctx context.Context, id uuid.UUID) (uuid.UUID, error)
GetProjectRoleForUser(ctx context.Context, arg GetProjectRoleForUserParams) (string, error)
GetServiceTemplateByID(ctx context.Context, id string) (ServiceTemplate, error)
InsertProjectEnvironment(ctx context.Context, arg InsertProjectEnvironmentParams) error
ListDatabaseBackupsByDatabaseAndUser(ctx context.Context, arg ListDatabaseBackupsByDatabaseAndUserParams) ([]DatabaseBackup, error)
ListDatabaseServicesByUser(ctx context.Context, userID string) ([]ListDatabaseServicesByUserRow, error)
ListProjectsWithStatsByUser(ctx context.Context, arg ListProjectsWithStatsByUserParams) ([]ListProjectsWithStatsByUserRow, error)
ListServiceTemplates(ctx context.Context) ([]ServiceTemplate, error)
ListServiceTemplatesByCategory(ctx context.Context, category string) ([]ServiceTemplate, error)
SetDatabaseBackupStatusByID(ctx context.Context, arg SetDatabaseBackupStatusByIDParams) error
SetDatabaseServiceStatusAndConnectionByID(ctx context.Context, arg SetDatabaseServiceStatusAndConnectionByIDParams) error
SetDatabaseServiceStatusByID(ctx context.Context, arg SetDatabaseServiceStatusByIDParams) error
SetDatabaseServiceStatusByIDAndUser(ctx context.Context, arg SetDatabaseServiceStatusByIDAndUserParams) error
UpdateDatabaseServiceNameAndPlanByIDAndUser(ctx context.Context, arg UpdateDatabaseServiceNameAndPlanByIDAndUserParams) error
UpdateDatabaseServiceNameByIDAndUser(ctx context.Context, arg UpdateDatabaseServiceNameByIDAndUserParams) error
UpdateDatabaseServicePlanByIDAndUser(ctx context.Context, arg UpdateDatabaseServicePlanByIDAndUserParams) error
UpdateProjectByID(ctx context.Context, arg UpdateProjectByIDParams) (int64, error)
UpsertEnvironmentVariable(ctx context.Context, arg UpsertEnvironmentVariableParams) error
}
var _ Querier = (*Queries)(nil)
@@ -0,0 +1,219 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: templates.sql
package sqlcdb
import (
"context"
"database/sql"
"github.com/google/uuid"
)
const countServicesByProjectAndName = `-- name: CountServicesByProjectAndName :one
SELECT COUNT(*)
FROM services
WHERE project_id = $1 AND name = $2
`
type CountServicesByProjectAndNameParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
}
func (q *Queries) CountServicesByProjectAndName(ctx context.Context, arg CountServicesByProjectAndNameParams) (int64, error) {
row := q.db.QueryRowContext(ctx, countServicesByProjectAndName, arg.ProjectID, arg.Name)
var count int64
err := row.Scan(&count)
return count, err
}
const createServiceFromTemplate = `-- name: CreateServiceFromTemplate :exec
INSERT INTO services (id, project_id, name, type, status, image, command, environment, cpu, memory, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
`
type CreateServiceFromTemplateParams struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"`
Image sql.NullString `json:"image"`
Command sql.NullString `json:"command"`
Environment sql.NullString `json:"environment"`
Cpu sql.NullString `json:"cpu"`
Memory sql.NullString `json:"memory"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
func (q *Queries) CreateServiceFromTemplate(ctx context.Context, arg CreateServiceFromTemplateParams) error {
_, err := q.db.ExecContext(ctx, createServiceFromTemplate,
arg.ID,
arg.ProjectID,
arg.Name,
arg.Type,
arg.Status,
arg.Image,
arg.Command,
arg.Environment,
arg.Cpu,
arg.Memory,
arg.CreatedAt,
arg.UpdatedAt,
)
return err
}
const getProjectOwnerID = `-- name: GetProjectOwnerID :one
SELECT owner_id
FROM projects
WHERE id = $1
`
func (q *Queries) GetProjectOwnerID(ctx context.Context, id uuid.UUID) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, getProjectOwnerID, id)
var owner_id uuid.UUID
err := row.Scan(&owner_id)
return owner_id, err
}
const getServiceTemplateByID = `-- name: GetServiceTemplateByID :one
SELECT id, name, description, category, logo, config, variables, is_official, created_at, updated_at
FROM service_templates
WHERE id = $1
`
func (q *Queries) GetServiceTemplateByID(ctx context.Context, id string) (ServiceTemplate, error) {
row := q.db.QueryRowContext(ctx, getServiceTemplateByID, id)
var i ServiceTemplate
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Category,
&i.Logo,
&i.Config,
&i.Variables,
&i.IsOfficial,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listServiceTemplates = `-- name: ListServiceTemplates :many
SELECT id, name, description, category, logo, config, variables, is_official, created_at, updated_at
FROM service_templates
ORDER BY is_official DESC, name ASC
`
func (q *Queries) ListServiceTemplates(ctx context.Context) ([]ServiceTemplate, error) {
rows, err := q.db.QueryContext(ctx, listServiceTemplates)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ServiceTemplate{}
for rows.Next() {
var i ServiceTemplate
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Category,
&i.Logo,
&i.Config,
&i.Variables,
&i.IsOfficial,
&i.CreatedAt,
&i.UpdatedAt,
); 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 listServiceTemplatesByCategory = `-- name: ListServiceTemplatesByCategory :many
SELECT id, name, description, category, logo, config, variables, is_official, created_at, updated_at
FROM service_templates
WHERE category = $1
ORDER BY is_official DESC, name ASC
`
func (q *Queries) ListServiceTemplatesByCategory(ctx context.Context, category string) ([]ServiceTemplate, error) {
rows, err := q.db.QueryContext(ctx, listServiceTemplatesByCategory, category)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ServiceTemplate{}
for rows.Next() {
var i ServiceTemplate
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Category,
&i.Logo,
&i.Config,
&i.Variables,
&i.IsOfficial,
&i.CreatedAt,
&i.UpdatedAt,
); 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 upsertEnvironmentVariable = `-- name: UpsertEnvironmentVariable :exec
INSERT INTO environment_variables (id, service_id, key, value, is_secret, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (service_id, key) DO UPDATE
SET value = EXCLUDED.value,
is_secret = EXCLUDED.is_secret,
updated_at = EXCLUDED.updated_at
`
type UpsertEnvironmentVariableParams struct {
ID uuid.UUID `json:"id"`
ServiceID uuid.UUID `json:"service_id"`
Key string `json:"key"`
Value string `json:"value"`
IsSecret sql.NullBool `json:"is_secret"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
func (q *Queries) UpsertEnvironmentVariable(ctx context.Context, arg UpsertEnvironmentVariableParams) error {
_, err := q.db.ExecContext(ctx, upsertEnvironmentVariable,
arg.ID,
arg.ServiceID,
arg.Key,
arg.Value,
arg.IsSecret,
arg.CreatedAt,
arg.UpdatedAt,
)
return err
}