store

package
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Init

func Init() error

Types

type FeedRefreshInput added in v0.4.0

type FeedRefreshInput struct {
	RunID        string
	FeedId       string
	FeedTitle    string
	FeedURL      string
	Success      bool
	TotalFetched int
	Stored       int
	Skipped      int
	FetchError   string
	Errors       []string // item-level errors; joined with "\n" into ErrorLog
	Warnings     []string // non-fatal notices; joined with "\n" into WarningLog
	RawBody      []byte   // raw fetched feed body; zstd-compressed before storage
	RawStatus    int
	RawType      string
	DurationMs   int64
}

FeedRefreshInput is the raw (uncompressed) outcome of refreshing one feed. RecordFeedRefresh joins and zstd-compresses Errors before persisting.

type FeedRefreshResultView added in v0.4.0

type FeedRefreshResultView struct {
	models.FeedRefreshResult
	Errors   []string
	Warnings []string
	RawBody  string
}

FeedRefreshResultView is a decompressed per-feed result for display; Errors and Warnings are the item-level logs split back into lines and RawBody is the raw fetched feed body as plain text.

type FeedRefreshRunSummary added in v0.4.0

type FeedRefreshRunSummary struct {
	models.FeedRefreshRun
	FeedCount    int
	OkCount      int
	FailCount    int
	TotalStored  int
	TotalFetched int
	ErrorCount   int
	WarningCount int
}

FeedRefreshRunSummary is a refresh-run row plus aggregate counters over its per-feed results, for the refresh list page.

type GormStore

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

GormStore implements the Store interface using GORM

var (
	Db *GormStore
)

func New

func New(path string) (*GormStore, error)

New creates a new GormStore instance

func (*GormStore) Close

func (s *GormStore) Close() error

Close closes the database connection by getting the underlying SQL DB instance

func (*GormStore) DeleteFeed

func (s *GormStore) DeleteFeed(id string) error

func (*GormStore) DeleteFeedArticles

func (s *GormStore) DeleteFeedArticles(feedId string) error

func (*GormStore) DeleteFeedGroup

func (s *GormStore) DeleteFeedGroup(id string) error

DeleteFeedGroup deletes a feed group

func (*GormStore) DeleteProfile added in v0.4.0

func (s *GormStore) DeleteProfile(id string) error

DeleteProfile deletes a profile and its feed-membership rows. The default profile cannot be deleted.

func (*GormStore) DeleteUnusedTags added in v0.5.0

func (s *GormStore) DeleteUnusedTags() error

DeleteUnusedTags removes tags no article references. Tags are created on article ingestion and never garbage-collected otherwise, so orphans (a tag whose only articles were later deleted, or a mis-typed tag) accumulate unbounded. An unused tag is attached to no article, so it is invisible to the UI; deleting it is safe.

func (*GormStore) FeedIDsByTopics added in v0.4.0

func (s *GormStore) FeedIDsByTopics(topics []string) ([]string, error)

FeedIDsByTopics returns the distinct ids of enabled feeds having any of the given topics. An empty topic list returns no feeds.

func (*GormStore) FindDigestsWithSameArticleSet

func (s *GormStore) FindDigestsWithSameArticleSet(digestId string) ([]models.Digest, error)

FindDigestsWithSameArticleSet returns all digests (including digestId itself) whose digest_articles set is exactly equal to digestId's set, ordered by created_at DESC. ProviderResults are populated so callers can label each sibling by its provider/model.

func (*GormStore) FinishFeedRefreshRun added in v0.4.0

func (s *GormStore) FinishFeedRefreshRun(id string, finishedAt time.Time) error

FinishFeedRefreshRun stamps a refresh run's completion time.

func (*GormStore) FinishLLMRun added in v0.4.0

func (s *GormStore) FinishLLMRun(id string, finishedAt time.Time) error

FinishLLMRun stamps a run's completion time.

func (*GormStore) GetAllArticleAnalyses

func (s *GormStore) GetAllArticleAnalyses(articleId, profileId string) ([]models.ArticleAnalysis, error)

