domain

package
v0.13.0 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 2 Imported by: 0

Documentation

Overview

Package domain holds the entities shared by every layer of the panel.

Index

Constants

This section is empty.

Variables

AllEventKinds is the list a channel picks its subscriptions from, and the list the UI renders. Keeping it beside the constants means a new event kind is subscribable the moment it exists.

Functions

This section is empty.

Types

type APIToken

type APIToken struct {
	UUID       string     `json:"uuid"`
	Name       string     `json:"name"`
	TokenHash  string     `json:"-"`
	Preview    string     `json:"tokenPreview"`
	CreatedBy  string     `json:"createdBy"`
	LastUsedAt *time.Time `json:"lastUsedAt"`
	ExpiresAt  *time.Time `json:"expiresAt"`
	CreatedAt  time.Time  `json:"createdAt"`
}

APIToken authenticates an external integration against the panel API.

type Admin

type Admin struct {
	UUID         string     `json:"uuid"`
	Username     string     `json:"username"`
	PasswordHash string     `json:"-"`
	Role         AdminRole  `json:"role"`
	IsDisabled   bool       `json:"isDisabled"`
	LastLoginAt  *time.Time `json:"lastLoginAt"`
	CreatedAt    time.Time  `json:"createdAt"`
	UpdatedAt    time.Time  `json:"updatedAt"`

	// Two-factor. The secret and the recovery digests never leave the panel —
	// TOTPEnabled is all the UI needs, and anything more would put a working
	// second factor into every response that carries an administrator.
	TOTPSecret         string     `json:"-"`
	TOTPEnabled        bool       `json:"totpEnabled"`
	TOTPConfirmedAt    *time.Time `json:"totpConfirmedAt"`
	TOTPLastStep       int64      `json:"-"`
	RecoveryCodeHashes []string   `json:"-"`

	// A count, never the codes: an operator needs to know they are running low,
	// and nothing more can be given back once they have been shown.
	RecoveryCodesLeft int `json:"recoveryCodesLeft"`
}

type AdminRole

type AdminRole string
const (
	RoleOwner  AdminRole = "OWNER"
	RoleAdmin  AdminRole = "ADMIN"
	RoleViewer AdminRole = "VIEWER"
)

func (AdminRole) CanWrite

func (r AdminRole) CanWrite() bool

CanWrite reports whether the role may mutate panel state.

func (AdminRole) Valid

func (r AdminRole) Valid() bool

type Announcement added in v0.2.0

type Announcement struct {
	UUID      string            `json:"uuid"`
	Title     string            `json:"title"`
	Body      string            `json:"body"`
	Level     AnnouncementLevel `json:"level"`
	IsEnabled bool              `json:"isEnabled"`
	StartsAt  *time.Time        `json:"startsAt"`
	EndsAt    *time.Time        `json:"endsAt"`
	CreatedAt time.Time         `json:"createdAt"`
	UpdatedAt time.Time         `json:"updatedAt"`
}

Announcement is a notice shown to subscribers on their subscription page.

func (Announcement) Live added in v0.2.0

func (a Announcement) Live(now time.Time) bool

Live reports whether the announcement should be shown at the given moment. A window with no bounds is always live, which is the common case.

type AnnouncementLevel added in v0.2.0

type AnnouncementLevel string

AnnouncementLevel tints the notice on the subscription page.

const (
	AnnouncementInfo    AnnouncementLevel = "INFO"
	AnnouncementWarning AnnouncementLevel = "WARNING"
	AnnouncementDanger  AnnouncementLevel = "DANGER"
)

func (AnnouncementLevel) Valid added in v0.2.0

func (l AnnouncementLevel) Valid() bool

type BillingCycle

type BillingCycle string

BillingCycle is how often a node has to be paid for.

const (
	BillingNone      BillingCycle = "NONE"
	BillingMonthly   BillingCycle = "MONTHLY"
	BillingQuarterly BillingCycle = "QUARTERLY"
	BillingYearly    BillingCycle = "YEARLY"
)

func (BillingCycle) Advance

func (c BillingCycle) Advance(from time.Time) time.Time

Advance returns the next payment date one cycle after the given one.

func (BillingCycle) MonthlyCost

func (c BillingCycle) MonthlyCost(amount float64) float64

MonthlyCost normalises a billing cycle onto a monthly figure so nodes on different cycles can be summed.

