Compare commits

...

No commits in common. "v1.0.0" and "master" have entirely different histories.

469 changed files with 18039 additions and 12978 deletions

View File

@ -26,7 +26,7 @@ jobs:
- name: Build Docker image - name: Build Docker image
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }} . run: docker build --build-arg VERSION=${{ env.COMMIT_ID }} -t ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }} .
- name: Push Docker image - name: Push Docker image
run: docker push ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }} run: docker push ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }}
@ -38,7 +38,7 @@ jobs:
username: ${{ secrets.SSH_USER }} username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }} key: ${{ secrets.SSH_PRIVATE_KEY }}
script: | script: |
if [ $(docker ps -q -f name=ppanel-server-dev) ]; then if [ $(docker ps -a -q -f name=ppanel-server-dev) ]; then
echo "Stopping and removing existing ppanel-server container..." echo "Stopping and removing existing ppanel-server container..."
docker stop ppanel-server-dev docker stop ppanel-server-dev
docker rm ppanel-server-dev docker rm ppanel-server-dev
@ -47,4 +47,4 @@ jobs:
fi fi
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} 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 --restart=always -d ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }} 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 }}

View File

@ -35,7 +35,6 @@ jobs:
mkdir -p swagger 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 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 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 app.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_app.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 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 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 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
@ -45,7 +44,6 @@ jobs:
run: | run: |
test -f ./swagger/common.json test -f ./swagger/common.json
test -f ./swagger/user.json test -f ./swagger/user.json
test -f ./swagger/app.json
test -f ./swagger/admin.json test -f ./swagger/admin.json
- name: Checkout target repository - name: Checkout target repository

3
.gitignore vendored
View File

@ -13,4 +13,5 @@
*.key *.key
node_modules node_modules
package-lock.json package-lock.json
package.json package.json
/bin

View File

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

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

108
README.md
View File

