postgres

package
v1.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: AGPL-3.0 Imports: 20 Imported by: 0

Documentation

Overview

Package postgres provides a Postgres-backed implementation of service.Repository for the identity service.

This is an alternative to the original graph-backed repository. The two are interchangeable from the AuthService's point of view: both implement the same service.Repository interface.

Why Postgres

Postgres is well understood, ubiquitous in production environments, and ships with a mature ops toolkit (pg_dump, pg_basebackup, replicas, PITR via WAL archiving). It is the right default for teams that want to run identity without taking a dependency on the tenant-shard-db stack.

Driver choice

The implementation uses pgx/v5 directly (not via database/sql). pgxpool.Pool gives us a tuneable connection pool; pgx is also faster than database/sql and exposes Postgres-specific features (LISTEN / NOTIFY, COPY, JSONB native typing) that we may want later.

Migrations

Schema DDL lives under migrations/ and is applied via golang-migrate/migrate using the embed.FS source. By default New() does NOT run pending migrations on connect — production deploys run migrations out-of-band as a separate Job, so a rolling rollout never races two replicas to apply the same change. Set Config.AutoMigrate to true (or GATEWAY_POSTGRES_AUTO_MIGRATE=true) only for local dev and single-replica environments.

Project isolation (ADR-0002)

The Project is identity's storage shard. Every data-plane table carries a project_id text not null column (FK to projects(id)) and uniqueness constraints are scoped to (project_id, ...). A pgRepository instance is bound to a single project_id and writes/reads only that project's rows; per-request scopes are derived via WithProject. The logical-tenant tenant_id columns on the governance tables (domains, login_policies, tenant_memberships, tenant_invitations) reference tenants(id) and are a separate concept from this storage shard.

Error mapping

Postgres unique-violation errors (SQLSTATE 23505) are mapped to service.ErrAlreadyExists by errors.go::wrapPgErr. ErrNoRows is surfaced as a nil result (not an error), matching the existing in-memory driver.

Index

Constants

View Source
const DefaultConnTimeout = 5 * time.Second

DefaultConnTimeout is used when Config.ConnTimeout is zero.

View Source
const DefaultMaxConns int32 = 25

DefaultMaxConns is used when Config.MaxConns is zero.

Variables

This section is empty.

Functions

func Migrate added in v0.15.0

func Migrate(dsn string) error

Migrate applies all pending schema migrations to the Postgres database at dsn, then returns. It is idempotent — a fully-migrated database is a no-op — and safe to run concurrently with other instances: the underlying runner holds a Postgres advisory lock for the duration, so exactly one caller applies the migrations and the rest wait, then no-op. It is the entry point behind the `identity migrate` deploy step (the explicit alternative to GATEWAY_POSTGRES_AUTO_MIGRATE).

func New

func New(ctx context.Context, cfg Config) (*pgRepository, error)

New constructs a Postgres-backed repository:

  1. Parse / validate cfg.
  2. Open a pgxpool with cfg.MaxConns, cfg.ConnTimeout.
  3. Optionally run pending migrations (cfg.AutoMigrate=true).
  4. Ping to fail fast on a misconfigured DSN.

The returned store implements both service.Repository and service.DB. The caller is responsible for keeping the *pgRepository alive for the lifetime of the service; pool resources are released by Close().

Types

type AutoFormStore added in v0.17.0

type AutoFormStore struct {
	// contains filtered or unexported fields
}

AutoFormStore turns a verified company-email signup into governance rows.

func NewAutoFormStore added in v0.17.0

func NewAutoFormStore(r *pgRepository) *AutoFormStore

NewAutoFormStore builds an auto-formation store sharing the repository's pool.

func (*AutoFormStore) EnsureTenantForDomain added in v0.17.0

func (s *AutoFormStore) EnsureTenantForDomain(ctx context.Context, projectID, domain, userID string) (string, error)

EnsureTenantForDomain idempotently ensures a latent Tenant + its email Domain exist for (projectID, domain) and records a domain-derived membership for userID. See the interface doc for the concurrency contract: tenant+domain are created in one transaction so a lost race leaves no orphan tenant.