func (BillingCycle) Valid

func (c BillingCycle) Valid() bool

type ChannelKind added in v0.2.0

type ChannelKind string

ChannelKind is how a notification leaves the panel.

const (
	ChannelWebhook  ChannelKind = "WEBHOOK"
	ChannelTelegram ChannelKind = "TELEGRAM"
)

func (ChannelKind) Valid added in v0.2.0

func (k ChannelKind) Valid() bool

type ConfigProfile

type ConfigProfile struct {
	UUID string `json:"uuid"`
	Name string `json:"name"`
	// Kind decides which engine validates and renders this document. Empty
	// reads as xray, so every profile written before this existed is correct.
	Kind      ProfileKind     `json:"kind"`
	Config    json.RawMessage `json:"config"`
	CreatedAt time.Time       `json:"createdAt"`
	UpdatedAt time.Time       `json:"updatedAt"`

	Inbounds []ConfigProfileInbound `json:"inbounds,omitempty"`
	NodeIDs  []string               `json:"nodeUuids,omitempty"`
}

type ConfigProfileInbound

type ConfigProfileInbound struct {
	UUID            string `json:"uuid"`
	ConfigProfileID string `json:"profileUuid"`
	ProfileName     string `json:"profileName,omitempty"`
	Tag             string `json:"tag"`
	Type            string `json:"type"`
	Network         string `json:"network"`
	Security        string `json:"security"`
	Port            int    `json:"port"`
}

ConfigProfileInbound mirrors one inbound of the profile document. It is extracted on save so squads and hosts can reference inbounds by identity instead of by fragile string tags.

type Device

type Device struct {
	UserID string `json:"userUuid"`
	// Username is filled by the cross-user inspector so the list is readable
	// without a lookup per row.
	Username  string    `json:"username,omitempty"`
	HWID      string    `json:"hwid"`
	UserAgent string    `json:"userAgent"`
	Platform  string    `json:"platform"`
	FirstSeen time.Time `json:"firstSeen"`
	LastSeen  time.Time `json:"lastSeen"`
}

Device is one client seen on a user's subscription.

type Event

type Event struct {
	ID        int64           `json:"id"`
	Kind      EventKind       `json:"kind"`
	Actor     string          `json:"actor"`
	Subject   string          `json:"subject"`
	Message   string          `json:"message"`
	Meta      json.RawMessage `json:"meta,omitempty"`
	CreatedAt time.Time       `json:"createdAt"`
}

type EventKind

type EventKind string

EventKind classifies audit log records.

const (
	EventNodeConnected    EventKind = "NODE_CONNECTED"
	EventNodeDisconnected EventKind = "NODE_DISCONNECTED"
	EventNodeConfigPushed EventKind = "NODE_CONFIG_PUSHED"
	EventNodeError        EventKind = "NODE_ERROR"
	EventUserCreated      EventKind = "USER_CREATED"
	EventUserUpdated      EventKind = "USER_UPDATED"
	EventUserDeleted      EventKind = "USER_DELETED"
	EventUserLimited      EventKind = "USER_LIMITED"
	EventUserExpired      EventKind = "USER_EXPIRED"
	// Sent before the fact rather than after, so an operator or a billing
	// system can act while the subscriber still has service.
	EventUserExpiringSoon EventKind = "USER_EXPIRING_SOON"
	EventUserQuotaWarning EventKind = "USER_QUOTA_WARNING"
	EventAdminLogin       EventKind = "ADMIN_LOGIN"
	EventAdminLoginFailed EventKind = "ADMIN_LOGIN_FAILED"
	// A second factor being turned on or off, and a sign-in locked for
	// repeated failures — both are worth waking someone up for.
	EventAdminSecurity   EventKind = "ADMIN_SECURITY"
	EventAdminLocked     EventKind = "ADMIN_LOCKED"
	EventProfileUpdated  EventKind = "PROFILE_UPDATED"
	EventNodePaymentDue  EventKind = "NODE_PAYMENT_DUE"
	EventDeviceBlocked   EventKind = "DEVICE_LIMIT_REACHED"
	EventSettingsUpdated EventKind = "SETTINGS_UPDATED"
)

func (EventKind) Valid added in v0.2.0

func (k EventKind) Valid() bool

type Host

