Documentation
¶
Index ¶
- Constants
- Variables
- func BackupsDir(dataDir string) string
- func IsUpdateSnapshotName(name string) bool
- func MappingsFromConfig(cfg *config.Instance) []database.Mapping
- func NormalizeID(uid string) string
- func RestoreFileTo(ctx context.Context, fs afero.Fs, backupPath, dbPath string) error
- type UserDB
- func (db *UserDB) ActivateProfile(profileID string, lastUsedAt int64) error
- func (db *UserDB) AddHistory(entry *database.HistoryEntry) error
- func (db *UserDB) AddInboxMessage(msg *database.InboxMessage) (*database.InboxMessage, error)
- func (db *UserDB) AddMapping(m *database.Mapping) error
- func (db *UserDB) AddMediaHistory(entry *database.MediaHistoryEntry) (int64, error)
- func (db *UserDB) Allocate() error
- func (db *UserDB) BackfillMediaHistoryUUIDs() (int64, error)
- func (db *UserDB) Backup(reason string, manual bool) (database.BackupInfo, error)
- func (db *UserDB) BackupForTransfer(ctx context.Context, reason string) (database.BackupInfo, func() error, error)
- func (db *UserDB) BackupForUpdate(targetVersion string) (database.BackupInfo, func() error, error)
- func (db *UserDB) ClaimRemoteCommand(command *database.RemoteCommand) (*database.RemoteCommand, bool, error)
- func (db *UserDB) CleanupHistory(retentionDays int) (int64, error)
- func (db *UserDB) CleanupMediaHistory(retentionDays int, requireSynced bool) (int64, error)
- func (db *UserDB) ClearCorruptMarker() error
- func (db *UserDB) Close() error
- func (db *UserDB) CloseHangingMediaHistory() error
- func (db *UserDB) CloseMediaHistory(dbid int64, endTime time.Time, playTime int) error
- func (db *UserDB) CountClients() (int, error)
- func (db *UserDB) CreateClient(c *database.Client) error
- func (db *UserDB) CreateProfile(p *database.Profile) error
- func (db *UserDB) DeleteAllInboxMessages() (int64, error)
- func (db *UserDB) DeleteClient(clientID string) error
- func (db *UserDB) DeleteDeviceState(key string) error
- func (db *UserDB) DeleteInboxMessage(id int64) error
- func (db *UserDB) DeleteMapping(id int64) error
- func (db *UserDB) DeleteMediaUserData(systemID, path string) error
- func (db *UserDB) DeleteProfile(profileID string) error
- func (db *UserDB) EnsureRecentBackup(maxAge time.Duration) (database.BackupInfo, bool, error)
- func (db *UserDB) GetAllMappings() ([]database.Mapping, error)
- func (db *UserDB) GetClientByToken(authToken string) (*database.Client, error)
- func (db *UserDB) GetDBPath() string
- func (db *UserDB) GetDeviceState(key string) (value string, found bool, err error)
- func (db *UserDB) GetDistinctMediaHistory(ctx context.Context, systemIDs []string, lastID int64, limit int) ([]database.MediaHistoryEntry, error)
- func (db *UserDB) GetEnabledMappings() ([]database.Mapping, error)
- func (db *UserDB) GetHistory(lastID int64) ([]database.HistoryEntry, error)
- func (db *UserDB) GetInboxMessages() ([]database.InboxMessage, error)
- func (db *UserDB) GetLatestMediaHistory() (database.MediaHistoryEntry, bool, error)
- func (db *UserDB) GetMapping(id int64) (database.Mapping, error)
- func (db *UserDB) GetMediaHistory(systemIDs []string, lastID int64, limit int) ([]database.MediaHistoryEntry, error)
- func (db *UserDB) GetMediaHistoryIdentityBackfillBatch(afterDBID int64, policyVersion int, limit int) ([]database.MediaHistoryEntry, error)
- func (db *UserDB) GetMediaHistorySyncBatch(after time.Time, afterDBID int64, limit int) ([]database.MediaHistoryEntry, error)
- func (db *UserDB) GetMediaHistoryTop(systemIDs []string, since *time.Time, limit int) ([]database.MediaHistoryTopEntry, error)
- func (db *UserDB) GetMediaUserData(systemID, path string) (database.MediaUserData, bool, error)
- func (db *UserDB) GetProfile(profileID string) (*database.Profile, error)
- func (db *UserDB) GetProfileBySwitchID(switchID string) (*database.Profile, error)
- func (db *UserDB) GetSupportedZapLinkHosts() ([]string, error)
- func (db *UserDB) GetZapLinkCache(url string) (string, error)
- func (db *UserDB) GetZapLinkHost(host string) (found, zapScript bool, err error)
- func (db *UserDB) HealTimestamps(bootUUID string, trueBootTime time.Time) (int64, error)
- func (db *UserDB) IntegrityReport() []string
- func (db *UserDB) IsMarkedCorrupt() bool
- func (db *UserDB) ListBackups() ([]database.BackupInfo, error)
- func (db *UserDB) ListClients() ([]database.Client, error)
- func (db *UserDB) ListMediaUserData() ([]database.MediaUserData, error)
- func (db *UserDB) ListProfiles() ([]database.Profile, error)
- func (db *UserDB) ListRecentRemoteCommands(limit int) ([]database.RemoteCommand, error)
- func (db *UserDB) ListUnreportedRemoteCommands(limit int) ([]database.RemoteCommand, error)
- func (db *UserDB) MarkCorrupt(reason string)
- func (db *UserDB) MarkMediaHistorySynced(refs []database.MediaHistorySyncRef, syncedAt time.Time) error
- func (db *UserDB) MarkRemoteCommandResultReported(commandID string) error
- func (db *UserDB) MigrateUp() error
- func (db *UserDB) NoteCorruption(err error) bool
- func (db *UserDB) Open() error
- func (db *UserDB) PruneExpiredZapLinkHosts(olderThan time.Duration) (int64, error)
- func (db *UserDB) PruneRemoteCommands(before time.Time) (int64, error)
- func (db *UserDB) RecoverFromCorruption() (database.RestoreInfo, error)
- func (db *UserDB) ReplaceAllClients(clients []database.Client) error
- func (db *UserDB) ResetMediaHistorySyncAfter(watermark *time.Time) error
- func (db *UserDB) RestoreBackup(name string) (database.RestoreInfo, error)
- func (db *UserDB) SetDeviceState(key, value string) error
- func (db *UserDB) SetMediaUserFavorite(systemID, path string, favorite bool) error
- func (db *UserDB) SetMediaUserLauncherOverride(systemID, path, launcherID string) error
- func (db *UserDB) SetMediaUserSnapshot(systemID, path, mediaName string, tags []string) error
- func (db *UserDB) SetSQLForTesting(ctx context.Context, sqlDB *sql.DB, platform platforms.Platform) error
- func (db *UserDB) StoreRemoteCommandResult(commandID, fromState, status string, result json.RawMessage, errorCode string) (bool, error)
- func (db *UserDB) SumMediaPlayTimeForDay(dayStart time.Time) (int64, error)
- func (db *UserDB) SumMediaPlayTimeForDayByProfile(dayStart time.Time, profileID string) (int64, error)
- func (db *UserDB) TransitionRemoteCommand(commandID, fromState, toState string, executionExpiresAt *time.Time) (bool, error)
- func (db *UserDB) Truncate() error
- func (db *UserDB) UnsafeGetSQLDb() *sql.DB
- func (db *UserDB) UpdateClientLastSeen(authToken string, lastSeenAt int64) error
- func (db *UserDB) UpdateMapping(id int64, m *database.Mapping) error
- func (db *UserDB) UpdateMediaHistoryIdentity(dbid int64, identity *database.MediaIdentity) (bool, error)
- func (db *UserDB) UpdateMediaHistoryIdentityAndPath(dbid int64, path string, identity *database.MediaIdentity) (bool, error)
- func (db *UserDB) UpdateMediaHistoryTime(dbid int64, playTime int) error
- func (db *UserDB) UpdateProfile(p *database.Profile) error
- func (db *UserDB) UpdateZapLinkCache(url, zapscript string) error
- func (db *UserDB) UpdateZapLinkHost(host string, zapscript int) error
- func (db *UserDB) UpsertMediaUserData(data *database.MediaUserData) error
- func (db *UserDB) Vacuum() error
Constants ¶
const ( MappingTypeID = "id" MappingTypeValue = "value" MappingTypeData = "data" MatchTypeExact = "exact" MatchTypePartial = "partial" MatchTypeRegex = "regex" LegacyMappingTypeUID = "uid" LegacyMappingTypeText = "text" )
Variables ¶
var ( // ErrProfileNotFound is returned when a profile lookup matches no row. ErrProfileNotFound = errors.New("profile not found") // ErrLastProfileAdmin is returned when an operation would remove the // final administrator profile. ErrLastProfileAdmin = errors.New("cannot remove the last admin profile") )
var AllowedMappingTypes = []string{ MappingTypeID, MappingTypeValue, MappingTypeData, }
var AllowedMatchTypes = []string{ MatchTypeExact, MatchTypePartial, MatchTypeRegex, }
var ErrConnDrainTimeout = errors.New("timed out waiting for user database queries to finish")
ErrConnDrainTimeout is returned when queries are still running after the connection pool has been closed and the drain deadline has passed.
var ErrInvalidAuthToken = errors.New("auth token must not contain ':'")
ErrInvalidAuthToken rejects ':' in auth tokens (AAD uses `<token>:ws`; also enforced by SQL CHECK).
var ErrInvalidBackup = errors.New("user database backup is invalid")
var ErrLastClientAdmin = errors.New("cannot delete the last admin client")
ErrLastClientAdmin is returned when deleting the final admin client.
var ErrNullSQL = errors.New("UserDB is not connected")
Functions ¶
func BackupsDir ¶
BackupsDir returns the directory backups live in for a data directory, so a caller with no open database can still find them.
func IsUpdateSnapshotName ¶
IsUpdateSnapshotName reports whether name is an update snapshot. The updater needs this to reclaim snapshots whose update is long resolved, and it holds no database handle when it runs.
func MappingsFromConfig ¶ added in v2.15.0
MappingsFromConfig converts the file-based mappings loaded into config into the database.Mapping shape used for matching and API responses. The returned mappings are always enabled and carry no database ID, since they originate from the mappings folder rather than UserDB.
func NormalizeID ¶
func RestoreFileTo ¶
RestoreFileTo installs a backup over a user database that nothing has open.
RestoreBackup is the normal path and cannot be used here: it needs a live *UserDB, and the only way to get one is OpenUserDB, which creates a fresh empty schema when the file is missing. The update watchdog runs before any database is opened — that is what lets it decide without consulting a schema version — so it needs the swap on its own.
The backup is quick_checked first, because installing a corrupt file over a working database turns a recoverable situation into an unrecoverable one. A crash partway through is repaired by recoverInterruptedRestore, which Open already runs before anything else touches the file.
Types ¶
type UserDB ¶
type UserDB struct {
// contains filtered or unexported fields
}
func (*UserDB) ActivateProfile ¶ added in v2.16.0
ActivateProfile atomically records profile use and persists it as active.
func (*UserDB) AddHistory ¶
func (db *UserDB) AddHistory(entry *database.HistoryEntry) error
func (*UserDB) AddInboxMessage ¶ added in v2.8.0
func (db *UserDB) AddInboxMessage(msg *database.InboxMessage) (*database.InboxMessage, error)
func (*UserDB) AddMediaHistory ¶ added in v2.7.0
func (db *UserDB) AddMediaHistory(entry *database.MediaHistoryEntry) (int64, error)
AddMediaHistory adds a new media history entry and returns the DBID.
func (*UserDB) BackfillMediaHistoryUUIDs ¶ added in v2.16.0
BackfillMediaHistoryUUIDs assigns stable session UUIDs to legacy MediaHistory rows written before UUIDs existed. Existing timestamps remain unchanged.
func (*UserDB) BackupForTransfer ¶ added in v2.16.0
func (db *UserDB) BackupForTransfer( ctx context.Context, reason string, ) (database.BackupInfo, func() error, error)
BackupForTransfer creates a validated portable snapshot without paired-client credentials. Cleanup owns the private temporary directory and is safe to call more than once.
func (*UserDB) BackupForUpdate ¶
func (db *UserDB) BackupForUpdate( targetVersion string, ) (database.BackupInfo, func() error, error)
BackupForUpdate drains and closes the live connection pool before taking the snapshot. The pool remains closed until the caller invokes resume after an aborted install; a successful install restarts the process instead. This closes the window where acknowledged outgoing-version writes could land after the rollback snapshot.
func (*UserDB) ClaimRemoteCommand ¶
func (db *UserDB) ClaimRemoteCommand(command *database.RemoteCommand) (*database.RemoteCommand, bool, error)
func (*UserDB) CleanupHistory ¶ added in v2.7.0
func (*UserDB) CleanupMediaHistory ¶ added in v2.7.0
CleanupMediaHistory removes media history older than the retention period. When requireSynced is true, only server-acknowledged versions are removed.
func (*UserDB) ClearCorruptMarker ¶ added in v2.15.0
func (*UserDB) CloseHangingMediaHistory ¶ added in v2.7.0
CloseHangingMediaHistory closes any media history entries left open from unclean shutdowns. It sets EndTime = StartTime + PlayTime for entries where EndTime is NULL.
func (*UserDB) CloseMediaHistory ¶ added in v2.7.0
CloseMediaHistory finalizes a media history entry with end time and final play time.
func (*UserDB) CountClients ¶ added in v2.11.0
func (*UserDB) CreateClient ¶ added in v2.11.0
func (*UserDB) CreateProfile ¶ added in v2.16.0
func (*UserDB) DeleteAllInboxMessages ¶ added in v2.8.0
func (*UserDB) DeleteClient ¶ added in v2.11.0
func (*UserDB) DeleteDeviceState ¶ added in v2.16.0
func (*UserDB) DeleteInboxMessage ¶ added in v2.8.0
func (*UserDB) DeleteMapping ¶
func (*UserDB) DeleteMediaUserData ¶ added in v2.15.0
DeleteMediaUserData removes the user-data row for (SystemID, Path). Deleting a row that does not exist is not an error.
func (*UserDB) DeleteProfile ¶ added in v2.16.0
DeleteProfile removes a profile. If the profile is the device's active profile, the active-profile device state is cleared in the same transaction.
func (*UserDB) EnsureRecentBackup ¶ added in v2.15.0
func (*UserDB) GetClientByToken ¶ added in v2.11.0
func (*UserDB) GetDeviceState ¶ added in v2.16.0
GetDeviceState returns the value for key and whether it exists.
func (*UserDB) GetDistinctMediaHistory ¶
func (db *UserDB) GetDistinctMediaHistory( ctx context.Context, systemIDs []string, lastID int64, limit int, ) ([]database.MediaHistoryEntry, error)
GetDistinctMediaHistory returns the newest lean history row for each (system, media path) identity, ordered for stable cursor pagination.
func (*UserDB) GetEnabledMappings ¶
func (*UserDB) GetHistory ¶
func (db *UserDB) GetHistory(lastID int64) ([]database.HistoryEntry, error)
func (*UserDB) GetInboxMessages ¶ added in v2.8.0
func (db *UserDB) GetInboxMessages() ([]database.InboxMessage, error)
func (*UserDB) GetLatestMediaHistory ¶ added in v2.14.0
func (db *UserDB) GetLatestMediaHistory() (database.MediaHistoryEntry, bool, error)
GetLatestMediaHistory retrieves the most recent media history entry with no enrichment.
func (*UserDB) GetMediaHistory ¶ added in v2.7.0
func (db *UserDB) GetMediaHistory(systemIDs []string, lastID int64, limit int) ([]database.MediaHistoryEntry, error)
GetMediaHistory retrieves media history entries with pagination and optional system filtering.
func (*UserDB) GetMediaHistoryIdentityBackfillBatch ¶ added in v2.16.1
func (db *UserDB) GetMediaHistoryIdentityBackfillBatch( afterDBID int64, policyVersion int, limit int, ) ([]database.MediaHistoryEntry, error)
GetMediaHistoryIdentityBackfillBatch returns legacy history rows whose scanner identity is absent or older than policyVersion. Future-policy rows are never selected for downgrade.
func (*UserDB) GetMediaHistorySyncBatch ¶ added in v2.16.0
func (db *UserDB) GetMediaHistorySyncBatch( after time.Time, afterDBID int64, limit int, ) ([]database.MediaHistoryEntry, error)
GetMediaHistorySyncBatch returns unsynced rows after the local (after, afterDBID) cursor in (UpdatedAt, DBID) order. Mutations clear SyncedAt, so acknowledged rows do not repeat and unreliable-clock rows remain eligible regardless of the server's timestamp watermark. Rows without a session UUID are excluded; startup backfills legacy rows first.
func (*UserDB) GetMediaHistoryTop ¶ added in v2.10.0
func (db *UserDB) GetMediaHistoryTop( systemIDs []string, since *time.Time, limit int, ) ([]database.MediaHistoryTopEntry, error)
GetMediaHistoryTop returns aggregated media history grouped by SystemID+MediaName, sorted by total play time descending.
func (*UserDB) GetMediaUserData ¶ added in v2.15.0
GetMediaUserData returns the user-data row for a media path. The bool is false when no row exists for the (systemID, path) key, in which case the media has no favourite or launcher-override intent recorded.
func (*UserDB) GetProfile ¶ added in v2.16.0
func (*UserDB) GetProfileBySwitchID ¶ added in v2.16.0
func (*UserDB) GetSupportedZapLinkHosts ¶ added in v2.8.0
func (*UserDB) GetZapLinkHost ¶
func (*UserDB) HealTimestamps ¶ added in v2.7.0
HealTimestamps corrects timestamps for records created with unreliable clocks (MiSTer boot without NTP). When NTP syncs, this reconstructs correct timestamps using: TrueStartTime = TrueBootTime + MonotonicStart
func (*UserDB) IntegrityReport ¶ added in v2.15.0
func (*UserDB) IsMarkedCorrupt ¶ added in v2.15.0
func (*UserDB) ListBackups ¶ added in v2.15.0
func (db *UserDB) ListBackups() ([]database.BackupInfo, error)
func (*UserDB) ListClients ¶ added in v2.11.0
func (*UserDB) ListMediaUserData ¶ added in v2.15.0
func (db *UserDB) ListMediaUserData() ([]database.MediaUserData, error)
ListMediaUserData returns every user-data row, used by the reindex re-apply step to re-materialize the media.db projection.
func (*UserDB) ListProfiles ¶ added in v2.16.0
func (*UserDB) ListRecentRemoteCommands ¶
func (db *UserDB) ListRecentRemoteCommands(limit int) ([]database.RemoteCommand, error)
ListRecentRemoteCommands returns the most recently created remote commands, newest first, for display as an owner-facing activity log. limit is clamped to at least 1 by the caller.
func (*UserDB) ListUnreportedRemoteCommands ¶
func (db *UserDB) ListUnreportedRemoteCommands(limit int) ([]database.RemoteCommand, error)
ListUnreportedRemoteCommands returns terminal commands whose result has not yet been posted, oldest first. limit bounds a single call so the caller can post the batch a little at a time; see remote.replayStoredResults.
func (*UserDB) MarkCorrupt ¶ added in v2.15.0
func (*UserDB) MarkMediaHistorySynced ¶ added in v2.16.0
func (db *UserDB) MarkMediaHistorySynced(refs []database.MediaHistorySyncRef, syncedAt time.Time) error
MarkMediaHistorySynced stamps SyncedAt only when each row still matches the uploaded version. Concurrent mutations advance UpdatedAt and remain unsynced.
func (*UserDB) MarkRemoteCommandResultReported ¶
func (*UserDB) NoteCorruption ¶ added in v2.15.0
func (*UserDB) PruneExpiredZapLinkHosts ¶ added in v2.8.0
func (*UserDB) PruneRemoteCommands ¶
func (*UserDB) RecoverFromCorruption ¶ added in v2.15.0
func (db *UserDB) RecoverFromCorruption() (database.RestoreInfo, error)
func (*UserDB) ReplaceAllClients ¶ added in v2.16.0
ReplaceAllClients atomically replaces every paired client row. Device backup restore uses this to carry the destination's paired clients across a restored user database, since portable snapshots are created without client rows.
func (*UserDB) ResetMediaHistorySyncAfter ¶ added in v2.16.0
ResetMediaHistorySyncAfter clears local acknowledgements newer than the server watermark so restored or reset server state is uploaded again. A nil watermark means the server has no sessions and resets every local row.
func (*UserDB) RestoreBackup ¶ added in v2.15.0
func (db *UserDB) RestoreBackup(name string) (database.RestoreInfo, error)
func (*UserDB) SetDeviceState ¶ added in v2.16.0
func (*UserDB) SetMediaUserFavorite ¶ added in v2.15.0
SetMediaUserFavorite records (or clears) the favourite intent for a media path without disturbing any launcher override on the same row. The write is a column-scoped upsert plus a conditional delete, run in one transaction so two concurrent edits to the same path (e.g. a favourite toggle and a launcher override) cannot read-modify-write over each other.
func (*UserDB) SetMediaUserLauncherOverride ¶ added in v2.15.0
SetMediaUserLauncherOverride records (or clears, when launcherID is empty) the launcher-override intent for a media path without disturbing the favourite flag on the same row. See SetMediaUserFavorite for the concurrency guarantee.
func (*UserDB) SetMediaUserSnapshot ¶ added in v2.16.0
SetMediaUserSnapshot records a successfully resolved scanner identity snapshot on an existing user-data row. It never inserts: a snapshot without user intent (favourite/override) is meaningless. Empty tags are significant and replace stale tags; callers must skip this method when lookup fails.
func (*UserDB) SetSQLForTesting ¶
func (db *UserDB) SetSQLForTesting(ctx context.Context, sqlDB *sql.DB, platform platforms.Platform) error
SetSQLForTesting allows injection of a sql.DB instance for testing purposes. This method should only be used in tests to set up in-memory databases.
func (*UserDB) StoreRemoteCommandResult ¶
func (*UserDB) SumMediaPlayTimeForDay ¶ added in v2.15.0
SumMediaPlayTimeForDay returns the total seconds of completed play-time that overlaps with the day starting at dayStart. Sessions that span midnight are pro-rated: only the portion after dayStart is counted. The currently-active session (EndTime IS NULL) is excluded; callers add it separately.
func (*UserDB) SumMediaPlayTimeForDayByProfile ¶ added in v2.16.0
func (db *UserDB) SumMediaPlayTimeForDayByProfile(dayStart time.Time, profileID string) (int64, error)
SumMediaPlayTimeForDayByProfile is SumMediaPlayTimeForDay scoped to history attributed to a single profile. History with no profile (the shared profile) is counted by SumMediaPlayTimeForDay, which sums all rows: shared limits are device-level, so deactivating a profile must not grant a fresh daily allowance.
func (*UserDB) TransitionRemoteCommand ¶
func (*UserDB) UnsafeGetSQLDb ¶
func (*UserDB) UpdateClientLastSeen ¶ added in v2.11.0
func (*UserDB) UpdateMediaHistoryIdentity ¶ added in v2.16.0
func (db *UserDB) UpdateMediaHistoryIdentity(dbid int64, identity *database.MediaIdentity) (bool, error)
UpdateMediaHistoryIdentity stores a complete scanner identity snapshot and reports whether this policy version changed the history row.
func (*UserDB) UpdateMediaHistoryIdentityAndPath ¶
func (db *UserDB) UpdateMediaHistoryIdentityAndPath( dbid int64, path string, identity *database.MediaIdentity, ) (bool, error)
UpdateMediaHistoryIdentityAndPath is UpdateMediaHistoryIdentity plus a MediaPath correction, for backfilling legacy rows recorded under a non-path external identifier (e.g. a MiSTer arcade set name).
func (*UserDB) UpdateMediaHistoryTime ¶ added in v2.7.0
UpdateMediaHistoryTime updates only the PlayTime for currently playing media.
func (*UserDB) UpdateProfile ¶ added in v2.16.0
func (*UserDB) UpdateZapLinkCache ¶
func (*UserDB) UpdateZapLinkHost ¶
func (*UserDB) UpsertMediaUserData ¶ added in v2.15.0
func (db *UserDB) UpsertMediaUserData(data *database.MediaUserData) error
UpsertMediaUserData inserts or updates the user-data row for (SystemID, Path). CreatedAt is set on insert only; UpdatedAt is set on every write. A row with no favourite and no launcher override carries no user intent, so it is deleted rather than persisted (keeping ListMediaUserData and the backfill guard honest).