Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .air.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ tmp_dir = "/tmp/air"
delay = 500
kill_delay = "1s"
rerun = false
poll = true
poll_interval = 1000

[log]
time = false
Expand Down
2 changes: 1 addition & 1 deletion internal/bootstrap/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func NewContainer(cfg *Config, logger *slog.Logger) (*Container, error) {
CatalogHandler: cataloghttp.NewHandler(catalogStore, logger),
OrganizationsHandler: organizationshttp.NewHandler(orgStore, notificationSvc, logger),
DeploymentsHandler: deploymentshttp.NewHandler(createDeployment, serverAction, deploymentStore, cfg.SecretKey, logger),
ProjectsHandler: projectshttp.NewHandler(projectsStore, orgStore, logger),
ProjectsHandler: projectshttp.NewHandler(projectsStore, orgStore, notificationSvc, logger),
WorkloadsHandler: workloadshttp.NewHandler(projectsStore, orgStore, workloadsStore, provisionHandler, workloadAction, bus, logger),
NodesHandler: nodeshttp.NewHandler(nodeStore, logger),
BillingHandler: billinghttp.NewHandler(logger),
Expand Down
12 changes: 12 additions & 0 deletions internal/core/notifications/adapters/persistence/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ func (s *PostgresNotificationStore) MarkAllRead(ctx context.Context, userID stri
return nil
}

func (s *PostgresNotificationStore) MarkReadByInviteID(ctx context.Context, userID, inviteID string) error {
_, err := s.db.ExecContext(ctx, `
UPDATE notifications SET read_at = $1
WHERE user_id = $2 AND type = 'project_invitation' AND read_at IS NULL
AND data->>'invite_id' = $3`,
time.Now().UTC(), userID, inviteID)
if err != nil {
return fmt.Errorf("mark notification read by invite id: %w", err)
}
return nil
}

func (s *PostgresNotificationStore) Delete(ctx context.Context, id, userID string) error {
_, err := s.db.ExecContext(ctx, `
DELETE FROM notifications WHERE id = $1 AND user_id = $2`,
Expand Down
5 changes: 5 additions & 0 deletions internal/core/notifications/application/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ func (s *Service) MarkAllRead(ctx context.Context, userID string) error {
return s.repo.MarkAllRead(ctx, userID)
}

// MarkReadByInviteID marks the project_invitation notification for this invite as read.
func (s *Service) MarkReadByInviteID(ctx context.Context, userID, inviteID string) error {
return s.repo.MarkReadByInviteID(ctx, userID, inviteID)
}

// Delete removes a notification. The notification must belong to userID.
func (s *Service) Delete(ctx context.Context, id, userID string) error {
return s.repo.Delete(ctx, id, userID)
Expand Down
4 changes: 4 additions & 0 deletions internal/core/notifications/ports/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type NotificationRepository interface {
// MarkAllRead sets read_at to now for every unread notification owned by userID.
MarkAllRead(ctx context.Context, userID string) error

// MarkReadByInviteID marks as read any unread project_invitation notification for userID
// whose data->>'invite_id' matches inviteID.
MarkReadByInviteID(ctx context.Context, userID, inviteID string) error

// Delete removes a notification owned by userID.
Delete(ctx context.Context, id, userID string) error
}
9 changes: 9 additions & 0 deletions internal/core/organizations/adapters/persistence/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ func (s *PostgresOrgStore) GetMember(ctx context.Context, orgID, userID string)
return scanMember(row)
}

func (s *PostgresOrgStore) FindMemberByEmail(ctx context.Context, email string) (*domain.Member, error) {
row := s.db.QueryRowContext(ctx, `
SELECT org_id, user_id, email, display_name, role, created_at
FROM organization_members
WHERE LOWER(email) = LOWER($1)
LIMIT 1`, email)
return scanMember(row)
}

func (s *PostgresOrgStore) AddMember(ctx context.Context, m *domain.Member) error {
_, err := s.db.ExecContext(ctx, `
INSERT INTO organization_members (org_id, user_id, email, display_name, role, created_at)
Expand Down
1 change: 1 addition & 0 deletions internal/core/organizations/ports/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type OrganizationRepository interface {
ListByUserID(ctx context.Context, userID string) ([]*domain.Organization, error)
ListMembers(ctx context.Context, orgID string) ([]*domain.Member, error)
GetMember(ctx context.Context, orgID, userID string) (*domain.Member, error)
FindMemberByEmail(ctx context.Context, email string) (*domain.Member, error)
AddMember(ctx context.Context, member *domain.Member) error
UpdateMemberRole(ctx context.Context, orgID, userID, role string) error
RemoveMember(ctx context.Context, orgID, userID string) error
Expand Down
Loading
Loading