db

package
v0.1.1 Latest Latest
Warning

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

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

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ApiToken

type ApiToken struct {
	ID             string
	ProjectID      string
	Name           string
	TokenHash      string
	CreatedAt      pgtype.Timestamptz
	RevokedAt      pgtype.Timestamptz
	TokenPlaintext string
}

type ConsumeInviteByHashParams

type ConsumeInviteByHashParams struct {
	TokenHash  string
	ConsumedBy *string
}

type CreateProjectForUserParams

type CreateProjectForUserParams struct {
	ID     string
	TeamID string
	Name   string
	UserID string
}

type CreateSessionParams

type CreateSessionParams struct {
	ID        string
	UserID    string
	ExpiresAt pgtype.Timestamptz
	CsrfToken string
}

type CreateSessionRow

type CreateSessionRow struct {
	ID        string
	UserID    string
	ExpiresAt pgtype.Timestamptz
	CsrfToken string
	CreatedAt pgtype.Timestamptz
}

type CreateTeamInviteForUserParams

type CreateTeamInviteForUserParams struct {
	ID        string
	TeamID    string
	CreatedBy string
	TokenHash string
	ExpiresAt pgtype.Timestamptz
}

type CreateTeamMembershipIfMissingParams

type CreateTeamMembershipIfMissingParams struct {
	TeamID string
	UserID string
}

type CreateTeamMembershipParams

type CreateTeamMembershipParams struct {
	TeamID string
	UserID string
}

type CreateTeamParams

type CreateTeamParams struct {
	ID   string
	Name string
}

type CreateUserParams

type CreateUserParams struct {
	ID                 string
	Email              string
	PasswordHash       string
	MustChangePassword bool
}

type DBTX

type DBTX interface {
	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
	QueryRow(context.Context, string, ...interface{}) pgx.Row
}

type FailedEvent

type FailedEvent struct {
	ID            string
	BatchPayload  []byte
	LastError     string
	AttemptCount  int32
	CreatedAt     pgtype.Timestamptz
	LastAttemptAt pgtype.Timestamptz
	QuarantinedAt pgtype.Timestamptz
}

type GetActiveIngestTokenByHashRow

type GetActiveIngestTokenByHashRow struct {
	ID        string
	ProjectID string
}

type GetActiveInviteByHashRow

type GetActiveInviteByHashRow struct {
	ID         string
	TeamID     string
	CreatedBy  string
	TokenHash  string
	ExpiresAt  pgtype.Timestamptz
	ConsumedAt pgtype.Timestamptz
	ConsumedBy *string
	CreatedAt  pgtype.Timestamptz
	TeamName   string
}

type GetProjectForUserParams

type GetProjectForUserParams struct {
	ID     string
	UserID string
}

type GetPublicTokenForProjectForUserParams

type GetPublicTokenForProjectForUserParams struct {
	ProjectID string
	UserID    string
}

type GetSessionWithUserRow

type GetSessionWithUserRow struct {
	SessionID          string
	UserID             string
	ExpiresAt          pgtype.Timestamptz
	CsrfToken          string
	CreatedAt          pgtype.Timestamptz
	UserEmail          string
	MustChangePassword bool
}

type GetTeamForUserParams

type GetTeamForUserParams struct {
	ID     string
	UserID string
}

type IncrementFailedEventAttemptParams

type IncrementFailedEventAttemptParams struct {
	ID        string
	LastError string
}

type InsertFailedEventParams

type InsertFailedEventParams struct {
	ID           string
	BatchPayload []byte
	LastError    string
}

type InsertOAuthAccessTokenParams

type InsertOAuthAccessTokenParams struct {
	ID        string
	TokenHash string
	ClientID  string
	UserID    string
	ProjectID string
	Scope     string
	ExpiresAt pgtype.Timestamptz
}

type InsertOAuthClientParams

type InsertOAuthClientParams struct {
	ID           string
	Name         string
	RedirectUris []string
}

type InsertOAuthCodeParams

type InsertOAuthCodeParams struct {
	ID                  string
	CodeHash            string
	ClientID            string
	UserID              string
	ProjectID           string
	RedirectUri         string
	Scope               string
	CodeChallenge       string
	CodeChallengeMethod string
	ExpiresAt           pgtype.Timestamptz
}

type InsertPublicAPITokenParams

type InsertPublicAPITokenParams struct {
	ID             string
	ProjectID      string
	Name           string
	TokenHash      string
	TokenPlaintext string
}

