sqlbstore

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package sqlbstore is a generic bridge between authit's DB-agnostic store interfaces and sqlb-generated row types. It is its own Go module (separate go.mod, requiring both authit and sqlb) so that authit's root module never gains an sqlb/pgx dependency — the same reason sqlb itself keeps its own auth-provider adapters under example/ rather than in its core module.

Table[R, T] is the shared primitive: given a two-way conversion between an app's row type R (e.g. a sqlb-generated struct like mybrain.APIToken) and authit's canonical type T (e.g. store.PersonalAccessToken), plus a small amount of column-name config, it implements the CRUD mechanics every authit store interface needs — so a concrete per-interface adapter (see pat.go for PersonalAccessTokenAdapter) only has to supply that conversion and config, not hand-write Create/Get/List/Update against sqlb's query builder.

Create goes through sqlb's InsertRows; Update goes through raw UpdateRows(...).Set(...) rather than an upsert — see ToUpdateColumns for why they can't share one code path despite both starting from the same T.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DeviceAuthorizationAdapter

type DeviceAuthorizationAdapter[R any] struct {
	Table[R, store.DeviceAuthorization]
	DB                   sqlb.Executor
	DeviceCodeHashColumn string
	UserCodeColumn       string
}

DeviceAuthorizationAdapter implements store.DeviceAuthorizationStore over an app's sqlb row type R.

func (DeviceAuthorizationAdapter[R]) CreateDeviceAuthorization

func (a DeviceAuthorizationAdapter[R]) CreateDeviceAuthorization(ctx context.Context, d *store.DeviceAuthorization) error

func (DeviceAuthorizationAdapter[R]) DeleteDeviceAuthorization

func (a DeviceAuthorizationAdapter[R]) DeleteDeviceAuthorization(ctx context.Context, id string) error

func (DeviceAuthorizationAdapter[R]) GetDeviceAuthorizationByDeviceCodeHash

func (a DeviceAuthorizationAdapter[R]) GetDeviceAuthorizationByDeviceCodeHash(ctx context.Context, hash string) (*store.DeviceAuthorization, error)

func (DeviceAuthorizationAdapter[R]) GetDeviceAuthorizationByUserCode

func (a DeviceAuthorizationAdapter[R]) GetDeviceAuthorizationByUserCode(ctx context.Context, userCode string) (*store.DeviceAuthorization, error)

func (DeviceAuthorizationAdapter[R]) UpdateDeviceAuthorization

func (a DeviceAuthorizationAdapter[R]) UpdateDeviceAuthorization(ctx context.Context, d *store.DeviceAuthorization) error

type EmailVerificationAdapter

type EmailVerificationAdapter[R any] struct {
	Table[R, store.EmailVerificationToken]
	DB              sqlb.Executor
	UserIDColumn    string
	TokenHashColumn string
	UsedAtColumn    string
}

EmailVerificationAdapter implements store.EmailVerificationStore over an app's sqlb row type R.

func (EmailVerificationAdapter[R]) CreateEmailVerificationToken

func (a EmailVerificationAdapter[R]) CreateEmailVerificationToken(ctx context.Context, t *store.EmailVerificationToken) error

func (EmailVerificationAdapter[R]) DeleteUserEmailVerificationTokens

func (a EmailVerificationAdapter[R]) DeleteUserEmailVerificationTokens(ctx context.Context, userID string) error

func (EmailVerificationAdapter[R]) GetEmailVerificationTokenByHash

func (a EmailVerificationAdapter[R]) GetEmailVerificationTokenByHash(ctx context.Context, hash string) (*store.EmailVerificationToken, error)

func (EmailVerificationAdapter[R]) MarkEmailVerificationTokenUsed

func (a EmailVerificationAdapter[R]) MarkEmailVerificationTokenUsed(ctx context.Context, id string) error

type InvitationAdapter

type InvitationAdapter[R any] struct {
	Table[R, store.Invitation]
	DB              sqlb.Executor
	TeamIDColumn    string
	TokenHashColumn string
}

InvitationAdapter implements store.InvitationStore over an app's sqlb row type R.

func (InvitationAdapter[R]) CreateInvitation

func (a InvitationAdapter[R]) CreateInvitation(ctx context.Context, i *store.Invitation) error

