Add admin users, BYOC teams, hosts schema, and Redis for host registration
Introduce three migrations: admin permissions (is_admin + permissions table), BYOC team tracking, and multi-host support (hosts, host_tokens, host_tags). Add Redis to dev infra and wire up client in control plane for ephemeral host registration tokens. Add go-redis dependency.
This commit is contained in:
@ -1,6 +1,9 @@
|
||||
# Database
|
||||
DATABASE_URL=postgres://wrenn:wrenn@localhost:5432/wrenn?sslmode=disable
|
||||
|
||||
# Redis
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
|
||||
# Control Plane
|
||||
CP_LISTEN_ADDR=:8000
|
||||
CP_HOST_AGENT_ADDR=localhost:50051
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"git.omukk.dev/wrenn/sandbox/internal/api"
|
||||
"git.omukk.dev/wrenn/sandbox/internal/auth/oauth"
|
||||
@ -50,6 +51,23 @@ func main() {
|
||||
|
||||
queries := db.New(pool)
|
||||
|
||||
// Redis client.
|
||||
redisOpts, err := redis.ParseURL(cfg.RedisURL)
|
||||
if err != nil {
|
||||
slog.Error("failed to parse REDIS_URL", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
rdb := redis.NewClient(redisOpts)
|
||||
defer rdb.Close()
|
||||
|
||||
if err := rdb.Ping(ctx).Err(); err != nil {
|
||||
slog.Error("failed to ping redis", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
slog.Info("connected to redis")
|
||||
|
||||
_ = rdb // TODO: pass to services that need it (host registration)
|
||||
|
||||
// Connect RPC client for the host agent.
|
||||
agentHTTP := &http.Client{Timeout: 10 * time.Minute}
|
||||
agentClient := hostagentv1connect.NewHostAgentServiceClient(
|
||||
|
||||
21
db/migrations/20260316203135_admin_users.sql
Normal file
21
db/migrations/20260316203135_admin_users.sql
Normal file
@ -0,0 +1,21 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE users
|
||||
ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
CREATE TABLE admin_permissions (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
permission TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (user_id, permission)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_admin_permissions_user ON admin_permissions(user_id);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE admin_permissions;
|
||||
|
||||
ALTER TABLE users
|
||||
DROP COLUMN is_admin;
|
||||
9
db/migrations/20260316203138_byoc_teams.sql
Normal file
9
db/migrations/20260316203138_byoc_teams.sql
Normal file
@ -0,0 +1,9 @@
|
||||
-- +goose Up
|
||||
|
||||
ALTER TABLE teams
|
||||
ADD COLUMN is_byoc BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
-- +goose Down
|
||||
|
||||
ALTER TABLE teams
|
||||
DROP COLUMN is_byoc;
|
||||
47
db/migrations/20260316203142_hosts.sql
Normal file
47
db/migrations/20260316203142_hosts.sql
Normal file
@ -0,0 +1,47 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE hosts (
|
||||
id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL DEFAULT 'regular', -- 'regular' or 'byoc'
|
||||
team_id TEXT REFERENCES teams(id) ON DELETE SET NULL,
|
||||
provider TEXT,
|
||||
availability_zone TEXT,
|
||||
arch TEXT,
|
||||
cpu_cores INTEGER,
|
||||
memory_mb INTEGER,
|
||||
disk_gb INTEGER,
|
||||
address TEXT, -- ip:port of host agent
|
||||
status TEXT NOT NULL DEFAULT 'pending', -- 'pending', 'online', 'offline', 'draining'
|
||||
last_heartbeat_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_by TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE host_tokens (
|
||||
id TEXT PRIMARY KEY,
|
||||
host_id TEXT NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
created_by TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
used_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE TABLE host_tags (
|
||||
host_id TEXT NOT NULL REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY (host_id, tag)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_hosts_type ON hosts(type);
|
||||
CREATE INDEX idx_hosts_team ON hosts(team_id);
|
||||
CREATE INDEX idx_hosts_status ON hosts(status);
|
||||
CREATE INDEX idx_host_tokens_host ON host_tokens(host_id);
|
||||
CREATE INDEX idx_host_tags_tag ON host_tags(tag);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE host_tags;
|
||||
DROP TABLE host_tokens;
|
||||
DROP TABLE hosts;
|
||||
@ -0,0 +1,66 @@
|
||||
-- name: InsertHost :one
|
||||
INSERT INTO hosts (id, type, team_id, provider, availability_zone, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetHost :one
|
||||
SELECT * FROM hosts WHERE id = $1;
|
||||
|
||||
-- name: ListHosts :many
|
||||
SELECT * FROM hosts ORDER BY created_at DESC;
|
||||
|
||||
-- name: ListHostsByType :many
|
||||
SELECT * FROM hosts WHERE type = $1 ORDER BY created_at DESC;
|
||||
|
||||
-- name: ListHostsByTeam :many
|
||||
SELECT * FROM hosts WHERE team_id = $1 ORDER BY created_at DESC;
|
||||
|
||||
-- name: ListHostsByStatus :many
|
||||
SELECT * FROM hosts WHERE status = $1 ORDER BY created_at DESC;
|
||||
|
||||
-- name: RegisterHost :exec
|
||||
UPDATE hosts
|
||||
SET arch = $2,
|
||||
cpu_cores = $3,
|
||||
memory_mb = $4,
|
||||
disk_gb = $5,
|
||||
address = $6,
|
||||
status = 'online',
|
||||
last_heartbeat_at = NOW(),
|
||||
updated_at = NOW()
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: UpdateHostStatus :exec
|
||||
UPDATE hosts SET status = $2, updated_at = NOW() WHERE id = $1;
|
||||
|
||||
-- name: UpdateHostHeartbeat :exec
|
||||
UPDATE hosts SET last_heartbeat_at = NOW(), updated_at = NOW() WHERE id = $1;
|
||||
|
||||
-- name: DeleteHost :exec
|
||||
DELETE FROM hosts WHERE id = $1;
|
||||
|
||||
-- name: AddHostTag :exec
|
||||
INSERT INTO host_tags (host_id, tag) VALUES ($1, $2) ON CONFLICT DO NOTHING;
|
||||
|
||||
-- name: RemoveHostTag :exec
|
||||
DELETE FROM host_tags WHERE host_id = $1 AND tag = $2;
|
||||
|
||||
-- name: GetHostTags :many
|
||||
SELECT tag FROM host_tags WHERE host_id = $1 ORDER BY tag;
|
||||
|
||||
-- name: ListHostsByTag :many
|
||||
SELECT h.* FROM hosts h
|
||||
JOIN host_tags ht ON ht.host_id = h.id
|
||||
WHERE ht.tag = $1
|
||||
ORDER BY h.created_at DESC;
|
||||
|
||||
-- name: InsertHostToken :one
|
||||
INSERT INTO host_tokens (id, host_id, created_by, expires_at)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *;
|
||||
|
||||
-- name: MarkHostTokenUsed :exec
|
||||
UPDATE host_tokens SET used_at = NOW() WHERE id = $1;
|
||||
|
||||
-- name: GetHostTokensByHost :many
|
||||
SELECT * FROM host_tokens WHERE host_id = $1 ORDER BY created_at DESC;
|
||||
|
||||
@ -15,3 +15,9 @@ SELECT t.* FROM teams t
|
||||
JOIN users_teams ut ON ut.team_id = t.id
|
||||
WHERE ut.user_id = $1 AND ut.is_default = TRUE
|
||||
LIMIT 1;
|
||||
|
||||
-- name: SetTeamBYOC :exec
|
||||
UPDATE teams SET is_byoc = $2 WHERE id = $1;
|
||||
|
||||
-- name: GetBYOCTeams :many
|
||||
SELECT * FROM teams WHERE is_byoc = TRUE ORDER BY created_at;
|
||||
|
||||
@ -13,3 +13,24 @@ SELECT * FROM users WHERE id = $1;
|
||||
INSERT INTO users (id, email)
|
||||
VALUES ($1, $2)
|
||||
RETURNING *;
|
||||
|
||||
-- name: SetUserAdmin :exec
|
||||
UPDATE users SET is_admin = $2, updated_at = NOW() WHERE id = $1;
|
||||
|
||||
-- name: GetAdminUsers :many
|
||||
SELECT * FROM users WHERE is_admin = TRUE ORDER BY created_at;
|
||||
|
||||
-- name: InsertAdminPermission :exec
|
||||
INSERT INTO admin_permissions (id, user_id, permission)
|
||||
VALUES ($1, $2, $3);
|
||||
|
||||
-- name: DeleteAdminPermission :exec
|
||||
DELETE FROM admin_permissions WHERE user_id = $1 AND permission = $2;
|
||||
|
||||
-- name: GetAdminPermissions :many
|
||||
SELECT * FROM admin_permissions WHERE user_id = $1 ORDER BY permission;
|
||||
|
||||
-- name: HasAdminPermission :one
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM admin_permissions WHERE user_id = $1 AND permission = $2
|
||||
) AS has_permission;
|
||||
|
||||
@ -10,6 +10,11 @@ services:
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
ports:
|
||||
|
||||
4
go.mod
4
go.mod
@ -10,6 +10,7 @@ require (
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jackc/pgx/v5 v5.8.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/redis/go-redis/v9 v9.18.0
|
||||
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f
|
||||
golang.org/x/crypto v0.49.0
|
||||
@ -19,9 +20,12 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
)
|
||||
|
||||
16
go.sum
16
go.sum
@ -1,8 +1,16 @@
|
||||
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
|
||||
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
@ -23,8 +31,12 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
|
||||
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -35,6 +47,10 @@ github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:tw
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
// Config holds the control plane configuration.
|
||||
type Config struct {
|
||||
DatabaseURL string
|
||||
RedisURL string
|
||||
ListenAddr string
|
||||
HostAgentAddr string
|
||||
JWTSecret string
|
||||
@ -28,6 +29,7 @@ func Load() Config {
|
||||
|
||||
cfg := Config{
|
||||
DatabaseURL: envOrDefault("DATABASE_URL", "postgres://wrenn:wrenn@localhost:5432/wrenn?sslmode=disable"),
|
||||
RedisURL: envOrDefault("REDIS_URL", "redis://localhost:6379/0"),
|
||||
ListenAddr: envOrDefault("CP_LISTEN_ADDR", ":8080"),
|
||||
HostAgentAddr: envOrDefault("CP_HOST_AGENT_ADDR", "http://localhost:50051"),
|
||||
JWTSecret: os.Getenv("JWT_SECRET"),
|
||||
|
||||
484
internal/db/hosts.sql.go
Normal file
484
internal/db/hosts.sql.go
Normal file
@ -0,0 +1,484 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: hosts.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const addHostTag = `-- name: AddHostTag :exec
|
||||
INSERT INTO host_tags (host_id, tag) VALUES ($1, $2) ON CONFLICT DO NOTHING
|
||||
`
|
||||
|
||||
type AddHostTagParams struct {
|
||||
HostID string `json:"host_id"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
func (q *Queries) AddHostTag(ctx context.Context, arg AddHostTagParams) error {
|
||||
_, err := q.db.Exec(ctx, addHostTag, arg.HostID, arg.Tag)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteHost = `-- name: DeleteHost :exec
|
||||
DELETE FROM hosts WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteHost(ctx context.Context, id string) error {
|
||||
_, err := q.db.Exec(ctx, deleteHost, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const getHost = `-- name: GetHost :one
|
||||
SELECT id, type, team_id, provider, availability_zone, arch, cpu_cores, memory_mb, disk_gb, address, status, last_heartbeat_at, metadata, created_by, created_at, updated_at FROM hosts WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetHost(ctx context.Context, id string) (Host, error) {
|
||||
row := q.db.QueryRow(ctx, getHost, id)
|
||||
var i Host
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Type,
|
||||
&i.TeamID,
|
||||
&i.Provider,
|
||||
&i.AvailabilityZone,
|
||||
&i.Arch,
|
||||
&i.CpuCores,
|
||||
&i.MemoryMb,
|
||||
&i.DiskGb,
|
||||
&i.Address,
|
||||
&i.Status,
|
||||
&i.LastHeartbeatAt,
|
||||
&i.Metadata,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getHostTags = `-- name: GetHostTags :many
|
||||
SELECT tag FROM host_tags WHERE host_id = $1 ORDER BY tag
|
||||
`
|
||||
|
||||
func (q *Queries) GetHostTags(ctx context.Context, hostID string) ([]string, error) {
|
||||
rows, err := q.db.Query(ctx, getHostTags, hostID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []string
|
||||
for rows.Next() {
|
||||
var tag string
|
||||
if err := rows.Scan(&tag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, tag)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getHostTokensByHost = `-- name: GetHostTokensByHost :many
|
||||
SELECT id, host_id, created_by, created_at, expires_at, used_at FROM host_tokens WHERE host_id = $1 ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) GetHostTokensByHost(ctx context.Context, hostID string) ([]HostToken, error) {
|
||||
rows, err := q.db.Query(ctx, getHostTokensByHost, hostID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []HostToken
|
||||
for rows.Next() {
|
||||
var i HostToken
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.HostID,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.UsedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertHost = `-- name: InsertHost :one
|
||||
INSERT INTO hosts (id, type, team_id, provider, availability_zone, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, type, team_id, provider, availability_zone, arch, cpu_cores, memory_mb, disk_gb, address, status, last_heartbeat_at, metadata, created_by, created_at, updated_at
|
||||
`
|
||||
|
||||
type InsertHostParams struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
TeamID pgtype.Text `json:"team_id"`
|
||||
Provider pgtype.Text `json:"provider"`
|
||||
AvailabilityZone pgtype.Text `json:"availability_zone"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) InsertHost(ctx context.Context, arg InsertHostParams) (Host, error) {
|
||||
row := q.db.QueryRow(ctx, insertHost,
|
||||
arg.ID,
|
||||
arg.Type,
|
||||
arg.TeamID,
|
||||
arg.Provider,
|
||||
arg.AvailabilityZone,
|
||||
arg.CreatedBy,
|
||||
)
|
||||
var i Host
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Type,
|
||||
&i.TeamID,
|
||||
&i.Provider,
|
||||
&i.AvailabilityZone,
|
||||
&i.Arch,
|
||||
&i.CpuCores,
|
||||
&i.MemoryMb,
|
||||
&i.DiskGb,
|
||||
&i.Address,
|
||||
&i.Status,
|
||||
&i.LastHeartbeatAt,
|
||||
&i.Metadata,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertHostToken = `-- name: InsertHostToken :one
|
||||
INSERT INTO host_tokens (id, host_id, created_by, expires_at)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, host_id, created_by, created_at, expires_at, used_at
|
||||
`
|
||||
|
||||
type InsertHostTokenParams struct {
|
||||
ID string `json:"id"`
|
||||
HostID string `json:"host_id"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) InsertHostToken(ctx context.Context, arg InsertHostTokenParams) (HostToken, error) {
|
||||
row := q.db.QueryRow(ctx, insertHostToken,
|
||||
arg.ID,
|
||||
arg.HostID,
|
||||
arg.CreatedBy,
|
||||
arg.ExpiresAt,
|
||||
)
|
||||
var i HostToken
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.HostID,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.UsedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listHosts = `-- name: ListHosts :many
|
||||
SELECT id, type, team_id, provider, availability_zone, arch, cpu_cores, memory_mb, disk_gb, address, status, last_heartbeat_at, metadata, created_by, created_at, updated_at FROM hosts ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListHosts(ctx context.Context) ([]Host, error) {
|
||||
rows, err := q.db.Query(ctx, listHosts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Host
|
||||
for rows.Next() {
|
||||
var i Host
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Type,
|
||||
&i.TeamID,
|
||||
&i.Provider,
|
||||
&i.AvailabilityZone,
|
||||
&i.Arch,
|
||||
&i.CpuCores,
|
||||
&i.MemoryMb,
|
||||
&i.DiskGb,
|
||||
&i.Address,
|
||||
&i.Status,
|
||||
&i.LastHeartbeatAt,
|
||||
&i.Metadata,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listHostsByStatus = `-- name: ListHostsByStatus :many
|
||||
SELECT id, type, team_id, provider, availability_zone, arch, cpu_cores, memory_mb, disk_gb, address, status, last_heartbeat_at, metadata, created_by, created_at, updated_at FROM hosts WHERE status = $1 ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListHostsByStatus(ctx context.Context, status string) ([]Host, error) {
|
||||
rows, err := q.db.Query(ctx, listHostsByStatus, status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Host
|
||||
for rows.Next() {
|
||||
var i Host
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Type,
|
||||
&i.TeamID,
|
||||
&i.Provider,
|
||||
&i.AvailabilityZone,
|
||||
&i.Arch,
|
||||
&i.CpuCores,
|
||||
&i.MemoryMb,
|
||||
&i.DiskGb,
|
||||
&i.Address,
|
||||
&i.Status,
|
||||
&i.LastHeartbeatAt,
|
||||
&i.Metadata,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listHostsByTag = `-- name: ListHostsByTag :many
|
||||
SELECT h.id, h.type, h.team_id, h.provider, h.availability_zone, h.arch, h.cpu_cores, h.memory_mb, h.disk_gb, h.address, h.status, h.last_heartbeat_at, h.metadata, h.created_by, h.created_at, h.updated_at FROM hosts h
|
||||
JOIN host_tags ht ON ht.host_id = h.id
|
||||
WHERE ht.tag = $1
|
||||
ORDER BY h.created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListHostsByTag(ctx context.Context, tag string) ([]Host, error) {
|
||||
rows, err := q.db.Query(ctx, listHostsByTag, tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Host
|
||||
for rows.Next() {
|
||||
var i Host
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Type,
|
||||
&i.TeamID,
|
||||
&i.Provider,
|
||||
&i.AvailabilityZone,
|
||||
&i.Arch,
|
||||
&i.CpuCores,
|
||||
&i.MemoryMb,
|
||||
&i.DiskGb,
|
||||
&i.Address,
|
||||
&i.Status,
|
||||
&i.LastHeartbeatAt,
|
||||
&i.Metadata,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listHostsByTeam = `-- name: ListHostsByTeam :many
|
||||
SELECT id, type, team_id, provider, availability_zone, arch, cpu_cores, memory_mb, disk_gb, address, status, last_heartbeat_at, metadata, created_by, created_at, updated_at FROM hosts WHERE team_id = $1 ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListHostsByTeam(ctx context.Context, teamID pgtype.Text) ([]Host, error) {
|
||||
rows, err := q.db.Query(ctx, listHostsByTeam, teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Host
|
||||
for rows.Next() {
|
||||
var i Host
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Type,
|
||||
&i.TeamID,
|
||||
&i.Provider,
|
||||
&i.AvailabilityZone,
|
||||
&i.Arch,
|
||||
&i.CpuCores,
|
||||
&i.MemoryMb,
|
||||
&i.DiskGb,
|
||||
&i.Address,
|
||||
&i.Status,
|
||||
&i.LastHeartbeatAt,
|
||||
&i.Metadata,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listHostsByType = `-- name: ListHostsByType :many
|
||||
SELECT id, type, team_id, provider, availability_zone, arch, cpu_cores, memory_mb, disk_gb, address, status, last_heartbeat_at, metadata, created_by, created_at, updated_at FROM hosts WHERE type = $1 ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListHostsByType(ctx context.Context, type_ string) ([]Host, error) {
|
||||
rows, err := q.db.Query(ctx, listHostsByType, type_)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Host
|
||||
for rows.Next() {
|
||||
var i Host
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Type,
|
||||
&i.TeamID,
|
||||
&i.Provider,
|
||||
&i.AvailabilityZone,
|
||||
&i.Arch,
|
||||
&i.CpuCores,
|
||||
&i.MemoryMb,
|
||||
&i.DiskGb,
|
||||
&i.Address,
|
||||
&i.Status,
|
||||
&i.LastHeartbeatAt,
|
||||
&i.Metadata,
|
||||
&i.CreatedBy,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const markHostTokenUsed = `-- name: MarkHostTokenUsed :exec
|
||||
UPDATE host_tokens SET used_at = NOW() WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) MarkHostTokenUsed(ctx context.Context, id string) error {
|
||||
_, err := q.db.Exec(ctx, markHostTokenUsed, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const registerHost = `-- name: RegisterHost :exec
|
||||
UPDATE hosts
|
||||
SET arch = $2,
|
||||
cpu_cores = $3,
|
||||
memory_mb = $4,
|
||||
disk_gb = $5,
|
||||
address = $6,
|
||||
status = 'online',
|
||||
last_heartbeat_at = NOW(),
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
type RegisterHostParams struct {
|
||||
ID string `json:"id"`
|
||||
Arch pgtype.Text `json:"arch"`
|
||||
CpuCores pgtype.Int4 `json:"cpu_cores"`
|
||||
MemoryMb pgtype.Int4 `json:"memory_mb"`
|
||||
DiskGb pgtype.Int4 `json:"disk_gb"`
|
||||
Address pgtype.Text `json:"address"`
|
||||
}
|
||||
|
||||
func (q *Queries) RegisterHost(ctx context.Context, arg RegisterHostParams) error {
|
||||
_, err := q.db.Exec(ctx, registerHost,
|
||||
arg.ID,
|
||||
arg.Arch,
|
||||
arg.CpuCores,
|
||||
arg.MemoryMb,
|
||||
arg.DiskGb,
|
||||
arg.Address,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const removeHostTag = `-- name: RemoveHostTag :exec
|
||||
DELETE FROM host_tags WHERE host_id = $1 AND tag = $2
|
||||
`
|
||||
|
||||
type RemoveHostTagParams struct {
|
||||
HostID string `json:"host_id"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
func (q *Queries) RemoveHostTag(ctx context.Context, arg RemoveHostTagParams) error {
|
||||
_, err := q.db.Exec(ctx, removeHostTag, arg.HostID, arg.Tag)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateHostHeartbeat = `-- name: UpdateHostHeartbeat :exec
|
||||
UPDATE hosts SET last_heartbeat_at = NOW(), updated_at = NOW() WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) UpdateHostHeartbeat(ctx context.Context, id string) error {
|
||||
_, err := q.db.Exec(ctx, updateHostHeartbeat, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateHostStatus = `-- name: UpdateHostStatus :exec
|
||||
UPDATE hosts SET status = $2, updated_at = NOW() WHERE id = $1
|
||||
`
|
||||
|
||||
type UpdateHostStatusParams struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateHostStatus(ctx context.Context, arg UpdateHostStatusParams) error {
|
||||
_, err := q.db.Exec(ctx, updateHostStatus, arg.ID, arg.Status)
|
||||
return err
|
||||
}
|
||||
@ -8,6 +8,46 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type AdminPermission struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Permission string `json:"permission"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type Host struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
TeamID pgtype.Text `json:"team_id"`
|
||||
Provider pgtype.Text `json:"provider"`
|
||||
AvailabilityZone pgtype.Text `json:"availability_zone"`
|
||||
Arch pgtype.Text `json:"arch"`
|
||||
CpuCores pgtype.Int4 `json:"cpu_cores"`
|
||||
MemoryMb pgtype.Int4 `json:"memory_mb"`
|
||||
DiskGb pgtype.Int4 `json:"disk_gb"`
|
||||
Address pgtype.Text `json:"address"`
|
||||
Status string `json:"status"`
|
||||
LastHeartbeatAt pgtype.Timestamptz `json:"last_heartbeat_at"`
|
||||
Metadata []byte `json:"metadata"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type HostTag struct {
|
||||
HostID string `json:"host_id"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type HostToken struct {
|
||||
ID string `json:"id"`
|
||||
HostID string `json:"host_id"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
|
||||
UsedAt pgtype.Timestamptz `json:"used_at"`
|
||||
}
|
||||
|
||||
type OauthProvider struct {
|
||||
Provider string `json:"provider"`
|
||||
ProviderID string `json:"provider_id"`
|
||||
@ -37,6 +77,7 @@ type Team struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
IsByoc bool `json:"is_byoc"`
|
||||
}
|
||||
|
||||
type TeamApiKey struct {
|
||||
@ -66,6 +107,7 @@ type User struct {
|
||||
PasswordHash pgtype.Text `json:"password_hash"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
|
||||
type UsersTeam struct {
|
||||
|
||||
@ -9,8 +9,37 @@ import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const getBYOCTeams = `-- name: GetBYOCTeams :many
|
||||
SELECT id, name, created_at, is_byoc FROM teams WHERE is_byoc = TRUE ORDER BY created_at
|
||||
`
|
||||
|
||||
func (q *Queries) GetBYOCTeams(ctx context.Context) ([]Team, error) {
|
||||
rows, err := q.db.Query(ctx, getBYOCTeams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Team
|
||||
for rows.Next() {
|
||||
var i Team
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.CreatedAt,
|
||||
&i.IsByoc,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getDefaultTeamForUser = `-- name: GetDefaultTeamForUser :one
|
||||
SELECT t.id, t.name, t.created_at FROM teams t
|
||||
SELECT t.id, t.name, t.created_at, t.is_byoc FROM teams t
|
||||
JOIN users_teams ut ON ut.team_id = t.id
|
||||
WHERE ut.user_id = $1 AND ut.is_default = TRUE
|
||||
LIMIT 1
|
||||
@ -19,25 +48,35 @@ LIMIT 1
|
||||
func (q *Queries) GetDefaultTeamForUser(ctx context.Context, userID string) (Team, error) {
|
||||
row := q.db.QueryRow(ctx, getDefaultTeamForUser, userID)
|
||||
var i Team
|
||||
err := row.Scan(&i.ID, &i.Name, &i.CreatedAt)
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.CreatedAt,
|
||||
&i.IsByoc,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTeam = `-- name: GetTeam :one
|
||||
SELECT id, name, created_at FROM teams WHERE id = $1
|
||||
SELECT id, name, created_at, is_byoc FROM teams WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTeam(ctx context.Context, id string) (Team, error) {
|
||||
row := q.db.QueryRow(ctx, getTeam, id)
|
||||
var i Team
|
||||
err := row.Scan(&i.ID, &i.Name, &i.CreatedAt)
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.CreatedAt,
|
||||
&i.IsByoc,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertTeam = `-- name: InsertTeam :one
|
||||
INSERT INTO teams (id, name)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, name, created_at
|
||||
RETURNING id, name, created_at, is_byoc
|
||||
`
|
||||
|
||||
type InsertTeamParams struct {
|
||||
@ -48,7 +87,12 @@ type InsertTeamParams struct {
|
||||
func (q *Queries) InsertTeam(ctx context.Context, arg InsertTeamParams) (Team, error) {
|
||||
row := q.db.QueryRow(ctx, insertTeam, arg.ID, arg.Name)
|
||||
var i Team
|
||||
err := row.Scan(&i.ID, &i.Name, &i.CreatedAt)
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.CreatedAt,
|
||||
&i.IsByoc,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@ -73,3 +117,17 @@ func (q *Queries) InsertTeamMember(ctx context.Context, arg InsertTeamMemberPara
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const setTeamBYOC = `-- name: SetTeamBYOC :exec
|
||||
UPDATE teams SET is_byoc = $2 WHERE id = $1
|
||||
`
|
||||
|
||||
type SetTeamBYOCParams struct {
|
||||
ID string `json:"id"`
|
||||
IsByoc bool `json:"is_byoc"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetTeamBYOC(ctx context.Context, arg SetTeamBYOCParams) error {
|
||||
_, err := q.db.Exec(ctx, setTeamBYOC, arg.ID, arg.IsByoc)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -11,8 +11,82 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const deleteAdminPermission = `-- name: DeleteAdminPermission :exec
|
||||
DELETE FROM admin_permissions WHERE user_id = $1 AND permission = $2
|
||||
`
|
||||
|
||||
type DeleteAdminPermissionParams struct {
|
||||
UserID string `json:"user_id"`
|
||||
Permission string `json:"permission"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteAdminPermission(ctx context.Context, arg DeleteAdminPermissionParams) error {
|
||||
_, err := q.db.Exec(ctx, deleteAdminPermission, arg.UserID, arg.Permission)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAdminPermissions = `-- name: GetAdminPermissions :many
|
||||
SELECT id, user_id, permission, created_at FROM admin_permissions WHERE user_id = $1 ORDER BY permission
|
||||
`
|
||||
|
||||
func (q *Queries) GetAdminPermissions(ctx context.Context, userID string) ([]AdminPermission, error) {
|
||||
rows, err := q.db.Query(ctx, getAdminPermissions, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []AdminPermission
|
||||
for rows.Next() {
|
||||
var i AdminPermission
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Permission,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getAdminUsers = `-- name: GetAdminUsers :many
|
||||
SELECT id, email, password_hash, created_at, updated_at, is_admin FROM users WHERE is_admin = TRUE ORDER BY created_at
|
||||
`
|
||||
|
||||
func (q *Queries) GetAdminUsers(ctx context.Context) ([]User, error) {
|
||||
rows, err := q.db.Query(ctx, getAdminUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []User
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.IsAdmin,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT id, email, password_hash, created_at, updated_at FROM users WHERE email = $1
|
||||
SELECT id, email, password_hash, created_at, updated_at, is_admin FROM users WHERE email = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
|
||||
@ -24,12 +98,13 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error
|
||||
&i.PasswordHash,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByID = `-- name: GetUserByID :one
|
||||
SELECT id, email, password_hash, created_at, updated_at FROM users WHERE id = $1
|
||||
SELECT id, email, password_hash, created_at, updated_at, is_admin FROM users WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, id string) (User, error) {
|
||||
@ -41,14 +116,49 @@ func (q *Queries) GetUserByID(ctx context.Context, id string) (User, error) {
|
||||
&i.PasswordHash,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const hasAdminPermission = `-- name: HasAdminPermission :one
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM admin_permissions WHERE user_id = $1 AND permission = $2
|
||||
) AS has_permission
|
||||
`
|
||||
|
||||
type HasAdminPermissionParams struct {
|
||||
UserID string `json:"user_id"`
|
||||
Permission string `json:"permission"`
|
||||
}
|
||||
|
||||
func (q *Queries) HasAdminPermission(ctx context.Context, arg HasAdminPermissionParams) (bool, error) {
|
||||
row := q.db.QueryRow(ctx, hasAdminPermission, arg.UserID, arg.Permission)
|
||||
var has_permission bool
|
||||
err := row.Scan(&has_permission)
|
||||
return has_permission, err
|
||||
}
|
||||
|
||||
const insertAdminPermission = `-- name: InsertAdminPermission :exec
|
||||
INSERT INTO admin_permissions (id, user_id, permission)
|
||||
VALUES ($1, $2, $3)
|
||||
`
|
||||
|
||||
type InsertAdminPermissionParams struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Permission string `json:"permission"`
|
||||
}
|
||||
|
||||
func (q *Queries) InsertAdminPermission(ctx context.Context, arg InsertAdminPermissionParams) error {
|
||||
_, err := q.db.Exec(ctx, insertAdminPermission, arg.ID, arg.UserID, arg.Permission)
|
||||
return err
|
||||
}
|
||||
|
||||
const insertUser = `-- name: InsertUser :one
|
||||
INSERT INTO users (id, email, password_hash)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id, email, password_hash, created_at, updated_at
|
||||
RETURNING id, email, password_hash, created_at, updated_at, is_admin
|
||||
`
|
||||
|
||||
type InsertUserParams struct {
|
||||
@ -66,6 +176,7 @@ func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (User, e
|
||||
&i.PasswordHash,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@ -73,7 +184,7 @@ func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (User, e
|
||||
const insertUserOAuth = `-- name: InsertUserOAuth :one
|
||||
INSERT INTO users (id, email)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, email, password_hash, created_at, updated_at
|
||||
RETURNING id, email, password_hash, created_at, updated_at, is_admin
|
||||
`
|
||||
|
||||
type InsertUserOAuthParams struct {
|
||||
@ -90,6 +201,21 @@ func (q *Queries) InsertUserOAuth(ctx context.Context, arg InsertUserOAuthParams
|
||||
&i.PasswordHash,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const setUserAdmin = `-- name: SetUserAdmin :exec
|
||||
UPDATE users SET is_admin = $2, updated_at = NOW() WHERE id = $1
|
||||
`
|
||||
|
||||
type SetUserAdminParams struct {
|
||||
ID string `json:"id"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetUserAdmin(ctx context.Context, arg SetUserAdminParams) error {
|
||||
_, err := q.db.Exec(ctx, setUserAdmin, arg.ID, arg.IsAdmin)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user