type IsMemberOfTeamParams

type IsMemberOfTeamParams struct {
	TeamID string
	UserID string
}

type ListActiveAccessTokensForUserRow

type ListActiveAccessTokensForUserRow struct {
	ID         string
	TokenHash  string
	ClientID   string
	UserID     string
	ProjectID  string
	Scope      string
	ExpiresAt  pgtype.Timestamptz
	RevokedAt  pgtype.Timestamptz
	CreatedAt  pgtype.Timestamptz
	LastUsedAt pgtype.Timestamptz
	ClientName string
}

type ListMembersForTeamForUserParams

type ListMembersForTeamForUserParams struct {
	TeamID string
	UserID string
}

type ListMembersForTeamForUserRow

type ListMembersForTeamForUserRow struct {
	ID        string
	Email     string
	CreatedAt pgtype.Timestamptz
	JoinedAt  pgtype.Timestamptz
}

type ListProjectsForTeamForUserParams

type ListProjectsForTeamForUserParams struct {
	TeamID string
	UserID string
}

type ListProjectsForTeamsForUserParams

type ListProjectsForTeamsForUserParams struct {
	Column1 []string
	UserID  string
}

type OauthAccessToken

type OauthAccessToken struct {
	ID         string
	TokenHash  string
	ClientID   string
	UserID     string
	ProjectID  string
	Scope      string
	ExpiresAt  pgtype.Timestamptz
	RevokedAt  pgtype.Timestamptz
	CreatedAt  pgtype.Timestamptz
	LastUsedAt pgtype.Timestamptz
}

type OauthClient

type OauthClient struct {
	ID           string
	Name         string
	RedirectUris []string
	CreatedAt    pgtype.Timestamptz
}

type OauthCode

type OauthCode struct {
	ID                  string
	CodeHash            string
	ClientID            string
	UserID              string
	ProjectID           string
	RedirectUri         string
	Scope               string
	CodeChallenge       string
	CodeChallengeMethod string
	ExpiresAt           pgtype.Timestamptz
	UsedAt              pgtype.Timestamptz
	CreatedAt           pgtype.Timestamptz
}

type Project

type Project struct {
	ID        string
	TeamID    string
	Name      string
	CreatedAt pgtype.Timestamptz
	DeletedAt pgtype.Timestamptz
}

type Queries

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

func New

func New(db DBTX) *Queries

func (*Queries) ConsumeInviteByHash

func (q *Queries) ConsumeInviteByHash(ctx context.Context, arg ConsumeInviteByHashParams) (TeamInvite, error)

Atomic burn. RowsAffected via RETURNING — if 0 rows, the invite was consumed, expired, or never existed; caller maps all three to 404.

func (*Queries) CountActiveFailedEvents

func (q *Queries) CountActiveFailedEvents(ctx context.Context) (int64, error)

Refreshes the in-process DLQ depth gauge after each drain pass.

func (*Queries) CreateProjectForUser

func (q *Queries) CreateProjectForUser(ctx context.Context, arg CreateProjectForUserParams) (Project, error)

The WHERE EXISTS guard makes the INSERT a no-op when the caller is not a member of the target team. RowsAffected == 0 → auth.ErrNotVisible.

func (*Queries) CreateSession

func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (CreateSessionRow, error)

func (*Queries) CreateTeam

func (q *Queries) CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)

func (*Queries) CreateTeamInviteForUser

func (q *Queries) CreateTeamInviteForUser(ctx context.Context, arg CreateTeamInviteForUserParams) (TeamInvite, error)

Invite tokens follow the same shape as api_tokens: plaintext shared via the URL only, sha256 hex persisted. Consume is a single atomic UPDATE so two concurrent claimants can never both succeed (Issue 14 race test). Membership-gated: caller must be in the team they're inviting into.

func (*Queries) CreateTeamMembership

func (q *Queries) CreateTeamMembership(ctx context.Context, arg CreateTeamMembershipParams) error

func (*Queries) CreateTeamMembershipIfMissing

func (q *Queries) CreateTeamMembershipIfMissing(ctx context.Context, arg CreateTeamMembershipIfMissingParams) error

Used by invite-consume and signup-with-invite where "user already in this team" must be silently tolerated (Issue 10). Avoids the 25P02 abort that a raw PK-violation would cause inside a transaction.

func (*Queries) CreateUser

func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error)

func (*Queries) DeleteExpiredOAuthAccessTokens

