scheduler

package
v0.36.0 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ProbeModeAuto     = "auto"
	ProbeModeManual   = "manual"
	ProbeModeDisabled = "disabled"
)
View Source
const (
	PauseReasonPeerRatio = "做种竞争度过高"
)

Variables

This section is empty.

Functions

func ComputeTier added in v0.35.0

func ComputeTier(state *models.SiteLoginState, effective, now time.Time) string

ComputeTier maps DaysRemaining into one of the predefined tier strings. The tier defines both the reminder urgency and whether quiet hours can be overridden (only banned-imminent overrides).

func DaysRemaining added in v0.35.0

func DaysRemaining(state *models.SiteLoginState, effective, now time.Time) int

DaysRemaining returns how many full days remain until the site's configured ban threshold elapses since the effective last-active time. Negative values mean the threshold has already been crossed.

func EffectiveLastActive added in v0.35.0

func EffectiveLastActive(state *models.SiteLoginState, now time.Time) time.Time

EffectiveLastActive returns the most recent confirmed activity timestamp for a site.

Per research: site cleanup.php scripts predominantly check `last_access` (NexusPHP) / `last_action` (Unit3D) / `LastAccess` (Gazelle) / `lastModifiedDate` (mTorrent) — NOT `last_login`. So v2 prefers last_access-class timestamps and treats last_login-class timestamps (ApiLastLoginAt, CookieLastLoginAt) as supplementary signals, used only when last_access is missing.

Precedence:

  1. state.LastAccessAt (NexusPHP last_access; primary)
  2. max(state.ApiLastLoginAt, state.CookieLastLoginAt) (R-Q-A5: take newer of dual timestamps)
  3. state.LastLoginAt (legacy v1 field)
  4. state.LastVisitAt (extension visit; only when probes failing >12h)
  5. zero value (no signal yet)

Types

type CleanupMonitor added in v0.14.0

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

func NewCleanupMonitor added in v0.14.0

func NewCleanupMonitor(db *gorm.DB, downloaderMgr *downloader.DownloaderManager) *CleanupMonitor

func (*CleanupMonitor) RunManual added in v0.14.0

func (c *CleanupMonitor) RunManual() (int, error)

func (*CleanupMonitor) Start added in v0.14.0

func (c *CleanupMonitor) Start() error

func (*CleanupMonitor) Stop added in v0.14.0

func (c *CleanupMonitor) Stop()

type CredentialDecryptor added in v0.35.0

type CredentialDecryptor interface {
	Decrypt(setting models.SiteSetting) (cookie string, err error)
}

CredentialDecryptor returns a usable cookie/api-key for a site. Real implementations decrypt SiteSetting.CookieEncrypted via core.ConfigStore; tests inject deterministic values.

type CronExpr added in v0.35.0

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

CronExpr is a tiny 5-field cron evaluator (minute hour dom month dow) covering the patterns used by LoginReminderMonitor:

  • Numeric value: "10", "22"
  • Comma list: "10,22"
  • Wildcard: "*"
  • Step: "*/5", "*/15"
  • Range: "1-5"

Day-of-week uses 0-6 with 0=Sunday (the standard cron convention). This is intentionally NOT a full cron implementation — it is purpose-built for reminder schedules and rejects unsupported syntax with a parse error so misconfigurations fail loud at config time, not silently at midnight.

func ParseCron added in v0.35.0

func ParseCron(spec string) (*CronExpr, error)

ParseCron parses a 5-field cron expression like "0 10,22 * * *".

func (*CronExpr) Match added in v0.35.0

func (c *CronExpr) Match(t time.Time) bool

Match reports whether the given time falls exactly on a cron tick (minute granularity). It does NOT consider seconds.

func (*CronExpr) WindowStart added in v0.35.0

func (c *CronExpr) WindowStart(t time.Time) time.Time

WindowStart returns the most recent cron tick at or before t. The boundary is used by reminder dedup: two events that map to the same WindowStart are considered the same window and at most one reminder fires per window.

type FreeEndMonitor added in v0.3.0

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

func NewFreeEndMonitor added in v0.3.0

func NewFreeEndMonitor(db *gorm.DB, downloaderMgr *downloader.DownloaderManager) *FreeEndMonitor

