refactor(db): port service clone/move ops to sqlc (#76)

Tranche 2: cloneServiceRow's INSERT...SELECT (volumes and traefik_labels
now read straight from the source row — traefik_labels was silently
dropped before, and the jsonb param round-trip goes away), domain and
variable copies, name-conflict counts, move UPDATE, project owner check.
Also fixes serviceWriteAccess: user_id is a context string, not a
uuid.UUID — the tranche-1 cast silently failed owner checks for
non-admins. Live tests cover the domain invariants and a full clone
round-trip (config, domains, variables).

Generated with [Devin](https://devin.ai)

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomáš DvořákandDevin authored and GitHub committed 2026-10-10 03:25:07 +02:00
1 parent 302c595380
commit 687555fc78
6 files changed
+328 -61

No files matched your search

@@ -0,0 +1,158 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: service_ops.sql
package sqlcdb
import (
"context"
"database/sql"
"github.com/google/uuid"
)
const cloneServiceDomains = `-- name: CloneServiceDomains :exec
INSERT INTO service_domains (service_id, domain, is_default, cert_type)
SELECT $1, domain, is_default, cert_type FROM service_domains sd WHERE sd.service_id = $2
`
type CloneServiceDomainsParams struct {
ServiceID uuid.UUID `json:"service_id"`
ServiceID_2 uuid.UUID `json:"service_id_2"`
}
func (q *Queries) CloneServiceDomains(ctx context.Context, arg CloneServiceDomainsParams) error {
_, err := q.db.ExecContext(ctx, cloneServiceDomains, arg.ServiceID, arg.ServiceID_2)
return err
}
const cloneServiceRow = `-- name: CloneServiceRow :exec
INSERT INTO services
(id, project_id, name, environment_id, service_type, source_type, source_url, image_name,
build_command, start_command, type, status, image, command, environment,
git_repo, git_branch, build_path, cpu, memory, replicas, port, domain,
healthcheck_path, restart_policy, volumes, maintenance_mode, basic_auth_users,
builder, cpu_reserve, memory_reserve, static_build_cmd, static_dir,
traefik_labels, created_at, updated_at)
SELECT $1, $2, $3, $4, service_type, source_type, source_url, image_name,
build_command, start_command, type, 'stopped', image, command, $5,
git_repo, git_branch, build_path, cpu, memory, replicas, port, domain,
healthcheck_path, restart_policy, volumes, $6, $7,
builder, cpu_reserve, memory_reserve, static_build_cmd, static_dir,
traefik_labels, $8, $8
FROM services s WHERE s.id = $9
`
type CloneServiceRowParams struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
EnvironmentID uuid.UUID `json:"environment_id"`
Environment sql.NullString `json:"environment"`
MaintenanceMode bool `json:"maintenance_mode"`
BasicAuthUsers string `json:"basic_auth_users"`
CreatedAt sql.NullTime `json:"created_at"`
ID_2 uuid.UUID `json:"id_2"`
}
func (q *Queries) CloneServiceRow(ctx context.Context, arg CloneServiceRowParams) error {
_, err := q.db.ExecContext(ctx, cloneServiceRow,
arg.ID,
arg.ProjectID,
arg.Name,
arg.EnvironmentID,
arg.Environment,
arg.MaintenanceMode,
arg.BasicAuthUsers,
arg.CreatedAt,
arg.ID_2,
)
return err
}
const cloneServiceVariables = `-- name: CloneServiceVariables :exec
INSERT INTO environment_variables (id, service_id, key, value, is_secret)
SELECT gen_random_uuid(), $1, key, value, is_secret FROM environment_variables ev WHERE ev.service_id = $2
`
type CloneServiceVariablesParams struct {
ServiceID uuid.UUID `json:"service_id"`
ServiceID_2 uuid.UUID `json:"service_id_2"`
}
func (q *Queries) CloneServiceVariables(ctx context.Context, arg CloneServiceVariablesParams) error {
_, err := q.db.ExecContext(ctx, cloneServiceVariables, arg.ServiceID, arg.ServiceID_2)
return err
}
const countServicesByName = `-- name: CountServicesByName :one
SELECT COUNT(*) FROM services WHERE project_id = $1 AND name = $2
`
type CountServicesByNameParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
}
func (q *Queries) CountServicesByName(ctx context.Context, arg CountServicesByNameParams) (int64, error) {
row := q.db.QueryRowContext(ctx, countServicesByName, arg.ProjectID, arg.Name)
var count int64
err := row.Scan(&count)
return count, err
}
const deleteServiceByID = `-- name: DeleteServiceByID :exec
DELETE FROM services WHERE id = $1
`
func (q *Queries) DeleteServiceByID(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteServiceByID, id)
return err
}
const getProjectOwner = `-- name: GetProjectOwner :one
SELECT owner_id FROM projects WHERE id = $1
`
func (q *Queries) GetProjectOwner(ctx context.Context, id uuid.UUID) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, getProjectOwner, id)
var owner_id uuid.UUID
err := row.Scan(&owner_id)
return owner_id, err
}
const getServiceEnvironment = `-- name: GetServiceEnvironment :one
SELECT COALESCE(environment, '') FROM services WHERE id = $1
`
func (q *Queries) GetServiceEnvironment(ctx context.Context, id uuid.UUID) (string, error) {
row := q.db.QueryRowContext(ctx, getServiceEnvironment, id)
var environment string
err := row.Scan(&environment)
return environment, err
}
const moveServiceProject = `-- name: MoveServiceProject :execrows
UPDATE services SET project_id = $1, environment_id = $2, updated_at = $3 WHERE id = $4
`
type MoveServiceProjectParams struct {
ProjectID uuid.UUID `json:"project_id"`
EnvironmentID uuid.UUID `json:"environment_id"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID uuid.UUID `json:"id"`
}
func (q *Queries) MoveServiceProject(ctx context.Context, arg MoveServiceProjectParams) (int64, error) {
result, err := q.db.ExecContext(ctx, moveServiceProject,
arg.ProjectID,
arg.EnvironmentID,
arg.UpdatedAt,
arg.ID,
)
if err != nil {
return 0, err
}
return result.RowsAffected()
}