2023-05-20 15:59:28 +00:00
|
|
|
package job
|
|
|
|
|
|
|
|
|
|
import (
|
2026-05-10 00:13:42 +00:00
|
|
|
"github.com/mhsanaei/3x-ui/v3/web/service"
|
2023-05-20 15:59:28 +00:00
|
|
|
)
|
|
|
|
|
|
2025-09-20 07:35:50 +00:00
|
|
|
// CheckHashStorageJob periodically cleans up expired hash entries from the Telegram bot's hash storage.
|
2023-05-20 15:59:28 +00:00
|
|
|
type CheckHashStorageJob struct {
|
|
|
|
|
tgbotService service.Tgbot
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-20 07:35:50 +00:00
|
|
|
// NewCheckHashStorageJob creates a new hash storage cleanup job instance.
|
2023-05-20 15:59:28 +00:00
|
|
|
func NewCheckHashStorageJob() *CheckHashStorageJob {
|
|
|
|
|
return new(CheckHashStorageJob)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-20 07:35:50 +00:00
|
|
|
// Run removes expired hash entries from the Telegram bot's hash storage.
|
2023-05-20 15:59:28 +00:00
|
|
|
func (j *CheckHashStorageJob) Run() {
|
fix: hash-storage panic on SIGHUP and seeder dup-key on cold restart (#4539)
Two bugs that combine into an unrecoverable crash loop after a user
enables the Telegram bot in settings on a fresh install.
1. CheckHashStorageJob.Run panics with a nil pointer dereference. The
cron job is scheduled whenever settings say the bot is enabled, but
the package-level hash storage is only initialized inside
Tgbot.Start, which StartPanelOnly intentionally skips
(startTgBot=false). Toggling the bot on via the panel triggers
SIGHUP, the storage stays nil, and the cron fires 2 minutes later
and panics, exiting 2.
2. seedClientsFromInboundJSON is not idempotent. The fresh-install
early-return path recorded only UserPasswordHash + ApiTokensTable,
never ClientsTable. After the admin adds clients via the panel
(which writes to the clients table through SyncInbound), the next
start runs the seeder for the first time, finds matching emails
already in the table, and fails with SQLSTATE 23505 on
idx_clients_email, turning the panic above into an unrecoverable
crash loop on PostgreSQL.
Fixes:
- web/job/check_hash_storage.go: nil-check the storage before calling
RemoveExpiredHashes.
- database/db.go: in the fresh-install early-return path, also record
ClientsTable so the seeder never re-runs against panel-added data.
- database/db.go: hydrate seedClientsFromInboundJSON's byEmail cache
from existing rows so it merges instead of inserting when a row with
the same email already lives in the clients table.
Regression tests cover both paths.
Closes #4539
2026-05-24 20:45:14 +00:00
|
|
|
storage := j.tgbotService.GetHashStorage()
|
|
|
|
|
if storage == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
storage.RemoveExpiredHashes()
|
2023-05-20 15:59:28 +00:00
|
|
|
}
|