Documentation
¶
Overview ¶
Package store is the SQLite-backed persistence layer. The DB is the single source of truth; the Xray config is always derived from it.
Index ¶
- Variables
- func Check(path string) error
- func InspectDB(path string) (users, admins int, secret string, err error)
- func PeekTimezone(dbPath string) string
- type AdminAuditFilter
- type NodeEdit
- type SessionAdmin
- type Store
- func (s *Store) ActiveDeviceCounts(since int64) (map[int64]int, error)
- func (s *Store) AddAdminAudit(ev model.AdminAudit) error
- func (s *Store) AddConnection(userID int64, ip string, ts int64) error
- func (s *Store) AddDailyTraffic(userID int64, day string, up, down int64) error
- func (s *Store) AddDailyTrafficNode(userID, nodeID int64, day string, up, down int64) error
- func (s *Store) AddUsedTraffic(userID, up, down int64) error
- func (s *Store) AddUserEvent(ev model.UserEvent) error
- func (s *Store) CancelPaymentOrderIfPending(id int64) (bool, error)
- func (s *Store) Checkpoint() error
- func (s *Store) ClaimNodeReport(id, reportID int64) (bool, error)
- func (s *Store) ClaimRegistrationRequest(id int64) (bool, error)
- func (s *Store) ClearUserTelegramChat(userID int64) error
- func (s *Store) ClearUserTgLinkCode(userID int64) error
- func (s *Store) Close() error
- func (s *Store) ConsumeJoinToken(raw string) (*model.Node, string, error)
- func (s *Store) CountAdmins() (int, error)
- func (s *Store) CountUsersOnPlan(planID int64) (int, error)
- func (s *Store) CreateAPIKey(name string) (*model.APIKey, error)
- func (s *Store) CreateAdmin(username, passwordHash, role string, mustChange bool) (int64, error)
- func (s *Store) CreateNode(name, host, decoyTemplate string) (*model.Node, error)
- func (s *Store) CreatePaymentOrder(userID, planID int64, amountRub int) (*model.PaymentOrder, error)
- func (s *Store) CreateRegistrationRequest(chatID int64, name string, now int64) (*model.RegistrationRequest, error)
- func (s *Store) CreateSession(adminID int64, ttl time.Duration) (string, error)
- func (s *Store) CreateUser(name, uuid, password, subToken string, dataLimit, expireAt int64, ...) (*model.User, error)
- func (s *Store) CreateWebhook(url string, events []string) (*model.Webhook, error)
- func (s *Store) DeleteAdmin(id int64) error
- func (s *Store) DeleteNode(id int64) error
- func (s *Store) DeleteRegistrationRequest(id int64) error
- func (s *Store) DeleteSession(token string) error
- func (s *Store) DeleteSessionsForAdmin(adminID int64) error
- func (s *Store) DeleteSessionsForAdminExcept(adminID int64, keepToken string) error
- func (s *Store) DeleteTariffPlan(id int64) error
- func (s *Store) DeleteUser(id int64) error
- func (s *Store) DeleteUsers(ids []int64) (int64, error)
- func (s *Store) DeleteWebhook(id int64) error
- func (s *Store) EnabledWebhooksForEvent(event string) ([]model.Webhook, error)
- func (s *Store) ExpiredUsersBefore(cutoff int64) ([]model.User, error)
- func (s *Store) GetAdmin(id int64) (model.Admin, error)
- func (s *Store) GetAdminAuth(username string) (id int64, hash, role string, err error)
- func (s *Store) GetAdminHash(id int64) (string, error)
- func (s *Store) GetDetachedUserByPrevChat(chatID int64) (*model.User, error)
- func (s *Store) GetNode(id int64) (*model.Node, error)
- func (s *Store) GetPaymentOrder(id int64) (*model.PaymentOrder, error)
- func (s *Store) GetPaymentOrderByProvider(provider, providerID string) (*model.PaymentOrder, error)
- func (s *Store) GetPaymentProvider(key string) (model.PaymentProvider, error)
- func (s *Store) GetRegistrationRequest(id int64) (*model.RegistrationRequest, error)
- func (s *Store) GetRegistrationRequestByChat(chatID int64) (*model.RegistrationRequest, error)
- func (s *Store) GetSettings() (*model.Settings, error)
- func (s *Store) GetTariffPlan(id int64) (*model.TariffPlan, error)
- func (s *Store) GetUser(id int64) (*model.User, error)
- func (s *Store) GetUserBySubToken(token string) (*model.User, error)
- func (s *Store) GetUserByTelegramChatID(chatID int64) (*model.User, error)
- func (s *Store) GetUserByTgLinkCode(code string) (*model.User, error)
- func (s *Store) GetWebhook(id int64) (*model.Webhook, error)
- func (s *Store) IssueJoinToken(id int64) (string, error)
- func (s *Store) LatestPendingManualOrder(userID, planID int64) (*model.PaymentOrder, error)
- func (s *Store) LatestPendingProviderOrder(userID int64) (*model.PaymentOrder, error)
- func (s *Store) LatestPendingProviderOrderForPlan(userID, planID int64, provider string) (*model.PaymentOrder, error)
- func (s *Store) ListAPIKeys() ([]model.APIKey, error)
- func (s *Store) ListAdminAudit(f AdminAuditFilter) ([]model.AdminAudit, error)
- func (s *Store) ListAdmins() ([]model.Admin, error)
- func (s *Store) ListEvents(f UserEventFilter) ([]model.UserEvent, error)
- func (s *Store) ListNodes() ([]model.Node, error)
- func (s *Store) ListPaymentOrders(status string, limit int) ([]model.PaymentOrder, error)
- func (s *Store) ListPaymentProviders() (map[string]model.PaymentProvider, error)
- func (s *Store) ListRegistrationRequests() ([]model.RegistrationRequest, error)
- func (s *Store) ListTariffPlans(includeDisabled bool) ([]model.TariffPlan, error)
- func (s *Store) ListUserEvents(userID int64, limit int, beforeID int64) ([]model.UserEvent, error)
- func (s *Store) ListUsers() ([]model.User, error)
- func (s *Store) ListWebhooks() ([]model.Webhook, error)
- func (s *Store) LookupAPIKey(raw string) (*model.APIKey, error)
- func (s *Store) LookupNodeByToken(raw string) (*model.Node, error)
- func (s *Store) LookupSession(token string) (SessionAdmin, bool)
- func (s *Store) MarkConfigApplied() error
- func (s *Store) MarkPaymentOrderPaidIfPending(id, paidAt int64) (bool, error)
- func (s *Store) MarkWebhookResult(id int64, status int, errStr string) error
- func (s *Store) NodeNameTaken(name string, excludeID int64) (bool, error)
- func (s *Store) NodeTrafficTotals(from, to string) (map[int64][2]int64, error)
- func (s *Store) PaidByProvider() ([]model.ProviderStat, error)
- func (s *Store) PaidSumSince(since int64) (int, error)
- func (s *Store) PendingProviderOrders(limit int) ([]model.PaymentOrder, error)
- func (s *Store) PendingTotals() (count, sum int, err error)
- func (s *Store) PurgeAdminAudit(before int64) (int64, error)
- func (s *Store) PurgeConnections(before int64) (int64, error)
- func (s *Store) PurgeDeletedNodes(before int64) (int64, error)
- func (s *Store) PurgeUserEvents(before int64) (int64, error)
- func (s *Store) RecentConnections(userID int64, limit int) ([]model.Connection, error)
- func (s *Store) ReencryptSensitiveFields() error
- func (s *Store) RegenJoinToken(id int64) (string, error)
- func (s *Store) ResetDailyStats() error
- func (s *Store) ResetTraffic(id, lastUp, lastDown int64) error
- func (s *Store) ResetUserQuota(id, now, lastUp, lastDown int64) error
- func (s *Store) RevertPaymentOrderToPending(id int64) error
- func (s *Store) RevokeAPIKey(id int64) error
- func (s *Store) SaveNodeReality(id int64, priv, pub, shortID, service string) error
- func (s *Store) SaveNodeWarp(id int64, priv, pub, endpoint, v4, v6, reserved string) error
- func (s *Store) SavePaymentProvider(p model.PaymentProvider) error
- func (s *Store) SaveTariffPlan(p *model.TariffPlan) error
- func (s *Store) SetACMEProvider(provider, eabKID, eabHMAC string) error
- func (s *Store) SetAPIPath(p string) error
- func (s *Store) SetAdminEvents(mask int64) error
- func (s *Store) SetAdminRole(id int64, role string) error
- func (s *Store) SetAntiDPI(tlsFragment, tlsMin13, blockQUIC bool, realityMaxTimeDiff int) error
- func (s *Store) SetBillingSettings(st *model.Settings) error
- func (s *Store) SetConfigError(msg string) error
- func (s *Store) SetDecoyTemplate(name string) error
- func (s *Store) SetFingerprints(vless, trojan, reality string) error
- func (s *Store) SetGeoRefresh(hours int) error
- func (s *Store) SetHysteriaPorts(port, hopStart, hopEnd int, interval string) error
- func (s *Store) SetIPListRefresh(hours int) error
- func (s *Store) SetLocalBackup(cron string, keep int) error
- func (s *Store) SetMasterLabel(label string) error
- func (s *Store) SetNodeACME(id int64, host, email, provider, eabKID, eabHMAC string) error
- func (s *Store) SetNodeAPIPath(p string) error
- func (s *Store) SetNodeConnections(id int64, c *model.NodeConnections) error
- func (s *Store) SetNodeDNS(id int64, dns *string) error
- func (s *Store) SetNodeEnabled(id int64, enabled bool) error
- func (s *Store) SetNodeGeoRefresh(id int64, hours int) error
- func (s *Store) SetNodeProtocols(id int64, vless, trojan, hysteria, reality bool) error
- func (s *Store) SetNodeRealityDest(id int64, dest string) error
- func (s *Store) SetNotifiedStatus(id int64, status string) error
- func (s *Store) SetOpera(enabled bool, country string, port int) error
- func (s *Store) SetPanelName(name string) error
- func (s *Store) SetPanelTheme(themeJSON string) error
- func (s *Store) SetPaymentOrderProvider(id int64, provider, providerID, payURL string) error
- func (s *Store) SetPaymentOrderStatus(id int64, status string, paidAt int64) error
- func (s *Store) SetPaymentWebhookSecret(secret string) error
- func (s *Store) SetProtocolEnabled(name string, enabled bool) error
- func (s *Store) SetProtocolNames(vless, reality, trojan, hysteria string) error
- func (s *Store) SetProxyMode(enabled bool, typ string, port int, user, pass string) error
- func (s *Store) SetRealityKeys(priv, pub, shortID, serviceName string) error
- func (s *Store) SetRealityPorts(port int, dest string) error
- func (s *Store) SetResetPeriod(id int64, period string, now int64) error
- func (s *Store) SetRoutingConfig(cfg model.RoutingConfig) error
- func (s *Store) SetSecretPath(p string) error
- func (s *Store) SetSetupDone(done bool) error
- func (s *Store) SetSubSettings(st *model.Settings) error
- func (s *Store) SetSubToken(id int64, token string) error
- func (s *Store) SetTLS(host, sni, mode, certPath, keyPath string) error
- func (s *Store) SetTLSMode(mode, host, sni, acmeEmail string) error
- func (s *Store) SetTelegramBot(enabled bool, token, cron string) error
- func (s *Store) SetTelegramChats(csv string) error
- func (s *Store) SetTelegramLinkCode(code string) error
- func (s *Store) SetTelegramUserBot(enabled bool, token, regMode, regCode string) error
- func (s *Store) SetTimezone(tz string) error
- func (s *Store) SetUserAutoDeleteDays(days int) error
- func (s *Store) SetUserEnabled(id int64, enabled bool) error
- func (s *Store) SetUserLimits(id, dataLimit, expireAt int64, deviceLimit int) error
- func (s *Store) SetUserName(id int64, name string) error
- func (s *Store) SetUserPlan(userID, planID int64, trialUsed bool) error
- func (s *Store) SetUserTelegramChat(userID, chatID int64) error
- func (s *Store) SetUserTgLinkCode(userID int64, code string) error
- func (s *Store) SetUsersEnabled(ids []int64, enabled bool) (int64, error)
- func (s *Store) SetWSPath(p string) error
- func (s *Store) SetWarp(st *model.Settings) error
- func (s *Store) SetXrayDNS(dns string) error
- func (s *Store) StatsByUser(from, to string) ([]model.UserTotal, error)
- func (s *Store) StatsSeries(userID int64, from, to string) ([]model.DailyPoint, error)
- func (s *Store) StatsSeriesNode(userID, nodeID int64, from, to string) ([]model.DailyPoint, error)
- func (s *Store) TouchAdminLogin(id int64) error
- func (s *Store) TouchLastSeen(userID, ts int64) error
- func (s *Store) UpdateAdminPassword(id int64, passwordHash string, mustChange bool) error
- func (s *Store) UpdateAdminUsername(id int64, username string) error
- func (s *Store) UpdateNode(id int64, e NodeEdit) error
- func (s *Store) UpdateNodeStatus(id int64, st model.NodeStatusUpdate) error
- func (s *Store) UpdateTraffic(id, addUp, addDown, lastUp, lastDown int64) error
- func (s *Store) UpdateWebhook(id int64, url string, events []string, enabled bool) error
- func (s *Store) UserIDsOnPlan(planID int64) ([]int64, error)
- func (s *Store) UsersWithExpiredPlan(now int64) ([]model.User, error)
- func (s *Store) WorkingUsers(now int64) ([]model.User, error)
- type UserEventFilter
Constants ¶
This section is empty.
Variables ¶
var ErrAdminNotFound = errors.New("admin not found")
ErrAdminNotFound is returned when an admin id matches no row.
var ErrCorrupt = errors.New("database is corrupt")
ErrCorrupt reports a database file that exists but SQLite cannot use: a torn page, a truncated header ("file is not a database"), or a failed integrity check. It is the one failure a caller can act on — the file is unusable, so the only way back is a restore. Everything else from Open is a plain error.
var ErrNodeNameTaken = errors.New("node name already in use")
ErrNodeNameTaken is returned when a node write violates the live-node name unique index (the app-level NodeNameTaken check lost a race). The manager maps it to a user-facing validation error.
var ErrRegistrationPending = errors.New("registration already pending")
ErrRegistrationPending means the chat already has a pending request.
Functions ¶
func Check ¶ added in v0.15.0
Check reports whether the database file at path is usable, without opening the panel's full Store (no migrations, no writes). A missing file is fine — that's a fresh install — so it returns nil. A damaged one returns ErrCorrupt.
This runs at boot before anything touches the data dir, because the alternative is a panel that crash-loops on a corrupt file with no idea it should restore.
func InspectDB ¶
InspectDB opens the database file at path read-only and reports whether it's a usable rospanel database, along with its user/admin counts and configured secret path. A non-nil error means the file isn't a valid panel DB (missing, corrupt, or — the classic empty-backup case — a header with no tables because the data was left in an unbacked-up WAL).
func PeekTimezone ¶ added in v0.13.0
PeekTimezone reads the operator's configured IANA timezone straight from the DB file, without opening the full store (no migrations, no encryption key needed — the zone isn't a secret). It exists so main() can stamp log lines in the operator's zone from the very FIRST line: the real store isn't open that early, and setting the zone later would leave the opening boot lines in the server's system zone while everything after them used the operator's — timestamps jumping an hour mid-boot.
Returns "" for a fresh install (no DB / no row yet), which the caller reads as "use server-local".
Types ¶
type AdminAuditFilter ¶ added in v0.14.0
type AdminAuditFilter struct {
// Actions matches any one of these action keys — a category filter expands to the
// keys in that category (model.AdminAuditActionsIn). Empty means every action.
Actions []string
Actor string // actor_name, exact
BeforeID int64 // page backwards from this id
Limit int
}
AdminAuditFilter narrows the trail. A zero field means "no filter".
type NodeEdit ¶ added in v1.0.0
type NodeEdit struct {
Name string
Host string
DecoyTemplate string
VLESS *bool
Trojan *bool
Hysteria *bool
Reality *bool
Routing *model.RoutingConfig // nil ⇒ inherit global routing
XrayDNS *string // nil ⇒ inherit global DNS
// Per-node egress toggles (independent of the master). WARP keys are provisioned
// separately (SaveNodeWarp) when WarpEnabled flips on.
WarpEnabled bool
OperaEnabled bool
OperaCountry string
}
NodeEdit carries the operator-editable fields of a node. Pointer fields left nil mean "inherit the global setting" for protocol/DNS toggles and routing.
type SessionAdmin ¶ added in v0.14.0
SessionAdmin is who a session belongs to — resolved fresh on every authenticated request, so a role change or a raised password gate takes effect on the admin's next request rather than on their next login.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store wraps the SQLite connection pool.
func Open ¶
Open opens (creating if needed) the SQLite database at path, applies pragmas, verifies the file's integrity, and runs pending migrations. A file SQLite can't read, or one that fails the integrity check, comes back wrapped in ErrCorrupt.
func (*Store) ActiveDeviceCounts ¶ added in v0.3.0
ActiveDeviceCounts returns how many distinct source IPs were seen per user since the given unix timestamp (typically now - DeviceOnlineWindow). INDEXED BY is deliberate. Left alone, SQLite picks the (user_id, ip) primary key so GROUP BY needs no sort, and scans the whole table — which grows a row per source IP per user. `since` is only DeviceOnlineWindow (120s) back, so the rows we want are a tiny slice of that: seeking them on last_seen and sorting the slice beats scanning everything, and the planner's row estimates (we never ANALYZE) don't know it. The clause also fails loudly if a migration ever drops the index.
func (*Store) AddAdminAudit ¶ added in v0.14.0
func (s *Store) AddAdminAudit(ev model.AdminAudit) error
AddAdminAudit appends one row to the admin trail. Details that won't marshal are dropped rather than failing the write — a row with no details still beats losing the event.
func (*Store) AddConnection ¶
AddConnection records activity from a source IP for a user (upserting the per-IP row) and bumps the user's last_seen.
func (*Store) AddDailyTraffic ¶
AddDailyTraffic adds up/down deltas to a user's row on the local server (node 0) for the given day.
func (*Store) AddDailyTrafficNode ¶ added in v1.0.0
AddDailyTrafficNode adds up/down deltas attributed to a specific node.
func (*Store) AddUsedTraffic ¶ added in v1.0.0
AddUsedTraffic bumps a user's lifetime totals WITHOUT touching last_up/last_down (the raw Xray counter baseline for the local poller). Remote-node traffic ingest uses this: the node already computed the delta, so the panel just accumulates.
func (*Store) AddUserEvent ¶ added in v0.13.0
AddUserEvent appends one audit row. details may be nil (stored as ""); a value that won't marshal is dropped rather than failing the write — an audit row with no details still beats losing the event.
func (*Store) CancelPaymentOrderIfPending ¶ added in v0.11.0
CancelPaymentOrderIfPending cancels an order only while it's still pending, so a stale-order sweep or a provider "canceled" status can't clobber an order a concurrent webhook just marked paid. It reports whether THIS call performed the cancellation — a caller that logs or notifies must not do so for an order someone else already resolved.
func (*Store) Checkpoint ¶
Checkpoint flushes the write-ahead log into the main database file and truncates it, so an on-disk copy (e.g. a backup) captures all committed data. Without this a live backup would copy a near-empty .db with everything still in the uncheckpointed .db-wal (which backups intentionally exclude).
func (*Store) ClaimNodeReport ¶ added in v1.0.0
ClaimNodeReport atomically advances a node's traffic-ingest watermark to reportID, but only if reportID is strictly greater than the stored one. It returns true iff this call won the claim — so two concurrent (or replayed) syncs carrying the same report can't both count their traffic. The caller applies the traffic deltas only when this returns true.
func (*Store) ClaimRegistrationRequest ¶ added in v1.0.0
ClaimRegistrationRequest atomically deletes a request and reports whether THIS call removed it. With MaxOpenConns(1) serialising writes, exactly one of several concurrent approvers/rejecters wins — so a double-click (or two admins) can't create the account twice or send contradictory notifications.
func (*Store) ClearUserTelegramChat ¶ added in v0.3.0
ClearUserTelegramChat unlinks a VPN user's Telegram chat, remembering the chat in tg_prev_chat_id so the same chat can restore this exact account (keeping its plan and consumed trial) by registering again — instead of getting a brand-new trial user. Only overwrites tg_prev_chat_id when actually detaching a chat, so a redundant clear can't wipe a prior pointer.
func (*Store) ClearUserTgLinkCode ¶ added in v0.4.0
ClearUserTgLinkCode burns a user's pending Telegram bind code (after a successful link or on demand).
func (*Store) ConsumeJoinToken ¶ added in v1.0.0
ConsumeJoinToken exchanges a valid, unexpired one-time join token for a fresh permanent bearer token: the join token is cleared (single use) and the permanent token's hash is stored. The raw permanent token is returned exactly once. Returns (nil, "", nil) when the token is unknown or expired.
func (*Store) CountAdmins ¶
CountAdmins returns the number of admin accounts.
func (*Store) CountUsersOnPlan ¶ added in v0.4.0
CountUsersOnPlan returns how many users currently have this plan assigned.
func (*Store) CreateAPIKey ¶ added in v0.10.0
CreateAPIKey mints a new named key, stores only its HMAC hash, and returns the model record with RawKey populated (shown to the operator exactly once).
func (*Store) CreateAdmin ¶
CreateAdmin inserts an admin with the given username, password hash and role. mustChange gates the account on a password change at first login — set for every account created with a password someone else picked.
func (*Store) CreateNode ¶ added in v1.0.0
CreateNode inserts a node with a fresh REALITY identity, a random decoy, and a one-time join token. The returned Node has RawJoinToken populated (shown to the operator exactly once, inside the install command); only its hash is stored.
func (*Store) CreatePaymentOrder ¶ added in v0.4.0
func (*Store) CreateRegistrationRequest ¶ added in v1.0.0
func (s *Store) CreateRegistrationRequest(chatID int64, name string, now int64) (*model.RegistrationRequest, error)
CreateRegistrationRequest inserts a pending request for a chat. chat_id is unique, so a chat with an existing request keeps the first one (ErrRegistrationPending).
func (*Store) CreateSession ¶
CreateSession issues a new opaque session token for an admin and stores only its HMAC hash. The raw token is returned to set as a cookie.
func (*Store) CreateUser ¶
func (s *Store) CreateUser(name, uuid, password, subToken string, dataLimit, expireAt int64, deviceLimit int) (*model.User, error)
CreateUser inserts a user with one credential set (UUID for VLESS, password for Trojan + Hysteria2), a subscription token, and optional quota/expiry.
func (*Store) CreateWebhook ¶ added in v0.10.0
CreateWebhook stores a new endpoint, generating a random HMAC signing secret (encrypted at rest) and returning the record with the plaintext secret.
func (*Store) DeleteAdmin ¶ added in v0.14.0
DeleteAdmin removes an admin. Their sessions go with them: admin_sessions has ON DELETE CASCADE, so a deleted admin's live cookies stop resolving immediately.
func (*Store) DeleteNode ¶ added in v1.0.0
DeleteNode soft-deletes a node: it tombstones the row (keeping the token) and disables it, so the node's next sync is answered Revoked=true and it stops serving — a hard delete would drop the token and leave the node running its last config with live user credentials (it would read the resulting decoy response as "panel unreachable" and keep serving). PurgeDeletedNodes reclaims the row later. Traffic history in traffic_daily is kept (rows carry the numeric node_id).
func (*Store) DeleteRegistrationRequest ¶ added in v1.0.0
DeleteRegistrationRequest removes a request (after approval or rejection).
func (*Store) DeleteSession ¶
DeleteSession revokes a session by its raw token.
func (*Store) DeleteSessionsForAdmin ¶ added in v0.4.0
DeleteSessionsForAdmin revokes every session for an admin.
func (*Store) DeleteSessionsForAdminExcept ¶
DeleteSessionsForAdminExcept revokes every session belonging to an admin except the one identified by keepToken — used after a credential change so a previously stolen cookie can't outlive the change, while the admin doing the change stays logged in.
func (*Store) DeleteTariffPlan ¶ added in v0.4.0
func (*Store) DeleteUsers ¶ added in v0.7.0
DeleteUsers removes many users in one statement, returning how many were deleted.
func (*Store) DeleteWebhook ¶ added in v0.10.0
DeleteWebhook removes an endpoint.
func (*Store) EnabledWebhooksForEvent ¶ added in v0.10.0
EnabledWebhooksForEvent returns the enabled endpoints subscribed to event — the dispatcher's fan-out set. Subscription is filtered in Go (the events CSV is tiny) so "empty = all" stays in one place (model.Webhook.Subscribed).
func (*Store) ExpiredUsersBefore ¶ added in v0.15.0
ExpiredUsersBefore returns users whose expiry date is older than cutoff (unix seconds) — the candidates for the auto-delete sweep.
It keys off expire_at rather than the `status` column on purpose. status is a derived value that a reset or a plan change can flip back to active, and a user who is active again must never be deleted because they were expired last week. expire_at is the fact: a date in the past that nobody extended. Users with no expiry (expire_at = 0) are excluded — there is nothing for them to be past.
func (*Store) GetAdminAuth ¶
GetAdminAuth returns the id, password hash and role for a username.
func (*Store) GetAdminHash ¶
GetAdminHash returns an admin's current password hash by id (for re-authenticating a credential change against the current password).
func (*Store) GetDetachedUserByPrevChat ¶ added in v0.11.0
GetDetachedUserByPrevChat finds an account this chat was previously unlinked from and that is still detached (no active chat), so registration can restore it instead of creating a new trial user. Returns sql.ErrNoRows when none.
func (*Store) GetNode ¶ added in v1.0.0
GetNode returns one live node by id, or (nil, nil) if it doesn't exist or was deleted. (A tombstoned node is invisible to the operator; only the token lookup still finds it, so its next sync can be answered Revoked.)
func (*Store) GetPaymentOrder ¶ added in v0.4.0
func (s *Store) GetPaymentOrder(id int64) (*model.PaymentOrder, error)
func (*Store) GetPaymentOrderByProvider ¶ added in v0.6.0
func (s *Store) GetPaymentOrderByProvider(provider, providerID string) (*model.PaymentOrder, error)
GetPaymentOrderByProvider finds a pending-or-any order by its external id.
func (*Store) GetPaymentProvider ¶ added in v1.0.0
func (s *Store) GetPaymentProvider(key string) (model.PaymentProvider, error)
GetPaymentProvider returns one provider's row. A provider that was never saved comes back as a zero-value row (disabled, empty config) — not an error.
func (*Store) GetRegistrationRequest ¶ added in v1.0.0
func (s *Store) GetRegistrationRequest(id int64) (*model.RegistrationRequest, error)
GetRegistrationRequest returns one request by id.
func (*Store) GetRegistrationRequestByChat ¶ added in v1.0.0
func (s *Store) GetRegistrationRequestByChat(chatID int64) (*model.RegistrationRequest, error)
GetRegistrationRequestByChat returns a chat's pending request, or nil when none.
func (*Store) GetSettings ¶
GetSettings returns the singleton settings row.
func (*Store) GetTariffPlan ¶ added in v0.4.0
func (s *Store) GetTariffPlan(id int64) (*model.TariffPlan, error)
func (*Store) GetUserBySubToken ¶
GetUserBySubToken resolves a subscription token to its user.
func (*Store) GetUserByTelegramChatID ¶ added in v0.3.0
GetUserByTelegramChatID resolves a linked Telegram chat to its VPN user.
func (*Store) GetUserByTgLinkCode ¶ added in v0.4.0
GetUserByTgLinkCode resolves a pending one-time Telegram bind code to its user, rejecting codes that are blank, unknown, or expired.
func (*Store) GetWebhook ¶ added in v0.10.0
GetWebhook returns one endpoint by id.
func (*Store) IssueJoinToken ¶ added in v1.0.0
IssueJoinToken issues a fresh one-time join token WITHOUT revoking the node's current permanent token. Used for SSH re-provisioning: if the install fails the live node keeps working on its old token, and a successful re-join replaces the token via ConsumeJoinToken. The raw join token is returned once.
func (*Store) LatestPendingManualOrder ¶ added in v0.11.0
func (s *Store) LatestPendingManualOrder(userID, planID int64) (*model.PaymentOrder, error)
LatestPendingManualOrder returns the newest still-pending manual order (no provider set) for a user+plan, or sql.ErrNoRows. Lets callers reuse an order instead of piling up duplicates when the user re-taps "Pay".
func (*Store) LatestPendingProviderOrder ¶ added in v0.11.0
func (s *Store) LatestPendingProviderOrder(userID int64) (*model.PaymentOrder, error)
LatestPendingProviderOrder returns the newest still-pending order that went through an automatic provider for a user (or sql.ErrNoRows). Used by the subscription page to show a "payment processing" state after the user returns from the provider until the webhook/poll confirms it.
func (*Store) LatestPendingProviderOrderForPlan ¶ added in v0.11.0
func (s *Store) LatestPendingProviderOrderForPlan(userID, planID int64, provider string) (*model.PaymentOrder, error)
LatestPendingProviderOrderForPlan returns the newest pending order for a user+plan+provider (or sql.ErrNoRows). Lets the pay flow reuse a fresh order instead of creating duplicates on repeated taps.
func (*Store) ListAPIKeys ¶ added in v0.10.0
ListAPIKeys returns all keys (active and revoked), newest first. RawKey is never populated here — it exists only in the CreateAPIKey response.
func (*Store) ListAdminAudit ¶ added in v0.14.0
func (s *Store) ListAdminAudit(f AdminAuditFilter) ([]model.AdminAudit, error)
ListAdminAudit returns the admin trail, newest first, filtered per f.
func (*Store) ListAdmins ¶ added in v0.14.0
ListAdmins returns the roster, owner first, then by creation order.
func (*Store) ListEvents ¶ added in v0.13.0
func (s *Store) ListEvents(f UserEventFilter) ([]model.UserEvent, error)
ListEvents returns the global audit trail, newest first, filtered per f.
func (*Store) ListNodes ¶ added in v1.0.0
ListNodes returns all live (non-tombstoned) nodes, oldest first. RawJoinToken is never populated here.
func (*Store) ListPaymentOrders ¶ added in v0.4.0
func (*Store) ListPaymentProviders ¶ added in v1.0.0
func (s *Store) ListPaymentProviders() (map[string]model.PaymentProvider, error)
ListPaymentProviders returns every saved provider row, keyed by provider key. Providers the operator never touched simply have no row.
func (*Store) ListRegistrationRequests ¶ added in v1.0.0
func (s *Store) ListRegistrationRequests() ([]model.RegistrationRequest, error)
ListRegistrationRequests returns all pending requests, oldest first.
func (*Store) ListTariffPlans ¶ added in v0.4.0
func (s *Store) ListTariffPlans(includeDisabled bool) ([]model.TariffPlan, error)
ListTariffPlans returns plans sorted for display.
func (*Store) ListUserEvents ¶ added in v0.13.0
ListUserEvents returns one user's audit trail, newest first. beforeID > 0 pages backwards from a previously-returned id (the rows are id-ordered, so the id is a stable cursor even as new events land at the top).
func (*Store) ListWebhooks ¶ added in v0.10.0
ListWebhooks returns every configured endpoint, newest first.
func (*Store) LookupAPIKey ¶ added in v0.10.0
LookupAPIKey resolves a raw key to its record, ignoring revoked keys. The lookup is by HMAC hash (a UNIQUE-indexed column) so the DB does the comparison; the raw key never touches storage. last_used_at is bumped on a successful, non-revoked match. Returns (nil, nil) when no active key matches.
func (*Store) LookupNodeByToken ¶ added in v1.0.0
LookupNodeByToken resolves a raw permanent token to its (enabled or disabled) node, by HMAC hash. Returns (nil, nil) when nothing matches. The caller decides what a disabled node means (it is told to stop serving, not deauthenticated).
func (*Store) LookupSession ¶
func (s *Store) LookupSession(token string) (SessionAdmin, bool)
LookupSession resolves a raw session token to its admin. Expired sessions are deleted and treated as invalid.
func (*Store) MarkConfigApplied ¶
MarkConfigApplied bumps the config revision and clears any prior error.
func (*Store) MarkPaymentOrderPaidIfPending ¶ added in v0.8.1
MarkPaymentOrderPaidIfPending atomically transitions an order pending→paid and reports whether THIS call performed the transition. Exactly one of several concurrent confirmers (provider webhook + the poll fallback + a re-delivered webhook) wins the CAS; a caller that gets false must not apply the plan, so a single payment can never extend the user twice.
func (*Store) MarkWebhookResult ¶ added in v0.10.0
MarkWebhookResult records the outcome of a delivery attempt (status is the HTTP code, 0 on a connection-level failure; errStr is "" on success).
func (*Store) NodeNameTaken ¶ added in v1.0.0
NodeNameTaken reports whether a live node other than excludeID already uses the given name (case-insensitively). Node names must be unique because they become Clash proxy names / sing-box outbound tags in multi-node subscriptions, which a client rejects if duplicated.
func (*Store) NodeTrafficTotals ¶ added in v1.0.0
NodeTrafficTotals returns each node's total up+down over the period, keyed by node_id (0 = local server). Used by the Nodes UI.
func (*Store) PaidByProvider ¶ added in v0.10.0
func (s *Store) PaidByProvider() ([]model.ProviderStat, error)
PaidByProvider returns paid-order revenue grouped by provider ("" = manual), highest-earning first. Queried against payment_orders directly (no user join) so revenue from since-deleted users is still counted.
func (*Store) PaidSumSince ¶ added in v0.10.0
PaidSumSince returns the total paid revenue whose paid_at is at or after since.
func (*Store) PendingProviderOrders ¶ added in v0.6.0
func (s *Store) PendingProviderOrders(limit int) ([]model.PaymentOrder, error)
PendingProviderOrders returns pending orders that were started through a payment provider (for the polling fallback). Stale ones (older than maxAge seconds) are skipped — the caller marks them cancelled.
func (*Store) PendingTotals ¶ added in v0.10.0
PendingTotals returns the count and rouble total of orders awaiting payment.
func (*Store) PurgeAdminAudit ¶ added in v0.14.0
PurgeAdminAudit drops trail rows older than the cutoff (unix seconds), returning how many were removed. Batched for the same reason as the user journal's sweep: the pool is a single connection, so an unbounded delete would stall every request behind it.
func (*Store) PurgeConnections ¶ added in v0.13.0
PurgeConnections drops connection rows not seen since the cutoff (unix seconds), returning how many were removed. Batched for the same reason PurgeUserEvents is: the pool is a single connection, so one unbounded DELETE would stall every query behind it. connections has no surrogate key, so this sweeps by rowid.
func (*Store) PurgeDeletedNodes ¶ added in v1.0.0
PurgeDeletedNodes hard-deletes tombstoned nodes whose deletion is older than the cutoff, once a decommissioned node has had ample time to receive its revocation.
func (*Store) PurgeUserEvents ¶ added in v0.13.0
PurgeUserEvents drops audit rows older than the cutoff (unix seconds), returning how many were removed. This is the retention sweep.
func (*Store) RecentConnections ¶
RecentConnections returns a user's source IPs, most recent first.
func (*Store) ReencryptSensitiveFields ¶ added in v0.4.0
ReencryptSensitiveFields migrates legacy plaintext secrets to enc:v1: at-rest blobs.
func (*Store) RegenJoinToken ¶ added in v1.0.0
RegenJoinToken issues a new one-time join token for a node (e.g. to re-install it) and revokes its current permanent token so the old install can't keep syncing. The raw join token is returned once.
func (*Store) ResetDailyStats ¶
ResetDailyStats clears the entire per-day traffic history.
func (*Store) ResetTraffic ¶
ResetTraffic zeroes a user's usage (so a "limited" user works again) and re-baselines the raw counters to the supplied live Xray values, so the next stats poll measures the delta from now. Passing 0/0 would make the poll re-add the user's whole lifetime Xray total straight back onto the freshly-zeroed usage. Does not touch enabled or expiry — an expired user stays expired.
func (*Store) ResetUserQuota ¶
ResetUserQuota zeroes a user's usage and records the reset time (automatic reset scheduler). It re-baselines the raw counters to the supplied live Xray values so the next stats poll measures the delta from now — passing 0/0 would make the poll re-add the whole lifetime total back. enabled/expiry untouched; status is derived on read.
func (*Store) RevertPaymentOrderToPending ¶ added in v0.8.1
RevertPaymentOrderToPending puts a claimed order back to pending so the polling fallback retries — used when applying the plan failed after the paid claim.
func (*Store) RevokeAPIKey ¶ added in v0.10.0
RevokeAPIKey marks a key revoked (idempotent). The row is kept so a revoked key still shows in the UI with its metadata; the hash stays so the same raw key can never be silently re-lived.
func (*Store) SaveNodeReality ¶ added in v1.0.0
SaveNodeReality replaces a node's REALITY keypair/shortId/service (regeneration). The private key is encrypted at rest.
func (*Store) SaveNodeWarp ¶ added in v1.0.0
SaveNodeWarp stores a node's WARP registration (WireGuard identity). The private key is encrypted at rest.
func (*Store) SavePaymentProvider ¶ added in v1.0.0
func (s *Store) SavePaymentProvider(p model.PaymentProvider) error
SavePaymentProvider upserts a provider's row. Config is stored encrypted.
func (*Store) SaveTariffPlan ¶ added in v0.4.0
func (s *Store) SaveTariffPlan(p *model.TariffPlan) error
func (*Store) SetACMEProvider ¶
SetACMEProvider persists the ACME CA selection and (for ZeroSSL) the External Account Binding credentials. An empty provider defaults to "letsencrypt".
func (*Store) SetAPIPath ¶ added in v0.10.0
SetAPIPath persists the external-API URL segment (empty disables the surface).
func (*Store) SetAdminEvents ¶ added in v0.7.0
SetAdminEvents persists the admin notification bitmask (model.AdminEvent* flags).
func (*Store) SetAdminRole ¶ added in v0.14.0
SetAdminRole changes an admin's role.
func (*Store) SetAntiDPI ¶
SetAntiDPI persists the anti-DPI transport-hardening settings (Settings → Подключения): client-config shaping (TLS fragmentation, QUIC block) and the server-inbound knobs (TLS 1.3 floor, REALITY anti-replay window + donor port).
func (*Store) SetBillingSettings ¶ added in v0.4.0
func (*Store) SetConfigError ¶
SetConfigError records the last failed config-apply error.
func (*Store) SetDecoyTemplate ¶
SetDecoyTemplate persists the masquerade (decoy) template slug.
func (*Store) SetFingerprints ¶
SetFingerprints persists the per-connection uTLS fingerprints used in links.
func (*Store) SetGeoRefresh ¶ added in v1.0.0
SetGeoRefresh persists the geo auto-refresh cadence in hours (0 ⇒ never).
func (*Store) SetHysteriaPorts ¶
SetHysteriaPorts persists the Hysteria2 base port, hop range, and hop interval.
func (*Store) SetIPListRefresh ¶ added in v1.1.0
SetIPListRefresh persists the iplist auto-refresh cadence (hours; 0 ⇒ never).
func (*Store) SetLocalBackup ¶ added in v0.13.0
SetLocalBackup persists the local backup schedule (a 5-field cron expression in the operator timezone; empty disables it) and how many archives to retain.
func (*Store) SetMasterLabel ¶ added in v1.0.0
SetMasterLabel persists the panel server's display name used in config labels.
func (*Store) SetNodeACME ¶ added in v1.0.0
SetNodeACME persists a node's own ACME config: host (target), e-mail, provider and ZeroSSL EAB. The HMAC is encrypted at rest.
func (*Store) SetNodeAPIPath ¶ added in v1.0.0
SetNodeAPIPath persists the node sync API URL segment (generated on first node).
func (*Store) SetNodeConnections ¶ added in v1.0.0
func (s *Store) SetNodeConnections(id int64, c *model.NodeConnections) error
SetNodeConnections persists a node's own transport config (JSON blob). A nil config clears it (the node reverts to inheriting the master's transport).
func (*Store) SetNodeDNS ¶ added in v1.0.0
SetNodeDNS persists a node's own DNS override, touching only the xray_dns column (nil ⇒ inherit the panel's). Kept separate from UpdateNode so the DNS tab saves without rewriting routing/egress.
func (*Store) SetNodeEnabled ¶ added in v1.0.0
SetNodeEnabled toggles whether a node serves traffic and appears in links.
func (*Store) SetNodeGeoRefresh ¶ added in v1.0.0
SetNodeGeoRefresh persists a node's own geo auto-refresh cadence (hours; 0 ⇒ never).
func (*Store) SetNodeProtocols ¶ added in v1.0.0
SetNodeProtocols sets a node's protocol on/off flags explicitly (the node's own — no inheritance).
func (*Store) SetNodeRealityDest ¶ added in v1.0.0
SetNodeRealityDest persists a node's own REALITY masquerade donor (empty ⇒ inherit the panel's).
func (*Store) SetNotifiedStatus ¶ added in v0.13.0
SetNotifiedStatus records the status a user was last alerted about, so the transition detector's comparison survives a panel restart (see the 0020 migration).
func (*Store) SetOpera ¶
SetOpera persists the Opera VPN egress settings (enable flag, region, and the local proxy port the opera-proxy helper listens on).
func (*Store) SetPanelName ¶ added in v0.5.0
func (*Store) SetPanelTheme ¶ added in v0.5.0
func (*Store) SetPaymentOrderProvider ¶ added in v0.6.0
SetPaymentOrderProvider links an order to an external provider payment.
func (*Store) SetPaymentOrderStatus ¶ added in v0.4.0
func (*Store) SetPaymentWebhookSecret ¶ added in v0.6.0
SetPaymentWebhookSecret stores the random webhook URL segment.
func (*Store) SetProtocolEnabled ¶
SetProtocolEnabled flips a single protocol's on/off toggle.
func (*Store) SetProtocolNames ¶ added in v0.11.0
SetProtocolNames persists the custom per-connection display names (empty ⇒ the default protocol label is used at render time).
func (*Store) SetProxyMode ¶
SetProxyMode persists the forward-proxy inbound configuration.
func (*Store) SetRealityKeys ¶
SetRealityKeys persists a freshly generated REALITY keypair, shortId, and gRPC service name.
func (*Store) SetRealityPorts ¶
SetRealityPorts persists the REALITY port and destination (SNI/serverName).
func (*Store) SetResetPeriod ¶
SetResetPeriod sets a user's automatic quota-reset period and anchors the cycle at now.
func (*Store) SetRoutingConfig ¶
func (s *Store) SetRoutingConfig(cfg model.RoutingConfig) error
SetRoutingConfig persists the structured routing configuration as JSON.
func (*Store) SetSecretPath ¶
SetSecretPath persists the hidden panel path segment.
func (*Store) SetSetupDone ¶
SetSetupDone marks the first-run wizard as completed.
func (*Store) SetSubSettings ¶
SetSubSettings persists the subscription delivery settings.
func (*Store) SetSubToken ¶ added in v0.3.0
SetSubToken replaces a user's subscription capability token. The old URL stops working immediately; protocol credentials (UUID/password) are unchanged.
func (*Store) SetTLSMode ¶
SetTLSMode persists the TLS mode, domain (host), SNI and ACME e-mail.
func (*Store) SetTelegramBot ¶ added in v0.2.0
SetTelegramBot persists the bot's enable flag, token, and backup schedule (a 5-field cron expression in the operator timezone; empty disables scheduling).
func (*Store) SetTelegramChats ¶ added in v0.2.0
SetTelegramChats replaces the comma-separated set of authorized chat IDs.
func (*Store) SetTelegramLinkCode ¶ added in v0.2.0
SetTelegramLinkCode stores (or clears, with "") the pending one-time linking code.
func (*Store) SetTelegramUserBot ¶ added in v0.3.0
SetTelegramUserBot persists the public user bot's enable flag, token, and the self-registration mode + invite code. tg_user_reg_enabled is kept as a derived mirror (mode != off) for any legacy reader.
func (*Store) SetTimezone ¶
SetTimezone persists the operator's IANA timezone (e.g. "Europe/Moscow").
func (*Store) SetUserAutoDeleteDays ¶ added in v0.15.0
SetUserAutoDeleteDays persists the grace period (in days, past a user's expiry date) after which the retention sweep deletes them. 0 disables deletion.
func (*Store) SetUserEnabled ¶
SetUserEnabled sets the independent manual on/off flag. Expiry/quota are separate and never change this.
func (*Store) SetUserLimits ¶
SetUserLimits sets the data limit (bytes), expiry (unix, 0 = none), and the simultaneous device cap (0 = unlimited). Does not touch the manual enabled flag; status is derived on read.
func (*Store) SetUserName ¶
SetUserName updates a user's display name.
func (*Store) SetUserPlan ¶ added in v0.4.0
func (*Store) SetUserTelegramChat ¶ added in v0.3.0
SetUserTelegramChat links a Telegram chat to a VPN user, first detaching the chat from any other user (one chat ⇒ at most one account).
func (*Store) SetUserTgLinkCode ¶ added in v0.4.0
SetUserTgLinkCode stores (or clears, with "") a user's pending Telegram bind code, stamping the issue time so it can expire.
func (*Store) SetUsersEnabled ¶ added in v0.7.0
SetUsersEnabled flips the manual enabled flag for many users in one statement, returning how many rows changed. Empty ids is a no-op.
func (*Store) SetXrayDNS ¶
SetXrayDNS persists the operator's Xray DNS servers.
func (*Store) StatsByUser ¶
StatsByUser returns each user's traffic total over the period (users with no traffic appear with zeros), busiest first.
func (*Store) StatsSeries ¶
StatsSeries returns per-day totals between from and to (inclusive, YYYY-MM-DD). userID == 0 aggregates across all users.
func (*Store) StatsSeriesNode ¶ added in v1.0.0
StatsSeriesNode returns per-day totals for a single node (nodeID, including 0 for the local server) between from and to. userID 0 aggregates across users.
func (*Store) TouchAdminLogin ¶ added in v0.14.0
TouchAdminLogin records a successful sign-in, so the roster can show who is still actually using their account.
func (*Store) TouchLastSeen ¶
TouchLastSeen updates a user's last activity time (used by the poller too).
func (*Store) UpdateAdminPassword ¶
UpdateAdminPassword replaces an admin's password hash and sets or clears the forced-change gate in the same statement: the two always move together — an admin choosing their own password clears it, an owner assigning one raises it.
func (*Store) UpdateAdminUsername ¶
UpdateAdminUsername changes an admin's login name.
func (*Store) UpdateNode ¶ added in v1.0.0
UpdateNode persists the operator-editable fields. Identity, tokens and reported status are untouched.
func (*Store) UpdateNodeStatus ¶ added in v1.0.0
func (s *Store) UpdateNodeStatus(id int64, st model.NodeStatusUpdate) error
UpdateNodeStatus records what a node reported on a sync: liveness, versions, live cert fingerprint, and the desired-state hash it has applied.
func (*Store) UpdateTraffic ¶
UpdateTraffic adds deltas to lifetime totals and records the raw counters.
func (*Store) UpdateWebhook ¶ added in v0.10.0
UpdateWebhook replaces the mutable fields (url, events, enabled) of an endpoint.
func (*Store) UserIDsOnPlan ¶ added in v0.11.0
UserIDsOnPlan returns the ids of users currently assigned to a plan (used to migrate them when a plan is retired).
func (*Store) UsersWithExpiredPlan ¶ added in v0.4.0
func (*Store) WorkingUsers ¶
WorkingUsers returns users that should be in the proxy config right now: manually enabled AND not expired AND within their data limit AND within their device limit. enabled is an independent manual flag — expiry/quota/devices never change it, they just exclude the user from the config here.