type Config

type Config struct {
	DSN         string
	MaxConns    int32
	ConnTimeout time.Duration
	AutoMigrate bool
	ProjectID   string
}

Config controls how the postgres repository connects to its database.

DSN is the libpq-style connection string, e.g.

postgres://user:pass@host:5432/dbname?sslmode=disable

MaxConns caps the underlying pgxpool. A zero value means "use the pgxpool default" (currently 4 + GOMAXPROCS-ish). 25 is the suggested default for an identity service node (see DefaultMaxConns).

ConnTimeout is the per-acquire timeout used when checking a connection out of the pool. It does NOT bound the total query time — callers are still responsible for passing a context with the appropriate deadline.

AutoMigrate controls whether New() applies pending schema migrations on first connect. In CI / dev / test we want true (the default); in strict production deploys teams may flip it to false and run `migrate ... up` from a deploy pipeline instead.

ProjectID is the project (storage shard) whose rows this repository instance writes and reads. Per ADR-0002 the Project is identity's isolation shard: every data-plane row carries project_id and the mandatory `WHERE project_id = $1` predicate is bound here. Multi-project deployments derive a per-request scope via WithProject; the common zero-config single-project boot plumbs cfg.DefaultProjectID straight through.

type DomainStore added in v0.17.0

type DomainStore struct {
	// contains filtered or unexported fields
}

DomainStore persists Domains within a Project.

func NewDomainStore added in v0.17.0

func NewDomainStore(r *pgRepository) *DomainStore

NewDomainStore builds a domain store sharing the repository's pool.

func (*DomainStore) CreateDomain added in v0.17.0

func (s *DomainStore) CreateDomain(ctx context.Context, d *service.Domain) (string, error)

CreateDomain inserts a domain. ProjectID, TenantID and Domain are required; a blank id is generated and written back. A duplicate (project_id, lower(domain)) surfaces service.ErrAlreadyExists.

func (*DomainStore) GetDomain added in v0.17.0

func (s *DomainStore) GetDomain(ctx context.Context, projectID, domainID string) (*service.Domain, error)

GetDomain returns the domain by id within a project, or (nil, nil).

func (*DomainStore) GetDomainByName added in v0.17.0

func (s *DomainStore) GetDomainByName(ctx context.Context, projectID, domain string) (*service.Domain, error)

GetDomainByName returns the domain row for a name (case-insensitive) within a project, or (nil, nil).

func (*DomainStore) ListDomainsByTenant added in v0.17.0

func (s *DomainStore) ListDomainsByTenant(ctx context.Context, projectID, tenantID string) ([]*service.Domain, error)

ListDomainsByTenant returns every domain bound to a tenant, newest first.

func (*DomainStore) SetDomainStatus added in v0.17.0

func (s *DomainStore) SetDomainStatus(ctx context.Context, projectID, domainID, status string, verifiedAtMs int64) error

SetDomainStatus transitions a domain's status and, when verifying, stamps verified_at_ms (0 defaults to now on a verify). Unknown ids are a no-op.

type InvitationStore added in v0.17.0

type InvitationStore struct {
	// contains filtered or unexported fields
}

InvitationStore persists TenantInvitations within a Project.

func NewInvitationStore added in v0.17.0

func NewInvitationStore(r *pgRepository) *InvitationStore

NewInvitationStore builds a store sharing the repository's pool.

func (*InvitationStore) CreateInvitation added in v0.17.0

func (s *InvitationStore) CreateInvitation(ctx context.Context, inv *service.TenantInvitation) (string, error)

CreateInvitation atomically enforces one-open-invite: in a single transaction it revokes any existing pending invitation for the same (project, tenant, lower(email)) and inserts the new one. This is the authoritative enforcement (the partial unique index is defense-in-depth, and the memory driver — should they ever gain invitations — must match these revoke-then-insert semantics).

func (*InvitationStore) GetInvitationByTokenHash added in v0.17.0