GetAllArticleAnalyses returns every analysis for an article, newest first. When profileId is set it returns only that profile's analyses; an empty profileId returns all of them (used by the analysis-history UI).

func (*GormStore) GetArticle

func (s *GormStore) GetArticle(id string) (models.Article, error)

func (*GormStore) GetArticleAnalysesBatch

func (s *GormStore) GetArticleAnalysesBatch(articleIds []string, profileId string) (map[string]*models.ArticleAnalysis, error)

GetArticleAnalysesBatch returns the most recent analysis for each of the given article IDs. When profileId is set, only that profile's analyses are considered; an empty profileId considers all profiles (historical behavior).

func (*GormStore) GetArticleAnalysis

func (s *GormStore) GetArticleAnalysis(articleId, profileId string) (*models.ArticleAnalysis, error)

GetArticleAnalysis returns the most recent analysis for an article. When profileId is set it returns the most recent analysis produced for that profile; an empty profileId returns the latest across all profiles, preserving the historical single-tenant behavior.

func (*GormStore) GetArticleCounts

func (s *GormStore) GetArticleCounts(filter models.ArticleFilter) (models.ArticleCounts, error)

func (*GormStore) GetArticlesBatch

func (s *GormStore) GetArticlesBatch(ids []string) ([]models.Article, error)

func (*GormStore) GetCategories

func (s *GormStore) GetCategories() ([]models.Category, error)

func (*GormStore) GetDigest

func (s *GormStore) GetDigest(id string) (models.Digest, error)

func (*GormStore) GetDigestAnalyses

func (s *GormStore) GetDigestAnalyses(digestId string) ([]models.DigestAnalysis, error)

func (*GormStore) GetDigestArticles

func (s *GormStore) GetDigestArticles(digestId string) ([]models.Article, error)

func (*GormStore) GetDigestGlossary added in v0.4.0

func (s *GormStore) GetDigestGlossary(digestId string) ([]models.DigestGlossary, error)

GetDigestGlossary loads the glossary entries referenced by a digest, with their entries.

func (*GormStore) GetDigestProviderResult

func (s *GormStore) GetDigestProviderResult(id string) (models.DigestProviderResult, error)

func (*GormStore) GetDigestProviderResults

func (s *GormStore) GetDigestProviderResults(digestId string) ([]models.DigestProviderResult, error)

func (*GormStore) GetFeed

func (s *GormStore) GetFeed(id string) (models.Feed, error)

func (*GormStore) GetFeedGroup

func (s *GormStore) GetFeedGroup(id string) (models.FeedGroup, error)

GetFeedGroup returns a feed group by Id

func (*GormStore) GetFeedRefreshRun added in v0.4.0

func (s *GormStore) GetFeedRefreshRun(id string) (models.FeedRefreshRun, error)

GetFeedRefreshRun returns a single refresh run by id.

func (*GormStore) GetGlossaryEntriesByKeys added in v0.4.0

func (s *GormStore) GetGlossaryEntriesByKeys(keys []string) (map[string]*models.GlossaryEntry, error)

GetGlossaryEntriesByKeys batch-loads existing entries for the given normalized keys, returned keyed by NormalizedKey. Used to skip the LLM for already-defined terms.

func (*GormStore) GetLLMRun added in v0.4.0

func (s *GormStore) GetLLMRun(id string) (models.LLMRun, error)

GetLLMRun returns a single run by id.

func (*GormStore) GetOrCreateCategory

func (s *GormStore) GetOrCreateCategory(name string) (*models.Category, error)

GetOrCreateCategory returns a category with the given name, creating it if it doesn't exist.

func (*GormStore) GetProfile added in v0.4.0

func (s *GormStore) GetProfile(id string) (models.Profile, error)

GetProfile returns a single profile by id (slug). Feeds are not preloaded (resolving editorial config does not need them); use ListProfileFeeds for the pool.

func (*GormStore) LinkLLMRunToDigest added in v0.4.0

func (s *GormStore) LinkLLMRunToDigest(runID, digestID, title string) error

LinkLLMRunToDigest attaches the produced digest id and title to a run once known.

