serverdb

package
v0.32.0 Latest Latest
Warning

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

Go to latest
Published: Feb 9, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	RoleOwner  = "owner"
	RoleWriter = "writer"
	RoleReader = "reader"
)

Role constants

View Source
const (
	AuthEventStarted      = "started"
	AuthEventCodeVerified = "code_verified"
	AuthEventKeyIssued    = "key_issued"
	AuthEventExpired      = "expired"
	AuthEventFailed       = "failed"
)

Auth event type constants.

View Source
const (
	AuthStatusPending  = "pending"
	AuthStatusVerified = "verified"
	AuthStatusExpired  = "expired"
	AuthStatusUsed     = "used"
	AuthRequestTTL     = 15 * time.Minute
	PollInterval       = 5
)
View Source
const (
	DefaultPageLimit = 50
	MaxPageLimit     = 200
)
View Source
const ServerSchemaVersion = 3

ServerSchemaVersion is the current server database schema version

Variables

View Source
var Migrations = []Migration{

	{
		Version:     2,
		Description: "Add auth_requests table for device auth flow",
		SQL: `CREATE TABLE IF NOT EXISTS auth_requests (
			id TEXT PRIMARY KEY,
			email TEXT NOT NULL,
			device_code TEXT UNIQUE NOT NULL,
			user_code TEXT UNIQUE NOT NULL,
			status TEXT NOT NULL DEFAULT 'pending',
			user_id TEXT,
			api_key_id TEXT,
			expires_at DATETIME NOT NULL,
			verified_at DATETIME,
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
		);
		CREATE INDEX IF NOT EXISTS idx_auth_requests_device_code ON auth_requests(device_code);
		CREATE INDEX IF NOT EXISTS idx_auth_requests_user_code ON auth_requests(user_code);
		CREATE INDEX IF NOT EXISTS idx_auth_requests_status ON auth_requests(status);
		CREATE INDEX IF NOT EXISTS idx_auth_requests_cleanup ON auth_requests(status, expires_at);`,
	},
	{
		Version:     3,
		Description: "Add is_admin, auth_events, rate_limit_events, project event caching",
		SQL: `ALTER TABLE users ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT 0;

		CREATE TABLE IF NOT EXISTS auth_events (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			auth_request_id TEXT NOT NULL,
			email TEXT NOT NULL,
			event_type TEXT NOT NULL,
			metadata TEXT DEFAULT '{}',
			created_at DATETIME DEFAULT CURRENT_TIMESTAMP
		);
		CREATE INDEX IF NOT EXISTS idx_auth_events_type ON auth_events(event_type);
		CREATE INDEX IF NOT EXISTS idx_auth_events_email ON auth_events(email);
		CREATE INDEX IF NOT EXISTS idx_auth_events_created ON auth_events(created_at);

		CREATE TABLE IF NOT EXISTS rate_limit_events (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			key_id TEXT,
			ip TEXT,
			endpoint_class TEXT NOT NULL,
			created_at DATETIME DEFAULT CURRENT_TIMESTAMP
		);
		CREATE INDEX IF NOT EXISTS idx_rle_created ON rate_limit_events(created_at);
		CREATE INDEX IF NOT EXISTS idx_rle_key ON rate_limit_events(key_id);

		ALTER TABLE projects ADD COLUMN event_count INTEGER NOT NULL DEFAULT 0;
		ALTER TABLE projects ADD COLUMN last_event_at DATETIME;`,
	},
}

Migrations is the list of all server database migrations in order

Functions

func EncodeCursor added in v0.32.0

func EncodeCursor(data CursorData) string

EncodeCursor encodes cursor data to an opaque base64 string.

func NewID

func NewID() string

NewID generates a project ID (exported for callers that need to pre-generate IDs).

func NormalizeLimit added in v0.32.0

func NormalizeLimit(limit int) int

NormalizeLimit clamps limit to valid range.

Types

type APIKey

type APIKey struct {
	ID         string
	UserID     string
	KeyPrefix  string
	Name       string
	Scopes     string
	ExpiresAt  *time.Time
	LastUsedAt *time.Time
	CreatedAt  time.Time
}

APIKey represents a stored API key (without the plaintext secret).

type AdminProject added in v0.32.0

type AdminProject struct {
	ID          string  `json:"id"`
	Name        string  `json:"name"`
	Description string  `json:"description,omitempty"`
	EventCount  int     `json:"event_count"`
	LastEventAt *string `json:"last_event_at"`
	MemberCount int     `json:"member_count"`
	CreatedAt   string  `json:"created_at"`
	UpdatedAt   string  `json:"updated_at"`
	DeletedAt   *string `json:"deleted_at,omitempty"`
}

AdminProject represents a project with aggregate info for admin API.

type AdminProjectMember added in v0.32.0

type AdminProjectMember struct {
	UserID    string `json:"user_id"`
	Email     string `json:"email"`
	Role      string `json:"role"`
	InvitedBy string `json:"invited_by"`
	CreatedAt string `json:"created_at"`
}

AdminProjectMember represents a project member with user email for admin view.

type AdminUser added in v0.32.0

type AdminUser struct {
	ID           string  `json:"id"`
	Email        string  `json:"email"`
	IsAdmin      bool    `json:"is_admin"`
	CreatedAt    string  `json:"created_at"`
	ProjectCount int     `json:"project_count"`
	LastActivity *string `json:"last_activity"`
}

AdminUser represents a user with aggregate info for the admin API.

type AdminUserDetail added in v0.32.0

type AdminUserDetail struct {
	AdminUser
	Projects []UserProject `json:"projects"`
}

AdminUserDetail extends AdminUser with project membership details.

type AuthEvent added in v0.32.0

type AuthEvent struct {
	ID            int64  `json:"id"`
	AuthRequestID string `json:"auth_request_id"`
	Email         string `json:"email"`
	EventType     string `json:"event_type"`
	Metadata      string `json:"metadata"`
	CreatedAt     string `json:"created_at"`
}

AuthEvent represents a row in the auth_events table.

type AuthRequest

type AuthRequest struct {
	ID         string
	Email      string
	DeviceCode string
	UserCode   string
	Status     string
	UserID     *string
	APIKeyID   *string
	ExpiresAt  time.Time
	VerifiedAt *time.Time
	CreatedAt  time.Time
}

AuthRequest represents a device authorization request.

type CursorData added in v0.32.0

type CursorData struct {
	ID        string `json:"id,omitempty"`
	CreatedAt string `json:"created_at,omitempty"`
	Value     string `json:"value,omitempty"`
}

CursorData holds the opaque cursor state.

func DecodeCursor added in v0.32.0

func DecodeCursor(cursor string) (CursorData, error)

DecodeCursor decodes an opaque cursor string back to CursorData.

type Membership

type Membership struct {
	ProjectID string
	UserID    string
	Role      string
	InvitedBy string
	CreatedAt time.Time
}

Membership represents a user's role in a project.

type Migration

type Migration struct {
	Version     int
	Description string
	SQL         string
}

Migration defines a server database migration

type PaginatedResult added in v0.32.0

type PaginatedResult[T any] struct {
	Data       []T    `json:"data"`
	NextCursor string `json:"next_cursor,omitempty"`
	HasMore    bool   `json:"has_more"`
}

PaginatedResult holds a page of results with cursor information.

func PaginatedQuery added in v0.32.0

func PaginatedQuery[T any](
	db *sql.DB,
	baseQuery string,
	args []any,
	limit int,
	cursor string,
	cursorColumn string,
	scanRow func(*sql.Rows) (T, string, error),
) (*PaginatedResult[T], error)

PaginatedQuery executes a paginated query using cursor-based pagination.

baseQuery is the SELECT query without ORDER BY or LIMIT clauses. args contains any existing WHERE clause parameters. cursor is the opaque cursor string from a previous PaginatedResult.NextCursor. cursorColumn is the column used for ordering and cursor comparison (e.g. "id", "created_at"). scanRow scans a single row and returns (item, cursorValue, error) where cursorValue is the value to encode in the next cursor.

The function fetches limit+1 rows to determine HasMore without a separate COUNT query.

type Project

type Project struct {
	ID          string
	Name        string
	Description string
	EventCount  int
	LastEventAt *time.Time
	CreatedAt   time.Time
	UpdatedAt   time.Time
	DeletedAt   *time.Time
}

Project represents a sync project.

type RateLimitEvent added in v0.32.0

type RateLimitEvent struct {
	ID            int64
	KeyID         string // empty string if IP-based (nullable in DB)
	IP            string
	EndpointClass string // auth, push, pull, other
	CreatedAt     string
}

RateLimitEvent represents a rate limit violation event.

type ServerDB

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

ServerDB wraps the server database connection

func Open

func Open(dbPath string) (*ServerDB, error)

Open opens the server database and runs any pending migrations. If the database file does not exist, it is created and initialized.

func (*ServerDB) AddMember

func (db *ServerDB) AddMember(projectID, userID, role, invitedByUserID string) (*Membership, error)

AddMember adds a user to a project with the given role.

func (*ServerDB) AdminGetProject added in v0.32.0

func (db *ServerDB) AdminGetProject(id string) (*AdminProject, error)

AdminGetProject returns a single project with aggregate info including description.

func (*ServerDB) AdminGetUser added in v0.32.0

func (db *ServerDB) AdminGetUser(id string) (*AdminUserDetail, error)

AdminGetUser returns a single user with aggregate info and project memberships.

func (*ServerDB) AdminListProjectMembers added in v0.32.0

func (db *ServerDB) AdminListProjectMembers(projectID string) ([]AdminProjectMember, error)

AdminListProjectMembers returns all members of a project with user email.

func (*ServerDB) AdminListProjects added in v0.32.0

func (db *ServerDB) AdminListProjects(query string, includeDeleted bool, limit int, cursor string) (*PaginatedResult[AdminProject], error)

AdminListProjects returns a paginated list of all projects with aggregate counts.

func (*ServerDB) AdminListUsers added in v0.32.0

func (db *ServerDB) AdminListUsers(query string, limit int, cursor string) (*PaginatedResult[AdminUser], error)

AdminListUsers returns a paginated list of users with aggregate counts. The query parameter filters by email (LIKE match).

func (*ServerDB) Authorize

func (db *ServerDB) Authorize(projectID, userID, requiredRole string) error

Authorize checks that the user has at least the required role in the project.

func (*ServerDB) CanDeleteProject

func (db *ServerDB) CanDeleteProject(projectID, userID string) error

CanDeleteProject checks if the user can delete the project (requires owner role).

func (*ServerDB) CanManageMembers

func (db *ServerDB) CanManageMembers(projectID, userID string) error

CanManageMembers checks if the user can manage members (requires owner role).

func (*ServerDB) CanPullEvents

func (db *ServerDB) CanPullEvents(projectID, userID string) error

CanPullEvents checks if the user can pull events (requires reader role).

func (*ServerDB) CanPushEvents

func (db *ServerDB) CanPushEvents(projectID, userID string) error

CanPushEvents checks if the user can push events (requires writer role).

func (*ServerDB) CanViewProject

func (db *ServerDB) CanViewProject(projectID, userID string) error

CanViewProject checks if the user can view the project (requires reader role).

func (*ServerDB) CleanupAuthEvents added in v0.32.0

func (db *ServerDB) CleanupAuthEvents(olderThan time.Duration) (int64, error)

CleanupAuthEvents deletes auth events older than the given duration. Returns the number of rows deleted.

func (*ServerDB) CleanupExpiredAuthRequests

func (db *ServerDB) CleanupExpiredAuthRequests() (int64, error)

CleanupExpiredAuthRequests marks pending auth requests past their expiry as expired.

func (*ServerDB) CleanupRateLimitEvents added in v0.32.0

func (db *ServerDB) CleanupRateLimitEvents(olderThan time.Duration) (int64, error)

CleanupRateLimitEvents deletes events older than the given duration. Returns the number of rows deleted.

func (*ServerDB) Close

func (db *ServerDB) Close() error

Close checkpoints the WAL and closes the database connection.

func (*ServerDB) CompleteAuthRequest

func (db *ServerDB) CompleteAuthRequest(deviceCode string) (*AuthRequest, error)

CompleteAuthRequest transitions a verified auth request to used and returns it. Returns nil if the request is not in verified status.

func (*ServerDB) CountAdmins added in v0.32.0

func (db *ServerDB) CountAdmins() (int, error)

CountAdmins returns the number of users with admin privileges.

func (*ServerDB) CountMembers added in v0.32.0

func (db *ServerDB) CountMembers() (int, error)

CountMembers returns the total number of memberships.

func (*ServerDB) CountProjects added in v0.32.0

func (db *ServerDB) CountProjects() (int, error)

CountProjects returns the total number of non-deleted projects.

func (*ServerDB) CountUsers added in v0.32.0

func (db *ServerDB) CountUsers() (int, error)

CountUsers returns the total number of users.

func (*ServerDB) CreateAuthRequest

func (db *ServerDB) CreateAuthRequest(email string) (*AuthRequest, error)

CreateAuthRequest creates a new device auth request for the given email.

func (*ServerDB) CreateProject

func (db *ServerDB) CreateProject(name, description, ownerUserID string) (*Project, error)

CreateProject creates a new project and adds the owner as a member in a single transaction.

func (*ServerDB) CreateProjectWithID

func (db *ServerDB) CreateProjectWithID(id, name, description, ownerUserID string) (*Project, error)

CreateProjectWithID creates a new project using a pre-generated ID and adds the owner as a member.

func (*ServerDB) CreateUser

func (db *ServerDB) CreateUser(email string) (*User, error)

CreateUser inserts a new user with the given email (lowercased). The first user created is automatically made an admin.

func (*ServerDB) ForceExpireAuthRequestForTest

func (db *ServerDB) ForceExpireAuthRequestForTest(id string, expiresAt time.Time)

ForceExpireAuthRequestForTest forces an auth request's expiry time (test-only helper).

func (*ServerDB) GenerateAPIKey

func (db *ServerDB) GenerateAPIKey(userID, name, scopes string, expiresAt *time.Time) (string, *APIKey, error)

GenerateAPIKey creates a new API key for the given user. Returns the plaintext key (shown once) and the stored APIKey record.

func (*ServerDB) GetAuthRequestByDeviceCode

func (db *ServerDB) GetAuthRequestByDeviceCode(deviceCode string) (*AuthRequest, error)

GetAuthRequestByDeviceCode returns the auth request with the given device code, or nil.

func (*ServerDB) GetAuthRequestByUserCode

func (db *ServerDB) GetAuthRequestByUserCode(userCode string) (*AuthRequest, error)

GetAuthRequestByUserCode returns the pending, non-expired auth request with the given user code, or nil.

func (*ServerDB) GetMembership

func (db *ServerDB) GetMembership(projectID, userID string) (*Membership, error)

GetMembership returns a user's membership in a project, or nil if not found.

func (*ServerDB) GetPendingExpiredAuthRequests added in v0.32.0

func (db *ServerDB) GetPendingExpiredAuthRequests() ([]AuthRequest, error)

GetPendingExpiredAuthRequests returns pending auth requests that have expired, for use when logging "expired" auth events before cleanup marks them.

func (*ServerDB) GetProject

func (db *ServerDB) GetProject(id string, includeSoftDeleted bool) (*Project, error)

GetProject returns a project by ID. If includeSoftDeleted is false, soft-deleted projects are excluded.

func (*ServerDB) GetProjectEventCount added in v0.32.0

func (db *ServerDB) GetProjectEventCount(projectID string) (int, *time.Time, error)

GetProjectEventCount returns the cached event count and last event timestamp for a project.

func (*ServerDB) GetSyncCursor

func (db *ServerDB) GetSyncCursor(projectID, clientID string) (*SyncCursor, error)

GetSyncCursor returns the sync cursor for a project/client pair, or nil if not found.

func (*ServerDB) GetUserByEmail

func (db *ServerDB) GetUserByEmail(email string) (*User, error)

GetUserByEmail returns the user with the given email (case-insensitive), or nil if not found.

func (*ServerDB) GetUserByID

func (db *ServerDB) GetUserByID(id string) (*User, error)

GetUserByID returns the user with the given ID, or nil if not found.

func (*ServerDB) InsertAuthEvent added in v0.32.0

func (db *ServerDB) InsertAuthEvent(authRequestID, email, eventType, metadata string) error

InsertAuthEvent inserts an auth event row.

func (*ServerDB) InsertRateLimitEvent added in v0.32.0

func (db *ServerDB) InsertRateLimitEvent(keyID, ip, endpointClass string) error

InsertRateLimitEvent inserts a rate limit violation event. keyID may be empty for IP-based rate limiting (stored as NULL).

func (*ServerDB) IsUserAdmin added in v0.32.0

func (db *ServerDB) IsUserAdmin(userID string) (bool, error)

IsUserAdmin returns whether the user with the given ID is an admin.

func (*ServerDB) ListAPIKeys

func (db *ServerDB) ListAPIKeys(userID string) ([]*APIKey, error)

ListAPIKeys returns all API keys for a user (without secrets).

func (*ServerDB) ListAPIKeysForUser added in v0.32.0

func (db *ServerDB) ListAPIKeysForUser(userID string) ([]*APIKey, error)

ListAPIKeysForUser returns all API keys for a user without the key_hash.

func (*ServerDB) ListMembers

func (db *ServerDB) ListMembers(projectID string) ([]*Membership, error)

ListMembers returns all members of a project.

func (*ServerDB) ListProjectsForUser

func (db *ServerDB) ListProjectsForUser(userID string) ([]*Project, error)

ListProjectsForUser returns all non-deleted projects the user is a member of.

func (*ServerDB) ListSyncCursorsForProject added in v0.32.0

func (db *ServerDB) ListSyncCursorsForProject(projectID string) ([]SyncCursor, error)

ListSyncCursorsForProject returns all sync cursors for a project.

func (*ServerDB) ListUsers

func (db *ServerDB) ListUsers() ([]*User, error)

ListUsers returns all users.

func (*ServerDB) Ping

func (db *ServerDB) Ping() error

Ping checks the database connection is alive.

func (*ServerDB) QueryAuthEvents added in v0.32.0

func (db *ServerDB) QueryAuthEvents(eventType, email, from, to string, limit int, cursor string) (*PaginatedResult[AuthEvent], error)

QueryAuthEvents queries auth events with optional filters and cursor-based pagination. Filters: eventType (exact match), email (LIKE), from/to (created_at range).

func (*ServerDB) QueryRateLimitEvents added in v0.32.0

func (db *ServerDB) QueryRateLimitEvents(keyID, ip, from, to string, limit int, cursor string) (*PaginatedResult[RateLimitEvent], error)

QueryRateLimitEvents queries rate limit events with optional filters. Filters: keyID (exact), ip (exact), from/to (created_at range, RFC3339 or datetime). Uses cursor-based pagination via the PaginatedQuery helper.

func (*ServerDB) RemoveMember

func (db *ServerDB) RemoveMember(projectID, userID string) error

RemoveMember removes a user from a project. Fails if removing the user would leave the project with no owners.

func (*ServerDB) RevokeAPIKey

func (db *ServerDB) RevokeAPIKey(keyID, userID string) error

RevokeAPIKey deletes an API key, only if owned by the given user.

func (*ServerDB) RunMigrations

func (db *ServerDB) RunMigrations() (int, error)

RunMigrations runs any pending database migrations.

func (*ServerDB) SetAuthRequestAPIKey

func (db *ServerDB) SetAuthRequestAPIKey(id, apiKeyID string) error

SetAuthRequestAPIKey sets the API key ID on an auth request.

func (*ServerDB) SetEmailVerified

func (db *ServerDB) SetEmailVerified(userID string) error

SetEmailVerified marks the user's email as verified.

func (*ServerDB) SetUserAdmin added in v0.32.0

func (db *ServerDB) SetUserAdmin(email string, isAdmin bool) error

SetUserAdmin sets or clears the admin flag for a user identified by email.

func (*ServerDB) SoftDeleteProject

func (db *ServerDB) SoftDeleteProject(id string) error

SoftDeleteProject marks a project as deleted.

func (*ServerDB) UpdateMemberRole

func (db *ServerDB) UpdateMemberRole(projectID, userID, newRole string) error

UpdateMemberRole changes a member's role.

func (*ServerDB) UpdateProject

func (db *ServerDB) UpdateProject(id, name, description string) (*Project, error)

UpdateProject updates a project's name and description.

func (*ServerDB) UpdateProjectEventCount added in v0.32.0

func (db *ServerDB) UpdateProjectEventCount(projectID string, newEvents int, lastEventAt time.Time) error

UpdateProjectEventCount atomically increments event_count and updates last_event_at.

func (*ServerDB) UpsertSyncCursor

func (db *ServerDB) UpsertSyncCursor(projectID, clientID string, lastEventID int64) error

UpsertSyncCursor creates or updates a sync cursor for a project/client pair.

func (*ServerDB) VerifyAPIKey

func (db *ServerDB) VerifyAPIKey(plaintextKey string) (*APIKey, *User, error)

VerifyAPIKey checks a plaintext key against stored hashes. Returns the matching APIKey and associated User, or an error.

func (*ServerDB) VerifyAuthRequest

func (db *ServerDB) VerifyAuthRequest(userCode, userID string) error

VerifyAuthRequest marks a pending auth request as verified with the given user ID.

type SyncCursor

type SyncCursor struct {
	ProjectID   string
	ClientID    string
	LastEventID int64
	LastSyncAt  *time.Time
}

SyncCursor tracks a client's sync position in a project.

type User

type User struct {
	ID              string
	Email           string
	EmailVerifiedAt *time.Time
	IsAdmin         bool
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

User represents a registered user.

type UserProject added in v0.32.0

type UserProject struct {
	ProjectID string `json:"project_id"`
	Name      string `json:"name"`
	Role      string `json:"role"`
}

UserProject represents a project membership for a user.

Jump to

Keyboard shortcuts

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