func (InvitationAdapter[R]) GetInvitation

func (a InvitationAdapter[R]) GetInvitation(ctx context.Context, id string) (*store.Invitation, error)

func (InvitationAdapter[R]) GetInvitationByTokenHash

func (a InvitationAdapter[R]) GetInvitationByTokenHash(ctx context.Context, hash string) (*store.Invitation, error)

func (InvitationAdapter[R]) ListInvitationsByTeam

func (a InvitationAdapter[R]) ListInvitationsByTeam(ctx context.Context, teamID string) ([]*store.Invitation, error)

func (InvitationAdapter[R]) UpdateInvitation

func (a InvitationAdapter[R]) UpdateInvitation(ctx context.Context, i *store.Invitation) error

type LockoutAdapter

type LockoutAdapter[A, L any] struct {
	Attempts               Table[A, store.FailedLoginAttempt]
	AttemptsDB             sqlb.Executor
	AttemptEmailColumn     string
	AttemptCreatedAtColumn string

	LocksDB          sqlb.Executor
	NewLockRow       func(userID string) L
	LockUserIDColumn string
}

LockoutAdapter implements store.LockoutStore over two app tables: A for failed-login attempts (fits Table[A, store.FailedLoginAttempt] like every other adapter) and L for the set of currently-locked accounts.

L has no canonical authit type — LockAccount/IsAccountLocked/ UnlockAccount are pure existence operations ("is this user_id present in the locks table"), not a CRUD-over-a-struct concern — so unlike every other adapter in this package, L's shape is entirely the host app's own (NewLockRow constructs whatever row it needs, e.g. just a user_id column and a locked_at default), and Lock/IsLocked/Unlock talk to it with raw sqlb calls rather than through Table.

func (LockoutAdapter[A, L]) ClearFailedLoginAttempts

func (a LockoutAdapter[A, L]) ClearFailedLoginAttempts(ctx context.Context, email string) error

func (LockoutAdapter[A, L]) CountRecentFailedLoginAttempts

func (a LockoutAdapter[A, L]) CountRecentFailedLoginAttempts(ctx context.Context, email string, since time.Time) (int, error)

func (LockoutAdapter[A, L]) IsAccountLocked

func (a LockoutAdapter[A, L]) IsAccountLocked(ctx context.Context, userID string) (bool, error)

func (LockoutAdapter[A, L]) LockAccount

func (a LockoutAdapter[A, L]) LockAccount(ctx context.Context, userID string) error

func (LockoutAdapter[A, L]) RecordFailedLoginAttempt

func (a LockoutAdapter[A, L]) RecordFailedLoginAttempt(ctx context.Context, at *store.FailedLoginAttempt) error

func (LockoutAdapter[A, L]) UnlockAccount

func (a LockoutAdapter[A, L]) UnlockAccount(ctx context.Context, userID string) error

type MemberAdapter

type MemberAdapter[R any] struct {
	Table[R, store.Member]
	DB           sqlb.Executor
	TeamIDColumn string
	UserIDColumn string
}

MemberAdapter implements store.MemberStore over an app's sqlb row type R.

func (MemberAdapter[R]) CreateMember

func (a MemberAdapter[R]) CreateMember(ctx context.Context, m *store.Member) error

func (MemberAdapter[R]) DeleteMember

func (a MemberAdapter[R]) DeleteMember(ctx context.Context, id string) error

func (MemberAdapter[R]) GetMember

func (a MemberAdapter[R]) GetMember(ctx context.Context, id string) (*store.Member, error)

func (MemberAdapter[R]) GetMemberByUserAndTeam

func (a MemberAdapter[R]) GetMemberByUserAndTeam(ctx context.Context, userID, teamID string) (*store.Member, error)

func (MemberAdapter[R]) ListMembersByTeam

func (a MemberAdapter[R]) ListMembersByTeam(ctx context.Context, teamID string) ([]*store.Member, error)

func (MemberAdapter[R]) ListMembershipsByUser

func (a MemberAdapter[R]) ListMembershipsByUser(ctx context.Context, userID string) ([]*store.Member, error)

func (MemberAdapter[R]) UpdateMember

