activity

package
v0.1.151 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: GPL-2.0, GPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package activity provides concurrent-safe activity and alert tracking for the subflux UI status indicator.

Index

Constants

View Source
const DefaultPruneAge = 15 * time.Minute

DefaultPruneAge is the duration after which completed activities are pruned.

View Source
const TransientAlertTTL = 1 * time.Hour

TransientAlertTTL is the default TTL for transient alerts.

Variables

This section is empty.

Functions

This section is empty.

Types

type ActivitySource

type ActivitySource string //nolint:revive // stutters but renaming breaks callers

ActivitySource is a typed string for activity entry source values.

const (
	SourceScheduled ActivitySource = "scheduled"
	SourceManual    ActivitySource = "manual"
)

Activity source constants.

type Alert

type Alert struct {
	Time      time.Time     `json:"time"`
	Level     AlertLevel    `json:"level"` // "error", "warn", "info"
	Message   string        `json:"message"`
	Source    string        `json:"source"`
	Kind      AlertKind     `json:"kind"`
	TTL       time.Duration `json:"-"` // per-alert TTL override; 0 = use default
	ID        int           `json:"id"`
	Dismissed bool          `json:"dismissed"`
}

Alert represents an actionable error or informational message.

type AlertKind

type AlertKind string

AlertKind controls how an alert is displayed and dismissed.

const (
	// AlertPersistent requires manual dismissal by the user.
	AlertPersistent AlertKind = "persistent"
	// AlertTransient auto-expires after TransientAlertTTL.
	AlertTransient AlertKind = "transient"
)

type AlertLevel

type AlertLevel string

AlertLevel is a typed string for alert severity levels.

const (
	LevelError AlertLevel = "error"
	LevelWarn  AlertLevel = "warn"
	LevelInfo  AlertLevel = "info"
)

Alert level constants.

type AlertLog

type AlertLog struct {
	// contains filtered or unexported fields
}

AlertLog tracks actionable errors for the UI.

func NewAlertLog

func NewAlertLog(capacity int) *AlertLog

NewAlertLog creates an AlertLog with the given max capacity.

func (*AlertLog) AddAlert

func (al *AlertLog) AddAlert(source, message string, kind AlertKind, level AlertLevel, ttl time.Duration)

AddAlert appends an alert with the given level and optional TTL override.

func (*AlertLog) AlertsUnsafe

func (al *AlertLog) AlertsUnsafe() []Alert

AlertsUnsafe returns the internal alerts slice without copying. Caller must hold the lock.

func (*AlertLog) AppendAlert

func (al *AlertLog) AppendAlert(a Alert)

AppendAlert appends an alert directly (for test setup). Caller must hold the lock.

func (*AlertLog) Dismiss

func (al *AlertLog) Dismiss(id int) bool

Dismiss marks an alert as dismissed by ID.

func (*AlertLog) DismissBySource

func (al *AlertLog) DismissBySource(source string)

DismissBySource dismisses all undismissed persistent alerts from a source.

func (*AlertLog) Lock

func (al *AlertLog) Lock()

Lock acquires a write lock (for test manipulation).

func (*AlertLog) RLock

func (al *AlertLog) RLock()

RLock acquires a read lock (for test inspection).

func (*AlertLog) RUnlock

func (al *AlertLog) RUnlock()

RUnlock releases a read lock.

func (*AlertLog) Record

func (al *AlertLog) Record(source, message string)

Record adds a transient error alert.

func (*AlertLog) RecordInfo

func (al *AlertLog) RecordInfo(message string)

RecordInfo adds a short-lived informational alert for scan results.

func (*AlertLog) RecordPersistent

func (al *AlertLog) RecordPersistent(source, message string)

RecordPersistent adds a persistent error that requires manual dismissal.

func (*AlertLog) RecordWarn

func (al *AlertLog) RecordWarn(source, message string)

RecordWarn adds a transient warning alert.

func (*AlertLog) Unlock

func (al *AlertLog) Unlock()

Unlock releases a write lock.

func (*AlertLog) VisibleAlerts

func (al *AlertLog) VisibleAlerts() []Alert

