store

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: AGPL-3.0 Imports: 19 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
var ErrAdminNotFound = errors.New("admin not found")

ErrAdminNotFound is returned when an admin id matches no row.

View Source
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.

View Source
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.

View Source
var ErrRegistrationPending = errors.New("registration already pending")

ErrRegistrationPending means the chat already has a pending request.

Functions

func Check added in v0.15.0

func Check(path string) error

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

func InspectDB(path string) (users, admins int, secret string, err error)

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

func PeekTimezone(dbPath string) string

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

type SessionAdmin struct {
	ID                 int64
	Username           string
	Role               string
	MustChangePassword bool
}

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

func Open(path string) (*Store, error)

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

func (s *Store) ActiveDeviceCounts(since int64) (map[int64]int, error)

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

func (s *Store) AddConnection(userID int64, ip string, ts int64) error

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

func (s *Store) AddDailyTraffic(userID int64, day string, up, down int64) error

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

func (s *Store) AddDailyTrafficNode(userID, nodeID int64, day string, up, down int64) error

AddDailyTrafficNode adds up/down deltas attributed to a specific node.

func (*Store) AddUsedTraffic added in v1.0.0

func (s *Store) AddUsedTraffic(userID, up, down int64) error

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

func (s *Store) AddUserEvent(ev model.UserEvent) error

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

func (s *Store) CancelPaymentOrderIfPending(id int64) (bool, error)

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

func (s *Store) Checkpoint() error

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

func (s *Store) ClaimNodeReport(id, reportID int64) (bool, error)

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

func (s *Store) ClaimRegistrationRequest(id int64) (bool, error)

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

func (s *Store) ClearUserTelegramChat(userID int64) error

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

func (s *Store) ClearUserTgLinkCode(userID int64) error

ClearUserTgLinkCode burns a user's pending Telegram bind code (after a successful link or on demand).

func (*Store) Close

func (s *Store) Close() error

Close releases the database.

func (*Store) ConsumeJoinToken added in v1.0.0

func (s *Store) ConsumeJoinToken(raw string) (*model.Node, string, error)

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

func (s *Store) CountAdmins() (int, error)

CountAdmins returns the number of admin accounts.

func (*Store) CountUsersOnPlan added in v0.4.0

func (s *Store) CountUsersOnPlan(planID int64) (int, error)

CountUsersOnPlan returns how many users currently have this plan assigned.

func (*Store) CreateAPIKey added in v0.10.0

func (s *Store) CreateAPIKey(name string) (*model.APIKey, error)

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

func (s *Store) CreateAdmin(username, passwordHash, role string, mustChange bool) (int64, error)

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

func (s *Store) CreateNode(name, host, decoyTemplate string) (*model.Node, error)

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 (s *Store) CreatePaymentOrder(userID, planID int64, amountRub int) (*model.PaymentOrder, error)

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

func (s *Store) CreateSession(adminID int64, ttl time.Duration) (string, error)

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

func (s *Store) CreateWebhook(url string, events []string) (*model.Webhook, error)

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

func (s *Store) DeleteAdmin(id int64) error

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

func (s *Store) DeleteNode(id int64) error

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

func (s *Store) DeleteRegistrationRequest(id int64) error

DeleteRegistrationRequest removes a request (after approval or rejection).

func (*Store) DeleteSession

func (s *Store) DeleteSession(token string) error

DeleteSession revokes a session by its raw token.

func (*Store) DeleteSessionsForAdmin added in v0.4.0

func (s *Store) DeleteSessionsForAdmin(adminID int64) error

DeleteSessionsForAdmin revokes every session for an admin.

func (*Store) DeleteSessionsForAdminExcept

func (s *Store) DeleteSessionsForAdminExcept(adminID int64, keepToken string) error

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 (s *Store) DeleteTariffPlan(id int64) error

func (*Store) DeleteUser

func (s *Store) DeleteUser(id int64) error

DeleteUser removes a user.

func (*Store) DeleteUsers added in v0.7.0

func (s *Store) DeleteUsers(ids []int64) (int64, error)

DeleteUsers removes many users in one statement, returning how many were deleted.