func (a MemberAdapter[R]) UpdateMember(ctx context.Context, m *store.Member) error

type PasswordResetAdapter

type PasswordResetAdapter[R any] struct {
	Table[R, store.PasswordResetToken]
	DB              sqlb.Executor
	UserIDColumn    string
	TokenHashColumn string
	UsedAtColumn    string
}

PasswordResetAdapter implements store.PasswordResetStore over an app's sqlb row type R.

func (PasswordResetAdapter[R]) CreatePasswordResetToken

func (a PasswordResetAdapter[R]) CreatePasswordResetToken(ctx context.Context, t *store.PasswordResetToken) error

func (PasswordResetAdapter[R]) DeleteUserPasswordResetTokens

func (a PasswordResetAdapter[R]) DeleteUserPasswordResetTokens(ctx context.Context, userID string) error

func (PasswordResetAdapter[R]) GetPasswordResetTokenByHash

func (a PasswordResetAdapter[R]) GetPasswordResetTokenByHash(ctx context.Context, hash string) (*store.PasswordResetToken, error)

func (PasswordResetAdapter[R]) MarkPasswordResetTokenUsed

func (a PasswordResetAdapter[R]) MarkPasswordResetTokenUsed(ctx context.Context, id string) error

type PendingTwoFactorAdapter

type PendingTwoFactorAdapter[R any] struct {
	Table[R, store.PendingTwoFactorSession]
	DB              sqlb.Executor
	TokenHashColumn string
}

PendingTwoFactorAdapter implements store.PendingTwoFactorStore over an app's sqlb row type R.

func (PendingTwoFactorAdapter[R]) CreatePendingTwoFactorSession

func (a PendingTwoFactorAdapter[R]) CreatePendingTwoFactorSession(ctx context.Context, s *store.PendingTwoFactorSession) error

func (PendingTwoFactorAdapter[R]) DeletePendingTwoFactorSession

func (a PendingTwoFactorAdapter[R]) DeletePendingTwoFactorSession(ctx context.Context, id string) error

func (PendingTwoFactorAdapter[R]) GetPendingTwoFactorSessionByHash

func (a PendingTwoFactorAdapter[R]) GetPendingTwoFactorSessionByHash(ctx context.Context, hash string) (*store.PendingTwoFactorSession, error)

type PersonalAccessTokenAdapter

type PersonalAccessTokenAdapter[R any] struct {
	Table[R, store.PersonalAccessToken]
	DB sqlb.Executor
	// UserIDColumn and TokenHashColumn name the columns
	// ListPersonalAccessTokensByUser and GetPersonalAccessTokenByHash
	// filter on, respectively.
	UserIDColumn    string
	TokenHashColumn string
}

PersonalAccessTokenAdapter implements store.PersonalAccessTokenStore against an app's sqlb-generated row type R (e.g. mybrain.APIToken), given a Table[R, store.PersonalAccessToken] describing the mapping.

DB must be an unhooked executor: resolving a token is what establishes the identity a hook later scopes by, so it cannot itself run behind one — same requirement as every other identity-resolving query in a sqlb app.

func (PersonalAccessTokenAdapter[R]) CreatePersonalAccessToken

func (a PersonalAccessTokenAdapter[R]) CreatePersonalAccessToken(ctx context.Context, t *store.PersonalAccessToken) error

func (PersonalAccessTokenAdapter[R]) GetPersonalAccessToken

func (a PersonalAccessTokenAdapter[R]) GetPersonalAccessToken(ctx context.Context, id string) (*store.PersonalAccessToken, error)

func (PersonalAccessTokenAdapter[R]) GetPersonalAccessTokenByHash

func (a PersonalAccessTokenAdapter[R]) GetPersonalAccessTokenByHash(ctx context.Context, hash string) (*store.PersonalAccessToken, error)

func (PersonalAccessTokenAdapter[R]) ListPersonalAccessTokensByUser

func (a PersonalAccessTokenAdapter[R]) ListPersonalAccessTokensByUser(ctx context.Context, userID string) ([]*store.PersonalAccessToken, error)

func (PersonalAccessTokenAdapter[R]) UpdatePersonalAccessToken

func (a PersonalAccessTokenAdapter[R]) UpdatePersonalAccessToken(ctx context.Context, t *store.PersonalAccessToken) error

