Files
cgalo5758 12f1d3fc00 Fix the four Slice 3 walk findings and page every operator list
Archives openspec change slice3-walk-fixes and syncs its five delta
specs (fedwiki-sites, entitlements, operator-panel-navigation,
operator-list-scale, ui-quality-gate).

- FedWiki site usage is read from active site rows in both quota
  readers; the reservation counter converges on the rows: raise-only
  after farm sync and inside the create quota check, exact at boot.
  The understated production counters repair on the first boot.
- The People tile caption excludes the reserved system person through
  the same query parameter the directory uses.
- The operator Domains live-claims list is a governed list: pages of
  50, true total, search over root name and organization, a
  pending/active facet.
- New lint rule table-without-list-controls refuses an unpaged
  page-body table unless it carries a list-scale exempt marker with a
  reason; six curated or detail tables carry one. Its first run caught
  the operator FedWiki sites list, which is now governed the same way.
- Entitlement-set rule copy: "Per unit", "Multiplied by the quantity
  purchased or granted."
2026-09-12 01:16:17 -05:00

827 lines
25 KiB
Go

// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: sites.sql
package fedwiki
import (
"context"
"database/sql"
"time"
)
const clearForceReducedByDomain = `-- name: ClearForceReducedByDomain :exec
UPDATE fedwiki.sites
SET force_reduced_at = NULL, updated_at = NOW()
WHERE domain = $1
`
func (q *Queries) ClearForceReducedByDomain(ctx context.Context, domain string) error {
_, err := q.db.ExecContext(ctx, clearForceReducedByDomain, domain)
return err
}
const countActiveSitesByWorkspace = `-- name: CountActiveSitesByWorkspace :one
SELECT COUNT(*) AS count FROM fedwiki.sites
WHERE workspace_id = $1 AND status = 'active'
`
func (q *Queries) CountActiveSitesByWorkspace(ctx context.Context, workspaceID string) (int64, error) {
row := q.db.QueryRowContext(ctx, countActiveSitesByWorkspace, workspaceID)
var count int64
err := row.Scan(&count)
return count, err
}
const countSitesByWorkspace = `-- name: CountSitesByWorkspace :one
SELECT COUNT(*) AS count FROM fedwiki.sites
WHERE workspace_id = $1
`
func (q *Queries) CountSitesByWorkspace(ctx context.Context, workspaceID string) (int64, error) {
row := q.db.QueryRowContext(ctx, countSitesByWorkspace, workspaceID)
var count int64
err := row.Scan(&count)
return count, err
}
const createSite = `-- name: CreateSite :one
INSERT INTO fedwiki.sites (site_id, workspace_id, domain)
VALUES ($1, $2, $3)
RETURNING site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at
`
type CreateSiteParams struct {
SiteID string `json:"site_id"`
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
}
// site_id is supplied by the caller, not defaulted: the creation saga
// pre-generates it so the registry placement can reserve the name
// (resource_ref = site_id) BEFORE the farm call, and this insert then binds
// the same id (domains-registry D8).
func (q *Queries) CreateSite(ctx context.Context, arg CreateSiteParams) (Site, error) {
row := q.db.QueryRowContext(ctx, createSite, arg.SiteID, arg.WorkspaceID, arg.Domain)
var i Site
err := row.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
)
return i, err
}
const deleteSite = `-- name: DeleteSite :exec
DELETE FROM fedwiki.sites
WHERE site_id = $1
`
func (q *Queries) DeleteSite(ctx context.Context, siteID string) error {
_, err := q.db.ExecContext(ctx, deleteSite, siteID)
return err
}
const deleteSiteByDomain = `-- name: DeleteSiteByDomain :exec
DELETE FROM fedwiki.sites
WHERE domain = $1
`
func (q *Queries) DeleteSiteByDomain(ctx context.Context, domain string) error {
_, err := q.db.ExecContext(ctx, deleteSiteByDomain, domain)
return err
}
const getSiteByDomain = `-- name: GetSiteByDomain :one
SELECT s.site_id, s.workspace_id, s.domain, s.created_at, s.updated_at, s.status, s.storage_bytes, s.last_modified_at, s.force_reduced_at, s.archived_at, (EXISTS (
SELECT 1 FROM domains.placements p
JOIN domains.claims c ON c.claim_id = p.claim_id
WHERE p.provider = 'fedwiki'
AND p.resource_ref = s.site_id::text
AND c.kind = 'external'
))::boolean AS is_custom_domain
FROM fedwiki.sites AS s
WHERE s.domain = $1
`
type GetSiteByDomainRow struct {
SiteID string `json:"site_id"`
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Status string `json:"status"`
StorageBytes sql.NullInt64 `json:"storage_bytes"`
LastModifiedAt sql.NullTime `json:"last_modified_at"`
ForceReducedAt sql.NullTime `json:"force_reduced_at"`
ArchivedAt sql.NullTime `json:"archived_at"`
IsCustomDomain bool `json:"is_custom_domain"`
}
func (q *Queries) GetSiteByDomain(ctx context.Context, domain string) (GetSiteByDomainRow, error) {
row := q.db.QueryRowContext(ctx, getSiteByDomain, domain)
var i GetSiteByDomainRow
err := row.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
&i.IsCustomDomain,
)
return i, err
}
const getSiteByID = `-- name: GetSiteByID :one
SELECT s.site_id, s.workspace_id, s.domain, s.created_at, s.updated_at, s.status, s.storage_bytes, s.last_modified_at, s.force_reduced_at, s.archived_at, (EXISTS (
SELECT 1 FROM domains.placements p
JOIN domains.claims c ON c.claim_id = p.claim_id
WHERE p.provider = 'fedwiki'
AND p.resource_ref = s.site_id::text
AND c.kind = 'external'
))::boolean AS is_custom_domain
FROM fedwiki.sites AS s
WHERE s.site_id = $1
`
type GetSiteByIDRow struct {
SiteID string `json:"site_id"`
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Status string `json:"status"`
StorageBytes sql.NullInt64 `json:"storage_bytes"`
LastModifiedAt sql.NullTime `json:"last_modified_at"`
ForceReducedAt sql.NullTime `json:"force_reduced_at"`
ArchivedAt sql.NullTime `json:"archived_at"`
IsCustomDomain bool `json:"is_custom_domain"`
}
// The display queries below derive `is_custom_domain` instead of reading a
// column: whether a site sits on a member-owned domain is a fact about its
// registry placement's claim, not about the site (domains-registry D8, the
// dropped fedwiki.sites.is_custom_domain). The EXISTS is cast to boolean so
// the generated field stays a plain `bool` named IsCustomDomain, keeping
// every render site unchanged. Only the queries that feed member, API, and
// operator display carry it; the reconcile/sweep queries
// (ListActiveSitesByWorkspace, ListForceReducedSitesByWorkspace,
// ListArchivedSitesArchivedBefore) do not render it and stay plain
// SELECT * so they keep returning the Site model.
func (q *Queries) GetSiteByID(ctx context.Context, siteID string) (GetSiteByIDRow, error) {
row := q.db.QueryRowContext(ctx, getSiteByID, siteID)
var i GetSiteByIDRow
err := row.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
&i.IsCustomDomain,
)
return i, err
}
const getSiteSwapPolicy = `-- name: GetSiteSwapPolicy :one
SELECT last_swap_at FROM fedwiki.site_swap_policy WHERE workspace_id = $1
`
func (q *Queries) GetSiteSwapPolicy(ctx context.Context, workspaceID string) (time.Time, error) {
row := q.db.QueryRowContext(ctx, getSiteSwapPolicy, workspaceID)
var last_swap_at time.Time
err := row.Scan(&last_swap_at)
return last_swap_at, err
}
const listActiveSitesByWorkspace = `-- name: ListActiveSitesByWorkspace :many
SELECT site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at FROM fedwiki.sites
WHERE workspace_id = $1 AND status = 'active'
ORDER BY last_modified_at DESC NULLS LAST, created_at DESC
`
// Active sites, most-recently-modified first — the force_reduce keep/park order.
func (q *Queries) ListActiveSitesByWorkspace(ctx context.Context, workspaceID string) ([]Site, error) {
rows, err := q.db.QueryContext(ctx, listActiveSitesByWorkspace, workspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Site{}
for rows.Next() {
var i Site
if err := rows.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
); 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 listAllSites = `-- name: ListAllSites :many
SELECT s.site_id, s.workspace_id, s.domain, s.created_at, s.updated_at, s.status, s.storage_bytes, s.last_modified_at, s.force_reduced_at, s.archived_at, (EXISTS (
SELECT 1 FROM domains.placements p
JOIN domains.claims c ON c.claim_id = p.claim_id
WHERE p.provider = 'fedwiki'
AND p.resource_ref = s.site_id::text
AND c.kind = 'external'
))::boolean AS is_custom_domain
FROM fedwiki.sites AS s
ORDER BY s.domain ASC
`
type ListAllSitesRow struct {
SiteID string `json:"site_id"`
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Status string `json:"status"`
StorageBytes sql.NullInt64 `json:"storage_bytes"`
LastModifiedAt sql.NullTime `json:"last_modified_at"`
ForceReducedAt sql.NullTime `json:"force_reduced_at"`
ArchivedAt sql.NullTime `json:"archived_at"`
IsCustomDomain bool `json:"is_custom_domain"`
}
func (q *Queries) ListAllSites(ctx context.Context) ([]ListAllSitesRow, error) {
rows, err := q.db.QueryContext(ctx, listAllSites)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListAllSitesRow{}
for rows.Next() {
var i ListAllSitesRow
if err := rows.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
&i.IsCustomDomain,
); 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 listAllSitesForOperatorPage = `-- name: ListAllSitesForOperatorPage :many
SELECT s.site_id, s.workspace_id, s.domain, s.status, s.created_at,
w.name AS workspace_name,
o.org_id, o.name AS org_name,
(EXISTS (
SELECT 1 FROM domains.placements p
JOIN domains.claims c ON c.claim_id = p.claim_id
WHERE p.provider = 'fedwiki'
AND p.resource_ref = s.site_id::text
AND c.kind = 'external'
))::boolean AS is_custom_domain,
count(*) OVER() AS total_count
FROM fedwiki.sites AS s
JOIN core.workspaces w ON w.workspace_id = s.workspace_id
JOIN core.organizations o ON o.org_id = w.org_id
WHERE ($1::text IS NULL OR s.status = $1::text)
AND ($2::text IS NULL
OR s.domain ILIKE '%' || $2::text || '%'
OR w.name ILIKE '%' || $2::text || '%'
OR o.name ILIKE '%' || $2::text || '%')
ORDER BY s.domain ASC
LIMIT $4::int OFFSET $3::int
`
type ListAllSitesForOperatorPageParams struct {
Status sql.NullString `json:"status"`
Q sql.NullString `json:"q"`
PageOffset int32 `json:"page_offset"`
PageLimit int32 `json:"page_limit"`
}
type ListAllSitesForOperatorPageRow struct {
SiteID string `json:"site_id"`
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
WorkspaceName string `json:"workspace_name"`
OrgID string `json:"org_id"`
OrgName string `json:"org_name"`
IsCustomDomain bool `json:"is_custom_domain"`
TotalCount int64 `json:"total_count"`
}
// The operator sites surface, governed by operator-list-scale: one page of
// every FedWiki site in the deployment with its holder attached, ordered by
// domain over the whole filtered set before LIMIT/OFFSET so paging is
// stable.
//
// The owning organization is joined here rather than looked up per row: the
// handler used to resolve workspace -> organization with a cache, which
// cannot answer a search over the holder's name at all once only fifty rows
// are in hand. This is the one query in this package that reads core
// (00001_init grants core_reader to fedwiki_writer); it projects columns
// rather than whole rows so no core model enters this package's API.
//
// sqlc.narg(q): NULL matches every row; set, a case-insensitive substring
// match against the site's domain, its workspace's name, or the owning
// organization's name.
// sqlc.narg(status): the status facet over fedwiki.sites' own vocabulary
// (active, readonly, archived); NULL means every status.
// count(*) OVER() carries the true total for the filtered set alongside the
// page's rows, so the section count never reflects only the page.
func (q *Queries) ListAllSitesForOperatorPage(ctx context.Context, arg ListAllSitesForOperatorPageParams) ([]ListAllSitesForOperatorPageRow, error) {
rows, err := q.db.QueryContext(ctx, listAllSitesForOperatorPage,
arg.Status,
arg.Q,
arg.PageOffset,
arg.PageLimit,
)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListAllSitesForOperatorPageRow{}
for rows.Next() {
var i ListAllSitesForOperatorPageRow
if err := rows.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.Status,
&i.CreatedAt,
&i.WorkspaceName,
&i.OrgID,
&i.OrgName,
&i.IsCustomDomain,
&i.TotalCount,
); 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 listArchivedSitesArchivedBefore = `-- name: ListArchivedSitesArchivedBefore :many
SELECT site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at FROM fedwiki.sites
WHERE status = 'archived' AND archived_at IS NOT NULL AND archived_at < $1
ORDER BY archived_at ASC
`
// Retention purge: archived sites whose archive predates the cutoff.
func (q *Queries) ListArchivedSitesArchivedBefore(ctx context.Context, archivedAt sql.NullTime) ([]Site, error) {
rows, err := q.db.QueryContext(ctx, listArchivedSitesArchivedBefore, archivedAt)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Site{}
for rows.Next() {
var i Site
if err := rows.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
); 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 listForceReducedSitesByWorkspace = `-- name: ListForceReducedSitesByWorkspace :many
SELECT site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at FROM fedwiki.sites
WHERE workspace_id = $1 AND force_reduced_at IS NOT NULL
ORDER BY last_modified_at DESC NULLS LAST, created_at DESC
`
// Sites parked read-only by a downgrade, most-recently-modified first — the
// re-upgrade reactivation order.
func (q *Queries) ListForceReducedSitesByWorkspace(ctx context.Context, workspaceID string) ([]Site, error) {
rows, err := q.db.QueryContext(ctx, listForceReducedSitesByWorkspace, workspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Site{}
for rows.Next() {
var i Site
if err := rows.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
); 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 listOrphanedPlacements = `-- name: ListOrphanedPlacements :many
SELECT p.placement_id, p.fqdn, p.resource_ref
FROM domains.placements p
WHERE p.provider = 'fedwiki'
AND NOT EXISTS (
SELECT 1 FROM fedwiki.sites s WHERE s.site_id::text = p.resource_ref
)
ORDER BY p.fqdn ASC
`
type ListOrphanedPlacementsRow struct {
PlacementID string `json:"placement_id"`
Fqdn string `json:"fqdn"`
ResourceRef string `json:"resource_ref"`
}
// Boot reconciliation's orphan set (domains-registry D8), the mirror image of
// ListSitesWithoutPlacement: every `fedwiki` placement whose resource_ref
// names no `fedwiki.sites` row. A create saga whose compensation never ran and
// a delete interrupted between freeing the row and freeing the name both leave
// one, and until it is released `/domains/ask` keeps authorizing certificates
// for a name nothing serves. Ordered by name for a stable pass.
func (q *Queries) ListOrphanedPlacements(ctx context.Context) ([]ListOrphanedPlacementsRow, error) {
rows, err := q.db.QueryContext(ctx, listOrphanedPlacements)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListOrphanedPlacementsRow{}
for rows.Next() {
var i ListOrphanedPlacementsRow
if err := rows.Scan(&i.PlacementID, &i.Fqdn, &i.ResourceRef); 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 listSitesByWorkspace = `-- name: ListSitesByWorkspace :many
SELECT s.site_id, s.workspace_id, s.domain, s.created_at, s.updated_at, s.status, s.storage_bytes, s.last_modified_at, s.force_reduced_at, s.archived_at, (EXISTS (
SELECT 1 FROM domains.placements p
JOIN domains.claims c ON c.claim_id = p.claim_id
WHERE p.provider = 'fedwiki'
AND p.resource_ref = s.site_id::text
AND c.kind = 'external'
))::boolean AS is_custom_domain
FROM fedwiki.sites AS s
WHERE s.workspace_id = $1
ORDER BY s.domain ASC
`
type ListSitesByWorkspaceRow struct {
SiteID string `json:"site_id"`
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Status string `json:"status"`
StorageBytes sql.NullInt64 `json:"storage_bytes"`
LastModifiedAt sql.NullTime `json:"last_modified_at"`
ForceReducedAt sql.NullTime `json:"force_reduced_at"`
ArchivedAt sql.NullTime `json:"archived_at"`
IsCustomDomain bool `json:"is_custom_domain"`
}
func (q *Queries) ListSitesByWorkspace(ctx context.Context, workspaceID string) ([]ListSitesByWorkspaceRow, error) {
rows, err := q.db.QueryContext(ctx, listSitesByWorkspace, workspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListSitesByWorkspaceRow{}
for rows.Next() {
var i ListSitesByWorkspaceRow
if err := rows.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
&i.IsCustomDomain,
); 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 listSitesWithoutPlacement = `-- name: ListSitesWithoutPlacement :many
SELECT site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at FROM fedwiki.sites
WHERE NOT EXISTS (
SELECT 1 FROM domains.placements p
WHERE p.provider = 'fedwiki' AND p.resource_ref = fedwiki.sites.site_id::text
)
ORDER BY length(domain) ASC, domain ASC
`
// Boot reconciliation's backfill set (domains-registry D8): every site row the
// registry holds no placement for. Ordered shortest name first so a nested
// pre-registry site (`blog.example.org`) is classified AFTER the name that
// contains it (`example.org`) and lands as a placement inside the claim that
// one created, instead of minting an overlapping second claim.
func (q *Queries) ListSitesWithoutPlacement(ctx context.Context) ([]Site, error) {
rows, err := q.db.QueryContext(ctx, listSitesWithoutPlacement)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Site{}
for rows.Next() {
var i Site
if err := rows.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
); 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 listWorkspaceIDsWithSites = `-- name: ListWorkspaceIDsWithSites :many
SELECT DISTINCT workspace_id FROM fedwiki.sites
`
// Workspaces that own at least one site — the units the quota reconcile sweeps.
func (q *Queries) ListWorkspaceIDsWithSites(ctx context.Context) ([]string, error) {
rows, err := q.db.QueryContext(ctx, listWorkspaceIDsWithSites)
if err != nil {
return nil, err
}
defer rows.Close()
items := []string{}
for rows.Next() {
var workspace_id string
if err := rows.Scan(&workspace_id); err != nil {
return nil, err
}
items = append(items, workspace_id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const setForceReducedByDomain = `-- name: SetForceReducedByDomain :exec
UPDATE fedwiki.sites
SET force_reduced_at = NOW(), updated_at = NOW()
WHERE domain = $1 AND force_reduced_at IS NULL
`
func (q *Queries) SetForceReducedByDomain(ctx context.Context, domain string) error {
_, err := q.db.ExecContext(ctx, setForceReducedByDomain, domain)
return err
}
const setSiteStatusByDomain = `-- name: SetSiteStatusByDomain :one
UPDATE fedwiki.sites
SET status = $2,
archived_at = CASE
WHEN $2 = 'archived' AND status <> 'archived' THEN NOW()
WHEN $2 <> 'archived' THEN NULL
ELSE archived_at
END,
updated_at = NOW()
WHERE domain = $1
RETURNING site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at
`
type SetSiteStatusByDomainParams struct {
Domain string `json:"domain"`
Status string `json:"status"`
}
// Project a lifecycle status change locally. archived_at is set on entering
// `archived` (idempotent — preserves the original timestamp) and cleared on
// leaving it, driving the Trash retention purge.
func (q *Queries) SetSiteStatusByDomain(ctx context.Context, arg SetSiteStatusByDomainParams) (Site, error) {
row := q.db.QueryRowContext(ctx, setSiteStatusByDomain, arg.Domain, arg.Status)
var i Site
err := row.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
)
return i, err
}
const upsertSiteByDomain = `-- name: UpsertSiteByDomain :one
INSERT INTO fedwiki.sites (workspace_id, domain)
VALUES ($1, $2)
ON CONFLICT(domain) DO UPDATE SET
workspace_id = excluded.workspace_id,
updated_at = NOW()
RETURNING site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at
`
type UpsertSiteByDomainParams struct {
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
}
func (q *Queries) UpsertSiteByDomain(ctx context.Context, arg UpsertSiteByDomainParams) (Site, error) {
row := q.db.QueryRowContext(ctx, upsertSiteByDomain, arg.WorkspaceID, arg.Domain)
var i Site
err := row.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
)
return i, err
}
const upsertSiteFromFarm = `-- name: UpsertSiteFromFarm :one
INSERT INTO fedwiki.sites (workspace_id, domain, status, storage_bytes, last_modified_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT(domain) DO UPDATE SET
status = excluded.status,
storage_bytes = excluded.storage_bytes,
last_modified_at = excluded.last_modified_at,
updated_at = NOW()
RETURNING site_id, workspace_id, domain, created_at, updated_at, status, storage_bytes, last_modified_at, force_reduced_at, archived_at
`
type UpsertSiteFromFarmParams struct {
WorkspaceID string `json:"workspace_id"`
Domain string `json:"domain"`
Status string `json:"status"`
StorageBytes sql.NullInt64 `json:"storage_bytes"`
LastModifiedAt sql.NullTime `json:"last_modified_at"`
}
// Sync projection: insert a newly-seen farm site (default workspace), or project
// the observed status/storage/last-modified onto an existing row WITHOUT touching
// its workspace_id (user-created sites keep their owner).
func (q *Queries) UpsertSiteFromFarm(ctx context.Context, arg UpsertSiteFromFarmParams) (Site, error) {
row := q.db.QueryRowContext(ctx, upsertSiteFromFarm,
arg.WorkspaceID,
arg.Domain,
arg.Status,
arg.StorageBytes,
arg.LastModifiedAt,
)
var i Site
err := row.Scan(
&i.SiteID,
&i.WorkspaceID,
&i.Domain,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.StorageBytes,
&i.LastModifiedAt,
&i.ForceReducedAt,
&i.ArchivedAt,
)
return i, err
}
const upsertSiteSwapPolicy = `-- name: UpsertSiteSwapPolicy :exec
INSERT INTO fedwiki.site_swap_policy (workspace_id, last_swap_at)
VALUES ($1, NOW())
ON CONFLICT (workspace_id) DO UPDATE SET last_swap_at = NOW()
`
// Records that a swap just happened (resets the cooldown clock for the workspace).
func (q *Queries) UpsertSiteSwapPolicy(ctx context.Context, workspaceID string) error {
_, err := q.db.ExecContext(ctx, upsertSiteSwapPolicy, workspaceID)
return err
}