VisibleAlerts returns a copy of non-dismissed, non-expired alerts.

type Entry

type Entry struct {
	StartedAt    time.Time      `json:"started_at"`
	EndedAt      *time.Time     `json:"ended_at,omitempty"`
	ID           string         `json:"id"`
	Action       string         `json:"action"`
	Detail       string         `json:"detail"`
	Source       ActivitySource `json:"source"` // "scheduled" or "manual"
	Kind         ScanKind       `json:"kind,omitempty"`
	MediaType    api.MediaType  `json:"media_type,omitempty"`
	RequiredRole auth.Role      `json:"required_role,omitempty"`
	MediaID      int            `json:"media_id,omitempty"`
	Season       int            `json:"season,omitempty"`
	Episode      int            `json:"episode,omitempty"`
	Current      int            `json:"current,omitempty"`
	Total        int            `json:"total,omitempty"`
	Done         bool           `json:"done"`
	Queued       bool           `json:"queued,omitempty"`
	Cancelled    bool           `json:"cancelled,omitempty"`
	Failed       bool           `json:"failed,omitempty"`
	Cancellable  bool           `json:"cancellable,omitempty"`
}

Entry represents an ongoing or recent action.

Scan entries additionally carry their structured scope (Kind, MediaType, MediaID, Season, Episode) and the role required to cancel them (RequiredRole: user for per-item scans, admin for full scans). Cancellable is a serialization-time flag merged from the StopRegistry by the activity GET handler; it is never persisted on the stored entry.

type Log

type Log struct {
	// contains filtered or unexported fields
}

Log tracks recent actions for the UI status indicator.

func New

func New(maxItems int) *Log

New creates an ActivityLog with the given max capacity.

func (*Log) ActiveScan added in v0.1.148

func (a *Log) ActiveScan(scope ScanScope) (string, bool)

ActiveScan returns the ID of the live (not done, not cancelled) scan entry matching scope, if any. It is the read-only half of StartScan's same-scope idempotency: endpoints guarding a start by other means (the full-scan CompareAndSwap flag) use it to answer a duplicate start with the RUNNING scan's id instead of a conflict.

func (*Log) Cancel

func (a *Log) Cancel(id string) bool

Cancel marks a queued activity as cancelled. Returns true if found and cancelled.

func (*Log) Dismiss

func (a *Log) Dismiss(id string)

Dismiss removes a completed activity by ID.

func (*Log) End

func (a *Log) End(id string)

End marks an activity as done.

func (*Log) Entries

func (a *Log) Entries() []Entry

Entries returns a snapshot of all entries (for serialization). Each entry is deep-copied (see snapshotEntry): callers may mutate the result freely.

func (*Log) Fail

func (a *Log) Fail(id string)

Fail marks an activity as done with failure.

func (*Log) FinishCancelled added in v0.1.148

func (a *Log) FinishCancelled(id string)

FinishCancelled marks an activity as TERMINALLY cancelled: Done=true, Cancelled=true, EndedAt set. This is the terminal state a user-stopped scan reaches — unlike the queued-dismiss Cancel flag alone, the entry stops rendering as running and becomes prunable.

func (*Log) Get added in v0.1.148

func (a *Log) Get(id string) (Entry, bool)

Get returns a snapshot copy of the entry with the given ID. The copy is deep where it matters: mutating it — including through its EndedAt pointer — never touches the log's internal state (see snapshotEntry).

func (*Log) IsCancelled

func (a *Log) IsCancelled(id string) bool

IsCancelled checks if an activity has been cancelled.

func (*Log) Progress

func (a *Log) Progress(id string, current, total int, detail string)

Progress updates the current/total counters and detail for an activity.

func (*Log) PruneCompleted

func (a *Log) PruneCompleted(maxAge time.Duration)

PruneCompleted removes completed activities older than maxAge.

func (*Log) SetQueued

func (a *Log) SetQueued(id string, queued bool)

SetQueued marks an activity as queued (waiting to run).

func (*Log) Start

func (a *Log) Start(action, detail string, source ActivitySource) string

