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
- func DeleteGroupRequestID(groupID uint, token string) string
- func GrantRequestID(userID, groupID uint, expireAt, transferEnable, nextRenewPrice *int64, ...) string
- func RevokeRequestID(userID, groupID uint, token string) string
- type Entitlement
- type NodeHeartbeat
- type NodeProtocol
- type PlanCatalog
- type PoolNode
- type PoolProtocol
- type Service
- func (s *Service) AssignPlanGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AvailableProtocols(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) CreateGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) CreateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) DeleteGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) DeleteTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Formats(context.Context, pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) GrantUserGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Group(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) GroupProtocols(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) GroupTemplates(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Groups(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Handlers() map[string]pluginhostsdk.NativeHandler
- func (s *Service) LinkProtocols(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) PlanGroups(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) ProtocolTypes(context.Context, pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) RemovePlanGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) RevokeUserGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Stats(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Summary(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Template(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UpdateGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UpdateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UserGroups(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- type Subscriber
- type Summary
- type UserGroup
Constants ¶
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.
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.
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 ¶
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 ¶
RevokeRequestID names an administrator's removal of a user's group, as GrantRequestID names a grant (service.SubscriptionGroupRevokeRequestID).
Types ¶
type Entitlement ¶
Entitlement is the part of a kapi_subscriber_entitlement_v1 row the routes read: that the user exists, and the traffic they used.
type NodeHeartbeat ¶
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 ¶
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.
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.
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 ¶
func (s *Service) AssignPlanGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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
func (s *Service) AvailableProtocols(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) CreateGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) CreateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) DeleteGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) DeleteTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
DeleteTemplate is DELETE /api/v2/admin/subscription/templates/:id.
func (*Service) Formats ¶
func (s *Service) Formats(context.Context, pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
Formats is GET /api/v2/admin/subscription/formats: the formats a subscription link can be rendered in.
func (*Service) GrantUserGroup ¶
func (s *Service) GrantUserGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) Group(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
Group is GET /api/v2/admin/subscription/groups/:id, with its templates.
func (*Service) GroupProtocols ¶ added in v4.2.0
func (s *Service) GroupProtocols(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) GroupTemplates(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
GroupTemplates is GET /api/v2/admin/subscription/groups/:id/templates.
func (*Service) Groups ¶
func (s *Service) Groups(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) LinkProtocols(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) PlanGroups(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
PlanGroups is GET /api/v2/admin/subscription/plans/:plan_id/groups.
func (*Service) ProtocolTypes ¶
func (s *Service) ProtocolTypes(context.Context, pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
ProtocolTypes is GET /api/v2/admin/subscription/protocols: the protocol types a template can have.
func (*Service) RemovePlanGroup ¶
func (s *Service) RemovePlanGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
RemovePlanGroup is DELETE /api/v2/admin/subscription/plans/:plan_id/groups/:group_id.
func (*Service) RevokeUserGroup ¶
func (s *Service) RevokeUserGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
RevokeUserGroup is DELETE /api/v2/admin/subscription/users/:user_id/groups/:group_id (KernelSubscriber.RevokeSubscriptionGroup).
func (*Service) Stats ¶
func (s *Service) Stats(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) Summary(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) Template(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
Template is GET /api/v2/admin/subscription/templates/:id.
func (*Service) UpdateGroup ¶
func (s *Service) UpdateGroup(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) UpdateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
func (s *Service) UserGroups(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
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 ¶
type Subscriber interface {
GrantSubscriptionGroup(ctx context.Context, in *kernelsubscriberv1.GrantSubscriptionGroupRequest, opts ...grpc.CallOption) (*kernelsubscriberv1.GrantSubscriptionGroupResponse, error)
RevokeSubscriptionGroup(ctx context.Context, in *kernelsubscriberv1.RevokeSubscriptionGroupRequest, opts ...grpc.CallOption) (*kernelsubscriberv1.RevokeSubscriptionGroupResponse, error)
RemoveSubscriptionGroupMembers(ctx context.Context, in *kernelsubscriberv1.RemoveSubscriptionGroupMembersRequest, opts ...grpc.CallOption) (*kernelsubscriberv1.RemoveSubscriptionGroupMembersResponse, error)
GetSubscriptionSummary(ctx context.Context, in *kernelsubscriberv1.GetSubscriptionSummaryRequest, opts ...grpc.CallOption) (*kernelsubscriberv1.GetSubscriptionSummaryResponse, error)
}
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.
Source Files
¶
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. |