type Host struct {
	UUID        string `json:"uuid"`
	InboundID   string `json:"inboundUuid"`
	InboundTag  string `json:"inboundTag,omitempty"`
	InboundType string `json:"inboundType,omitempty"`
	ProfileID   string `json:"configProfileUuid,omitempty"`
	ProfileName string `json:"configProfileName,omitempty"`

	Remark  string `json:"remark"`
	Address string `json:"address"`
	Port    int    `json:"port"`

	Path          string   `json:"path"`
	SNI           string   `json:"sni"`
	HostHeader    string   `json:"hostHeader"`
	ALPN          string   `json:"alpn"`
	Fingerprint   string   `json:"fingerprint"`
	PublicKey     string   `json:"publicKey"`
	ShortID       string   `json:"shortId"`
	SpiderX       string   `json:"spiderX"`
	Flow          string   `json:"flow"`
	Security      string   `json:"security"`
	AllowInsecure bool     `json:"allowInsecure"`
	Tags          []string `json:"tags"`

	IsDisabled   bool `json:"isDisabled"`
	ViewPosition int  `json:"viewPosition"`

	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
}

Host is a client-facing entry point advertised in subscriptions. It points at one inbound of a config profile but may carry its own address/SNI/etc so a single inbound can be published behind many domains or CDNs.

type Node

type Node struct {
	UUID        string `json:"uuid"`
	Name        string `json:"name"`
	Address     string `json:"address"`
	CountryCode string `json:"countryCode"`
	Description string `json:"description"`

	TokenHash string `json:"-"`
	// TokenPreview keeps the last characters of the enrolment token so the UI can
	// tell two nodes apart without ever exposing the secret again.
	TokenPreview string `json:"tokenPreview"`

	IsDisabled bool       `json:"isDisabled"`
	Health     NodeHealth `json:"health"`

	ConfigProfileID    *string              `json:"configProfileUuid"`
	ConfigProfileName  string               `json:"configProfileName,omitempty"`
	ActiveInboundTags  []string             `json:"activeInboundTags"`
	ConsumptionMultip  float64              `json:"consumptionMultiplier"`
	TrafficLimitBytes  int64                `json:"trafficLimitBytes"`
	TrafficUsedBytes   int64                `json:"trafficUsedBytes"`
	TrafficResetPolicy TrafficResetStrategy `json:"trafficResetStrategy"`
	NotifyPercent      int                  `json:"notifyPercent"`
	LastTrafficResetAt *time.Time           `json:"lastTrafficResetAt"`

	AgentVersion  string     `json:"agentVersion"`
	XrayVersion   string     `json:"xrayVersion"`
	XrayRunning   bool       `json:"xrayRunning"`
	XrayStartedAt *time.Time `json:"xrayStartedAt"`
	ConfigHash    string     `json:"configHash"`

	Hostname      string  `json:"hostname"`
	OS            string  `json:"os"`
	Arch          string  `json:"arch"`
	Kernel        string  `json:"kernel"`
	CPUCount      int     `json:"cpuCount"`
	CPUModel      string  `json:"cpuModel"`
	CPUUsage      float64 `json:"cpuUsagePercent"`
	TotalRAMBytes int64   `json:"totalRamBytes"`
	UsedRAMBytes  int64   `json:"usedRamBytes"`
	LoadAvg1      float64 `json:"loadAvg1"`
	OnlineUsers   int     `json:"onlineUsers"`

	// Infrastructure billing: what this node costs and who it is rented from.
	Provider      string       `json:"provider"`
	ProviderURL   string       `json:"providerUrl"`
	CostAmount    float64      `json:"costAmount"`
	CostCurrency  string       `json:"costCurrency"`
	BillingCycle  BillingCycle `json:"billingCycle"`
	NextPaymentAt *time.Time   `json:"nextPaymentAt"`
	BillingNotes  string       `json:"billingNotes"`
	Tags          []string     `json:"tags"`

	StatusMessage   string     `json:"statusMessage"`
	LastStatusAt    *time.Time `json:"lastStatusChangeAt"`
	LastConnectedAt *time.Time `json:"lastConnectedAt"`
	ViewPosition    int        `json:"viewPosition"`

	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
}

Node is a server running the agent and an xray-core process.

type NodeHealth