func (*Store) DeleteWebhook added in v0.10.0

func (s *Store) DeleteWebhook(id int64) error

DeleteWebhook removes an endpoint.

func (*Store) EnabledWebhooksForEvent added in v0.10.0

func (s *Store) EnabledWebhooksForEvent(event string) ([]model.Webhook, error)

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

func (s *Store) ExpiredUsersBefore(cutoff int64) ([]model.User, error)

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) GetAdmin added in v0.14.0

func (s *Store) GetAdmin(id int64) (model.Admin, error)

GetAdmin returns one admin by id, or ErrAdminNotFound.

func (*Store) GetAdminAuth

func (s *Store) GetAdminAuth(username string) (id int64, hash, role string, err error)

GetAdminAuth returns the id, password hash and role for a username.

func (*Store) GetAdminHash

func (s *Store) GetAdminHash(id int64) (string, error)

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

func (s *Store) GetDetachedUserByPrevChat(chatID int64) (*model.User, error)

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

func (s *Store) GetNode(id int64) (*model.Node, error)

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

func (s *Store) GetSettings() (*model.Settings, error)

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) GetUser added in v0.3.0

func (s *Store) GetUser(id int64) (*model.User, error)

GetUser returns one user by id.

func (*Store) GetUserBySubToken

func (s *Store) GetUserBySubToken(token string) (*model.User, error)

GetUserBySubToken resolves a subscription token to its user.

func (*Store) GetUserByTelegramChatID added in v0.3.0

func (s *Store) GetUserByTelegramChatID(chatID int64) (*model.User, error)

GetUserByTelegramChatID resolves a linked Telegram chat to its VPN user.

func (*Store) GetUserByTgLinkCode added in v0.4.0

func (s *Store) GetUserByTgLinkCode(code string) (*model.User, error)

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

func (s *Store) GetWebhook(id int64) (*model.Webhook, error)

GetWebhook returns one endpoint by id.

func (*Store) IssueJoinToken added in v1.0.0

func (s *Store) IssueJoinToken(id int64) (string, error)

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

func (s *Store) ListAPIKeys() ([]model.APIKey, error)

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

func (s *Store) ListAdmins() ([]model.Admin, error)

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

func (s *Store) ListNodes() ([]model.Node, error)

ListNodes returns all live (non-tombstoned) nodes, oldest first. RawJoinToken is never populated here.

func (*Store) ListPaymentOrders added in v0.4.0

func (s *Store) ListPaymentOrders(status string, limit int) ([]model.PaymentOrder, error)

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

func (s *Store) ListUserEvents(userID int64, limit int, beforeID int64) ([]model.UserEvent, error)

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) ListUsers

func (s *Store) ListUsers() ([]model.User, error)

ListUsers returns all users, newest first.

func (*Store) ListWebhooks added in v0.10.0

func (s *Store) ListWebhooks() ([]model.Webhook, error)

ListWebhooks returns every configured endpoint, newest first.

func (*Store) LookupAPIKey added in v0.10.0

func (s *Store) LookupAPIKey(raw string) (*model.APIKey, error)

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

func (s *Store) LookupNodeByToken(raw string) (*model.Node, error)

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

func (s *Store) MarkConfigApplied() error

MarkConfigApplied bumps the config revision and clears any prior error.

func (*Store) MarkPaymentOrderPaidIfPending added in v0.8.1

func (s *Store) MarkPaymentOrderPaidIfPending(id, paidAt int64) (bool, error)

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

func (s *Store) MarkWebhookResult(id int64, status int, errStr string) error

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

func (s *Store) NodeNameTaken(name string, excludeID int64) (bool, error)

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

func (s *Store) NodeTrafficTotals(from, to string) (map[int64][2]int64, error)

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

func (s *Store) PaidSumSince(since int64) (int, error)

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

func (s *Store) PendingTotals() (count, sum int, err error)

PendingTotals returns the count and rouble total of orders awaiting payment.

func (*Store) PurgeAdminAudit added in v0.14.0

func (s *Store) PurgeAdminAudit(before int64) (int64, error)

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

func (s *Store) PurgeConnections(before int64) (int64, error)

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

func (s *Store) PurgeDeletedNodes(before int64) (int64, error)

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

func (s *Store) PurgeUserEvents(before int64) (int64, error)

PurgeUserEvents drops audit rows older than the cutoff (unix seconds), returning how many were removed. This is the retention sweep.

func (*Store) RecentConnections

func (s *Store) RecentConnections(userID int64, limit int) ([]model.Connection, error)

RecentConnections returns a user's source IPs, most recent first.

func (*Store) ReencryptSensitiveFields added in v0.4.0

func (s *Store) ReencryptSensitiveFields() error

ReencryptSensitiveFields migrates legacy plaintext secrets to enc:v1: at-rest blobs.

func (*Store) RegenJoinToken added in v1.0.0

func (s *Store) RegenJoinToken(id int64) (string, error)

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

func (s *Store) ResetDailyStats() error

ResetDailyStats clears the entire per-day traffic history.

func (*Store) ResetTraffic

func (s *Store) ResetTraffic(id, lastUp, lastDown int64) error

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

func (s *Store) ResetUserQuota(id, now, lastUp, lastDown int64) error

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

func (s *Store) RevertPaymentOrderToPending(id int64) error

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

func (s *Store) RevokeAPIKey(id int64) error

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

func (s *Store) SaveNodeReality(id int64, priv, pub, shortID, service string) error

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

func (s *Store) SaveNodeWarp(id int64, priv, pub, endpoint, v4, v6, reserved string) error

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

func (s *Store) SetACMEProvider(provider, eabKID, eabHMAC string) error

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

func (s *Store) SetAPIPath(p string) error

SetAPIPath persists the external-API URL segment (empty disables the surface).

func (*Store) SetAdminEvents added in v0.7.0

func (s *Store) SetAdminEvents(mask int64) error

SetAdminEvents persists the admin notification bitmask (model.AdminEvent* flags).

func (*Store) SetAdminRole added in v0.14.0

func (s *Store) SetAdminRole(id int64, role string) error

SetAdminRole changes an admin's role.

func (*Store) SetAntiDPI

func (s *Store) SetAntiDPI(tlsFragment, tlsMin13, blockQUIC bool, realityMaxTimeDiff int) error

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 (s *Store) SetBillingSettings(st *model.Settings) error

func (*Store) SetConfigError

func (s *Store) SetConfigError(msg string) error

SetConfigError records the last failed config-apply error.

func (*Store) SetDecoyTemplate

func (s *Store) SetDecoyTemplate(name string) error

SetDecoyTemplate persists the masquerade (decoy) template slug.

func (*Store) SetFingerprints

func (s *Store) SetFingerprints(vless, trojan, reality string) error

SetFingerprints persists the per-connection uTLS fingerprints used in links.

func (*Store) SetGeoRefresh added in v1.0.0

func (s *Store) SetGeoRefresh(hours int) error

SetGeoRefresh persists the geo auto-refresh cadence in hours (0 ⇒ never).

func (*Store) SetHysteriaPorts

func (s *Store) SetHysteriaPorts(port, hopStart, hopEnd int, interval string) error

SetHysteriaPorts persists the Hysteria2 base port, hop range, and hop interval.

func (*Store) SetIPListRefresh added in v1.1.0

func (s *Store) SetIPListRefresh(hours int) error

SetIPListRefresh persists the iplist auto-refresh cadence (hours; 0 ⇒ never).

func (*Store) SetLocalBackup added in v0.13.0

func (s *Store) SetLocalBackup(cron string, keep int) error

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

func (s *Store) SetMasterLabel(label string) error

SetMasterLabel persists the panel server's display name used in config labels.

func (*Store) SetNodeACME added in v1.0.0

func (s *Store) SetNodeACME(id int64, host, email, provider, eabKID, eabHMAC string) error

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

func (s *Store) SetNodeAPIPath(p string) error

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

func (s *Store) SetNodeDNS(id int64, dns *string) error

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

func (s *Store) SetNodeEnabled(id int64, enabled bool) error

SetNodeEnabled toggles whether a node serves traffic and appears in links.