func (s *InvitationStore) GetInvitationByTokenHash(ctx context.Context, projectID, tokenHash string) (*service.TenantInvitation, error)

GetInvitationByTokenHash resolves an invitation by its hashed token within a project, or (nil, nil).

func (*InvitationStore) ListInvitationsForTenant added in v0.17.0

func (s *InvitationStore) ListInvitationsForTenant(ctx context.Context, projectID, tenantID string) ([]*service.TenantInvitation, error)

ListInvitationsForTenant returns every invitation in a tenant, newest first.

func (*InvitationStore) SetInvitationStatus added in v0.17.0

func (s *InvitationStore) SetInvitationStatus(ctx context.Context, projectID, invitationID, status string, acceptedAtMs int64) error

SetInvitationStatus transitions an invitation's status and, when accepting, stamps accepted_at_ms (0 defaults to now on accept). Unknown ids are a no-op.

type LoginPolicyStore added in v0.17.0

type LoginPolicyStore struct {
	// contains filtered or unexported fields
}

LoginPolicyStore persists per-tenant login policies within a Project.

func NewLoginPolicyStore added in v0.17.0

func NewLoginPolicyStore(r *pgRepository) *LoginPolicyStore

NewLoginPolicyStore builds a store sharing the repository's pool.

func (*LoginPolicyStore) DeleteLoginPolicy added in v1.3.0

func (s *LoginPolicyStore) DeleteLoginPolicy(ctx context.Context, projectID, tenantID string) error

DeleteLoginPolicy removes the policy for (projectID, tenantID). It is idempotent: deleting an absent policy affects no row and returns nil. Both ids are required.

func (*LoginPolicyStore) GetLoginPolicy added in v0.17.0

func (s *LoginPolicyStore) GetLoginPolicy(ctx context.Context, projectID, tenantID string) (*service.LoginPolicy, error)

GetLoginPolicy returns the policy for (projectID, tenantID), or (nil, nil) when none is set.

func (*LoginPolicyStore) UpsertLoginPolicy added in v0.17.0

func (s *LoginPolicyStore) UpsertLoginPolicy(ctx context.Context, p *service.LoginPolicy) (string, error)

UpsertLoginPolicy inserts or replaces the policy for (ProjectID, TenantID). On conflict it updates the policy fields and stamps updated_at_ms, leaving id and created_at_ms intact. Returns the id of the resulting row.

type MembershipStore added in v0.17.0

type MembershipStore struct {
	// contains filtered or unexported fields
}

MembershipStore persists TenantMemberships within a Project.

func NewMembershipStore added in v0.17.0

func NewMembershipStore(r *pgRepository) *MembershipStore

NewMembershipStore builds a store sharing the repository's pool.

func (*MembershipStore) GetMembership added in v0.17.0

func (s *MembershipStore) GetMembership(ctx context.Context, projectID, tenantID, userID string) (*service.TenantMembership, error)

GetMembership returns the membership for (project, tenant, user), or (nil, nil).

func (*MembershipStore) ListMembershipsForTenant added in v0.17.0

func (s *MembershipStore) ListMembershipsForTenant(ctx context.Context, projectID, tenantID string) ([]*service.TenantMembership, error)

ListMembershipsForTenant returns every membership in a tenant, newest first.

func (*MembershipStore) ListMembershipsForUser added in v0.17.0

func (s *MembershipStore) ListMembershipsForUser(ctx context.Context, projectID, userID string) ([]*service.TenantMembership, error)

ListMembershipsForUser returns every membership a user holds across a project's tenants, newest first.

func (*MembershipStore) RemoveMembership added in v0.17.0

func (s *MembershipStore) RemoveMembership(ctx context.Context, projectID, tenantID, userID string) error

RemoveMembership deletes the membership for (project, tenant, user). Unknown rows are a no-op.

func (*MembershipStore) UpsertMembership added in v0.17.0

func (s *MembershipStore) UpsertMembership(ctx context.Context, m *service.TenantMembership) (string, error)

UpsertMembership inserts or, on a (project, tenant, user) conflict, updates source/role/status in place (keeping id + created_at_ms) and stamps updated_at_ms. Returns the surviving row id.