Start records a new activity and returns its ID.

func (*Log) StartScan added in v0.1.148

func (a *Log) StartScan(action, detail string, source ActivitySource,
	scope ScanScope, role auth.Role,
) (id string, existing bool)

StartScan records a new scan activity carrying its structured scope and the role required to cancel it. Idempotent same-scope start: when an active (not done, not cancelled) entry with the same scope already exists, its ID is returned with existing=true and no new entry is created — the find-and-create pair runs under one lock so two concurrent same-scope starts cannot both create an entry.

type Outcome added in v0.1.148

type Outcome string

Outcome is the four-valued terminal outcome of a background scan runner. It replaces the former activityOK bool: a user-requested stop (cancelled) and process shutdown must never collapse into one state, and a cancelled scan must not end as success.

const (
	OutcomeCompleted Outcome = "completed"
	OutcomeFailed    Outcome = "failed"
	OutcomeCancelled Outcome = "cancelled"
	OutcomeShutdown  Outcome = "shutdown"
)

Scan outcome constants.

type ScanKind added in v0.1.148

type ScanKind string

ScanKind identifies which scan endpoint family a scan activity belongs to. Together with the media fields it forms the structured scan scope carried on activity entries (background-scans S12): the UI reconstructs running scans per scope from these fields instead of parsing human Action/Detail strings.

const (
	ScanKindSeries ScanKind = "series"
	ScanKindSeason ScanKind = "season"
	ScanKindMovie  ScanKind = "movie"
	ScanKindItem   ScanKind = "item"
	ScanKindFull   ScanKind = "full"
)

Scan kind constants, one per scan start.

type ScanScope added in v0.1.148

type ScanScope struct {
	Kind      ScanKind
	MediaType api.MediaType
	MediaID   int
	Season    int
	Episode   int
}

ScanScope is the structured identity of a scan: which endpoint family started it and which media it covers. Zero fields mean "not applicable" (e.g. a full scan has only Kind). It is a parameter/matching struct; the fields are stored flat on the Entry for serialization.

type StopRegistry added in v0.1.148

type StopRegistry struct {
	// contains filtered or unexported fields
}

StopRegistry tracks the live stop callbacks of running background scans, keyed by activity ID. The zero value is ready to use.

func (*StopRegistry) Cancellable added in v0.1.148

func (r *StopRegistry) Cancellable(id string) bool

Cancellable reports whether a live stop registration exists for id — the serialization-time source of the activity DTO's cancellable flag.

func (*StopRegistry) RegisterStop added in v0.1.148

func (r *StopRegistry) RegisterStop(id string, stop func()) (unregister func())

RegisterStop stores the stop callback for a running scan and returns the matching unregister func. The registration MUST be released on every terminal transition (end, fail, cancelled) or failed scans leak registrations and keep reporting cancellable; callers release via defer so every outcome path (including panics) unregisters. Unregister is idempotent.

func (*StopRegistry) RequestStop added in v0.1.148

func (r *StopRegistry) RequestStop(id string) StopResult

RequestStop asks the scan registered under id to stop gracefully. The first request invokes the stop callback AFTER releasing the registry lock (a callback must never run under any mutex) and returns StopRequested; repeated requests are idempotent (StopAlreadyStopping, callback not re-invoked). An id without a live registration — never registered, or already released by a terminal transition (the cancel-vs-end race resolves here as a no-op) — returns StopNotFound.

type StopResult added in v0.1.148

type StopResult int

StopResult is the outcome of a stop request against the registry.

const (
	StopRequested StopResult = iota
	StopAlreadyStopping
	StopNotFound
	StopNotCancellable
)

Stop request outcomes. StopNotCancellable is never returned by the registry itself (every registration is stoppable); it exists as shared vocabulary for the composing endpoint, which maps StopNotFound plus an existing terminal entry onto "not cancellable" (409).

type WarnRecorder

type WarnRecorder interface {
	RecordWarn(source, msg string)
}

WarnRecorder is the narrow interface for recording warning alerts. Both manualops and polling consume this single-method contract. The concrete type AlertLog satisfies it via structural typing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL