Documentation
¶
Index ¶
- Variables
- type AdminAuditLogEntry
- type AdminRepository
- type AdminStats
- type AdminUserRow
- type AircraftRepository
- type AircraftTypeTrendRow
- type AirportDirectionCount
- type AnalyticsAircraftRow
- type AnalyticsAirportRow
- type AnalyticsApproachTypeRow
- type AnalyticsBucket
- type AnalyticsFlightRef
- type AnalyticsGroupRow
- type AnalyticsMonthRow
- type AnalyticsPersonRow
- type AnalyticsRouteRow
- type AnalyticsTotals
- type AnalyticsYearRow
- type AnnouncementRepository
- type BackupDestinationRepository
- type BackupRunRepository
- type CategoryStatRow
- type ClassStatRow
- type ContactRepository
- type CredentialRepository
- type CustomCurrencyRuleRepository
- type CustomReportGroup
- type CustomReportRepository
- type DeletionRepository
- type DocumentFileRepository
- type EmailDeliveryRepository
- type EmailVerificationTokenRepository
- type FlightBaselineRepository
- type FlightCrewRepository
- type FlightImportRepository
- type FlightQueryOptions
- type FlightRepository
- type FlightSessionRepository
- type FlightSignatureRepository
- type IdempotencyRepository
- type LicenseRepository
- type MonthlyTrendRow
- type NotificationRepository
- type OIDCIdentityRepository
- type PasswordResetTokenRepository
- type RefreshTokenRepository
- type ReportsRepository
- type RouteCount
- type UserContentRepository
- type UserRepository
- type WebAuthnCredentialRepository
- type WebAuthnSessionRepository
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotFound is returned when a resource is not found ErrNotFound = errors.New("not found") // ErrDuplicateEmail is returned when attempting to create a user with an email that already exists ErrDuplicateEmail = errors.New("email already exists") // ErrDuplicateRegistration is returned when a user already has an aircraft with the same registration ErrDuplicateRegistration = errors.New("aircraft registration already exists") // ErrDuplicate is returned when a uniqueness constraint is violated ErrDuplicate = errors.New("duplicate resource") // ErrDocumentFileLimit is returned when an image would push a licence or // credential past its per-document image cap. ErrDocumentFileLimit = errors.New("document image limit reached") )
Functions ¶
This section is empty.
Types ¶
type AdminAuditLogEntry ¶ added in v1.3.4
type AdminAuditLogEntry struct {
ID uuid.UUID
AdminUserID uuid.UUID
AdminEmail *string
AdminName *string
Action string
TargetUserID *uuid.UUID
TargetUserEmail *string
TargetUserName *string
// Details is the JSON metadata payload for the action.
Details []byte
CreatedAt time.Time
}
AdminAuditLogEntry is one admin action, joined with the acting and target users' current email/name (nil when the account has since been deleted — audit rows outlive their users via ON DELETE SET NULL).
type AdminRepository ¶ added in v1.3.4
type AdminRepository interface {
// GetStats returns the dashboard counters, evaluated relative to `now`
// (month-to-date, trailing week, and lock expiry all derive from it).
GetStats(ctx context.Context, now time.Time) (*AdminStats, error)
// MigrationVersion returns the highest cleanly-applied schema migration
// version, or 0 when none is recorded.
MigrationVersion(ctx context.Context) (int, error)
// CountAuditLog counts all audit log entries.
CountAuditLog(ctx context.Context) (int, error)
// ListAuditLog returns audit entries newest first.
ListAuditLog(ctx context.Context, limit, offset int) ([]*AdminAuditLogEntry, error)
// InsertAuditLog appends one action. Only ID, AdminUserID, Action,
// TargetUserID, Details and CreatedAt are stored; the joined email/name
// fields are read-side decoration.
InsertAuditLog(ctx context.Context, entry *AdminAuditLogEntry) error
// ListUsers returns one page of the user listing, newest first, plus the
// total row count for the (optionally searched) set. A non-empty search
// matches email or name case-insensitively as a substring.
ListUsers(ctx context.Context, search string, limit, offset int) ([]*AdminUserRow, int, error)
// UnlockUser resets the failed-login counter and clears any lockout.
UnlockUser(ctx context.Context, userID uuid.UUID) error
// DeleteExpiredRefreshTokens removes refresh tokens that are expired or
// revoked, returning how many rows went.
DeleteExpiredRefreshTokens(ctx context.Context, now time.Time) (int64, error)
// DeleteExpiredPasswordResetTokens removes password-reset tokens that are
// expired or already used, returning how many rows went.
DeleteExpiredPasswordResetTokens(ctx context.Context, now time.Time) (int64, error)
}
AdminRepository backs the admin console: instance-wide statistics, the audit log, the user listing, and the maintenance sweeps that need affected-row counts.
type AdminStats ¶ added in v1.3.4
type AdminStats struct {
TotalUsers int
TotalFlights int
TotalSimulatorSessions int
TotalPassengerFlights int
TotalAircraft int
TotalContacts int
TotalCredentials int
TotalImports int
FlightsThisMonth int
NewUsersThisWeek int
LockedAccounts int
DisabledAccounts int
// ActiveSessions counts live sessions across all users.
ActiveSessions int
// TotalCustomReports counts saved custom reports across all users.
TotalCustomReports int
// ImportsByFormat maps import_format value to import count. Never nil;
// formats with no imports are absent.
ImportsByFormat map[string]int
// BackupDestinationsByProvider maps provider name to destination count.
// Never nil.
BackupDestinationsByProvider map[string]int
}
AdminStats aggregates the instance-wide counters shown on the admin dashboard. A counter whose query fails is reported as zero.
type AdminUserRow ¶ added in v1.3.4
type AdminUserRow struct {
ID uuid.UUID
Email string
Name string
CreatedAt time.Time
LastLoginAt *time.Time
EmailVerified bool
TwoFactorEnabled bool
Disabled bool
FailedLoginAttempts int
LockedUntil *time.Time
VerificationReminderSentAt *time.Time
EmailSuppressed bool
FlightCount int
AircraftCount int
}
AdminUserRow is one row of the admin user listing: the account plus the per-user content counts and suppression flag the console displays.
type AircraftRepository ¶
type AircraftRepository interface {
Create(ctx context.Context, aircraft *models.Aircraft) error
GetByID(ctx context.Context, id uuid.UUID) (*models.Aircraft, error)
// GetByUserID retrieves all aircraft for a user. A non-nil updatedSince
// narrows the result to aircraft changed strictly after that instant.
GetByUserID(ctx context.Context, userID uuid.UUID, updatedSince *time.Time) ([]*models.Aircraft, error)
// GetPageByUserID retrieves one registration-ordered page of a user's
// aircraft, bounded by LIMIT/OFFSET in SQL, and the total row count
// matching the same filter. A non-nil updatedSince narrows both to
// aircraft changed strictly after that instant.
GetPageByUserID(ctx context.Context, userID uuid.UUID, updatedSince *time.Time, limit, offset int) ([]*models.Aircraft, int, error)
Update(ctx context.Context, aircraft *models.Aircraft) error
// UpdateWithFlightRename updates the aircraft and, in the same
// transaction, repoints flights (and open flight sessions) logged under
// oldRegistration to the aircraft's new registration. Returns the number
// of flights updated.
UpdateWithFlightRename(ctx context.Context, aircraft *models.Aircraft, oldRegistration string) (int, error)
Delete(ctx context.Context, id uuid.UUID) error
CountByUserID(ctx context.Context, userID uuid.UUID) (int, error)
// GetStatsByUserID aggregates flight statistics per aircraft registration
GetStatsByUserID(ctx context.Context, userID uuid.UUID) ([]*models.AircraftStats, error)
// GetTypeStatsByUserID aggregates flight statistics per aircraft type
GetTypeStatsByUserID(ctx context.Context, userID uuid.UUID) ([]*models.AircraftTypeStats, error)
// GetRecencyRowsByUserID returns per-day landing counts for flights in
// the preceding 90 days, newest first, for recency derivation
GetRecencyRowsByUserID(ctx context.Context, userID uuid.UUID) ([]*models.AircraftRecencyRow, error)
}
type AircraftTypeTrendRow ¶ added in v1.3.4
AircraftTypeTrendRow is the per-type rollup of the legacy trends report.
type AirportDirectionCount ¶ added in v1.3.4
AirportDirectionCount is a per-airport leg count in one direction ("dep" or "arr").
type AnalyticsAircraftRow ¶ added in v1.3.4
type AnalyticsAircraftRow struct {
Label string
SubLabel *string
Flights int
TotalMinutes int
PicMinutes int
DualMinutes int
NightMinutes int
IfrMinutes int
Landings int
DistanceNM float64
FirstFlightDate *time.Time
LastFlightDate *time.Time
}
AnalyticsAircraftRow is a per-aircraft-type or per-registration rollup.
type AnalyticsAirportRow ¶ added in v1.3.4
AnalyticsAirportRow counts one airport's legs. Flights counts each flight once even when it both departs from and arrives at the airport.
type AnalyticsApproachTypeRow ¶ added in v1.3.4
AnalyticsApproachTypeRow counts structured approaches by type.
type AnalyticsBucket ¶ added in v1.3.4
AnalyticsBucket is one histogram cell keyed by an integer (ISO weekday, month number, hour, or duration bucket index).
type AnalyticsFlightRef ¶ added in v1.3.4
type AnalyticsFlightRef struct {
ID uuid.UUID
Date time.Time
AircraftReg *string
AircraftType *string
DepartureICAO *string
ArrivalICAO *string
TotalMinutes int
DistanceNM float64
}
AnalyticsFlightRef identifies a single record-holding flight.
type AnalyticsGroupRow ¶ added in v1.3.4
type AnalyticsGroupRow struct {
Label string
Flights int
TotalMinutes int
PicMinutes int
DualMinutes int
Landings int
}
AnalyticsGroupRow is a generic labelled rollup (class, category).
type AnalyticsMonthRow ¶ added in v1.3.4
type AnalyticsMonthRow struct {
Month string // "YYYY-MM"
Flights int
TotalMinutes int
PicMinutes int
SicMinutes int
DualMinutes int
DualGivenMinutes int
SoloMinutes int
NightMinutes int
IfrMinutes int
LandingsDay int
LandingsNight int
DistanceNM float64
}
AnalyticsMonthRow is one month of the analytics time series.
type AnalyticsPersonRow ¶ added in v1.3.4
type AnalyticsPersonRow struct {
Name string
// Role is the role the person most flies as, by time.
Role *string
// Roles is every role the person has held, most time first.
Roles []string
// ContactID is the contact the person's crew entries link to, if any.
ContactID *string
Flights int
TotalMinutes int
LastFlightDate *time.Time
}
AnalyticsPersonRow is a per-person rollup (instructor or crew member). Role, Roles and ContactID are set only for crew rows; TotalMinutes is dual time for instructors and block time for crew, mirroring what each view displays.
type AnalyticsRouteRow ¶ added in v1.3.4
type AnalyticsRouteRow struct {
DepartureICAO string
ArrivalICAO string
Flights int
TotalMinutes int
DistanceNM float64
}
AnalyticsRouteRow is a per-route rollup. DistanceNM is the stored per-leg maximum; zero when no flight on the route recorded a distance.
type AnalyticsTotals ¶ added in v1.3.4
type AnalyticsTotals struct {
TotalFlights int
TotalMinutes int
PicMinutes int
SicMinutes int
DualMinutes int
DualGivenMinutes int
PicusMinutes int
SpicMinutes int
ExaminerMinutes int
ReliefMinutes int
SoloMinutes int
NightMinutes int
IfrMinutes int
ActualInstrumentMinutes int
SimulatedInstrumentMinutes int
CrossCountryMinutes int
CrossCountryPicMinutes int
MultiPilotMinutes int
SimulatedFlightMinutes int
GroundTrainingMinutes int
LandingsDay int
LandingsNight int
TakeoffsDay int
TakeoffsNight int
Approaches int
Holds int
DistanceNM float64
DistinctRegistrations int
DistinctTypes int
FirstFlightDate *time.Time
LastFlightDate *time.Time
}
AnalyticsTotals is the whole-timeframe aggregate for the analytics report.
type AnalyticsYearRow ¶ added in v1.3.4
type AnalyticsYearRow struct {
Year int
Flights int
TotalMinutes int
PicMinutes int
DualMinutes int
NightMinutes int
IfrMinutes int
Landings int
DistanceNM float64
}
AnalyticsYearRow is one calendar year of the analytics time series.
type AnnouncementRepository ¶ added in v1.3.4
type AnnouncementRepository interface {
// ListActive returns announcements that have not expired as of `now`,
// newest first.
ListActive(ctx context.Context, now time.Time) ([]*models.SystemAnnouncement, error)
// Create stores a new announcement.
Create(ctx context.Context, a *models.SystemAnnouncement) error
// Delete removes an announcement. Returns ErrNotFound when no row had
// this id.
Delete(ctx context.Context, id uuid.UUID) error
}
AnnouncementRepository persists operator-authored system announcements.
type BackupDestinationRepository ¶ added in v1.2.4
type BackupDestinationRepository interface {
Create(ctx context.Context, dest *models.BackupDestination) error
GetByID(ctx context.Context, id uuid.UUID) (*models.BackupDestination, error)
GetByUserID(ctx context.Context, userID uuid.UUID) ([]*models.BackupDestination, error)
Update(ctx context.Context, dest *models.BackupDestination) error
Delete(ctx context.Context, id uuid.UUID) error
// ListDueForRun returns enabled, active destinations whose schedule
// indicates they are due to run at the supplied wall-clock time.
ListDueForRun(ctx context.Context, now time.Time) ([]*models.BackupDestination, error)
}
BackupDestinationRepository persists per-user cloud backup destination configurations (provider + encrypted credentials + schedule + status).
type BackupRunRepository ¶ added in v1.2.4
type BackupRunRepository interface {
Create(ctx context.Context, run *models.BackupRun) error
Update(ctx context.Context, run *models.BackupRun) error
GetByID(ctx context.Context, id uuid.UUID) (*models.BackupRun, error)
// GetByDestinationID returns the requested page of runs newest-first
// along with the total row count.
GetByDestinationID(ctx context.Context, destinationID uuid.UUID, limit, offset int) ([]*models.BackupRun, int, error)
DeleteByDestinationID(ctx context.Context, destinationID uuid.UUID) error
}
BackupRunRepository persists immutable audit records for each backup run (one per attempt — success, skipped, or failed).
type CategoryStatRow ¶ added in v1.3.4
CategoryStatRow is a per-capability rollup (Tailwheel / Complex / High Performance). Rows overlap by design: one flight can count toward several.
type ClassStatRow ¶ added in v1.3.4
type ClassStatRow struct {
Class string
Flights int
Minutes int
PICMinutes int
DualMinutes int
Landings int
}
ClassStatRow is a per-aircraft-class rollup ("Unclassified" when the flight has no matching fleet entry).
type ContactRepository ¶
type ContactRepository interface {
Create(ctx context.Context, contact *models.Contact) error
GetByID(ctx context.Context, id uuid.UUID) (*models.Contact, error)
// GetByUserID retrieves all contacts for a user. A non-nil updatedSince
// narrows the result to contacts changed strictly after that instant.
GetByUserID(ctx context.Context, userID uuid.UUID, updatedSince *time.Time) ([]*models.Contact, error)
GetByExactName(ctx context.Context, userID uuid.UUID, name string) (*models.Contact, error)
// FindOrCreateByName returns the user's contact with this name — creating
// it if absent — and reports whether it created one. Concurrent callers
// converge on a single row.
FindOrCreateByName(ctx context.Context, userID uuid.UUID, name string) (*models.Contact, bool, error)
Search(ctx context.Context, userID uuid.UUID, query string, limit int) ([]*models.Contact, error)
Update(ctx context.Context, contact *models.Contact) error
// UpdateWithCrewRename updates the contact and carries a name change into
// the crew rows of the user's unsigned flights, returning the number of
// crew rows rewritten. Signed flights keep the name they were signed with.
UpdateWithCrewRename(ctx context.Context, contact *models.Contact) (int, error)
// RolesByContact maps each of the user's contacts to the distinct crew
// roles it has been logged in, most-used first.
RolesByContact(ctx context.Context, userID uuid.UUID) (map[uuid.UUID][]string, error)
Delete(ctx context.Context, id uuid.UUID) error
}
ContactRepository defines the interface for contact (reusable people) data access
type CredentialRepository ¶
type CredentialRepository interface {
Create(ctx context.Context, credential *models.Credential) error
GetByID(ctx context.Context, id uuid.UUID) (*models.Credential, error)
// GetByUserID retrieves all credentials for a user. A non-nil updatedSince
// narrows the result to credentials changed strictly after that instant.
GetByUserID(ctx context.Context, userID uuid.UUID, updatedSince *time.Time) ([]*models.Credential, error)
Update(ctx context.Context, credential *models.Credential) error
Delete(ctx context.Context, id uuid.UUID) error
}
CredentialRepository defines the interface for credential data access
type CustomCurrencyRuleRepository ¶ added in v1.3.1
type CustomCurrencyRuleRepository interface {
Create(ctx context.Context, rule *models.CustomCurrencyRule) error
GetByID(ctx context.Context, id uuid.UUID) (*models.CustomCurrencyRule, error)
GetByUserID(ctx context.Context, userID uuid.UUID) ([]*models.CustomCurrencyRule, error)
Update(ctx context.Context, rule *models.CustomCurrencyRule) error
Delete(ctx context.Context, id uuid.UUID) error
}
CustomCurrencyRuleRepository defines data access for user-authored currency rules.
type CustomReportGroup ¶ added in v1.3.9
type CustomReportGroup struct {
Key string
Totals models.CustomReportTotals
}
CustomReportGroup is one aggregated group of a custom report query.
type CustomReportRepository ¶ added in v1.3.9
type CustomReportRepository interface {
// Create inserts a report positioned after the user's existing reports.
Create(ctx context.Context, report *models.CustomReport) error
GetByID(ctx context.Context, id uuid.UUID) (*models.CustomReport, error)
// ListByUserID returns the user's reports ordered by position, then creation.
ListByUserID(ctx context.Context, userID uuid.UUID) ([]*models.CustomReport, error)
CountByUserID(ctx context.Context, userID uuid.UUID) (int, error)
// Update writes name and definition. Returns ErrNotFound when absent.
Update(ctx context.Context, report *models.CustomReport) error
// Delete returns ErrNotFound when absent.
Delete(ctx context.Context, id uuid.UUID) error
// SetPositions assigns position i to ids[i] for the user's reports.
SetPositions(ctx context.Context, userID uuid.UUID, ids []uuid.UUID) error
// Aggregate groups the user's flights matching opts by groupBy (a
// models.ReportGroup* value). Pagination and sorting in opts are ignored.
Aggregate(ctx context.Context, userID uuid.UUID, opts *FlightQueryOptions, groupBy string) ([]CustomReportGroup, error)
}
CustomReportRepository defines data access for saved custom reports.
type DeletionRepository ¶ added in v1.3.2
type DeletionRepository interface {
// ListSince returns the user's deletions strictly after `since`, oldest
// first, optionally narrowed to one entity type.
ListSince(ctx context.Context, userID uuid.UUID, since time.Time, entity *models.DeletionEntityType, limit, offset int) ([]*models.Deletion, error)
// CountSince counts the set ListSince pages over.
CountSince(ctx context.Context, userID uuid.UUID, since time.Time, entity *models.DeletionEntityType) (int, error)
// DeleteExpired removes tombstones older than `before`. Returns the
// number deleted.
DeleteExpired(ctx context.Context, before time.Time) (int64, error)
}
DeletionRepository reads deletion tombstones for delta sync. Rows are written by database triggers, never from Go.
type DocumentFileRepository ¶ added in v1.3.2
type DocumentFileRepository interface {
// Create atomically inserts the image only if the subject is still below
// maxPerSubject images, returning repository.ErrDocumentFileLimit
// otherwise, or ErrNotFound if the document does not exist.
Create(ctx context.Context, image *models.DocumentFile, maxPerSubject int) error
// ListBySubject returns the images attached to one licence or credential,
// oldest first, without their payload (DocumentFile.Data is nil).
ListBySubject(ctx context.Context, userID uuid.UUID, subject models.DocumentSubjectType, subjectID uuid.UUID) ([]*models.DocumentFile, error)
// GetWithData returns a single image including its bytes, scoped by
// subjectID.
GetWithData(ctx context.Context, userID uuid.UUID, subject models.DocumentSubjectType, subjectID, imageID uuid.UUID) (*models.DocumentFile, error)
// Delete removes one image, scoped the same way as GetWithData.
Delete(ctx context.Context, userID uuid.UUID, subject models.DocumentSubjectType, subjectID, imageID uuid.UUID) error
// CountBySubject reports how many images the subject already carries.
CountBySubject(ctx context.Context, userID uuid.UUID, subject models.DocumentSubjectType, subjectID uuid.UUID) (int, error)
}
DocumentFileRepository stores reference photos attached to a licence or a credential. Every method is scoped by the owning user and subject.
type EmailDeliveryRepository ¶ added in v1.3.2
type EmailDeliveryRepository interface {
// RecordEvent appends one attempt to the delivery log.
RecordEvent(ctx context.Context, event *models.EmailDeliveryEvent) error
// ListEvents returns the most recent events, newest first, optionally
// narrowed to one recipient address.
ListEvents(ctx context.Context, recipient string, limit int) ([]*models.EmailDeliveryEvent, error)
// IsSuppressed reports whether the address is on the suppression list.
IsSuppressed(ctx context.Context, email string) (bool, error)
// Suppress adds or updates a suppression, bumping the bounce count when the
// address is already listed.
Suppress(ctx context.Context, email, reason string, smtpCode *int, detail string) error
// Unsuppress removes an address from the suppression list. Returns
// ErrNotFound when the address was not listed.
Unsuppress(ctx context.Context, email string) error
// ListSuppressions returns suppressed addresses, most recently bounced first.
ListSuppressions(ctx context.Context, limit int) ([]*models.EmailSuppression, error)
// CountSuppressions reports the size of the suppression list.
CountSuppressions(ctx context.Context) (int, error)
// UserIDForEmail resolves an address to an account. Returns ErrNotFound
// when no account has that address.
UserIDForEmail(ctx context.Context, email string) (uuid.UUID, error)
}
EmailDeliveryRepository stores what SMTP said about each send, and the set of addresses that have been given up on.
type EmailVerificationTokenRepository ¶ added in v1.2.5
type EmailVerificationTokenRepository interface {
Create(ctx context.Context, token *models.EmailVerificationToken) error
GetByTokenHash(ctx context.Context, tokenHash string) (*models.EmailVerificationToken, error)
MarkAsUsed(ctx context.Context, tokenHash string) error
DeleteForUser(ctx context.Context, userID uuid.UUID) error
DeleteExpired(ctx context.Context) error
}
EmailVerificationTokenRepository defines the interface for email verification token data access.
type FlightBaselineRepository ¶ added in v1.2.3
type FlightBaselineRepository interface {
// Get returns the baseline for the given user, or repository.ErrNotFound.
Get(ctx context.Context, userID uuid.UUID) (*models.FlightBaseline, error)
// Upsert inserts or updates the baseline for a user. The user_id field on
// the model must be set; created_at / updated_at are populated by the DB.
Upsert(ctx context.Context, baseline *models.FlightBaseline) error
// Delete removes the baseline for the given user. Returns ErrNotFound when
// no baseline existed.
Delete(ctx context.Context, userID uuid.UUID) error
}
FlightBaselineRepository defines the interface for the per-user "initial hours snapshot" data access.
type FlightCrewRepository ¶
type FlightCrewRepository interface {
SetCrewMembers(ctx context.Context, flightID uuid.UUID, members []models.FlightCrewMember) error
GetByFlightID(ctx context.Context, flightID uuid.UUID) ([]models.FlightCrewMember, error)
GetByFlightIDs(ctx context.Context, flightIDs []uuid.UUID) (map[uuid.UUID][]models.FlightCrewMember, error)
DeleteByFlightID(ctx context.Context, flightID uuid.UUID) error
}
FlightCrewRepository defines the interface for flight crew member data access
type FlightImportRepository ¶ added in v1.3.4
type FlightImportRepository interface {
// Create stores one import record (created_at is set by the database).
Create(ctx context.Context, rec *models.FlightImport) error
// CountByUserID counts a user's import records.
CountByUserID(ctx context.Context, userID uuid.UUID) (int, error)
// ListByUserID returns a user's import records newest first.
ListByUserID(ctx context.Context, userID uuid.UUID, limit, offset int) ([]*models.FlightImport, error)
// GetByIDForUser returns one import record scoped to its owner, or
// ErrNotFound.
GetByIDForUser(ctx context.Context, id, userID uuid.UUID) (*models.FlightImport, error)
// DeleteByUserID removes all of a user's import records.
DeleteByUserID(ctx context.Context, userID uuid.UUID) error
}
FlightImportRepository persists the per-run import history records.
type FlightQueryOptions ¶
type FlightQueryOptions struct {
StartDate *time.Time
EndDate *time.Time
AircraftReg *string
DepartureICAO *string
ArrivalICAO *string
IsPIC *bool
IsDual *bool
Search *string
// UpdatedSince restricts the result to flights whose updated_at is
// strictly after the given instant, compared at full timestamp precision.
UpdatedSince *time.Time
// Query is a parsed advanced search query (see internal/flightsearch),
// ANDed with the other filters.
Query *flightsearch.Query
Page int
PageSize int
SortBy string // "date", "totalTime", "createdAt"
SortOrder string // "asc", "desc"
// Logbook filtering: when FilterByRegistrations is true, only flights whose
// aircraft_reg matches one of AircraftRegistrations (case-insensitive) are
// returned. Registrations should be supplied upper-cased. An empty slice with
// FilterByRegistrations=true matches no flights. Applied at the SQL level.
FilterByRegistrations bool
AircraftRegistrations []string
}
FlightQueryOptions represents query parameters for filtering flights
type FlightRepository ¶
type FlightRepository interface {
// Create creates a new flight
Create(ctx context.Context, flight *models.Flight) error
// GetByID retrieves a flight by its ID
GetByID(ctx context.Context, id uuid.UUID) (*models.Flight, error)
// GetByUserID retrieves flights for a user with optional filters
GetByUserID(ctx context.Context, userID uuid.UUID, opts *FlightQueryOptions) ([]*models.Flight, error)
// Update updates a flight
Update(ctx context.Context, flight *models.Flight) error
// Delete deletes a flight
Delete(ctx context.Context, id uuid.UUID) error
// DeleteAllByUserID deletes all flights for a user, returns count deleted
DeleteAllByUserID(ctx context.Context, userID uuid.UUID) (int64, error)
// CountByUserID counts flights for a user with optional filters
CountByUserID(ctx context.Context, userID uuid.UUID, opts *FlightQueryOptions) (int, error)
// GetStatsByUserID returns aggregated flight statistics for a user
GetStatsByUserID(ctx context.Context, userID uuid.UUID, startDate, endDate *time.Time) (*models.FlightStatistics, error)
// GetCurrencyData returns landing counts and flight count for a user within a date range
GetCurrencyData(ctx context.Context, userID uuid.UUID, since time.Time) (*models.CurrencyData, error)
// SetSignatureLock sets (or, passing nil, clears) the flight's signature
// lock pointer. Locked iff signatureID is non-nil.
SetSignatureLock(ctx context.Context, flightID uuid.UUID, signatureID *uuid.UUID) error
}
FlightRepository defines the interface for flight data access
type FlightSessionRepository ¶ added in v1.2.8
type FlightSessionRepository interface {
// Create creates a new flight session. Returns ErrDuplicate when the
// user already has an open session.
Create(ctx context.Context, session *models.FlightSession) error
// GetOpenByUserID returns the user's open session, or ErrNotFound.
GetOpenByUserID(ctx context.Context, userID uuid.UUID) (*models.FlightSession, error)
// Update persists mutable session fields (timestamps, aircraft, route,
// status, flight_id). Returns ErrNotFound when the session does not exist.
Update(ctx context.Context, session *models.FlightSession) error
}
FlightSessionRepository defines the interface for in-progress flight session (tap-to-log quick log) data access.
type FlightSignatureRepository ¶ added in v1.2.9
type FlightSignatureRepository interface {
// Create creates a new flight signature row (live 'completed' or
// deferred 'pending'). Returns ErrDuplicate if a pending row already
// exists for the flight.
Create(ctx context.Context, sig *models.FlightSignature) error
// GetByID retrieves a signature by ID.
GetByID(ctx context.Context, id uuid.UUID) (*models.FlightSignature, error)
// GetByIDs batch-loads signatures, keyed by signature ID. IDs with no
// row are absent from the map.
GetByIDs(ctx context.Context, ids []uuid.UUID) (map[uuid.UUID]*models.FlightSignature, error)
// GetByTokenHash retrieves a signature by its hashed token. Returns
// ErrNotFound if no row has this hash, regardless of status.
GetByTokenHash(ctx context.Context, tokenHash string) (*models.FlightSignature, error)
// GetPendingByFlightID returns the flight's current pending request, or
// ErrNotFound if none.
GetPendingByFlightID(ctx context.Context, flightID uuid.UUID) (*models.FlightSignature, error)
// ListByFlightID returns the full signature history for a flight, newest
// first.
// CountEmailsSentSince returns how many signature-request emails the user
// has triggered since the given instant.
CountEmailsSentSince(ctx context.Context, userID uuid.UUID, since time.Time) (int, error)
ListByFlightID(ctx context.Context, flightID uuid.UUID) ([]*models.FlightSignature, error)
// Update persists all mutable fields of a signature row.
Update(ctx context.Context, sig *models.FlightSignature) error
// ExpirePendingPastDue flips any 'pending' row whose token_expires_at
// has passed to 'expired' and returns the number of rows affected.
ExpirePendingPastDue(ctx context.Context) (int64, error)
}
FlightSignatureRepository defines the interface for instructor sign-off request/record data access. Rows are append-only history, with flights.signature_id as the "is locked" pointer.
type IdempotencyRepository ¶ added in v1.3.2
type IdempotencyRepository interface {
// Claim atomically takes ownership of (rec.UserID, rec.Key) for the
// current request.
//
// It returns (nil, nil) when the claim succeeded, meaning the caller must
// run the request and then either Complete or Release the record. It
// returns the live record when the key is already held — either still
// in progress, or completed and awaiting replay — and the caller must not
// execute the request.
//
// A record that has expired, or whose in-progress claim was taken before
// staleBefore, is taken over rather than reported as a conflict.
Claim(ctx context.Context, rec *models.IdempotencyRecord, staleBefore time.Time) (*models.IdempotencyRecord, error)
// Complete finalizes a claimed record with the captured response. It is a
// no-op when the claim identified by rec.CreatedAt is no longer held.
Complete(ctx context.Context, rec *models.IdempotencyRecord) error
// Release drops a claim so the key can be used again. Like Complete it
// only acts on a claim still owned by claimedAt.
Release(ctx context.Context, userID uuid.UUID, key string, claimedAt time.Time) error
// DeleteExpired removes records that expired at or before `before`.
// Returns the number deleted.
DeleteExpired(ctx context.Context, before time.Time) (int64, error)
}
IdempotencyRepository stores server-side idempotency replay records. Every method is scoped by user ID.
type LicenseRepository ¶
type LicenseRepository interface {
// Create creates a new license
Create(ctx context.Context, license *models.License) error
// GetByID retrieves a license by its ID
GetByID(ctx context.Context, id uuid.UUID) (*models.License, error)
// GetByUserID retrieves all licenses for a user. A non-nil updatedSince
// narrows the result to licences changed strictly after that instant.
GetByUserID(ctx context.Context, userID uuid.UUID, updatedSince *time.Time) ([]*models.License, error)
// Update updates a license
Update(ctx context.Context, license *models.License) error
// Delete deletes a license
Delete(ctx context.Context, id uuid.UUID) error
}
LicenseRepository defines the interface for license data access
type MonthlyTrendRow ¶ added in v1.3.4
type MonthlyTrendRow struct {
Month string // "YYYY-MM"
TotalFlights int
TotalMinutes int
PICMinutes int
DualMinutes int
NightMinutes int
IFRMinutes int
LandingsDay int
LandingsNight int
}
MonthlyTrendRow is one month of the legacy trends report.
type NotificationRepository ¶
type NotificationRepository interface {
GetPreferences(ctx context.Context, userID uuid.UUID) (*models.NotificationPreferences, error)
UpsertPreferences(ctx context.Context, prefs *models.NotificationPreferences) error
LogNotification(ctx context.Context, log *models.NotificationLog) error
HasBeenSent(ctx context.Context, userID uuid.UUID, notificationType string, referenceID uuid.UUID, daysBeforeExpiry int, expiryReferenceDate *time.Time) (bool, error)
GetAllUsersWithPreferences(ctx context.Context) ([]*models.NotificationPreferences, error)
GetNotificationHistory(ctx context.Context, userID uuid.UUID, limit, offset int) ([]*models.NotificationLog, int, error)
}
NotificationRepository defines the interface for notification data access
type OIDCIdentityRepository ¶ added in v1.3.2
type OIDCIdentityRepository interface {
// GetBySubject returns the identity for an (issuer, subject) pair, or
// ErrNotFound.
GetBySubject(ctx context.Context, issuer, subject string) (*models.OIDCIdentity, error)
// GetByUserID returns the identity linked to a local user, or ErrNotFound.
GetByUserID(ctx context.Context, userID uuid.UUID) (*models.OIDCIdentity, error)
// Create links an external identity to an existing user.
Create(ctx context.Context, identity *models.OIDCIdentity) error
// TouchLogin records a successful login and refreshes the cached email.
TouchLogin(ctx context.Context, id uuid.UUID, email string, at time.Time) error
// CreateLoginState stores the nonce and PKCE verifier for one pending
// authorization request, keyed by the SHA-256 of the state parameter.
CreateLoginState(ctx context.Context, state *models.OIDCLoginState) error
// ConsumeLoginState atomically deletes and returns an unexpired login
// state. Returns ErrNotFound for expired, already-consumed and forged
// state values alike.
ConsumeLoginState(ctx context.Context, stateHash []byte) (*models.OIDCLoginState, error)
// CreateHandoffCode stores a single-use code the SPA can exchange for a
// token pair, keyed by the SHA-256 of the code.
CreateHandoffCode(ctx context.Context, code *models.OIDCHandoffCode) error
// ConsumeHandoffCode atomically deletes and returns an unexpired handoff
// code, or ErrNotFound.
ConsumeHandoffCode(ctx context.Context, codeHash []byte) (*models.OIDCHandoffCode, error)
// DeleteExpired removes expired login states and handoff codes. Returns
// the number of rows deleted across both tables.
DeleteExpired(ctx context.Context) (int64, error)
}
OIDCIdentityRepository persists the mapping between external OpenID Connect identities and local users, plus short-lived single-use login state (authorization state and browser handoff codes).
type PasswordResetTokenRepository ¶
type PasswordResetTokenRepository interface {
// Create creates a new password reset token
Create(ctx context.Context, token *models.PasswordResetToken) error
// GetByTokenHash retrieves a password reset token by its hash
GetByTokenHash(ctx context.Context, tokenHash string) (*models.PasswordResetToken, error)
// MarkAsUsed marks a password reset token as used
MarkAsUsed(ctx context.Context, tokenHash string) error
// DeleteExpired deletes expired password reset tokens
DeleteExpired(ctx context.Context) error
// DeleteForUser deletes all password reset tokens for a user
DeleteForUser(ctx context.Context, userID uuid.UUID) error
}
PasswordResetTokenRepository defines the interface for password reset token data access
type RefreshTokenRepository ¶
type RefreshTokenRepository interface {
// Create creates a new refresh token
Create(ctx context.Context, token *models.RefreshToken) error
// GetByTokenHash retrieves a refresh token by its hash
GetByTokenHash(ctx context.Context, tokenHash string) (*models.RefreshToken, error)
// RevokeByTokenHash revokes a refresh token outright, with no reuse grace.
RevokeByTokenHash(ctx context.Context, tokenHash string) error
// MarkRotated revokes a refresh token as superseded by a refresh, making
// it eligible for the reuse grace window.
MarkRotated(ctx context.Context, tokenHash string) error
// RevokeAllForUser revokes all refresh tokens for a user
RevokeAllForUser(ctx context.Context, userID uuid.UUID) error
// DeleteForUser deletes all refresh tokens for a user
DeleteForUser(ctx context.Context, userID uuid.UUID) error
// DeleteExpired deletes expired refresh tokens
DeleteExpired(ctx context.Context) error
// TouchSession stamps last_used_at on every token in a session.
TouchSession(ctx context.Context, sessionID uuid.UUID, at time.Time) error
// ListSessions returns the user's live sessions, most recently used first.
// A session is live when it holds at least one unrevoked, unexpired token.
ListSessions(ctx context.Context, userID uuid.UUID) ([]*models.Session, error)
// RevokeSession revokes every token in one session. It returns
// ErrNotFound when the session does not exist or belongs to another user.
RevokeSession(ctx context.Context, userID, sessionID uuid.UUID) error
// RevokeSessionsExcept revokes every session the user holds apart from
// keep, and returns how many sessions were revoked.
RevokeSessionsExcept(ctx context.Context, userID, keep uuid.UUID) (int64, error)
// EvictOldestSessions revokes the least recently used live sessions until
// at most keep remain, and returns how many were evicted.
EvictOldestSessions(ctx context.Context, userID uuid.UUID, keep int) (int64, error)
// CountActiveSessions counts live sessions across all users.
CountActiveSessions(ctx context.Context) (int64, error)
// AccessTokenState reports the account's disabled flag and whether the
// session still holds a live refresh token, in one round trip. It returns
// ErrNotFound when no such user exists.
AccessTokenState(ctx context.Context, userID, sessionID uuid.UUID) (disabled bool, live bool, err error)
}
RefreshTokenRepository defines the interface for refresh token data access
type ReportsRepository ¶ added in v1.3.4
type ReportsRepository interface {
// RouteCounts returns every flown departure→arrival pair with counts,
// most-flown first, across all time.
RouteCounts(ctx context.Context, userID uuid.UUID) ([]*RouteCount, error)
// AirportCounts returns per-airport leg counts split by direction,
// across all time.
AirportCounts(ctx context.Context, userID uuid.UUID) ([]*AirportDirectionCount, error)
// MonthlyTrends returns the legacy per-month trend rows, oldest first.
MonthlyTrends(ctx context.Context, userID uuid.UUID, months int) ([]*MonthlyTrendRow, error)
// AircraftTypeTrends returns the legacy per-type rollup, most minutes first.
AircraftTypeTrends(ctx context.Context, userID uuid.UUID, months int) ([]*AircraftTypeTrendRow, error)
// StatsByClass returns per-class rollups, most minutes first.
StatsByClass(ctx context.Context, userID uuid.UUID, months int) ([]*ClassStatRow, error)
// StatsByCategory returns the overlapping capability rollups.
StatsByCategory(ctx context.Context, userID uuid.UUID, months int) ([]*CategoryStatRow, error)
// Totals returns the timeframe-wide aggregate.
Totals(ctx context.Context, userID uuid.UUID, months int) (*AnalyticsTotals, error)
// Monthly returns the analytics month series, oldest first.
Monthly(ctx context.Context, userID uuid.UUID, months int) ([]*AnalyticsMonthRow, error)
// Yearly returns the analytics year series, oldest first.
Yearly(ctx context.Context, userID uuid.UUID, months int) ([]*AnalyticsYearRow, error)
// ByAircraft groups by aircraft type, or by registration when
// byRegistration is true, top `limit` rows by block time.
ByAircraft(ctx context.Context, userID uuid.UUID, months, limit int, byRegistration bool) ([]*AnalyticsAircraftRow, error)
// ByClass groups by aircraft class.
ByClass(ctx context.Context, userID uuid.UUID, months int) ([]*AnalyticsGroupRow, error)
// ByCategory returns the overlapping capability rollups.
ByCategory(ctx context.Context, userID uuid.UUID, months int) ([]*AnalyticsGroupRow, error)
// ByAirport returns the top `limit` airports by distinct flights.
ByAirport(ctx context.Context, userID uuid.UUID, months, limit int) ([]*AnalyticsAirportRow, error)
// ByRoute returns the top `limit` routes by flight count.
ByRoute(ctx context.Context, userID uuid.UUID, months, limit int) ([]*AnalyticsRouteRow, error)
// ByInstructor returns the top `limit` instructors by dual time received.
ByInstructor(ctx context.Context, userID uuid.UUID, months, limit int) ([]*AnalyticsPersonRow, error)
// ByCrew returns the top `limit` crew members by block time — one row
// per person. Entries linked to the same contact are one person; unlinked
// entries fold by trimmed, case-insensitive name.
ByCrew(ctx context.Context, userID uuid.UUID, months, limit int) ([]*AnalyticsPersonRow, error)
// ApproachTypes counts structured approach entries by type, most first.
ApproachTypes(ctx context.Context, userID uuid.UUID, months int) ([]*AnalyticsApproachTypeRow, error)
// ApproachesCountTotal sums the flights' approaches_count column, which
// also covers legacy flights that carry only a count.
ApproachesCountTotal(ctx context.Context, userID uuid.UUID, months int) (int, error)
// DayOfWeekBuckets keys by ISO weekday (1=Mon..7=Sun).
DayOfWeekBuckets(ctx context.Context, userID uuid.UUID, months int) (map[int]AnalyticsBucket, error)
// MonthOfYearBuckets keys by month number (1..12).
MonthOfYearBuckets(ctx context.Context, userID uuid.UUID, months int) (map[int]AnalyticsBucket, error)
// HourOfDayBuckets keys by the hour of the off-block (or departure) time
// (0..23); flights with neither timestamp are excluded.
HourOfDayBuckets(ctx context.Context, userID uuid.UUID, months int) (map[int]AnalyticsBucket, error)
// DurationBuckets keys by duration bucket index
// (0: <30m, 1: 30-60m, 2: 1-2h, 3: 2-3h, 4: 3-5h, 5: >5h).
DurationBuckets(ctx context.Context, userID uuid.UUID, months int) (map[int]AnalyticsBucket, error)
// LongestFlight returns the flight with the most block time in the
// timeframe, or nil when there are none.
LongestFlight(ctx context.Context, userID uuid.UUID, months int) (*AnalyticsFlightRef, error)
// FarthestFlight returns the flight with the greatest recorded distance,
// or nil when there are none.
FarthestFlight(ctx context.Context, userID uuid.UUID, months int) (*AnalyticsFlightRef, error)
// BusiestDay returns the date with the most flights and its count, or
// (nil, 0) when the timeframe holds no flights.
BusiestDay(ctx context.Context, userID uuid.UUID, months int) (*time.Time, int, error)
// LastFlightDate returns the date of the most recent flight across the
// whole logbook (deliberately ignoring the timeframe), or nil.
LastFlightDate(ctx context.Context, userID uuid.UUID) (*time.Time, error)
// CarriedForwardMinutes sums block time of flights dated before the
// timeframe's first month. Zero when months <= 0 (nothing precedes an
// all-time window).
CarriedForwardMinutes(ctx context.Context, userID uuid.UUID, months int) (int, error)
}
ReportsRepository serves the aggregate queries behind the reports, analytics and map endpoints. It is read-only.
type RouteCount ¶ added in v1.3.4
RouteCount is one departure→arrival pair with its flight count (map view).
type UserContentRepository ¶ added in v1.3.4
type UserContentRepository interface {
DeleteAllContent(ctx context.Context, userID uuid.UUID) error
}
UserContentRepository wipes a user's logbook content (flights, crew rows, imports, licenses and their class ratings, aircraft, contacts, credentials, notification history) in one transaction, keeping the account itself.
type UserRepository ¶
type UserRepository interface {
// Create creates a new user
Create(ctx context.Context, user *models.User) error
// GetByID retrieves a user by ID
GetByID(ctx context.Context, id uuid.UUID) (*models.User, error)
// GetByEmail retrieves a user by email
GetByEmail(ctx context.Context, email string) (*models.User, error)
// Update updates a user
Update(ctx context.Context, user *models.User) error
// Delete deletes a user
Delete(ctx context.Context, id uuid.UUID) error
// IncrementFailedLoginAttempts increments the failed login counter
IncrementFailedLoginAttempts(ctx context.Context, id uuid.UUID) error
// ResetFailedLoginAttempts resets the failed login counter to 0
ResetFailedLoginAttempts(ctx context.Context, id uuid.UUID) error
// LockAccount locks the account until the given time
LockAccount(ctx context.Context, id uuid.UUID, until time.Time) error
// UpdateLastLogin stamps the last successful login.
UpdateLastLogin(ctx context.Context, id uuid.UUID, at time.Time) error
// MarkEmailVerified flips the email_verified flag to true.
// ConsumeRecoveryCode atomically removes a recovery code hash, returning
// true only for the caller that actually removed it.
ConsumeRecoveryCode(ctx context.Context, id uuid.UUID, codeHash string) (bool, error)
MarkEmailVerified(ctx context.Context, id uuid.UUID) error
// ListUnverifiedForReminder returns accounts that are still unverified,
// were created before `createdBefore`, and have not yet been sent the
// follow-up verification reminder. Accounts that have ever logged in and
// accounts linked to an OIDC identity are excluded.
ListUnverifiedForReminder(ctx context.Context, createdBefore time.Time, limit int) ([]*models.User, error)
// MarkVerificationReminderSent stamps the reminder time.
MarkVerificationReminderSent(ctx context.Context, id uuid.UUID, at time.Time) error
// DeleteUnverifiedRemindedBefore deletes still-unverified accounts whose
// reminder was sent before the given instant, and reports how many went.
// It applies the same never-logged-in and non-OIDC guards as the listing.
DeleteUnverifiedRemindedBefore(ctx context.Context, remindedBefore time.Time) (int64, error)
}
UserRepository defines the interface for user data access
type WebAuthnCredentialRepository ¶ added in v1.2.0
type WebAuthnCredentialRepository interface {
Create(ctx context.Context, credential *models.WebAuthnCredential) error
GetByID(ctx context.Context, id uuid.UUID) (*models.WebAuthnCredential, error)
GetByCredentialID(ctx context.Context, credentialID []byte) (*models.WebAuthnCredential, error)
GetByUserID(ctx context.Context, userID uuid.UUID) ([]*models.WebAuthnCredential, error)
UpdateSignCount(ctx context.Context, id uuid.UUID, signCount uint32, lastUsedAt time.Time) error
Delete(ctx context.Context, id uuid.UUID, userID uuid.UUID) error
}
WebAuthnCredentialRepository defines the interface for WebAuthn / passkey credential data access.
type WebAuthnSessionRepository ¶ added in v1.2.0
type WebAuthnSessionRepository interface {
// Create stores ceremony state keyed by the SHA-256 of the handle.
Create(ctx context.Context, session *models.WebAuthnSession) error
// Consume atomically deletes and returns the session if it exists, matches
// the ceremony, and has not expired. Returns ErrNotFound otherwise.
Consume(ctx context.Context, idHash []byte, ceremony string) (*models.WebAuthnSession, error)
// DeleteOldestForUser keeps the newest `keep` sessions belonging to userID
// and deletes the rest. Returns the number deleted.
DeleteOldestForUser(ctx context.Context, userID uuid.UUID, keep int) (int64, error)
// DeleteExpired removes expired rows. Returns the number deleted.
DeleteExpired(ctx context.Context) (int64, error)
}
WebAuthnSessionRepository defines the interface for transient WebAuthn ceremony session storage.