func (*Store) SetNodeGeoRefresh added in v1.0.0

func (s *Store) SetNodeGeoRefresh(id int64, hours int) error

SetNodeGeoRefresh persists a node's own geo auto-refresh cadence (hours; 0 ⇒ never).

func (*Store) SetNodeProtocols added in v1.0.0

func (s *Store) SetNodeProtocols(id int64, vless, trojan, hysteria, reality bool) error

SetNodeProtocols sets a node's protocol on/off flags explicitly (the node's own — no inheritance).

func (*Store) SetNodeRealityDest added in v1.0.0

func (s *Store) SetNodeRealityDest(id int64, dest string) error

SetNodeRealityDest persists a node's own REALITY masquerade donor (empty ⇒ inherit the panel's).

func (*Store) SetNotifiedStatus added in v0.13.0

func (s *Store) SetNotifiedStatus(id int64, status string) error

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

func (s *Store) SetOpera(enabled bool, country string, port int) error

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 (s *Store) SetPanelName(name string) error

func (*Store) SetPanelTheme added in v0.5.0

func (s *Store) SetPanelTheme(themeJSON string) error

func (*Store) SetPaymentOrderProvider added in v0.6.0

func (s *Store) SetPaymentOrderProvider(id int64, provider, providerID, payURL string) error

SetPaymentOrderProvider links an order to an external provider payment.

func (*Store) SetPaymentOrderStatus added in v0.4.0

func (s *Store) SetPaymentOrderStatus(id int64, status string, paidAt int64) error

func (*Store) SetPaymentWebhookSecret added in v0.6.0

func (s *Store) SetPaymentWebhookSecret(secret string) error

SetPaymentWebhookSecret stores the random webhook URL segment.

func (*Store) SetProtocolEnabled

func (s *Store) SetProtocolEnabled(name string, enabled bool) error

SetProtocolEnabled flips a single protocol's on/off toggle.

func (*Store) SetProtocolNames added in v0.11.0

func (s *Store) SetProtocolNames(vless, reality, trojan, hysteria string) error

SetProtocolNames persists the custom per-connection display names (empty ⇒ the default protocol label is used at render time).

func (*Store) SetProxyMode

func (s *Store) SetProxyMode(enabled bool, typ string, port int, user, pass string) error

SetProxyMode persists the forward-proxy inbound configuration.

func (*Store) SetRealityKeys

func (s *Store) SetRealityKeys(priv, pub, shortID, serviceName string) error

SetRealityKeys persists a freshly generated REALITY keypair, shortId, and gRPC service name.

func (*Store) SetRealityPorts

func (s *Store) SetRealityPorts(port int, dest string) error

SetRealityPorts persists the REALITY port and destination (SNI/serverName).

func (*Store) SetResetPeriod

func (s *Store) SetResetPeriod(id int64, period string, now int64) error

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

func (s *Store) SetSecretPath(p string) error

SetSecretPath persists the hidden panel path segment.

func (*Store) SetSetupDone

func (s *Store) SetSetupDone(done bool) error

SetSetupDone marks the first-run wizard as completed.

func (*Store) SetSubSettings

func (s *Store) SetSubSettings(st *model.Settings) error

SetSubSettings persists the subscription delivery settings.

func (*Store) SetSubToken added in v0.3.0

func (s *Store) SetSubToken(id int64, token string) error

SetSubToken replaces a user's subscription capability token. The old URL stops working immediately; protocol credentials (UUID/password) are unchanged.

func (*Store) SetTLS

func (s *Store) SetTLS(host, sni, mode, certPath, keyPath string) error

SetTLS persists host/SNI/cert configuration (used on first boot).

func (*Store) SetTLSMode

func (s *Store) SetTLSMode(mode, host, sni, acmeEmail string) error

SetTLSMode persists the TLS mode, domain (host), SNI and ACME e-mail.

func (*Store) SetTelegramBot added in v0.2.0

func (s *Store) SetTelegramBot(enabled bool, token, cron string) error

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

func (s *Store) SetTelegramChats(csv string) error

SetTelegramChats replaces the comma-separated set of authorized chat IDs.

func (*Store) SetTelegramLinkCode added in v0.2.0