type RefreshTokenAdapter

type RefreshTokenAdapter[R any] struct {
	Table[R, store.RefreshToken]
	DB sqlb.Executor
	// UserIDColumn, TokenHashColumn, RevokedAtColumn, ExpiresAtColumn name
	// the corresponding columns — needed beyond IDColumn because
	// RevokeAllUserRefreshTokens and ListActiveRefreshTokens filter on
	// more than the primary key.
	UserIDColumn    string
	TokenHashColumn string
	RevokedAtColumn string
	ExpiresAtColumn string
}

RefreshTokenAdapter implements store.RefreshTokenStore over an app's sqlb row type R.

func (RefreshTokenAdapter[R]) CreateRefreshToken

func (a RefreshTokenAdapter[R]) CreateRefreshToken(ctx context.Context, t *store.RefreshToken) error

func (RefreshTokenAdapter[R]) GetRefreshTokenByHash

func (a RefreshTokenAdapter[R]) GetRefreshTokenByHash(ctx context.Context, hash string) (*store.RefreshToken, error)

func (RefreshTokenAdapter[R]) ListActiveRefreshTokens

func (a RefreshTokenAdapter[R]) ListActiveRefreshTokens(ctx context.Context, userID string) ([]*store.RefreshToken, error)

func (RefreshTokenAdapter[R]) RevokeAllUserRefreshTokens

func (a RefreshTokenAdapter[R]) RevokeAllUserRefreshTokens(ctx context.Context, userID string) error

func (RefreshTokenAdapter[R]) RevokeRefreshToken

func (a RefreshTokenAdapter[R]) RevokeRefreshToken(ctx context.Context, id string) error

type SuperuserAdapter

type SuperuserAdapter[R any] struct {
	Table[R, store.Superuser]
	DB          sqlb.Executor
	EmailColumn string
}

SuperuserAdapter implements store.SuperuserStore over an app's sqlb row type R.

func (SuperuserAdapter[R]) CountSuperusers

func (a SuperuserAdapter[R]) CountSuperusers(ctx context.Context) (int, error)

func (SuperuserAdapter[R]) CreateSuperuser

func (a SuperuserAdapter[R]) CreateSuperuser(ctx context.Context, s *store.Superuser) error

func (SuperuserAdapter[R]) GetSuperuserByEmail

func (a SuperuserAdapter[R]) GetSuperuserByEmail(ctx context.Context, email string) (*store.Superuser, error)

func (SuperuserAdapter[R]) GetSuperuserByID

func (a SuperuserAdapter[R]) GetSuperuserByID(ctx context.Context, id string) (*store.Superuser, error)

func (SuperuserAdapter[R]) ListSuperusers

func (a SuperuserAdapter[R]) ListSuperusers(ctx context.Context) ([]*store.Superuser, error)

func (SuperuserAdapter[R]) UpdateSuperuser

func (a SuperuserAdapter[R]) UpdateSuperuser(ctx context.Context, s *store.Superuser) error

type SuperuserRefreshTokenAdapter

type SuperuserRefreshTokenAdapter[R any] struct {
	Table[R, store.SuperuserRefreshToken]
	DB                sqlb.Executor
	SuperuserIDColumn string
	TokenHashColumn   string
	RevokedAtColumn   string
}

SuperuserRefreshTokenAdapter implements store.SuperuserRefreshTokenStore over an app's sqlb row type R.

func (SuperuserRefreshTokenAdapter[R]) CreateSuperuserRefreshToken

func (a SuperuserRefreshTokenAdapter[R]) CreateSuperuserRefreshToken(ctx context.Context, t *store.SuperuserRefreshToken) error

func (SuperuserRefreshTokenAdapter[R]) GetSuperuserRefreshTokenByHash

func (a SuperuserRefreshTokenAdapter[R]) GetSuperuserRefreshTokenByHash(ctx context.Context, hash string) (*store.SuperuserRefreshToken, error)

func (SuperuserRefreshTokenAdapter[R]) RevokeAllSuperuserRefreshTokens

func (a SuperuserRefreshTokenAdapter[R]) RevokeAllSuperuserRefreshTokens(ctx context.Context, superuserID string) error

