native

package
v4.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package native implements the subscription package's v2 administrator routes in the package itself, on the kernel's v2_subscription_group, v2_subscription_template, v2_plan_subscription_group and v2_subscription_group_node_protocols tables adopted in place (kernel.storage.adopt). Legacy handlers and native routes share the tables, so a route can switch between them at any time. Responses are byte-compatible with the legacy handlers (internal/tests/subscriptioncompat); the bound and answered types mirror the kernel model (package model).

Subscription group membership (v2_user_subscription_group) is subscriber state, which only the kernel writes: granting a user a group, taking it away and deleting a group's members go through KernelSubscriber (kernel.subscriber.groups.v1), and without it those routes stay legacy. So does the user's subscription summary, which the kernel caches and answers through KernelSubscriber (kernel.subscriber.summary.v1).

Other domains are read through kernel views only:

  • kapi_plan_catalog_v1 for whether a plan exists;
  • kapi_subscriber_entitlement_v1 for whether a user exists and the traffic group members used;
  • kapi_user_subscription_group_v1 for the subscription groups a user holds and until when;
  • kapi_node_protocol_v1 and kapi_node_heartbeat_v1 for which node a protocol belongs to and when it last reported;
  • kapi_node_protocol_public_v1 and kapi_node_public_v1 for a group's protocols and the protocol pool, once the kernel grants them: only after the node credential split of v2_node_protocol and v2_node is finalized, when they show the redacted documents (pool.go).

Two routes have no native handler and stay bridged; see bridgedRoutes in packages/subscription/control.

Index

Constants

View Source
const (
	DeleteGroupRouteID     = "subscription.admin.subscription.groups.id.delete"
	GrantUserGroupRouteID  = "subscription.admin.subscription.users.user_id.groups.post"
	RevokeUserGroupRouteID = "subscription.admin.subscription.users.user_id.groups.group_id.delete"
)

The routes that change subscription group membership; they have a native handler only with a Subscriber.

View Source
const (
	GroupProtocolsRouteID     = "subscription.admin.subscription.groups.id.protocols.get"
	AvailableProtocolsRouteID = "subscription.admin.subscription.protocols.available.get"
)

Route ids of the protocol pool routes, which read the views of the node credential split's remainder.

View Source
const SummaryRouteID = "subscription.user.subscription.get"

SummaryRouteID is GET /api/v2/user/subscription; it has a native handler only with a Subscriber.

Variables

This section is empty.

Functions

func DeleteGroupRequestID

func DeleteGroupRequestID(groupID uint, token string) string

DeleteGroupRequestID names the removal of a deleted group's members, as GrantRequestID names a grant (service.SubscriptionGroupDeleteRequestID).

func GrantRequestID

func GrantRequestID(userID, groupID uint, expireAt, transferEnable, nextRenewPrice *int64, token string) string

GrantRequestID names an administrator's grant of a subscription group to a user in Control's subscriber request ledger, so a retried request is applied once. token identifies the HTTP request: its Idempotency-Key, else its X-Request-ID, else a fresh random value. The digest also covers the granted fields, so a key reused for other values is another grant. The kernel's legacy handler derives the same id (service.SubscriptionGroupGrantRequestID), so a retry is recognized whichever side serves it.

func RevokeRequestID

func RevokeRequestID(userID, groupID uint, token string) string

RevokeRequestID names an administrator's removal of a user's group, as GrantRequestID names a grant (service.SubscriptionGroupRevokeRequestID).

Types

type Entitlement

type Entitlement struct {
	ID uint `gorm:"primaryKey"`
	U  int64
	D  int64
}

Entitlement is the part of a kapi_subscriber_entitlement_v1 row the routes read: that the user exists, and the traffic they used.

func (Entitlement) TableName

func (Entitlement) TableName() string

TableName is the kernel view.

type NodeHeartbeat

type NodeHeartbeat struct {
	ID          uint `gorm:"primaryKey"`
	LastCheckAt *int64
}