func (q *Queries) DeleteExpiredOAuthAccessTokens(ctx context.Context) (int64, error)

Called by cmd/maintenance. Sweeps tokens past their expires_at. Revoked- but-not-yet-expired rows are deliberately left alone: the 1h TTL means they vanish on the next sweep anyway, and keeping them preserves the option to surface a revoked-tokens audit view without a schema change.

func (*Queries) DeleteExpiredOAuthCodes

func (q *Queries) DeleteExpiredOAuthCodes(ctx context.Context) (int64, error)

Called by cmd/maintenance. Codes have a 10-min TTL and are one-shot, so any row past expires_at is unreachable (an active /oauth/token call would have already failed the GetActiveOAuthCodeByHash predicate). Used-but-not-yet- expired codes are kept until the same predicate sweeps them — keeps this query a single, unambiguous filter.

func (*Queries) DeleteExpiredSessions

func (q *Queries) DeleteExpiredSessions(ctx context.Context) (int64, error)

Called by cmd/maintenance. The sliding-expiry/hard-cap math runs at touch time, so a row whose expires_at has passed is permanently inert and safe to delete.

func (*Queries) DeleteFailedEvent

func (q *Queries) DeleteFailedEvent(ctx context.Context, id string) error

Removes a row after a successful drain replay.

func (*Queries) DeleteSession

func (q *Queries) DeleteSession(ctx context.Context, id string) error

func (*Queries) GetActiveIngestTokenByHash

func (q *Queries) GetActiveIngestTokenByHash(ctx context.Context, tokenHash string) (GetActiveIngestTokenByHashRow, error)

Resolves a mere_pub_* token hash to its project. Excludes soft-deleted projects so a deleted project can't keep accepting writes. Used by the ingest path's requirePublicToken middleware; the caller has already verified the PublicTokenPrefix so a non-prefix bearer never reaches here.

func (*Queries) GetActiveInviteByHash

func (q *Queries) GetActiveInviteByHash(ctx context.Context, tokenHash string) (GetActiveInviteByHashRow, error)

Used by the GET /invites/:t confirmation page; expired or consumed → no row.

func (*Queries) GetActiveOAuthAccessTokenByHash

func (q *Queries) GetActiveOAuthAccessTokenByHash(ctx context.Context, tokenHash string) (OauthAccessToken, error)

Active = not revoked AND not expired. The unique partial index on (token_hash) WHERE revoked_at IS NULL keeps this on a single index probe.

func (*Queries) GetActiveOAuthCodeByHash

func (q *Queries) GetActiveOAuthCodeByHash(ctx context.Context, codeHash string) (OauthCode, error)

Read-side of the lookup-then-update. Returns the live row (not used, not expired) so the caller can validate client_id / redirect_uri / PKCE before committing the consume. pgx.ErrNoRows means unknown / expired / already used — all collapse to invalid_grant at the handler.

func (*Queries) GetOAuthClientByID

func (q *Queries) GetOAuthClientByID(ctx context.Context, id string) (OauthClient, error)

func (*Queries) GetProjectForUser

func (q *Queries) GetProjectForUser(ctx context.Context, arg GetProjectForUserParams) (Project, error)

All project queries are membership-gated via a JOIN on team_memberships. A row that doesn't belong to a team the viewer is in returns zero rows, which the viewer translates to auth.ErrNotVisible (Issue 6). Soft-deleted rows (deleted_at IS NOT NULL) are excluded from every read.

func (*Queries) GetPublicTokenForProjectForUser

func (q *Queries) GetPublicTokenForProjectForUser(ctx context.Context, arg GetPublicTokenForProjectForUserParams) (ApiToken, error)

Public ingest token storage. Only one flavour left: the `mere_pub_…` snippet token that lives in client HTML. token_plaintext is intentionally persisted because the project page re-displays it on every visit; the partial unique index api_tokens_one_active_per_project_idx (migration 0007) enforces at most one active token per project so rotation = insert new + revoke old in one tx.