func (*GormStore) ListAllTopics added in v0.4.0

func (s *GormStore) ListAllTopics() ([]string, error)

ListAllTopics returns the distinct topics in use across all feeds, sorted.

func (*GormStore) ListArticles

func (s *GormStore) ListArticles(filter models.ArticleFilter) ([]models.Article, error)

func (*GormStore) ListDigests

func (s *GormStore) ListDigests(limit int, full bool) ([]models.Digest, error)

ListDigests returns digests across all profiles, newest first (historical behavior). See listDigests for the meaning of full.

func (*GormStore) ListDigestsByProfile added in v0.4.0

func (s *GormStore) ListDigestsByProfile(profileId string, limit int, full bool) ([]models.Digest, error)

ListDigestsByProfile returns a profile's digests, newest first. See ListDigests for the meaning of full.

func (*GormStore) ListEnabledFeedIDs added in v0.4.0

func (s *GormStore) ListEnabledFeedIDs() ([]string, error)

ListEnabledFeedIDs returns the ids of all enabled feeds.

func (*GormStore) ListFeedGroups

func (s *GormStore) ListFeedGroups() ([]models.FeedGroup, error)

ListFeedGroups returns all feed groups

func (*GormStore) ListFeedRefreshResultsForRun added in v0.4.0

func (s *GormStore) ListFeedRefreshResultsForRun(runID string) ([]FeedRefreshResultView, error)

ListFeedRefreshResultsForRun returns a run's per-feed results in chronological order, with the item-level error log decompressed into lines.

func (*GormStore) ListFeedRefreshRunSummaries added in v0.4.0

func (s *GormStore) ListFeedRefreshRunSummaries(limit int) ([]FeedRefreshRunSummary, error)

ListFeedRefreshRunSummaries returns the most recent refresh runs with per-run result counts and totals, newest first. limit <= 0 means no limit.

func (*GormStore) ListFeedTopics added in v0.4.0

func (s *GormStore) ListFeedTopics(feedId string) ([]string, error)

ListFeedTopics returns a feed's topics.

func (*GormStore) ListFeeds

func (s *GormStore) ListFeeds() ([]models.Feed, error)

func (*GormStore) ListGlossaryEntries added in v0.4.0

func (s *GormStore) ListGlossaryEntries(limit int) ([]models.GlossaryEntry, error)

ListGlossaryEntries returns persisted glossary entries ordered by term. limit <= 0 returns all.

func (*GormStore) ListLLMCallsForRun added in v0.4.0

func (s *GormStore) ListLLMCallsForRun(runID string) ([]LLMCallView, error)

ListLLMCallsForRun returns a run's calls in chronological order, with prompt and response decompressed to plain text for display.

func (*GormStore) ListLLMRunSummaries added in v0.4.0

func (s *GormStore) ListLLMRunSummaries(limit int, profileID string) ([]LLMRunSummary, error)

ListLLMRunSummaries returns the most recent runs with per-run call counts and token totals, newest first. limit <= 0 means no limit. A non-empty profileID restricts results to that profile's runs.

func (*GormStore) ListProfileFeeds added in v0.4.0

func (s *GormStore) ListProfileFeeds(profileId string) ([]models.Feed, error)

ListProfileFeeds returns the feeds in a profile's pool.

func (*GormStore) ListProfiles added in v0.4.0

func (s *GormStore) ListProfiles() ([]models.Profile, error)

ListProfiles returns all profiles ordered by sort order, with their feeds preloaded.

func (*GormStore) MarkFeedArticlesAsRead

func (s *GormStore) MarkFeedArticlesAsRead(feedId string) error

func (*GormStore) PruneAnalyses added in v0.5.0

func (s *GormStore) PruneAnalyses(keep int) error

PruneAnalyses keeps only the most recent keep analyses per (article, profile), deleting older ones to bound growth. keep <= 0 is a no-op. Analyses referenced by any digest (via digest_analyses.analysis_id) are never deleted, so digest history stays intact regardless of retention.

func (*GormStore) PruneFeedRefreshRuns added in v0.4.0