type NodeHealth string
const (
	NodeHealthUnknown      NodeHealth = "UNKNOWN"
	NodeHealthConnecting   NodeHealth = "CONNECTING"
	NodeHealthOnline       NodeHealth = "ONLINE"
	NodeHealthDegraded     NodeHealth = "DEGRADED"
	NodeHealthOffline      NodeHealth = "OFFLINE"
	NodeHealthDisabled     NodeHealth = "DISABLED"
	NodeHealthTrafficLimit NodeHealth = "TRAFFIC_LIMITED"
)

type NodeMetric added in v0.2.0

type NodeMetric struct {
	At          time.Time `json:"at"`
	CPUPercent  float64   `json:"cpuPercent"`
	UsedRAM     int64     `json:"usedRamBytes"`
	TotalRAM    int64     `json:"totalRamBytes"`
	LoadAvg1    float64   `json:"loadAvg1"`
	OnlineUsers int       `json:"onlineUsers"`
}

NodeMetric is one heartbeat sample, kept so load can be read as a trend.

type NodeUsageSample

type NodeUsageSample struct {
	NodeID   string    `json:"nodeUuid"`
	NodeName string    `json:"nodeName,omitempty"`
	At       time.Time `json:"at"`
	Bytes    int64     `json:"bytes"`
}

NodeUsageSample is one bucketed traffic measurement for a node.

type NotificationChannel added in v0.2.0

type NotificationChannel struct {
	UUID   string          `json:"uuid"`
	Name   string          `json:"name"`
	Kind   ChannelKind     `json:"kind"`
	Config json.RawMessage `json:"config"`
	// Empty subscribes to everything, which is what most deployments want and
	// avoids a channel that silently delivers nothing.
	Events    []EventKind `json:"events"`
	IsEnabled bool        `json:"isEnabled"`

	LastOK     *bool      `json:"lastOk"`
	LastDetail string     `json:"lastDetail"`
	LastSentAt *time.Time `json:"lastSentAt"`

	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
}

NotificationChannel is one destination events are delivered to. Config holds the transport-specific fields — a URL and signing secret, or a bot token and chat id — so adding a transport does not mean adding columns.

func (NotificationChannel) Wants added in v0.2.0

func (c NotificationChannel) Wants(kind EventKind) bool

Wants reports whether this channel should receive an event.

type NotificationDelivery added in v0.2.0

type NotificationDelivery struct {
	ID         int64     `json:"id"`
	ChannelID  string    `json:"channelUuid"`
	EventKind  EventKind `json:"eventKind"`
	OK         bool      `json:"ok"`
	Detail     string    `json:"detail"`
	Attempts   int       `json:"attempts"`
	DurationMS int       `json:"durationMs"`
	CreatedAt  time.Time `json:"createdAt"`
}

NotificationDelivery is one attempt to reach a channel, kept so "the webhook never arrived" is an answerable question.

type ProfileKind added in v0.11.0

type ProfileKind string

ConfigProfile is a reusable xray-core configuration document. Nodes are bound to exactly one profile and serve a subset of its inbounds. ProfileKind names the engine a profile's document is written for.

const (
	ProfileXray      ProfileKind = "xray"
	ProfileHysteria2 ProfileKind = "hysteria2"
	ProfileSingBox   ProfileKind = "singbox"
)

func (ProfileKind) Valid added in v0.11.0

func (k ProfileKind) Valid() bool

type Settings

type Settings struct {
	BrandName         string `json:"brandName"`
	BrandTagline      string `json:"brandTagline"`
	BrandAccent       string `json:"brandAccent"`
	SubscriptionTitle string `json:"subscriptionTitle"`
	SupportURL        string `json:"supportUrl"`
	Currency          string `json:"currency"`
	// SubscriptionFormat is what an unrecognised client is served. Empty keeps
	// the base64 list, which every client can read. Clients that announce
	// themselves — Clash, sing-box — still get their own format regardless.
	SubscriptionFormat string `json:"subscriptionFormat"`
	// What the subscriber's page offers. Both default to on, so an existing
	// deployment looks exactly as it did.
	SubPageShowLinks   bool `json:"subPageShowLinks"`
	SubPageShowFormats bool `json:"subPageShowFormats"`
	// Custom documents for the two formats that are whole config files rather
	// than a list of links. Empty means the built-in template.
	ClashTemplate   string `json:"clashTemplate"`
	SingBoxTemplate string `json:"singboxTemplate"`
	// RequireTOTP refuses a session to any administrator without a second
	// factor, sending them to enrolment instead.
	RequireTOTP bool `json:"requireTotp"`
	// How early to warn. Zero disables either warning, which is what every
	// install did before these existed.
	WarnExpiryDays   int       `json:"warnExpiryDays"`
	WarnQuotaPercent int       `json:"warnQuotaPercent"`
	UpdatedAt        time.Time `json:"updatedAt"`
}