func (*FreeEndMonitor) CancelTorrent added in v0.3.0

func (m *FreeEndMonitor) CancelTorrent(torrentID uint)

func (*FreeEndMonitor) ScheduleTorrent added in v0.3.0

func (m *FreeEndMonitor) ScheduleTorrent(torrent models.TorrentInfo)

func (*FreeEndMonitor) Start added in v0.3.0

func (m *FreeEndMonitor) Start() error

func (*FreeEndMonitor) Stop added in v0.3.0

func (m *FreeEndMonitor) Stop()

func (*FreeEndMonitor) TestHandleFreeEndedTorrent added in v0.3.0

func (m *FreeEndMonitor) TestHandleFreeEndedTorrent(torrent models.TorrentInfo)

TestHandleFreeEndedTorrent 暴露给测试/调试命令使用

type JobStatus added in v0.31.0

type JobStatus struct {
	SiteName  string
	RSSName   string
	Running   bool
	StartedAt time.Time
}

type LoginReminderConfig added in v0.35.0

type LoginReminderConfig struct {
	DB           *gorm.DB
	Router       *notify.Router
	Resolver     SiteResolver
	Decryptor    CredentialDecryptor
	Clock        sitelogin.Clock
	Logger       *zap.SugaredLogger
	QuietHours   QuietHours
	ProbeEvery   time.Duration
	ReminderTick time.Duration
}

LoginReminderConfig holds the dependencies needed to construct a LoginReminderMonitor. All fields are required except QuietHours, which defaults to "no quiet window".

type LoginReminderMonitor added in v0.35.0

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

LoginReminderMonitor runs two cooperating loops:

  1. probe loop (every 6h, per-site jitter): calls sitelogin.Probe() and persists last_login/last_access into models.SiteLoginState. On consecutive failure it backs off and after 24h emits a "cookie may have expired" warning notification.

  2. reminder loop (every minute): for each enabled site whose effective last-active time is within (BanThresholdDays - RemindBeforeDays) of today, it computes a tier and fires a notification through notify.Router. Dedup is window-based (see CronExpr.WindowStart): within the same cron window only one reminder is sent, but a tier escalation immediately re-fires regardless of window.