func (s *GormStore) PruneFeedRefreshRuns(keep int) error

PruneFeedRefreshRuns deletes all but the most recent keep runs (and their per-feed results). keep <= 0 is a no-op.

func (*GormStore) PruneLLMRuns added in v0.4.0

func (s *GormStore) PruneLLMRuns(keep int) error

PruneLLMRuns deletes all but the most recent keep runs (and their calls). keep <= 0 is a no-op.

func (*GormStore) RecordFeedRefresh added in v0.4.0

func (s *GormStore) RecordFeedRefresh(in FeedRefreshInput) error

RecordFeedRefresh persists one feed's refresh outcome, gzip-compressing the item-level error log.

func (*GormStore) RecordLLMCall added in v0.4.0

func (s *GormStore) RecordLLMCall(in LLMCallInput) error

RecordLLMCall persists one LLM call, zstd-compressing the prompt and response.

func (*GormStore) SaveArticleAnalysis

func (s *GormStore) SaveArticleAnalysis(analysis *models.ArticleAnalysis) error

func (*GormStore) SaveCategory

func (s *GormStore) SaveCategory(category models.Category) error

func (*GormStore) SetFeedTopics added in v0.4.0

func (s *GormStore) SetFeedTopics(feedId string, topics []string) error

SetFeedTopics replaces a feed's topic rows with exactly topics (lowercased, trimmed, de-duplicated). An empty list clears the feed's topics.

func (*GormStore) SetGlossaryManualOverride added in v0.4.0

func (s *GormStore) SetGlossaryManualOverride(key, curatedDef string) error

SetGlossaryManualOverride sets a curated definition that wins over and is never overwritten by the generated definition.

func (*GormStore) SetProfileEnabled added in v0.5.1

func (s *GormStore) SetProfileEnabled(id string, enabled bool) error

SetProfileEnabled flips a profile's enabled flag in place, without touching any other column (used by the apply reconcile to disable absent profiles).

func (*GormStore) SetProfileFeeds added in v0.4.0

func (s *GormStore) SetProfileFeeds(profileId string, feedIds []string) error

SetProfileFeeds replaces a profile's feed membership with exactly feedIds.

func (*GormStore) StartFeedRefreshRun added in v0.4.0

func (s *GormStore) StartFeedRefreshRun(id, trigger string, startedAt time.Time) error

StartFeedRefreshRun inserts a new refresh-run row at the start of a cycle.

func (*GormStore) StartLLMRun added in v0.4.0

func (s *GormStore) StartLLMRun(id, profileId string, startedAt time.Time) error

StartLLMRun inserts a new run row at the start of digest generation, tagged with the profile the digest is being generated for.

func (*GormStore) StoreArticle

func (s *GormStore) StoreArticle(article models.Article) error

func (*GormStore) StoreDigest

func (s *GormStore) StoreDigest(digest models.Digest) error

func (*GormStore) StoreDigestAnalysesBatch

func (s *GormStore) StoreDigestAnalysesBatch(entries []models.DigestAnalysis) error

func (*GormStore) StoreDigestAnalysis

func (s *GormStore) StoreDigestAnalysis(entry models.DigestAnalysis) error

func (*GormStore) StoreDigestArticle

func (s *GormStore) StoreDigestArticle(digestId string, articleId string) error

func (*GormStore) StoreDigestArticlesBatch

func (s *GormStore) StoreDigestArticlesBatch(digestId string, articleIds []string) error

StoreDigestArticlesBatch associates a batch of articles with a digest in a single operation.

func (*GormStore) StoreDigestGlossaryBatch added in v0.4.0

func (s *GormStore) StoreDigestGlossaryBatch(rows []models.DigestGlossary) error

StoreDigestGlossaryBatch records the glossary entries referenced by a digest. Existing links are ignored (idempotent).

func (*GormStore) StoreDigestProviderResult

func (s *GormStore) StoreDigestProviderResult(result models.DigestProviderResult) error

func (*GormStore) StoreFeed

func (s *GormStore) StoreFeed(feed models.Feed) error

func (*GormStore) StoreFeedGroup

