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
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
run: docker push ${{ secrets.DOCKER_USERNAME }}/ppanel-server-dev:${{ env.COMMIT_ID }}
@ -38,7 +38,7 @@ jobs:
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
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..."
docker stop ppanel-server-dev
docker rm ppanel-server-dev
@ -47,4 +47,4 @@ jobs:
fi
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker run -d --restart=always --log-driver=journald --name ppanel-server-dev -p 8080:8080 -v /www/wwwroot/api/etc:/app/etc --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
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 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 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
@ -45,7 +44,6 @@ jobs:
run: |
test -f ./swagger/common.json
test -f ./swagger/user.json
test -f ./swagger/app.json
test -f ./swagger/admin.json
- name: Checkout target repository

1
.gitignore vendored
View File

@ -14,3 +14,4 @@
node_modules
package-lock.json
package.json
/bin

View File

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

View File

@ -20,7 +20,7 @@ RUN go mod download
COPY . .
# 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
# Final minimal image

108
README.md
View File

@ -2,17 +2,31 @@
<div align="center">
[![License](https://img.shields.io/github/license/perfect-panel/ppanel-server)](LICENSE)
![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue)
[![License](https://img.shields.io/github/license/perfect-panel/server)](LICENSE)
[![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)
[![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.**
[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>
> **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
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:
- **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.
@ -124,6 +138,10 @@ The documentation covers all endpoints, request/response formats, and authentica
Visit [ppanel.dev](https://ppanel.dev/) for more details.
## 🏛 Architecture
![Architecture Diagram](./doc/image/architecture-en.png)
## 📁 Directory Structure
```
@ -188,6 +206,86 @@ Supported platforms include:
Contributions are welcome! Please follow the [Contribution Guidelines](CONTRIBUTING.md) for bug fixes, features, or
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
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 {
List []AuthMethodConfig `json:"list"`
}
TestSmsSendRequest {
AreaCode string `json:"area_code" validate:"required"`
AreaCode string `json:"area_code" validate:"required"`
Telephone string `json:"telephone" validate:"required"`
}
// Test email smtp request
TestEmailSendRequest {
Email string `json:"email" validate:"required"`
}
)
@server (
@ -56,7 +53,6 @@ service ppanel {
@handler UpdateAuthMethodConfig
put /config (UpdateAuthMethodConfigRequest) returns (AuthMethodConfig)
@doc "Test sms send"
@handler TestSmsSend
post /test_sms_send (TestSmsSendRequest)

View File

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

View File

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

View File

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

View File

@ -12,19 +12,184 @@ import "../types.api"
type (
GetMessageLogListRequest {
Page int `form:"page"`
Size int `form:"size"`
Type string `form:"type"`
Platform string `form:"platform,omitempty"`
To string `form:"to,omitempty"`
Subject string `form:"subject,omitempty"`
Content string `form:"content,omitempty"`
Status int `form:"status,omitempty"`
Page int `form:"page"`
Size int `form:"size"`
Type uint8 `form:"type"`
Search string `form:"search,optional"`
}
GetMessageLogListResponse {
Total int64 `json:"total"`
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 (
@ -36,5 +201,61 @@ service ppanel {
@doc "Get message log list"
@handler GetMessageLogList
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"
type (
GetNodeServerListRequest {
Page int `form:"page" validate:"required"`
Size int `form:"size" validate:"required"`
Tag string `form:"tag,omitempty"`
GroupId int64 `form:"group_id,omitempty"`
Search string `form:"search,omitempty"`
ServerOnlineIP {
IP string `json:"ip"`
Protocol string `json:"protocol"`
}
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"`
List []Server `json:"list"`
}
UpdateNodeRequest {
Id int64 `json:"id" validate:"required"`
Tags []string `json:"tags"`
Country string `json:"country"`
City string `json:"city"`
Name string `json:"name" validate:"required"`
ServerAddr string `json:"server_addr" validate:"required"`
RelayMode string `json:"relay_mode"`
RelayNode []NodeRelay `json:"relay_node"`
SpeedLimit int `json:"speed_limit"`
TrafficRatio float32 `json:"traffic_ratio"`
GroupId int64 `json:"group_id"`
Protocol string `json:"protocol" validate:"required"`
Config interface{} `json:"config" validate:"required"`
Enable *bool `json:"enable"`
Sort int64 `json:"sort"`
GetServerProtocolsRequest {
Id int64 `form:"id"`
}
GetServerProtocolsResponse {
Protocols []Protocol `json:"protocols"`
}
Node {
Id int64 `json:"id"`
Name string `json:"name"`
Tags []string `json:"tags"`
Port uint16 `json:"port"`
Address string `json:"address"`
ServerId int64 `json:"server_id"`
Protocol string `json:"protocol"`
Enabled *bool `json:"enabled"`
Sort int `json:"sort,omitempty"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
CreateNodeRequest {
Name string `json:"name" validate:"required"`
Tags []string `json:"tags"`
Country string `json:"country"`
City string `json:"city"`
ServerAddr string `json:"server_addr" validate:"required"`
RelayMode string `json:"relay_mode"`
RelayNode []NodeRelay `json:"relay_node"`
SpeedLimit int `json:"speed_limit"`
TrafficRatio float32 `json:"traffic_ratio"`
GroupId int64 `json:"group_id"`
Protocol string `json:"protocol" validate:"required"`
Config interface{} `json:"config" validate:"required"`
Enable *bool `json:"enable"`
Sort int64 `json:"sort"`
Name string `json:"name"`
Tags []string `json:"tags,omitempty"`
Port uint16 `json:"port"`
Address string `json:"address"`
ServerId int64 `json:"server_id"`
Protocol string `json:"protocol"`
Enabled *bool `json:"enabled"`
}
UpdateNodeRequest {
Id int64 `json:"id"`
Name string `json:"name"`
Tags []string `json:"tags,omitempty"`
Port uint16 `json:"port"`
Address string `json:"address"`
ServerId int64 `json:"server_id"`
Protocol string `json:"protocol"`
Enabled *bool `json:"enabled"`
}
ToggleNodeStatusRequest {
Id int64 `json:"id"`
Enable *bool `json:"enable"`
}
DeleteNodeRequest {
Id int64 `json:"id" validate:"required"`
Id int64 `json:"id"`
}
GetNodeGroupListResponse {
Total int64 `json:"total"`
List []ServerGroup `json:"list"`
FilterNodeListRequest {
Page int `form:"page"`
Size int `form:"size"`
Search string `form:"search,omitempty"`
}
CreateNodeGroupRequest {
Name string `json:"name" validate:"required"`
Description string `json:"description"`
FilterNodeListResponse {
Total int64 `json:"total"`
List []Node `json:"list"`
}
UpdateNodeGroupRequest {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Description string `json:"description"`
HasMigrateSeverNodeResponse {
HasMigrate bool `json:"has_migrate"`
}
DeleteNodeGroupRequest {
Id int64 `json:"id" validate:"required"`
MigrateServerNodeResponse {
Succee uint64 `json:"succee"`
Fail uint64 `json:"fail"`
Message string `json:"message,omitempty"`
}
BatchDeleteNodeRequest {
Ids []int64 `json:"ids" validate:"required"`
}
BatchDeleteNodeGroupRequest {
Ids []int64 `json:"ids" validate:"required"`
}
GetNodeDetailRequest {
Id int64 `form:"id" validate:"required"`
}
NodeSortRequest {
ResetSortRequest {
Sort []SortItem `json:"sort"`
}
CreateRuleGroupRequest {
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 {
QueryNodeTagResponse {
Tags []string `json:"tags"`
}
)
@ -119,72 +152,64 @@ type (
middleware: AuthMiddleware
)
service ppanel {
@doc "Get node tag list"
@handler GetNodeTagList
get /tag/list returns (GetNodeTagListResponse)
@doc "Create Server"
@handler CreateServer
post /create (CreateServerRequest)
@doc "Get node list"
@handler GetNodeList
get /list (GetNodeServerListRequest) returns (GetNodeServerListResponse)
@doc "Update Server"
@handler UpdateServer
post /update (UpdateServerRequest)
@doc "Get node detail"
@handler GetNodeDetail
get /detail (GetNodeDetailRequest) returns (Server)
@doc "Delete Server"
@handler DeleteServer
post /delete (DeleteServerRequest)
@doc "Update node"
@handler UpdateNode
put / (UpdateNodeRequest)
@doc "Filter Server List"
@handler FilterServerList
get /list (FilterServerListRequest) returns (FilterServerListResponse)
@doc "Create node"
@doc "Get Server Protocols"
@handler GetServerProtocols
get /protocols (GetServerProtocolsRequest) returns (GetServerProtocolsResponse)
@doc "Create Node"
@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
delete / (DeleteNodeRequest)
post /node/delete (DeleteNodeRequest)
@doc "Batch delete node"
@handler BatchDeleteNode
delete /batch (BatchDeleteNodeRequest)
@doc "Filter Node List"
@handler FilterNodeList
get /node/list (FilterNodeListRequest) returns (FilterNodeListResponse)
@doc "Get node group list"
@handler GetNodeGroupList
get /group/list returns (GetNodeGroupListResponse)
@doc "Toggle Node Status"
@handler ToggleNodeStatus
post /node/status/toggle (ToggleNodeStatusRequest)
@doc "Create node group"
@handler CreateNodeGroup
post /group (CreateNodeGroupRequest)
@doc "Check if there is any server or node to migrate"
@handler HasMigrateSeverNode
get /migrate/has returns (HasMigrateSeverNodeResponse)
@doc "Update node group"
@handler UpdateNodeGroup
put /group (UpdateNodeGroupRequest)
@doc "Migrate server and node data to new database"
@handler MigrateServerNode
post /migrate/run returns (MigrateServerNodeResponse)
@doc "Delete node group"
@handler DeleteNodeGroup
delete /group (DeleteNodeGroupRequest)
@doc "Reset server sort"
@handler ResetSortWithServer
post /server/sort (ResetSortRequest)
@doc "Batch delete node group"
@handler BatchDeleteNodeGroup
delete /group/batch (BatchDeleteNodeGroupRequest)
@doc "Reset node sort"
@handler ResetSortWithNode
post /node/sort (ResetSortRequest)
@doc "Node sort "
@handler NodeSort
post /sort (NodeSortRequest)
@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)
@doc "Query all node tags"
@handler QueryNodeTag
get /node/tags returns (QueryNodeTagResponse)
}

View File

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

View File

@ -11,50 +11,6 @@ info (
import "../types.api"
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 {
Periods []TimePeriod `json:"periods"`
}
@ -86,46 +42,6 @@ service ppanel {
@handler UpdateSubscribeConfig
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"
@handler GetRegisterConfig
get /register_config returns (RegisterConfig)

View File

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

View File

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

View File

@ -32,17 +32,19 @@ type (
Id int64 `form:"id" validate:"required"`
}
UpdateUserBasiceInfoRequest {
UserId int64 `json:"user_id" validate:"required"`
Password string `json:"password"`
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
Commission int64 `json:"commission"`
GiftAmount int64 `json:"gift_amount"`
Telegram int64 `json:"telegram"`
ReferCode string `json:"refer_code"`
RefererId int64 `json:"referer_id"`
Enable bool `json:"enable"`
IsAdmin bool `json:"is_admin"`
UserId int64 `json:"user_id" validate:"required"`
Password string `json:"password"`
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
Commission int64 `json:"commission"`
ReferralPercentage uint8 `json:"referral_percentage"`
OnlyFirstPurchase bool `json:"only_first_purchase"`
GiftAmount int64 `json:"gift_amount"`
Telegram int64 `json:"telegram"`
ReferCode string `json:"refer_code"`
RefererId int64 `json:"referer_id"`
Enable bool `json:"enable"`
IsAdmin bool `json:"is_admin"`
}
UpdateUserNotifySettingRequest {
UserId int64 `json:"user_id" validate:"required"`
@ -52,18 +54,20 @@ type (
EnableTradeNotify bool `json:"enable_trade_notify"`
}
CreateUserRequest {
Email string `json:"email"`
Telephone string `json:"telephone"`
TelephoneAreaCode string `json:"telephone_area_code"`
Password string `json:"password"`
ProductId int64 `json:"product_id"`
Duration int64 `json:"duration"`
RefererUser string `json:"referer_user"`
ReferCode string `json:"refer_code"`
Balance int64 `json:"balance"`
Commission int64 `json:"commission"`
GiftAmount int64 `json:"gift_amount"`
IsAdmin bool `json:"is_admin"`
Email string `json:"email"`
Telephone string `json:"telephone"`
TelephoneAreaCode string `json:"telephone_area_code"`
Password string `json:"password"`
ProductId int64 `json:"product_id"`
Duration int64 `json:"duration"`
ReferralPercentage uint8 `json:"referral_percentage"`
OnlyFirstPurchase bool `json:"only_first_purchase"`
RefererUser string `json:"referer_user"`
ReferCode string `json:"refer_code"`
Balance int64 `json:"balance"`
Commission int64 `json:"commission"`
GiftAmount int64 `json:"gift_amount"`
IsAdmin bool `json:"is_admin"`
}
UserSubscribeDetail {
Id int64 `json:"id"`
@ -164,6 +168,15 @@ type (
List []UserLoginLog `json:"list"`
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 {
UserSubscribeId int64 `json:"user_subscribe_id"`
}
@ -251,6 +264,10 @@ service ppanel {
@handler GetUserSubscribeLogs
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"
@handler GetUserSubscribeTrafficLogs
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 (
// User login request
UserLoginRequest {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
IP string `header:"X-Original-Forwarded-For"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
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
CheckUserRequest {
@ -23,43 +23,41 @@ type (
}
// User login response
CheckUserResponse {
exist bool `json:"exist"`
Exist bool `json:"exist"`
}
// User login response
UserRegisterRequest {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Invite string `json:"invite,optional"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Invite string `json:"invite,optional"`
Code string `json:"code,optional"`
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
ResetPasswordRequest {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"`
CfToken string `json:"cf_token,optional"`
}
LoginResponse {
Token string `json:"token"`
}
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"`
}
OAuthLoginResponse {
Redirect string `json:"redirect"`
}
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"`
}
// login request
TelephoneLoginRequest {
Telephone string `json:"telephone" validate:"required"`
@ -67,6 +65,7 @@ type (
TelephoneAreaCode string `json:"telephone_area_code" validate:"required"`
Password string `json:"password"`
IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"`
}
// Check user is exist request
@ -76,7 +75,7 @@ type (
}
// User login response
TelephoneCheckUserResponse {
exist bool `json:"exist"`
Exist bool `json:"exist"`
}
// User login response
TelephoneRegisterRequest {
@ -86,6 +85,7 @@ type (
Invite string `json:"invite,optional"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"`
}
// User login response
@ -95,6 +95,7 @@ type (
Password string `json:"password" validate:"required"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"`
}
AppleLoginCallbackRequest {
@ -103,7 +104,7 @@ type (
State string `form:"state"`
}
GoogleLoginCallbackRequest {
Code string `form:"code"`
Code string `form:"code"`
State string `form:"state"`
}
)

View File

@ -35,10 +35,6 @@ type (
GetTosResponse {
TosContent string `json:"tos_content"`
}
GetAppcationResponse {
Config ApplicationConfig `json:"config"`
Applications []ApplicationResponseInfo `json:"applications"`
}
// GetCodeRequest Get code request
SendCodeRequest {
Email string `json:"email" validate:"required"`
@ -70,14 +66,26 @@ type (
List []Ads `json:"list"`
}
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"`
Code string `json:"code" validate:"required"`
Type uint8 `json:"type" validate:"required"`
Code string `json:"code" validate:"required"`
Type uint8 `json:"type" validate:"required"`
}
CheckVerificationCodeRespone{
Status bool `json:"status"`
CheckVerificationCodeRespone {
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
get /site/config returns (GetGlobalConfigResponse)
@doc "Get Tos Content"
@handler GetApplication
get /application returns (GetAppcationResponse)
@doc "Get Tos Content"
@handler GetTos
get /site/tos returns (GetTosResponse)
@ -121,5 +125,9 @@ service ppanel {
@doc "Check verification code"
@handler CheckVerificationCode
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"
type (
OnlineUser {
SID int64 `json:"uid"`
IP string `json:"ip"`
}
ShadowsocksProtocol {
Port int `json:"port"`
Method string `json:"method"`
@ -44,9 +48,9 @@ type (
ServerCommon
}
GetServerConfigResponse {
Basic ServerBasic `json:"basic"`
Protocol string `json:"protocol"`
Config interface{} `json:"config"`
Basic ServerBasic `json:"basic"`
Protocol string `json:"protocol"`
Config interface{} `json:"config"`
}
ServerBasic {
PushInterval int64 `json:"push_interval"`
@ -60,8 +64,8 @@ type (
ServerUser {
Id int64 `json:"id"`
UUID string `json:"uuid"`
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
}
GetServerUserListRequest {
ServerCommon
@ -78,7 +82,6 @@ type (
ServerCommon
Traffic []UserTraffic `json:"traffic"`
}
ServerPushStatusRequest {
ServerCommon
Cpu float64 `json:"cpu"`
@ -90,11 +93,18 @@ type (
ServerCommon
Users []OnlineUser `json:"users"`
}
QueryServerConfigRequest {
ServerID int64 `path:"server_id"`
SecretKey string `header:"secret_key"`
}
QueryServerConfigResponse {
Protocols []Protocol `json:"protocols"`
}
)
@server (
prefix: v1/server
group: server
prefix: v1/server
group: server
middleware: ServerMiddleware
)
service ppanel {
@ -119,3 +129,13 @@ service ppanel {
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"
@server (
prefix: v1/public/announcement
group: public/announcement

View File

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

View File

@ -19,17 +19,20 @@ type (
SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity"`
Coupon string `json:"coupon,omitempty"`
InviteCode string `json:"invite_code,omitempty"`
InviteCode string `json:"invite_code,omitempty"`
TurnstileToken string `json:"turnstile_token,omitempty"`
}
PortalPurchaseResponse {
OrderNo string `json:"order_no"`
}
GetSubscriptionRequest {
Language string `form:"language"`
}
GetSubscriptionResponse {
List []Subscribe `json:"list"`
}
PrePurchaseOrderRequest {
Payment int64 `json:"payment,omitempty"`
Payment int64 `json:"payment,omitempty"`
SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity"`
Coupon string `json:"coupon,omitempty"`
@ -75,7 +78,7 @@ service ppanel {
@doc "Get Subscription"
@handler GetSubscription
get /subscribe returns (GetSubscriptionResponse)
get /subscribe (GetSubscriptionRequest) returns (GetSubscriptionResponse)
@doc "Pre Purchase Order"
@handler PrePurchaseOrder

View File

@ -10,6 +10,12 @@ info (
import "../types.api"
type (
QuerySubscribeListRequest {
Language string `form:"language"`
}
)
@server (
prefix: v1/public/subscribe
group: public/subscribe
@ -18,14 +24,6 @@ import "../types.api"
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)
get /list (QuerySubscribeListRequest) returns (QuerySubscribeListResponse)
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +1,21 @@
syntax = "v1"
info(
title: "User API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
info (
title: "User API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import (
"./public/user.api"
"./public/subscribe.api"
"./public/order.api"
"./public/announcement.api"
"./public/ticket.api"
"./public/payment.api"
"./public/document.api"
"./public/portal.api"
"./public/user.api"
"./public/subscribe.api"
"./public/order.api"
"./public/announcement.api"
"./public/ticket.api"
"./public/payment.api"
"./public/document.api"
"./public/portal.api"
)

View File

@ -14,6 +14,8 @@ type (
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
Commission int64 `json:"commission"`
ReferralPercentage uint8 `json:"referral_percentage"`
OnlyFirstPurchase bool `json:"only_first_purchase"`
GiftAmount int64 `json:"gift_amount"`
Telegram int64 `json:"telegram"`
ReferCode string `json:"refer_code"`
@ -63,6 +65,8 @@ type (
SubscribePath string `json:"subscribe_path"`
SubscribeDomain string `json:"subscribe_domain"`
PanDomain bool `json:"pan_domain"`
UserAgentLimit bool `json:"user_agent_limit"`
UserAgentList string `json:"user_agent_list"`
}
VerifyCodeConfig {
VerifyCodeExpireTime int64 `json:"verify_code_expire_time"`
@ -181,6 +185,7 @@ type (
Subscribe {
Id int64 `json:"id"`
Name string `json:"name"`
Language string `json:"language"`
Description string `json:"description"`
UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"`
@ -191,9 +196,8 @@ type (
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"`
ServerGroup []int64 `json:"server_group"`
Server []int64 `json:"server"`
Nodes []int64 `json:"nodes"`
NodeTags []string `json:"node_tags"`
Show bool `json:"show"`
Sell bool `json:"sell"`
Sort int64 `json:"sort"`
@ -246,6 +250,14 @@ type (
SecurityConfig SecurityConfig `json:"security_config"`
}
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"`
SecurityConfig SecurityConfig `json:"security_config"`
}
@ -264,37 +276,37 @@ type (
Host string `json:"host"`
ServiceName string `json:"service_name"`
}
Server {
Id int64 `json:"id"`
Tags []string `json:"tags"`
Country string `json:"country"`
City string `json:"city"`
Name string `json:"name"`
ServerAddr string `json:"server_addr"`
RelayMode string `json:"relay_mode"`
RelayNode []NodeRelay `json:"relay_node"`
SpeedLimit int `json:"speed_limit"`
TrafficRatio float32 `json:"traffic_ratio"`
GroupId int64 `json:"group_id"`
Protocol string `json:"protocol"`
Config interface{} `json:"config"`
Enable *bool `json:"enable"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
Status *NodeStatus `json:"status"`
Sort int64 `json:"sort"`
}
OnlineUser {
SID int64 `json:"uid"`
IP string `json:"ip"`
}
NodeStatus {
Online interface{} `json:"online"`
Cpu float64 `json:"cpu"`
Mem float64 `json:"mem"`
Disk float64 `json:"disk"`
UpdatedAt int64 `json:"updated_at"`
}
// Server {
// Id int64 `json:"id"`
// Tags []string `json:"tags"`
// Country string `json:"country"`
// City string `json:"city"`
// Name string `json:"name"`
// ServerAddr string `json:"server_addr"`
// RelayMode string `json:"relay_mode"`
// RelayNode []NodeRelay `json:"relay_node"`
// SpeedLimit int `json:"speed_limit"`
// TrafficRatio float32 `json:"traffic_ratio"`
// GroupId int64 `json:"group_id"`
// Protocol string `json:"protocol"`
// Config interface{} `json:"config"`
// Enable *bool `json:"enable"`
// CreatedAt int64 `json:"created_at"`
// UpdatedAt int64 `json:"updated_at"`
// Status *NodeStatus `json:"status"`
// Sort int64 `json:"sort"`
// }
// OnlineUser {
// SID int64 `json:"uid"`
// IP string `json:"ip"`
// }
// NodeStatus {
// Online interface{} `json:"online"`
// Cpu float64 `json:"cpu"`
// Mem float64 `json:"mem"`
// Disk float64 `json:"disk"`
// UpdatedAt int64 `json:"updated_at"`
// }
ServerGroup {
Id int64 `json:"id"`
Name string `json:"name"`
@ -426,7 +438,7 @@ type (
Subscribe Subscribe `json:"subscribe"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
FinishedAt int64 `json:"finished_at"`
FinishedAt int64 `json:"finished_at"`
ResetTime int64 `json:"reset_time"`
Traffic int64 `json:"traffic"`
Download int64 `json:"download"`
@ -436,15 +448,6 @@ type (
CreatedAt int64 `json:"created_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 {
Avatar string `json:"avatar"`
Identifier string `json:"identifier"`
@ -465,14 +468,6 @@ type (
Port int `json:"port"`
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 {
Id int64 `json:"id"`
Ip string `json:"ip"`
@ -505,11 +500,13 @@ type (
}
ServerRuleGroup {
Id int64 `json:"id"`
Icon string `json:"icon"`
Icon string `json:"icon"`
Name string `json:"name" validate:"required"`
Type string `json:"type"`
Tags []string `json:"tags"`
Rules string `json:"rules"`
Enable bool `json:"enable"`
Default bool `json:"default"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
@ -520,7 +517,7 @@ type (
Token string `json:"token"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
CreatedAt int64 `json:"created_at"`
Timestamp int64 `json:"timestamp"`
}
UserLoginLog {
Id int64 `json:"id"`
@ -528,18 +525,17 @@ type (
LoginIP string `json:"login_ip"`
UserAgent string `json:"user_agent"`
Success bool `json:"success"`
CreatedAt int64 `json:"created_at"`
Timestamp int64 `json:"timestamp"`
}
MessageLog {
Id int64 `json:"id"`
Type string `json:"type"`
Platform string `json:"platform"`
To string `json:"to"`
Subject string `json:"subject"`
Content string `json:"content"`
Status int `json:"status"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
Id int64 `json:"id"`
Type uint8 `json:"type"`
Platform string `json:"platform"`
To string `json:"to"`
Subject string `json:"subject"`
Content interface{} `json:"content"`
Status uint8 `json:"status"`
CreatedAt int64 `json:"created_at"`
}
Ads {
Id int `json:"id"`
@ -557,8 +553,8 @@ type (
//public order
PurchaseOrderRequest {
SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity" validate:"required,gt=0"`
Payment int64 `json:"payment,omitempty"`
Quantity int64 `json:"quantity" validate:"required,gt=0"`
Payment int64 `json:"payment,omitempty"`
Coupon string `json:"coupon,omitempty"`
}
PreOrderResponse {
@ -707,44 +703,116 @@ type (
Telephone string `json:"telephone"`
Address string `json:"address"`
}
QueryUserAffiliateCountResponse {
Registers int64 `json:"registers"`
TotalCommission int64 `json:"total_commission"`
}
AppUserSubcbribe{
Id int64 `json:"id"`
Name string `json:"name"`
Upload int64 `json:"upload"`
Traffic int64 `json:"traffic"`
Download int64 `json:"download"`
DeviceLimit int64 `json:"device_limit"`
StartTime string `json:"start_time"`
ExpireTime string `json:"expire_time"`
List []AppUserSubscbribeNode `json:"list"`
AppUserSubcbribe {
Id int64 `json:"id"`
Name string `json:"name"`
Upload int64 `json:"upload"`
Traffic int64 `json:"traffic"`
Download int64 `json:"download"`
DeviceLimit int64 `json:"device_limit"`
StartTime string `json:"start_time"`
ExpireTime string `json:"expire_time"`
List []AppUserSubscbribeNode `json:"list"`
}
AppUserSubscbribeNode{
Id int64 `json:"id"`
Name string `json:"name"`
Uuid string `json:"uuid"`
Protocol string `json:"protocol"`
RelayMode string `json:"relay_mode"`
RelayNode string `json:"relay_node"`
ServerAddr string `json:"server_addr"`
SpeedLimit int `json:"speed_limit"`
Tags []string `json:"tags"`
Traffic int64 `json:"traffic"`
TrafficRatio float64 `json:"traffic_ratio"`
Upload int64 `json:"upload"`
Config string `json:"config"`
Country string `json:"country"`
City string `json:"city"`
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
CreatedAt int64 `json:"created_at"`
Download int64 `json:"download"`
AppUserSubscbribeNode {
Id int64 `json:"id"`
Name string `json:"name"`
Uuid string `json:"uuid"`
Protocol string `json:"protocol"`
RelayMode string `json:"relay_mode"`
RelayNode string `json:"relay_node"`
ServerAddr string `json:"server_addr"`
SpeedLimit int `json:"speed_limit"`
Tags []string `json:"tags"`
Traffic int64 `json:"traffic"`
TrafficRatio float64 `json:"traffic_ratio"`
Upload int64 `json:"upload"`
Config string `json:"config"`
Country string `json:"country"`
City string `json:"city"`
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
CreatedAt int64 `json:"created_at"`
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.确定系统架构,并下载对应的二进制文件
下载地址:`https://github.com/perfect-panel/ppanel/releases`
下载地址:`https://github.com/perfect-panel/server/releases`
示例说明系统Linux amd64用户root当前目录/root
- 下载二进制文件
```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.
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`
- Download the binary file:
```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:

12
go.mod
View File

@ -20,7 +20,7 @@ require (
github.com/go-sql-driver/mysql v1.8.1
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
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/gorilla/websocket v1.5.3
github.com/hibiken/asynq v0.24.1
@ -28,7 +28,7 @@ require (
github.com/klauspost/compress v1.17.7
github.com/nyaruka/phonenumbers v1.5.0
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/spf13/cast v1.7.0 // indirect
github.com/spf13/cobra v1.8.1
@ -56,6 +56,7 @@ require (
)
require (
github.com/Masterminds/sprig/v3 v3.3.0
github.com/fatih/color v1.18.0
github.com/goccy/go-json v0.10.4
github.com/golang-migrate/migrate/v4 v4.18.2
@ -66,7 +67,10 @@ require (
require (
cloud.google.com/go/compute/metadata v0.6.0 // indirect
dario.cat/mergo v1.0.1 // 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/debug v1.0.1 // 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/hashicorp/errwrap v1.1.0 // 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/jinzhu/inflection v1.0.0 // 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/mattn/go-colorable v0.1.13 // 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/reflect2 v1.0.2 // indirect
github.com/openzipkin/zipkin-go v0.4.2 // 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/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/ngx 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/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
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/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
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/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/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/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
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/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
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.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
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/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk=
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/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw=
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/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
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.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
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/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
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/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.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/redis/go-redis/v9 v9.7.2 h1:PSGhv13dJyrTCw1+55H0pIKM3WFov7HuUrKUmInGL0o=
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/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/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
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/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE=
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")
method, err := ctx.AuthModel.FindOneByMethod(context.Background(), "email")
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 emailConfig = new(auth.EmailAuthConfig)
if err := emailConfig.Unmarshal(method.Config); err != nil {
panic(fmt.Sprintf("failed to unmarshal email auth config: %v", err.Error()))
}
emailConfig.Unmarshal(method.Config)
tool.DeepCopy(&cfg, emailConfig)
cfg.Enable = *method.Enabled
value, _ := json.Marshal(emailConfig.PlatformConfig)

View File

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

View File

@ -91,7 +91,6 @@ CREATE TABLE IF NOT EXISTS `auth_method`
PRIMARY KEY (`id`),
UNIQUE KEY `uni_auth_method` (`method`)
) ENGINE = InnoDB
AUTO_INCREMENT = 9
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
@ -305,7 +304,6 @@ CREATE TABLE IF NOT EXISTS `subscribe_type`
`updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 15
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
@ -323,7 +321,6 @@ CREATE TABLE IF NOT EXISTS `system`
UNIQUE KEY `uni_system_key` (`key`),
KEY `index_key` (`key`)
) ENGINE = InnoDB
AUTO_INCREMENT = 42
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
@ -398,7 +395,6 @@ CREATE TABLE IF NOT EXISTS `user`
PRIMARY KEY (`id`),
KEY `idx_referer` (`referer_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
@ -415,7 +411,6 @@ CREATE TABLE IF NOT EXISTS `user_auth_methods`
UNIQUE KEY `idx_auth_identifier` (`auth_identifier`),
KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
@ -545,6 +540,7 @@ CREATE TABLE IF NOT EXISTS `server_rule_group`
`id` bigint NOT NULL AUTO_INCREMENT,
`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',
`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',
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Rule Group Enable',
`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'),
(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'),
(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'),
(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'),

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 (
"testing"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/pkg/orm"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func getDSN() string {
@ -30,3 +33,17 @@ func TestMigrate(t *testing.T) {
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 (
"context"
"encoding/json"
"fmt"
"github.com/perfect-panel/server/pkg/logger"
@ -21,9 +20,7 @@ func Mobile(ctx *svc.ServiceContext) {
}
var cfg config.MobileConfig
var mobileConfig auth.MobileAuthConfig
if err := mobileConfig.Unmarshal(method.Config); err != nil {
panic(fmt.Sprintf("failed to unmarshal mobile auth config: %v", err.Error()))
}
mobileConfig.Unmarshal(method.Config)
tool.DeepCopy(&cfg, mobileConfig)
cfg.Enable = *method.Enabled
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
const SubscribeConfigKey = "system:subscribe_config"
// ApplicationKey Application Key
const ApplicationKey = "system:application"
// RegisterConfigKey Register Config Key
const RegisterConfigKey = "system:register_config"
@ -51,26 +48,12 @@ const AuthCodeCacheKey = "auth:verify:email"
// AuthCodeTelephoneCacheKey Register Code Cache Key
const AuthCodeTelephoneCacheKey = "auth:verify:telephone"
// ServerUserListCacheKey Server User List Cache Key
const ServerUserListCacheKey = "server:user_list:id:"
// ServerConfigCacheKey Server Config Cache Key
const ServerConfigCacheKey = "server:config:id:"
// CommonStat Cache Key
// CommonStatCacheKey CommonStat Cache Key
const CommonStatCacheKey = "common:stat"
// ServerStatusCacheKey Server Status Cache Key
const ServerStatusCacheKey = "server:status:id:"
// ServerCountCacheKey Server Count Cache Key
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
const SendIntervalKeyPrefix = "send:interval:"

View File

@ -25,6 +25,7 @@ type Config struct {
Subscribe SubscribeConfig `yaml:"Subscribe"`
Invite InviteConfig `yaml:"Invite"`
Telegram Telegram `yaml:"Telegram"`
Log Log `yaml:"Log"`
Administrator struct {
Email string `yaml:"Email" default:"admin@ppanel.dev"`
Password string `yaml:"Password" default:"password"`
@ -52,9 +53,11 @@ type Verify struct {
type SubscribeConfig struct {
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:""`
PanDomain bool `yaml:"PanDomain" default:"false"`
UserAgentLimit bool `yaml:"UserAgentLimit" default:"false"`
UserAgentList string `yaml:"UserAgentList" default:""`
}
type RegisterConfig struct {
@ -144,3 +147,8 @@ type VerifyCode struct {
Limit int64 `yaml:"Limit" default:"15"`
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 (
"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/types"
"github.com/perfect-panel/server/pkg/result"
)
// Reset user subscription period
func ResetUserSubscribePeriodHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
// Preview Template
func PreviewSubscribeTemplateHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.UserSubscribeResetPeriodRequest
var req types.PreviewSubscribeTemplateRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
@ -19,8 +19,9 @@ func ResetUserSubscribePeriodHandler(svcCtx *svc.ServiceContext) func(c *gin.Con
return
}
l := subscribe.NewResetUserSubscribePeriodLogic(c.Request.Context(), svcCtx)
resp, err := l.ResetUserSubscribePeriod(&req)
l := application.NewPreviewSubscribeTemplateLogic(c.Request.Context(), svcCtx)
resp, err := l.PreviewSubscribeTemplate(&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"
)
// 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