type PlatformAdminStore added in v1.1.0

type PlatformAdminStore struct {
	// contains filtered or unexported fields
}

PlatformAdminStore is the Postgres-backed, control-plane store for platform operator accounts (the platform_admins table from migration 0013). Like the other control-plane stores it is platform-global (not project/tenant scoped) and shares its caller's connection pool.

func NewPlatformAdminStore added in v1.1.0

func NewPlatformAdminStore(r *pgRepository) *PlatformAdminStore

NewPlatformAdminStore builds a platform-admin store sharing the given repository's connection pool. The store must NOT be closed independently — closing the owning *pgRepository releases the pool for every derived store.

func (*PlatformAdminStore) CountPlatformAdmins added in v1.1.0

func (s *PlatformAdminStore) CountPlatformAdmins(ctx context.Context) (int, error)

CountPlatformAdmins returns the number of platform admins (any status).

func (*PlatformAdminStore) CreateFirstPlatformAdmin added in v1.1.0

func (s *PlatformAdminStore) CreateFirstPlatformAdmin(ctx context.Context, a *service.PlatformAdmin) (bool, error)

CreateFirstPlatformAdmin inserts the first platform admin atomically and ONLY while the table is empty. It takes a transaction-scoped advisory lock first, so concurrent bootstraps are fully serialized: the winner sees an empty table and inserts; every loser, running after the winner commits, sees a non-empty table and returns (created=false, nil) without writing.

The advisory lock — rather than relying on the table's own constraints — is what makes "empty?" race-safe: under READ COMMITTED two plain SELECT-then-INSERT transactions could each see zero rows and both insert distinct-email admins, defeating the one-time guarantee. Serializing on a single lock key closes that window.

type Project added in v0.15.0

type Project struct {
	ID             string
	StorageScopeID string
	Name           string
	Status         string // active | suspended
	ConfigJSON     string // JSON object; "" is normalised to "{}".
	CreatedAtMs    int64
	UpdatedAtMs    int64
}

Project is a control-plane registry row: one logical, control-plane isolation entity (a Firebase-style project) mapped onto exactly one physical storage scope (shard) via StorageScopeID.

type ProjectAuthDomain added in v0.15.0

type ProjectAuthDomain struct {
	ID           string
	ProjectID    string
	Hostname     string
	IsPrimary    bool
	VerifiedAtMs int64
	CreatedAtMs  int64
}

ProjectAuthDomain is a per-project serving hostname. One host resolves to exactly one project (Hostname is globally unique, case-insensitive), so the Host header alone can resolve a project.

type ProjectCredential added in v0.15.0

type ProjectCredential struct {
	ID           string
	ProjectID    string
	Kind         string // publishable | secret | mtls
	PublicID     string
	SecretHash   string
	Status       string // active | revoked
	CreatedAtMs  int64
	LastUsedAtMs int64
	RevokedAtMs  int64
}

ProjectCredential is a lookup key used to resolve a project on a request, by its globally-unique PublicID.

type ProjectStore added in v0.15.0

type ProjectStore struct {
	// contains filtered or unexported fields
}

ProjectStore is the Postgres-backed, control-plane registry store. It is platform-global (not project/tenant-scoped) and shares its caller's connection pool.

func NewProjectStore added in v0.15.0

func NewProjectStore(r *pgRepository, requireVerifiedAuthDomain bool) *ProjectStore

NewProjectStore builds a control-plane store that shares the given repository's connection pool. The store must NOT be closed independently — closing the owning *pgRepository releases the pool for every derived store. requireVerifiedAuthDomain, when true, restricts the resolved primary auth-domain to DNS-verified hostnames (see primaryAuthHostname).

func (*ProjectStore) CreateAuthDomain added in v1.1.0

func (s *ProjectStore) CreateAuthDomain(ctx context.Context, projectID, hostname string, isPrimary bool) error