func (s *GormStore) StoreFeedGroup(group models.FeedGroup) error

StoreFeedGroup stores a feed group

func (*GormStore) StoreProfile added in v0.4.0

func (s *GormStore) StoreProfile(profile models.Profile) error

StoreProfile upserts a profile row. Feed membership is managed separately via SetProfileFeeds, so the many-to-many association is omitted here.

func (*GormStore) UpdateArticle

func (s *GormStore) UpdateArticle(id string, update models.ArticleUpdate) error

func (*GormStore) UpdateArticleAnalysisGlossaryTerms added in v0.4.0

func (s *GormStore) UpdateArticleAnalysisGlossaryTerms(id, glossaryTermsJson string) error

UpdateArticleAnalysisGlossaryTerms writes only the serialized glossary_terms column for one analysis. ArticleAnalysis has no BeforeUpdate hook, so a full Save would not re-serialize the slice; this scoped update sidesteps that and touches a single column.

func (*GormStore) UpdateFeedLastFetch

func (s *GormStore) UpdateFeedLastFetch(id string, lastFetch time.Time) error

func (*GormStore) UpsertGlossaryEntry added in v0.4.0

func (s *GormStore) UpsertGlossaryEntry(entry *models.GlossaryEntry) error

UpsertGlossaryEntry inserts a new glossary entry or, when an entry with the same NormalizedKey already exists, fills in a missing generated definition without ever touching the curated override. First non-empty generated definition wins (sticky).

type LLMCallInput added in v0.4.0

type LLMCallInput struct {
	RunID            string
	Label            string
	ProviderType     string
	ModelName        string
	Prompt           string
	Response         string
	PromptTokens     int
	CompletionTokens int
	TotalTokens      int
	TokensKnown      bool
	DurationMs       int64
	Err              string
}

LLMCallInput is the raw (uncompressed) data for one recorded LLM call. RecordLLMCall compresses Prompt/Response before persisting.

type LLMCallView added in v0.4.0

type LLMCallView struct {
	models.LLMCall
	Prompt   string
	Response string
}

LLMCallView is a decompressed LLM call for display; Prompt/Response are plain text.

type LLMRunSummary added in v0.4.0

type LLMRunSummary struct {
	models.LLMRun
	CallCount             int
	TotalTokens           int
	TotalPromptTokens     int // sent
	TotalCompletionTokens int // received
	ArticleCount          int // articles in the run's digest (0 if no digest yet)
}

LLMRunSummary is a run row plus aggregate counters for the runs list page.

func (LLMRunSummary) AvgTokensPerArticle added in v0.4.0

func (r LLMRunSummary) AvgTokensPerArticle() int

AvgTokensPerArticle is total tokens spread across the run's articles, or 0 when the article count is unknown (no linked digest).

type Store