func (SuperuserRefreshTokenAdapter[R]) RevokeSuperuserRefreshToken

func (a SuperuserRefreshTokenAdapter[R]) RevokeSuperuserRefreshToken(ctx context.Context, id string) error

type TOTPAdapter

type TOTPAdapter[R any] struct {
	Table[R, store.TOTPSettings]
	DB           sqlb.Executor
	UserIDColumn string
}

TOTPAdapter implements store.TOTPStore over an app's sqlb row type R.

func (TOTPAdapter[R]) CreateTOTPSettings

func (a TOTPAdapter[R]) CreateTOTPSettings(ctx context.Context, t *store.TOTPSettings) error

func (TOTPAdapter[R]) DeleteTOTPSettings

func (a TOTPAdapter[R]) DeleteTOTPSettings(ctx context.Context, userID string) error

func (TOTPAdapter[R]) GetTOTPSettingsByUserID

func (a TOTPAdapter[R]) GetTOTPSettingsByUserID(ctx context.Context, userID string) (*store.TOTPSettings, error)

func (TOTPAdapter[R]) UpdateTOTPSettings

func (a TOTPAdapter[R]) UpdateTOTPSettings(ctx context.Context, t *store.TOTPSettings) error

type Table

type Table[R, T any] struct {
	// ToRow converts a fully-populated T into the row to insert, EXCEPT the
	// primary key — Create handles that separately via SetID, so ToRow's
	// own handling of R's ID field is irrelevant and can be left however's
	// convenient. Used only by Create; Update uses ToUpdateColumns
	// instead (see there for why they aren't the same function).
	ToRow func(T) R
	// FromRow converts a row read back from the database into T. Called
	// after every Create/Update/Get/List, so DB-generated values (a
	// defaulted ID, created_at) make it into the value the caller
	// receives even though ToRow never sent them.
	FromRow func(R) T
	// GetID extracts T's primary key.
	GetID func(T) string
	// SetID returns a copy of row with its primary key column set to id.
	// Create calls this with "" (so a defaulted column, e.g. a generated
	// UUIDv7, is left to the database rather than whatever the row
	// happened to hold), then inserts — sqlb's InsertRows only writes an
	// explicit value for a database-defaulted column when the Go field is
	// non-zero, so leaving ID at its zero value here is what makes the
	// database generate one.
	SetID func(R, string) R
	// IDColumn is the row's primary key column name — used by GetByID and
	// as Update's WHERE clause.
	IDColumn string
	// ToUpdateColumns extracts exactly the (column name, value) pairs
	// Update should write, as explicit bound parameters via
	// UpdateRows(...).Set(...) — deliberately NOT built from ToRow's
	// output. ToRow feeds Create's InsertRows, which treats a Go zero
	// value on a database-defaulted column as "leave this to the
	// database" (so a fresh row's ID/created_at defer to their defaults,
	// as intended) — but Update has no such thing as "leave it unset": v
	// is always an already-fetched, already-mutated value, so a field
	// that's genuinely false/0/"" must be written as exactly that. Reusing
	// ToRow for Update silently reverts any explicitly-zero,
	// database-defaulted column (e.g. an `is_active boolean DEFAULT true`
	// column being set to false) back to its default, with no error —
	// caught by this package's own test suite before it shipped.
	ToUpdateColumns func(T) map[string]any
}

Table binds authit's canonical type T to an app's sqlb row type R.

func (Table[R, T]) CountWhere

func (t Table[R, T]) CountWhere(ctx context.Context, db sqlb.Executor, preds ...sqlb.Pred) (int64, error)

