a7df9ffc6c
- store: add tables and CRUD for sandboxes (with services), templates (with services, clone-into-sandbox), environments (named key/value sets), and routes (per-sandbox <service>_url overrides). - api: split into one file per resource. handleSandboxes/handleSandboxByID covers CRUD + 'clone from template' + 'deploy one service in a sandbox' (which merges the sandbox's env into the request, picks the port, and dispatches the deploy frame to the right node). handleTemplates/handleTemplateByID, handleEnvironments/handleEnvironmentByID, handlePushRoutes cover the rest. The control plane's repo->node resolution still lives in resolveNode (api-gateway -> gateway, everything else -> micro).
114 lines
2.8 KiB
Go
114 lines
2.8 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"time"
|
|
)
|
|
|
|
// Environment is a named set of env vars that can be attached to a
|
|
// deploy, a sandbox service, or a sandbox's gateway.
|
|
type Environment struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Values map[string]string `json:"values"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
UpdatedAt int64 `json:"updatedAt"`
|
|
}
|
|
|
|
// CreateEnvironment persists a new env. Caller assigns id and timestamps.
|
|
func (s *Store) CreateEnvironment(e Environment) error {
|
|
now := time.Now().UnixMilli()
|
|
if e.CreatedAt == 0 {
|
|
e.CreatedAt = now
|
|
}
|
|
e.UpdatedAt = now
|
|
json, err := marshalJSON(e.Values)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = s.db.Exec(
|
|
`INSERT INTO environments(id, name, values_json, created_at, updated_at) VALUES(?,?,?,?,?)`,
|
|
e.ID, e.Name, json, e.CreatedAt, e.UpdatedAt)
|
|
return err
|
|
}
|
|
|
|
// UpdateEnvironment replaces name + values. ErrNotFound if missing.
|
|
func (s *Store) UpdateEnvironment(e Environment) error {
|
|
now := time.Now().UnixMilli()
|
|
e.UpdatedAt = now
|
|
json, err := marshalJSON(e.Values)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res, err := s.db.Exec(
|
|
`UPDATE environments SET name=?, values_json=?, updated_at=? WHERE id=?`,
|
|
e.Name, json, e.UpdatedAt, e.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n, _ := res.RowsAffected()
|
|
if n == 0 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetEnvironment returns one env. ErrNotFound if missing.
|
|
func (s *Store) GetEnvironment(id string) (*Environment, error) {
|
|
row := s.db.QueryRow(
|
|
`SELECT id, name, values_json, created_at, updated_at FROM environments WHERE id=?`, id)
|
|
var e Environment
|
|
var raw string
|
|
err := row.Scan(&e.ID, &e.Name, &raw, &e.CreatedAt, &e.UpdatedAt)
|
|
if err == sql.ErrNoRows {
|
|
return nil, ErrNotFound
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v, err := unmarshalJSON(raw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.Values = v
|
|
return &e, nil
|
|
}
|
|
|
|
// ListEnvironments returns all envs, newest first.
|
|
func (s *Store) ListEnvironments() ([]Environment, error) {
|
|
rows, err := s.db.Query(
|
|
`SELECT id, name, values_json, created_at, updated_at FROM environments ORDER BY created_at DESC`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []Environment
|
|
for rows.Next() {
|
|
var e Environment
|
|
var raw string
|
|
if err := rows.Scan(&e.ID, &e.Name, &raw, &e.CreatedAt, &e.UpdatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
v, err := unmarshalJSON(raw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.Values = v
|
|
out = append(out, e)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// DeleteEnvironment removes one env. ErrNotFound if missing.
|
|
func (s *Store) DeleteEnvironment(id string) error {
|
|
res, err := s.db.Exec(`DELETE FROM environments WHERE id=?`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n, _ := res.RowsAffected()
|
|
if n == 0 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|