pg

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Mar 15, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package pg implements the storage layer for the application using a PostgreSQL database. It provides concrete implementations of the service layer's storage interfaces.

This package follows a "Public/Private Method" pattern:

  1. Public, exported methods match the service interfaces. Their primary role is to manage database transactions (begin, commit, rollback) and call the corresponding private methods.
  2. Private, unexported methods contain the core database logic. They accept a `Querier` interface, making them transaction-agnostic and highly testable.

This design keeps the service layer clean and unaware of the database implementation details while allowing for robust, atomic, and testable database operations.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PartitionName

func PartitionName(shortName domain.BoardShortName, table string) string

PartitionName is a convenience wrapper around shared storage's PartitionName. It generates a properly quoted and escaped partition table name for use in SQL queries. Example: ("tech", "messages") -> `"messages_tech"`

func ViewTableName

func ViewTableName(shortName domain.BoardShortName) string

ViewTableName is a convenience wrapper around shared storage's ViewTableName. It generates a properly quoted and escaped materialized view name for use in SQL queries. Example: ("tech") -> `"board_preview_tech"`

Types

type MsgKey

type MsgKey struct {
	ThreadId domain.ThreadId
	MsgId    domain.MsgId
}

MsgKey is a composite key for identifying a message within a board. Since message IDs are per-thread sequential, we need both thread_id and msg_id.

type Querier

type Querier = sharedstorage.Querier

Querier is an alias to the shared Querier interface. It abstracts database operations and is satisfied by *sql.DB and *sql.Tx. This interface is defined in shared/storage/pg and used throughout the application.

type Storage

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

Storage is the central struct for the PostgreSQL persistence layer. It holds the database connection pool and application configuration, and acts as the receiver for all storage methods.

func New

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

New creates and returns a new Storage instance. It establishes a connection to the database and starts any necessary background processes, such as the materialized view refresher. This function is the main entry point for initializing the persistence layer.

func (*Storage) BlacklistUser

func (s *Storage) BlacklistUser(userId domain.UserId, reason string, blacklistedBy domain.UserId) error

BlacklistUser adds a user to the blacklist. This is the public entry point that wraps the operation in a transaction.

func (*Storage) Cleanup

func (s *Storage) Cleanup() error

Cleanup gracefully closes the database connection pool. It should be called during application shutdown.

func (*Storage) ConfirmationData

func (s *Storage) ConfirmationData(emailHash []byte) (domain.ConfirmationData, error)

ConfirmationData is a public, read-only method to retrieve confirmation data.

func (*Storage) CountActiveInvites

func (s *Storage) CountActiveInvites(userId domain.UserId) (int, error)

CountActiveInvites returns the number of active (unused, unexpired) invites for a user

func (*Storage) CreateBoard

func (s *Storage) CreateBoard(creationData domain.BoardCreationData) error

CreateBoard is the public entry point for creating a new board. It wraps the entire board creation process, including metadata insertion, partition creation, and view creation, within a single atomic transaction. This guarantees that a board is either fully created or not created at all.

func (*Storage) CreateMessage

func (s *Storage) CreateMessage(creationData domain.MessageCreationData, attachments domain.Attachments) (domain.MsgId, error)

CreateMessage serves as the public entry point for creating a new message. It is responsible for wrapping the core message creation logic in a single, atomic database transaction. This ensures that all related database operations (updating board/thread metadata, inserting the message, attachments, and replies) either succeed together or fail together, maintaining data integrity.

func (*Storage) CreateThread

func (s *Storage) CreateThread(creationData domain.ThreadCreationData, maxThreadCount *int) (domain.ThreadId, time.Time, error)

CreateThread creates a thread and optionally enforces the max thread count per board using pg_advisory_xact_lock to prevent race conditions. When maxThreadCount is non-nil, it acquires a board-level advisory lock, creates the thread, and deletes the oldest non-pinned threads if the board exceeds the limit.

func (*Storage) DeleteBoard

func (s *Storage) DeleteBoard(shortName domain.BoardShortName) error

DeleteBoard is the public entry point for deleting a board. It manages the transaction for this destructive operation, ensuring that the board's materialized view, all its table partitions, and its metadata are removed atomically.

func (*Storage) DeleteConfirmationData

func (s *Storage) DeleteConfirmationData(emailHash []byte) error

DeleteConfirmationData is the public entry point for removing used or expired confirmation data.