CountWhere counts rows matching every pred (AND'd together).

func (Table[R, T]) Create

func (t Table[R, T]) Create(ctx context.Context, db sqlb.Executor, v T) (T, error)

Create inserts v and returns what was actually persisted (DB defaults included).

func (Table[R, T]) Delete

func (t Table[R, T]) Delete(ctx context.Context, db sqlb.Executor, id string) error

Delete removes the row identified by id (IDColumn). Idempotent: deleting an already-gone row is not an error.

func (Table[R, T]) DeleteWhere

func (t Table[R, T]) DeleteWhere(ctx context.Context, db sqlb.Executor, preds ...sqlb.Pred) (int64, error)

DeleteWhere removes every row matching every pred (AND'd together). Idempotent: matching zero rows is not an error — a caller wanting "was anything actually deleted" should check the returned count.

func (Table[R, T]) ExistsWhere

func (t Table[R, T]) ExistsWhere(ctx context.Context, db sqlb.Executor, preds ...sqlb.Pred) (bool, error)

ExistsWhere reports whether any row matches every pred (AND'd together).

func (Table[R, T]) GetBy

func (t Table[R, T]) GetBy(ctx context.Context, db sqlb.Executor, column string, value any) (T, error)

GetBy returns the row where column equals value, or store.ErrNotFound. Sugar for the common single-column case of GetWhere.

func (Table[R, T]) GetWhere

func (t Table[R, T]) GetWhere(ctx context.Context, db sqlb.Executor, preds ...sqlb.Pred) (T, error)

GetWhere returns the row matching every pred (AND'd together), or store.ErrNotFound — for lookups GetBy's single-column shape can't express, e.g. a compound key (user_id = ? AND team_id = ?).

func (Table[R, T]) ListBy

func (t Table[R, T]) ListBy(ctx context.Context, db sqlb.Executor, column string, value any) ([]T, error)

ListBy returns every row where column equals value. Sugar for the common single-column case of ListWhere.

func (Table[R, T]) ListWhere

func (t Table[R, T]) ListWhere(ctx context.Context, db sqlb.Executor, preds ...sqlb.Pred) ([]T, error)

ListWhere returns every row matching every pred (AND'd together).

func (Table[R, T]) SetColumnWhere

func (t Table[R, T]) SetColumnWhere(ctx context.Context, db sqlb.Executor, column string, value any, preds ...sqlb.Pred) error

SetColumnWhere writes value to column on every row matching every pred (AND'd together) — for a bulk state change across many rows at once (e.g. "revoke every refresh token for this user") that isn't shaped like Update's "one row, identified by ID." Same explicit-bound-parameter mechanism as Update, so it has no zero-value trap either.

func (Table[R, T]) Update

func (t Table[R, T]) Update(ctx context.Context, db sqlb.Executor, v T) error

Update writes exactly ToUpdateColumns(v) to the row identified by GetID(v).

type TeamAdapter

type TeamAdapter[R any] struct {
	Table[R, store.Team]
	DB sqlb.Executor
	// SlugColumn names the column GetTeamBySlug filters on.
	SlugColumn string
}

TeamAdapter implements store.TeamStore over an app's sqlb row type R.

func (TeamAdapter[R]) CreateTeam

func (a TeamAdapter[R]) CreateTeam(ctx context.Context, t *store.Team) error

func (TeamAdapter[R]) DeleteTeam

func (a TeamAdapter[R]) DeleteTeam(ctx context.Context, id string) error

func (TeamAdapter[R]) GetTeam

func (a TeamAdapter[R]) GetTeam(ctx context.Context, id string) (*store.Team, error)

func (TeamAdapter[R]) GetTeamBySlug

func (a TeamAdapter[R]) GetTeamBySlug(ctx context.Context, slug string) (*store.Team, error)

func (TeamAdapter[R]) UpdateTeam

func (a TeamAdapter[R]) UpdateTeam(ctx context.Context, t *store.Team) error

type UserAdapter

type UserAdapter[R any] struct {
	Table[R, store.User]
	DB sqlb.Executor
	// EmailColumn names the column GetUserByEmail filters on.
	EmailColumn string
}

UserAdapter implements store.UserStore over an app's sqlb row type R.

func (UserAdapter[R]) CreateUser

func (a UserAdapter[R]) CreateUser(ctx context.Context, u *store.User) error

func (UserAdapter[R]) GetUserByEmail

func (a UserAdapter[R]) GetUserByEmail(ctx context.Context, email string) (*store.User, error)

func (UserAdapter[R]) GetUserByID

func (a UserAdapter[R]) GetUserByID(ctx context.Context, id string) (*store.User, error)

func (UserAdapter[R]) UpdateUser

func (a UserAdapter[R]) UpdateUser(ctx context.Context, u *store.User) error

Jump to

Keyboard shortcuts

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