CreateAuthDomain registers an UNVERIFIED custom serving hostname (the customer-domain flow). VerifiedAtMs is left 0 so the resolver does not resolve it until VerifyProjectAuthDomain proves ownership. A hostname already bound to any project surfaces service.ErrAlreadyExists.

func (*ProjectStore) CreateProject added in v0.15.0

func (s *ProjectStore) CreateProject(ctx context.Context, p *service.AdminProject) (string, error)

CreateProject inserts a project from the admin service's value type and returns its id. EnsureAuthDomain and the resolver read the same tables.

func (*ProjectStore) CreateProjectAuthDomain added in v0.15.0

func (s *ProjectStore) CreateProjectAuthDomain(ctx context.Context, d *ProjectAuthDomain) (string, error)

CreateProjectAuthDomain inserts a serving hostname for a project. Hostname is globally unique on lower(hostname) — a duplicate (in any case) surfaces service.ErrAlreadyExists. At most one is_primary domain is allowed per project (partial unique index); a second primary for the same project likewise surfaces service.ErrAlreadyExists. The id is caller-supplied (random hex when empty) and written back to d.ID.

func (*ProjectStore) CreateProjectCredential added in v0.15.0

func (s *ProjectStore) CreateProjectCredential(ctx context.Context, c *service.AdminProjectCredential) (string, error)

CreateProjectCredential inserts a credential from the admin service's value type and returns its id. Only the secret HASH is carried in — the raw secret never reaches the store.

func (*ProjectStore) EnsureAuthDomain added in v0.17.0

func (s *ProjectStore) EnsureAuthDomain(ctx context.Context, projectID, hostname string, isPrimary bool, verifiedAtMs int64) error

EnsureAuthDomain idempotently ensures hostname is a serving auth-domain of projectID. It is safe to call on every boot: if the hostname already exists it is a no-op when it belongs to projectID, and an error when it belongs to a DIFFERENT project (a misconfiguration the operator must resolve). verifiedAtMs marks the domain verified at seed time — used for deployer-owned domains, which need no DNS challenge. A concurrent creator is tolerated by re-reading the winner. Note: passing isPrimary=true when the project already has a different primary surfaces ErrAlreadyExists (the per-project primary partial-unique); changing the primary host is an explicit reconfiguration, not a silent re-seed.

func (*ProjectStore) EnsureDefaultProject added in v0.15.0

func (s *ProjectStore) EnsureDefaultProject(ctx context.Context, projectID, storageScopeID, name string) (*Project, error)

EnsureDefaultProject idempotently ensures the default Project exists, mapped onto the given storage scope (typically GATEWAY_DEFAULT_TENANT_ID). It is safe to call on every boot and from multiple instances at once: it returns the existing project when one is already present (looked up by id, then by storage scope), and otherwise creates it, tolerating a concurrent creator (an ErrAlreadyExists race is resolved by re-reading the row).

The default project is a logical control-plane entity that POINTS AT the storage scope via StorageScopeID — it is not the same value as the storage id, and the two must not be conflated.

If the storage scope is already mapped to a project (storage_scope_id is globally unique), that existing project is returned even when its id differs from projectID — the scope binding wins and no second project is created for the same scope.

func (*ProjectStore) GetAuthDomain added in v1.1.0

func (s *ProjectStore) GetAuthDomain(ctx context.Context, projectID, hostname string) (*service.AdminProjectAuthDomain, error)

GetAuthDomain returns a project's own auth-domain (any state) as the admin service's value type, or (nil, nil) when the project has no such hostname.

func (*ProjectStore) GetProjectAuthDomain added in v1.1.0

func (s *ProjectStore) GetProjectAuthDomain(ctx context.Context, projectID, hostname string) (*ProjectAuthDomain, error)

GetProjectAuthDomain returns a project's auth-domain by hostname (case-insensitive), or (nil, nil) when the project has no such domain. It is scoped to projectID — a hostname owned by a different project is a miss — so the custom-domain RPCs only ever read/verify a project's own domains. Unlike GetProjectByAuthHostname it does NOT filter on verified, so the caller can observe (and verify) a still-unverified domain.

