Merge old/master into main, resolving conflicts by preferring old in workflows and removing deprecated adapter files
Some checks failed
Build docker and publish / build (20.15.1) (push) Has been cancelled

This commit is contained in:
shanshanzhong 2025-12-28 15:56:07 -08:00
commit 49d59eccb0
478 changed files with 13812 additions and 16686 deletions

51
.github/workflows/develop.yaml vendored Normal file
View File

@ -0,0 +1,51 @@
name: Deploy
on:
push:
branches: ["develop"]
pull_request:
branches: ["develop"]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get short Git commit ID
id: vars
run: echo "COMMIT_ID=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Build Docker image
run: docker build --build-arg VERSION=${{ env.COMMIT_ID }} -t ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }} .
- name: Push Docker image
run: docker push ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }}
# - name: Deploy to server
# uses: appleboy/ssh-action@v0.1.6
# with:
# host: ${{ secrets.SSH_HOST }}
# username: ${{ secrets.SSH_USER }}
# key: ${{ secrets.SSH_PRIVATE_KEY }}
# script: |
# if [ $(docker ps -a -q -f name=ppanel-server-dev) ]; then
# echo "Stopping and removing existing ppanel-server container..."
# docker stop ppanel-server-dev
# docker rm ppanel-server-dev
# else
# echo "No existing ppanel-server-dev container running."
# fi
#
# docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
# docker run -d --restart=always --log-driver=journald --name ppanel-server-dev -p 8080:8080 -v /www/wwwroot/api/etc:/app/etc -v /www/wwwroot/api/logs:/app/logs --restart=always -d ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }}
#

81
.github/workflows/swagger.yaml vendored Normal file
View File