NodeHeartbeat is a row of kapi_node_heartbeat_v1: when a node last reported.

func (NodeHeartbeat) TableName

func (NodeHeartbeat) TableName() string

TableName is the kernel view.

type NodeProtocol

type NodeProtocol struct {
	ID     uint `gorm:"primaryKey"`
	NodeID uint
}

NodeProtocol is a row of kapi_node_protocol_v1: a node protocol and its node, without its settings.

func (NodeProtocol) TableName

func (NodeProtocol) TableName() string

TableName is the kernel view.

type PlanCatalog

type PlanCatalog struct {
	ID uint `gorm:"primaryKey"`
}

PlanCatalog is the part of a kapi_plan_catalog_v1 row the routes read.

func (PlanCatalog) TableName

func (PlanCatalog) TableName() string

TableName is the kernel view.

type PoolNode added in v4.2.0

type PoolNode struct {
	ID           uint             `gorm:"primaryKey" json:"id"`
	Name         string           `json:"name"`
	Host         string           `json:"host"`
	Port         int              `json:"port"`
	Status       model.NodeStatus `json:"status"`
	Tags         *string          `json:"tags"`
	GroupID      *uint            `json:"group_id"`
	Rate         float64          `json:"rate"`
	TrafficRate  float64          `json:"traffic_rate"`
	Sort         int              `json:"sort"`
	Show         int              `json:"show"`
	AutoRegister int              `json:"auto_register"`

	ParentID *uint `json:"parent_id"`

	MonthlyLimit    *int64 `json:"monthly_limit"`
	MonthlyUpload   int64  `json:"monthly_upload"`
	MonthlyDownload int64  `json:"monthly_download"`
	MonthlyResetDay int    `json:"monthly_reset_day"`

	RawConfig *string `json:"raw_config"`

	ServerIP      *string `json:"server_ip"`
	ServerVersion *string `json:"server_version"`
	ServerOS      *string `json:"server_os"`
	CPUUsage      float64 `json:"cpu_usage"`
	MemoryUsage   float64 `json:"memory_usage"`
	DiskUsage     float64 `json:"disk_usage"`
	Uptime        int64   `json:"uptime"`
	OnlineUsers   int     `json:"online_users"`

	RuntimeHealthy   bool   `json:"runtime_healthy"`
	RuntimeError     string `json:"runtime_error"`
	RuntimeCheckedAt *int64 `json:"runtime_checked_at"`

	TotalUpload   int64 `json:"total_upload"`
	TotalDownload int64 `json:"total_download"`

	LastCheckAt *int64    `json:"last_check_at"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

PoolNode is a row of kapi_node_public_v1: a proxy node without its API key, key hash and shared secret, its raw configuration redacted. It answers as the kernel's model.Node does, whose credentials are never serialized.

func (PoolNode) TableName added in v4.2.0

func (PoolNode) TableName() string

TableName is the kernel view.

type PoolProtocol added in v4.2.0

type PoolProtocol struct {
	ID      uint               `gorm:"primaryKey" json:"id"`
	NodeID  uint               `json:"node_id"`
	Name    string             `json:"name"`
	Type    model.ProtocolType `json:"type"`
	Port    int                `json:"port"`
	Enable  int                `json:"enable"`
	Show    int                `json:"show"`
	Sort    int                `json:"sort"`
	GroupID *uint              `json:"group_id"`

	Host *string `json:"host"`
	TLS  int     `json:"tls"`
	ALPN *string `json:"alpn"`

	Settings          *string `json:"settings"`
	TLSSettings       *string `json:"tls_settings"`
	Transport         *string `json:"transport"`
	TransportSettings *string `json:"transport_settings"`

	RealitySettings *string `json:"reality_settings"`

	CustomConfig *string `json:"custom_config"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`

	Node               *PoolNode                 `gorm:"foreignKey:NodeID" json:"node,omitempty"`
	SubscriptionGroups []model.SubscriptionGroup `` /* 155-byte string literal not displayed */
}

PoolProtocol is a row of kapi_node_protocol_public_v1: a node protocol with its settings as a finalized v2_node_protocol keeps them, the placeholder at every secret position. It answers as the kernel's model.NodeProtocol does.

func (PoolProtocol) TableName added in v4.2.0

func (PoolProtocol) TableName() string

TableName is the kernel view.

type Service

type Service struct {
	// Open returns the package's storage connection, on which the adopted
	// tables and the granted views are visible.
	Open func(ctx context.Context) (*gorm.DB, error)
	// Subscriber is the kernel's KernelSubscriber; without it the routes
	// that change subscription group membership and the user's
	// subscription summary have no native handler and stay legacy.
	Subscriber Subscriber
	// Leased reports whether the package's storage lease adopts the kernel
	// table, or grants the kernel view, called name
	// (packagestoresdk.Store.Leased). The protocol pool reads views the
	// kernel grants only once the node credential split is finalized; nil
	// leaves those routes legacy.
	Leased func(ctx context.Context, name string) bool
	// Now defaults to time.Now.
	Now func() time.Time
	// NewToken names a request that carries neither an Idempotency-Key nor
	// an X-Request-ID; it defaults to a random UUID.
	NewToken func() string
}

Service holds what the native routes need.

func (*Service) AssignPlanGroup

AssignPlanGroup is POST /api/v2/admin/subscription/plans/:plan_id/groups: the plan grants the group, once.

func (*Service) AvailableProtocols added in v4.2.0

AvailableProtocols is GET /api/v2/admin/subscription/protocols/available: the protocol pool, every protocol shown in subscriptions, by node and sort order, each with its node and groups, redacted.

func (*Service) CreateGroup

CreateGroup is POST /api/v2/admin/subscription/groups. As in the kernel, only the group's own columns are written: templates and protocols in the body are dropped.

func (*Service) CreateTemplate

CreateTemplate is POST /api/v2/admin/subscription/groups/:id/templates. As in the kernel, only the template's own columns are written, in the group the path names: a nested "group" is dropped.

func (*Service) DeleteGroup

DeleteGroup is DELETE /api/v2/admin/subscription/groups/:id. Control first takes the group from its members (KernelSubscriber.RemoveSubscriptionGroupMembers); then the group's templates, plan links and node protocol links go with the group, in one transaction. A retry after a failure in between finds no members and completes.

func (*Service) DeleteTemplate

DeleteTemplate is DELETE /api/v2/admin/subscription/templates/:id.

func (*Service) Formats

Formats is GET /api/v2/admin/subscription/formats: the formats a subscription link can be rendered in.

func (*Service) GrantUserGroup

GrantUserGroup is POST /api/v2/admin/subscription/users/:user_id/groups: the user holds the group, with its own expiry, traffic and renewal price when given. Control creates the membership, or sets the given fields of an existing one (KernelSubscriber.GrantSubscriptionGroup).

func (*Service) Group

Group is GET /api/v2/admin/subscription/groups/:id, with its templates.

func (*Service) GroupProtocols added in v4.2.0

GroupProtocols is GET /api/v2/admin/subscription/groups/:id/protocols: the node protocols linked to a group, each with its node, settings and raw configuration redacted.

func (*Service) GroupTemplates

GroupTemplates is GET /api/v2/admin/subscription/groups/:id/templates.

func (*Service) Groups

Groups is GET /api/v2/admin/subscription/groups.

func (*Service) Handlers

func (s *Service) Handlers() map[string]pluginhostsdk.NativeHandler

Handlers returns the native handlers by route id.

func (*Service) LinkProtocols

LinkProtocols is POST /api/v2/admin/subscription/groups/:id/protocols: the group's node protocols become exactly the listed ones. A protocol is known by kapi_node_protocol_v1; the link rows are the adopted v2_subscription_group_node_protocols. The kernel's association replace also touches the group's updated_at, and so does this.

func (*Service) PlanGroups

PlanGroups is GET /api/v2/admin/subscription/plans/:plan_id/groups.

func (*Service) ProtocolTypes

ProtocolTypes is GET /api/v2/admin/subscription/protocols: the protocol types a template can have.

func (*Service) RemovePlanGroup

RemovePlanGroup is DELETE /api/v2/admin/subscription/plans/:plan_id/groups/:group_id.

func (*Service) RevokeUserGroup

RevokeUserGroup is DELETE /api/v2/admin/subscription/users/:user_id/groups/:group_id (KernelSubscriber.RevokeSubscriptionGroup).

func (*Service) Stats

Stats is GET /api/v2/admin/subscription/stats. Members and their expiry come from kapi_user_subscription_group_v1, their traffic from kapi_subscriber_entitlement_v1, and a protocol's node and its last report from kapi_node_protocol_v1 and kapi_node_heartbeat_v1. As in the kernel, a count that fails stays zero.

func (*Service) Summary

Summary is GET /api/v2/user/subscription: the caller's summary the kernel caches for 30 seconds, read through KernelSubscriber.GetSubscriptionSummary (kernel.subscriber.summary.v1), with the subscription link settings the kernel adds on every call. The kernel keeps the one cache both modes read, so a native answer carries the summary and cached_at the legacy handler would, and refresh=true (only that exact value, as gin's c.Query) rebuilds it.

func (*Service) Template

Template is GET /api/v2/admin/subscription/templates/:id.

func (*Service) UpdateGroup

UpdateGroup is PUT /api/v2/admin/subscription/groups/:id. The body replaces every column, as the kernel's Save does: a missing field is written as its zero value, and an unknown id creates the group.

func (*Service) UpdateTemplate

UpdateTemplate is PUT /api/v2/admin/subscription/templates/:id: a partial update by column, as the kernel's UpdateTemplateFields does, answered with the stored template.

func (*Service) UserGroups

UserGroups is GET /api/v2/admin/subscription/users/:user_id/groups: the groups a user holds and that have not expired, from kapi_user_subscription_group_v1.

type Subscriber

Subscriber is the part of KernelSubscriber the native routes call: the membership family (kernel.subscriber.groups.v1), since v2_user_subscription_group is subscriber state the kernel alone writes, and the subscription summary (kernel.subscriber.summary.v1), which the kernel caches.

type Summary

type Summary struct {
	UserID           uint64    `json:"user_id"`
	Email            string    `json:"email"`
	PlanID           *uint64   `json:"plan_id"`
	PlanName         string    `json:"plan_name"`
	TransferEnable   int64     `json:"transfer_enable"`
	UsedTraffic      int64     `json:"used_traffic"`
	UploadTraffic    int64     `json:"upload_traffic"`
	DownloadTraffic  int64     `json:"download_traffic"`
	ExpiredAt        int64     `json:"expired_at"`
	IsExpired        bool      `json:"is_expired"`
	DaysRemaining    int64     `json:"days_remaining"`
	UsagePercent     float64   `json:"usage_percent"`
	SubscribePath    string    `json:"subscribe_path,omitempty"`
	SubscribeDomains []string  `json:"subscribe_domains,omitempty"`
	CachedAt         time.Time `json:"cached_at"`
}

Summary is the user's subscription summary, with the fields and tags of the kernel's service.UserSubscription.

type UserGroup

type UserGroup struct {
	UserID   uint
	GroupID  uint
	ExpireAt *int64
}

UserGroup is a row of kapi_user_subscription_group_v1: a subscription group a user holds, until expire_at (null: no expiry). The kernel is the only writer of v2_user_subscription_group.

func (UserGroup) TableName

func (UserGroup) TableName() string

TableName is the kernel view.

Directories

Path Synopsis
Package model mirrors the kernel model types the subscription routes bind request bodies to and answer with: the same type names, fields and tags, in a package of the same name.
Package model mirrors the kernel model types the subscription routes bind request bodies to and answer with: the same type names, fields and tags, in a package of the same name.

Jump to

Keyboard shortcuts

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