func (*ProjectStore) GetProjectByAuthHostname added in v0.15.0

func (s *ProjectStore) GetProjectByAuthHostname(ctx context.Context, hostname string) (*Project, error)

GetProjectByAuthHostname resolves a project from a request's Host header. The hostname match is case-insensitive (lower(hostname)), matching the global unique index, so one host resolves to exactly one project. Only a VERIFIED auth-domain (verified_at_ms > 0) resolves: a customer-registered custom domain stays non-resolving until its DNS-TXT ownership challenge is proven, so an attacker cannot point an unverified hostname at a project. Returns (nil, nil) when no verified auth domain matches the host.

func (*ProjectStore) GetProjectByID added in v0.15.0

func (s *ProjectStore) GetProjectByID(ctx context.Context, projectID string) (*Project, error)

GetProjectByID returns the project with the given id, or (nil, nil) when no such project exists.

func (*ProjectStore) GetProjectByStorageScope added in v0.15.0

func (s *ProjectStore) GetProjectByStorageScope(ctx context.Context, storageScopeID string) (*Project, error)

GetProjectByStorageScope returns the single project mapped onto the given physical storage scope, or (nil, nil) when none is. storage_scope_id is globally unique, so this resolves at most one project.

func (*ProjectStore) GetProjectConfig added in v1.3.0

func (s *ProjectStore) GetProjectConfig(ctx context.Context, projectID string) (string, error)

GetProjectConfig returns a project's stored config_json ("{}" when unset). An unknown project surfaces service.ErrNotFound.

func (*ProjectStore) GetProjectCredentialByPublicID added in v0.15.0

func (s *ProjectStore) GetProjectCredentialByPublicID(ctx context.Context, publicID string) (*ProjectCredential, error)

GetProjectCredentialByPublicID resolves a credential (and thus its owning project_id, kind and status) by its globally-unique public_id, or returns (nil, nil) when none matches. This is the key-based per-request project-resolution path.

func (*ProjectStore) ListAuthDomains added in v1.1.0

func (s *ProjectStore) ListAuthDomains(ctx context.Context, projectID string) ([]*service.AdminProjectAuthDomain, error)

ListAuthDomains returns every auth-domain of a project (primary-first) as the admin service's value type.

func (*ProjectStore) ListProjectAuthDomains added in v0.15.0

func (s *ProjectStore) ListProjectAuthDomains(ctx context.Context, projectID string) ([]*ProjectAuthDomain, error)

ListProjectAuthDomains returns every auth domain for a project, ordered primary-first then by creation time, so callers can pick the link-building host deterministically. An unknown project yields an empty slice.

func (*ProjectStore) ResolveByCredential added in v0.16.0

func (s *ProjectStore) ResolveByCredential(ctx context.Context, publicID string) (*service.ResolvedProject, error)

ResolveByCredential resolves the active project an active credential public id belongs to. A revoked credential, a suspended project, an unknown id, or a blank id is a clean miss (nil, nil); only an infrastructure failure returns an error.

func (*ProjectStore) ResolveByHostname added in v0.16.0

func (s *ProjectStore) ResolveByHostname(ctx context.Context, hostname string) (*service.ResolvedProject, error)

ResolveByHostname resolves the active project a serving hostname maps onto (case-insensitive). An unmapped hostname, a suspended project, or a blank hostname is a clean miss (nil, nil).

func (*ProjectStore) RevokeProjectCredential added in v0.15.0

func (s *ProjectStore) RevokeProjectCredential(ctx context.Context, credentialID string, atMs int64) error

RevokeProjectCredential marks a credential revoked at atMs (defaulting to now when zero). Revoking is idempotent: an already-revoked or non-existent credential is a no-op that returns nil. A revoked credential no longer resolves a project for new requests, though the row is retained for audit.

func (*ProjectStore) SetAuthDomainVerified added in v1.1.0

func (s *ProjectStore) SetAuthDomainVerified(ctx context.Context, projectID, hostname string, verifiedAtMs int64) error