@ -2,17 +2,31 @@
<div align="center"> <div align="center">
[![License](https://img.shields.io/github/license/perfect-panel/ppanel-server)](LICENSE) [![License](https://img.shields.io/github/license/perfect-panel/server)](LICENSE)
![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue) [![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue)](https://go.dev/)
[![Go Report Card](https://goreportcard.com/badge/github.com/perfect-panel/server)](https://goreportcard.com/report/github.com/perfect-panel/server)
[![Docker](https://img.shields.io/badge/Docker-Available-blue)](Dockerfile) [![Docker](https://img.shields.io/badge/Docker-Available-blue)](Dockerfile)
[![CI/CD](https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-server/release.yml)](.github/workflows/release.yml) [![CI/CD](https://img.shields.io/github/actions/workflow/status/perfect-panel/server/release.yml)](.github/workflows/release.yml)
**PPanel is a pure, professional, and perfect open-source proxy panel tool, designed for learning and practical use.** **PPanel is a pure, professional, and perfect open-source proxy panel tool, designed for learning and practical use.**
[English](README.md) | [中文](README_zh.md) | [Report Bug](https://github.com/perfect-panel/ppanel-server/issues/new) | [Request Feature](https://github.com/perfect-panel/ppanel-server/issues/new) [English](README.md) | [中文](readme_zh.md) | [Report Bug](https://github.com/perfect-panel/server/issues/new) | [Request Feature](https://github.com/perfect-panel/server/issues/new)
</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
@ -108,7 +122,7 @@ proxy services. Built with Go, it emphasizes performance, security, and scalabil
Explore the full API documentation: Explore the full API documentation:
- **Swagger**: [https://ppanel.dev/swagger/ppanel](https://ppanel.dev/swagger/ppanel) - **Swagger**: [https://ppanel.dev/en-US/swagger/ppanel](https://ppanel.dev/swagger/ppanel)
The documentation covers all endpoints, request/response formats, and authentication details. The documentation covers all endpoints, request/response formats, and authentication details.
@ -124,6 +138,10 @@ The documentation covers all endpoints, request/response formats, and authentica
Visit [ppanel.dev](https://ppanel.dev/) for more details. Visit [ppanel.dev](https://ppanel.dev/) for more details.
## 🏛 Architecture
![Architecture Diagram](./doc/image/architecture-en.png)
## 📁 Directory Structure ## 📁 Directory Structure
``` ```
@ -188,6 +206,86 @@ Supported platforms include:
Contributions are welcome! Please follow the [Contribution Guidelines](CONTRIBUTING.md) for bug fixes, features, or Contributions are welcome! Please follow the [Contribution Guidelines](CONTRIBUTING.md) for bug fixes, features, or
documentation improvements. documentation improvements.
## ✨ Special Thanks
A huge thank you to the following outstanding open-source projects that have provided invaluable support for this
project's development! 🚀
<div style="overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
<thead>
<tr style="background-color: #f5f5f5;">
<th style="padding: 10px; text-align: center;">Project</th>
<th style="padding: 10px; text-align: left;">Description</th>
<th style="padding: 10px; text-align: center;">Project</th>
<th style="padding: 10px; text-align: left;">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center" style="padding: 15px; vertical-align: middle;">
<a href="https://gin-gonic.com/" style="text-decoration: none;">
<img src="https://raw.githubusercontent.com/gin-gonic/logo/master/color.png" width="25" alt="Gin" style="border-radius: 8px;" /><br/>
<strong>Gin</strong><br/>
<img src="https://img.shields.io/github/stars/gin-gonic/gin?style=social" alt="Gin Stars" />
</a>
</td>
<td style="padding: 15px; vertical-align: middle;">
High-performance Go Web framework<br/>
</td>
<td align="center" style="padding: 15px; vertical-align: middle;">
<a href="https://gorm.io/" style="text-decoration: none;">
<img src="https://gorm.io/gorm.svg" width="50" alt="Gorm" style="border-radius: 8px;" /><br/>
<strong>Gorm</strong><br/>
<img src="https://img.shields.io/github/stars/go-gorm/gorm?style=social" alt="Gorm Stars" />
</a>
</td>
<td style="padding: 15px; vertical-align: middle;">
Powerful Go ORM framework<br/>
</td>
</tr>
<tr>
<td align="center" style="padding: 15px; vertical-align: middle;">
<a href="https://github.com/hibiken/asynq" style="text-decoration: none;">
<img src="https://user-images.githubusercontent.com/11155743/114697792-ffbfa580-9d26-11eb-8e5b-33bef69476dc.png" width="50" alt="Asynq" style="border-radius: 8px;" /><br/>
<strong>Asynq</strong><br/>
<img src="https://img.shields.io/github/stars/hibiken/asynq?style=social" alt="Asynq Stars" />
</a>
</td>
<td style="padding: 15px; vertical-align: middle;">
Asynchronous task queue for Go<br/>
</td>
<td align="center" style="padding: 15px; vertical-align: middle;">
<a href="https://goswagger.io/" style="text-decoration: none;">
<img src="https://goswagger.io/go-swagger/logo.png" width="30" alt="Go-Swagger" style="border-radius: 8px;" /><br/>
<strong>Go-Swagger</strong><br/>
<img src="https://img.shields.io/github/stars/go-swagger/go-swagger?style=social" alt="Go-Swagger Stars" />
</a>
</td>
<td style="padding: 15px; vertical-align: middle;">
Comprehensive Go Swagger toolkit<br/>
</td>
</tr>
<tr>
<td align="center" style="padding: 15px; vertical-align: middle;">
<a href="https://go-zero.dev/" style="text-decoration: none;">
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/go-zero.png" width="30" alt="Go-Zero" style="border-radius: 8px;" /><br/>
<strong>Go-Zero</strong><br/>
<img src="https://img.shields.io/github/stars/zeromicro/go-zero?style=social" alt="Go-Zero Stars" />
</a>
</td>
<td colspan="3" style="padding: 15px; vertical-align: middle;">
Go microservices framework (this project's API generator is built on Go-Zero)<br/>
</td>
</tr>
</tbody>
</table>
</div>
---
🎉 **Salute to Open Source**: Thank you to the open-source community for making development simpler and more efficient!
Please give these projects a ⭐ to support the open-source movement!
## 📄 License ## 📄 License
This project is licensed under the [GPL-3.0 License](LICENSE). This project is licensed under the [GPL-3.0 License](LICENSE).

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

141
adapter/client.go Normal file
View File

@ -0,0 +1,141 @@
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
}
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

@ -25,17 +25,14 @@ type (
GetAuthMethodListResponse { GetAuthMethodListResponse {
List []AuthMethodConfig `json:"list"` List []AuthMethodConfig `json:"list"`
} }
TestSmsSendRequest { TestSmsSendRequest {
AreaCode string `json:"area_code" validate:"required"` AreaCode string `json:"area_code" validate:"required"`
Telephone string `json:"telephone" validate:"required"` Telephone string `json:"telephone" validate:"required"`
} }
// Test email smtp request // Test email smtp request
TestEmailSendRequest { TestEmailSendRequest {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
} }
) )
@server ( @server (
@ -56,7 +53,6 @@ service ppanel {
@handler UpdateAuthMethodConfig @handler UpdateAuthMethodConfig
put /config (UpdateAuthMethodConfigRequest) returns (AuthMethodConfig) put /config (UpdateAuthMethodConfigRequest) returns (AuthMethodConfig)
@doc "Test sms send" @doc "Test sms send"
@handler TestSmsSend @handler TestSmsSend
post /test_sms_send (TestSmsSendRequest) post /test_sms_send (TestSmsSendRequest)

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

@ -21,8 +21,8 @@ type (
ExpireTime int64 `json:"expire_time" validate:"required"` ExpireTime int64 `json:"expire_time" validate:"required"`
UserLimit int64 `json:"user_limit,omitempty"` UserLimit int64 `json:"user_limit,omitempty"`
Subscribe []int64 `json:"subscribe,omitempty"` Subscribe []int64 `json:"subscribe,omitempty"`
UsedCount int64 `json:"used_count,omitempty"` UsedCount int64 `json:"used_count,omitempty"`
Enable *bool `json:"enable,omitempty"` Enable *bool `json:"enable,omitempty"`
} }
UpdateCouponRequest { UpdateCouponRequest {
Id int64 `json:"id" validate:"required"` Id int64 `json:"id" validate:"required"`
@ -35,8 +35,8 @@ type (
ExpireTime int64 `json:"expire_time" validate:"required"` ExpireTime int64 `json:"expire_time" validate:"required"`
UserLimit int64 `json:"user_limit,omitempty"` UserLimit int64 `json:"user_limit,omitempty"`
Subscribe []int64 `json:"subscribe,omitempty"` Subscribe []int64 `json:"subscribe,omitempty"`
UsedCount int64 `json:"used_count,omitempty"` UsedCount int64 `json:"used_count,omitempty"`
Enable *bool `json:"enable,omitempty"` Enable *bool `json:"enable,omitempty"`
} }
DeleteCouponRequest { DeleteCouponRequest {
Id int64 `json:"id" validate:"required"` Id int64 `json:"id" validate:"required"`

View File

@ -1,13 +1,10 @@
syntax = "v1" syntax = "v1"
info( info (
title: "Device API" title: "Device API"
desc: "API for ppanel" desc: "API for ppanel"
author: "Tension" author: "Tension"
email: "tension@ppanel.com" email: "tension@ppanel.com"
version: "0.0.1" version: "0.0.1"
) )
type (
)

View File

@ -12,19 +12,184 @@ import "../types.api"
type ( 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,104 +11,137 @@ 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"`
Tag string `form:"tag,omitempty"`
GroupId int64 `form:"group_id,omitempty"`
Search string `form:"search,omitempty"`
} }
GetNodeServerListResponse { 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"`
Ratio float32 `json:"ratio"`
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"`
Ratio float32 `json:"ratio"`
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"`
Ratio float32 `json:"ratio"`
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"`
}
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"`
Tags []string `json:"tags"` }
Country string `json:"country"` GetServerProtocolsResponse {
City string `json:"city"` Protocols []Protocol `json:"protocols"`
Name string `json:"name" validate:"required"` }
ServerAddr string `json:"server_addr" validate:"required"` Node {
RelayMode string `json:"relay_mode"` Id int64 `json:"id"`
RelayNode []NodeRelay `json:"relay_node"` Name string `json:"name"`
SpeedLimit int `json:"speed_limit"` Tags []string `json:"tags"`
TrafficRatio float32 `json:"traffic_ratio"` Port uint16 `json:"port"`
GroupId int64 `json:"group_id"` Address string `json:"address"`
Protocol string `json:"protocol" validate:"required"` ServerId int64 `json:"server_id"`
Config interface{} `json:"config" validate:"required"` Protocol string `json:"protocol"`
Enable *bool `json:"enable"` Enabled *bool `json:"enabled"`
Sort int64 `json:"sort"` Sort int `json:"sort,omitempty"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
} }
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"`
Enable *bool `json:"enable"` Port uint16 `json:"port"`
Sort int64 `json:"sort"` 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"`
} }
DeleteNodeRequest { DeleteNodeRequest {
Id int64 `json:"id" validate:"required"` Id int64 `json:"id"`
} }
GetNodeGroupListResponse { FilterNodeListRequest {
Total int64 `json:"total"` Page int `form:"page"`
List []ServerGroup `json:"list"` Size int `form:"size"`
Search string `form:"search,omitempty"`
} }
CreateNodeGroupRequest { FilterNodeListResponse {
Name string `json:"name" validate:"required"` Total int64 `json:"total"`
Description string `json:"description"` List []Node `json:"list"`
} }
UpdateNodeGroupRequest { HasMigrateSeverNodeResponse {
Id int64 `json:"id" validate:"required"` HasMigrate bool `json:"has_migrate"`
Name string `json:"name" validate:"required"`
Description string `json:"description"`
} }
DeleteNodeGroupRequest { MigrateServerNodeResponse {
Id int64 `json:"id" validate:"required"` Succee uint64 `json:"succee"`
Fail uint64 `json:"fail"`
Message string `json:"message,omitempty"`
} }
BatchDeleteNodeRequest { ResetSortRequest {
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"`
Tags []string `json:"tags"`
Rules string `json:"rules"`
Enable bool `json:"enable"`
}
UpdateRuleGroupRequest {
Id int64 `json:"id" validate:"required"`
Icon string `json:"icon"`
Name string `json:"name" validate:"required"`
Tags []string `json:"tags"`
Rules string `json:"rules"`
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"`
} }
) )
@ -119,72 +152,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

@ -34,70 +34,67 @@ type (
Ids []int64 `json:"ids" validate:"required"` Ids []int64 `json:"ids" validate:"required"`
} }
CreateSubscribeRequest { CreateSubscribeRequest {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Description string `json:"description"` Language string `json:"language"`
UnitPrice int64 `json:"unit_price"` Description string `json:"description"`
UnitTime string `json:"unit_time"` UnitPrice int64 `json:"unit_price"`
Discount []SubscribeDiscount `json:"discount"` UnitTime string `json:"unit_time"`
Replacement int64 `json:"replacement"` Discount []SubscribeDiscount `json:"discount"`
Inventory int64 `json:"inventory"` Replacement int64 `json:"replacement"`
Traffic int64 `json:"traffic"` Inventory int64 `json:"inventory"`
SpeedLimit int64 `json:"speed_limit"` Traffic int64 `json:"traffic"`
DeviceLimit int64 `json:"device_limit"` SpeedLimit int64 `json:"speed_limit"`
Quota int64 `json:"quota"` DeviceLimit int64 `json:"device_limit"`
GroupId int64 `json:"group_id"` Quota int64 `json:"quota"`
ServerGroup []int64 `json:"server_group"` Nodes []int64 `json:"nodes"`
Server []int64 `json:"server"` NodeTags []string `json:"node_tags"`
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"`
AllowDeduction *bool `json:"allow_deduction"` AllowDeduction *bool `json:"allow_deduction"`
ResetCycle int64 `json:"reset_cycle"` ResetCycle int64 `json:"reset_cycle"`
RenewalReset *bool `json:"renewal_reset"` RenewalReset *bool `json:"renewal_reset"`
} }
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"`
Description string `json:"description"` Language string `json:"language"`
UnitPrice int64 `json:"unit_price"` Description string `json:"description"`
UnitTime string `json:"unit_time"` UnitPrice int64 `json:"unit_price"`
Discount []SubscribeDiscount `json:"discount"` UnitTime string `json:"unit_time"`
Replacement int64 `json:"replacement"` Discount []SubscribeDiscount `json:"discount"`
Inventory int64 `json:"inventory"` Replacement int64 `json:"replacement"`
Traffic int64 `json:"traffic"` Inventory int64 `json:"inventory"`
SpeedLimit int64 `json:"speed_limit"` Traffic int64 `json:"traffic"`
DeviceLimit int64 `json:"device_limit"` SpeedLimit int64 `json:"speed_limit"`
Quota int64 `json:"quota"` DeviceLimit int64 `json:"device_limit"`
GroupId int64 `json:"group_id"` Quota int64 `json:"quota"`
ServerGroup []int64 `json:"server_group"` Nodes []int64 `json:"nodes"`
Server []int64 `json:"server"` NodeTags []string `json:"node_tags"`
Show *bool `json:"show"` Show *bool `json:"show"`
Sell *bool `json:"sell"` Sell *bool `json:"sell"`
Sort int64 `json:"sort"` Sort int64 `json:"sort"`
DeductionRatio int64 `json:"deduction_ratio"` DeductionRatio int64 `json:"deduction_ratio"`
AllowDeduction *bool `json:"allow_deduction"` AllowDeduction *bool `json:"allow_deduction"`
ResetCycle int64 `json:"reset_cycle"` ResetCycle int64 `json:"reset_cycle"`
RenewalReset *bool `json:"renewal_reset"` RenewalReset *bool `json:"renewal_reset"`
} }
SubscribeSortRequest { SubscribeSortRequest {
Sort []SortItem `json:"sort"` Sort []SortItem `json:"sort"`
} }
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 {
Subscribe Subscribe
Sold int64 `json:"sold"`
Sold int64 `json:"sold"`
} }
GetSubscribeListResponse { GetSubscribeListResponse {
List []SubscribeItem `json:"list"` List []SubscribeItem `json:"list"`
Total int64 `json:"total"` Total int64 `json:"total"`
} }
DeleteSubscribeRequest { DeleteSubscribeRequest {
Id int64 `json:"id" validate:"required"` Id int64 `json:"id" validate:"required"`

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"`
} }
@ -86,46 +42,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)

View File

@ -12,14 +12,14 @@ import "../types.api"
type ( type (
UpdateTicketStatusRequest { UpdateTicketStatusRequest {
Id int64 `json:"id" validate:"required"` Id int64 `json:"id" validate:"required"`
Status *uint8 `json:"status" validate:"required"` Status *uint8 `json:"status" validate:"required"`
} }
GetTicketListRequest { GetTicketListRequest {
Page int64 `form:"page"` Page int64 `form:"page"`
Size int64 `form:"size"` Size int64 `form:"size"`
UserId int64 `form:"user_id,omitempty"` UserId int64 `form:"user_id,omitempty"`
Status *uint8 `form:"status,omitempty"` Status *uint8 `form:"status,omitempty"`
Search string `form:"search,omitempty"` Search string `form:"search,omitempty"`
} }
GetTicketListResponse { GetTicketListResponse {

View File

@ -1,33 +1,40 @@
syntax = "v1" syntax = "v1"
info( info (
title: "Tools Api" title: "Tools Api"
desc: "API for ppanel" desc: "API for ppanel"
author: "Tension" author: "Tension"
email: "tension@ppanel.com" email: "tension@ppanel.com"
version: "0.0.1" version: "0.0.1"
) )
import "../types.api" import "../types.api"
type ( type (
LogResponse { LogResponse {
List interface{} `json:"list"` List interface{} `json:"list"`
} }
VersionResponse {
Version string `json:"version"`
}
) )
@server ( @server (
prefix: v1/admin/tool prefix: v1/admin/tool
group: admin/tool group: admin/tool
middleware: AuthMiddleware middleware: AuthMiddleware
) )
service ppanel { service ppanel {
@doc "Get System Log" @doc "Get System Log"
@handler GetSystemLog @handler GetSystemLog
get /log returns (LogResponse) get /log returns (LogResponse)
@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

@ -32,17 +32,19 @@ type (
Id int64 `form:"id" validate:"required"` Id int64 `form:"id" validate:"required"`
} }
UpdateUserBasiceInfoRequest { UpdateUserBasiceInfoRequest {
UserId int64 `json:"user_id" validate:"required"` UserId int64 `json:"user_id" validate:"required"`
Password string `json:"password"` Password string `json:"password"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Balance int64 `json:"balance"` Balance int64 `json:"balance"`
Commission int64 `json:"commission"` Commission int64 `json:"commission"`
GiftAmount int64 `json:"gift_amount"` ReferralPercentage uint8 `json:"referral_percentage"`
Telegram int64 `json:"telegram"` OnlyFirstPurchase bool `json:"only_first_purchase"`
ReferCode string `json:"refer_code"` GiftAmount int64 `json:"gift_amount"`
RefererId int64 `json:"referer_id"` Telegram int64 `json:"telegram"`
Enable bool `json:"enable"` ReferCode string `json:"refer_code"`
IsAdmin bool `json:"is_admin"` RefererId int64 `json:"referer_id"`
Enable bool `json:"enable"`
IsAdmin bool `json:"is_admin"`
} }
UpdateUserNotifySettingRequest { UpdateUserNotifySettingRequest {
UserId int64 `json:"user_id" validate:"required"` UserId int64 `json:"user_id" validate:"required"`
@ -52,18 +54,20 @@ type (
EnableTradeNotify bool `json:"enable_trade_notify"` EnableTradeNotify bool `json:"enable_trade_notify"`
} }
CreateUserRequest { CreateUserRequest {
Email string `json:"email"` Email string `json:"email"`
Telephone string `json:"telephone"` Telephone string `json:"telephone"`
TelephoneAreaCode string `json:"telephone_area_code"` TelephoneAreaCode string `json:"telephone_area_code"`
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"`
RefererUser string `json:"referer_user"` ReferralPercentage uint8 `json:"referral_percentage"`
ReferCode string `json:"refer_code"` OnlyFirstPurchase bool `json:"only_first_purchase"`
Balance int64 `json:"balance"` RefererUser string `json:"referer_user"`
Commission int64 `json:"commission"` ReferCode string `json:"refer_code"`
GiftAmount int64 `json:"gift_amount"` Balance int64 `json:"balance"`
IsAdmin bool `json:"is_admin"` Commission int64 `json:"commission"`
GiftAmount int64 `json:"gift_amount"`
IsAdmin bool `json:"is_admin"`
} }
UserSubscribeDetail { UserSubscribeDetail {
Id int64 `json:"id"` Id int64 `json:"id"`
@ -164,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"`
} }
@ -251,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,24 +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,49 +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,58 +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,75 +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,90 +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,23 +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

@ -11,11 +11,11 @@ info (
type ( type (
// User login request // User login request
UserLoginRequest { UserLoginRequest {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
IP string `header:"X-Original-Forwarded-For"` IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"` 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
CheckUserRequest { CheckUserRequest {
@ -23,43 +23,41 @@ type (
} }
// User login response // User login response
CheckUserResponse { CheckUserResponse {
exist bool `json:"exist"` Exist bool `json:"exist"`
} }
// User login response // User login response
UserRegisterRequest { UserRegisterRequest {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
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"` UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"` CfToken string `json:"cf_token,optional"`
} }
// User login response // User login response
ResetPasswordRequest { ResetPasswordRequest {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
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"` UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"` CfToken string `json:"cf_token,optional"`
} }
LoginResponse { LoginResponse {
Token string `json:"token"` Token string `json:"token"`
} }
OAthLoginRequest { OAthLoginRequest {
Method string `json:"method" validate:"required"` // google, facebook, apple, telegram, github etc. Method string `json:"method" validate:"required"` // google, facebook, apple, telegram, github etc.
Redirect string `json:"redirect"` Redirect string `json:"redirect"`
} }
OAuthLoginResponse { OAuthLoginResponse {
Redirect string `json:"redirect"` Redirect string `json:"redirect"`
} }
OAuthLoginGetTokenRequest { OAuthLoginGetTokenRequest {
Method string `json:"method" validate:"required"` // google, facebook, apple, telegram, github etc. Method string `json:"method" validate:"required"` // google, facebook, apple, telegram, github etc.
Callback interface{} `json:"callback" validate:"required"` Callback interface{} `json:"callback" validate:"required"`
} }
// login request // login request
TelephoneLoginRequest { TelephoneLoginRequest {
Telephone string `json:"telephone" validate:"required"` Telephone string `json:"telephone" validate:"required"`
@ -67,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
@ -76,7 +75,7 @@ type (
} }
// User login response // User login response
TelephoneCheckUserResponse { TelephoneCheckUserResponse {
exist bool `json:"exist"` Exist bool `json:"exist"`
} }
// User login response // User login response
TelephoneRegisterRequest { TelephoneRegisterRequest {
@ -86,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
@ -95,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 {
@ -103,7 +104,7 @@ type (
State string `form:"state"` State string `form:"state"`
} }
GoogleLoginCallbackRequest { GoogleLoginCallbackRequest {
Code string `form:"code"` Code string `form:"code"`
State string `form:"state"` State string `form:"state"`
} }
) )

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"`
@ -70,14 +66,26 @@ type (
List []Ads `json:"list"` List []Ads `json:"list"`
} }
CheckVerificationCodeRequest { CheckVerificationCodeRequest {
Method string `json:"method" validate:"required,oneof=email mobile"` Method string `json:"method" validate:"required,oneof=email mobile"`
Account string `json:"account" validate:"required"` Account string `json:"account" validate:"required"`
Code string `json:"code" validate:"required"` Code string `json:"code" validate:"required"`
Type uint8 `json:"type" validate:"required"` Type uint8 `json:"type" validate:"required"`
} }
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"`
} }
) )
@ -90,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)
@ -121,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"`
@ -44,9 +48,9 @@ type (
ServerCommon ServerCommon
} }
GetServerConfigResponse { GetServerConfigResponse {
Basic ServerBasic `json:"basic"` Basic ServerBasic `json:"basic"`
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
Config interface{} `json:"config"` Config interface{} `json:"config"`
} }
ServerBasic { ServerBasic {
PushInterval int64 `json:"push_interval"` PushInterval int64 `json:"push_interval"`
@ -60,8 +64,8 @@ type (
ServerUser { ServerUser {
Id int64 `json:"id"` Id int64 `json:"id"`
UUID string `json:"uuid"` UUID string `json:"uuid"`
SpeedLimit int64 `json:"speed_limit"` SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"` DeviceLimit int64 `json:"device_limit"`
} }
GetServerUserListRequest { GetServerUserListRequest {
ServerCommon ServerCommon
@ -78,7 +82,6 @@ type (
ServerCommon ServerCommon
Traffic []UserTraffic `json:"traffic"` Traffic []UserTraffic `json:"traffic"`
} }
ServerPushStatusRequest { ServerPushStatusRequest {
ServerCommon ServerCommon
Cpu float64 `json:"cpu"` Cpu float64 `json:"cpu"`
@ -90,11 +93,18 @@ type (
ServerCommon ServerCommon
Users []OnlineUser `json:"users"` Users []OnlineUser `json:"users"`
} }
QueryServerConfigRequest {
ServerID int64 `path:"server_id"`
SecretKey string `header:"secret_key"`
}
QueryServerConfigResponse {
Protocols []Protocol `json:"protocols"`
}
) )
@server ( @server (
prefix: v1/server prefix: v1/server
group: server group: server
middleware: ServerMiddleware middleware: ServerMiddleware
) )
service ppanel { service ppanel {
@ -119,3 +129,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

@ -10,7 +10,6 @@ info (
import "../types.api" import "../types.api"
@server ( @server (
prefix: v1/public/announcement prefix: v1/public/announcement
group: public/announcement group: public/announcement

View File

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

View File

@ -10,7 +10,6 @@ info (
import "../types.api" import "../types.api"
@server ( @server (
prefix: v1/public/payment prefix: v1/public/payment
group: public/payment group: public/payment

View File

@ -19,17 +19,20 @@ type (
SubscribeId int64 `json:"subscribe_id"` SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity"` Quantity int64 `json:"quantity"`
Coupon string `json:"coupon,omitempty"` Coupon string `json:"coupon,omitempty"`
InviteCode string `json:"invite_code,omitempty"` InviteCode string `json:"invite_code,omitempty"`
TurnstileToken string `json:"turnstile_token,omitempty"` TurnstileToken string `json:"turnstile_token,omitempty"`
} }
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"`
} }
PrePurchaseOrderRequest { PrePurchaseOrderRequest {
Payment int64 `json:"payment,omitempty"` Payment int64 `json:"payment,omitempty"`
SubscribeId int64 `json:"subscribe_id"` SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity"` Quantity int64 `json:"quantity"`
Coupon string `json:"coupon,omitempty"` Coupon string `json:"coupon,omitempty"`
@ -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

@ -9,7 +9,13 @@ 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

@ -11,10 +11,9 @@ info (
import "../types.api" import "../types.api"
type ( type (
GetUserTicketListResponse { GetUserTicketListResponse {
Total int64 `json:"total"` Total int64 `json:"total"`
List []Ticket `json:"list"` List []Ticket `json:"list"`
} }
CreateUserTicketRequest { CreateUserTicketRequest {
Title string `json:"title"` Title string `json:"title"`
@ -34,10 +33,10 @@ type (
Status *uint8 `json:"status" validate:"required"` Status *uint8 `json:"status" validate:"required"`
} }
CreateUserTicketFollowRequest { CreateUserTicketFollowRequest {
TicketId int64 `json:"ticket_id"` TicketId int64 `json:"ticket_id"`
From string `json:"from"` From string `json:"from"`
Type uint8 `json:"type"` Type uint8 `json:"type"`
Content string `json:"content"` Content string `json:"content"`
} }
) )

View File

@ -25,16 +25,8 @@ 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"`
@ -77,40 +69,33 @@ type (
ResetUserSubscribeTokenRequest { ResetUserSubscribeTokenRequest {
UserSubscribeId int64 `json:"user_subscribe_id"` UserSubscribeId int64 `json:"user_subscribe_id"`
} }
GetLoginLogRequest { GetLoginLogRequest {
Page int `form:"page"` Page int `form:"page"`
Size int `form:"size"` Size int `form:"size"`
} }
GetLoginLogResponse { GetLoginLogResponse {
List []UserLoginLog `json:"list"` List []UserLoginLog `json:"list"`
Total int64 `json:"total"` Total int64 `json:"total"`
} }
GetSubscribeLogRequest { GetSubscribeLogRequest {
Page int `form:"page"` Page int `form:"page"`
Size int `form:"size"` Size int `form:"size"`
} }
GetSubscribeLogResponse { GetSubscribeLogResponse {
List []UserSubscribeLog `json:"list"` List []UserSubscribeLog `json:"list"`
Total int64 `json:"total"` Total int64 `json:"total"`
} }
UpdateBindMobileRequest {
UpdateBindMobileRequest{ AreaCode string `json:"area_code" validate:"required"`
AreaCode string `json:"area_code" validate:"required"` Mobile string `json:"mobile" validate:"required"`
Mobile string `json:"mobile" validate:"required"` Code string `json:"code" validate:"required"`
Code string `json:"code" validate:"required"`
} }
UpdateBindEmailRequest {
UpdateBindEmailRequest{ Email string `json:"email" validate:"required"`
Email string `json:"email" validate:"required"`
} }
VerifyEmailRequest { VerifyEmailRequest {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
Code string `json:"code" validate:"required"` Code string `json:"code" validate:"required"`
} }
) )

View File

@ -1,26 +1,30 @@
syntax = "v1" syntax = "v1"
info( info (
title: "admin API" title: "admin API"
desc: "API for ppanel" desc: "API for ppanel"
author: "Tension" author: "Tension"
email: "tension@ppanel.com" email: "tension@ppanel.com"
version: "0.0.1" version: "0.0.1"
) )
import ( import (
"./admin/system.api" "./admin/system.api"
"./admin/user.api" "./admin/user.api"
"./admin/server.api" "./admin/server.api"
"./admin/subscribe.api" "./admin/subscribe.api"
"./admin/payment.api" "./admin/payment.api"
"./admin/coupon.api" "./admin/coupon.api"
"./admin/order.api" "./admin/order.api"
"./admin/ticket.api" "./admin/ticket.api"
"./admin/announcement.api" "./admin/announcement.api"
"./admin/document.api" "./admin/document.api"
"./admin/tool.api" "./admin/tool.api"
"./admin/console.api" "./admin/console.api"
"./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,21 +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

@ -1,14 +1,15 @@
syntax = "v1" syntax = "v1"
info( info (
title: "common API" title: "common API"
desc: "API for ppanel" desc: "API for ppanel"
author: "Tension" author: "Tension"
email: "tension@ppanel.com" email: "tension@ppanel.com"
version: "0.0.1" version: "0.0.1"
) )
import ( import (
"./common.api" "./common.api"
"./auth/auth.api" "./auth/auth.api"
) )

View File

@ -1,11 +1,11 @@
syntax = "v1" syntax = "v1"
info( info (
title: "Node API" title: "Node API"
desc: "API for ppanel" desc: "API for ppanel"
author: "Tension" author: "Tension"
email: "tension@ppanel.com" email: "tension@ppanel.com"
version: "0.0.1" version: "0.0.1"
) )
import "./node/node.api" import "./node/node.api"

View File

@ -1,19 +1,21 @@
syntax = "v1" syntax = "v1"
info( info (
title: "User API" title: "User API"
desc: "API for ppanel" desc: "API for ppanel"
author: "Tension" author: "Tension"
email: "tension@ppanel.com" email: "tension@ppanel.com"
version: "0.0.1" version: "0.0.1"
) )
import ( import (
"./public/user.api" "./public/user.api"
"./public/subscribe.api" "./public/subscribe.api"
"./public/order.api" "./public/order.api"
"./public/announcement.api" "./public/announcement.api"
"./public/ticket.api" "./public/ticket.api"
"./public/payment.api" "./public/payment.api"
"./public/document.api" "./public/document.api"
"./public/portal.api" "./public/portal.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"`
@ -181,6 +185,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"`
@ -191,9 +196,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"`
@ -246,6 +250,14 @@ type (
SecurityConfig SecurityConfig `json:"security_config"` SecurityConfig SecurityConfig `json:"security_config"`
} }
Tuic { Tuic {
Port int `json:"port" validate:"required"`
DisableSNI bool `json:"disable_sni"`
ReduceRtt bool `json:"reduce_rtt"`
UDPRelayMode string `json:"udp_relay_mode"`
CongestionController string `json:"congestion_controller"`
SecurityConfig SecurityConfig `json:"security_config"`
}
AnyTLS {
Port int `json:"port" validate:"required"` Port int `json:"port" validate:"required"`
SecurityConfig SecurityConfig `json:"security_config"` SecurityConfig SecurityConfig `json:"security_config"`
} }
@ -264,37 +276,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"`
@ -426,7 +438,7 @@ type (
Subscribe Subscribe `json:"subscribe"` Subscribe Subscribe `json:"subscribe"`
StartTime int64 `json:"start_time"` StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"` ExpireTime int64 `json:"expire_time"`
FinishedAt int64 `json:"finished_at"` FinishedAt int64 `json:"finished_at"`
ResetTime int64 `json:"reset_time"` ResetTime int64 `json:"reset_time"`
Traffic int64 `json:"traffic"` Traffic int64 `json:"traffic"`
Download int64 `json:"download"` Download int64 `json:"download"`
@ -436,15 +448,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"`
@ -465,14 +468,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"`
@ -505,11 +500,13 @@ type (
} }
ServerRuleGroup { ServerRuleGroup {
Id int64 `json:"id"` Id int64 `json:"id"`
Icon string `json:"icon"` Icon string `json:"icon"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Type string `json:"type"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
Rules string `json:"rules"` Rules string `json:"rules"`
Enable bool `json:"enable"` Enable bool `json:"enable"`
Default bool `json:"default"`
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"` UpdatedAt int64 `json:"updated_at"`
} }
@ -520,7 +517,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"`
@ -528,18 +525,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"`
@ -557,8 +553,8 @@ type (
//public order //public order
PurchaseOrderRequest { PurchaseOrderRequest {
SubscribeId int64 `json:"subscribe_id"` SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity" validate:"required,gt=0"` Quantity int64 `json:"quantity" validate:"required,gt=0"`
Payment int64 `json:"payment,omitempty"` Payment int64 `json:"payment,omitempty"`
Coupon string `json:"coupon,omitempty"` Coupon string `json:"coupon,omitempty"`
} }
PreOrderResponse { PreOrderResponse {
@ -707,44 +703,116 @@ type (
Telephone string `json:"telephone"` Telephone string `json:"telephone"`
Address string `json:"address"` Address string `json:"address"`
} }
QueryUserAffiliateCountResponse { QueryUserAffiliateCountResponse {
Registers int64 `json:"registers"` Registers int64 `json:"registers"`
TotalCommission int64 `json:"total_commission"` TotalCommission int64 `json:"total_commission"`
} }
AppUserSubcbribe {
AppUserSubcbribe{ Id int64 `json:"id"`
Id int64 `json:"id"` Name string `json:"name"`
Name string `json:"name"` Upload int64 `json:"upload"`
Upload int64 `json:"upload"` Traffic int64 `json:"traffic"`
Traffic int64 `json:"traffic"` Download int64 `json:"download"`
Download int64 `json:"download"` DeviceLimit int64 `json:"device_limit"`
DeviceLimit int64 `json:"device_limit"` StartTime string `json:"start_time"`
StartTime string `json:"start_time"` ExpireTime string `json:"expire_time"`
ExpireTime string `json:"expire_time"` List []AppUserSubscbribeNode `json:"list"`
List []AppUserSubscbribeNode `json:"list"`
} }
AppUserSubscbribeNode {
AppUserSubscbribeNode{ Id int64 `json:"id"`
Id int64 `json:"id"` Name string `json:"name"`
Name string `json:"name"` Uuid string `json:"uuid"`
Uuid string `json:"uuid"` Protocol string `json:"protocol"`
Protocol string `json:"protocol"` RelayMode string `json:"relay_mode"`
RelayMode string `json:"relay_mode"` RelayNode string `json:"relay_node"`
RelayNode string `json:"relay_node"` ServerAddr string `json:"server_addr"`
ServerAddr string `json:"server_addr"` SpeedLimit int `json:"speed_limit"`
SpeedLimit int `json:"speed_limit"` Tags []string `json:"tags"`
Tags []string `json:"tags"` Traffic int64 `json:"traffic"`
Traffic int64 `json:"traffic"` TrafficRatio float64 `json:"traffic_ratio"`
TrafficRatio float64 `json:"traffic_ratio"` Upload int64 `json:"upload"`
Upload int64 `json:"upload"` Config string `json:"config"`
Config string `json:"config"` Country string `json:"country"`
Country string `json:"country"` City string `json:"city"`
City string `json:"city"` Latitude string `json:"latitude"`
Latitude string `json:"latitude"` Longitude string `json:"longitude"`
Longitude string `json:"longitude"` 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
} }
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

View File

@ -6,14 +6,14 @@
#### 二进制安装 #### 二进制安装
1.确定系统架构,并下载对应的二进制文件 1.确定系统架构,并下载对应的二进制文件
下载地址:`https://github.com/perfect-panel/ppanel/releases` 下载地址:`https://github.com/perfect-panel/server/releases`
示例说明系统Linux amd64用户root当前目录/root 示例说明系统Linux amd64用户root当前目录/root
- 下载二进制文件 - 下载二进制文件
```shell ```shell
$ wget https://github.com/perfect-panel/ppanel/releases/download/v0.1.0/ppanel-server-linux-amd64.tar.gz $ wget https://github.com/perfect-panel/server/releases/download/v1.0.0/ppanel-server-linux-amd64.tar.gz
``` ```
- 解压二进制文件 - 解压二进制文件

View File

@ -8,14 +8,14 @@
1. Determine your system architecture and download the corresponding binary file. 1. Determine your system architecture and download the corresponding binary file.
Download URL: `https://github.com/perfect-panel/ppanel/releases` Download URL: `https://github.com/perfect-panel/server/releases`
Example setup: OS: Linux amd64, User: root, Current directory: `/root` Example setup: OS: Linux amd64, User: root, Current directory: `/root`
- Download the binary file: - Download the binary file:
```shell ```shell
$ wget https://github.com/perfect-panel/ppanel/releases/download/v0.1.0/ppanel-server-linux-amd64.tar.gz $ wget https://github.com/perfect-panel/server/releases/download/v1.0.0/ppanel-server-linux-amd64.tar.gz
``` ```
- Extract the binary file: - Extract the binary file:

12
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
@ -56,6 +56,7 @@ require (
) )
require ( require (
github.com/Masterminds/sprig/v3 v3.3.0
github.com/fatih/color v1.18.0 github.com/fatih/color v1.18.0
github.com/goccy/go-json v0.10.4 github.com/goccy/go-json v0.10.4
github.com/golang-migrate/migrate/v4 v4.18.2 github.com/golang-migrate/migrate/v4 v4.18.2
@ -66,7 +67,10 @@ require (
require ( require (
cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
@ -99,6 +103,7 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
@ -107,12 +112,15 @@ require (
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/openzipkin/zipkin-go v0.4.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/smartwalle/ncrypto v1.0.4 // indirect github.com/smartwalle/ncrypto v1.0.4 // indirect
github.com/smartwalle/ngx v1.0.9 // indirect github.com/smartwalle/ngx v1.0.9 // indirect
github.com/smartwalle/nsign v1.0.9 // indirect github.com/smartwalle/nsign v1.0.9 // indirect

24
go.sum
View File

@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
@ -8,6 +10,12 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f h1:RDkg3pyE1qGbBpRWmvSN9RNZC5nUrOaEPiEpEb8y2f0= github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f h1:RDkg3pyE1qGbBpRWmvSN9RNZC5nUrOaEPiEpEb8y2f0=
github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f/go.mod h1:zA7AF9RTfpluCfz0omI4t5KCMaWHUMicsZoMccnaT44= github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f/go.mod h1:zA7AF9RTfpluCfz0omI4t5KCMaWHUMicsZoMccnaT44=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
@ -147,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=
@ -208,6 +216,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw= github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw=
github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts= github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
@ -249,6 +259,10 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
@ -280,13 +294,15 @@ 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=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/smartwalle/alipay/v3 v3.2.23 h1:i1VwJeu70EmwpsXXz6GZZnMAtRx5MTfn2dPoql/L3zE= github.com/smartwalle/alipay/v3 v3.2.23 h1:i1VwJeu70EmwpsXXz6GZZnMAtRx5MTfn2dPoql/L3zE=
github.com/smartwalle/alipay/v3 v3.2.23/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE= github.com/smartwalle/alipay/v3 v3.2.23/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE=
github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8= github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8=

View File

@ -17,13 +17,11 @@ func Email(ctx *svc.ServiceContext) {
logger.Debug("Email config initialization") logger.Debug("Email config initialization")
method, err := ctx.AuthModel.FindOneByMethod(context.Background(), "email") method, err := ctx.AuthModel.FindOneByMethod(context.Background(), "email")
if err != nil { if err != nil {
panic(fmt.Sprintf("failed to find email auth method: %v", err.Error())) panic(fmt.Sprintf("[Error] Initialization Failed to find email auth method: %v", err.Error()))
} }
var cfg config.EmailConfig var cfg config.EmailConfig
var emailConfig = new(auth.EmailAuthConfig) var emailConfig = new(auth.EmailAuthConfig)
if err := emailConfig.Unmarshal(method.Config); err != nil { emailConfig.Unmarshal(method.Config)
panic(fmt.Sprintf("failed to unmarshal email auth config: %v", err.Error()))
}
tool.DeepCopy(&cfg, emailConfig) tool.DeepCopy(&cfg, emailConfig)
cfg.Enable = *method.Enabled cfg.Enable = *method.Enabled
value, _ := json.Marshal(emailConfig.PlatformConfig) value, _ := json.Marshal(emailConfig.PlatformConfig)

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;
@ -398,7 +395,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;
@ -415,7 +411,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;
@ -545,6 +540,7 @@ CREATE TABLE IF NOT EXISTS `server_rule_group`
`id` bigint NOT NULL AUTO_INCREMENT, `id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Rule Group Name', `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Rule Group Name',
`icon` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Rule Group Icon', `icon` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Rule Group Icon',
`tags` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Selected Node Tags',
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Rule Group Description', `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Rule Group Description',
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Rule Group Enable', `enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Rule Group Enable',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time', `created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',

View File

@ -37,7 +37,7 @@ VALUES (1, 'Clash', 'Clash', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648
(6, 'Netch', 'Netch', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'), (6, 'Netch', 'Netch', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(7, 'Quantumult', 'Quantumult', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'), (7, 'Quantumult', 'Quantumult', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(8, 'Shadowrocket', 'Shadowrocket', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'), (8, 'Shadowrocket', 'Shadowrocket', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(9, 'Singhandle', 'Singhandle', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'), (9, 'SingBox', ' SingBox', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(10, 'Surfboard', 'Surfboard', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'), (10, 'Surfboard', 'Surfboard', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(11, 'Surge', 'Surge', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'), (11, 'Surge', 'Surge', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(12, 'V2box', 'V2box', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'), (12, 'V2box', 'V2box', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),

View File

@ -0,0 +1,3 @@
ALTER TABLE `server_rule_group`
DROP COLUMN `default`,
DROP COLUMN `type`;

View File

@ -0,0 +1,3 @@
ALTER TABLE `server_rule_group`
ADD COLUMN `default` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Is Default Group',
ADD COLUMN `type` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'Rule Group Type';

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

@ -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"
"fmt"
"github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/logger"
@ -21,9 +20,7 @@ func Mobile(ctx *svc.ServiceContext) {
} }
var cfg config.MobileConfig var cfg config.MobileConfig
var mobileConfig auth.MobileAuthConfig var mobileConfig auth.MobileAuthConfig
if err := mobileConfig.Unmarshal(method.Config); err != nil { mobileConfig.Unmarshal(method.Config)
panic(fmt.Sprintf("failed to unmarshal mobile auth config: %v", err.Error()))
}
tool.DeepCopy(&cfg, mobileConfig) tool.DeepCopy(&cfg, mobileConfig)
cfg.Enable = *method.Enabled cfg.Enable = *method.Enabled
value, _ := json.Marshal(mobileConfig.PlatformConfig) value, _ := json.Marshal(mobileConfig.PlatformConfig)

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

@ -25,6 +25,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 +53,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 {
@ -144,3 +147,8 @@ 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"`
}

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

@ -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"
)
// Delete subscribe application
func DeleteSubscribeApplicationHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.DeleteSubscribeApplicationRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := application.NewDeleteSubscribeApplicationLogic(c.Request.Context(), svcCtx)
err := l.DeleteSubscribeApplication(&req)
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)
}
}

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