func (*Storage) DeleteInviteCode

func (s *Storage) DeleteInviteCode(codeHash string) error

DeleteInviteCode deletes an invite code by its hash

func (*Storage) DeleteInvitesByUser

func (s *Storage) DeleteInvitesByUser(userId domain.UserId) error

DeleteInvitesByUser deletes all unused invite codes created by a user

func (*Storage) DeleteMessage

func (s *Storage) DeleteMessage(board domain.BoardShortName, threadId domain.ThreadId, id domain.MsgId) error

DeleteMessage is the public entry point for deleting a message. It manages the transaction for this operation, ensuring that the board's last activity is updated and the message is deleted atomically. The cascading deletion of related attachments and replies is handled by the database schema.

func (*Storage) DeleteOrphanedFileRecords

func (s *Storage) DeleteOrphanedFileRecords() (int64, error)

DeleteOrphanedFileRecords deletes file records not referenced by any attachment. Returns the number of records deleted. This is used by the garbage collector to clean up orphaned database records.

func (*Storage) DeleteThread

func (s *Storage) DeleteThread(board domain.BoardShortName, id domain.ThreadId) error

DeleteThread is the public entry point for deleting a thread. It wraps the core deletion logic in a transaction to ensure atomicity. The database schema's foreign key constraints will cascade the delete from the thread to all of its contained messages, attachments, and replies.

func (*Storage) DeleteUser

func (s *Storage) DeleteUser(emailHash []byte) error

DeleteUser is the public entry point for deleting a user account. It wraps the deletion in a transaction. The database schema's ON DELETE CASCADE constraints will handle cleaning up related data (e.g., confirmation data).

func (*Storage) GetActiveBoards

func (s *Storage) GetActiveBoards(interval time.Duration) ([]domain.Board, error)

GetActiveBoards is a public, read-only method used by the view refresh background process to find boards with recent activity.

func (*Storage) GetAllFilePaths

func (s *Storage) GetAllFilePaths() ([]string, error)

GetAllFilePaths returns all file paths stored in the database. This is used by the garbage collector to identify orphaned files. Returns both original file paths and thumbnail paths.

func (*Storage) GetBlacklistedUsersWithDetails

func (s *Storage) GetBlacklistedUsersWithDetails(limit, offset int) ([]domain.BlacklistEntry, error)

GetBlacklistedUsersWithDetails retrieves blacklisted users with their full details (reason, blacklisted_at, blacklisted_by) for admin display purposes, with pagination.

func (*Storage) GetBoard

func (s *Storage) GetBoard(shortName domain.BoardShortName, page int) (domain.Board, error)

GetBoard is a public, read-only method for fetching a single page of a board's content. It delegates directly to the internal method using the main database connection pool.

func (*Storage) GetBoardLastModified

func (s *Storage) GetBoardLastModified(shortName domain.BoardShortName) (time.Time, error)

GetBoardLastModified returns the view_last_modified_at timestamp for a board. This reflects the last_activity_at value snapshotted after each materialized view refresh, ensuring the returned timestamp only covers changes the view has actually incorporated.

func (*Storage) GetBoards

func (s *Storage) GetBoards() ([]domain.BoardMetadata, error)

GetBoards is a public, read-only method to fetch metadata for all boards.

func (*Storage) GetBoardsWithPermissions

func (s *Storage) GetBoardsWithPermissions() (map[string][]string, error)

GetBoardsWithPermissions returns a map of board short names to their allowed email domains. Returns nil for boards without restrictions (public boards).

func (*Storage) GetInvitesByUser

func (s *Storage) GetInvitesByUser(userId domain.UserId, limit, offset int) ([]domain.InviteCode, error)

GetInvitesByUser returns invite codes created by a user, with pagination.

func (*Storage) GetMessage

func (s *Storage) GetMessage(board domain.BoardShortName, threadId domain.ThreadId, id domain.MsgId) (domain.Message, error)

GetMessage is a read-only operation that fetches a complete message, including its attachments and replies. Since it doesn't modify data, it doesn't need to create its own transaction. It can use the main database connection pool (s.db) as the Querier, allowing for concurrent reads.

func (*Storage) GetRecentlyBlacklistedUsers

func (s *Storage) GetRecentlyBlacklistedUsers(since time.Time) ([]domain.UserId, error)

GetRecentlyBlacklistedUsers fetches all user IDs that were blacklisted after the specified time. This is used for cache updates with TTL-based filtering.