func (s *Store) SetTelegramLinkCode(code string) error

SetTelegramLinkCode stores (or clears, with "") the pending one-time linking code.

func (*Store) SetTelegramUserBot added in v0.3.0

func (s *Store) SetTelegramUserBot(enabled bool, token, regMode, regCode string) error

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

func (s *Store) SetTimezone(tz string) error

SetTimezone persists the operator's IANA timezone (e.g. "Europe/Moscow").

func (*Store) SetUserAutoDeleteDays added in v0.15.0

func (s *Store) SetUserAutoDeleteDays(days int) error

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

func (s *Store) SetUserEnabled(id int64, enabled bool) error

SetUserEnabled sets the independent manual on/off flag. Expiry/quota are separate and never change this.

func (*Store) SetUserLimits

func (s *Store) SetUserLimits(id, dataLimit, expireAt int64, deviceLimit int) error

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

func (s *Store) SetUserName(id int64, name string) error

SetUserName updates a user's display name.

func (*Store) SetUserPlan added in v0.4.0

func (s *Store) SetUserPlan(userID, planID int64, trialUsed bool) error

func (*Store) SetUserTelegramChat added in v0.3.0

func (s *Store) SetUserTelegramChat(userID, chatID int64) error

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

func (s *Store) SetUserTgLinkCode(userID int64, code string) error

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

func (s *Store) SetUsersEnabled(ids []int64, enabled bool) (int64, error)

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) SetWSPath

func (s *Store) SetWSPath(p string) error

SetWSPath persists the Trojan-WS path.

func (*Store) SetWarp

func (s *Store) SetWarp(st *model.Settings) error

SetWarp persists the WARP enabled flag plus the provisioned account fields.

func (*Store) SetXrayDNS

func (s *Store) SetXrayDNS(dns string) error

SetXrayDNS persists the operator's Xray DNS servers.

func (*Store) StatsByUser

func (s *Store) StatsByUser(from, to string) ([]model.UserTotal, error)

StatsByUser returns each user's traffic total over the period (users with no traffic appear with zeros), busiest first.

func (*Store) StatsSeries

func (s *Store) StatsSeries(userID int64, from, to string) ([]model.DailyPoint, error)

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

func (s *Store) StatsSeriesNode(userID, nodeID int64, from, to string) ([]model.DailyPoint, error)

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

func (s *Store) TouchAdminLogin(id int64) error

TouchAdminLogin records a successful sign-in, so the roster can show who is still actually using their account.

func (*Store) TouchLastSeen

func (s *Store) TouchLastSeen(userID, ts int64) error

TouchLastSeen updates a user's last activity time (used by the poller too).

func (*Store) UpdateAdminPassword

func (s *Store) UpdateAdminPassword(id int64, passwordHash string, mustChange bool) error

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

func (s *Store) UpdateAdminUsername(id int64, username string) error

UpdateAdminUsername changes an admin's login name.

func (*Store) UpdateNode added in v1.0.0

func (s *Store) UpdateNode(id int64, e NodeEdit) error

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

func (s *Store) UpdateTraffic(id, addUp, addDown, lastUp, lastDown int64) error

UpdateTraffic adds deltas to lifetime totals and records the raw counters.

func (*Store) UpdateWebhook added in v0.10.0

func (s *Store) UpdateWebhook(id int64, url string, events []string, enabled bool) error

UpdateWebhook replaces the mutable fields (url, events, enabled) of an endpoint.

func (*Store) UserIDsOnPlan added in v0.11.0

func (s *Store) UserIDsOnPlan(planID int64) ([]int64, error)

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 (s *Store) UsersWithExpiredPlan(now int64) ([]model.User, error)

func (*Store) WorkingUsers

func (s *Store) WorkingUsers(now int64) ([]model.User, error)

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.

type UserEventFilter added in v0.13.0

type UserEventFilter struct {
	Action    string
	ActorKind string
	UserID    int64
	BeforeID  int64 // page backwards from this id
	Limit     int
}

UserEventFilter narrows the global journal. A zero field means "no filter".

Jump to

Keyboard shortcuts

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