mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25a8443548 | ||
|
|
302c595380 |
No files matched your search
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@@ -210,7 +211,7 @@ func basicAuthUsernames(encoded string) []string {
|
||||
// serviceWriteAccess mirrors the service update handler's access check.
|
||||
func serviceWriteAccess(c *gin.Context, db *database.DB, serviceID uuid.UUID) bool {
|
||||
userID, _ := c.Get("user_id")
|
||||
uid, _ := userID.(uuid.UUID)
|
||||
uid, _ := uuid.Parse(fmt.Sprint(userID))
|
||||
ok, err := sqlcdb.New(db.DB).ServiceWriteAccess(context.Background(), sqlcdb.ServiceWriteAccessParams{
|
||||
ID: serviceID,
|
||||
OwnerID: uid,
|
||||
|
||||
@@ -143,3 +143,83 @@ func TestServiceDomainsLiveInvariants(t *testing.T) {
|
||||
t.Fatalf("legacy after delete = %q, want %q", legacy, d1.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
// Live clone test: cloneServiceRow must copy config, domains, and variables
|
||||
// verbatim, start stopped, and land in the target project/environment.
|
||||
func TestCloneServiceRowLive(t *testing.T) {
|
||||
dsn := testDSNString(t)
|
||||
db, err := database.NewConnection(dsn)
|
||||
if err != nil {
|
||||
t.Fatalf("connect: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
q := sqlcdb.New(db.DB)
|
||||
|
||||
userID := uuid.New()
|
||||
projectID := uuid.New()
|
||||
envID := uuid.New()
|
||||
serviceID := uuid.New()
|
||||
suffix := strings.ReplaceAll(serviceID.String()[:8], "-", "")
|
||||
must := func(err error) {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
t.Fatalf("setup: %v", err)
|
||||
}
|
||||
}
|
||||
_, err = db.Exec(`INSERT INTO users (id, email, password_hash, name) VALUES ($1, $2, 'x', 'test')`,
|
||||
userID, "clone-"+suffix+"@test.local")
|
||||
must(err)
|
||||
_, err = db.Exec(`INSERT INTO projects (id, name, owner_id, is_approved) VALUES ($1, $2, $3, true)`,
|
||||
projectID, "clone-"+suffix, userID)
|
||||
must(err)
|
||||
_, err = db.Exec(`INSERT INTO environments (id, name, project_id) VALUES ($1, 'production', $2)`,
|
||||
envID, projectID)
|
||||
must(err)
|
||||
_, err = db.Exec(`INSERT INTO services (id, name, project_id, environment_id, service_type, source_type,
|
||||
image_name, port, volumes, traefik_labels, maintenance_mode)
|
||||
VALUES ($1, $2, $3, $4, 'web', 'image', 'traefik/whoami:latest', 80,
|
||||
'[{"mount":"/data"}]'::jsonb,
|
||||
'{"middlewares.rl.ratelimit.average":"100"}'::jsonb, true)`,
|
||||
serviceID, "clonesrc-"+suffix, projectID, envID)
|
||||
must(err)
|
||||
defer func() { _, _ = db.Exec(`DELETE FROM users WHERE id = $1`, userID) }()
|
||||
|
||||
if _, err := q.CreateServiceDomain(ctx, sqlcdb.CreateServiceDomainParams{
|
||||
ServiceID: serviceID, Domain: "src-" + suffix + ".example.com", IsDefault: true}); err != nil {
|
||||
t.Fatalf("seed domain: %v", err)
|
||||
}
|
||||
if _, err := db.Exec(`INSERT INTO environment_variables (id, service_id, key, value, is_secret)
|
||||
VALUES ($1, $2, 'FOO', 'bar', false)`, uuid.New(), serviceID); err != nil {
|
||||
t.Fatalf("seed var: %v", err)
|
||||
}
|
||||
|
||||
cloneID, err := cloneServiceRow(db, serviceID, projectID, "clone-"+suffix, "")
|
||||
if err != nil {
|
||||
t.Fatalf("clone: %v", err)
|
||||
}
|
||||
|
||||
var status, imageName, env string
|
||||
var maintenance bool
|
||||
var volumes, labels []byte
|
||||
if err := db.QueryRow(`SELECT status, image_name, COALESCE(environment,''), maintenance_mode,
|
||||
volumes, traefik_labels FROM services WHERE id = $1`, cloneID).
|
||||
Scan(&status, &imageName, &env, &maintenance, &volumes, &labels); err != nil {
|
||||
t.Fatalf("read clone: %v", err)
|
||||
}
|
||||
if status != "stopped" || imageName != "traefik/whoami:latest" || env != "production" {
|
||||
t.Fatalf("clone mismatch: %v %v %v", status, imageName, env)
|
||||
}
|
||||
if !maintenance || string(volumes) == "[]" || string(labels) == "{}" {
|
||||
t.Fatalf("clone dropped config: maint=%v vol=%s labels=%s", maintenance, volumes, labels)
|
||||
}
|
||||
doms := loadServiceDomains(db, cloneID)
|
||||
if len(doms) != 1 || doms[0].Domain != "src-"+suffix+".example.com" || !doms[0].IsDefault {
|
||||
t.Fatalf("domains not cloned: %+v", doms)
|
||||
}
|
||||
var varVal string
|
||||
if err := db.QueryRow(`SELECT value FROM environment_variables WHERE service_id = $1 AND key = 'FOO'`,
|
||||
cloneID).Scan(&varVal); err != nil || varVal != "bar" {
|
||||
t.Fatalf("vars not cloned: %v %q", err, varVal)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"containr/internal/database"
|
||||
"containr/internal/database/sqlcdb"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
@@ -53,11 +56,8 @@ func handleCloneService(c *gin.Context) {
|
||||
if name == "" {
|
||||
name = source.Name + "-copy"
|
||||
}
|
||||
var count int
|
||||
_ = db.(*database.DB).QueryRow(
|
||||
`SELECT COUNT(*) FROM services WHERE project_id = $1 AND name = $2`,
|
||||
targetProjectID, name,
|
||||
).Scan(&count)
|
||||
count, _ := sqlcdb.New(db.(*database.DB).DB).CountServicesByName(
|
||||
c.Request.Context(), sqlcdb.CountServicesByNameParams{ProjectID: targetProjectID, Name: name})
|
||||
if count > 0 {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "Service name already exists in target project", "code": "CONFLICT"})
|
||||
return
|
||||
@@ -84,8 +84,11 @@ func handleCloneService(c *gin.Context) {
|
||||
// inherits the source's. Used by the clone endpoint and preview
|
||||
// environments, which additionally override git_branch and domains.
|
||||
func cloneServiceRow(db *database.DB, sourceID uuid.UUID, targetProjectID uuid.UUID, name, environment string) (uuid.UUID, error) {
|
||||
var sourceEnv string
|
||||
if err := db.QueryRow(`SELECT COALESCE(environment, '') FROM services WHERE id = $1`, sourceID).Scan(&sourceEnv); err != nil {
|
||||
ctx := context.Background()
|
||||
q := sqlcdb.New(db.DB)
|
||||
|
||||
sourceEnv, err := q.GetServiceEnvironment(ctx, sourceID)
|
||||
if err != nil {
|
||||
return uuid.Nil, err
|
||||
}
|
||||
if environment == "" {
|
||||
@@ -102,46 +105,31 @@ func cloneServiceRow(db *database.DB, sourceID uuid.UUID, targetProjectID uuid.U
|
||||
maintenance, basicAuth := serviceAccess(db, sourceID)
|
||||
newID := uuid.New()
|
||||
now := time.Now()
|
||||
// string() so pq sends text — []byte would go as bytea and fail ::jsonb.
|
||||
volumesJSON := string(loadServiceVolumesJSON(db, sourceID))
|
||||
|
||||
_, err = db.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,
|
||||
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, $6::jsonb, $7, $8,
|
||||
builder, cpu_reserve, memory_reserve, static_build_cmd, static_dir,
|
||||
$9, $9
|
||||
FROM services WHERE id = $10`,
|
||||
newID, targetProjectID, name, environmentID, environment, volumesJSON,
|
||||
maintenance, basicAuth, now, sourceID,
|
||||
)
|
||||
err = q.CloneServiceRow(ctx, sqlcdb.CloneServiceRowParams{
|
||||
ID: newID,
|
||||
ProjectID: targetProjectID,
|
||||
Name: name,
|
||||
EnvironmentID: environmentID,
|
||||
Environment: sql.NullString{String: environment, Valid: true},
|
||||
MaintenanceMode: maintenance,
|
||||
BasicAuthUsers: basicAuth,
|
||||
CreatedAt: sql.NullTime{Time: now, Valid: true},
|
||||
ID_2: sourceID,
|
||||
})
|
||||
if err != nil {
|
||||
return uuid.Nil, err
|
||||
}
|
||||
|
||||
if _, err := db.Exec(
|
||||
`INSERT INTO service_domains (service_id, domain, is_default, cert_type)
|
||||
SELECT $1, domain, is_default, cert_type FROM service_domains WHERE service_id = $2`,
|
||||
newID, sourceID,
|
||||
); err != nil {
|
||||
_, _ = db.Exec(`DELETE FROM services WHERE id = $1`, newID)
|
||||
if err := q.CloneServiceDomains(ctx, sqlcdb.CloneServiceDomainsParams{
|
||||
ServiceID: newID, ServiceID_2: sourceID}); err != nil {
|
||||
_ = q.DeleteServiceByID(ctx, newID)
|
||||
return uuid.Nil, err
|
||||
}
|
||||
// Variable values are copied as stored — ciphertext stays ciphertext.
|
||||
if _, err := db.Exec(
|
||||
`INSERT INTO environment_variables (id, service_id, key, value, is_secret)
|
||||
SELECT gen_random_uuid(), $1, key, value, is_secret FROM environment_variables WHERE service_id = $2`,
|
||||
newID, sourceID,
|
||||
); err != nil {
|
||||
_, _ = db.Exec(`DELETE FROM services WHERE id = $1`, newID)
|
||||
if err := q.CloneServiceVariables(ctx, sqlcdb.CloneServiceVariablesParams{
|
||||
ServiceID: newID, ServiceID_2: sourceID}); err != nil {
|
||||
_ = q.DeleteServiceByID(ctx, newID)
|
||||
return uuid.Nil, err
|
||||
}
|
||||
return newID, nil
|
||||
@@ -187,11 +175,8 @@ func handleMoveService(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var count int
|
||||
_ = db.(*database.DB).QueryRow(
|
||||
`SELECT COUNT(*) FROM services WHERE project_id = $1 AND name = $2`,
|
||||
req.ProjectID, service.Name,
|
||||
).Scan(&count)
|
||||
count, _ := sqlcdb.New(db.(*database.DB).DB).CountServicesByName(
|
||||
c.Request.Context(), sqlcdb.CountServicesByNameParams{ProjectID: req.ProjectID, Name: service.Name})
|
||||
if count > 0 {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "A service with this name already exists in the target project", "code": "CONFLICT"})
|
||||
return
|
||||
@@ -207,15 +192,18 @@ func handleMoveService(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := db.(*database.DB).Exec(
|
||||
`UPDATE services SET project_id = $1, environment_id = $2, updated_at = $3 WHERE id = $4`,
|
||||
req.ProjectID, environmentID, time.Now(), serviceID,
|
||||
)
|
||||
n, err := sqlcdb.New(db.(*database.DB).DB).MoveServiceProject(
|
||||
c.Request.Context(), sqlcdb.MoveServiceProjectParams{
|
||||
ProjectID: req.ProjectID,
|
||||
EnvironmentID: environmentID,
|
||||
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
ID: serviceID,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to move service"})
|
||||
return
|
||||
}
|
||||
if n, _ := res.RowsAffected(); n == 0 {
|
||||
if n == 0 {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Service not found", "code": "NOT_FOUND"})
|
||||
return
|
||||
}
|
||||
@@ -229,16 +217,10 @@ func ownsProject(db *database.DB, projectID uuid.UUID, userID string, isAdmin bo
|
||||
if isAdmin {
|
||||
return true
|
||||
}
|
||||
var owner string
|
||||
err := db.QueryRow(`SELECT owner_id FROM projects WHERE id = $1`, projectID).Scan(&owner)
|
||||
return err == nil && owner == userID
|
||||
uid, err := uuid.Parse(userID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// loadServiceVolumesJSON returns the raw stored mounts for a verbatim copy.
|
||||
func loadServiceVolumesJSON(db *database.DB, serviceID uuid.UUID) []byte {
|
||||
var raw []byte
|
||||
if err := db.QueryRow(`SELECT volumes FROM services WHERE id = $1`, serviceID).Scan(&raw); err != nil || len(raw) == 0 {
|
||||
return []byte("[]")
|
||||
}
|
||||
return raw
|
||||
owner, err := sqlcdb.New(db.DB).GetProjectOwner(context.Background(), projectID)
|
||||
return err == nil && owner == uid
|
||||
}
|
||||
@@ -18,12 +18,16 @@ type Querier interface {
|
||||
AggregateNodeMetrics(ctx context.Context, arg AggregateNodeMetricsParams) ([]AggregateNodeMetricsRow, error)
|
||||
ClearServiceDomainDefault(ctx context.Context, serviceID uuid.UUID) error
|
||||
ClearServiceNodePins(ctx context.Context, nodeID sql.NullString) (int64, error)
|
||||
CloneServiceDomains(ctx context.Context, arg CloneServiceDomainsParams) error
|
||||
CloneServiceRow(ctx context.Context, arg CloneServiceRowParams) error
|
||||
CloneServiceVariables(ctx context.Context, arg CloneServiceVariablesParams) error
|
||||
CompleteCommand(ctx context.Context, arg CompleteCommandParams) (AgentCommand, error)
|
||||
CountDatabaseServicesByUserAndName(ctx context.Context, arg CountDatabaseServicesByUserAndNameParams) (int64, error)
|
||||
CountDatabasesUsingBackupTarget(ctx context.Context, targetID sql.NullString) (int64, error)
|
||||
CountEnvironmentServices(ctx context.Context, environmentID uuid.UUID) (int64, error)
|
||||
CountProjectsByUser(ctx context.Context, arg CountProjectsByUserParams) (int64, error)
|
||||
CountServiceDomains(ctx context.Context, serviceID uuid.UUID) (int64, error)
|
||||
CountServicesByName(ctx context.Context, arg CountServicesByNameParams) (int64, error)
|
||||
CountServicesByProjectAndName(ctx context.Context, arg CountServicesByProjectAndNameParams) (int64, error)
|
||||
CountUnreadNotificationsByUser(ctx context.Context, userID uuid.UUID) (int64, error)
|
||||
CreateAgent(ctx context.Context, arg CreateAgentParams) (NodeAgent, error)
|
||||
@@ -54,6 +58,7 @@ type Querier interface {
|
||||
DeleteOutboundWebhookByIDAndUser(ctx context.Context, arg DeleteOutboundWebhookByIDAndUserParams) error
|
||||
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) (int64, error)
|
||||
DeleteProjectEnvironment(ctx context.Context, arg DeleteProjectEnvironmentParams) error
|
||||
DeleteServiceByID(ctx context.Context, id uuid.UUID) error
|
||||
DeleteServiceContainersOnAgent(ctx context.Context, arg DeleteServiceContainersOnAgentParams) error
|
||||
DeleteServiceDomain(ctx context.Context, arg DeleteServiceDomainParams) (int64, error)
|
||||
DeleteUserInvite(ctx context.Context, id uuid.UUID) error
|
||||
@@ -76,12 +81,14 @@ type Querier interface {
|
||||
GetOutboundWebhookByIDAndUser(ctx context.Context, arg GetOutboundWebhookByIDAndUserParams) (OutboundWebhook, error)
|
||||
GetProjectByIDForUser(ctx context.Context, arg GetProjectByIDForUserParams) (Project, error)
|
||||
GetProjectEnvironmentByName(ctx context.Context, arg GetProjectEnvironmentByNameParams) (Environment, error)
|
||||
GetProjectOwner(ctx context.Context, id uuid.UUID) (uuid.UUID, 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)
|
||||
GetServiceAccess(ctx context.Context, id uuid.UUID) (GetServiceAccessRow, error)
|
||||
GetServiceDomainDefault(ctx context.Context, serviceID uuid.UUID) (string, error)
|
||||
GetServiceDomainFirst(ctx context.Context, serviceID uuid.UUID) (string, error)
|
||||
GetServiceEnvironment(ctx context.Context, id uuid.UUID) (string, error)
|
||||
GetServicePlacementBrief(ctx context.Context, id uuid.UUID) (GetServicePlacementBriefRow, error)
|
||||
GetServiceTemplateByID(ctx context.Context, id string) (ServiceTemplate, error)
|
||||
GetUserInviteByTokenHash(ctx context.Context, tokenHash string) (UserInvite, error)
|
||||
@@ -135,6 +142,7 @@ type Querier interface {
|
||||
MarkAllNotificationsReadByUser(ctx context.Context, arg MarkAllNotificationsReadByUserParams) error
|
||||
MarkNotificationReadByIDAndUser(ctx context.Context, arg MarkNotificationReadByIDAndUserParams) error
|
||||
MarkUserInviteUsed(ctx context.Context, arg MarkUserInviteUsedParams) error
|
||||
MoveServiceProject(ctx context.Context, arg MoveServiceProjectParams) (int64, error)
|
||||
// Resource-aware: lowest memory utilisation first, then cpu, then container
|
||||
// count. Agents without telemetry sort last (NULL → worst score).
|
||||
PickLeastLoadedAgent(ctx context.Context) (string, error)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
-- name: CountServicesByName :one
|
||||
SELECT COUNT(*) FROM services WHERE project_id = $1 AND name = $2;
|
||||
|
||||
-- name: GetServiceEnvironment :one
|
||||
SELECT COALESCE(environment, '') FROM services WHERE id = $1;
|
||||
|
||||
-- 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;
|
||||
|
||||
-- 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;
|
||||
|
||||
-- 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;
|
||||
|
||||
-- name: DeleteServiceByID :exec
|
||||
DELETE FROM services WHERE id = $1;
|
||||
|
||||
-- name: MoveServiceProject :execrows
|
||||
UPDATE services SET project_id = $1, environment_id = $2, updated_at = $3 WHERE id = $4;
|
||||
|
||||
-- name: GetProjectOwner :one
|
||||
SELECT owner_id FROM projects WHERE id = $1;
|
||||
Reference in new issue
Block a user