/v1/* + /mcp bearer auth has moved to oauth_access_tokens (migration 0006); secret_api tokens were retired with the api_tokens.kind column. Membership-gated read: only succeeds when the viewer is a member of the project's team and the project is not soft-deleted. Plaintext is returned because this token is public by design.

func (*Queries) GetSessionWithUser

func (q *Queries) GetSessionWithUser(ctx context.Context, id string) (GetSessionWithUserRow, error)

func (*Queries) GetTeamByID

func (q *Queries) GetTeamByID(ctx context.Context, id string) (Team, error)

func (*Queries) GetTeamForUser

func (q *Queries) GetTeamForUser(ctx context.Context, arg GetTeamForUserParams) (Team, error)

Membership-gated read used by viewer.Teams().ByID(). Zero rows → ErrNotVisible.

func (*Queries) GetUserByEmail

func (q *Queries) GetUserByEmail(ctx context.Context, lower string) (User, error)

func (*Queries) GetUserByID

func (q *Queries) GetUserByID(ctx context.Context, id string) (User, error)

func (*Queries) IncrementFailedEventAttempt

func (q *Queries) IncrementFailedEventAttempt(ctx context.Context, arg IncrementFailedEventAttemptParams) error

Recorded after a drain CH-insert attempt fails. The fresh last_error displaces the previous one — only the most recent failure matters for triage.

func (*Queries) InsertFailedEvent

func (q *Queries) InsertFailedEvent(ctx context.Context, arg InsertFailedEventParams) error

Dead-letter queue for events the flusher couldn't deliver to ClickHouse. One row per failed flush, storing the entire validated batch as JSONB so the drain goroutine can retry without losing event boundaries.

Drain progress: oldest-first via failed_events_drain_idx (partial WHERE quarantined_at IS NULL). After 20 attempts OR 24h age the row quarantines and is left alone; a future cmd/maintenance sweep reaps quarantined rows. Called by the flusher after a CH insert fails. id is a UUID v7 from idgen.New(); batch_payload carries the validated Event slice as JSON; the last_error column records the CH error string for forensics.

func (*Queries) InsertOAuthAccessToken

func (q *Queries) InsertOAuthAccessToken(ctx context.Context, arg InsertOAuthAccessTokenParams) error

Access tokens: opaque random bytes, only sha256 stored. RequireBearer looks the token up by hash and applies the expiry + revocation filters in SQL so the index does the work.

func (*Queries) InsertOAuthClient

func (q *Queries) InsertOAuthClient(ctx context.Context, arg InsertOAuthClientParams) (OauthClient, error)

OAuth client registry: minimal RFC 7591 surface. Public clients only (no client_secret), so the row carries only the identity + the redirect-URI allowlist. Redirect URIs are matched exactly at /oauth/authorize; the application layer applies the additional scheme/host rules (HTTPS or localhost) at registration time.

func (*Queries) InsertOAuthCode

func (q *Queries) InsertOAuthCode(ctx context.Context, arg InsertOAuthCodeParams) error

Authorization codes: short-lived (10 min), one-shot. Consumption is a two-step lookup-then-update so the application layer can validate the bound (client_id, redirect_uri, PKCE) tuple BEFORE the row is burnt — a failed PKCE check leaves the code intact for a legitimate retry rather than forcing the user back through /oauth/authorize. One-shot is still enforced by MarkOAuthCodeUsed's `WHERE used_at IS NULL`: a concurrent /oauth/token call racing on the same row produces RowsAffected == 0 on the loser, which the handler maps to invalid_grant.

expires_at filtering happens in SQL on the lookup so the partial index continues to do the work; the application never sees expired rows.

This file pairs with internal/oauth/codes.go.

func (*Queries) InsertPublicAPIToken

func (q *Queries) InsertPublicAPIToken(ctx context.Context, arg InsertPublicAPITokenParams) error

Bootstraps the project's public_ingest token. Called from inside the project-create transaction (auth.Service.createProjectWithPublicToken), so no membership EXISTS guard is needed — the project row was just inserted in the same tx by a query that already enforced membership.

func (*Queries) IsMemberOfTeam

func (q *Queries) IsMemberOfTeam(ctx context.Context, arg IsMemberOfTeamParams) (bool, error)

Cheap predicate for the invite-confirm page ("you are already a member").

func (*Queries) ListActiveAccessTokensForUser

func (q *Queries) ListActiveAccessTokensForUser(ctx context.Context, userID string) ([]ListActiveAccessTokensForUserRow, error)

Future "connected apps" page. Returns the joinable surface (client name + project id + scope + lifecycle timestamps) for the viewer's active grants.

func (*Queries) ListFailedEventsForDrain

func (q *Queries) ListFailedEventsForDrain(ctx context.Context, limit int32) ([]FailedEvent, error)

Oldest-first slice for the drain goroutine. The LIMIT is bounded by INGEST_DLQ_DRAIN_BATCH_LIMIT so a deep DLQ doesn't starve other work.

func (*Queries) ListMembersForTeamForUser

func (q *Queries) ListMembersForTeamForUser(ctx context.Context, arg ListMembersForTeamForUserParams) ([]ListMembersForTeamForUserRow, error)

Returns members of $1 if the caller ($2) is themselves a member; otherwise zero rows (which surfaces as ErrNotVisible at the viewer).

func (*Queries) ListProjectsForTeamForUser

func (q *Queries) ListProjectsForTeamForUser(ctx context.Context, arg ListProjectsForTeamForUserParams) ([]Project, error)

func (*Queries) ListProjectsForTeamsForUser

func (q *Queries) ListProjectsForTeamsForUser(ctx context.Context, arg ListProjectsForTeamsForUserParams) ([]Project, error)

Used by the rebuilt home page (Issue 15): bounded 2-query pattern.

func (*Queries) ListTeamsForUser

func (q *Queries) ListTeamsForUser(ctx context.Context, userID string) ([]Team, error)

func (*Queries) MarkOAuthCodeUsed

func (q *Queries) MarkOAuthCodeUsed(ctx context.Context, id string) (int64, error)

Write-side of the lookup-then-update. The `used_at IS NULL` predicate is the one-shot guard: a parallel /oauth/token call that also validated the same code races on this UPDATE; exactly one row is touched and the loser sees RowsAffected == 0.

func (*Queries) QuarantineFailedEvent

func (q *Queries) QuarantineFailedEvent(ctx context.Context, id string) error

Marks a row as accepted-data-loss after the retry budget is exhausted (>=20 attempts OR >24h age). The drain index excludes quarantined rows.

func (*Queries) RevokeOAuthAccessToken

func (q *Queries) RevokeOAuthAccessToken(ctx context.Context, tokenHash string) (int64, error)

func (*Queries) SoftDeleteProjectForUser

func (q *Queries) SoftDeleteProjectForUser(ctx context.Context, arg SoftDeleteProjectForUserParams) (int64, error)

Returns RowsAffected; 0 means either not-a-member or already-deleted, both of which collapse to ErrNotVisible at the viewer.

func (*Queries) TouchSession

func (q *Queries) TouchSession(ctx context.Context, arg TouchSessionParams) error

func (*Queries) UpdateOAuthAccessTokenLastUsed

func (q *Queries) UpdateOAuthAccessTokenLastUsed(ctx context.Context, id string) error

Stamped fire-and-forget by RequireBearer after every successful bearer lookup. The 60s predicate is the entire throttle: bounds WAL + lock contention once /v1/query and /mcp are hot paths without an extra round trip. Granularity is intentional — the connected-apps UI shows "minutes ago" precision.

func (*Queries) UpdateUserPassword

func (q *Queries) UpdateUserPassword(ctx context.Context, arg UpdateUserPasswordParams) error

func (*Queries) WithTx

func (q *Queries) WithTx(tx pgx.Tx) *Queries

type Session

type Session struct {
	ID        string
	UserID    string
	ExpiresAt pgtype.Timestamptz
	CreatedAt pgtype.Timestamptz
	CsrfToken string
}

type SoftDeleteProjectForUserParams

type SoftDeleteProjectForUserParams struct {
	ID     string
	UserID string
}

type Team

type Team struct {
	ID        string
	Name      string
	CreatedAt pgtype.Timestamptz
}

type TeamInvite

type TeamInvite struct {
	ID         string
	TeamID     string
	CreatedBy  string
	TokenHash  string
	ExpiresAt  pgtype.Timestamptz
	ConsumedAt pgtype.Timestamptz
	ConsumedBy *string
	CreatedAt  pgtype.Timestamptz
}

type TeamMembership

type TeamMembership struct {
	TeamID   string
	UserID   string
	JoinedAt pgtype.Timestamptz
}

type TouchSessionParams

type TouchSessionParams struct {
	ID        string
	ExpiresAt pgtype.Timestamptz
}

type UpdateUserPasswordParams

type UpdateUserPasswordParams struct {
	ID                 string
	PasswordHash       string
	MustChangePassword bool
}

type User

type User struct {
	ID                 string
	Email              string
	PasswordHash       string
	MustChangePassword bool
	CreatedAt          pgtype.Timestamptz
	UpdatedAt          pgtype.Timestamptz
}

Jump to

Keyboard shortcuts

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