type Store interface {
	Close() error

	StoreFeed(feed models.Feed) error
	GetFeed(id string) (models.Feed, error)
	ListFeeds() ([]models.Feed, error)
	UpdateFeedLastFetch(id string, lastFetch time.Time) error
	DeleteFeed(id string) error // Method for deleting a feed

	SetFeedTopics(feedId string, topics []string) error
	ListFeedTopics(feedId string) ([]string, error)
	ListAllTopics() ([]string, error)
	FeedIDsByTopics(topics []string) ([]string, error)
	ListEnabledFeedIDs() ([]string, error)

	StoreArticle(article models.Article) error
	GetArticle(id string) (models.Article, error)
	GetArticlesBatch(ids []string) ([]models.Article, error)
	ListArticles(filter models.ArticleFilter) ([]models.Article, error)
	GetArticleCounts(filter models.ArticleFilter) (models.ArticleCounts, error)
	UpdateArticle(id string, update models.ArticleUpdate) error
	MarkFeedArticlesAsRead(feedId string) error
	DeleteFeedArticles(feedId string) error
	DeleteUnusedTags() error

	StoreDigest(digest models.Digest) error
	GetDigest(id string) (models.Digest, error)
	ListDigests(limit int, full bool) ([]models.Digest, error)
	ListDigestsByProfile(profileId string, limit int, full bool) ([]models.Digest, error)
	StoreDigestArticle(digestId string, articleId string) error
	StoreDigestArticlesBatch(digestId string, articleIds []string) error
	GetDigestArticles(digestId string) ([]models.Article, error)
	FindDigestsWithSameArticleSet(digestId string) ([]models.Digest, error)

	StoreDigestProviderResult(result models.DigestProviderResult) error
	GetDigestProviderResults(digestId string) ([]models.DigestProviderResult, error)
	GetDigestProviderResult(id string) (models.DigestProviderResult, error)

	StoreDigestAnalysis(entry models.DigestAnalysis) error
	StoreDigestAnalysesBatch(entries []models.DigestAnalysis) error
	GetDigestAnalyses(digestId string) ([]models.DigestAnalysis, error)

	UpsertGlossaryEntry(entry *models.GlossaryEntry) error
	GetGlossaryEntriesByKeys(keys []string) (map[string]*models.GlossaryEntry, error)
	ListGlossaryEntries(limit int) ([]models.GlossaryEntry, error)
	StoreDigestGlossaryBatch(rows []models.DigestGlossary) error
	GetDigestGlossary(digestId string) ([]models.DigestGlossary, error)
	SetGlossaryManualOverride(key, curatedDef string) error

	GetCategories() ([]models.Category, error)
	SaveCategory(category models.Category) error
	GetOrCreateCategory(name string) (*models.Category, error)

	SaveArticleAnalysis(analysis *models.ArticleAnalysis) error
	UpdateArticleAnalysisGlossaryTerms(id, glossaryTermsJson string) error
	GetArticleAnalysis(articleId, profileId string) (*models.ArticleAnalysis, error)
	GetArticleAnalysesBatch(articleIds []string, profileId string) (map[string]*models.ArticleAnalysis, error)
	GetAllArticleAnalyses(articleId, profileId string) ([]models.ArticleAnalysis, error)
	PruneAnalyses(keep int) error

	ListFeedGroups() ([]models.FeedGroup, error)
	GetFeedGroup(id string) (models.FeedGroup, error)
	StoreFeedGroup(group models.FeedGroup) error
	DeleteFeedGroup(id string) error

	ListProfiles() ([]models.Profile, error)
	GetProfile(id string) (models.Profile, error)
	StoreProfile(profile models.Profile) error
	SetProfileEnabled(id string, enabled bool) error
	DeleteProfile(id string) error
	SetProfileFeeds(profileId string, feedIds []string) error
	ListProfileFeeds(profileId string) ([]models.Feed, error)

	ListLLMRunSummaries(limit int, profileID string) ([]LLMRunSummary, error)
	GetLLMRun(id string) (models.LLMRun, error)
	ListLLMCallsForRun(runID string) ([]LLMCallView, error)

	StartFeedRefreshRun(id, trigger string, startedAt time.Time) error
	RecordFeedRefresh(in FeedRefreshInput) error
	FinishFeedRefreshRun(id string, finishedAt time.Time) error
	ListFeedRefreshRunSummaries(limit int) ([]FeedRefreshRunSummary, error)
	GetFeedRefreshRun(id string) (models.FeedRefreshRun, error)
	ListFeedRefreshResultsForRun(runID string) ([]FeedRefreshResultView, error)
	PruneFeedRefreshRuns(keep int) error
}

type ZstdSerializer added in v0.5.0

type ZstdSerializer struct{}

ZstdSerializer transparently zstd-compresses string fields tagged `serializer:zstd`, so callers keep reading and writing plain Go strings while the column stores a compressed blob. Register once (init above); apply per field via the gorm tag.

func (ZstdSerializer) Scan added in v0.5.0

func (ZstdSerializer) Scan(_ context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) error

Scan decompresses the stored blob back into the string field on read.

func (ZstdSerializer) Value added in v0.5.0

func (ZstdSerializer) Value(_ context.Context, _ *schema.Field, _ reflect.Value, fieldValue interface{}) (interface{}, error)

Value compresses the field value on write.

Jump to

Keyboard shortcuts

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