func (*Storage) GetReferralActionStats added in v0.3.2

func (s *Storage) GetReferralActionStats() ([]domain.ReferralActionStats, error)

func (*Storage) GetThread

func (s *Storage) GetThread(board domain.BoardShortName, id domain.ThreadId, page int) (domain.Thread, error)

GetThread is the public entry point for fetching a full thread, including all of its messages, replies, and attachments. It decides whether to use the optimized single-page fetch or the paginated fetch based on thread size. The page parameter controls pagination (1-based). Page 0 or 1 returns the first page.

func (*Storage) GetThreadLastModified

func (s *Storage) GetThreadLastModified(board domain.BoardShortName, id domain.ThreadId) (time.Time, error)

GetThreadLastModified returns only the last_modified_at timestamp for a thread.

func (*Storage) GetUserMessages

func (s *Storage) GetUserMessages(userId domain.UserId, limit int) ([]domain.Message, error)

GetUserMessages fetches user's last N messages across all boards. Returns FULLY enriched domain.Message objects with Author, Attachments, Replies.

func (*Storage) InviteCodeByHash

func (s *Storage) InviteCodeByHash(codeHash string) (domain.InviteCode, error)

InviteCodeByHash fetches an invite code by its hash

func (*Storage) IsUserBlacklisted

func (s *Storage) IsUserBlacklisted(userId domain.UserId) (bool, error)

IsUserBlacklisted checks if a specific user is currently blacklisted. This is a read-only operation used for direct DB checks (e.g., at login).

func (*Storage) MarkInviteUsed

func (s *Storage) MarkInviteUsed(codeHash string, usedBy domain.UserId) error

MarkInviteUsed marks an invite code as used by a specific user

func (*Storage) Ping

func (s *Storage) Ping(ctx context.Context) error

Ping checks if the database connection is alive. Used by health check endpoints to verify database connectivity.

func (*Storage) SaveConfirmationData

func (s *Storage) SaveConfirmationData(data domain.ConfirmationData) error

SaveConfirmationData is the public entry point for storing password reset or account confirmation tokens.

func (*Storage) SaveInviteCode

func (s *Storage) SaveInviteCode(invite domain.InviteCode) error

SaveInviteCode saves a new invite code to the database

func (*Storage) SaveReferralAction added in v0.3.2

func (s *Storage) SaveReferralAction(source, action, ip string) error

func (*Storage) SaveUser

func (s *Storage) SaveUser(user domain.User) (domain.UserId, error)

SaveUser is the public entry point for creating a new user. It wraps the core logic in a transaction to ensure the operation is atomic.

func (*Storage) StartPeriodicViewRefresh

func (s *Storage) StartPeriodicViewRefresh(ctx context.Context, refreshInterval, activityWindow time.Duration)

StartPeriodicViewRefresh initiates a background goroutine that periodically refreshes materialized views for active boards. This ensures board preview data stays up-to-date without blocking user requests.

The refresh process:

  1. Runs on a ticker (refreshInterval)
  2. Identifies boards with recent activity within the activityWindow period
  3. Concurrently refreshes each active board's materialized view
  4. Stops gracefully when the context is canceled

The activityWindow should be larger than refreshInterval to ensure boards don't "fall out" of the active window between refresh cycles. For example, with a 5s refresh and 30s activity window, a board gets up to 6 refresh opportunities after each post.

This is called once during application initialization from New().

func (*Storage) TogglePinnedStatus

func (s *Storage) TogglePinnedStatus(board domain.BoardShortName, threadId domain.ThreadId) (bool, error)

TogglePinnedStatus is the public entry point for toggling a thread's pinned status. It wraps the update in a transaction and returns the new pinned status.

func (*Storage) UnblacklistUser

func (s *Storage) UnblacklistUser(userId domain.UserId) error

UnblacklistUser removes a user from the blacklist. This is the public entry point that wraps the operation in a transaction.

func (*Storage) UpdatePassword

func (s *Storage) UpdatePassword(emailHash []byte, newPasswordHash domain.Password) error

UpdatePassword is the public entry point for changing a user's password. It manages the transaction for this security-sensitive operation.

func (*Storage) User

func (s *Storage) User(emailHash []byte) (domain.User, error)

User is a public, read-only method to fetch a user by their email hash. It uses the main database connection pool for efficiency.

Jump to

Keyboard shortcuts

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