The monitor never issues raw HTTP itself; all site I/O passes through the site/v2 driver layer (which inherits the project's circuit breaker and rate limiter).

func NewLoginReminderMonitor added in v0.35.0

func NewLoginReminderMonitor(cfg LoginReminderConfig) *LoginReminderMonitor

NewLoginReminderMonitor builds a LoginReminderMonitor. It does not start the loops; call Start to begin processing.

func (*LoginReminderMonitor) RunProbeOnce added in v0.35.0

func (m *LoginReminderMonitor) RunProbeOnce(ctx context.Context)

RunProbeOnce iterates enabled sites, probes each, and persists the result. It is exported so tests can drive the probe phase directly with a fake clock. This is cron-driven (manualTrigger=false). Sites whose probe slot is held by another trigger source (manual REST / extension push) are skipped this iteration with a debug log — R23 shared single-flight.

func (*LoginReminderMonitor) RunProbeOnceForSite added in v0.35.0

func (m *LoginReminderMonitor) RunProbeOnceForSite(ctx context.Context, siteName string) bool

RunProbeOnceForSite probes a single site by name. Used for manual endpoint triggering (manualTrigger=true), bypassing mode restrictions. Acquires the shared probe lock; returns false if another caller already holds it.

func (*LoginReminderMonitor) RunProbeOnceForSiteLocked added in v0.35.0

func (m *LoginReminderMonitor) RunProbeOnceForSiteLocked(ctx context.Context, siteName string)

RunProbeOnceForSiteLocked runs the probe assuming the caller already holds the per-site probe lock (via TryAcquireProbeLock). The web handler uses this split so the HTTP layer can shape its 409 response from TryAcquireProbeLock before dispatching the actual probe work.

func (*LoginReminderMonitor) RunReminderOnce added in v0.35.0

func (m *LoginReminderMonitor) RunReminderOnce(ctx context.Context)

RunReminderOnce iterates enabled sites, computes each site's tier, and fires reminders that are due. Exported for testability.

func (*LoginReminderMonitor) SendTestReminder added in v0.35.0

func (m *LoginReminderMonitor) SendTestReminder(ctx context.Context, siteName string) error

SendTestReminder immediately sends a test login-reminder notification for one site.

func (*LoginReminderMonitor) Start added in v0.35.0

func (m *LoginReminderMonitor) Start()

Start launches the probe and reminder loops. Calling Start twice is a no-op.

func (*LoginReminderMonitor) Stop added in v0.35.0

func (m *LoginReminderMonitor) Stop()

Stop signals both loops to exit and waits for them to drain.

func (*LoginReminderMonitor) TryAcquireProbeLock added in v0.35.0

func (m *LoginReminderMonitor) TryAcquireProbeLock(siteName string) (release func(), ok bool)

TryAcquireProbeLock attempts to acquire the per-site probe mutex without blocking. Returns (release, true) on success — caller MUST invoke release() in defer to free the slot. Returns (nil, false) if another caller (cron, REST, or extension) already holds the mutex; the caller should surface a 409-style "probe in progress" response. R23: this single map is the shared single-flight registry across all probe trigger sources.

type Manager

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

func NewManager

func NewManager() *Manager

func (*Manager) GetDownloaderManager added in v0.2.0

func (m *Manager) GetDownloaderManager() *downloader.DownloaderManager

GetDownloaderManager 获取下载器管理器

func (*Manager) GetFreeEndMonitor added in v0.3.0

func (m *Manager) GetFreeEndMonitor() *FreeEndMonitor

GetFreeEndMonitor 获取免费结束监控器

func (*Manager) GetLoginReminderMonitor added in v0.35.0

func (m *Manager) GetLoginReminderMonitor() *LoginReminderMonitor

GetLoginReminderMonitor returns the registered monitor, or nil if not yet wired. Used by web handlers that want to trigger ad-hoc probes.

func (*Manager) InitFreeEndMonitor added in v0.3.0

func (m *Manager) InitFreeEndMonitor()

func (*Manager) LastVersion

func (m *Manager) LastVersion() int64

func (*Manager) ListJobs added in v0.31.0

func (m *Manager) ListJobs() []JobStatus

func (*Manager) Reload

func (m *Manager) Reload(cfg *models.Config)

func (*Manager) SetLoginReminderMonitor added in v0.35.0

func (m *Manager) SetLoginReminderMonitor(mon *LoginReminderMonitor)

SetLoginReminderMonitor wires a fully-constructed LoginReminderMonitor into the manager. It is intended to be called by the web layer after the monitor has been built with a SiteResolver that knows how to produce v2.Site instances. Calling this twice replaces and stops the previous instance. Pass nil to detach without stopping.

func (*Manager) Start

func (m *Manager) Start(site models.SiteGroup, r models.RSSConfig, runner func(ctx context.Context))

func (*Manager) StartAll

func (m *Manager) StartAll(cfg *models.Config)

StartAll 按配置启动所有任务(不做停止)

func (*Manager) Stop

func (m *Manager) Stop(site models.SiteGroup, rssName string)

func (*Manager) StopAll

func (m *Manager) StopAll()

StopAll 取消所有任务并等待当前执行结束

type PeerRatioMonitor added in v0.23.0

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

func NewPeerRatioMonitor added in v0.23.0

func NewPeerRatioMonitor(db *gorm.DB, downloaderMgr *downloader.DownloaderManager) *PeerRatioMonitor

func (*PeerRatioMonitor) Start added in v0.23.0

func (p *PeerRatioMonitor) Start() error

func (*PeerRatioMonitor) Stop added in v0.23.0

func (p *PeerRatioMonitor) Stop()

type QuietHours added in v0.35.0

type QuietHours struct {
	Start string
	End   string
}

QuietHours represents the system-wide quiet window for non-critical notifications. Time strings use 24h "HH:MM" format. Empty start/end disables the window. The banned-imminent tier overrides quiet hours.

type SiteResolver added in v0.35.0

type SiteResolver interface {
	Resolve(setting models.SiteSetting) (*v2.SiteDefinition, v2.Site, error)
}

SiteResolver wires a SiteSetting row into a v2.Site instance plus its SiteDefinition. It is injected so that tests can substitute fake sites without touching the global site registry or HTTP layer.

Jump to

Keyboard shortcuts

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