mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-06-04 04:22:57 +00:00
small fix, don't worry about it
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user