diff --git a/README.md b/README.md index f18b5d3..9ecc299 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Watch tower provides a lite weight approach to grouping information by product w | Group repositories by Product | User github topics to group repos by products | | Dashboard view | Quickly view the overall health of a group of products by viewing open PRs, security vulnerabilities and repositories | | Product view | Focused view for open PRs, security vulnerabilities and repositories | +| Notification on new PR / Issue | Get notifications when new PRs or Issues are created in the watched repositories accross all orgs | | | Local only | Data will never leave your device, settings page includes a kill switch to remove all data from the device. GITHUB PAT token only required read access | ## Roadmap diff --git a/frontend/src/lib/wailsjs/go/watchtower/Service.d.ts b/frontend/src/lib/wailsjs/go/watchtower/Service.d.ts index 8421724..1484ec8 100755 --- a/frontend/src/lib/wailsjs/go/watchtower/Service.d.ts +++ b/frontend/src/lib/wailsjs/go/watchtower/Service.d.ts @@ -9,11 +9,7 @@ export function CreateOrganisation(arg1:string,arg2:string,arg3:string,arg4:stri export function CreateProduct(arg1:string,arg2:string,arg3:Array,arg4:number):Promise; -export function CreateUnreadNotification():Promise; - -export function CreateUnreadPRNotification():Promise; - -export function CreateUnreadSecurityNotification():Promise; +export function CreateUnreadNotification():Promise; export function DeleteAllOrgs():Promise; diff --git a/frontend/src/lib/wailsjs/go/watchtower/Service.js b/frontend/src/lib/wailsjs/go/watchtower/Service.js index f2ab0d3..c6a0221 100755 --- a/frontend/src/lib/wailsjs/go/watchtower/Service.js +++ b/frontend/src/lib/wailsjs/go/watchtower/Service.js @@ -14,14 +14,6 @@ export function CreateUnreadNotification() { return window['go']['watchtower']['Service']['CreateUnreadNotification'](); } -export function CreateUnreadPRNotification() { - return window['go']['watchtower']['Service']['CreateUnreadPRNotification'](); -} - -export function CreateUnreadSecurityNotification() { - return window['go']['watchtower']['Service']['CreateUnreadSecurityNotification'](); -} - export function DeleteAllOrgs() { return window['go']['watchtower']['Service']['DeleteAllOrgs'](); } diff --git a/internal/database/database.go b/internal/database/database.go index 854cfb6..4157c18 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -1,3 +1,4 @@ +// Package database provides functionality for interacting with the SQLite database, including migrations and query initialization. package database import ( @@ -14,14 +15,17 @@ var ddl string const dbName = "watchtower.db" +// Migrator handles database schema initialization and migrations. type Migrator struct { db *sql.DB } +// NewMigrator creates a new Migrator instance with the provided database connection. func NewMigrator(db *sql.DB) *Migrator { return &Migrator{db: db} } +// Init initializes the database schema by executing the DDL. func (m *Migrator) Init() error { _, err := m.db.Exec(ddl) @@ -41,6 +45,7 @@ func NewDBFromProvider(filePath string) (*Queries, *sql.DB, error) { return New(db), db, nil } +// resolveDBPath resolves the database file path, handling the special ":memory:" case. func resolveDBPath(filePath string) string { if filePath == ":memory:" { return filePath @@ -49,6 +54,7 @@ func resolveDBPath(filePath string) string { return path.Join(filePath, dbName, "?_busy_timeout=5000") } +// IsErrUniqueConstraint checks if the error is a SQLite unique constraint violation. func IsErrUniqueConstraint(err error) bool { return err != nil && strings.Contains(err.Error(), "constraint failed: UNIQUE constraint failed") } diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 982d316..6c9394b 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -1,3 +1,4 @@ +// Package logging provides structured logging using slog and context-based logger propagation. package logging import ( @@ -5,9 +6,11 @@ import ( "os" ) +// LoggerType defines the available logger output formats. type LoggerType string const ( + // LoggerText represents a text-based logger output format. LoggerText LoggerType = "Text" ) @@ -18,7 +21,7 @@ func init() { baseLogger = New(slog.LevelInfo) } -// New logger +// New initializes and returns a new text-based logger with the specified level. func New(level slog.Level) *slog.Logger { baseLogger = textHandler(level) return baseLogger diff --git a/internal/notifications/service.go b/internal/notifications/service.go index 12ca5f5..b2eb641 100644 --- a/internal/notifications/service.go +++ b/internal/notifications/service.go @@ -1,3 +1,4 @@ +// Package notifications provides a service for managing and retrieving organization notifications. package notifications import ( @@ -8,6 +9,7 @@ import ( "watchtower/internal/logging" ) +// New creates a new Service instance with the provided store and transaction database. func New(db Store, txnDB *sql.DB, txnFunc func(tx *sql.Tx) Store) *Service { return &Service{ store: db, @@ -16,6 +18,7 @@ func New(db Store, txnDB *sql.DB, txnFunc func(tx *sql.Tx) Store) *Service { } } +// CreateNotificationParams holds the parameters for creating a new notification. type CreateNotificationParams struct { OrgID int64 ExternalID string @@ -80,6 +83,7 @@ func (s *Service) GetUnreadNotifications(ctx context.Context) ([]Notification, e return fromNotificationModels(models), nil } +// GetNotificationByExternalID fetches a single notification by its external ID. func (s *Service) GetNotificationByExternalID(ctx context.Context, externalID string) (Notification, error) { logger := logging.FromContext(ctx).With("externalID", externalID, "service", "notifications") logger.Debug("Fetching notification by external ID") diff --git a/internal/organisations/service.go b/internal/organisations/service.go index 9821f44..13e72d5 100644 --- a/internal/organisations/service.go +++ b/internal/organisations/service.go @@ -1,3 +1,4 @@ +// Package organisations provides a service for managing and retrieving GitHub organizations and their configurations. package organisations import ( @@ -8,12 +9,14 @@ import ( "watchtower/internal/logging" ) +// Service handles organization-related business logic. type Service struct { store OrgStore txnDB *sql.DB txnFunc func(tx *sql.Tx) OrgStore } +// New creates a new Service instance with the provided store, transaction database and transaction function. func New(store OrgStore, txnDB *sql.DB, txnFunc func(tx *sql.Tx) OrgStore) *Service { return &Service{ store: store, @@ -22,7 +25,7 @@ func New(store OrgStore, txnDB *sql.DB, txnFunc func(tx *sql.Tx) OrgStore) *Serv } } -// Create creates a new organisation in the database with the given parameters and returns its DTO representation. +// Create creates a new organization with the given parameters and returns the created organization or an error. func (s Service) Create(ctx context.Context, params CreateOrgParams) (OrganisationDTO, error) { logger := logging.FromContext(ctx).With("service", "organisations") diff --git a/internal/products/service.go b/internal/products/service.go index 33d362a..0e791e3 100644 --- a/internal/products/service.go +++ b/internal/products/service.go @@ -1,3 +1,4 @@ +// Package products provides a service for managing products, repositories, pull requests, and security vulnerabilities. package products import ( @@ -10,16 +11,19 @@ import ( "watchtower/internal/logging" ) +// Service handles product-related business logic. type Service struct { store ProductStore } +// New creates a new Service instance with the provided product store. func New(store ProductStore) *Service { return &Service{ store: store, } } +// Create creates a new product with the given parameters and returns the created product or an error. func (s *Service) Create(ctx context.Context, params CreateProductParams) (ProductDTO, error) { logger := logging.FromContext(ctx).With("service", "products") diff --git a/internal/watchtower/notifications.go b/internal/watchtower/notifications.go index 8269964..fbc1128 100644 --- a/internal/watchtower/notifications.go +++ b/internal/watchtower/notifications.go @@ -15,6 +15,7 @@ func (s *Service) MarkNotificationAsRead(notificationID int64) error { return s.notificationSvc.MarkNotificationAsRead(s.ctx, notificationID) } +// DeleteOldNotifications deletes notifications that are older than 90 days. func (s *Service) DeleteOldNotifications() error { nintyDaysAgo := time.Now().AddDate(0, 0, -90) return s.notificationSvc.DeleteNotificationsByDate(s.ctx, nintyDaysAgo) diff --git a/internal/watchtower/organisations.go b/internal/watchtower/organisations.go index 0e6658f..5eedfa5 100644 --- a/internal/watchtower/organisations.go +++ b/internal/watchtower/organisations.go @@ -18,10 +18,12 @@ func (s *Service) GetDefaultOrganisation() (organisations.OrganisationDTO, error return s.orgSvc.GetDefault(s.ctx) } +// SetDefaultOrg marks the organization with the specified ID as the default organization. func (s *Service) SetDefaultOrg(id int64) (organisations.OrganisationDTO, error) { return s.orgSvc.SetDefault(s.ctx, id) } +// GetOrganisationByID retrieves an organization's details by its ID. func (s *Service) GetOrganisationByID(id int64) (organisations.OrganisationDTO, error) { return s.orgSvc.Get(s.ctx, id) } @@ -31,6 +33,7 @@ func (s *Service) GetAllOrganisations() ([]organisations.OrganisationDTO, error) return s.orgSvc.GetAll(s.ctx) } +// DeleteAllOrgs deletes all organizations and their associated products. func (s *Service) DeleteAllOrgs() error { list, err := s.orgSvc.GetAll(s.ctx) if err != nil { diff --git a/internal/watchtower/sync.go b/internal/watchtower/sync.go index 51ef08f..9774b54 100644 --- a/internal/watchtower/sync.go +++ b/internal/watchtower/sync.go @@ -163,7 +163,7 @@ func (s *Service) SyncOrg(orgId int64) error { return nil } -// SyncProduct synchronizes a product with the given ID by retrieving its details and associated organization data. +// SyncProduct synchronizes a single product by its ID. func (s *Service) SyncProduct(id int64) error { logger := logging.FromContext(s.ctx) diff --git a/internal/watchtower/worker.go b/internal/watchtower/worker.go index 9039043..0c771b2 100644 --- a/internal/watchtower/worker.go +++ b/internal/watchtower/worker.go @@ -1,3 +1,4 @@ +// Package watchtower provides the main application logic, including the service layer and background workers. package watchtower import ( @@ -12,6 +13,7 @@ import ( "github.com/wailsapp/wails/v2/pkg/runtime" ) +// Workers manages background jobs for syncing data and cleaning up old records. type Workers struct { ctx context.Context watchTower *Service @@ -19,6 +21,7 @@ type Workers struct { logger *slog.Logger } +// NewWorkers initializes and returns a new Workers instance with the provided service. func NewWorkers(wt *Service) (*Workers, error) { logger := logging.FromContext(context.Background()).With("service", "workers") s, err := gocron.NewScheduler() @@ -34,6 +37,7 @@ func NewWorkers(wt *Service) (*Workers, error) { }, nil } +// AddJobs registers the background jobs to the scheduler. func (w *Workers) AddJobs() error { if _, err := w.cron.NewJob( @@ -54,6 +58,7 @@ func (w *Workers) AddJobs() error { return nil } +// Start begins the execution of scheduled jobs. func (w *Workers) Start(ctx context.Context) { w.ctx = ctx @@ -62,6 +67,7 @@ func (w *Workers) Start(ctx context.Context) { w.cron.Start() } +// Stop halts the execution of scheduled jobs and shuts down the scheduler. func (w *Workers) Stop() { w.logger.Debug("Stopping workers")