SetAuthDomainVerified stamps verifiedAtMs on a project's own auth-domain, flipping it to resolving. A hostname the project does not own surfaces service.ErrNotFound.

func (*ProjectStore) SetPrimaryAuthDomain added in v1.2.0

func (s *ProjectStore) SetPrimaryAuthDomain(ctx context.Context, projectID, hostname string) (*service.AdminProjectAuthDomain, error)

SetPrimaryAuthDomain promotes a project's VERIFIED auth-domain to primary, atomically demoting the current primary, and returns the promoted record as the admin service's value type. An unverified target surfaces service.ErrAuthDomainNotVerified; a hostname the project does not own surfaces service.ErrNotFound.

func (*ProjectStore) SetPrimaryProjectAuthDomain added in v1.2.0

func (s *ProjectStore) SetPrimaryProjectAuthDomain(ctx context.Context, projectID, hostname string) (*ProjectAuthDomain, error)

SetPrimaryProjectAuthDomain promotes a project's VERIFIED auth-domain to its primary serving host, atomically demoting the current primary in the SAME transaction so the per-project partial-unique primary index is never violated — even under concurrent promotions (both lock the same project's rows, so they serialize rather than colliding on the index). Only a verified (verified_at_ms > 0) domain may be promoted: an unverified target is ErrAuthDomainNotVerified. A hostname the project does not own is ErrNotFound. Promoting the already-primary host is a no-op that returns its record.

func (*ProjectStore) SetProjectAuthDomainVerified added in v1.1.0

func (s *ProjectStore) SetProjectAuthDomainVerified(ctx context.Context, projectID, hostname string, verifiedAtMs int64) error

SetProjectAuthDomainVerified stamps verified_at_ms on a project's auth-domain, flipping it from non-resolving to resolving. verifiedAtMs must be > 0 (the resolver treats 0 as unverified); atMs defaulting is the caller's job. The update is scoped to projectID so one project cannot mark another's domain verified. A hostname the project does not own affects no row and surfaces ErrNotFound, so a caller can distinguish a no-op verify from a successful one.

func (*ProjectStore) UpdateProjectConfig added in v1.3.0

func (s *ProjectStore) UpdateProjectConfig(ctx context.Context, projectID, configJSON string) (string, error)

UpdateProjectConfig REPLACES a project's config_json blob and returns the stored (normalised) value. An unknown project surfaces service.ErrNotFound.

type TenantStore added in v0.17.0

type TenantStore struct {
	// contains filtered or unexported fields
}

TenantStore persists Tenants within a Project.

func NewTenantStore added in v0.17.0

func NewTenantStore(r *pgRepository) *TenantStore

NewTenantStore builds a tenant store sharing the repository's pool. Do not close it independently; closing the *pgRepository releases the pool.

func (*TenantStore) CreateTenant added in v0.17.0

func (s *TenantStore) CreateTenant(ctx context.Context, t *service.Tenant) (string, error)

CreateTenant inserts a tenant. ProjectID is required; a blank id is generated and written back to t.ID.

func (*TenantStore) GetTenant added in v0.17.0

func (s *TenantStore) GetTenant(ctx context.Context, projectID, tenantID string) (*service.Tenant, error)

GetTenant returns the tenant by id within a project, or (nil, nil).

func (*TenantStore) GetTenantByPrimaryDomain added in v0.17.0

func (s *TenantStore) GetTenantByPrimaryDomain(ctx context.Context, projectID, domain string) (*service.Tenant, error)

GetTenantByPrimaryDomain returns the tenant whose primary_domain equals domain (case-insensitive) within a project, or (nil, nil). A blank domain never matches.

func (*TenantStore) ListTenants added in v0.17.0

func (s *TenantStore) ListTenants(ctx context.Context, projectID string) ([]*service.Tenant, error)

ListTenants returns every tenant in a project, newest first.

func (*TenantStore) SetTenantStatus added in v0.17.0

func (s *TenantStore) SetTenantStatus(ctx context.Context, projectID, tenantID, status string) error

SetTenantStatus transitions a tenant's status and stamps updated_at. Unknown ids are a no-op.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL