1// Package sqlite provides SQLite implementations of domain repositories.
2//
3// # Lazy Repository Infrastructure
4//
5// This file contains lazy-loading repository wrappers that defer database initialization
6// until first access. While currently unused (the application uses eager initialization
7// via OpenDatabase in RunParallelDBWebKit), this infrastructure is kept for potential
8// future optimization scenarios:
9//
10// - CLI commands that may not need database access
11// - Further startup latency reduction by deferring DB init past first paint
12// - Testing with mock DatabaseProvider implementations
13//
14// The lazy wrappers implement the same repository interfaces as their eager counterparts,
15// making them drop-in replacements when lazy initialization becomes beneficial.
16package sqlite
17
18import (
19 "context"
20 "database/sql"
21 "fmt"
22 "sync"
23 "time"
24
25 "github.com/bnema/dumber/internal/application/port"
26 "github.com/bnema/dumber/internal/domain/entity"
27 "github.com/bnema/dumber/internal/domain/repository"
28)
29
30// LazyHistoryRepository wraps a history repository with lazy database initialization.
31type LazyHistoryRepository struct {
32 provider port.DatabaseProvider
33 repo repository.HistoryRepository
34 once sync.Once
35 initErr error
36}
37
38// NewLazyHistoryRepository creates a lazy-loading history repository.
39func NewLazyHistoryRepository(provider port.DatabaseProvider) repository.HistoryRepository {
40 return &LazyHistoryRepository{provider: provider}
41}
42
43// LazyPermissionRepository wraps a permission repository with lazy database initialization.
44type LazyPermissionRepository struct {
45 provider port.DatabaseProvider
46 repo port.PermissionRepository
47 once sync.Once
48 initErr error
49}
50
51// NewLazyPermissionRepository creates a lazy-loading permission repository.
52func NewLazyPermissionRepository(provider port.DatabaseProvider) port.PermissionRepository {
53 return &LazyPermissionRepository{provider: provider}
54}
55
56func (r *LazyPermissionRepository) init(ctx context.Context) error {
57 r.once.Do(func() {
58 db, err := r.provider.DB(ctx)
59 if err != nil {
60 r.initErr = err
61 return
62 }
63 r.repo = NewPermissionRepository(db)
64 })
65 return r.initErr
66}
67
68func (r *LazyPermissionRepository) Get(
69 ctx context.Context,
70 origin string,
71 permType entity.PermissionType,
72) (*entity.PermissionRecord, error) {
73 if err := r.init(ctx); err != nil {
74 return nil, err
75 }
76 return r.repo.Get(ctx, origin, permType)
77}
78
79func (r *LazyPermissionRepository) Set(ctx context.Context, record *entity.PermissionRecord) error {
80 if err := r.init(ctx); err != nil {
81 return err
82 }
83 return r.repo.Set(ctx, record)
84}
85
86func (r *LazyPermissionRepository) Delete(ctx context.Context, origin string, permType entity.PermissionType) error {
87 if err := r.init(ctx); err != nil {
88 return err
89 }
90 return r.repo.Delete(ctx, origin, permType)
91}
92
93func (r *LazyPermissionRepository) GetAll(ctx context.Context, origin string) ([]*entity.PermissionRecord, error) {
94 if err := r.init(ctx); err != nil {
95 return nil, err
96 }
97 return r.repo.GetAll(ctx, origin)
98}
99
100func (r *LazyHistoryRepository) init(ctx context.Context) error {
101 r.once.Do(func() {
102 db, err := r.provider.DB(ctx)
103 if err != nil {
104 r.initErr = err
105 return
106 }
107 r.repo = NewHistoryRepository(db)
108 })
109 return r.initErr
110}
111
112func (r *LazyHistoryRepository) Save(ctx context.Context, entry *entity.HistoryEntry) error {
113 if err := r.init(ctx); err != nil {
114 return err
115 }
116 return r.repo.Save(ctx, entry)
117}
118
119func (r *LazyHistoryRepository) FindByURL(ctx context.Context, url string) (*entity.HistoryEntry, error) {
120 if err := r.init(ctx); err != nil {
121 return nil, err
122 }
123 return r.repo.FindByURL(ctx, url)
124}
125
126func (r *LazyHistoryRepository) UpdateMetadata(ctx context.Context, entry *entity.HistoryEntry) error {
127 if err := r.init(ctx); err != nil {
128 return err
129 }
130 if updater, ok := r.repo.(interface {
131 UpdateMetadata(context.Context, *entity.HistoryEntry) error
132 }); ok {
133 return updater.UpdateMetadata(ctx, entry)
134 }
135 return r.repo.Save(ctx, entry)
136}
137
138func (r *LazyHistoryRepository) Search(ctx context.Context, query string, limit int) ([]entity.HistoryMatch, error) {
139 if err := r.init(ctx); err != nil {
140 return nil, err
141 }
142 return r.repo.Search(ctx, query, limit)
143}
144
145func (r *LazyHistoryRepository) GetRecent(ctx context.Context, limit, offset int) ([]*entity.HistoryEntry, error) {
146 if err := r.init(ctx); err != nil {
147 return nil, err
148 }
149 return r.repo.GetRecent(ctx, limit, offset)
150}
151
152func (r *LazyHistoryRepository) GetRecentByDomain(ctx context.Context, domain string, limit, offset int) ([]*entity.HistoryEntry, error) {
153 if err := r.init(ctx); err != nil {
154 return nil, err
155 }
156 return r.repo.GetRecentByDomain(ctx, domain, limit, offset)
157}
158
159func (r *LazyHistoryRepository) GetRecentWindow(
160 ctx context.Context,
161 before time.Time,
162 beforeID int64,
163 limit int,
164) ([]*entity.HistoryEntry, error) {
165 if err := r.init(ctx); err != nil {
166 return nil, err
167 }
168 return r.repo.GetRecentWindow(ctx, before, beforeID, limit)
169}
170
171func (r *LazyHistoryRepository) GetRecentWindowByDomain(
172 ctx context.Context,
173 domain string,
174 before time.Time,
175 beforeID int64,
176 limit int,
177) ([]*entity.HistoryEntry, error) {
178 if err := r.init(ctx); err != nil {
179 return nil, err
180 }
181 return r.repo.GetRecentWindowByDomain(ctx, domain, before, beforeID, limit)
182}
183
184func (r *LazyHistoryRepository) GetRecentSince(ctx context.Context, days int) ([]*entity.HistoryEntry, error) {
185 if err := r.init(ctx); err != nil {
186 return nil, err
187 }
188 return r.repo.GetRecentSince(ctx, days)
189}
190
191func (r *LazyHistoryRepository) GetMostVisited(ctx context.Context, days int) ([]*entity.HistoryEntry, error) {
192 if err := r.init(ctx); err != nil {
193 return nil, err
194 }
195 return r.repo.GetMostVisited(ctx, days)
196}
197
198func (r *LazyHistoryRepository) GetAllRecentHistory(ctx context.Context) ([]*entity.HistoryEntry, error) {
199 if err := r.init(ctx); err != nil {
200 return nil, err
201 }
202 return r.repo.GetAllRecentHistory(ctx)
203}
204
205func (r *LazyHistoryRepository) GetAllMostVisited(ctx context.Context) ([]*entity.HistoryEntry, error) {
206 if err := r.init(ctx); err != nil {
207 return nil, err
208 }
209 return r.repo.GetAllMostVisited(ctx)
210}
211
212func (r *LazyHistoryRepository) IncrementVisitCount(ctx context.Context, url string) error {
213 if err := r.init(ctx); err != nil {
214 return err
215 }
216 return r.repo.IncrementVisitCount(ctx, url)
217}
218
219func (r *LazyHistoryRepository) IncrementVisitCountBy(ctx context.Context, url string, delta int) error {
220 if delta <= 0 {
221 return fmt.Errorf("visit delta must be > 0, got %d", delta)
222 }
223 if err := r.init(ctx); err != nil {
224 return err
225 }
226 if deltaRepo, ok := r.repo.(interface {
227 IncrementVisitCountBy(context.Context, string, int) error
228 }); ok {
229 return deltaRepo.IncrementVisitCountBy(ctx, url, delta)
230 }
231 // Fallback: single-increment loop for repos that don't support delta increment.
232 const fallbackCap = 256
233 if delta > fallbackCap {
234 return fmt.Errorf("visit delta %d exceeds fallback cap %d and repo does not support IncrementVisitCountBy", delta, fallbackCap)
235 }
236 for range delta {
237 if err := r.repo.IncrementVisitCount(ctx, url); err != nil {
238 return err
239 }
240 }
241 return nil
242}
243
244func (r *LazyHistoryRepository) Delete(ctx context.Context, id int64) error {
245 if err := r.init(ctx); err != nil {
246 return err
247 }
248 return r.repo.Delete(ctx, id)
249}
250
251func (r *LazyHistoryRepository) DeleteOlderThan(ctx context.Context, before time.Time) error {
252 if err := r.init(ctx); err != nil {
253 return err
254 }
255 return r.repo.DeleteOlderThan(ctx, before)
256}
257
258func (r *LazyHistoryRepository) DeleteSince(ctx context.Context, since time.Time) error {
259 if err := r.init(ctx); err != nil {
260 return err
261 }
262 return r.repo.DeleteSince(ctx, since)
263}
264
265func (r *LazyHistoryRepository) DeleteAll(ctx context.Context) error {
266 if err := r.init(ctx); err != nil {
267 return err
268 }
269 return r.repo.DeleteAll(ctx)
270}
271
272func (r *LazyHistoryRepository) DeleteByDomain(ctx context.Context, domain string) error {
273 if err := r.init(ctx); err != nil {
274 return err
275 }
276 return r.repo.DeleteByDomain(ctx, domain)
277}
278
279func (r *LazyHistoryRepository) GetStats(ctx context.Context) (*entity.HistoryStats, error) {
280 if err := r.init(ctx); err != nil {
281 return nil, err
282 }
283 return r.repo.GetStats(ctx)
284}
285
286func (r *LazyHistoryRepository) GetDomainStats(ctx context.Context, limit int) ([]*entity.DomainStat, error) {
287 if err := r.init(ctx); err != nil {
288 return nil, err
289 }
290 return r.repo.GetDomainStats(ctx, limit)
291}
292
293func (r *LazyHistoryRepository) GetHourlyDistribution(ctx context.Context) ([]*entity.HourlyDistribution, error) {
294 if err := r.init(ctx); err != nil {
295 return nil, err
296 }
297 return r.repo.GetHourlyDistribution(ctx)
298}
299
300func (r *LazyHistoryRepository) GetDailyVisitCount(ctx context.Context, daysAgo string) ([]*entity.DailyVisitCount, error) {
301 if err := r.init(ctx); err != nil {
302 return nil, err
303 }
304 return r.repo.GetDailyVisitCount(ctx, daysAgo)
305}
306
307// LazyFavoriteRepository wraps a favorite repository with lazy database initialization.
308type LazyFavoriteRepository struct {
309 provider port.DatabaseProvider
310 repo repository.FavoriteRepository
311 once sync.Once
312 initErr error
313}
314
315// NewLazyFavoriteRepository creates a lazy-loading favorite repository.
316func NewLazyFavoriteRepository(provider port.DatabaseProvider) repository.FavoriteRepository {
317 return &LazyFavoriteRepository{provider: provider}
318}
319
320func (r *LazyFavoriteRepository) init(ctx context.Context) error {
321 r.once.Do(func() {
322 db, err := r.provider.DB(ctx)
323 if err != nil {
324 r.initErr = err
325 return
326 }
327 r.repo = NewFavoriteRepository(db)
328 })
329 return r.initErr
330}
331
332func (r *LazyFavoriteRepository) Save(ctx context.Context, fav *entity.Favorite) error {
333 if err := r.init(ctx); err != nil {
334 return err
335 }
336 return r.repo.Save(ctx, fav)
337}
338
339func (r *LazyFavoriteRepository) FindByID(ctx context.Context, id entity.FavoriteID) (*entity.Favorite, error) {
340 if err := r.init(ctx); err != nil {
341 return nil, err
342 }
343 return r.repo.FindByID(ctx, id)
344}
345
346func (r *LazyFavoriteRepository) FindByURL(ctx context.Context, url string) (*entity.Favorite, error) {
347 if err := r.init(ctx); err != nil {
348 return nil, err
349 }
350 return r.repo.FindByURL(ctx, url)
351}
352
353func (r *LazyFavoriteRepository) GetAll(ctx context.Context) ([]*entity.Favorite, error) {
354 if err := r.init(ctx); err != nil {
355 return nil, err
356 }
357 return r.repo.GetAll(ctx)
358}
359
360func (r *LazyFavoriteRepository) GetByTag(ctx context.Context, tagID entity.TagID) ([]*entity.Favorite, error) {
361 if err := r.init(ctx); err != nil {
362 return nil, err
363 }
364 return r.repo.GetByTag(ctx, tagID)
365}
366
367func (r *LazyFavoriteRepository) GetByShortcut(ctx context.Context, key int) (*entity.Favorite, error) {
368 if err := r.init(ctx); err != nil {
369 return nil, err
370 }
371 return r.repo.GetByShortcut(ctx, key)
372}
373
374func (r *LazyFavoriteRepository) UpdatePosition(ctx context.Context, id entity.FavoriteID, position int) error {
375 if err := r.init(ctx); err != nil {
376 return err
377 }
378 return r.repo.UpdatePosition(ctx, id, position)
379}
380
381func (r *LazyFavoriteRepository) SetShortcut(ctx context.Context, id entity.FavoriteID, key *int) error {
382 if err := r.init(ctx); err != nil {
383 return err
384 }
385 return r.repo.SetShortcut(ctx, id, key)
386}
387
388func (r *LazyFavoriteRepository) Delete(ctx context.Context, id entity.FavoriteID) error {
389 if err := r.init(ctx); err != nil {
390 return err
391 }
392 return r.repo.Delete(ctx, id)
393}
394
395// LazyTagRepository wraps a tag repository with lazy database initialization.
396type LazyTagRepository struct {
397 provider port.DatabaseProvider
398 repo repository.TagRepository
399 once sync.Once
400 initErr error
401}
402
403// NewLazyTagRepository creates a lazy-loading tag repository.
404func NewLazyTagRepository(provider port.DatabaseProvider) repository.TagRepository {
405 return &LazyTagRepository{provider: provider}
406}
407
408func (r *LazyTagRepository) init(ctx context.Context) error {
409 r.once.Do(func() {
410 db, err := r.provider.DB(ctx)
411 if err != nil {
412 r.initErr = err
413 return
414 }
415 r.repo = NewTagRepository(db)
416 })
417 return r.initErr
418}
419
420func (r *LazyTagRepository) Save(ctx context.Context, tag *entity.Tag) error {
421 if err := r.init(ctx); err != nil {
422 return err
423 }
424 return r.repo.Save(ctx, tag)
425}
426
427func (r *LazyTagRepository) FindByID(ctx context.Context, id entity.TagID) (*entity.Tag, error) {
428 if err := r.init(ctx); err != nil {
429 return nil, err
430 }
431 return r.repo.FindByID(ctx, id)
432}
433
434func (r *LazyTagRepository) FindByName(ctx context.Context, name string) (*entity.Tag, error) {
435 if err := r.init(ctx); err != nil {
436 return nil, err
437 }
438 return r.repo.FindByName(ctx, name)
439}
440
441func (r *LazyTagRepository) GetAll(ctx context.Context) ([]*entity.Tag, error) {
442 if err := r.init(ctx); err != nil {
443 return nil, err
444 }
445 return r.repo.GetAll(ctx)
446}
447
448func (r *LazyTagRepository) AssignToFavorite(ctx context.Context, tagID entity.TagID, favID entity.FavoriteID) error {
449 if err := r.init(ctx); err != nil {
450 return err
451 }
452 return r.repo.AssignToFavorite(ctx, tagID, favID)
453}
454
455func (r *LazyTagRepository) RemoveFromFavorite(ctx context.Context, tagID entity.TagID, favID entity.FavoriteID) error {
456 if err := r.init(ctx); err != nil {
457 return err
458 }
459 return r.repo.RemoveFromFavorite(ctx, tagID, favID)
460}
461
462func (r *LazyTagRepository) GetForFavorite(ctx context.Context, favID entity.FavoriteID) ([]*entity.Tag, error) {
463 if err := r.init(ctx); err != nil {
464 return nil, err
465 }
466 return r.repo.GetForFavorite(ctx, favID)
467}
468
469func (r *LazyTagRepository) Delete(ctx context.Context, id entity.TagID) error {
470 if err := r.init(ctx); err != nil {
471 return err
472 }
473 return r.repo.Delete(ctx, id)
474}
475
476// LazyZoomRepository wraps a zoom repository with lazy database initialization.
477type LazyZoomRepository struct {
478 provider port.DatabaseProvider
479 repo repository.ZoomRepository
480 once sync.Once
481 initErr error
482}
483
484// NewLazyZoomRepository creates a lazy-loading zoom repository.
485func NewLazyZoomRepository(provider port.DatabaseProvider) repository.ZoomRepository {
486 return &LazyZoomRepository{provider: provider}
487}
488
489func (r *LazyZoomRepository) init(ctx context.Context) error {
490 r.once.Do(func() {
491 db, err := r.provider.DB(ctx)
492 if err != nil {
493 r.initErr = err
494 return
495 }
496 r.repo = NewZoomRepository(db)
497 })
498 return r.initErr
499}
500
501func (r *LazyZoomRepository) Get(ctx context.Context, domain string) (*entity.ZoomLevel, error) {
502 if err := r.init(ctx); err != nil {
503 return nil, err
504 }
505 return r.repo.Get(ctx, domain)
506}
507
508func (r *LazyZoomRepository) Set(ctx context.Context, zoom *entity.ZoomLevel) error {
509 if err := r.init(ctx); err != nil {
510 return err
511 }
512 return r.repo.Set(ctx, zoom)
513}
514
515func (r *LazyZoomRepository) Delete(ctx context.Context, domain string) error {
516 if err := r.init(ctx); err != nil {
517 return err
518 }
519 return r.repo.Delete(ctx, domain)
520}
521
522func (r *LazyZoomRepository) GetAll(ctx context.Context) ([]*entity.ZoomLevel, error) {
523 if err := r.init(ctx); err != nil {
524 return nil, err
525 }
526 return r.repo.GetAll(ctx)
527}
528
529// LazySessionRepository wraps a session repository with lazy database initialization.
530type LazySessionRepository struct {
531 provider port.DatabaseProvider
532 repo repository.SessionRepository
533 once sync.Once
534 initErr error
535}
536
537// NewLazySessionRepository creates a lazy-loading session repository.
538func NewLazySessionRepository(provider port.DatabaseProvider) repository.SessionRepository {
539 return &LazySessionRepository{provider: provider}
540}
541
542func (r *LazySessionRepository) init(ctx context.Context) error {
543 r.once.Do(func() {
544 db, err := r.provider.DB(ctx)
545 if err != nil {
546 r.initErr = err
547 return
548 }
549 r.repo = NewSessionRepository(db)
550 })
551 return r.initErr
552}
553
554func (r *LazySessionRepository) Save(ctx context.Context, session *entity.Session) error {
555 if err := r.init(ctx); err != nil {
556 return err
557 }
558 return r.repo.Save(ctx, session)
559}
560
561func (r *LazySessionRepository) FindByID(ctx context.Context, id entity.SessionID) (*entity.Session, error) {
562 if err := r.init(ctx); err != nil {
563 return nil, err
564 }
565 return r.repo.FindByID(ctx, id)
566}
567
568func (r *LazySessionRepository) GetActive(ctx context.Context) (*entity.Session, error) {
569 if err := r.init(ctx); err != nil {
570 return nil, err
571 }
572 return r.repo.GetActive(ctx)
573}
574
575func (r *LazySessionRepository) GetRecent(ctx context.Context, limit int) ([]*entity.Session, error) {
576 if err := r.init(ctx); err != nil {
577 return nil, err
578 }
579 return r.repo.GetRecent(ctx, limit)
580}
581
582func (r *LazySessionRepository) MarkEnded(ctx context.Context, id entity.SessionID, endedAt time.Time) error {
583 if err := r.init(ctx); err != nil {
584 return err
585 }
586 return r.repo.MarkEnded(ctx, id, endedAt)
587}
588
589func (r *LazySessionRepository) Delete(ctx context.Context, id entity.SessionID) error {
590 if err := r.init(ctx); err != nil {
591 return err
592 }
593 return r.repo.Delete(ctx, id)
594}
595
596func (r *LazySessionRepository) DeleteOldestExited(ctx context.Context, keepCount int) (int64, error) {
597 if err := r.init(ctx); err != nil {
598 return 0, err
599 }
600 return r.repo.DeleteOldestExited(ctx, keepCount)
601}
602
603func (r *LazySessionRepository) DeleteExitedBefore(ctx context.Context, cutoff time.Time) (int64, error) {
604 if err := r.init(ctx); err != nil {
605 return 0, err
606 }
607 return r.repo.DeleteExitedBefore(ctx, cutoff)
608}
609
610// LazySessionStateRepository wraps a session state repository with lazy database initialization.
611type LazySessionStateRepository struct {
612 provider port.DatabaseProvider
613 repo repository.SessionStateRepository
614 once sync.Once
615 initErr error
616}
617
618// NewLazySessionStateRepository creates a lazy-loading session state repository.
619func NewLazySessionStateRepository(provider port.DatabaseProvider) repository.SessionStateRepository {
620 return &LazySessionStateRepository{provider: provider}
621}
622
623func (r *LazySessionStateRepository) init(ctx context.Context) error {
624 r.once.Do(func() {
625 db, err := r.provider.DB(ctx)
626 if err != nil {
627 r.initErr = err
628 return
629 }
630 r.repo = NewSessionStateRepository(db)
631 })
632 return r.initErr
633}
634
635func (r *LazySessionStateRepository) SaveSnapshot(ctx context.Context, state *entity.SessionState) error {
636 if err := r.init(ctx); err != nil {
637 return err
638 }
639 return r.repo.SaveSnapshot(ctx, state)
640}
641
642func (r *LazySessionStateRepository) GetSnapshot(ctx context.Context, sessionID entity.SessionID) (*entity.SessionState, error) {
643 if err := r.init(ctx); err != nil {
644 return nil, err
645 }
646 return r.repo.GetSnapshot(ctx, sessionID)
647}
648
649func (r *LazySessionStateRepository) DeleteSnapshot(ctx context.Context, sessionID entity.SessionID) error {
650 if err := r.init(ctx); err != nil {
651 return err
652 }
653 return r.repo.DeleteSnapshot(ctx, sessionID)
654}
655
656func (r *LazySessionStateRepository) GetAllSnapshots(ctx context.Context) ([]*entity.SessionState, error) {
657 if err := r.init(ctx); err != nil {
658 return nil, err
659 }
660 return r.repo.GetAllSnapshots(ctx)
661}
662
663func (r *LazySessionStateRepository) GetTotalSnapshotsSize(ctx context.Context) (int64, error) {
664 if err := r.init(ctx); err != nil {
665 return 0, err
666 }
667 return r.repo.GetTotalSnapshotsSize(ctx)
668}
669
670// LazyContentWhitelistRepository wraps a content whitelist repository with lazy database initialization.
671type LazyContentWhitelistRepository struct {
672 provider port.DatabaseProvider
673 repo repository.ContentWhitelistRepository
674 once sync.Once
675 initErr error
676}
677
678// NewLazyContentWhitelistRepository creates a lazy-loading content whitelist repository.
679func NewLazyContentWhitelistRepository(provider port.DatabaseProvider) repository.ContentWhitelistRepository {
680 return &LazyContentWhitelistRepository{provider: provider}
681}
682
683func (r *LazyContentWhitelistRepository) init(ctx context.Context) error {
684 r.once.Do(func() {
685 db, err := r.provider.DB(ctx)
686 if err != nil {
687 r.initErr = err
688 return
689 }
690 r.repo = NewContentWhitelistRepository(db)
691 })
692 return r.initErr
693}
694
695func (r *LazyContentWhitelistRepository) Add(ctx context.Context, domain string) error {
696 if err := r.init(ctx); err != nil {
697 return err
698 }
699 return r.repo.Add(ctx, domain)
700}
701
702func (r *LazyContentWhitelistRepository) Remove(ctx context.Context, domain string) error {
703 if err := r.init(ctx); err != nil {
704 return err
705 }
706 return r.repo.Remove(ctx, domain)
707}
708
709func (r *LazyContentWhitelistRepository) Contains(ctx context.Context, domain string) (bool, error) {
710 if err := r.init(ctx); err != nil {
711 return false, err
712 }
713 return r.repo.Contains(ctx, domain)
714}
715
716func (r *LazyContentWhitelistRepository) GetAll(ctx context.Context) ([]string, error) {
717 if err := r.init(ctx); err != nil {
718 return nil, err
719 }
720 return r.repo.GetAll(ctx)
721}
722
723// LazyRepositories holds all lazy-loaded repositories.
724type LazyRepositories struct {
725 History repository.HistoryRepository
726 Favorite repository.FavoriteRepository
727 Tag repository.TagRepository
728 Zoom repository.ZoomRepository
729 Session repository.SessionRepository
730 SessionState repository.SessionStateRepository
731 Filter repository.ContentWhitelistRepository
732 Permission port.PermissionRepository
733}
734
735// NewLazyRepositories creates all lazy repositories from a database provider.
736func NewLazyRepositories(provider port.DatabaseProvider) *LazyRepositories {
737 return &LazyRepositories{
738 History: NewLazyHistoryRepository(provider),
739 Favorite: NewLazyFavoriteRepository(provider),
740 Tag: NewLazyTagRepository(provider),
741 Zoom: NewLazyZoomRepository(provider),
742 Session: NewLazySessionRepository(provider),
743 SessionState: NewLazySessionStateRepository(provider),
744 Filter: NewLazyContentWhitelistRepository(provider),
745 Permission: NewLazyPermissionRepository(provider),
746 }
747}
748
749// GetDB returns the underlying *sql.DB for operations that need it directly.
750// Returns nil if the database hasn't been initialized yet.
751func GetDB(provider port.DatabaseProvider, ctx context.Context) *sql.DB {
752 db, err := provider.DB(ctx)
753 if err != nil {
754 return nil
755 }
756 return db
757}