Settings holds the panel-wide knobs an operator can change at runtime, including white-label branding for corporate deployments.

type Squad

type Squad struct {
	UUID      string    `json:"uuid"`
	Name      string    `json:"name"`
	Info      string    `json:"info"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`

	InboundIDs  []string               `json:"inboundUuids"`
	Inbounds    []ConfigProfileInbound `json:"inbounds,omitempty"`
	MemberCount int                    `json:"membersCount"`
}

Squad groups inbounds together; users are attached to squads rather than to individual inbounds, which keeps bulk membership changes cheap.

type TrafficResetStrategy

type TrafficResetStrategy string
const (
	ResetNever TrafficResetStrategy = "NO_RESET"
	ResetDay   TrafficResetStrategy = "DAY"
	ResetWeek  TrafficResetStrategy = "WEEK"
	ResetMonth TrafficResetStrategy = "MONTH"
)

func (TrafficResetStrategy) Valid

func (s TrafficResetStrategy) Valid() bool

type User

type User struct {
	UUID      string `json:"uuid"`
	ShortUUID string `json:"shortUuid"`
	Username  string `json:"username"`

	SubscriptionUUID string `json:"subscriptionUuid"`
	VlessUUID        string `json:"vlessUuid"`
	TrojanPassword   string `json:"trojanPassword"`
	SSPassword       string `json:"ssPassword"`

	Status               UserStatus           `json:"status"`
	TrafficLimitBytes    int64                `json:"trafficLimitBytes"`
	UsedTrafficBytes     int64                `json:"usedTrafficBytes"`
	LifetimeTrafficBytes int64                `json:"lifetimeUsedTrafficBytes"`
	TrafficResetPolicy   TrafficResetStrategy `json:"trafficLimitStrategy"`
	LastTrafficResetAt   *time.Time           `json:"lastTrafficResetAt"`

	ExpireAt *time.Time `json:"expireAt"`
	OnlineAt *time.Time `json:"onlineAt"`

	Description string `json:"description"`
	Tag         string `json:"tag"`
	Email       string `json:"email"`
	TelegramID  *int64 `json:"telegramId"`

	HWIDDeviceLimit int        `json:"hwidDeviceLimit"`
	SubLastUA       string     `json:"subLastUserAgent"`
	SubLastOpenedAt *time.Time `json:"subLastOpenedAt"`
	SubRevokedAt    *time.Time `json:"subRevokedAt"`

	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`

	SquadIDs []string `json:"squadUuids"`
	Squads   []Squad  `json:"squads,omitempty"`

	// WireGuard needs a key pair per subscriber rather than a shared secret.
	// The private half is stored because the panel is what hands out the
	// .conf; only the public half ever reaches a node.
	WGPrivateKey string `json:"-"`
	WGPublicKey  string `json:"-"`
	// A stable number the tunnel address is derived from, so an address never
	// moves under a subscriber who already imported their configuration.
	WGIndex int64 `json:"-"`
}

User is a VPN client.

func (*User) IsUsable

func (u *User) IsUsable() bool

IsUsable reports whether the user should currently be provisioned to nodes.

func (*User) XrayEmail

func (u *User) XrayEmail() string

XrayEmail is the identity xray-core reports usage under. Keeping the uuid in front guarantees uniqueness even when usernames are recycled.

type UserStatus

type UserStatus string
const (
	UserActive   UserStatus = "ACTIVE"
	UserDisabled UserStatus = "DISABLED"
	UserLimited  UserStatus = "LIMITED"
	UserExpired  UserStatus = "EXPIRED"
)

func (UserStatus) Valid

func (s UserStatus) Valid() bool

type UserUsageSample

type UserUsageSample struct {
	UserID string    `json:"userUuid"`
	NodeID string    `json:"nodeUuid"`
	At     time.Time `json:"at"`
	Bytes  int64     `json:"bytes"`
}

UserUsageSample is one bucketed traffic measurement for a user.

Jump to

Keyboard shortcuts

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