@ -0,0 +1,81 @@
name: Go CI/CD with goctl and Swagger
on:
# release:
# types: [published]
push:
branches:
- develop
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install goctl
run: |
curl -L https://github.com/zeromicro/go-zero/releases/download/tools%2Fgoctl%2Fv1.7.2/goctl-v1.7.2-linux-amd64.tar.gz -o goctl-v1.7.2-linux-amd64.tar.gz
tar -xvzf goctl-v1.7.2-linux-amd64.tar.gz
chmod +x goctl
sudo mv goctl /usr/local/bin/goctl
goctl --version
- name: Install goctl-swagger
run: |
curl -L https://github.com/tensionc/goctl-swagger/releases/download/v1.0.1/goctl-swagger-v1.0.1-linux-amd64.tar.gz -o goctl-swagger.tar.gz
tar -xvzf goctl-swagger.tar.gz
chmod +x goctl-swagger
sudo mv goctl-swagger /usr/local/bin/
- name: Generate Swagger file
run: |
mkdir -p swagger
goctl api plugin -plugin goctl-swagger='swagger -filename common.json -pack Response -response "[{\"name\":\"code\",\"type\":\"integer\",\"description\":\"状态码\"},{\"name\":\"msg\",\"type\":\"string\",\"description\":\"消息\"},{\"name\":\"data\",\"type\":\"object\",\"description\":\"数据\",\"is_data\":true}]";' -api ./apis/swagger_common.api -dir ./swagger
goctl api plugin -plugin goctl-swagger='swagger -filename user.json -pack Response -response "[{\"name\":\"code\",\"type\":\"integer\",\"description\":\"状态码\"},{\"name\":\"msg\",\"type\":\"string\",\"description\":\"消息\"},{\"name\":\"data\",\"type\":\"object\",\"description\":\"数据\",\"is_data\":true}]";' -api ./apis/swagger_user.api -dir ./swagger
goctl api plugin -plugin goctl-swagger='swagger -filename admin.json -pack Response -response "[{\"name\":\"code\",\"type\":\"integer\",\"description\":\"状态码\"},{\"name\":\"msg\",\"type\":\"string\",\"description\":\"消息\"},{\"name\":\"data\",\"type\":\"object\",\"description\":\"数据\",\"is_data\":true}]";' -api ./apis/swagger_admin.api -dir ./swagger
goctl api plugin -plugin goctl-swagger='swagger -filename ppanel.json -pack Response -response "[{\"name\":\"code\",\"type\":\"integer\",\"description\":\"状态码\"},{\"name\":\"msg\",\"type\":\"string\",\"description\":\"消息\"},{\"name\":\"data\",\"type\":\"object\",\"description\":\"数据\",\"is_data\":true}]";' -api ppanel.api -dir ./swagger
goctl api plugin -plugin goctl-swagger='swagger -filename node.json -pack Response -response "[{\"name\":\"code\",\"type\":\"integer\",\"description\":\"状态码\"},{\"name\":\"msg\",\"type\":\"string\",\"description\":\"消息\"},{\"name\":\"data\",\"type\":\"object\",\"description\":\"数据\",\"is_data\":true}]";' -api ./apis/swagger_node.api -dir ./swagger
- name: Verify Swagger file
run: |
test -f ./swagger/common.json
test -f ./swagger/user.json
test -f ./swagger/admin.json
- name: Checkout target repository
uses: actions/checkout@v4
with:
repository: perfect-panel/ppanel-docs
token: ${{ secrets.GH_TOKEN }}
path: ppanel-docs
persist-credentials: true
- name: Verify or create public/swagger directory
run: |
mkdir -p ./ppanel-docs/public/swagger
- name: Copy Swagger files
run: |
cp -rf swagger/* ppanel-docs/public/swagger
cd ppanel-docs
- name: Check for file changes
run: |
cd ppanel-docs
git add .
git status
if [ "$(git status --porcelain)" ]; then
echo "Changes detected in the doc repository."
git config user.name "GitHub Actions"
git config user.email "actions@ppanel.dev"
git commit -m "Update Swagger files"
git push
else
echo "No changes detected."
exit 0
fi

View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="go build github.com/perfect-panel/server" type="GoApplicationRunConfiguration" factoryName="Go Application" nameIsGenerated="true">
<module name="server" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="run --config etc/ppanel-dev.yaml" />
<kind value="PACKAGE" />
<package value="github.com/perfect-panel/server" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/ppanel.go" />
<method v="2" />
</configuration>
</component>

View File

@ -20,7 +20,7 @@ RUN go mod download
COPY . . COPY . .
# Build the binary with version and build time # Build the binary with version and build time
RUN BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") && \ RUN BUILD_TIME=$(date -u +"%Y-%m-%d %H:%M:%S") && \
go build -ldflags="-s -w -X 'github.com/perfect-panel/server/pkg/constant.Version=${VERSION}' -X 'github.com/perfect-panel/server/pkg/constant.BuildTime=${BUILD_TIME}'" -o /app/ppanel ppanel.go go build -ldflags="-s -w -X 'github.com/perfect-panel/server/pkg/constant.Version=${VERSION}' -X 'github.com/perfect-panel/server/pkg/constant.BuildTime=${BUILD_TIME}'" -o /app/ppanel ppanel.go
# Final minimal image # Final minimal image

View File

@ -14,6 +14,19 @@
</div> </div>
> **Article 1.**
> All human beings are born free and equal in dignity and rights.
> They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.
>
> **Article 12.**
> No one shall be subjected to arbitrary interference with his privacy, family, home or correspondence, nor to attacks upon his honour and reputation.
> Everyone has the right to the protection of the law against such interference or attacks.
>
> **Article 19.**
> Everyone has the right to freedom of opinion and expression; this right includes freedom to hold opinions without interference and to seek, receive and impart information and ideas through any media and regardless of frontiers.
>
> *Source: [United Nations Universal Declaration of Human Rights (UN.org)](https://www.un.org/sites/un2.un.org/files/2021/03/udhr.pdf)*
## 📋 Overview ## 📋 Overview
PPanel Server is the backend component of the PPanel project, providing robust APIs and core functionality for managing PPanel Server is the backend component of the PPanel project, providing robust APIs and core functionality for managing

140
adapter/adapter.go Normal file
View File

@ -0,0 +1,140 @@
package adapter
import (
"strings"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/pkg/logger"
)
type Adapter struct {
SiteName string // 站点名称
Servers []*node.Node // 服务器列表
UserInfo User // 用户信息
ClientTemplate string // 客户端配置模板
OutputFormat string // 输出格式,默认是 base64
SubscribeName string // 订阅名称
}
type Option func(*Adapter)
// WithServers 设置服务器列表
func WithServers(servers []*node.Node) Option {
return func(opts *Adapter) {
opts.Servers = servers
}
}
// WithUserInfo 设置用户信息
func WithUserInfo(user User) Option {
return func(opts *Adapter) {
opts.UserInfo = user
}
}
// WithOutputFormat 设置输出格式
func WithOutputFormat(format string) Option {
return func(opts *Adapter) {
opts.OutputFormat = format
}
}
// WithSiteName 设置站点名称
func WithSiteName(name string) Option {
return func(opts *Adapter) {
opts.SiteName = name
}
}
// WithSubscribeName 设置订阅名称
func WithSubscribeName(name string) Option {
return func(opts *Adapter) {
opts.SubscribeName = name
}
}
func NewAdapter(tpl string, opts ...Option) *Adapter {
adapter := &Adapter{
Servers: []*node.Node{},
UserInfo: User{},
ClientTemplate: tpl,
OutputFormat: "base64", // 默认输出格式
}
for _, opt := range opts {
opt(adapter)
}
return adapter
}
func (adapter *Adapter) Client() (*Client, error) {
client := &Client{
SiteName: adapter.SiteName,
SubscribeName: adapter.SubscribeName,
ClientTemplate: adapter.ClientTemplate,
OutputFormat: adapter.OutputFormat,
Proxies: []Proxy{},
UserInfo: adapter.UserInfo,
}
proxies, err := adapter.Proxies(adapter.Servers)
if err != nil {
return nil, err
}
client.Proxies = proxies
return client, nil
}
func (adapter *Adapter) Proxies(servers []*node.Node) ([]Proxy, error) {
var proxies []Proxy
for _, item := range servers {
if item.Server == nil {
logger.Errorf("[Adapter] Server is nil for node ID: %d", item.Id)
continue
}
protocols, err := item.Server.UnmarshalProtocols()
if err != nil {
logger.Errorf("[Adapter] Unmarshal Protocols error: %s; server id : %d", err.Error(), item.ServerId)
continue
}
for _, protocol := range protocols {
if protocol.Type == item.Protocol {
proxies = append(proxies, Proxy{
Sort: item.Sort,
Name: item.Name,
Server: item.Address,
Port: item.Port,
Type: item.Protocol,
Tags: strings.Split(item.Tags, ","),
Security: protocol.Security,
SNI: protocol.SNI,
AllowInsecure: protocol.AllowInsecure,
Fingerprint: protocol.Fingerprint,
RealityServerAddr: protocol.RealityServerAddr,
RealityServerPort: protocol.RealityServerPort,
RealityPrivateKey: protocol.RealityPrivateKey,
RealityPublicKey: protocol.RealityPublicKey,
RealityShortId: protocol.RealityShortId,
Transport: protocol.Transport,
Host: protocol.Host,
Path: protocol.Path,
ServiceName: protocol.ServiceName,
Method: protocol.Cipher,
ServerKey: protocol.ServerKey,
Flow: protocol.Flow,
HopPorts: protocol.HopPorts,
HopInterval: protocol.HopInterval,
ObfsPassword: protocol.ObfsPassword,
DisableSNI: protocol.DisableSNI,
ReduceRtt: protocol.ReduceRtt,
UDPRelayMode: protocol.UDPRelayMode,
CongestionController: protocol.CongestionController,
})
}
}
}
return proxies, nil
}

34
adapter/adapter_test.go Normal file
View File

@ -0,0 +1,34 @@
package adapter
import (
"testing"
"time"
)
func TestAdapter_Client(t *testing.T) {
servers := getServers()
if len(servers) == 0 {
t.Errorf("[Test] No servers found")
return
}
a := NewAdapter(tpl, WithServers(servers), WithUserInfo(User{
Password: "test-password",
ExpiredAt: time.Now().AddDate(1, 0, 0),
Download: 0,
Upload: 0,
Traffic: 1000,
SubscribeURL: "https://example.com/subscribe",
}))
client, err := a.Client()
if err != nil {
t.Errorf("[Test] Failed to get client: %v", err.Error())
return
}
bytes, err := client.Build()
if err != nil {
t.Errorf("[Test] Failed to build client config: %v", err.Error())
return
}
t.Logf("[Test] Client config built successfully: %s", string(bytes))
}

146
adapter/client.go Normal file
View File

@ -0,0 +1,146 @@
package adapter
import (
"bytes"
"encoding/base64"
"reflect"
"text/template"
"time"
"github.com/Masterminds/sprig/v3"
)
type Proxy struct {
Sort int
Name string
Server string
Port uint16
Type string
Tags []string
// Security Options
Security string
SNI string // Server Name Indication for TLS
AllowInsecure bool // Allow insecure connections (skip certificate verification)
Fingerprint string // Client fingerprint for TLS connections
RealityServerAddr string // Reality server address
RealityServerPort int // Reality server port
RealityPrivateKey string // Reality private key for authentication
RealityPublicKey string // Reality public key for authentication
RealityShortId string // Reality short ID for authentication
// Transport Options
Transport string // Transport protocol (e.g., ws, http, grpc)
Host string // For WebSocket/HTTP/HTTPS
Path string // For HTTP/HTTPS
ServiceName string // For gRPC
// Shadowsocks Options
Method string
ServerKey string // For Shadowsocks 2022
// Vmess/Vless/Trojan Options
Flow string // Flow for Vmess/Vless/Trojan
// Hysteria2 Options
HopPorts string // Comma-separated list of hop ports
HopInterval int // Interval for hop ports in seconds
ObfsPassword string // Obfuscation password for Hysteria2
UpMbps int // Upload speed in Mbps
DownMbps int // Download speed in Mbps
// Tuic Options
DisableSNI bool // Disable SNI
ReduceRtt bool // Reduce RTT
UDPRelayMode string // UDP relay mode (e.g., "full", "partial")
CongestionController string // Congestion controller (e.g., "cubic", "bbr")
// AnyTLS
PaddingScheme string
// Mieru
Multiplex string
// Obfs
//Obfs string // obfs, 'none', 'http', 'tls'
//ObfsHost string // obfs host
//ObfsPath string // obfs path
// Vless
XhttpMode string // xhttp mode
XhttpExtra string // xhttp path
// encryption
Encryption string // encryption'none', 'mlkem768x25519plus'
EncryptionMode string // encryption mode'native', 'xorpub', 'random'
EncryptionRtt string // encryption rtt'0rtt', '1rtt'
EncryptionTicket string // encryption ticket
EncryptionServerPadding string // encryption server padding
EncryptionPrivateKey string // encryption private key
EncryptionClientPadding string // encryption client padding
EncryptionPassword string // encryption password
Ratio float64 // Traffic ratio, default is 1
CertMode string // Certificate mode, `none``http``dns``self`
CertDNSProvider string // DNS provider for certificate
CertDNSEnv string // Environment for DNS provider
}
type User struct {
Password string
ExpiredAt time.Time
Download int64
Upload int64
Traffic int64
SubscribeURL string
}
type Client struct {
SiteName string // Name of the site
SubscribeName string // Name of the subscription
ClientTemplate string // Template for the entire client configuration
OutputFormat string // json, yaml, etc.
Proxies []Proxy // List of proxy configurations
UserInfo User // User information
}
func (c *Client) Build() ([]byte, error) {
var buf bytes.Buffer
tmpl, err := template.New("client").Funcs(sprig.TxtFuncMap()).Parse(c.ClientTemplate)
if err != nil {
return nil, err
}
proxies := make([]map[string]interface{}, len(c.Proxies))
for i, p := range c.Proxies {
proxies[i] = StructToMap(p)
}
err = tmpl.Execute(&buf, map[string]interface{}{
"SiteName": c.SiteName,
"SubscribeName": c.SubscribeName,
"OutputFormat": c.OutputFormat,
"Proxies": proxies,
"UserInfo": c.UserInfo,
})
if err != nil {
return nil, err
}
result := buf.String()
if c.OutputFormat == "base64" {
encoded := base64.StdEncoding.EncodeToString([]byte(result))
return []byte(encoded), nil
}
return buf.Bytes(), nil
}
func StructToMap(obj interface{}) map[string]interface{} {
m := make(map[string]interface{})
v := reflect.ValueOf(obj)
t := reflect.TypeOf(obj)
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
m[field.Name] = v.Field(i).Interface()
}
return m
}

153
adapter/client_test.go Normal file
View File

@ -0,0 +1,153 @@
package adapter
import (
"testing"
"time"
)
var tpl = `
{{- range $n := .Proxies }}
{{- $dn := urlquery (default "node" $n.Name) -}}
{{- $sni := default $n.Host $n.SNI -}}
{{- if eq $n.Type "shadowsocks" -}}
{{- $userinfo := b64enc (print $n.Method ":" $.UserInfo.Password) -}}
{{- printf "ss://%s@%s:%v#%s" $userinfo $n.Host $n.Port $dn -}}
{{- "\n" -}}
{{- end -}}
{{- if eq $n.Type "trojan" -}}
{{- $qs := "security=tls" -}}
{{- if $sni }}{{ $qs = printf "%s&sni=%s" $qs (urlquery $sni) }}{{ end -}}
{{- if $n.AllowInsecure }}{{ $qs = printf "%s&allowInsecure=%v" $qs $n.AllowInsecure }}{{ end -}}
{{- if $n.Fingerprint }}{{ $qs = printf "%s&fp=%s" $qs (urlquery $n.Fingerprint) }}{{ end -}}
{{- printf "trojan://%s@%s:%v?%s#%s" $.UserInfo.Password $n.Host $n.Port $qs $dn -}}
{{- "\n" -}}
{{- end -}}
{{- if eq $n.Type "vless" -}}
{{- $qs := "encryption=none" -}}
{{- if $n.RealityPublicKey -}}
{{- $qs = printf "%s&security=reality" $qs -}}
{{- $qs = printf "%s&pbk=%s" $qs (urlquery $n.RealityPublicKey) -}}
{{- if $n.RealityShortId }}{{ $qs = printf "%s&sid=%s" $qs (urlquery $n.RealityShortId) }}{{ end -}}
{{- else -}}
{{- if or $n.SNI $n.Fingerprint $n.AllowInsecure }}
{{- $qs = printf "%s&security=tls" $qs -}}
{{- end -}}
{{- end -}}
{{- if $n.SNI }}{{ $qs = printf "%s&sni=%s" $qs (urlquery $n.SNI) }}{{ end -}}
{{- if $n.AllowInsecure }}{{ $qs = printf "%s&allowInsecure=%v" $qs $n.AllowInsecure }}{{ end -}}
{{- if $n.Fingerprint }}{{ $qs = printf "%s&fp=%s" $qs (urlquery $n.Fingerprint) }}{{ end -}}
{{- if $n.Network }}{{ $qs = printf "%s&type=%s" $qs $n.Network }}{{ end -}}
{{- if $n.Path }}{{ $qs = printf "%s&path=%s" $qs (urlquery $n.Path) }}{{ end -}}
{{- if $n.ServiceName }}{{ $qs = printf "%s&serviceName=%s" $qs (urlquery $n.ServiceName) }}{{ end -}}
{{- if $n.Flow }}{{ $qs = printf "%s&flow=%s" $qs (urlquery $n.Flow) }}{{ end -}}
{{- printf "vless://%s@%s:%v?%s#%s" $n.ServerKey $n.Host $n.Port $qs $dn -}}
{{- "\n" -}}
{{- end -}}
{{- if eq $n.Type "vmess" -}}
{{- $obj := dict
"v" "2"
"ps" $n.Name
"add" $n.Host
"port" $n.Port
"id" $n.ServerKey
"aid" 0
"net" (or $n.Network "tcp")
"type" "none"
"path" (or $n.Path "")
"host" $n.Host
-}}
{{- if or $n.SNI $n.Fingerprint $n.AllowInsecure }}{{ set $obj "tls" "tls" }}{{ end -}}
{{- if $n.SNI }}{{ set $obj "sni" $n.SNI }}{{ end -}}
{{- if $n.Fingerprint }}{{ set $obj "fp" $n.Fingerprint }}{{ end -}}
{{- printf "vmess://%s" (b64enc (toJson $obj)) -}}
{{- "\n" -}}
{{- end -}}
{{- if or (eq $n.Type "hysteria2") (eq $n.Type "hy2") -}}
{{- $qs := "" -}}
{{- if $n.SNI }}{{ $qs = printf "sni=%s" (urlquery $n.SNI) }}{{ end -}}
{{- if $n.AllowInsecure }}{{ $qs = printf "%s&insecure=%v" $qs $n.AllowInsecure }}{{ end -}}
{{- if $n.ObfsPassword }}{{ $qs = printf "%s&obfs-password=%s" $qs (urlquery $n.ObfsPassword) }}{{ end -}}
{{- printf "hy2://%s@%s:%v%s#%s"
$.UserInfo.Password
$n.Host
$n.Port
(ternary (gt (len $qs) 0) (print "?" $qs) "")
$dn -}}
{{- "\n" -}}
{{- end -}}
{{- if eq $n.Type "tuic" -}}
{{- $qs := "" -}}
{{- if $n.SNI }}{{ $qs = printf "sni=%s" (urlquery $n.SNI) }}{{ end -}}
{{- if $n.AllowInsecure }}{{ $qs = printf "%s&insecure=%v" $qs $n.AllowInsecure }}{{ end -}}
{{- printf "tuic://%s:%s@%s:%v%s#%s"
$n.ServerKey
$.UserInfo.Password
$n.Host
$n.Port
(ternary (gt (len $qs) 0) (print "?" $qs) "")
$dn -}}
{{- "\n" -}}
{{- end -}}
{{- if eq $n.Type "anytls" -}}
{{- $qs := "" -}}
{{- if $n.SNI }}{{ $qs = printf "sni=%s" (urlquery $n.SNI) }}{{ end -}}
{{- printf "anytls://%s@%s:%v%s#%s"
$.UserInfo.Password
$n.Host
$n.Port
(ternary (gt (len $qs) 0) (print "?" $qs) "")
$dn -}}
{{- "\n" -}}
{{- end -}}
{{- end }}
`
func TestClient_Build(t *testing.T) {
client := &Client{
SiteName: "TestSite",
SubscribeName: "TestSubscribe",
ClientTemplate: tpl,
Proxies: []Proxy{
{
Name: "TestShadowSocks",
Type: "shadowsocks",
Host: "127.0.0.1",
Port: 1234,
Method: "aes-256-gcm",
},
{
Name: "TestTrojan",
Type: "trojan",
Host: "example.com",
Port: 443,
AllowInsecure: true,
Security: "tls",
Transport: "tcp",
SNI: "v1-dy.ixigua.com",
},
},
UserInfo: User{
Password: "testpassword",
ExpiredAt: time.Now().Add(24 * time.Hour),
Download: 1000000,
Upload: 500000,
Traffic: 1500000,
SubscribeURL: "https://example.com/subscribe",
},
}
buf, err := client.Build()
if err != nil {
t.Fatalf("Failed to build client: %v", err)
}
t.Logf("[测试] 输出: %s", buf)
}

1
adapter/utils.go Normal file
View File

@ -0,0 +1 @@
package adapter

46
adapter/utils_test.go Normal file
View File

@ -0,0 +1,46 @@
package adapter
import (
"testing"
"github.com/perfect-panel/server/internal/model/server"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func TestAdapterProxy(t *testing.T) {
servers := getServers()
if len(servers) == 0 {
t.Fatal("no servers found")
}
for _, srv := range servers {
proxy, err := adapterProxy(*srv, "example.com", 0)
if err != nil {
t.Errorf("failed to adapt server %s: %v", srv.Name, err)
}
t.Logf("[测试] 适配服务器 %s 成功: %+v", srv.Name, proxy)
}
}
func getServers() []*server.Server {
db, err := connectMySQL("root:mylove520@tcp(localhost:3306)/perfectlink?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
return nil
}
var servers []*server.Server
if err = db.Model(&server.Server{}).Find(&servers).Error; err != nil {
return nil
}
return servers
}
func connectMySQL(dsn string) (*gorm.DB, error) {
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: dsn,
}), &gorm.Config{})
if err != nil {
return nil, err
}
return db, nil
}

View File

@ -0,0 +1,96 @@
syntax = "v1"
info (
title: "Application API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
SubscribeApplication {
Id int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
Scheme string `json:"scheme,omitempty"`
UserAgent string `json:"user_agent"`
IsDefault bool `json:"is_default"`
SubscribeTemplate string `json:"template"`
OutputFormat string `json:"output_format"`
DownloadLink DownloadLink `json:"download_link,omitempty"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
GetSubscribeApplicationListResponse {
Total int64 `json:"total"`
List []SubscribeApplication `json:"list"`
}
CreateSubscribeApplicationRequest {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
Scheme string `json:"scheme,omitempty"`
UserAgent string `json:"user_agent"`
IsDefault bool `json:"is_default"`
SubscribeTemplate string `json:"template"`
OutputFormat string `json:"output_format"`
DownloadLink DownloadLink `json:"download_link"`
}
UpdateSubscribeApplicationRequest {
Id int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
Scheme string `json:"scheme,omitempty"`
UserAgent string `json:"user_agent"`
IsDefault bool `json:"is_default"`
SubscribeTemplate string `json:"template"`
OutputFormat string `json:"output_format"`
DownloadLink DownloadLink `json:"download_link,omitempty"`
}
DeleteSubscribeApplicationRequest {
Id int64 `json:"id"`
}
GetSubscribeApplicationListRequest {
Page int `form:"page"`
Size int `form:"size"`
}
PreviewSubscribeTemplateRequest {
Id int64 `form:"id"`
}
PreviewSubscribeTemplateResponse {
Template string `json:"template"` // 预览的模板内容
}
)
@server (
prefix: v1/admin/application
group: admin/application
middleware: AuthMiddleware
)
service ppanel {
@doc "Create subscribe application"
@handler CreateSubscribeApplication
post / (CreateSubscribeApplicationRequest) returns (SubscribeApplication)
@doc "Update subscribe application"
@handler UpdateSubscribeApplication
put /subscribe_application (UpdateSubscribeApplicationRequest) returns (SubscribeApplication)
@doc "Get subscribe application list"
@handler GetSubscribeApplicationList
get /subscribe_application_list (GetSubscribeApplicationListRequest) returns (GetSubscribeApplicationListResponse)
@doc "Delete subscribe application"
@handler DeleteSubscribeApplication
delete /subscribe_application (DeleteSubscribeApplicationRequest)
@doc "Preview Template"
@handler PreviewSubscribeTemplate
get /preview (PreviewSubscribeTemplateRequest) returns (PreviewSubscribeTemplateResponse)
}

View File

@ -21,7 +21,7 @@ type (
Download int64 `json:"download"` Download int64 `json:"download"`
} }
ServerTotalDataResponse { ServerTotalDataResponse {
OnlineUserIPs int64 `json:"online_user_ips"` OnlineUsers int64 `json:"online_users"`
OnlineServers int64 `json:"online_servers"` OnlineServers int64 `json:"online_servers"`
OfflineServers int64 `json:"offline_servers"` OfflineServers int64 `json:"offline_servers"`
TodayUpload int64 `json:"today_upload"` TodayUpload int64 `json:"today_upload"`

View File

@ -14,17 +14,182 @@ type (
GetMessageLogListRequest { GetMessageLogListRequest {
Page int `form:"page"` Page int `form:"page"`
Size int `form:"size"` Size int `form:"size"`
Type string `form:"type"` Type uint8 `form:"type"`
Platform string `form:"platform,omitempty"` Search string `form:"search,optional"`
To string `form:"to,omitempty"`
Subject string `form:"subject,omitempty"`
Content string `form:"content,omitempty"`
Status int `form:"status,omitempty"`
} }
GetMessageLogListResponse { GetMessageLogListResponse {
Total int64 `json:"total"` Total int64 `json:"total"`
List []MessageLog `json:"list"` List []MessageLog `json:"list"`
} }
FilterLogParams {
Page int `form:"page"`
Size int `form:"size"`
Date string `form:"date,optional"`
Search string `form:"search,optional"`
}
FilterEmailLogResponse {
Total int64 `json:"total"`
List []MessageLog `json:"list"`
}
FilterMobileLogResponse {
Total int64 `json:"total"`
List []MessageLog `json:"list"`
}
SubscribeLog {
UserId int64 `json:"user_id"`
Token string `json:"token"`
UserAgent string `json:"user_agent"`
ClientIP string `json:"client_ip"`
UserSubscribeId int64 `json:"user_subscribe_id"`
Timestamp int64 `json:"timestamp"`
}
FilterSubscribeLogRequest {
FilterLogParams
UserId int64 `form:"user_id,optional"`
UserSubscribeId int64 `form:"user_subscribe_id,optional"`
}
FilterSubscribeLogResponse {
Total int64 `json:"total"`
List []SubscribeLog `json:"list"`
}
LoginLog {
UserId int64 `json:"user_id"`
Method string `json:"method"`
LoginIP string `json:"login_ip"`
UserAgent string `json:"user_agent"`
Success bool `json:"success"`
Timestamp int64 `json:"timestamp"`
}
FilterLoginLogRequest {
FilterLogParams
UserId int64 `form:"user_id,optional"`
}
FilterLoginLogResponse {
Total int64 `json:"total"`
List []LoginLog `json:"list"`
}
RegisterLog {
UserId int64 `json:"user_id"`
AuthMethod string `json:"auth_method"`
Identifier string `json:"identifier"`
RegisterIP string `json:"register_ip"`
UserAgent string `json:"user_agent"`
Timestamp int64 `json:"timestamp"`
}
FilterRegisterLogRequest {
FilterLogParams
UserId int64 `form:"user_id,optional"`
}
FilterRegisterLogResponse {
Total int64 `json:"total"`
List []RegisterLog `json:"list"`
}
ResetSubscribeLog {
Type uint16 `json:"type"`
UserId int64 `json:"user_id"`
UserSubscribeId int64 `json:"user_subscribe_id"`
OrderNo string `json:"order_no,omitempty"`
Timestamp int64 `json:"timestamp"`
}
FilterResetSubscribeLogRequest {
FilterLogParams
UserSubscribeId int64 `form:"user_subscribe_id,optional"`
}
FilterResetSubscribeLogResponse {
Total int64 `json:"total"`
List []ResetSubscribeLog `json:"list"`
}
UserSubscribeTrafficLog {
SubscribeId int64 `json:"subscribe_id"` // Subscribe ID
UserId int64 `json:"user_id"` // User ID
Upload int64 `json:"upload"` // Upload traffic in bytes
Download int64 `json:"download"` // Download traffic in bytes
Total int64 `json:"total"` // Total traffic in bytes (Upload + Download)
Date string `json:"date"` // Date in YYYY-MM-DD format
Details bool `json:"details"` // Whether to show detailed traffic
}
FilterSubscribeTrafficRequest {
FilterLogParams
UserId int64 `form:"user_id,optional"`
UserSubscribeId int64 `form:"user_subscribe_id,optional"`
}
FilterSubscribeTrafficResponse {
Total int64 `json:"total"`
List []UserSubscribeTrafficLog `json:"list"`
}
ServerTrafficLog {
ServerId int64 `json:"server_id"` // Server ID
Upload int64 `json:"upload"` // Upload traffic in bytes
Download int64 `json:"download"` // Download traffic in bytes
Total int64 `json:"total"` // Total traffic in bytes (Upload + Download)
Date string `json:"date"` // Date in YYYY-MM-DD format
Details bool `json:"details"` // Whether to show detailed traffic
}
FilterServerTrafficLogRequest {
FilterLogParams
ServerId int64 `form:"server_id,optional"`
}
FilterServerTrafficLogResponse {
Total int64 `json:"total"`
List []ServerTrafficLog `json:"list"`
}
FilterBalanceLogRequest {
FilterLogParams
UserId int64 `form:"user_id,optional"`
}
FilterBalanceLogResponse {
Total int64 `json:"total"`
List []BalanceLog `json:"list"`
}
FilterCommissionLogRequest {
FilterLogParams
UserId int64 `form:"user_id,optional"`
}
FilterCommissionLogResponse {
Total int64 `json:"total"`
List []CommissionLog `json:"list"`
}
GiftLog {
Type uint16 `json:"type"`
userId int64 `json:"user_id"`
OrderNo string `json:"order_no"`
SubscribeId int64 `json:"subscribe_id"`
Amount int64 `json:"amount"`
Balance int64 `json:"balance"`
Remark string `json:"remark,omitempty"`
Timestamp int64 `json:"timestamp"`
}
FilterGiftLogRequest {
FilterLogParams
UserId int64 `form:"user_id,optional"`
}
FilterGiftLogResponse {
Total int64 `json:"total"`
List []GiftLog `json:"list"`
}
TrafficLogDetails {
Id int64 `json:"id"`
ServerId int64 `json:"server_id"`
UserId int64 `json:"user_id"`
SubscribeId int64 `json:"subscribe_id"`
Download int64 `json:"download"`
Upload int64 `json:"upload"`
Timestamp int64 `json:"timestamp"`
}
FilterTrafficLogDetailsRequest {
FilterLogParams
ServerId int64 `form:"server_id,optional"`
SubscribeId int64 `form:"subscribe_id,optional"`
UserId int64 `form:"user_id,optional"`
}
FilterTrafficLogDetailsResponse {
Total int64 `json:"total"`
List []TrafficLogDetails `json:"list"`
}
LogSetting {
AutoClear *bool `json:"auto_clear"`
ClearDays int64 `json:"clear_days"`
}
) )
@server ( @server (
@ -36,5 +201,61 @@ service ppanel {
@doc "Get message log list" @doc "Get message log list"
@handler GetMessageLogList @handler GetMessageLogList
get /message/list (GetMessageLogListRequest) returns (GetMessageLogListResponse) get /message/list (GetMessageLogListRequest) returns (GetMessageLogListResponse)
@doc "Filter email log"
@handler FilterEmailLog
get /email/list (FilterLogParams) returns (FilterEmailLogResponse)
@doc "Filter mobile log"
@handler FilterMobileLog
get /mobile/list (FilterLogParams) returns (FilterMobileLogResponse)
@doc "Filter subscribe log"
@handler FilterSubscribeLog
get /subscribe/list (FilterSubscribeLogRequest) returns (FilterSubscribeLogResponse)
@doc "Filter login log"
@handler FilterLoginLog
get /login/list (FilterLoginLogRequest) returns (FilterLoginLogResponse)
@doc "Filter register log"
@handler FilterRegisterLog
get /register/list (FilterRegisterLogRequest) returns (FilterRegisterLogResponse)
@doc "Filter reset subscribe log"
@handler FilterResetSubscribeLog
get /subscribe/reset/list (FilterResetSubscribeLogRequest) returns (FilterResetSubscribeLogResponse)
@doc "Filter user subscribe traffic log"
@handler FilterUserSubscribeTrafficLog
get /subscribe/traffic/list (FilterSubscribeTrafficRequest) returns (FilterSubscribeTrafficResponse)
@doc "Filter server traffic log"
@handler FilterServerTrafficLog
get /server/traffic/list (FilterServerTrafficLogRequest) returns (FilterServerTrafficLogResponse)
@doc "Filter balance log"
@handler FilterBalanceLog
get /balance/list (FilterBalanceLogRequest) returns (FilterBalanceLogResponse)
@doc "Filter commission log"
@handler FilterCommissionLog
get /commission/list (FilterCommissionLogRequest) returns (FilterCommissionLogResponse)
@doc "Filter gift log"
@handler FilterGiftLog
get /gift/list (FilterGiftLogRequest) returns (FilterGiftLogResponse)
@doc "Filter traffic log details"
@handler FilterTrafficLogDetails
get /traffic/details (FilterTrafficLogDetailsRequest) returns (FilterTrafficLogDetailsResponse)
@doc "Get log setting"
@handler GetLogSetting
get /setting returns (LogSetting)
@doc "Update log setting"
@handler UpdateLogSetting
post /setting (LogSetting)
} }

167
apis/admin/marketing.api Normal file
View File

@ -0,0 +1,167 @@
syntax = "v1"
info (
title: "Marketing API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
type (
CreateBatchSendEmailTaskRequest {
Subject string `json:"subject"`
Content string `json:"content"`
Scope int8 `json:"scope"`
RegisterStartTime int64 `json:"register_start_time,omitempty"`
RegisterEndTime int64 `json:"register_end_time,omitempty"`
Additional string `json:"additional,omitempty"`
Scheduled int64 `json:"scheduled,omitempty"`
Interval uint8 `json:"interval,omitempty"`
Limit uint64 `json:"limit,omitempty"`
}
BatchSendEmailTask {
Id int64 `json:"id"`
Subject string `json:"subject"`
Content string `json:"content"`
Recipients string `json:"recipients"`
Scope int8 `json:"scope"`
RegisterStartTime int64 `json:"register_start_time"`
RegisterEndTime int64 `json:"register_end_time"`
Additional string `json:"additional"`
Scheduled int64 `json:"scheduled"`
Interval uint8 `json:"interval"`
Limit uint64 `json:"limit"`
Status uint8 `json:"status"`
Errors string `json:"errors"`
Total uint64 `json:"total"`
Current uint64 `json:"current"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
GetBatchSendEmailTaskListRequest {
Page int `form:"page"`
Size int `form:"size"`
Scope *int8 `form:"scope,omitempty"`
Status *uint8 `form:"status,omitempty"`
}
GetBatchSendEmailTaskListResponse {
Total int64 `json:"total"`
List []BatchSendEmailTask `json:"list"`
}
StopBatchSendEmailTaskRequest {
Id int64 `json:"id"`
}
GetPreSendEmailCountRequest {
Scope int8 `json:"scope"`
RegisterStartTime int64 `json:"register_start_time,omitempty"`
RegisterEndTime int64 `json:"register_end_time,omitempty"`
}
GetPreSendEmailCountResponse {
Count int64 `json:"count"`
}
GetBatchSendEmailTaskStatusRequest {
Id int64 `json:"id"`
}
GetBatchSendEmailTaskStatusResponse {
Status uint8 `json:"status"`
Current int64 `json:"current"`
Total int64 `json:"total"`
Errors string `json:"errors"`
}
CreateQuotaTaskRequest {
Subscribers []int64 `json:"subscribers"`
IsActive *bool `json:"is_active"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
ResetTraffic bool `json:"reset_traffic"`
Days uint64 `json:"days"`
GiftType uint8 `json:"gift_type"`
GiftValue uint64 `json:"gift_value"`
}
QuotaTask {
Id int64 `json:"id"`
Subscribers []int64 `json:"subscribers"`
IsActive *bool `json:"is_active"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
ResetTraffic bool `json:"reset_traffic"`
Days uint64 `json:"days"`
GiftType uint8 `json:"gift_type"`
GiftValue uint64 `json:"gift_value"`
Objects []int64 `json:"objects"` // UserSubscribe IDs
Status uint8 `json:"status"`
Total int64 `json:"total"`
Current int64 `json:"current"`
Errors string `json:"errors"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
QueryQuotaTaskPreCountRequest {
Subscribers []int64 `json:"subscribers"`
IsActive *bool `json:"is_active"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
}
QueryQuotaTaskPreCountResponse {
Count int64 `json:"count"`
}
QueryQuotaTaskListRequest {
Page int `form:"page"`
Size int `form:"size"`
Status *uint8 `form:"status,omitempty"`
}
QueryQuotaTaskListResponse {
Total int64 `json:"total"`
List []QuotaTask `json:"list"`
}
QueryQuotaTaskStatusRequest {
Id int64 `json:"id"`
}
QueryQuotaTaskStatusResponse {
Status uint8 `json:"status"`
Current int64 `json:"current"`
Total int64 `json:"total"`
Errors string `json:"errors"`
}
)
@server (
prefix: v1/admin/marketing
group: admin/marketing
middleware: AuthMiddleware
)
service ppanel {
@doc "Create a batch send email task"
@handler CreateBatchSendEmailTask
post /email/batch/send (CreateBatchSendEmailTaskRequest)
@doc "Get batch send email task list"
@handler GetBatchSendEmailTaskList
get /email/batch/list (GetBatchSendEmailTaskListRequest) returns (GetBatchSendEmailTaskListResponse)
@doc "Stop a batch send email task"
@handler StopBatchSendEmailTask
post /email/batch/stop (StopBatchSendEmailTaskRequest)
@doc "Get pre-send email count"
@handler GetPreSendEmailCount
post /email/batch/pre-send-count (GetPreSendEmailCountRequest) returns (GetPreSendEmailCountResponse)
@doc "Get batch send email task status"
@handler GetBatchSendEmailTaskStatus
post /email/batch/status (GetBatchSendEmailTaskStatusRequest) returns (GetBatchSendEmailTaskStatusResponse)
@doc "Create a quota task"
@handler CreateQuotaTask
post /quota/create (CreateQuotaTaskRequest)
@doc "Query quota task pre-count"
@handler QueryQuotaTaskPreCount
post /quota/pre-count (QueryQuotaTaskPreCountRequest) returns (QueryQuotaTaskPreCountResponse)
@doc "Query quota task list"
@handler QueryQuotaTaskList
get /quota/list (QueryQuotaTaskListRequest) returns (QueryQuotaTaskListResponse)
}

View File

@ -11,108 +11,134 @@ info (
import "../types.api" import "../types.api"
type ( type (
GetNodeServerListRequest { ServerOnlineIP {
Page int `form:"page" validate:"required"` IP string `json:"ip"`
Size int `form:"size" validate:"required"` Protocol string `json:"protocol"`
Tags string `form:"tags,omitempty"` }
GroupId int64 `form:"group_id,omitempty"` ServerOnlineUser {
IP []ServerOnlineIP `json:"ip"`
UserId int64 `json:"user_id"`
Subscribe string `json:"subscribe"`
SubscribeId int64 `json:"subscribe_id"`
Traffic int64 `json:"traffic"`
ExpiredAt int64 `json:"expired_at"`
}
ServerStatus {
Cpu float64 `json:"cpu"`
Mem float64 `json:"mem"`
Disk float64 `json:"disk"`
Protocol string `json:"protocol"`
Online []ServerOnlineUser `json:"online"`
Status string `json:"status"`
}
Server {
Id int64 `json:"id"`
Name string `json:"name"`
Country string `json:"country"`
City string `json:"city"`
Address string `json:"address"`
Sort int `json:"sort"`
Protocols []Protocol `json:"protocols"`
LastReportedAt int64 `json:"last_reported_at"`
Status ServerStatus `json:"status"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
CreateServerRequest {
Name string `json:"name"`
Country string `json:"country,omitempty"`
City string `json:"city,omitempty"`
Address string `json:"address"`
Sort int `json:"sort,omitempty"`
Protocols []Protocol `json:"protocols"`
}
UpdateServerRequest {
Id int64 `json:"id"`
Name string `json:"name"`
Country string `json:"country,omitempty"`
City string `json:"city,omitempty"`
Address string `json:"address"`
Sort int `json:"sort,omitempty"`
Protocols []Protocol `json:"protocols"`
}
DeleteServerRequest {
Id int64 `json:"id"`
}
FilterServerListRequest {
Page int `form:"page"`
Size int `form:"size"`
Search string `form:"search,omitempty"` Search string `form:"search,omitempty"`
} }
GetNodeServerListResponse { FilterServerListResponse {
Total int64 `json:"total"` Total int64 `json:"total"`
List []Server `json:"list"` List []Server `json:"list"`
} }
UpdateNodeRequest { GetServerProtocolsRequest {
Id int64 `json:"id" validate:"required"` Id int64 `form:"id"`
}
GetServerProtocolsResponse {
Protocols []Protocol `json:"protocols"`
}
Node {
Id int64 `json:"id"`
Name string `json:"name"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
Country string `json:"country"` Port uint16 `json:"port"`
City string `json:"city"` Address string `json:"address"`
Name string `json:"name" validate:"required"` ServerId int64 `json:"server_id"`
ServerAddr string `json:"server_addr" validate:"required"` Protocol string `json:"protocol"`
RelayMode string `json:"relay_mode"` Enabled *bool `json:"enabled"`
RelayNode []NodeRelay `json:"relay_node"` Sort int `json:"sort,omitempty"`
SpeedLimit int `json:"speed_limit"` CreatedAt int64 `json:"created_at"`
TrafficRatio float32 `json:"traffic_ratio"` UpdatedAt int64 `json:"updated_at"`
GroupId int64 `json:"group_id"`
Protocol string `json:"protocol" validate:"required"`
Config interface{} `json:"config" validate:"required"`
Enable *bool `json:"enable"`
Sort int64 `json:"sort"`
} }
CreateNodeRequest { CreateNodeRequest {
Name string `json:"name" validate:"required"` Name string `json:"name"`
Tags []string `json:"tags"` Tags []string `json:"tags,omitempty"`
Country string `json:"country"` Port uint16 `json:"port"`
City string `json:"city"` Address string `json:"address"`
ServerAddr string `json:"server_addr" validate:"required"` ServerId int64 `json:"server_id"`
RelayMode string `json:"relay_mode"` Protocol string `json:"protocol"`
RelayNode []NodeRelay `json:"relay_node"` Enabled *bool `json:"enabled"`
SpeedLimit int `json:"speed_limit"` }
TrafficRatio float32 `json:"traffic_ratio"` UpdateNodeRequest {
GroupId int64 `json:"group_id"` Id int64 `json:"id"`
Protocol string `json:"protocol" validate:"required"` Name string `json:"name"`
Config interface{} `json:"config" validate:"required"` Tags []string `json:"tags,omitempty"`
Port uint16 `json:"port"`
Address string `json:"address"`
ServerId int64 `json:"server_id"`
Protocol string `json:"protocol"`
Enabled *bool `json:"enabled"`
}
ToggleNodeStatusRequest {
Id int64 `json:"id"`
Enable *bool `json:"enable"` Enable *bool `json:"enable"`
Sort int64 `json:"sort"`
} }
DeleteNodeRequest { DeleteNodeRequest {
Id int64 `json:"id" validate:"required"` Id int64 `json:"id"`
} }
GetNodeGroupListResponse { FilterNodeListRequest {
Page int `form:"page"`
Size int `form:"size"`
Search string `form:"search,omitempty"`
}
FilterNodeListResponse {
Total int64 `json:"total"` Total int64 `json:"total"`
List []ServerGroup `json:"list"` List []Node `json:"list"`
} }
CreateNodeGroupRequest { HasMigrateSeverNodeResponse {
Name string `json:"name" validate:"required"` HasMigrate bool `json:"has_migrate"`
Description string `json:"description"`
} }
UpdateNodeGroupRequest { MigrateServerNodeResponse {
Id int64 `json:"id" validate:"required"` Succee uint64 `json:"succee"`
Name string `json:"name" validate:"required"` Fail uint64 `json:"fail"`
Description string `json:"description"` Message string `json:"message,omitempty"`
} }
DeleteNodeGroupRequest { ResetSortRequest {
Id int64 `json:"id" validate:"required"`
}
BatchDeleteNodeRequest {
Ids []int64 `json:"ids" validate:"required"`
}
BatchDeleteNodeGroupRequest {
Ids []int64 `json:"ids" validate:"required"`
}
GetNodeDetailRequest {
Id int64 `form:"id" validate:"required"`
}
NodeSortRequest {
Sort []SortItem `json:"sort"` Sort []SortItem `json:"sort"`
} }
CreateRuleGroupRequest { QueryNodeTagResponse {
Name string `json:"name" validate:"required"`
Icon string `json:"icon"`
Type string `json:"type"`
Tags []string `json:"tags"`
Rules string `json:"rules"`
Default bool `json:"default"`
Enable bool `json:"enable"`
}
UpdateRuleGroupRequest {
Id int64 `json:"id" validate:"required"`
Icon string `json:"icon"`
Type string `json:"type"`
Name string `json:"name" validate:"required"`
Tags []string `json:"tags"`
Rules string `json:"rules"`
Default bool `json:"default"`
Enable bool `json:"enable"`
}
DeleteRuleGroupRequest {
Id int64 `json:"id" validate:"required"`
}
GetRuleGroupResponse {
Total int64 `json:"total"`
List []ServerRuleGroup `json:"list"`
}
GetNodeTagListResponse {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
) )
@ -123,72 +149,64 @@ type (
middleware: AuthMiddleware middleware: AuthMiddleware
) )
service ppanel { service ppanel {
@doc "Get node tag list" @doc "Create Server"
@handler GetNodeTagList @handler CreateServer
get /tag/list returns (GetNodeTagListResponse) post /create (CreateServerRequest)
@doc "Get node list" @doc "Update Server"
@handler GetNodeList @handler UpdateServer
get /list (GetNodeServerListRequest) returns (GetNodeServerListResponse) post /update (UpdateServerRequest)
@doc "Get node detail" @doc "Delete Server"
@handler GetNodeDetail @handler DeleteServer
get /detail (GetNodeDetailRequest) returns (Server) post /delete (DeleteServerRequest)
@doc "Update node" @doc "Filter Server List"
@handler UpdateNode @handler FilterServerList
put / (UpdateNodeRequest) get /list (FilterServerListRequest) returns (FilterServerListResponse)
@doc "Create node" @doc "Get Server Protocols"
@handler GetServerProtocols
get /protocols (GetServerProtocolsRequest) returns (GetServerProtocolsResponse)
@doc "Create Node"
@handler CreateNode @handler CreateNode
post / (CreateNodeRequest) post /node/create (CreateNodeRequest)
@doc "Delete node" @doc "Update Node"
@handler UpdateNode
post /node/update (UpdateNodeRequest)
@doc "Delete Node"
@handler DeleteNode @handler DeleteNode
delete / (DeleteNodeRequest) post /node/delete (DeleteNodeRequest)
@doc "Batch delete node" @doc "Filter Node List"
@handler BatchDeleteNode @handler FilterNodeList
delete /batch (BatchDeleteNodeRequest) get /node/list (FilterNodeListRequest) returns (FilterNodeListResponse)
@doc "Get node group list" @doc "Toggle Node Status"
@handler GetNodeGroupList @handler ToggleNodeStatus
get /group/list returns (GetNodeGroupListResponse) post /node/status/toggle (ToggleNodeStatusRequest)
@doc "Create node group" @doc "Check if there is any server or node to migrate"
@handler CreateNodeGroup @handler HasMigrateSeverNode
post /group (CreateNodeGroupRequest) get /migrate/has returns (HasMigrateSeverNodeResponse)
@doc "Update node group" @doc "Migrate server and node data to new database"
@handler UpdateNodeGroup @handler MigrateServerNode
put /group (UpdateNodeGroupRequest) post /migrate/run returns (MigrateServerNodeResponse)
@doc "Delete node group" @doc "Reset server sort"
@handler DeleteNodeGroup @handler ResetSortWithServer
delete /group (DeleteNodeGroupRequest) post /server/sort (ResetSortRequest)
@doc "Batch delete node group" @doc "Reset node sort"
@handler BatchDeleteNodeGroup @handler ResetSortWithNode
delete /group/batch (BatchDeleteNodeGroupRequest) post /node/sort (ResetSortRequest)
@doc "Node sort " @doc "Query all node tags"
@handler NodeSort @handler QueryNodeTag
post /sort (NodeSortRequest) get /node/tags returns (QueryNodeTagResponse)
@doc "Create rule group"
@handler CreateRuleGroup
post /rule_group (CreateRuleGroupRequest)
@doc "Update rule group"
@handler UpdateRuleGroup
put /rule_group (UpdateRuleGroupRequest)
@doc "Delete rule group"
@handler DeleteRuleGroup
delete /rule_group (DeleteRuleGroupRequest)
@doc "Get rule group list"
@handler GetRuleGroupList
get /rule_group_list returns (GetRuleGroupResponse)
} }

View File

@ -35,6 +35,7 @@ type (
} }
CreateSubscribeRequest { CreateSubscribeRequest {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Language string `json:"language"`
Description string `json:"description"` Description string `json:"description"`
UnitPrice int64 `json:"unit_price"` UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"` UnitTime string `json:"unit_time"`
@ -45,9 +46,8 @@ type (
SpeedLimit int64 `json:"speed_limit"` SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"` DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"` Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"` Nodes []int64 `json:"nodes"`
ServerGroup []int64 `json:"server_group"` NodeTags []string `json:"node_tags"`
Server []int64 `json:"server"`
Show *bool `json:"show"` Show *bool `json:"show"`
Sell *bool `json:"sell"` Sell *bool `json:"sell"`
DeductionRatio int64 `json:"deduction_ratio"` DeductionRatio int64 `json:"deduction_ratio"`
@ -58,6 +58,7 @@ type (
UpdateSubscribeRequest { UpdateSubscribeRequest {
Id int64 `json:"id" validate:"required"` Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Language string `json:"language"`
Description string `json:"description"` Description string `json:"description"`
UnitPrice int64 `json:"unit_price"` UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"` UnitTime string `json:"unit_time"`
@ -68,9 +69,8 @@ type (
SpeedLimit int64 `json:"speed_limit"` SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"` DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"` Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"` Nodes []int64 `json:"nodes"`
ServerGroup []int64 `json:"server_group"` NodeTags []string `json:"node_tags"`
Server []int64 `json:"server"`
Show *bool `json:"show"` Show *bool `json:"show"`
Sell *bool `json:"sell"` Sell *bool `json:"sell"`
Sort int64 `json:"sort"` Sort int64 `json:"sort"`
@ -85,7 +85,7 @@ type (
GetSubscribeListRequest { GetSubscribeListRequest {
Page int64 `form:"page" validate:"required"` Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"` Size int64 `form:"size" validate:"required"`
GroupId int64 `form:"group_id,omitempty"` Language string `form:"language,omitempty"`
Search string `form:"search,omitempty"` Search string `form:"search,omitempty"`
} }
SubscribeItem { SubscribeItem {

View File

@ -11,50 +11,6 @@ info (
import "../types.api" import "../types.api"
type ( type (
// Update application request
UpdateApplicationRequest {
Id int64 `json:"id" validate:"required"`
Icon string `json:"icon"`
Name string `json:"name"`
Description string `json:"description"`
SubscribeType string `json:"subscribe_type"`
Platform ApplicationPlatform `json:"platform"`
}
// Create application request
CreateApplicationRequest {
Icon string `json:"icon"`
Name string `json:"name"`
Description string `json:"description"`
SubscribeType string `json:"subscribe_type"`
Platform ApplicationPlatform `json:"platform"`
}
// Update application request
UpdateApplicationVersionRequest {
Id int64 `json:"id" validate:"required"`
Url string `json:"url"`
Version string `json:"version" validate:"required"`
Description string `json:"description"`
Platform string `json:"platform" validate:"required,oneof=windows mac linux android ios harmony"`
IsDefault bool `json:"is_default"`
ApplicationId int64 `json:"application_id" validate:"required"`
}
// Create application request
CreateApplicationVersionRequest {
Url string `json:"url"`
Version string `json:"version" validate:"required"`
Description string `json:"description"`
Platform string `json:"platform" validate:"required,oneof=windows mac linux android ios harmony"`
IsDefault bool `json:"is_default"`
ApplicationId int64 `json:"application_id" validate:"required"`
}
// Delete application request
DeleteApplicationRequest {
Id int64 `json:"id" validate:"required"`
}
// Delete application request
DeleteApplicationVersionRequest {
Id int64 `json:"id" validate:"required"`
}
GetNodeMultiplierResponse { GetNodeMultiplierResponse {
Periods []TimePeriod `json:"periods"` Periods []TimePeriod `json:"periods"`
} }
@ -62,6 +18,10 @@ type (
SetNodeMultiplierRequest { SetNodeMultiplierRequest {
Periods []TimePeriod `json:"periods"` Periods []TimePeriod `json:"periods"`
} }
PreViewNodeMultiplierResponse {
CurrentTime string `json:"current_time"`
Ratio float32 `json:"ratio"`
}
) )
@server ( @server (
@ -86,46 +46,6 @@ service ppanel {
@handler UpdateSubscribeConfig @handler UpdateSubscribeConfig
put /subscribe_config (SubscribeConfig) put /subscribe_config (SubscribeConfig)
@doc "Get subscribe type"
@handler GetSubscribeType
get /subscribe_type returns (SubscribeType)
@doc "update application config"
@handler UpdateApplicationConfig
put /application_config (ApplicationConfig)
@doc "get application config"
@handler GetApplicationConfig
get /application_config returns (ApplicationConfig)
@doc "Get application"
@handler GetApplication
get /application returns (ApplicationResponse)
@doc "Update application"
@handler UpdateApplication
put /application (UpdateApplicationRequest)
@doc "Create application"
@handler CreateApplication
post /application (CreateApplicationRequest)
@doc "Delete application"
@handler DeleteApplication
delete /application (DeleteApplicationRequest)
@doc "Update application version"
@handler UpdateApplicationVersion
put /application_version (UpdateApplicationVersionRequest)
@doc "Create application version"
@handler CreateApplicationVersion
post /application_version (CreateApplicationVersionRequest)
@doc "Delete application"
@handler DeleteApplicationVersion
delete /application_version (DeleteApplicationVersionRequest)
@doc "Get register config" @doc "Get register config"
@handler GetRegisterConfig @handler GetRegisterConfig
get /register_config returns (RegisterConfig) get /register_config returns (RegisterConfig)
@ -201,5 +121,9 @@ service ppanel {
@doc "Update Verify Code Config" @doc "Update Verify Code Config"
@handler UpdateVerifyCodeConfig @handler UpdateVerifyCodeConfig
put /verify_code_config (VerifyCodeConfig) put /verify_code_config (VerifyCodeConfig)
@doc "PreView Node Multiplier"
@handler PreViewNodeMultiplier
get /node_multiplier/preview returns (PreViewNodeMultiplierResponse)
} }

View File

@ -14,6 +14,9 @@ type (
LogResponse { LogResponse {
List interface{} `json:"list"` List interface{} `json:"list"`
} }
VersionResponse {
Version string `json:"version"`
}
) )
@server ( @server (
@ -29,5 +32,9 @@ service ppanel {
@doc "Restart System" @doc "Restart System"
@handler RestartSystem @handler RestartSystem
get /restart get /restart
@doc "Get Version"
@handler GetVersion
get /version returns (VersionResponse)
} }

View File

@ -37,11 +37,12 @@ type (
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Balance int64 `json:"balance"` Balance int64 `json:"balance"`
Commission int64 `json:"commission"` Commission int64 `json:"commission"`
ReferralPercentage uint8 `json:"referral_percentage"`
OnlyFirstPurchase bool `json:"only_first_purchase"`
GiftAmount int64 `json:"gift_amount"` GiftAmount int64 `json:"gift_amount"`
Telegram int64 `json:"telegram"` Telegram int64 `json:"telegram"`
ReferCode string `json:"refer_code"` ReferCode string `json:"refer_code"`
RefererId int64 `json:"referer_id"` RefererId int64 `json:"referer_id"`
Remark string `json:"remark"`
Enable bool `json:"enable"` Enable bool `json:"enable"`
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`
} }
@ -59,6 +60,8 @@ type (
Password string `json:"password"` Password string `json:"password"`
ProductId int64 `json:"product_id"` ProductId int64 `json:"product_id"`
Duration int64 `json:"duration"` Duration int64 `json:"duration"`
ReferralPercentage uint8 `json:"referral_percentage"`
OnlyFirstPurchase bool `json:"only_first_purchase"`
RefererUser string `json:"referer_user"` RefererUser string `json:"referer_user"`
ReferCode string `json:"refer_code"` ReferCode string `json:"refer_code"`
Balance int64 `json:"balance"` Balance int64 `json:"balance"`
@ -165,6 +168,15 @@ type (
List []UserLoginLog `json:"list"` List []UserLoginLog `json:"list"`
Total int64 `json:"total"` Total int64 `json:"total"`
} }
GetUserSubscribeResetTrafficLogsRequest {
Page int `form:"page"`
Size int `form:"size"`
UserSubscribeId int64 `form:"user_subscribe_id"`
}
GetUserSubscribeResetTrafficLogsResponse {
List []ResetSubscribeTrafficLog `json:"list"`
Total int64 `json:"total"`
}
DeleteUserSubscribeRequest { DeleteUserSubscribeRequest {
UserSubscribeId int64 `json:"user_subscribe_id"` UserSubscribeId int64 `json:"user_subscribe_id"`
} }
@ -252,6 +264,10 @@ service ppanel {
@handler GetUserSubscribeLogs @handler GetUserSubscribeLogs
get /subscribe/logs (GetUserSubscribeLogsRequest) returns (GetUserSubscribeLogsResponse) get /subscribe/logs (GetUserSubscribeLogsRequest) returns (GetUserSubscribeLogsResponse)
@doc "Get user subcribe reset traffic logs"
@handler GetUserSubscribeResetTrafficLogs
get /subscribe/reset/logs (GetUserSubscribeResetTrafficLogsRequest) returns (GetUserSubscribeResetTrafficLogsResponse)
@doc "Get user subcribe traffic logs" @doc "Get user subcribe traffic logs"
@handler GetUserSubscribeTrafficLogs @handler GetUserSubscribeTrafficLogs
get /subscribe/traffic_logs (GetUserSubscribeTrafficLogsRequest) returns (GetUserSubscribeTrafficLogsResponse) get /subscribe/traffic_logs (GetUserSubscribeTrafficLogsRequest) returns (GetUserSubscribeTrafficLogsResponse)

View File

@ -1,23 +0,0 @@
syntax = "v1"
info (
title: "Announcement API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
@server (
prefix: v1/app/announcement
group: app/announcement
middleware: AppMiddleware,AuthMiddleware
)
service ppanel {
@doc "Query announcement"
@handler QueryAnnouncement
get /list (QueryAnnouncementRequest) returns (QueryAnnouncementResponse)
}

View File

@ -1,104 +0,0 @@
syntax = "v1"
info (
title: "App Auth Api"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import (
"../types.api"
)
type (
AppAuthCheckRequest {
Method string `json:"method" validate:"required" validate:"required,oneof=device email mobile"`
Account string `json:"account"`
Identifier string `json:"identifier" validate:"required"`
UserAgent string `json:"user_agent" validate:"required,oneof=windows mac linux android ios harmony"`
AreaCode string `json:"area_code"`
}
AppAuthCheckResponse {
Status bool
}
AppAuthRequest {
Method string `json:"method" validate:"required" validate:"required,oneof=device email mobile"`
Account string `json:"account"`
Password string `json:"password"`
Identifier string `json:"identifier" validate:"required"`
UserAgent string `json:"user_agent" validate:"required,oneof=windows mac linux android ios harmony"`
Code string `json:"code"`
Invite string `json:"invite"`
AreaCode string `json:"area_code"`
CfToken string `json:"cf_token,optional"`
}
AppAuthRespone {
Token string `json:"token"`
}
AppSendCodeRequest {
Method string `json:"method" validate:"required" validate:"required,oneof=email mobile"`
Account string `json:"account"`
AreaCode string `json:"area_code"`
CfToken string `json:"cf_token,optional"`
}
AppSendCodeRespone {
Status bool `json:"status"`
Code string `json:"code,omitempty"`
}
AppConfigRequest {
UserAgent string `json:"user_agent" validate:"required,oneof=windows mac linux android ios harmony"`
}
AppConfigResponse {
EncryptionKey string `json:"encryption_key"`
EncryptionMethod string `json:"encryption_method"`
Domains []string `json:"domains"`
StartupPicture string `json:"startup_picture"`
StartupPictureSkipTime int64 `json:"startup_picture_skip_time"`
Application AppInfo `json:"applications"`
OfficialEmail string `json:"official_email"`
OfficialWebsite string `json:"official_website"`
OfficialTelegram string `json:"official_telegram"`
OfficialTelephone string `json:"official_telephone"`
InvitationLink string `json:"invitation_link"`
KrWebsiteId string `json:"kr_website_id"`
}
AppInfo {
Id int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Url string `json:"url"`
Version string `json:"version"`
VersionDescription string `json:"version_description"`
IsDefault bool `json:"is_default"`
}
)
@server (
prefix: v1/app/auth
group: app/auth
middleware: AppMiddleware
)
service ppanel {
@doc "Check Account"
@handler Check
post /check (AppAuthCheckRequest) returns (AppAuthCheckResponse)
@doc "Login"
@handler Login
post /login (AppAuthRequest) returns (AppAuthRespone)
@doc "Register"
@handler Register
post /register (AppAuthRequest) returns (AppAuthRespone)
@doc "Reset Password"
@handler ResetPassword
post /reset_password (AppAuthRequest) returns (AppAuthRespone)
@doc "GetAppConfig"
@handler GetAppConfig
post /config (AppConfigRequest) returns (AppConfigResponse)
}

View File

@ -1,27 +0,0 @@
syntax = "v1"
info (
title: "Document API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
@server (
prefix: v1/app/document
group: app/document
middleware: AppMiddleware,AuthMiddleware
)
service ppanel {
@doc "Get document list"
@handler QueryDocumentList
get /list returns (QueryDocumentListResponse)
@doc "Get document detail"
@handler QueryDocumentDetail
get /detail (QueryDocumentDetailRequest) returns (Document)
}

View File

@ -1,40 +0,0 @@
syntax = "v1"
info (
title: "App Node Api"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
AppRuleGroupListResponse {
Total int64 `json:"total"`
List []ServerRuleGroup `json:"list"`
}
AppUserSubscbribeNodeRequest {
Id int64 `form:"id" validate:"required"`
}
AppUserSubscbribeNodeResponse {
List []AppUserSubscbribeNode `json:"list"`
}
)
@server (
prefix: v1/app/node
group: app/node
middleware: AppMiddleware,AuthMiddleware
)
service ppanel {
@doc "Get Node list"
@handler GetNodeList
get /list (AppUserSubscbribeNodeRequest) returns (AppUserSubscbribeNodeResponse)
@doc "Get rule group list"
@handler GetRuleGroupList
get /rule_group_list returns (AppRuleGroupListResponse)
}

View File

@ -1,57 +0,0 @@
syntax = "v1"
info (
title: "Order API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import (
"../types.api"
)
@server (
prefix: v1/app/order
group: app/order
middleware: AppMiddleware,AuthMiddleware
)
service ppanel {
@doc "Pre create order"
@handler PreCreateOrder
post /pre (PurchaseOrderRequest) returns (PreOrderResponse)
@doc "purchase Subscription"
@handler Purchase
post /purchase (PurchaseOrderRequest) returns (PurchaseOrderResponse)
@doc "Renewal Subscription"
@handler Renewal
post /renewal (RenewalOrderRequest) returns (RenewalOrderResponse)
@doc "Reset traffic"
@handler ResetTraffic
post /reset (ResetTrafficOrderRequest) returns (ResetTrafficOrderResponse)
@doc "Recharge"
@handler Recharge
post /recharge (RechargeOrderRequest) returns (RechargeOrderResponse)
@doc "Checkout order"
@handler CheckoutOrder
post /checkout (CheckoutOrderRequest) returns (CheckoutOrderResponse)
@doc "Close order"
@handler CloseOrder
post /close (CloseOrderRequest)
@doc "Get order"
@handler QueryOrderDetail
get /detail (QueryOrderDetailRequest) returns (OrderDetail)
@doc "Get order list"
@handler QueryOrderList
get /list (QueryOrderListRequest) returns (QueryOrderListResponse)
}

View File

@ -1,23 +0,0 @@
syntax = "v1"
info (
title: "payment API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
@server (
prefix: v1/app/payment
group: app/payment
middleware: AppMiddleware,AuthMiddleware
)
service ppanel {
@doc "Get available payment methods"
@handler GetAvailablePaymentMethods
get /methods returns (GetAvailablePaymentMethodsResponse)
}

View File

@ -1,65 +0,0 @@
syntax = "v1"
info (
title: "Subscribe API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
QueryUserSubscribeResp {
Data []UserSubscribeData `json:"data"`
}
UserSubscribeData {
SubscribeId int64 `json:"subscribe_id"`
UserSubscribeId int64 `json:"user_subscribe_id"`
}
UserSubscribeResetPeriodRequest {
UserSubscribeId int64 `json:"user_subscribe_id"`
}
UserSubscribeResetPeriodResponse {
Status bool `json:"status"`
}
AppUserSubscribeRequest {
ContainsNodes *bool `form:"contains_nodes"`
}
AppUserSubscbribeResponse {
List []AppUserSubcbribe `json:"list"`
}
)
@server (
prefix: v1/app/subscribe
group: app/subscribe
middleware: AppMiddleware,AuthMiddleware
)
service ppanel {
@doc "Get subscribe list"
@handler QuerySubscribeList
get /list returns (QuerySubscribeListResponse)
@doc "Get subscribe group list"
@handler QuerySubscribeGroupList
get /group/list returns (QuerySubscribeGroupListResponse)
@doc "Get application config"
@handler QueryApplicationConfig
get /application/config returns (ApplicationResponse)
@doc "Get Already subscribed to package"
@handler QueryUserAlreadySubscribe
get /user/already_subscribe returns (QueryUserSubscribeResp)
@doc "Get Available subscriptions for users"
@handler QueryUserAvailableUserSubscribe
get /user/available_subscribe (AppUserSubscribeRequest) returns (AppUserSubscbribeResponse)
@doc "Reset user subscription period"
@handler ResetUserSubscribePeriod
post /reset/period (UserSubscribeResetPeriodRequest) returns (UserSubscribeResetPeriodResponse)
}

View File

@ -1,86 +0,0 @@
syntax = "v1"
info (
title: "App User Api"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import (
"../types.api"
)
type (
UserInfoResponse {
Id int64 `json:"id"`
Balance int64 `json:"balance"`
Email string `json:"email"`
RefererId int64 `json:"referer_id"`
ReferCode string `json:"refer_code"`
Avatar string `json:"avatar"`
AreaCode string `json:"area_code"`
Telephone string `json:"telephone"`
Devices []UserDevice `json:"devices"`
AuthMethods []UserAuthMethod `json:"auth_methods"`
}
UpdatePasswordRequeset {
Password string `json:"password"`
NewPassword string `json:"new_password"`
}
DeleteAccountRequest {
Method string `json:"method" validate:"required" validate:"required,oneof=email telephone device"`
Code string `json:"code"`
}
GetUserOnlineTimeStatisticsResponse {
WeeklyStats []WeeklyStat `json:"weekly_stats"`
ConnectionRecords ConnectionRecords `json:"connection_records"`
}
WeeklyStat {
Day int `json:"day"`
DayName string `json:"day_name"`
Hours float64 `json:"hours"`
}
ConnectionRecords {
CurrentContinuousDays int64 `json:"current_continuous_days"`
HistoryContinuousDays int64 `json:"history_continuous_days"`
LongestSingleConnection int64 `json:"longest_single_connection"`
}
)
@server (
prefix: v1/app/user
group: app/user
middleware: AppMiddleware,AuthMiddleware
)
service ppanel {
@doc "query user info"
@handler QueryUserInfo
get /info returns (UserInfoResponse)
@doc "Update Password "
@handler UpdatePassword
put /password (UpdatePasswordRequeset)
@doc "Delete Account"
@handler DeleteAccount
delete /account (DeleteAccountRequest)
@doc "Get user subcribe traffic logs"
@handler GetUserSubscribeTrafficLogs
get /subscribe/traffic_logs (GetUserSubscribeTrafficLogsRequest) returns (GetUserSubscribeTrafficLogsResponse)
@doc "Get user online time total"
@handler GetUserOnlineTimeStatistics
get /online_time/statistics returns (GetUserOnlineTimeStatisticsResponse)
@doc "Query User Affiliate List"
@handler QueryUserAffiliateList
get /affiliate/list (QueryUserAffiliateListRequest) returns (QueryUserAffiliateListResponse)
@doc "Query User Affiliate Count"
@handler QueryUserAffiliate
get /affiliate/count returns (QueryUserAffiliateCountResponse)
}

View File

@ -1,21 +0,0 @@
syntax = "v1"
info (
title: "App Heartbeat Api"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
@server (
prefix: v1/app/ws
group: app/ws
middleware: AuthMiddleware
)
service ppanel {
@doc "App heartbeat"
@handler AppWs
get /:userid/:identifier
}

View File

@ -65,6 +65,7 @@ type (
TelephoneAreaCode string `json:"telephone_area_code" validate:"required"` TelephoneAreaCode string `json:"telephone_area_code" validate:"required"`
Password string `json:"password"` Password string `json:"password"`
IP string `header:"X-Original-Forwarded-For"` IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"` CfToken string `json:"cf_token,optional"`
} }
// Check user is exist request // Check user is exist request
@ -84,6 +85,7 @@ type (
Invite string `json:"invite,optional"` Invite string `json:"invite,optional"`
Code string `json:"code,optional"` Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"` IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"` CfToken string `json:"cf_token,optional"`
} }
// User login response // User login response
@ -93,6 +95,7 @@ type (
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
Code string `json:"code,optional"` Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"` IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"` CfToken string `json:"cf_token,optional"`
} }
AppleLoginCallbackRequest { AppleLoginCallbackRequest {

View File

@ -35,10 +35,6 @@ type (
GetTosResponse { GetTosResponse {
TosContent string `json:"tos_content"` TosContent string `json:"tos_content"`
} }
GetAppcationResponse {
Config ApplicationConfig `json:"config"`
Applications []ApplicationResponseInfo `json:"applications"`
}
// GetCodeRequest Get code request // GetCodeRequest Get code request
SendCodeRequest { SendCodeRequest {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
@ -78,6 +74,19 @@ type (
CheckVerificationCodeRespone { CheckVerificationCodeRespone {
Status bool `json:"status"` Status bool `json:"status"`
} }
SubscribeClient {
Id int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
Scheme string `json:"scheme,omitempty"`
IsDefault bool `json:"is_default"`
DownloadLink DownloadLink `json:"download_link,omitempty"`
}
GetSubscribeClientResponse {
Total int64 `json:"total"`
List []SubscribeClient `json:"list"`
}
) )
@server ( @server (
@ -89,10 +98,6 @@ service ppanel {
@handler GetGlobalConfig @handler GetGlobalConfig
get /site/config returns (GetGlobalConfigResponse) get /site/config returns (GetGlobalConfigResponse)
@doc "Get Tos Content"
@handler GetApplication
get /application returns (GetAppcationResponse)
@doc "Get Tos Content" @doc "Get Tos Content"
@handler GetTos @handler GetTos
get /site/tos returns (GetTosResponse) get /site/tos returns (GetTosResponse)
@ -120,5 +125,9 @@ service ppanel {
@doc "Check verification code" @doc "Check verification code"
@handler CheckVerificationCode @handler CheckVerificationCode
post /check_verification_code (CheckVerificationCodeRequest) returns (CheckVerificationCodeRespone) post /check_verification_code (CheckVerificationCodeRequest) returns (CheckVerificationCodeRespone)
@doc "Get Client"
@handler GetClient
get /client returns (GetSubscribeClientResponse)
} }

View File

@ -11,6 +11,10 @@ info (
import "../types.api" import "../types.api"
type ( type (
OnlineUser {
SID int64 `json:"uid"`
IP string `json:"ip"`
}
ShadowsocksProtocol { ShadowsocksProtocol {
Port int `json:"port"` Port int `json:"port"`
Method string `json:"method"` Method string `json:"method"`
@ -89,6 +93,20 @@ type (
ServerCommon ServerCommon
Users []OnlineUser `json:"users"` Users []OnlineUser `json:"users"`
} }
QueryServerConfigRequest {
ServerID int64 `path:"server_id"`
SecretKey string `form:"secret_key"`
Protocols []string `form:"protocols,omitempty"`
}
QueryServerConfigResponse {
TrafficReportThreshold int64 `json:"traffic_report_threshold"`
IPStrategy string `json:"ip_strategy"`
DNS []NodeDNS `json:"dns"`
Block []string `json:"block"`
Outbound []NodeOutbound `json:"outbound"`
Protocols []Protocol `json:"protocols"`
Total int64 `json:"total"`
}
) )
@server ( @server (
@ -118,3 +136,13 @@ service ppanel {
post /online (OnlineUsersRequest) post /online (OnlineUsersRequest)
} }
@server (
prefix: v2/server
group: server
)
service ppanel {
@doc "Get Server Protocol Config"
@handler QueryServerProtocolConfig
get /:server_id (QueryServerConfigRequest) returns (QueryServerConfigResponse)
}

View File

@ -25,6 +25,9 @@ type (
PortalPurchaseResponse { PortalPurchaseResponse {
OrderNo string `json:"order_no"` OrderNo string `json:"order_no"`
} }
GetSubscriptionRequest {
Language string `form:"language"`
}
GetSubscriptionResponse { GetSubscriptionResponse {
List []Subscribe `json:"list"` List []Subscribe `json:"list"`
} }
@ -75,7 +78,7 @@ service ppanel {
@doc "Get Subscription" @doc "Get Subscription"
@handler GetSubscription @handler GetSubscription
get /subscribe returns (GetSubscriptionResponse) get /subscribe (GetSubscriptionRequest) returns (GetSubscriptionResponse)
@doc "Pre Purchase Order" @doc "Pre Purchase Order"
@handler PrePurchaseOrder @handler PrePurchaseOrder

View File

@ -10,6 +10,12 @@ info (
import "../types.api" import "../types.api"
type (
QuerySubscribeListRequest {
Language string `form:"language"`
}
)
@server ( @server (
prefix: v1/public/subscribe prefix: v1/public/subscribe
group: public/subscribe group: public/subscribe
@ -18,14 +24,6 @@ import "../types.api"
service ppanel { service ppanel {
@doc "Get subscribe list" @doc "Get subscribe list"
@handler QuerySubscribeList @handler QuerySubscribeList
get /list returns (QuerySubscribeListResponse) get /list (QuerySubscribeListRequest) returns (QuerySubscribeListResponse)
@doc "Get subscribe group list"
@handler QuerySubscribeGroupList
get /group/list returns (QuerySubscribeGroupListResponse)
@doc "Get application config"
@handler QueryApplicationConfig
get /application/config returns (ApplicationResponse)
} }

View File

@ -25,16 +25,9 @@ type (
Total int64 `json:"total"` Total int64 `json:"total"`
} }
QueryUserBalanceLogListResponse { QueryUserBalanceLogListResponse {
List []UserBalanceLog `json:"list"` List []BalanceLog `json:"list"`
Total int64 `json:"total"` Total int64 `json:"total"`
} }
CommissionLog {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
OrderNo string `json:"order_no"`
Amount int64 `json:"amount"`
CreatedAt int64 `json:"created_at"`
}
QueryUserCommissionLogListRequest { QueryUserCommissionLogListRequest {
Page int `form:"page"` Page int `form:"page"`
Size int `form:"size"` Size int `form:"size"`

View File

@ -24,5 +24,7 @@ import (
"./admin/auth.api" "./admin/auth.api"
"./admin/log.api" "./admin/log.api"
"./admin/ads.api" "./admin/ads.api"
"./admin/marketing.api"
"./admin/application.api"
) )

View File

@ -1,22 +0,0 @@
syntax = "v1"
info (
title: "App API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import (
"./app/auth.api"
"./app/user.api"
"./app/node.api"
"./app/ws.api"
"./app/order.api"
"./app/announcement.api"
"./app/payment.api"
"./app/document.api"
"./app/subscribe.api"
)

View File

@ -14,6 +14,8 @@ type (
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Balance int64 `json:"balance"` Balance int64 `json:"balance"`
Commission int64 `json:"commission"` Commission int64 `json:"commission"`
ReferralPercentage uint8 `json:"referral_percentage"`
OnlyFirstPurchase bool `json:"only_first_purchase"`
GiftAmount int64 `json:"gift_amount"` GiftAmount int64 `json:"gift_amount"`
Telegram int64 `json:"telegram"` Telegram int64 `json:"telegram"`
ReferCode string `json:"refer_code"` ReferCode string `json:"refer_code"`
@ -63,6 +65,8 @@ type (
SubscribePath string `json:"subscribe_path"` SubscribePath string `json:"subscribe_path"`
SubscribeDomain string `json:"subscribe_domain"` SubscribeDomain string `json:"subscribe_domain"`
PanDomain bool `json:"pan_domain"` PanDomain bool `json:"pan_domain"`
UserAgentLimit bool `json:"user_agent_limit"`
UserAgentList string `json:"user_agent_list"`
} }
VerifyCodeConfig { VerifyCodeConfig {
VerifyCodeExpireTime int64 `json:"verify_code_expire_time"` VerifyCodeExpireTime int64 `json:"verify_code_expire_time"`
@ -151,6 +155,24 @@ type (
NodeSecret string `json:"node_secret"` NodeSecret string `json:"node_secret"`
NodePullInterval int64 `json:"node_pull_interval"` NodePullInterval int64 `json:"node_pull_interval"`
NodePushInterval int64 `json:"node_push_interval"` NodePushInterval int64 `json:"node_push_interval"`
TrafficReportThreshold int64 `json:"traffic_report_threshold"`
IPStrategy string `json:"ip_strategy"`
DNS []NodeDNS `json:"dns"`
Block []string `json:"block"`
Outbound []NodeOutbound `json:"outbound"`
}
NodeDNS {
Proto string `json:"proto"`
Address string `json:"address"`
Domains []string `json:"domains"`
}
NodeOutbound {
Name string `json:"name"`
Protocol string `json:"protocol"`
Address string `json:"address"`
Port int64 `json:"port"`
Password string `json:"password"`
Rules []string `json:"rules"`
} }
InviteConfig { InviteConfig {
ForcedInvite bool `json:"forced_invite"` ForcedInvite bool `json:"forced_invite"`
@ -182,6 +204,7 @@ type (
Subscribe { Subscribe {
Id int64 `json:"id"` Id int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Language string `json:"language"`
Description string `json:"description"` Description string `json:"description"`
UnitPrice int64 `json:"unit_price"` UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"` UnitTime string `json:"unit_time"`
@ -192,9 +215,8 @@ type (
SpeedLimit int64 `json:"speed_limit"` SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"` DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"` Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"` Nodes []int64 `json:"nodes"`
ServerGroup []int64 `json:"server_group"` NodeTags []string `json:"node_tags"`
Server []int64 `json:"server"`
Show bool `json:"show"` Show bool `json:"show"`
Sell bool `json:"sell"` Sell bool `json:"sell"`
Sort int64 `json:"sort"` Sort int64 `json:"sort"`
@ -273,37 +295,37 @@ type (
Host string `json:"host"` Host string `json:"host"`
ServiceName string `json:"service_name"` ServiceName string `json:"service_name"`
} }
Server { // Server {
Id int64 `json:"id"` // Id int64 `json:"id"`
Tags []string `json:"tags"` // Tags []string `json:"tags"`
Country string `json:"country"` // Country string `json:"country"`
City string `json:"city"` // City string `json:"city"`
Name string `json:"name"` // Name string `json:"name"`
ServerAddr string `json:"server_addr"` // ServerAddr string `json:"server_addr"`
RelayMode string `json:"relay_mode"` // RelayMode string `json:"relay_mode"`
RelayNode []NodeRelay `json:"relay_node"` // RelayNode []NodeRelay `json:"relay_node"`
SpeedLimit int `json:"speed_limit"` // SpeedLimit int `json:"speed_limit"`
TrafficRatio float32 `json:"traffic_ratio"` // TrafficRatio float32 `json:"traffic_ratio"`
GroupId int64 `json:"group_id"` // GroupId int64 `json:"group_id"`
Protocol string `json:"protocol"` // Protocol string `json:"protocol"`
Config interface{} `json:"config"` // Config interface{} `json:"config"`
Enable *bool `json:"enable"` // Enable *bool `json:"enable"`
CreatedAt int64 `json:"created_at"` // CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"` // UpdatedAt int64 `json:"updated_at"`
Status *NodeStatus `json:"status"` // Status *NodeStatus `json:"status"`
Sort int64 `json:"sort"` // Sort int64 `json:"sort"`
} // }
OnlineUser { // OnlineUser {
SID int64 `json:"uid"` // SID int64 `json:"uid"`
IP string `json:"ip"` // IP string `json:"ip"`
} // }
NodeStatus { // NodeStatus {
Online interface{} `json:"online"` // Online interface{} `json:"online"`
Cpu float64 `json:"cpu"` // Cpu float64 `json:"cpu"`
Mem float64 `json:"mem"` // Mem float64 `json:"mem"`
Disk float64 `json:"disk"` // Disk float64 `json:"disk"`
UpdatedAt int64 `json:"updated_at"` // UpdatedAt int64 `json:"updated_at"`
} // }
ServerGroup { ServerGroup {
Id int64 `json:"id"` Id int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@ -445,15 +467,6 @@ type (
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"` UpdatedAt int64 `json:"updated_at"`
} }
UserBalanceLog {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
Amount int64 `json:"amount"`
Type uint8 `json:"type"`
OrderId int64 `json:"order_id"`
Balance int64 `json:"balance"`
CreatedAt int64 `json:"created_at"`
}
UserAffiliate { UserAffiliate {
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Identifier string `json:"identifier"` Identifier string `json:"identifier"`
@ -474,14 +487,6 @@ type (
Port int `json:"port"` Port int `json:"port"`
Prefix string `json:"prefix"` Prefix string `json:"prefix"`
} }
ApplicationConfig {
AppId int64 `json:"app_id"`
EncryptionKey string `json:"encryption_key"`
EncryptionMethod string `json:"encryption_method"`
Domains []string `json:"domains" validate:"required"`
StartupPicture string `json:"startup_picture"`
StartupPictureSkipTime int64 `json:"startup_picture_skip_time"`
}
UserDevice { UserDevice {
Id int64 `json:"id"` Id int64 `json:"id"`
Ip string `json:"ip"` Ip string `json:"ip"`
@ -531,7 +536,7 @@ type (
Token string `json:"token"` Token string `json:"token"`
IP string `json:"ip"` IP string `json:"ip"`
UserAgent string `json:"user_agent"` UserAgent string `json:"user_agent"`
CreatedAt int64 `json:"created_at"` Timestamp int64 `json:"timestamp"`
} }
UserLoginLog { UserLoginLog {
Id int64 `json:"id"` Id int64 `json:"id"`
@ -539,18 +544,17 @@ type (
LoginIP string `json:"login_ip"` LoginIP string `json:"login_ip"`
UserAgent string `json:"user_agent"` UserAgent string `json:"user_agent"`
Success bool `json:"success"` Success bool `json:"success"`
CreatedAt int64 `json:"created_at"` Timestamp int64 `json:"timestamp"`
} }
MessageLog { MessageLog {
Id int64 `json:"id"` Id int64 `json:"id"`
Type string `json:"type"` Type uint8 `json:"type"`
Platform string `json:"platform"` Platform string `json:"platform"`
To string `json:"to"` To string `json:"to"`
Subject string `json:"subject"` Subject string `json:"subject"`
Content string `json:"content"` Content interface{} `json:"content"`
Status int `json:"status"` Status uint8 `json:"status"`
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
} }
Ads { Ads {
Id int `json:"id"` Id int `json:"id"`
@ -754,5 +758,84 @@ type (
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
Download int64 `json:"download"` Download int64 `json:"download"`
} }
DownloadLink {
IOS string `json:"ios,omitempty"`
Android string `json:"android,omitempty"`
Windows string `json:"windows,omitempty"`
Mac string `json:"mac,omitempty"`
Linux string `json:"linux,omitempty"`
Harmony string `json:"harmony,omitempty"`
}
ResetSubscribeTrafficLog {
Id int64 `json:"id"`
Type uint16 `json:"type"`
UserSubscribeId int64 `json:"user_subscribe_id"`
OrderNo string `json:"order_no,omitempty"`
Timestamp int64 `json:"timestamp"`
}
BalanceLog {
Type uint16 `json:"type"`
UserId int64 `json:"user_id"`
Amount int64 `json:"amount"`
OrderNo string `json:"order_no,omitempty"`
Balance int64 `json:"balance"`
Timestamp int64 `json:"timestamp"`
}
CommissionLog {
Type uint16 `json:"type"`
UserId int64 `json:"user_id"`
Amount int64 `json:"amount"`
OrderNo string `json:"order_no"`
Timestamp int64 `json:"timestamp"`
}
Protocol {
Type string `json:"type"`
Port uint16 `json:"port"`
Enable bool `json:"enable"`
Security string `json:"security,omitempty"`
SNI string `json:"sni,omitempty"`
AllowInsecure bool `json:"allow_insecure,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
RealityServerAddr string `json:"reality_server_addr,omitempty"`
RealityServerPort int `json:"reality_server_port,omitempty"`
RealityPrivateKey string `json:"reality_private_key,omitempty"`
RealityPublicKey string `json:"reality_public_key,omitempty"`
RealityShortId string `json:"reality_short_id,omitempty"`
Transport string `json:"transport,omitempty"`
Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"`
ServiceName string `json:"service_name,omitempty"`
Cipher string `json:"cipher,omitempty"`
ServerKey string `json:"server_key,omitempty"`
Flow string `json:"flow,omitempty"`
HopPorts string `json:"hop_ports,omitempty"`
HopInterval int `json:"hop_interval,omitempty"`
ObfsPassword string `json:"obfs_password,omitempty"`
DisableSNI bool `json:"disable_sni,omitempty"`
ReduceRtt bool `json:"reduce_rtt,omitempty"`
UDPRelayMode string `json:"udp_relay_mode,omitempty"`
CongestionController string `json:"congestion_controller,omitempty"`
Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high
PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme
UpMbps int `json:"up_mbps,omitempty"` // upload speed limit
DownMbps int `json:"down_mbps,omitempty"` // download speed limit
Obfs string `json:"obfs,omitempty"` // obfs, 'none', 'http', 'tls'
ObfsHost string `json:"obfs_host,omitempty"` // obfs host
ObfsPath string `json:"obfs_path,omitempty"` // obfs path
XhttpMode string `json:"xhttp_mode,omitempty"` // xhttp mode
XhttpExtra string `json:"xhttp_extra,omitempty"` // xhttp extra path
Encryption string `json:"encryption,omitempty"` // encryption'none', 'mlkem768x25519plus'
EncryptionMode string `json:"encryption_mode,omitempty"` // encryption mode'native', 'xorpub', 'random'
EncryptionRtt string `json:"encryption_rtt,omitempty"` // encryption rtt'0rtt', '1rtt'
EncryptionTicket string `json:"encryption_ticket,omitempty"` // encryption ticket
EncryptionServerPadding string `json:"encryption_server_padding,omitempty"` // encryption server padding
EncryptionPrivateKey string `json:"encryption_private_key,omitempty"` // encryption private key
EncryptionClientPadding string `json:"encryption_client_padding,omitempty"` // encryption client padding
EncryptionPassword string `json:"encryption_password,omitempty"` // encryption password
Ratio float64 `json:"ratio,omitempty"` // Traffic ratio, default is 1
CertMode string `json:"cert_mode,omitempty"` // Certificate mode, `none``http``dns``self`
CertDNSProvider string `json:"cert_dns_provider,omitempty"` // DNS provider for certificate
CertDNSEnv string `json:"cert_dns_env,omitempty"` // Environment for DNS provider
}
) )

4
go.mod
View File

@ -20,7 +20,7 @@ require (
github.com/go-sql-driver/mysql v1.8.1 github.com/go-sql-driver/mysql v1.8.1
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/gofrs/uuid/v5 v5.3.0 github.com/gofrs/uuid/v5 v5.3.0
github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/hibiken/asynq v0.24.1 github.com/hibiken/asynq v0.24.1
@ -28,7 +28,7 @@ require (
github.com/klauspost/compress v1.17.7 github.com/klauspost/compress v1.17.7
github.com/nyaruka/phonenumbers v1.5.0 github.com/nyaruka/phonenumbers v1.5.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/redis/go-redis/v9 v9.6.1 github.com/redis/go-redis/v9 v9.7.2
github.com/smartwalle/alipay/v3 v3.2.23 github.com/smartwalle/alipay/v3 v3.2.23
github.com/spf13/cast v1.7.0 // indirect github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1

8
go.sum
View File

@ -155,8 +155,8 @@ github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8= github.com/golang-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8=
github.com/golang-migrate/migrate/v4 v4.18.2/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk= github.com/golang-migrate/migrate/v4 v4.18.2/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@ -294,8 +294,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.7.2 h1:PSGhv13dJyrTCw1+55H0pIKM3WFov7HuUrKUmInGL0o=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/redis/go-redis/v9 v9.7.2/go.mod h1:yp5+a5FnEEP0/zTYuw6u6/2nn3zivwhv274qYgWQhDM=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=

View File

@ -14,7 +14,6 @@ func StartInitSystemConfig(svc *svc.ServiceContext) {
Subscribe(svc) Subscribe(svc)
Register(svc) Register(svc)
Mobile(svc) Mobile(svc)
TrafficDataToRedis(svc)
if !svc.Config.Debug { if !svc.Config.Debug {
Telegram(svc) Telegram(svc)
} }

View File

@ -91,7 +91,6 @@ CREATE TABLE IF NOT EXISTS `auth_method`
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `uni_auth_method` (`method`) UNIQUE KEY `uni_auth_method` (`method`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 9
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -305,7 +304,6 @@ CREATE TABLE IF NOT EXISTS `subscribe_type`
`updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间', `updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 15
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -323,7 +321,6 @@ CREATE TABLE IF NOT EXISTS `system`
UNIQUE KEY `uni_system_key` (`key`), UNIQUE KEY `uni_system_key` (`key`),
KEY `index_key` (`key`) KEY `index_key` (`key`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 42
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -399,7 +396,6 @@ CREATE TABLE IF NOT EXISTS `user`
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `idx_referer` (`referer_id`) KEY `idx_referer` (`referer_id`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -416,7 +412,6 @@ CREATE TABLE IF NOT EXISTS `user_auth_methods`
UNIQUE KEY `idx_auth_identifier` (`auth_identifier`), UNIQUE KEY `idx_auth_identifier` (`auth_identifier`),
KEY `idx_user_id` (`user_id`) KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS `email_task`;

View File

@ -0,0 +1,23 @@
DROP TABLE IF EXISTS `email_task`;
CREATE TABLE `email_task` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
`subject` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Email Subject',
`content` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Email Content',
`recipient` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Email Recipient',
`scope` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Email Scope',
`register_start_time` datetime(3) DEFAULT NULL COMMENT 'Register Start Time',
`register_end_time` datetime(3) DEFAULT NULL COMMENT 'Register End Time',
`additional` text COLLATE utf8mb4_general_ci COMMENT 'Additional Information',
`scheduled` datetime(3) NOT NULL COMMENT 'Scheduled Time',
`interval` tinyint unsigned NOT NULL COMMENT 'Interval in Seconds',
`limit` bigint unsigned NOT NULL COMMENT 'Daily send limit',
`status` tinyint unsigned NOT NULL COMMENT 'Daily Status',
`errors` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Errors',
`total` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Total Number',
`current` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Current Number',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS `subscribe_application`;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
INSERT IGNORE INTO `system` (`id`, `category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`)
VALUES
(42, 'subscribe', 'UserAgentLimit', 'false', 'bool', 'User Agent Limit', '2025-04-22 14:25:16.637', '2025-04-22 14:25:16.637'),
(43, 'subscribe', 'UserAgentList', '', 'string', 'User Agent List', '2025-04-22 14:25:16.637','2025-04-22 14:25:16.637');

View File

@ -0,0 +1,3 @@
DROP TABLE IF EXISTS `application`;
DROP TABLE IF EXISTS `application_version`;
DROP TABLE IF EXISTS `application_config`;

View File

@ -0,0 +1,106 @@
CREATE TABLE IF NOT EXISTS `user_balance_log`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`amount` bigint NOT NULL COMMENT 'Amount',
`type` tinyint(1) NOT NULL COMMENT 'Type: 1: Recharge 2: Withdraw 3: Payment 4: Refund 5: Reward',
`order_id` bigint DEFAULT NULL COMMENT 'Order ID',
`balance` bigint NOT NULL COMMENT 'Balance',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user_commission_log`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`order_no` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Order No.',
`amount` bigint NOT NULL COMMENT 'Amount',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user_gift_amount_log`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`user_subscribe_id` bigint DEFAULT NULL COMMENT 'Deduction User Subscribe ID',
`order_no` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Order No.',
`type` tinyint(1) NOT NULL COMMENT 'Type: 1: Increase 2: Reduce',
`amount` bigint NOT NULL COMMENT 'Amount',
`balance` bigint NOT NULL COMMENT 'Balance',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Remark',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user_login_log`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`login_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Login IP',
`user_agent` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'UserAgent',
`success` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Login Success',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user_reset_subscribe_log`
(
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_id` BIGINT NOT NULL COMMENT 'User ID',
`type` TINYINT(1) NOT NULL COMMENT 'Type: 1: Auto 2: Advance 3: Paid',
`order_no` VARCHAR(255) DEFAULT NULL COMMENT 'Order No.',
`user_subscribe_id` BIGINT NOT NULL COMMENT 'User Subscribe ID',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
INDEX `idx_user_id` (`user_id`),
INDEX `idx_user_subscribe_id` (`user_subscribe_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user_subscribe_log`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`user_subscribe_id` bigint NOT NULL COMMENT 'User Subscribe ID',
`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Token',
`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'IP',
`user_agent` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'UserAgent',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_user_subscribe_id` (`user_subscribe_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `message_log`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'email' COMMENT 'Message Type',
`platform` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'smtp' COMMENT 'Platform',
`to` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'To',
`subject` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Subject',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Content',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Status',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
DROP TABLE IF EXISTS `system_logs`;

View File

@ -0,0 +1,19 @@
DROP TABLE IF EXISTS `user_balance_log`;
DROP TABLE IF EXISTS `user_commission_log`;
DROP TABLE IF EXISTS `user_gift_amount_log`;
DROP TABLE IF EXISTS `user_login_log`;
DROP TABLE IF EXISTS `user_reset_subscribe_log`;
DROP TABLE IF EXISTS `user_subscribe_log`;
DROP TABLE IF EXISTS `message_log`;
DROP TABLE IF EXISTS `system_logs`;
CREATE TABLE `system_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`type` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Log Type: 1: Email Message 2: Mobile Message 3: Subscribe 4: Subscribe Traffic 5: Server Traffic 6: Login 7: Register 8: Balance 9: Commission 10: Reset Subscribe 11: Gift',
`date` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Log Date',
`object_id` bigint NOT NULL DEFAULT '0' COMMENT 'Object ID',
`content` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Log Content',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
PRIMARY KEY (`id`),
KEY `idx_type` (`type`),
KEY `idx_object_id` (`object_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS `nodes`;
DROP TABLE IF EXISTS `servers`;

View File

@ -0,0 +1,28 @@
CREATE TABLE IF NOT EXISTS `servers` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Server Name',
`country` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Country',
`city` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'City',
`ratio` decimal(4,2) NOT NULL DEFAULT '0.00' COMMENT 'Traffic Ratio',
`address` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Server Address',
`sort` bigint NOT NULL DEFAULT '0' COMMENT 'Sort',
`protocols` text COLLATE utf8mb4_general_ci COMMENT 'Protocol',
`last_reported_at` datetime(3) DEFAULT NULL COMMENT 'Last Reported Time',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `nodes` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Node Name',
`tags` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Tags',
`port` smallint unsigned NOT NULL DEFAULT '0' COMMENT 'Connect Port',
`address` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Connect Address',
`server_id` bigint NOT NULL DEFAULT '0' COMMENT 'Server ID',
`protocol` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Protocol',
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Enabled',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@ -0,0 +1,5 @@
ALTER TABLE `subscribe`
DROP COLUMN `nodes`,
DROP COLUMN `node_tags`,
ADD COLUMN `server` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Server',
ADD COLUMN `server_group` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Server Group';

View File

@ -0,0 +1,7 @@
ALTER TABLE `subscribe`
ADD COLUMN `nodes` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Node IDs',
ADD COLUMN `node_tags` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Node Tags',
DROP COLUMN `server`,
DROP COLUMN `server_group`;
DROP TABLE IF EXISTS `server_rule_group`;

View File

@ -0,0 +1,4 @@
INSERT IGNORE INTO `system` (`category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`)
VALUES
('log', 'AutoClear', 'true', 'bool', 'Auto Clear Log', '2025-04-22 14:25:16.637', '2025-04-22 14:25:16.637'),
('log', 'ClearDays', '7', 'int', 'Clear Days', '2025-04-22 14:25:16.637','2025-04-22 14:25:16.637');

View File

@ -0,0 +1,3 @@
ALTER TABLE `user`
DROP COLUMN `referral_percentage`,
DROP COLUMN `only_first_purchase`;

View File

@ -0,0 +1,7 @@
ALTER TABLE `user`
ADD COLUMN `referral_percentage` TINYINT UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Referral Percentage'
AFTER `commission`,
ADD COLUMN `only_first_purchase` TINYINT(1) NOT NULL DEFAULT 1
COMMENT 'Only First Purchase'
AFTER `referral_percentage`;

View File

@ -0,0 +1,2 @@
ALTER TABLE `nodes`
DROP COLUMN `sort`;

View File

@ -0,0 +1,3 @@
ALTER TABLE `nodes`
ADD COLUMN `sort` INT UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Sort' AFTER `enabled`;

View File

@ -0,0 +1 @@
DROP INDEX idx_traffic_log_time_user_sub ON traffic_log;

View File

@ -0,0 +1 @@
CREATE INDEX idx_traffic_log_time_user_sub ON traffic_log (timestamp, user_id, subscribe_id);

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS `subscribe_type`;
DROP TABLE IF EXISTS `sms`;

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS `subscribe_type`;
DROP TABLE IF EXISTS `sms`;

View File

@ -0,0 +1,7 @@
ALTER TABLE `subscribe`
DROP COLUMN `group_id`,
ADD COLUMN `language` VARCHAR(255) NOT NULL DEFAULT ''
COMMENT 'Language'
AFTER `name`;
DROP TABLE IF EXISTS `subscribe_group`;

View File

@ -0,0 +1,14 @@
DROP TABLE IF EXISTS `email_task`;
CREATE TABLE `task` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
`type` tinyint NOT NULL COMMENT 'Task Type',
`scope` text COLLATE utf8mb4_general_ci COMMENT 'Task Scope',
`content` text COLLATE utf8mb4_general_ci COMMENT 'Task Content',
`status` tinyint NOT NULL DEFAULT '0' COMMENT 'Task Status: 0: Pending, 1: In Progress, 2: Completed, 3: Failed',
`errors` text COLLATE utf8mb4_general_ci COMMENT 'Task Errors',
`total` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Total Number',
`current` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Current Number',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@ -0,0 +1,8 @@
INSERT
IGNORE INTO `system` (`category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`)
VALUE
('server', 'TrafficReportThreshold', '0', 'int', 'Traffic report threshold', '2025-04-22 14:25:16.637','2025-04-22 14:25:16.637'),
('server', 'IPStrategy', '', 'string', 'IP Strategy', '2025-04-22 14:25:16.637','2025-04-22 14:25:16.637'),
('server', 'DNS', '', 'string', 'DNS', '2025-04-22 14:25:16.637','2025-04-22 14:25:16.637'),
('server', 'Block', '', 'string', 'Block', '2025-04-22 14:25:16.637','2025-04-22 14:25:16.637'),
('server', 'Outbound', '', 'string', 'Proxy Outbound', '2025-04-22 14:25:16.637','2025-04-22 14:25:16.637');

View File

@ -3,7 +3,10 @@ package migrate
import ( import (
"testing" "testing"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/pkg/orm" "github.com/perfect-panel/server/pkg/orm"
"gorm.io/driver/mysql"
"gorm.io/gorm"
) )
func getDSN() string { func getDSN() string {
@ -30,3 +33,17 @@ func TestMigrate(t *testing.T) {
t.Log("migrate success") t.Log("migrate success")
} }
} }
func TestMysql(t *testing.T) {
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "root:mylove520@tcp(localhost:3306)/vpnboard",
}))
if err != nil {
t.Fatalf("Failed to connect to MySQL: %v", err)
}
err = db.Migrator().AutoMigrate(&node.Node{})
if err != nil {
t.Fatalf("Failed to auto migrate: %v", err)
return
}
t.Log("MySQL connection and migration successful")
}

View File

@ -3,7 +3,6 @@ package initialize
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/internal/config" "github.com/perfect-panel/server/internal/config"
@ -19,9 +18,40 @@ func Node(ctx *svc.ServiceContext) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
var nodeConfig config.NodeConfig var nodeConfig config.NodeDBConfig
tool.SystemConfigSliceReflectToStruct(configs, &nodeConfig) tool.SystemConfigSliceReflectToStruct(configs, &nodeConfig)
ctx.Config.Node = nodeConfig c := config.NodeConfig{
NodeSecret: nodeConfig.NodeSecret,
NodePullInterval: nodeConfig.NodePullInterval,
NodePushInterval: nodeConfig.NodePushInterval,
IPStrategy: nodeConfig.IPStrategy,
TrafficReportThreshold: nodeConfig.TrafficReportThreshold,
}
if nodeConfig.DNS != "" {
var dns []config.NodeDNS
err = json.Unmarshal([]byte(nodeConfig.DNS), &dns)
if err != nil {
logger.Errorf("[Node] Unmarshal DNS config error: %s", err.Error())
panic(err)
}
c.DNS = dns
}
if nodeConfig.Block != "" {
var block []string
_ = json.Unmarshal([]byte(nodeConfig.Block), &block)
c.Block = tool.RemoveDuplicateElements(block...)
}
if nodeConfig.Outbound != "" {
var outbound []config.NodeOutbound
err = json.Unmarshal([]byte(nodeConfig.Outbound), &outbound)
if err != nil {
logger.Errorf("[Node] Unmarshal Outbound config error: %s", err.Error())
panic(err)
}
c.Outbound = outbound
}
ctx.Config.Node = c
// Manager initialization // Manager initialization
if ctx.DB.Model(&system.System{}).Where("`key` = ?", "NodeMultiplierConfig").Find(&system.System{}).RowsAffected == 0 { if ctx.DB.Model(&system.System{}).Where("`key` = ?", "NodeMultiplierConfig").Find(&system.System{}).RowsAffected == 0 {
@ -39,7 +69,6 @@ func Node(ctx *svc.ServiceContext) {
nodeMultiplierData, err := ctx.SystemModel.FindNodeMultiplierConfig(context.Background()) nodeMultiplierData, err := ctx.SystemModel.FindNodeMultiplierConfig(context.Background())
if err != nil { if err != nil {
logger.Error("Get Node Multiplier Config Error: ", logger.Field("error", err.Error())) logger.Error("Get Node Multiplier Config Error: ", logger.Field("error", err.Error()))
return return
} }

View File

@ -1,57 +0,0 @@
package initialize
import (
"context"
"time"
"github.com/perfect-panel/server/internal/model/cache"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/logger"
)
func TrafficDataToRedis(svcCtx *svc.ServiceContext) {
ctx := context.Background()
// 统计昨天的节点流量数据排行榜前10
nodeData, err := svcCtx.TrafficLogModel.TopServersTrafficByDay(ctx, time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-1, 0, 0, 0, 0, time.Local), 10)
if err != nil {
logger.Errorw("统计昨天的流量数据失败", logger.Field("error", err.Error()))
}
var nodeCacheData []cache.NodeTodayTrafficRank
for _, node := range nodeData {
serverInfo, err := svcCtx.ServerModel.FindOne(ctx, node.ServerId)
if err != nil {
logger.Errorw("查询节点信息失败", logger.Field("error", err.Error()))
continue
}
nodeCacheData = append(nodeCacheData, cache.NodeTodayTrafficRank{
ID: node.ServerId,
Name: serverInfo.Name,
Upload: node.Upload,
Download: node.Download,
Total: node.Upload + node.Download,
})
}
// 写入缓存
if err = svcCtx.NodeCache.UpdateYesterdayNodeTotalTrafficRank(ctx, nodeCacheData); err != nil {
logger.Errorw("写入昨天的流量数据到缓存失败", logger.Field("error", err.Error()))
}
// 统计昨天的用户流量数据排行榜前10
userData, err := svcCtx.TrafficLogModel.TopUsersTrafficByDay(ctx, time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-1, 0, 0, 0, 0, time.Local), 10)
if err != nil {
logger.Errorw("统计昨天的流量数据失败", logger.Field("error", err.Error()))
}
var userCacheData []cache.UserTodayTrafficRank
for _, user := range userData {
userCacheData = append(userCacheData, cache.UserTodayTrafficRank{
SID: user.SubscribeId,
Upload: user.Upload,
Download: user.Download,
Total: user.Upload + user.Download,
})
}
// 写入缓存
if err = svcCtx.NodeCache.UpdateYesterdayUserTotalTrafficRank(ctx, userCacheData); err != nil {
logger.Errorw("写入昨天的流量数据到缓存失败", logger.Field("error", err.Error()))
}
logger.Infow("初始化昨天的流量数据到缓存成功")
}

View File

@ -12,9 +12,6 @@ const SiteConfigKey = "system:site_config"
// SubscribeConfigKey Subscribe Config Key // SubscribeConfigKey Subscribe Config Key
const SubscribeConfigKey = "system:subscribe_config" const SubscribeConfigKey = "system:subscribe_config"
// ApplicationKey Application Key
const ApplicationKey = "system:application"
// RegisterConfigKey Register Config Key // RegisterConfigKey Register Config Key
const RegisterConfigKey = "system:register_config" const RegisterConfigKey = "system:register_config"
@ -51,26 +48,12 @@ const AuthCodeCacheKey = "auth:verify:email"
// AuthCodeTelephoneCacheKey Register Code Cache Key // AuthCodeTelephoneCacheKey Register Code Cache Key
const AuthCodeTelephoneCacheKey = "auth:verify:telephone" const AuthCodeTelephoneCacheKey = "auth:verify:telephone"
// ServerUserListCacheKey Server User List Cache Key // CommonStatCacheKey CommonStat Cache Key
const ServerUserListCacheKey = "server:user_list:id:"
// ServerConfigCacheKey Server Config Cache Key
const ServerConfigCacheKey = "server:config:id:"
// CommonStat Cache Key
const CommonStatCacheKey = "common:stat" const CommonStatCacheKey = "common:stat"
// ServerStatusCacheKey Server Status Cache Key
const ServerStatusCacheKey = "server:status:id:"
// ServerCountCacheKey Server Count Cache Key // ServerCountCacheKey Server Count Cache Key
const ServerCountCacheKey = "server:count" const ServerCountCacheKey = "server:count"
// UserBindTelegramCacheKey User Bind Telegram Cache Key
const UserBindTelegramCacheKey = "user:bind:telegram:code:"
const CacheSmsCount = "cache:sms:count"
// SendIntervalKeyPrefix Auth Code Send Interval Key Prefix // SendIntervalKeyPrefix Auth Code Send Interval Key Prefix
const SendIntervalKeyPrefix = "send:interval:" const SendIntervalKeyPrefix = "send:interval:"

View File

@ -1,6 +1,7 @@
package config package config
import ( import (
"encoding/json"
"github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/orm" "github.com/perfect-panel/server/pkg/orm"
) )
@ -25,6 +26,7 @@ type Config struct {
Subscribe SubscribeConfig `yaml:"Subscribe"` Subscribe SubscribeConfig `yaml:"Subscribe"`
Invite InviteConfig `yaml:"Invite"` Invite InviteConfig `yaml:"Invite"`
Telegram Telegram `yaml:"Telegram"` Telegram Telegram `yaml:"Telegram"`
Log Log `yaml:"Log"`
Administrator struct { Administrator struct {
Email string `yaml:"Email" default:"admin@ppanel.dev"` Email string `yaml:"Email" default:"admin@ppanel.dev"`
Password string `yaml:"Password" default:"password"` Password string `yaml:"Password" default:"password"`
@ -52,9 +54,11 @@ type Verify struct {
type SubscribeConfig struct { type SubscribeConfig struct {
SingleModel bool `yaml:"SingleModel" default:"false"` SingleModel bool `yaml:"SingleModel" default:"false"`
SubscribePath string `yaml:"SubscribePath" default:"/api/subscribe"` SubscribePath string `yaml:"SubscribePath" default:"/v1/subscribe/config"`
SubscribeDomain string `yaml:"SubscribeDomain" default:""` SubscribeDomain string `yaml:"SubscribeDomain" default:""`
PanDomain bool `yaml:"PanDomain" default:"false"` PanDomain bool `yaml:"PanDomain" default:"false"`
UserAgentLimit bool `yaml:"UserAgentLimit" default:"false"`
UserAgentList string `yaml:"UserAgentList" default:""`
} }
type RegisterConfig struct { type RegisterConfig struct {
@ -105,6 +109,73 @@ type NodeConfig struct {
NodeSecret string `yaml:"NodeSecret" default:""` NodeSecret string `yaml:"NodeSecret" default:""`
NodePullInterval int64 `yaml:"NodePullInterval" default:"60"` NodePullInterval int64 `yaml:"NodePullInterval" default:"60"`
NodePushInterval int64 `yaml:"NodePushInterval" default:"60"` NodePushInterval int64 `yaml:"NodePushInterval" default:"60"`
TrafficReportThreshold int64 `yaml:"TrafficReportThreshold" default:"0"`
IPStrategy string `yaml:"IPStrategy" default:""`
DNS []NodeDNS `yaml:"DNS"`
Block []string `yaml:"Block" `
Outbound []NodeOutbound `yaml:"Outbound"`
}
func (n *NodeConfig) Marshal() ([]byte, error) {
type Alias NodeConfig
return json.Marshal(&struct {
*Alias
}{
Alias: (*Alias)(n),
})
}
func (n *NodeConfig) Unmarshal(data []byte) error {
type Alias NodeConfig
aux := &struct {
*Alias
}{
Alias: (*Alias)(n),
}
return json.Unmarshal(data, &aux)
}
type NodeDNS struct {
Proto string `json:"proto"`
Address string `json:"address"`
Domains []string `json:"domains"`
}
func (n *NodeDNS) Marshal() ([]byte, error) {
type Alias NodeDNS
return json.Marshal(&struct {
*Alias
}{
Alias: (*Alias)(n),
})
}
func (n *NodeDNS) Unmarshal(data []byte) error {
type Alias NodeDNS
aux := &struct {
*Alias
}{
Alias: (*Alias)(n),
}
return json.Unmarshal(data, &aux)
}
type NodeOutbound struct {
Name string `json:"name"`
Protocol string `json:"protocol"`
Address string `json:"address"`
Port int64 `json:"port"`
Password string `json:"password"`
Rules []string `json:"rules"`
}
func (n *NodeOutbound) Marshal() ([]byte, error) {
type Alias NodeOutbound
return json.Marshal(&struct {
*Alias
}{
Alias: (*Alias)(n),
})
} }
type File struct { type File struct {
@ -145,3 +216,19 @@ type VerifyCode struct {
Limit int64 `yaml:"Limit" default:"15"` Limit int64 `yaml:"Limit" default:"15"`
Interval int64 `yaml:"Interval" default:"60"` Interval int64 `yaml:"Interval" default:"60"`
} }
type Log struct {
AutoClear bool `yaml:"AutoClear" default:"true"`
ClearDays int64 `yaml:"ClearDays" default:"7"`
}
type NodeDBConfig struct {
NodeSecret string
NodePullInterval int64
NodePushInterval int64
TrafficReportThreshold int64
IPStrategy string
DNS string
Block string
Outbound string
}

View File

@ -0,0 +1,26 @@
package application
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/application"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Create subscribe application
func CreateSubscribeApplicationHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.CreateSubscribeApplicationRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := application.NewCreateSubscribeApplicationLogic(c.Request.Context(), svcCtx)
resp, err := l.CreateSubscribeApplication(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -1,17 +1,17 @@
package server package application
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/server" "github.com/perfect-panel/server/internal/logic/admin/application"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Batch delete node group // Delete subscribe application
func BatchDeleteNodeGroupHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func DeleteSubscribeApplicationHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.BatchDeleteNodeGroupRequest var req types.DeleteSubscribeApplicationRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func BatchDeleteNodeGroupHandler(svcCtx *svc.ServiceContext) func(c *gin.Context
return return
} }
l := server.NewBatchDeleteNodeGroupLogic(c.Request.Context(), svcCtx) l := application.NewDeleteSubscribeApplicationLogic(c.Request.Context(), svcCtx)
err := l.BatchDeleteNodeGroup(&req) err := l.DeleteSubscribeApplication(&req)
result.HttpResult(c, nil, err) result.HttpResult(c, nil, err)
} }
} }

View File

@ -0,0 +1,26 @@
package application
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/application"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Get subscribe application list
func GetSubscribeApplicationListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetSubscribeApplicationListRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := application.NewGetSubscribeApplicationListLogic(c.Request.Context(), svcCtx)
resp, err := l.GetSubscribeApplicationList(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -1,17 +1,17 @@
package subscribe package application
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/subscribe" "github.com/perfect-panel/server/internal/logic/admin/application"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Reset user subscription period // Preview Template
func ResetUserSubscribePeriodHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func PreviewSubscribeTemplateHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.UserSubscribeResetPeriodRequest var req types.PreviewSubscribeTemplateRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,9 @@ func ResetUserSubscribePeriodHandler(svcCtx *svc.ServiceContext) func(c *gin.Con
return return
} }
l := subscribe.NewResetUserSubscribePeriodLogic(c.Request.Context(), svcCtx) l := application.NewPreviewSubscribeTemplateLogic(c.Request.Context(), svcCtx)
resp, err := l.ResetUserSubscribePeriod(&req) resp, err := l.PreviewSubscribeTemplate(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -0,0 +1,26 @@
package application
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/application"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Update subscribe application
func UpdateSubscribeApplicationHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.UpdateSubscribeApplicationRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := application.NewUpdateSubscribeApplicationLogic(c.Request.Context(), svcCtx)
resp, err := l.UpdateSubscribeApplication(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -1,17 +1,17 @@
package order package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/order" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Get order // Filter balance log
func QueryOrderDetailHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterBalanceLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.QueryOrderDetailRequest var req types.FilterBalanceLogRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func QueryOrderDetailHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := order.NewQueryOrderDetailLogic(c.Request.Context(), svcCtx) l := log.NewFilterBalanceLogLogic(c.Request.Context(), svcCtx)
resp, err := l.QueryOrderDetail(&req) resp, err := l.FilterBalanceLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -1,17 +1,17 @@
package document package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/document" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Get document detail // Filter commission log
func QueryDocumentDetailHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterCommissionLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.QueryDocumentDetailRequest var req types.FilterCommissionLogRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func QueryDocumentDetailHandler(svcCtx *svc.ServiceContext) func(c *gin.Context)
return return
} }
l := document.NewQueryDocumentDetailLogic(c.Request.Context(), svcCtx) l := log.NewFilterCommissionLogLogic(c.Request.Context(), svcCtx)
resp, err := l.QueryDocumentDetail(&req) resp, err := l.FilterCommissionLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -1,17 +1,17 @@
package order package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/order" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Get order list // Filter email log
func QueryOrderListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterEmailLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.QueryOrderListRequest var req types.FilterLogParams
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func QueryOrderListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := order.NewQueryOrderListLogic(c.Request.Context(), svcCtx) l := log.NewFilterEmailLogLogic(c.Request.Context(), svcCtx)
resp, err := l.QueryOrderList(&req) resp, err := l.FilterEmailLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -1,17 +1,17 @@
package order package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/order" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Checkout order // Filter gift log
func CheckoutOrderHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterGiftLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.CheckoutOrderRequest var req types.FilterGiftLogRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func CheckoutOrderHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := order.NewCheckoutOrderLogic(c.Request.Context(), svcCtx) l := log.NewFilterGiftLogLogic(c.Request.Context(), svcCtx)
resp, err := l.CheckoutOrder(&req, c.Request.Host) resp, err := l.FilterGiftLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -1,17 +1,17 @@
package order package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/order" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Pre create order // Filter login log
func PreCreateOrderHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterLoginLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.PurchaseOrderRequest var req types.FilterLoginLogRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func PreCreateOrderHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := order.NewPreCreateOrderLogic(c.Request.Context(), svcCtx) l := log.NewFilterLoginLogLogic(c.Request.Context(), svcCtx)
resp, err := l.PreCreateOrder(&req) resp, err := l.FilterLoginLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -1,17 +1,17 @@
package node package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/node" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Get Node list // Filter mobile log
func GetNodeListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterMobileLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.AppUserSubscbribeNodeRequest var req types.FilterLogParams
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func GetNodeListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := node.NewGetNodeListLogic(c.Request.Context(), svcCtx) l := log.NewFilterMobileLogLogic(c.Request.Context(), svcCtx)
resp, err := l.GetNodeList(&req) resp, err := l.FilterMobileLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -1,17 +1,17 @@
package announcement package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/announcement" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Query announcement // Filter register log
func QueryAnnouncementHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterRegisterLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.QueryAnnouncementRequest var req types.FilterRegisterLogRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func QueryAnnouncementHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := announcement.NewQueryAnnouncementLogic(c.Request.Context(), svcCtx) l := log.NewFilterRegisterLogLogic(c.Request.Context(), svcCtx)
resp, err := l.QueryAnnouncement(&req) resp, err := l.FilterRegisterLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -0,0 +1,26 @@
package log
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Filter reset subscribe log
func FilterResetSubscribeLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.FilterResetSubscribeLogRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := log.NewFilterResetSubscribeLogLogic(c.Request.Context(), svcCtx)
resp, err := l.FilterResetSubscribeLog(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -1,17 +1,17 @@
package user package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/user" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Query User Affiliate List // Filter server traffic log
func QueryUserAffiliateListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterServerTrafficLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.QueryUserAffiliateListRequest var req types.FilterServerTrafficLogRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func QueryUserAffiliateListHandler(svcCtx *svc.ServiceContext) func(c *gin.Conte
return return
} }
l := user.NewQueryUserAffiliateListLogic(c.Request.Context(), svcCtx) l := log.NewFilterServerTrafficLogLogic(c.Request.Context(), svcCtx)
resp, err := l.QueryUserAffiliateList(&req) resp, err := l.FilterServerTrafficLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -1,17 +1,17 @@
package auth package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/auth" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// GetAppConfig // Filter subscribe log
func GetAppConfigHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func FilterSubscribeLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.AppConfigRequest var req types.FilterSubscribeLogRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func GetAppConfigHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := auth.NewGetAppConfigLogic(c, svcCtx) l := log.NewFilterSubscribeLogLogic(c.Request.Context(), svcCtx)
resp, err := l.GetAppConfig(&req) resp, err := l.FilterSubscribeLog(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

View File

@ -0,0 +1,26 @@
package log
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Filter traffic log details
func FilterTrafficLogDetailsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.FilterTrafficLogDetailsRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := log.NewFilterTrafficLogDetailsLogic(c.Request.Context(), svcCtx)
resp, err := l.FilterTrafficLogDetails(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -0,0 +1,26 @@
package log
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Filter user subscribe traffic log
func FilterUserSubscribeTrafficLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.FilterSubscribeTrafficRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := log.NewFilterUserSubscribeTrafficLogLogic(c.Request.Context(), svcCtx)
resp, err := l.FilterUserSubscribeTrafficLog(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -0,0 +1,18 @@
package log
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
)
// Get log setting
func GetLogSettingHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
l := log.NewGetLogSettingLogic(c.Request.Context(), svcCtx)
resp, err := l.GetLogSetting()
result.HttpResult(c, resp, err)
}
}

View File

@ -1,17 +1,17 @@
package order package log
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/order" "github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Close order // Update log setting
func CloseOrderHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func UpdateLogSettingHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.CloseOrderRequest var req types.LogSetting
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func CloseOrderHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return return
} }
l := order.NewCloseOrderLogic(c.Request.Context(), svcCtx) l := log.NewUpdateLogSettingLogic(c.Request.Context(), svcCtx)
err := l.CloseOrder(&req) err := l.UpdateLogSetting(&req)
result.HttpResult(c, nil, err) result.HttpResult(c, nil, err)
} }
} }

View File

@ -1,17 +1,17 @@
package system package marketing
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/system" "github.com/perfect-panel/server/internal/logic/admin/marketing"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Create application version // Create a batch send email task
func CreateApplicationVersionHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func CreateBatchSendEmailTaskHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.CreateApplicationVersionRequest var req types.CreateBatchSendEmailTaskRequest
_ = c.ShouldBind(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
@ -19,8 +19,8 @@ func CreateApplicationVersionHandler(svcCtx *svc.ServiceContext) func(c *gin.Con
return return
} }
l := system.NewCreateApplicationVersionLogic(c.Request.Context(), svcCtx) l := marketing.NewCreateBatchSendEmailTaskLogic(c.Request.Context(), svcCtx)
err := l.CreateApplicationVersion(&req) err := l.CreateBatchSendEmailTask(&req)
result.HttpResult(c, nil, err) result.HttpResult(c, nil, err)
} }
} }

View File

@ -0,0 +1,26 @@
package marketing
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/marketing"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Create a quota task
func CreateQuotaTaskHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.CreateQuotaTaskRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := marketing.NewCreateQuotaTaskLogic(c.Request.Context(), svcCtx)
err := l.CreateQuotaTask(&req)
result.HttpResult(c, nil, err)
}
}

View File

@ -0,0 +1,26 @@
package marketing
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/marketing"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Get batch send email task list
func GetBatchSendEmailTaskListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetBatchSendEmailTaskListRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := marketing.NewGetBatchSendEmailTaskListLogic(c.Request.Context(), svcCtx)
resp, err := l.GetBatchSendEmailTaskList(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -1,26 +1,26 @@
package user package marketing
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/app/user" "github.com/perfect-panel/server/internal/logic/admin/marketing"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/result"
) )
// Get user subcribe traffic logs // Get batch send email task status
func GetUserSubscribeTrafficLogsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { func GetBatchSendEmailTaskStatusHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
var req types.GetUserSubscribeTrafficLogsRequest var req types.GetBatchSendEmailTaskStatusRequest
_ = c.BindQuery(&req) _ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req) validateErr := svcCtx.Validate(&req)
if validateErr != nil { if validateErr != nil {
result.ParamErrorResult(c, validateErr) result.ParamErrorResult(c, validateErr)
return return
} }
l := user.NewGetUserSubscribeTrafficLogsLogic(c.Request.Context(), svcCtx) l := marketing.NewGetBatchSendEmailTaskStatusLogic(c.Request.Context(), svcCtx)
resp, err := l.GetUserSubscribeTrafficLogs(&req) resp, err := l.GetBatchSendEmailTaskStatus(&req)
result.HttpResult(c, resp, err) result.HttpResult(c, resp, err)
} }
} }

Some files were not shown because too many files have changed in this diff Show More