init: 1.0.0

This commit is contained in:
Chang lue Tsen 2025-04-25 12:08:29 +09:00
commit 8addcc584b
1031 changed files with 76472 additions and 0 deletions

3
.commitlintrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": ["@commitlint/config-conventional"]
}

74
.github/workflows/bindist_external.yaml vendored Normal file
View File

@ -0,0 +1,74 @@
name: Build matrix binaries and push to external repo
run-name: Build ${{ github.ref_name }} of ${{ github.repository }}
on:
push:
tags:
- 'v*' # match semver only, e.g. v1.2.3
jobs:
prepare_release:
runs-on: ubuntu-24.04
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- name: Extract Go version (from go.mod)
id: extract_go_version
run: | # Read Go version from go.mod and save it to the VER_GO environment variable.
VER_GO=$(grep -m 1 go go.mod | cut -d\ -f2)
echo "VER_GO=$VER_GO" >> $GITHUB_ENV
- name: Extract PPanel(api) version (from pkg/constant/types.go)
id: extract_api_version
run: | # Read API version from pkg/constant/types.go and save it to the VER_API environment variable.
VER_API=$(grep -oP '(?<=const Version = \")[0-9]+\.[0-9]+\.[0-9]+' pkg/constant/version.go)
echo "VER_API=$VER_API" >> $GITHUB_ENV
- name: Create release in destination repo
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.REL_TOKEN }}
with:
tag_name: ${{ github.ref_name }}
release_name: PPanel Server ${{ github.ref_name }}
body: |
PPanel Server ${{ github.ref_name }}
PPanel API v${{ env.VER_API }}
draft: false
prerelease: false
owner: ${{ vars.REL_OWNER }}
repo: ${{ vars.REL_REPO }}
commitish: master
build_matrix:
needs: prepare_release
strategy:
matrix:
goos: [linux, windows, darwin]
goarch: [amd64, arm64]
runs-on: ubuntu-24.04
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- name: Build binaries and push to external repo
id: build_and_push
uses: wangyoucao577/go-release-action@v1
with:
github_token: ${{ secrets.REL_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
goversion: ${{ env.VER_GO }}
binary_name: ppanel-server
pre_command: export CGO_ENABLED=0
ldflags: -s -w
release_repo: ${{ vars.REL_OWNER }}/${{ vars.REL_REPO }}
extra_files: LICENSE etc
md5sum: false

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

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

124
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,124 @@
name: Build and Publish Docker Image
on:
# release:
# types: [published]
push:
tags:
- 'v*'
jobs:
build-docker:
runs-on: ubuntu-latest
env:
IMAGE_NAME: ppanel-server
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract version from pkg/constant/types.go
id: version
run: |
# 提取版本号 0.1.3
VERSION=$(grep -oP '(?<=const Version = ")[0-9]+\.[0-9]+\.[0-9]+' pkg/constant/version.go)
echo "VERSION=$VERSION"
# 提取构建号 01300
BUILD_NUMBER=$(grep -oP 'const Version = "[0-9]+\.[0-9]+\.[0-9]+\(\K[0-9]+' pkg/constant/version.go)
echo "BUILD_NUMBER=$BUILD_NUMBER"
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
- name: Get short SHA
id: sha
run: echo "GIT_SHA=${GITHUB_SHA::8}" >> $GITHUB_ENV
- name: Build and push Docker image for main release
if: "!contains(github.ref_name, 'beta')"
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest
${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}-${{ env.BUILD_NUMBER }}
- name: Build and push Docker image for beta release
if: contains(github.ref_name, 'beta')
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:beta
${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}-${{ env.BUILD_NUMBER }}-beta
release-notes:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23
- name: Install GoReleaser
run: |
go install github.com/goreleaser/goreleaser@latest
- name: Run GoReleaser
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
goreleaser check
goreleaser release --clean
releases-matrix:
name: Release ppanel-server binary
runs-on: ubuntu-latest
needs: release-notes # wait for release-notes job to finish
strategy:
matrix:
# build and publish in parallel: linux/386, linux/amd64, linux/arm64,
# windows/386, windows/amd64, windows/arm64, darwin/amd64, darwin/arm64
goos: [ linux, windows, darwin ]
goarch: [ '386', amd64, arm64 ]
exclude:
- goarch: '386'
goos: darwin
steps:
- uses: actions/checkout@v4
- uses: wangyoucao577/go-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
asset_name: "ppanel-server-${{ matrix.goos }}-${{ matrix.goarch }}"
goversion: "https://dl.google.com/go/go1.23.3.linux-amd64.tar.gz"
project_path: "."
binary_name: "ppanel-server"
extra_files: LICENSE etc

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

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

17
.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
.idea/
.vscode/
*-dev.yaml
*.local.yaml
/test/
*.log
.DS_Store
*_test_config.go
*.log*
/build/
etc/ppanel.yaml
*.p8
*.crt
*.key
node_modules
package-lock.json
package.json

66
.goreleaser.yaml Normal file
View File

@ -0,0 +1,66 @@
project_name: ppanel
version: 1
release:
prerelease: auto
builds:
- # If true, skip the build.
# Useful for library projects.
# Default is false
skip: true
changelog:
# Set it to true if you wish to skip the changelog generation.
# This may result in an empty release notes on GitHub/GitLab/Gitea.
disable: false
# Changelog generation implementation to use.
#
# Valid options are:
# - `git`: uses `git log`;
# - `github`: uses the compare GitHub API, appending the author login to the changelog.
# - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog.
# - `github-native`: uses the GitHub release notes generation API, disables the groups feature.
#
# Defaults to `git`.
use: github
# Sorts the changelog by the commit's messages.
# Could either be asc, desc or empty
# Default is empty
sort: asc
# Format to use for commit formatting.
# Only available when use is one of `github`, `gitea`, or `gitlab`.
#
# Default: '{{ .SHA }}: {{ .Message }} ({{ with .AuthorUsername }}@{{ . }}{{ else }}{{ .AuthorName }} <{{ .AuthorEmail }}>{{ end }})'.
# Extra template fields: `SHA`, `Message`, `AuthorName`, `AuthorEmail`, and
# `AuthorUsername`.
format: "{{ .Message }}"
# Group commits messages by given regex and title.
# Order value defines the order of the groups.
# Proving no regex means all commits will be grouped under the default group.
# Groups are disabled when using github-native, as it already groups things by itself.
#
# Default is no groups.
groups:
- title: "✨ Features"
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: "🐛 Bug Fixes"
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: "🎫 Chores"
regexp: "^.*chore[(\\w)]*:+.*$"
order: 2
- title: "🔨 Refactor"
regexp: "^.*refactor[(\\w)]*:+.*$"
order: 3
- title: "🔧 Build"
regexp: "^.*?(ci)(\\(.+\\))??!?:.+$"
order: 4
- title: "📝 Documentation"
regexp: "^.*?docs?(\\(.+\\))??!?:.+$"
order: 5
- title: "✨ Others"
order: 999

View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="go build github.com/perfect-panel/ppanel-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/ppanel-server" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/ppanel.go" />
<method v="2" />
</configuration>
</component>

40
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,40 @@
# Pull Request Submission Guidelines
To ensure the quality of the codebase and maintainability of the project, please follow these guidelines before submitting a Pull Request (PR):
## 1. PR Title and Description
- **Clear Title**: Concisely describe the main content of the PR, for example:
- Fix: Correct error messages in user login
- Feature: Add order export functionality
- **Detailed Description**: Include the following details in the description:
- Purpose and background of this PR.
- Detailed explanation of the changes.
- For bug fixes, describe the steps to reproduce the issue.
- For new features, explain how to use them.
- Link related issues (if any) using keywords like `Closes #123`.
## 2. Code Checks Before Submission
- **Code Style**: Ensure the code adheres to the project's coding standards (e.g., ESLint, Prettier, or GoLint).
- **Functional Testing**: Fully test new features or bug fixes to ensure no missing functionality or regressions.
- **Unit Tests**: Write unit tests for added or modified functionality and ensure all tests pass.
- **Documentation Updates**: Update documentation if the PR includes new features or API changes.
## 3. Branch Strategy
- **Correct Branch**:
- Develop new features based on `feature/*` branches.
- Fix bugs based on `fix/*` branches.
- Ensure the target branch of the PR aligns with the project's branching strategy.
- **Sync with Base Branch**: Before submitting the PR, ensure your branch is up-to-date with the target branch (e.g., `main` or `develop`).
## 4. Review Process
- **Small Commits**: Avoid submitting excessive changes in a single PR; break it into smaller logical units.
---
Thank you for your contribution!

44
CONTRIBUTING_ZH.md Normal file
View File

@ -0,0 +1,44 @@
# Pull Request 提交须知
为了确保代码库的质量和项目的可维护性,在提交 Pull RequestPR之前请务必遵循以下准则
## 1. PR 标题和描述
- **标题清晰**:简明扼要地描述 PR 的主要内容,例如:
- Fix: 修复用户登录时的错误提示
- Feature: 添加订单导出功能
- **描述详细**:在描述中包括以下内容:
- 此 PR 的目的和背景。
- 变更的详细说明。
- 如果涉及 Bug 修复,需描述问题重现的步骤。
- 如果涉及新功能,需描述其使用方式。
- 关联的 Issue如有使用关键字关闭 Issue例如`Closes #123`
## 2. 提交代码前的检查
- **代码风格**:确保代码符合项目的代码规范。
- **功能测试**:对新功能或 Bug 修复进行全面测试,确保没有功能缺失或回归问题。
- **单元测试**:为新增或修改的功能编写单元测试,并确保所有测试通过(工具类即`pkg/*`下面的必须带有单元测试)。
- **文档更新**:如果 PR 涉及新功能或接口更改,确保文档同步更新。
## 3. 分支策略
- **正确的分支**
- 新功能应基于 `feature/*` 分支进行开发。
- Bug 修复应基于 `fix/*` 分支。
- 确保 PR 的目标分支与项目的分支策略一致。
- **同步主干代码**:在提交 PR 之前请确保分支已经与目标分支develop同步。
## 4. 审查流程
- **小型提交**:避免一次性提交过多的更改,将 PR 拆分为更小的逻辑单元。
---
感谢您的贡献!

44
Dockerfile Normal file
View File

@ -0,0 +1,44 @@
# Use a smaller base image for the build stage
FROM golang:alpine AS builder
LABEL stage=gobuilder
ARG TARGETARCH
ENV CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH}
# Combine apk commands into one to reduce layer size
RUN apk update --no-cache && apk add --no-cache tzdata ca-certificates
WORKDIR /build
# Copy go.mod and go.sum first to take advantage of Docker caching
COPY go.mod go.sum ./
RUN go mod download
# Copy the rest of the application code
COPY . .
# Build the binary with optimization flags to reduce binary size
RUN go build -ldflags="-s -w" -o /app/ppanel ppanel.go
# Final minimal image
FROM scratch
# Copy CA certificates and timezone data
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ=Asia/Shanghai
# Set working directory and copy binary
WORKDIR /app
COPY --from=builder /app/ppanel /app/ppanel
COPY --from=builder /etc /app/etc
# Expose the port (optional)
EXPOSE 8080
# Specify entry point
ENTRYPOINT ["/app/ppanel"]
CMD ["run", "--config", "etc/ppanel.yaml"]

674
LICENSE Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

79
apis/admin/ads.api Normal file
View File

@ -0,0 +1,79 @@
syntax = "v1"
info (
title: "Ads API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
type (
CreateAdsRequest {
Title string `json:"title"`
Type string `json:"type"`
Content string `json:"content"`
Description string `json:"description"`
TargetURL string `json:"target_url"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
Status int `json:"status"`
}
UpdateAdsRequest {
Id int64 `json:"id"`
Title string `json:"title"`
Type string `json:"type"`
Content string `json:"content"`
Description string `json:"description"`
TargetURL string `json:"target_url"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
Status int `json:"status"`
}
DeleteAdsRequest {
Id int64 `json:"id"`
}
GetAdsListRequest {
Page int `form:"page"`
Size int `form:"size"`
Status *int `form:"status,omitempty"`
Search string `form:"search,omitempty"`
}
GetAdsListResponse {
Total int64 `json:"total"`
List []Ads `json:"list"`
}
GetAdsDetailRequest {
Id int64 `form:"id"`
}
)
import "../types.api"
@server (
prefix: v1/admin/ads
group: admin/ads
middleware: AuthMiddleware
)
service ppanel {
@doc "Create Ads"
@handler CreateAds
post / (CreateAdsRequest)
@doc "Update Ads"
@handler UpdateAds
put / (UpdateAdsRequest)
@doc "Delete Ads"
@handler DeleteAds
delete / (DeleteAdsRequest)
@doc "Get Ads List"
@handler GetAdsList
get /list (GetAdsListRequest) returns (GetAdsListResponse)
@doc "Get Ads Detail"
@handler GetAdsDetail
get /detail (GetAdsDetailRequest) returns (Ads)
}

View File

@ -0,0 +1,76 @@
syntax = "v1"
info (
title: "Announcement API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
CreateAnnouncementRequest {
Title string `json:"title" validate:"required"`
Content string `json:"content" validate:"required"`
}
UpdateAnnouncementRequest {
Id int64 `json:"id" validate:"required"`
Title string `json:"title"`
Content string `json:"content"`
Show *bool `json:"show"`
Pinned *bool `json:"pinned"`
Popup *bool `json:"popup"`
}
UpdateAnnouncementEnableRequest {
Id int64 `json:"id" validate:"required"`
Enable *bool `json:"enable" validate:"required"`
}
DeleteAnnouncementRequest {
Id int64 `json:"id" validate:"required"`
}
GetAnnouncementListRequest {
Page int64 `form:"page"`
Size int64 `form:"size"`
Show *bool `form:"show,omitempty"`
Pinned *bool `form:"pinned,omitempty"`
Popup *bool `form:"popup,omitempty"`
Search string `form:"search,omitempty"`
}
GetAnnouncementListResponse {
Total int64 `json:"total"`
List []Announcement `json:"list"`
}
GetAnnouncementRequest {
Id int64 `form:"id" validate:"required"`
}
)
@server (
prefix: v1/admin/announcement
group: admin/announcement
middleware: AuthMiddleware
)
service ppanel {
@doc "Create announcement"
@handler CreateAnnouncement
post / (CreateAnnouncementRequest)
@doc "Update announcement"
@handler UpdateAnnouncement
put / (UpdateAnnouncementRequest)
@doc "Get announcement list"
@handler GetAnnouncementList
get /list (GetAnnouncementListRequest) returns (GetAnnouncementListResponse)
@doc "Delete announcement"
@handler DeleteAnnouncement
delete / (DeleteAnnouncementRequest)
@doc "Get announcement"
@handler GetAnnouncement
get /detail (GetAnnouncementRequest) returns (Announcement)
}

76
apis/admin/auth.api Normal file
View File

@ -0,0 +1,76 @@
syntax = "v1"
info (
title: "Auth Method Management"
desc: "System auth method management"
author: "Tension"
email: "tension@ppanel.com"
version: "0.1.2"
)
import (
"../types.api"
)
type (
UpdateAuthMethodConfigRequest {
Id int64 `json:"id"`
Method string `json:"method"`
Config interface{} `json:"config"`
Enabled *bool `json:"enabled"`
}
GetAuthMethodConfigRequest {
Method string `form:"method"`
}
GetAuthMethodListResponse {
List []AuthMethodConfig `json:"list"`
}
TestSmsSendRequest {
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 (
prefix: v1/admin/auth-method
group: admin/authMethod
middleware: AuthMiddleware
)
service ppanel {
@doc "Get auth method list"
@handler GetAuthMethodList
get /list returns (GetAuthMethodListResponse)
@doc "Get auth method config"
@handler GetAuthMethodConfig
get /config (GetAuthMethodConfigRequest) returns (AuthMethodConfig)
@doc "Update auth method config"
@handler UpdateAuthMethodConfig
put /config (UpdateAuthMethodConfigRequest) returns (AuthMethodConfig)
@doc "Test sms send"
@handler TestSmsSend
post /test_sms_send (TestSmsSendRequest)
@doc "Test email send"
@handler TestEmailSend
post /test_email_send (TestEmailSendRequest)
@doc "Get sms support platform"
@handler GetSmsPlatform
get /sms_platform returns (PlatformResponse)
@doc "Get email support platform"
@handler GetEmailPlatform
get /email_platform returns (PlatformResponse)
}

88
apis/admin/console.api Normal file
View File

@ -0,0 +1,88 @@
syntax = "v1"
info (
title: "Console API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
type (
ServerTrafficData {
ServerId int64 `json:"server_id"`
Name string `json:"name"`
Upload int64 `json:"upload"`
Download int64 `json:"download"`
}
UserTrafficData {
SID int64 `json:"sid"`
Upload int64 `json:"upload"`
Download int64 `json:"download"`
}
ServerTotalDataResponse {
OnlineUserIPs int64 `json:"online_user_ips"`
OnlineServers int64 `json:"online_servers"`
OfflineServers int64 `json:"offline_servers"`
TodayUpload int64 `json:"today_upload"`
TodayDownload int64 `json:"today_download"`
MonthlyUpload int64 `json:"monthly_upload"`
MonthlyDownload int64 `json:"monthly_download"`
UpdatedAt int64 `json:"updated_at"`
ServerTrafficRankingToday []ServerTrafficData `json:"server_traffic_ranking_today"`
ServerTrafficRankingYesterday []ServerTrafficData `json:"server_traffic_ranking_yesterday"`
UserTrafficRankingToday []UserTrafficData `json:"user_traffic_ranking_today"`
UserTrafficRankingYesterday []UserTrafficData `json:"user_traffic_ranking_yesterday"`
}
UserStatistics {
Date string `json:"date,omitempty"`
Register int64 `json:"register"`
NewOrderUsers int64 `json:"new_order_users"`
RenewalOrderUsers int64 `json:"renewal_order_users"`
List []UserStatistics `json:"list,omitempty"`
}
OrdersStatistics {
Date string `json:"date,omitempty"`
AmountTotal int64 `json:"amount_total"`
NewOrderAmount int64 `json:"new_order_amount"`
RenewalOrderAmount int64 `json:"renewal_order_amount"`
List []OrdersStatistics `json:"list,omitempty"`
}
RevenueStatisticsResponse {
Today OrdersStatistics `json:"today"`
Monthly OrdersStatistics `json:"monthly"`
All OrdersStatistics `json:"all"`
}
UserStatisticsResponse {
Today UserStatistics `json:"today"`
Monthly UserStatistics `json:"monthly"`
All UserStatistics `json:"all"`
}
TicketWaitRelpyResponse {
Count int64 `json:"count"`
}
)
@server (
prefix: v1/admin/console
group: admin/console
middleware: AuthMiddleware
)
service ppanel {
@doc "Query server total data"
@handler QueryServerTotalData
get /server returns (ServerTotalDataResponse)
@doc "Query revenue statistics"
@handler QueryRevenueStatistics
get /revenue returns (RevenueStatisticsResponse)
@doc "Query user statistics"
@handler QueryUserStatistics
get /user returns (UserStatisticsResponse)
@doc "Query ticket wait reply"
@handler QueryTicketWaitReply
get /ticket returns (TicketWaitRelpyResponse)
}

85
apis/admin/coupon.api Normal file
View File

@ -0,0 +1,85 @@
syntax = "v1"
info (
title: "coupon API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
CreateCouponRequest {
Name string `json:"name" validate:"required"`
Code string `json:"code,omitempty"`
Count int64 `json:"count,omitempty"`
Type uint8 `json:"type" validate:"required"`
Discount int64 `json:"discount" validate:"required"`
StartTime int64 `json:"start_time" validate:"required"`
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"`
}
UpdateCouponRequest {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Code string `json:"code,omitempty"`
Count int64 `json:"count,omitempty"`
Type uint8 `json:"type" validate:"required"`
Discount int64 `json:"discount" validate:"required"`
StartTime int64 `json:"start_time" validate:"required"`
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"`
}
DeleteCouponRequest {
Id int64 `json:"id" validate:"required"`
}
BatchDeleteCouponRequest {
Ids []int64 `json:"ids" validate:"required"`
}
GetCouponListRequest {
Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"`
Subscribe int64 `form:"subscribe,omitempty"`
Search string `form:"search,omitempty"`
}
GetCouponListResponse {
Total int64 `json:"total"`
List []Coupon `json:"list"`
}
)
@server (
prefix: v1/admin/coupon
group: admin/coupon
middleware: AuthMiddleware
)
service ppanel {
@doc "Create coupon"
@handler CreateCoupon
post / (CreateCouponRequest)
@doc "Update coupon"
@handler UpdateCoupon
put / (UpdateCouponRequest)
@doc "Delete coupon"
@handler DeleteCoupon
delete / (DeleteCouponRequest)
@doc "Batch delete coupon"
@handler BatchDeleteCoupon
delete /batch (BatchDeleteCouponRequest)
@doc "Get coupon list"
@handler GetCouponList
get /list (GetCouponListRequest) returns (GetCouponListResponse)
}

13
apis/admin/device.api Normal file
View File

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

78
apis/admin/document.api Normal file
View File

@ -0,0 +1,78 @@
syntax = "v1"
info (
title: "Document API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
CreateDocumentRequest {
Title string `json:"title" validate:"required"`
Content string `json:"content" validate:"required"`
Tags []string `json:"tags,omitempty" `
Show *bool `json:"show"`
}
UpdateDocumentRequest {
Id int64 `json:"id" validate:"required"`
Title string `json:"title" validate:"required"`
Content string `json:"content" validate:"required"`
Tags []string `json:"tags,omitempty" `
Show *bool `json:"show"`
}
DeleteDocumentRequest {
Id int64 `json:"id" validate:"required"`
}
BatchDeleteDocumentRequest {
Ids []int64 `json:"ids" validate:"required"`
}
GetDocumentListRequest {
Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"`
Tag string `form:"tag,omitempty"`
Search string `form:"search,omitempty"`
}
GetDocumentListResponse {
Total int64 `json:"total"`
List []Document `json:"list"`
}
GetDocumentDetailRequest {
Id int64 `json:"id" validate:"required"`
}
)
@server (
prefix: v1/admin/document
group: admin/document
middleware: AuthMiddleware
)
service ppanel {
@doc "Create document"
@handler CreateDocument
post / (CreateDocumentRequest)
@doc "Update document"
@handler UpdateDocument
put / (UpdateDocumentRequest)
@doc "Delete document"
@handler DeleteDocument
delete / (DeleteDocumentRequest)
@doc "Batch delete document"
@handler BatchDeleteDocument
delete /batch (BatchDeleteDocumentRequest)
@doc "Get document list"
@handler GetDocumentList
get /list (GetDocumentListRequest) returns (GetDocumentListResponse)
@doc "Get document detail"
@handler GetDocumentDetail
get /detail (GetDocumentDetailRequest) returns (Document)
}

40
apis/admin/log.api Normal file
View File

@ -0,0 +1,40 @@
syntax = "v1"
info (
title: "Log API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
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"`
}
GetMessageLogListResponse {
Total int64 `json:"total"`
List []MessageLog `json:"list"`
}
)
@server (
prefix: v1/admin/log
group: admin/log
middleware: AuthMiddleware
)
service ppanel {
@doc "Get message log list"
@handler GetMessageLogList
get /message/list (GetMessageLogListRequest) returns (GetMessageLogListResponse)
}

68
apis/admin/order.api Normal file
View File

@ -0,0 +1,68 @@
syntax = "v1"
info (
title: "order API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
CreateOrderRequest {
UserId int64 `json:"user_id" validate:"required"`
Type uint8 `json:"type" validate:"required"`
Quantity int64 `json:"quantity,omitempty"`
Price int64 `json:"price" validate:"required"`
Amount int64 `json:"amount" validate:"required"`
Discount int64 `json:"discount,omitempty"`
Coupon string `json:"coupon,omitempty"`
CouponDiscount int64 `json:"coupon_discount,omitempty"`
Commission int64 `json:"commission"`
FeeAmount int64 `json:"fee_amount" validate:"required"`
PaymentId int64 `json:"payment_id" validate:"required"`
TradeNo string `json:"trade_no,omitempty"`
Status uint8 `json:"status,omitempty"`
SubscribeId int64 `json:"subscribe_id,omitempty"`
}
UpdateOrderStatusRequest {
Id int64 `json:"id" validate:"required"`
Status uint8 `json:"status" validate:"required"`
PaymentId int64 `json:"payment_id,omitempty"`
TradeNo string `json:"trade_no,omitempty"`
}
GetOrderListRequest {
Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"`
UserId int64 `form:"user_id,omitempty"`
Status uint8 `form:"status,omitempty"`
SubscribeId int64 `form:"subscribe_id,omitempty"`
Search string `form:"search,omitempty"`
}
GetOrderListResponse {
Total int64 `json:"total"`
List []Order `json:"list"`
}
)
@server (
prefix: v1/admin/order
group: admin/order
middleware: AuthMiddleware
)
service ppanel {
@doc "Create order"
@handler CreateOrder
post / (CreateOrderRequest)
@doc "Get order list"
@handler GetOrderList
get /list (GetOrderListRequest) returns (GetOrderListResponse)
@doc "Update order status"
@handler UpdateOrderStatus
put /status (UpdateOrderStatusRequest)
}

81
apis/admin/payment.api Normal file
View File

@ -0,0 +1,81 @@
syntax = "v1"
info (
title: "payment API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
CreatePaymentMethodRequest {
Name string `json:"name" validate:"required"`
Platform string `json:"platform" validate:"required"`
Description string `json:"description"`
Icon string `json:"icon,omitempty"`
Domain string `json:"domain,omitempty"`
Config interface{} `json:"config" validate:"required"`
FeeMode uint `json:"fee_mode"`
FeePercent int64 `json:"fee_percent,omitempty"`
FeeAmount int64 `json:"fee_amount,omitempty"`
Enable *bool `json:"enable" validate:"required"`
}
UpdatePaymentMethodRequest {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Platform string `json:"platform" validate:"required"`
Description string `json:"description"`
Icon string `json:"icon,omitempty"`
Domain string `json:"domain,omitempty"`
Config interface{} `json:"config" validate:"required"`
FeeMode uint `json:"fee_mode"`
FeePercent int64 `json:"fee_percent,omitempty"`
FeeAmount int64 `json:"fee_amount,omitempty"`
Enable *bool `json:"enable" validate:"required"`
}
DeletePaymentMethodRequest {
Id int64 `json:"id" validate:"required"`
}
GetPaymentMethodListRequest {
Page int `form:"page"`
Size int `form:"size"`
Platform string `form:"platform,omitempty"`
Search string `form:"search,omitempty"`
Enable *bool `form:"enable,omitempty"`
}
GetPaymentMethodListResponse {
Total int64 `json:"total"`
List []PaymentMethodDetail `json:"list"`
}
)
@server (
prefix: v1/admin/payment
group: admin/payment
middleware: AuthMiddleware
)
service ppanel {
@doc "Create Payment Method"
@handler CreatePaymentMethod
post / (CreatePaymentMethodRequest) returns (PaymentConfig)
@doc "Update Payment Method"
@handler UpdatePaymentMethod
put / (UpdatePaymentMethodRequest) returns (PaymentConfig)
@doc "Delete Payment Method"
@handler DeletePaymentMethod
delete / (DeletePaymentMethodRequest)
@doc "Get Payment Method List"
@handler GetPaymentMethodList
get /list (GetPaymentMethodListRequest) returns (GetPaymentMethodListResponse)
@doc "Get supported payment platform"
@handler GetPaymentPlatform
get /platform returns (PlatformResponse)
}

190
apis/admin/server.api Normal file
View File

@ -0,0 +1,190 @@
syntax = "v1"
info (
title: "Node API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
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"`
}
GetNodeServerListResponse {
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"`
}
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"`
}
DeleteNodeRequest {
Id int64 `json:"id" validate:"required"`
}
GetNodeGroupListResponse {
Total int64 `json:"total"`
List []ServerGroup `json:"list"`
}
CreateNodeGroupRequest {
Name string `json:"name" validate:"required"`
Description string `json:"description"`
}
UpdateNodeGroupRequest {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Description string `json:"description"`
}
DeleteNodeGroupRequest {
Id int64 `json:"id" validate:"required"`
}
BatchDeleteNodeRequest {
Ids []int64 `json:"ids" validate:"required"`
}
BatchDeleteNodeGroupRequest {
Ids []int64 `json:"ids" validate:"required"`
}
GetNodeDetailRequest {
Id int64 `form:"id" validate:"required"`
}
NodeSortRequest {
Sort []SortItem `json:"sort"`
}
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 {
Tags []string `json:"tags"`
}
)
@server (
prefix: v1/admin/server
group: admin/server
middleware: AuthMiddleware
)
service ppanel {
@doc "Get node tag list"
@handler GetNodeTagList
get /tag/list returns (GetNodeTagListResponse)
@doc "Get node list"
@handler GetNodeList
get /list (GetNodeServerListRequest) returns (GetNodeServerListResponse)
@doc "Get node detail"
@handler GetNodeDetail
get /detail (GetNodeDetailRequest) returns (Server)
@doc "Update node"
@handler UpdateNode
put / (UpdateNodeRequest)
@doc "Create node"
@handler CreateNode
post / (CreateNodeRequest)
@doc "Delete node"
@handler DeleteNode
delete / (DeleteNodeRequest)
@doc "Batch delete node"
@handler BatchDeleteNode
delete /batch (BatchDeleteNodeRequest)
@doc "Get node group list"
@handler GetNodeGroupList
get /group/list returns (GetNodeGroupListResponse)
@doc "Create node group"
@handler CreateNodeGroup
post /group (CreateNodeGroupRequest)
@doc "Update node group"
@handler UpdateNodeGroup
put /group (UpdateNodeGroupRequest)
@doc "Delete node group"
@handler DeleteNodeGroup
delete /group (DeleteNodeGroupRequest)
@doc "Batch delete node group"
@handler BatchDeleteNodeGroup
delete /group/batch (BatchDeleteNodeGroupRequest)
@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)
}

164
apis/admin/subscribe.api Normal file
View File

@ -0,0 +1,164 @@
syntax = "v1"
info (
title: "Subscribe API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
GetSubscribeDetailsRequest {
Id int64 `form:"id" validate:"required"`
}
CreateSubscribeGroupRequest {
Name string `json:"name" validate:"required"`
Description string `json:"description"`
}
UpdateSubscribeGroupRequest {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Description string `json:"description"`
}
GetSubscribeGroupListResponse {
List []SubscribeGroup `json:"list"`
Total int64 `json:"total"`
}
DeleteSubscribeGroupRequest {
Id int64 `json:"id" validate:"required"`
}
BatchDeleteSubscribeGroupRequest {
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"`
}
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"`
}
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"`
}
SubscribeItem {
Subscribe
Sold int64 `json:"sold"`
}
GetSubscribeListResponse {
List []SubscribeItem `json:"list"`
Total int64 `json:"total"`
}
DeleteSubscribeRequest {
Id int64 `json:"id" validate:"required"`
}
BatchDeleteSubscribeRequest {
Ids []int64 `json:"ids" validate:"required"`
}
)
@server (
prefix: v1/admin/subscribe
group: admin/subscribe
middleware: AuthMiddleware
)
service ppanel {
@doc "Create subscribe group"
@handler CreateSubscribeGroup
post /group (CreateSubscribeGroupRequest)
@doc "Update subscribe group"
@handler UpdateSubscribeGroup
put /group (UpdateSubscribeGroupRequest)
@doc "Get subscribe group list"
@handler GetSubscribeGroupList
get /group/list returns (GetSubscribeGroupListResponse)
@doc "Delete subscribe group"
@handler DeleteSubscribeGroup
delete /group (DeleteSubscribeGroupRequest)
@doc "Batch delete subscribe group"
@handler BatchDeleteSubscribeGroup
delete /group/batch (BatchDeleteSubscribeGroupRequest)
@doc "Create subscribe"
@handler CreateSubscribe
post / (CreateSubscribeRequest)
@doc "Update subscribe"
@handler UpdateSubscribe
put / (UpdateSubscribeRequest)
@doc "Get subscribe list"
@handler GetSubscribeList
get /list (GetSubscribeListRequest) returns (GetSubscribeListResponse)
@doc "Delete subscribe"
@handler DeleteSubscribe
delete / (DeleteSubscribeRequest)
@doc "Batch delete subscribe"
@handler BatchDeleteSubscribe
delete /batch (BatchDeleteSubscribeRequest)
@doc "Get subscribe details"
@handler GetSubscribeDetails
get /details (GetSubscribeDetailsRequest) returns (Subscribe)
@doc "Subscribe sort"
@handler SubscribeSort
post /sort (SubscribeSortRequest)
}

205
apis/admin/system.api Normal file
View File

@ -0,0 +1,205 @@
syntax = "v1"
info (
title: "System API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
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"`
}
// SetNodeMultiplierRequest
SetNodeMultiplierRequest {
Periods []TimePeriod `json:"periods"`
}
)
@server (
prefix: v1/admin/system
group: admin/system
middleware: AuthMiddleware
)
service ppanel {
@doc "Get site config"
@handler GetSiteConfig
get /site_config returns (SiteConfig)
@doc "Update site config"
@handler UpdateSiteConfig
put /site_config (SiteConfig)
@doc "Get subscribe config"
@handler GetSubscribeConfig
get /subscribe_config returns (SubscribeConfig)
@doc "Update subscribe config"
@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)
@doc "Update register config"
@handler UpdateRegisterConfig
put /register_config (RegisterConfig)
@doc "Get verify config"
@handler GetVerifyConfig
get /verify_config returns (VerifyConfig)
@doc "Update verify config"
@handler UpdateVerifyConfig
put /verify_config (VerifyConfig)
@doc "Get node config"
@handler GetNodeConfig
get /node_config returns (NodeConfig)
@doc "Update node config"
@handler UpdateNodeConfig
put /node_config (NodeConfig)
@doc "Get invite config"
@handler GetInviteConfig
get /invite_config returns (InviteConfig)
@doc "Update invite config"
@handler UpdateInviteConfig
put /invite_config (InviteConfig)
@doc "Get Team of Service Config"
@handler GetTosConfig
get /tos_config returns (TosConfig)
@doc "Update Team of Service Config"
@handler UpdateTosConfig
put /tos_config (TosConfig)
@doc "get Privacy Policy Config"
@handler GetPrivacyPolicyConfig
get /privacy returns (PrivacyPolicyConfig)
@doc "Update Privacy Policy Config"
@handler UpdatePrivacyPolicyConfig
put /privacy (PrivacyPolicyConfig)
@doc "Get Currency Config"
@handler GetCurrencyConfig
get /currency_config returns (CurrencyConfig)
@doc "Update Currency Config"
@handler UpdateCurrencyConfig
put /currency_config (CurrencyConfig)
@doc "setting telegram bot"
@handler SettingTelegramBot
post /setting_telegram_bot
@doc "Get Node Multiplier"
@handler GetNodeMultiplier
get /get_node_multiplier returns (GetNodeMultiplierResponse)
@doc "Set Node Multiplier"
@handler SetNodeMultiplier
post /set_node_multiplier (SetNodeMultiplierRequest)
@doc "Get Verify Code Config"
@handler GetVerifyCodeConfig
get /verify_code_config returns (VerifyCodeConfig)
@doc "Update Verify Code Config"
@handler UpdateVerifyCodeConfig
put /verify_code_config (VerifyCodeConfig)
}

62
apis/admin/ticket.api Normal file
View File

@ -0,0 +1,62 @@
syntax = "v1"
info (
title: "Ticket API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
UpdateTicketStatusRequest {
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"`
Search string `form:"search,omitempty"`
}
GetTicketListResponse {
Total int64 `json:"total"`
List []Ticket `json:"list"`
}
GetTicketRequest {
Id int64 `form:"id" validate:"required"`
}
CreateTicketFollowRequest {
TicketId int64 `json:"ticket_id" validate:"required"`
From string `json:"from" validate:"required"`
Type uint8 `json:"type" validate:"required"`
Content string `json:"content" validate:"required"`
}
)
@server (
prefix: v1/admin/ticket
group: admin/ticket
middleware: AuthMiddleware
)
service ppanel {
@doc "Get ticket list"
@handler GetTicketList
get /list (GetTicketListRequest) returns (GetTicketListResponse)
@doc "Get ticket detail"
@handler GetTicket
get /detail (GetTicketRequest) returns (Ticket)
@doc "Update ticket status"
@handler UpdateTicketStatus
put / (UpdateTicketStatusRequest)
@doc "Create ticket follow"
@handler CreateTicketFollow
post /follow (CreateTicketFollowRequest)
}

33
apis/admin/tool.api Normal file
View File

@ -0,0 +1,33 @@
syntax = "v1"
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"`
}
)
@server (
prefix: v1/admin/tool
group: admin/tool
middleware: AuthMiddleware
)
service ppanel {
@doc "Get System Log"
@handler GetSystemLog
get /log returns (LogResponse)
@doc "Restart System"
@handler RestartSystem
get /restart
}

278
apis/admin/user.api Normal file
View File

@ -0,0 +1,278 @@
syntax = "v1"
info (
title: "User API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import (
"../types.api"
)
type (
// GetUserListRequest
GetUserListRequest {
Page int `form:"page"`
Size int `form:"size"`
Search string `form:"search,omitempty"`
UserId *int64 `form:"user_id,omitempty"`
SubscribeId *int64 `form:"subscribe_id,omitempty"`
UserSubscribeId *int64 `form:"user_subscribe_id,omitempty"`
}
// GetUserListResponse
GetUserListResponse {
Total int64 `json:"total"`
List []User `json:"list"`
}
// GetUserDetail
GetDetailRequest {
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"`
}
UpdateUserNotifySettingRequest {
UserId int64 `json:"user_id" validate:"required"`
EnableBalanceNotify bool `json:"enable_balance_notify"`
EnableLoginNotify bool `json:"enable_login_notify"`
EnableSubscribeNotify bool `json:"enable_subscribe_notify"`
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"`
}
UserSubscribeDetail {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
User User `json:"user"`
OrderId int64 `json:"order_id"`
SubscribeId int64 `json:"subscribe_id"`
Subscribe Subscribe `json:"subscribe"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
ResetTime int64 `json:"reset_time"`
Traffic int64 `json:"traffic"`
Download int64 `json:"download"`
Upload int64 `json:"upload"`
Token string `json:"token"`
Status uint8 `json:"status"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
BatchDeleteUserRequest {
Ids []int64 `json:"ids" validate:"required"`
}
DeleteUserDeivceRequest {
Id int64 `json:"id"`
}
KickOfflineRequest {
Id int64 `json:"id"`
}
CreateUserAuthMethodRequest {
UserId int64 `json:"user_id"`
AuthType string `json:"auth_type"`
AuthIdentifier string `json:"auth_identifier"`
}
DeleteUserAuthMethodRequest {
UserId int64 `json:"user_id"`
AuthType string `json:"auth_type"`
}
UpdateUserAuthMethodRequest {
UserId int64 `json:"user_id"`
AuthType string `json:"auth_type"`
AuthIdentifier string `json:"auth_identifier"`
}
GetUserAuthMethodRequest {
UserId int64 `json:"user_id"`
}
GetUserAuthMethodResponse {
AuthMethods []UserAuthMethod `json:"auth_methods"`
}
GetUserSubscribeListRequest {
Page int `form:"page"`
Size int `form:"size"`
UserId int64 `form:"user_id"`
}
GetUserSubscribeListResponse {
List []UserSubscribe `json:"list"`
Total int64 `json:"total"`
}
GetUserSubscribeLogsRequest {
Page int `form:"page"`
Size int `form:"size"`
UserId int64 `form:"user_id"`
SubscribeId int64 `form:"subscribe_id,omitempty"`
}
GetUserSubscribeLogsResponse {
List []UserSubscribeLog `json:"list"`
Total int64 `json:"total"`
}
GetUserSubscribeDevicesRequest {
Page int `form:"page"`
Size int `form:"size"`
UserId int64 `form:"user_id"`
SubscribeId int64 `form:"subscribe_id"`
}
GetUserSubscribeDevicesResponse {
List []UserDevice `json:"list"`
Total int64 `json:"total"`
}
CreateUserSubscribeRequest {
UserId int64 `json:"user_id"`
ExpiredAt int64 `json:"expired_at"`
Traffic int64 `json:"traffic"`
SubscribeId int64 `json:"subscribe_id"`
}
UpdateUserSubscribeRequest {
UserSubscribeId int64 `json:"user_subscribe_id"`
SubscribeId int64 `json:"subscribe_id"`
Traffic int64 `json:"traffic"`
ExpiredAt int64 `json:"expired_at"`
Upload int64 `json:"upload"`
Download int64 `json:"download"`
}
GetUserLoginLogsRequest {
Page int `form:"page"`
Size int `form:"size"`
UserId int64 `form:"user_id"`
}
GetUserLoginLogsResponse {
List []UserLoginLog `json:"list"`
Total int64 `json:"total"`
}
DeleteUserSubscribeRequest {
UserSubscribeId int64 `json:"user_subscribe_id"`
}
GetUserSubscribeByIdRequest {
Id int64 `form:"id" validate:"required"`
}
)
@server (
prefix: v1/admin/user
group: admin/user
jwt: JwtAuth
middleware: AuthMiddleware
)
service ppanel {
@doc "Get user list"
@handler GetUserList
get /list (GetUserListRequest) returns (GetUserListResponse)
@doc "Get user detail"
@handler GetUserDetail
get /detail (GetDetailRequest) returns (User)
@doc "Update user basic info"
@handler UpdateUserBasicInfo
put /basic (UpdateUserBasiceInfoRequest)
@doc "Update user notify setting"
@handler UpdateUserNotifySetting
put /notify (UpdateUserNotifySettingRequest)
@doc "Delete user"
@handler DeleteUser
delete / (GetDetailRequest)
@doc "Current user"
@handler CurrentUser
get /current returns (User)
@doc "Batch delete user"
@handler BatchDeleteUser
delete /batch (BatchDeleteUserRequest)
@doc "Create user"
@handler CreateUser
post / (CreateUserRequest)
@doc "User device"
@handler UpdateUserDevice
put /device (UserDevice)
@doc "Delete user device"
@handler DeleteUserDevice
delete /device (DeleteUserDeivceRequest)
@doc "kick offline user device"
@handler KickOfflineByUserDevice
put /device/kick_offline (KickOfflineRequest)
@doc "Create user auth method"
@handler CreateUserAuthMethod
post /auth_method (CreateUserAuthMethodRequest)
@doc "Delete user auth method"
@handler DeleteUserAuthMethod
delete /auth_method (DeleteUserAuthMethodRequest)
@doc "Update user auth method"
@handler UpdateUserAuthMethod
put /auth_method (UpdateUserAuthMethodRequest)
@doc "Get user auth method"
@handler GetUserAuthMethod
get /auth_method (GetUserAuthMethodRequest) returns (GetUserAuthMethodResponse)
@doc "Get user subcribe"
@handler GetUserSubscribe
get /subscribe (GetUserSubscribeListRequest) returns (GetUserSubscribeListResponse)
@doc "Get user subcribe by id"
@handler GetUserSubscribeById
get /subscribe/detail (GetUserSubscribeByIdRequest) returns (UserSubscribeDetail)
@doc "Get user subcribe logs"
@handler GetUserSubscribeLogs
get /subscribe/logs (GetUserSubscribeLogsRequest) returns (GetUserSubscribeLogsResponse)
@doc "Get user subcribe traffic logs"
@handler GetUserSubscribeTrafficLogs
get /subscribe/traffic_logs (GetUserSubscribeTrafficLogsRequest) returns (GetUserSubscribeTrafficLogsResponse)
@doc "Get user subcribe devices"
@handler GetUserSubscribeDevices
get /subscribe/device (GetUserSubscribeDevicesRequest) returns (GetUserSubscribeDevicesResponse)
@doc "Create user subcribe"
@handler CreateUserSubscribe
post /subscribe (CreateUserSubscribeRequest)
@doc "Update user subcribe"
@handler UpdateUserSubscribe
put /subscribe (UpdateUserSubscribeRequest)
@doc "Delete user subcribe"
@handler DeleteUserSubscribe
delete /subscribe (DeleteUserSubscribeRequest)
@doc "Get user login logs"
@handler GetUserLoginLogs
get /login/logs (GetUserLoginLogsRequest) returns (GetUserLoginLogsResponse)
}

24
apis/app/announcement.api Normal file
View File

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

104
apis/app/auth.api Normal file
View File

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

27
apis/app/document.api Normal file
View File

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

49
apis/app/node.api Normal file
View File

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

58
apis/app/order.api Normal file
View File

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

23
apis/app/payment.api Normal file
View File

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

75
apis/app/subscribe.api Normal file
View File

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

90
apis/app/user.api Normal file
View File

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

23
apis/app/ws.api Normal file
View File

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

166
apis/auth/auth.api Normal file
View File

@ -0,0 +1,166 @@
syntax = "v1"
info (
title: "User auth API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
type (
// User login request
UserLoginRequest {
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"`
}
// Check user is exist request
CheckUserRequest {
Email string `form:"email" validate:"required"`
}
// User login response
CheckUserResponse {
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"`
UserAgent string `header:"User-Agent"`
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"`
UserAgent string `header:"User-Agent"`
CfToken string `json:"cf_token,optional"`
}
LoginResponse {
Token string `json:"token"`
}
OAthLoginRequest {
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.
Callback interface{} `json:"callback" validate:"required"`
}
// login request
TelephoneLoginRequest {
Telephone string `json:"telephone" validate:"required"`
TelephoneCode string `json:"telephone_code"`
TelephoneAreaCode string `json:"telephone_area_code" validate:"required"`
Password string `json:"password"`
IP string `header:"X-Original-Forwarded-For"`
CfToken string `json:"cf_token,optional"`
}
// Check user is exist request
TelephoneCheckUserRequest {
Telephone string `form:"telephone" validate:"required"`
TelephoneAreaCode string `json:"telephone_area_code" validate:"required"`
}
// User login response
TelephoneCheckUserResponse {
exist bool `json:"exist"`
}
// User login response
TelephoneRegisterRequest {
Telephone string `json:"telephone" validate:"required"`
TelephoneAreaCode string `json:"telephone_area_code" 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"`
CfToken string `json:"cf_token,optional"`
}
// User login response
TelephoneResetPasswordRequest {
Telephone string `json:"telephone" validate:"required"`
TelephoneAreaCode string `json:"telephone_area_code" validate:"required"`
Password string `json:"password" validate:"required"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For"`
CfToken string `json:"cf_token,optional"`
}
AppleLoginCallbackRequest {
Code string `form:"code"`
IDToken string `form:"id_token"`
State string `form:"state"`
}
GoogleLoginCallbackRequest {
Code string `form:"code"`
State string `form:"state"`
}
)
@server (
prefix: v1/auth
group: auth
)
service ppanel {
@doc "User login"
@handler UserLogin
post /login (UserLoginRequest) returns (LoginResponse)
@doc "Check user is exist"
@handler CheckUser
get /check (CheckUserRequest) returns (CheckUserResponse)
@doc "User register"
@handler UserRegister
post /register (UserRegisterRequest) returns (LoginResponse)
@doc "Reset password"
@handler ResetPassword
post /reset (ResetPasswordRequest) returns (LoginResponse)
@doc "User Telephone login"
@handler TelephoneLogin
post /login/telephone (TelephoneLoginRequest) returns (LoginResponse)
@doc "Check user telephone is exist"
@handler CheckUserTelephone
get /check/telephone (TelephoneCheckUserRequest) returns (TelephoneCheckUserResponse)
@doc "User Telephone register"
@handler TelephoneUserRegister
post /register/telephone (TelephoneRegisterRequest) returns (LoginResponse)
@doc "Reset password"
@handler TelephoneResetPassword
post /reset/telephone (TelephoneResetPasswordRequest) returns (LoginResponse)
}
@server (
prefix: v1/auth/oauth
group: auth/oauth
)
service ppanel {
@doc "OAuth login"
@handler OAuthLogin
post /login (OAthLoginRequest) returns (OAuthLoginResponse)
@doc "OAuth login get token"
@handler OAuthLoginGetToken
post /login/token (OAuthLoginGetTokenRequest) returns (LoginResponse)
@doc "Apple Login Callback"
@handler AppleLoginCallback
post /callback/apple (AppleLoginCallbackRequest)
}

125
apis/common.api Normal file
View File

@ -0,0 +1,125 @@
syntax = "v1"
info (
title: "Common API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "./types.api"
type (
VeifyConfig {
TurnstileSiteKey string `json:"turnstile_site_key"`
EnableLoginVerify bool `json:"enable_login_verify"`
EnableRegisterVerify bool `json:"enable_register_verify"`
EnableResetPasswordVerify bool `json:"enable_reset_password_verify"`
}
GetGlobalConfigResponse {
Site SiteConfig `json:"site"`
Verify VeifyConfig `json:"verify"`
Auth AuthConfig `json:"auth"`
Invite InviteConfig `json:"invite"`
Currency Currency `json:"currency"`
Subscribe SubscribeConfig `json:"subscribe"`
VerifyCode PubilcVerifyCodeConfig `json:"verify_code"`
OAuthMethods []string `json:"oauth_methods"`
WebAd bool `json:"web_ad"`
}
Currency {
CurrencyUnit string `json:"currency_unit"`
CurrencySymbol string `json:"currency_symbol"`
}
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"`
Type uint8 `json:"type" validate:"required"`
}
SendSmsCodeRequest {
Type uint8 `json:"type" validate:"required"`
Telephone string `json:"telephone" validate:"required"`
TelephoneAreaCode string `json:"telephone_area_code" validate:"required"`
}
// GetCodeResponse Get code response
SendCodeResponse {
Code string `json:"code,omitempty"`
Status bool `json:"status"`
}
// GetStatResponse Get stat response
GetStatResponse {
User int64 `json:"user"`
Node int64 `json:"node"`
Country int64 `json:"country"`
Protocol []string `json:"protocol"`
}
// Get ads
GetAdsRequest {
Device string `form:"device"`
Position string `form:"position"`
}
GetAdsResponse {
List []Ads `json:"list"`
}
CheckVerificationCodeRequest {
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"`
}
CheckVerificationCodeRespone{
Status bool `json:"status"`
}
)
@server (
prefix: v1/common
group: common
)
service ppanel {
@doc "Get global config"
@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)
@doc "Get Privacy Policy"
@handler GetPrivacyPolicy
get /site/privacy returns (PrivacyPolicyConfig)
@doc "Get stat"
@handler GetStat
get /site/stat returns (GetStatResponse)
@doc "Get verification code"
@handler SendEmailCode
post /send_code (SendCodeRequest) returns (SendCodeResponse)
@doc "Get sms verification code"
@handler SendSmsCode
post /send_sms_code (SendSmsCodeRequest) returns (SendCodeResponse)
@doc "Get Ads"
@handler GetAds
get /ads (GetAdsRequest) returns (GetAdsResponse)
@doc "Check verification code"
@handler CheckVerificationCode
post /check_verification_code (CheckVerificationCodeRequest) returns (CheckVerificationCodeRespone)
}

121
apis/node/node.api Normal file
View File

@ -0,0 +1,121 @@
syntax = "v1"
info (
title: "Node API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
ShadowsocksProtocol {
Port int `json:"port"`
Method string `json:"method"`
}
VmessProtocol {
Host string `json:"host"`
Port int `json:"port"`
EnableTLS *bool `json:"enable_tls"`
TLSConfig string `json:"tls_config"`
Network string `json:"network"`
Transport string `json:"transport"`
}
VlessProtocol {
Host string `json:"host"`
Port int `json:"port"`
Network string `json:"network"`
Transport string `json:"transport"`
Security string `json:"security"`
SecurityConfig string `json:"security_config"`
XTLS string `json:"xtls"`
}
TrojanProtocol {
Host string `json:"host"`
Port int `json:"port"`
EnableTLS *bool `json:"enable_tls"`
TLSConfig string `json:"tls_config"`
Network string `json:"network"`
Transport string `json:"transport"`
}
GetServerConfigRequest {
ServerCommon
}
GetServerConfigResponse {
Basic ServerBasic `json:"basic"`
Protocol string `json:"protocol"`
Config interface{} `json:"config"`
}
ServerBasic {
PushInterval int64 `json:"push_interval"`
PullInterval int64 `json:"pull_interval"`
}
ServerCommon {
Protocol string `form:"protocol"`
ServerId int64 `form:"server_id"`
SecretKey string `form:"secret_key"`
}
ServerUser {
Id int64 `json:"id"`
UUID string `json:"uuid"`
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
}
GetServerUserListRequest {
ServerCommon
}
GetServerUserListResponse {
Users []ServerUser `json:"users"`
}
UserTraffic {
SID int64 `json:"uid"`
Upload int64 `json:"upload"`
Download int64 `json:"download"`
}
ServerPushUserTrafficRequest {
ServerCommon
Traffic []UserTraffic `json:"traffic"`
}
ServerPushStatusRequest {
ServerCommon
Cpu float64 `json:"cpu"`
Mem float64 `json:"mem"`
Disk float64 `json:"disk"`
UpdatedAt int64 `json:"updated_at"`
}
OnlineUsersRequest {
ServerCommon
Users []OnlineUser `json:"users"`
}
)
@server (
prefix: v1/server
group: server
middleware: ServerMiddleware
)
service ppanel {
@doc "Get user list"
@handler GetServerUserList
get /user (GetServerUserListRequest) returns (GetServerUserListResponse)
@doc "Push user Traffic"
@handler ServerPushUserTraffic
post /push (ServerPushUserTrafficRequest)
@doc "Push server status"
@handler ServerPushStatus
post /status (ServerPushStatusRequest)
@doc "Get server config"
@handler GetServerConfig
get /config (GetServerConfigRequest) returns (GetServerConfigResponse)
@doc "Push online users"
@handler PushOnlineUsers
post /online (OnlineUsersRequest)
}

View File

@ -0,0 +1,24 @@
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/public/announcement
group: public/announcement
middleware: AuthMiddleware
)
service ppanel {
@doc "Query announcement"
@handler QueryAnnouncement
get /list (QueryAnnouncementRequest) returns (QueryAnnouncementResponse)
}

28
apis/public/document.api Normal file
View File

@ -0,0 +1,28 @@
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/public/document
group: public/document
middleware: AuthMiddleware
)
service ppanel {
@doc "Get document list"
@handler QueryDocumentList
get /list returns (QueryDocumentListResponse)
@doc "Get document detail"
@handler QueryDocumentDetail
get /detail (QueryDocumentDetailRequest) returns (Document)
}

51
apis/public/order.api Normal file
View File

@ -0,0 +1,51 @@
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/public/order
group: public/order
middleware: 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 "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)
}

24
apis/public/payment.api Normal file
View File

@ -0,0 +1,24 @@
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/public/payment
group: public/payment
middleware: AuthMiddleware
)
service ppanel {
@doc "Get available payment methods"
@handler GetAvailablePaymentMethods
get /methods returns (GetAvailablePaymentMethodsResponse)
}

96
apis/public/portal.api Normal file
View File

@ -0,0 +1,96 @@
syntax = "v1"
info (
title: "Portal API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
PortalPurchaseRequest {
AuthType string `json:"auth_type"`
Identifier string `json:"identifier"`
Password string `json:"password,omitempty"`
Payment int64 `json:"payment"`
SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity"`
Coupon string `json:"coupon,omitempty"`
InviteCode string `json:"invite_code,omitempty"`
TurnstileToken string `json:"turnstile_token,omitempty"`
}
PortalPurchaseResponse {
OrderNo string `json:"order_no"`
}
GetSubscriptionResponse {
List []Subscribe `json:"list"`
}
PrePurchaseOrderRequest {
Payment int64 `json:"payment,omitempty"`
SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity"`
Coupon string `json:"coupon,omitempty"`
}
PrePurchaseOrderResponse {
Price int64 `json:"price"`
Amount int64 `json:"amount"`
Discount int64 `json:"discount"`
Coupon string `json:"coupon"`
CouponDiscount int64 `json:"coupon_discount"`
FeeAmount int64 `json:"fee_amount"`
}
QueryPurchaseOrderRequest {
AuthType string `form:"auth_type"`
Identifier string `form:"identifier"`
OrderNo string `form:"order_no"`
}
QueryPurchaseOrderResponse {
OrderNo string `json:"order_no"`
Subscribe Subscribe `json:"subscribe"`
Quantity int64 `json:"quantity"`
Price int64 `json:"price"`
Amount int64 `json:"amount"`
Discount int64 `json:"discount"`
Coupon string `json:"coupon"`
CouponDiscount int64 `json:"coupon_discount"`
FeeAmount int64 `json:"fee_amount"`
Payment PaymentMethod `json:"payment"`
Status uint8 `json:"status"`
CreatedAt int64 `json:"created_at"`
Token string `json:"token,omitempty"`
}
)
@server (
prefix: v1/public/portal
group: public/portal
)
service ppanel {
@doc "Get available payment methods"
@handler GetAvailablePaymentMethods
get /payment-method returns (GetAvailablePaymentMethodsResponse)
@doc "Get Subscription"
@handler GetSubscription
get /subscribe returns (GetSubscriptionResponse)
@doc "Pre Purchase Order"
@handler PrePurchaseOrder
post /pre (PrePurchaseOrderRequest) returns (PrePurchaseOrderResponse)
@doc "Purchase subscription"
@handler Purchase
post /purchase (PortalPurchaseRequest) returns (PortalPurchaseResponse)
@doc "Query Purchase Order"
@handler QueryPurchaseOrder
get /order/status (QueryPurchaseOrderRequest) returns (QueryPurchaseOrderResponse)
@doc "Purchase Checkout"
@handler PurchaseCheckout
post /order/checkout (CheckoutOrderRequest) returns (CheckoutOrderResponse)
}

31
apis/public/subscribe.api Normal file
View File

@ -0,0 +1,31 @@
syntax = "v1"
info (
title: "Subscribe API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
@server (
prefix: v1/public/subscribe
group: public/subscribe
middleware: 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)
}

70
apis/public/ticket.api Normal file
View File

@ -0,0 +1,70 @@
syntax = "v1"
info (
title: "Ticket API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
GetUserTicketListResponse {
Total int64 `json:"total"`
List []Ticket `json:"list"`
}
CreateUserTicketRequest {
Title string `json:"title"`
Description string `json:"description"`
}
GetUserTicketListRequest {
Page int `form:"page"`
Size int `form:"size"`
Status *uint8 `form:"status,omitempty"`
Search string `form:"search,omitempty"`
}
GetUserTicketDetailRequest {
Id int64 `form:"id" validate:"required"`
}
UpdateUserTicketStatusRequest {
Id int64 `json:"id" validate:"required"`
Status *uint8 `json:"status" validate:"required"`
}
CreateUserTicketFollowRequest {
TicketId int64 `json:"ticket_id"`
From string `json:"from"`
Type uint8 `json:"type"`
Content string `json:"content"`
}
)
@server (
prefix: v1/public/ticket
group: public/ticket
middleware: AuthMiddleware
)
service ppanel {
@doc "Get ticket list"
@handler GetUserTicketList
get /list (GetUserTicketListRequest) returns (GetUserTicketListResponse)
@doc "Get ticket detail"
@handler GetUserTicketDetails
get /detail (GetUserTicketDetailRequest) returns (Ticket)
@doc "Update ticket status"
@handler UpdateUserTicketStatus
put / (UpdateUserTicketStatusRequest)
@doc "Create ticket follow"
@handler CreateUserTicketFollow
post /follow (CreateUserTicketFollowRequest)
@doc "Create ticket"
@handler CreateUserTicket
post / (CreateUserTicketRequest)
}

211
apis/public/user.api Normal file
View File

@ -0,0 +1,211 @@
syntax = "v1"
info (
title: "User API"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
import "../types.api"
type (
UpdateUserNotifyRequest {
EnableBalanceNotify *bool `json:"enable_balance_notify"`
EnableLoginNotify *bool `json:"enable_login_notify"`
EnableSubscribeNotify *bool `json:"enable_subscribe_notify"`
EnableTradeNotify *bool `json:"enable_trade_notify"`
}
UpdateUserPasswordRequest {
Password string `json:"password" validate:"required"`
}
QueryUserSubscribeListResponse {
List []UserSubscribe `json:"list"`
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"`
}
QueryUserCommissionLogListRequest {
Page int `form:"page"`
Size int `form:"size"`
}
QueryUserCommissionLogListResponse {
List []CommissionLog `json:"list"`
Total int64 `json:"total"`
}
BindTelegramResponse {
Url string `json:"url"`
ExpiredAt int64 `json:"expired_at"`
}
PreUnsubscribeRequest {
Id int64 `json:"id"`
}
PreUnsubscribeResponse {
DeductionAmount int64 `json:"deduction_amount"`
}
UnsubscribeRequest {
Id int64 `json:"id"`
}
BindOAuthRequest {
Method string `json:"method"`
Redirect string `json:"redirect"`
}
BindOAuthResponse {
Redirect string `json:"redirect"`
}
BindOAuthCallbackRequest {
Method string `json:"method"`
Callback interface{} `json:"callback"`
}
GetOAuthMethodsResponse {
Methods []UserAuthMethod `json:"methods"`
}
UnbindOAuthRequest {
Method string `json:"method"`
}
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"`
}
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"`
}
VerifyEmailRequest {
Email string `json:"email" validate:"required"`
Code string `json:"code" validate:"required"`
}
)
@server (
prefix: v1/public/user
group: public/user
middleware: AuthMiddleware
)
service ppanel {
@doc "Query User Info"
@handler QueryUserInfo
get /info returns (User)
@doc "Update User Notify"
@handler UpdateUserNotify
put /notify (UpdateUserNotifyRequest)
@doc "Update User Password"
@handler UpdateUserPassword
put /password (UpdateUserPasswordRequest)
@doc "Query User Subscribe"
@handler QueryUserSubscribe
get /subscribe returns (QueryUserSubscribeListResponse)
@doc "Pre Unsubscribe"
@handler PreUnsubscribe
post /unsubscribe/pre (PreUnsubscribeRequest) returns (PreUnsubscribeResponse)
@doc "Unsubscribe"
@handler Unsubscribe
post /unsubscribe (UnsubscribeRequest)
@doc "Query User Balance Log"
@handler QueryUserBalanceLog
get /balance_log returns (QueryUserBalanceLogListResponse)
@doc "Query User Affiliate Count"
@handler QueryUserAffiliate
get /affiliate/count returns (QueryUserAffiliateCountResponse)
@doc "Query User Affiliate List"
@handler QueryUserAffiliateList
get /affiliate/list (QueryUserAffiliateListRequest) returns (QueryUserAffiliateListResponse)
@doc "Bind Telegram"
@handler BindTelegram
get /bind_telegram returns (BindTelegramResponse)
@doc "Unbind Telegram"
@handler UnbindTelegram
post /unbind_telegram
@doc "Query User Commission Log"
@handler QueryUserCommissionLog
get /commission_log (QueryUserCommissionLogListRequest) returns (QueryUserCommissionLogListResponse)
@doc "Bind OAuth"
@handler BindOAuth
post /bind_oauth (BindOAuthRequest) returns (BindOAuthResponse)
@doc "Bind OAuth Callback"
@handler BindOAuthCallback
post /bind_oauth/callback (BindOAuthCallbackRequest)
@doc "Get OAuth Methods"
@handler GetOAuthMethods
get /oauth_methods returns (GetOAuthMethodsResponse)
@doc "Unbind OAuth"
@handler UnbindOAuth
post /unbind_oauth (UnbindOAuthRequest)
@doc "Reset User Subscribe Token"
@handler ResetUserSubscribeToken
put /subscribe_token (ResetUserSubscribeTokenRequest)
@doc "Get Login Log"
@handler GetLoginLog
get /login_log (GetLoginLogRequest) returns (GetLoginLogResponse)
@doc "Get Subscribe Log"
@handler GetSubscribeLog
get /subscribe_log (GetSubscribeLogRequest) returns (GetSubscribeLogResponse)
@doc "Verify Email"
@handler VerifyEmail
post /verify_email (VerifyEmailRequest)
@doc "Update Bind Mobile"
@handler UpdateBindMobile
put /bind_mobile (UpdateBindMobileRequest)
@doc "Update Bind Email"
@handler UpdateBindEmail
put /bind_email (UpdateBindEmailRequest)
}

26
apis/swagger_admin.api Normal file
View File

@ -0,0 +1,26 @@
syntax = "v1"
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"
)

21
apis/swagger_app.api Normal file
View File

@ -0,0 +1,21 @@
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"
)

14
apis/swagger_common.api Normal file
View File

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

11
apis/swagger_node.api Normal file
View File

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

19
apis/swagger_user.api Normal file
View File

@ -0,0 +1,19 @@
syntax = "v1"
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"
)

750
apis/types.api Normal file
View File

@ -0,0 +1,750 @@
syntax = "v1"
info (
title: "Interface Model"
desc: "API for ppanel"
author: "Tension"
email: "tension@ppanel.com"
version: "0.0.1"
)
type (
User {
Id int64 `json:"id"`
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,omitempty"`
EnableBalanceNotify bool `json:"enable_balance_notify"`
EnableLoginNotify bool `json:"enable_login_notify"`
EnableSubscribeNotify bool `json:"enable_subscribe_notify"`
EnableTradeNotify bool `json:"enable_trade_notify"`
AuthMethods []UserAuthMethod `json:"auth_methods"`
UserDevices []UserDevice `json:"user_devices"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
DeletedAt int64 `json:"deleted_at,omitempty"`
IsDel bool `json:"is_del,omitempty"`
}
Follow {
Id int64 `json:"id"`
TicketId int64 `json:"ticket_id"`
From string `json:"from"`
Type uint8 `json:"type"`
Content string `json:"content"`
CreatedAt int64 `json:"created_at"`
}
Ticket {
Id int64 `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
UserId int64 `json:"user_id"`
Follows []Follow `json:"follow,omitempty"`
Status uint8 `json:"status"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
SiteConfig {
Host string `json:"host"`
SiteName string `json:"site_name"`
SiteDesc string `json:"site_desc"`
SiteLogo string `json:"site_logo"`
Keywords string `json:"keywords"`
CustomHTML string `json:"custom_html"`
CustomData string `json:"custom_data"`
}
SubscribeConfig {
SingleModel bool `json:"single_model"`
SubscribePath string `json:"subscribe_path"`
SubscribeDomain string `json:"subscribe_domain"`
PanDomain bool `json:"pan_domain"`
}
VerifyCodeConfig {
VerifyCodeExpireTime int64 `json:"verify_code_expire_time"`
VerifyCodeLimit int64 `json:"verify_code_limit"`
VerifyCodeInterval int64 `json:"verify_code_interval"`
}
PubilcVerifyCodeConfig {
VerifyCodeInterval int64 `json:"verify_code_interval"`
}
SubscribeType {
SubscribeTypes []string `json:"subscribe_types"`
}
Application {
Id int64 `json:"id"`
Icon string `json:"icon"`
Name string `json:"name"`
Description string `json:"description"`
SubscribeType string `json:"subscribe_type"`
}
ApplicationVersion {
Id int64 `json:"id"`
Url string `json:"url"`
Version string `json:"version" validate:"required"`
Description string `json:"description"`
IsDefault bool `json:"is_default"`
}
ApplicationResponse {
Applications []ApplicationResponseInfo `json:"applications"`
}
ApplicationResponseInfo {
Id int64 `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
Description string `json:"description"`
SubscribeType string `json:"subscribe_type"`
Platform ApplicationPlatform `json:"platform"`
}
ApplicationPlatform {
IOS []*ApplicationVersion `json:"ios,omitempty"`
MacOS []*ApplicationVersion `json:"macos,omitempty"`
Linux []*ApplicationVersion `json:"linux,omitempty"`
Android []*ApplicationVersion `json:"android,omitempty"`
Windows []*ApplicationVersion `json:"windows,omitempty"`
Harmony []*ApplicationVersion `json:"harmony,omitempty"`
}
AuthConfig {
Mobile MobileAuthenticateConfig `json:"mobile"`
Email EmailAuthticateConfig `json:"email"`
Register PubilcRegisterConfig `json:"register"`
}
PubilcRegisterConfig {
StopRegister bool `json:"stop_register"`
EnableIpRegisterLimit bool `json:"enable_ip_register_limit"`
IpRegisterLimit int64 `json:"ip_register_limit"`
IpRegisterLimitDuration int64 `json:"ip_register_limit_duration"`
}
MobileAuthenticateConfig {
Enable bool `json:"enable"`
EnableWhitelist bool `json:"enable_whitelist"`
Whitelist []string `json:"whitelist"`
}
EmailAuthticateConfig {
Enable bool `json:"enable"`
EnableVerify bool `json:"enable_verify"`
EnableDomainSuffix bool `json:"enable_domain_suffix"`
DomainSuffixList string `json:"domain_suffix_list"`
}
RegisterConfig {
StopRegister bool `json:"stop_register"`
EnableTrial bool `json:"enable_trial"`
TrialSubscribe int64 `json:"trial_subscribe"`
TrialTime int64 `json:"trial_time"`
TrialTimeUnit string `json:"trial_time_unit"`
EnableIpRegisterLimit bool `json:"enable_ip_register_limit"`
IpRegisterLimit int64 `json:"ip_register_limit"`
IpRegisterLimitDuration int64 `json:"ip_register_limit_duration"`
}
VerifyConfig {
TurnstileSiteKey string `json:"turnstile_site_key"`
TurnstileSecret string `json:"turnstile_secret"`
EnableLoginVerify bool `json:"enable_login_verify"`
EnableRegisterVerify bool `json:"enable_register_verify"`
EnableResetPasswordVerify bool `json:"enable_reset_password_verify"`
}
NodeConfig {
NodeSecret string `json:"node_secret"`
NodePullInterval int64 `json:"node_pull_interval"`
NodePushInterval int64 `json:"node_push_interval"`
}
InviteConfig {
ForcedInvite bool `json:"forced_invite"`
ReferralPercentage int64 `json:"referral_percentage"`
OnlyFirstPurchase bool `json:"only_first_purchase"`
}
TelegramConfig {
TelegramBotToken string `json:"telegram_bot_token"`
TelegramGroupUrl string `json:"telegram_group_url"`
TelegramNotify bool `json:"telegram_notify"`
TelegramWebHookDomain string `json:"telegram_web_hook_domain"`
}
TosConfig {
TosContent string `json:"tos_content"`
}
PrivacyPolicyConfig {
PrivacyPolicy string `json:"privacy_policy"`
}
CurrencyConfig {
AccessKey string `json:"access_key"`
CurrencyUnit string `json:"currency_unit"`
CurrencySymbol string `json:"currency_symbol"`
}
SubscribeDiscount {
Quantity int64 `json:"quantity"`
Discount int64 `json:"discount"`
}
Subscribe {
Id int64 `json:"id"`
Name string `json:"name"`
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"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
SubscribeGroup {
Id int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
Shadowsocks {
Method string `json:"method" validate:"required"`
Port int `json:"port" validate:"required"`
ServerKey string `json:"server_key"`
}
Vmess {
Port int `json:"port" validate:"required"`
Transport string `json:"transport" validate:"required"`
TransportConfig TransportConfig `json:"transport_config"`
Security string `json:"security" validate:"required"`
SecurityConfig SecurityConfig `json:"security_config"`
}
Vless {
Port int `json:"port" validate:"required"`
Flow string `json:"flow" validate:"required"`
Transport string `json:"transport" validate:"required"`
TransportConfig TransportConfig `json:"transport_config"`
Security string `json:"security" validate:"required"`
SecurityConfig SecurityConfig `json:"security_config"`
}
Trojan {
Port int `json:"port" validate:"required"`
Transport string `json:"transport" validate:"required"`
TransportConfig TransportConfig `json:"transport_config"`
Security string `json:"security" validate:"required"`
SecurityConfig SecurityConfig `json:"security_config"`
}
Hysteria2 {
Port int `json:"port" validate:"required"`
HopPorts string `json:"hop_ports" validate:"required"`
HopInterval int `json:"hop_interval" validate:"required"`
ObfsPassword string `json:"obfs_password" validate:"required"`
SecurityConfig SecurityConfig `json:"security_config"`
}
Tuic {
Port int `json:"port" validate:"required"`
SecurityConfig SecurityConfig `json:"security_config"`
}
SecurityConfig {
SNI string `json:"sni"`
AllowInsecure *bool `json:"allow_insecure"`
Fingerprint string `json:"fingerprint"`
RealityServerAddr string `json:"reality_server_addr"`
RealityServerPort int `json:"reality_server_port"`
RealityPrivateKey string `json:"reality_private_key"`
RealityPublicKey string `json:"reality_public_key"`
RealityShortId string `json:"reality_short_id"`
}
TransportConfig {
Path string `json:"path"`
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"`
}
ServerGroup {
Id int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
PaymentMethod {
Id int64 `json:"id"`
Name string `json:"name"`
Platform string `json:"platform"`
Description string `json:"description"`
Icon string `json:"icon"`
FeeMode uint `json:"fee_mode"`
FeePercent int64 `json:"fee_percent"`
FeeAmount int64 `json:"fee_amount"`
}
PaymentConfig {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Platform string `json:"platform" validate:"required"`
Description string `json:"description"`
Icon string `json:"icon,omitempty"`
Domain string `json:"domain,omitempty"`
Config interface{} `json:"config" validate:"required"`
FeeMode uint `json:"fee_mode"`
FeePercent int64 `json:"fee_percent,omitempty"`
FeeAmount int64 `json:"fee_amount,omitempty"`
Enable *bool `json:"enable" validate:"required"`
}
PaymentMethodDetail {
Id int64 `json:"id"`
Name string `json:"name"`
Platform string `json:"platform"`
Description string `json:"description"`
Icon string `json:"icon"`
Domain string `json:"domain"`
Config interface{} `json:"config"`
FeeMode uint `json:"fee_mode"`
FeePercent int64 `json:"fee_percent"`
FeeAmount int64 `json:"fee_amount"`
Enable bool `json:"enable"`
NotifyURL string `json:"notify_url"`
}
Order {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
OrderNo string `json:"order_no"`
Type uint8 `json:"type"`
Quantity int64 `json:"quantity"`
Price int64 `json:"price"`
Amount int64 `json:"amount"`
GiftAmount int64 `json:"gift_amount"`
Discount int64 `json:"discount"`
Coupon string `json:"coupon"`
CouponDiscount int64 `json:"coupon_discount"`
Commission int64 `json:"commission,omitempty"`
Payment PaymentMethod `json:"payment"`
FeeAmount int64 `json:"fee_amount"`
TradeNo string `json:"trade_no"`
Status uint8 `json:"status"`
SubscribeId int64 `json:"subscribe_id"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
OrderDetail {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
OrderNo string `json:"order_no"`
Type uint8 `json:"type"`
Quantity int64 `json:"quantity"`
Price int64 `json:"price"`
Amount int64 `json:"amount"`
GiftAmount int64 `json:"gift_amount"`
Discount int64 `json:"discount"`
Coupon string `json:"coupon"`
CouponDiscount int64 `json:"coupon_discount"`
Commission int64 `json:"commission,omitempty"`
Payment PaymentMethod `json:"payment"`
Method string `json:"method"`
FeeAmount int64 `json:"fee_amount"`
TradeNo string `json:"trade_no"`
Status uint8 `json:"status"`
SubscribeId int64 `json:"subscribe_id"`
Subscribe Subscribe `json:"subscribe"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
Document {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Tags []string `json:"tags"`
Show bool `json:"show"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
Coupon {
Id int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
Count int64 `json:"count"`
Type uint8 `json:"type"`
Discount int64 `json:"discount"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
UserLimit int64 `json:"user_limit"`
Subscribe []int64 `json:"subscribe"`
UsedCount int64 `json:"used_count"`
Enable bool `json:"enable"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
Announcement {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Show *bool `json:"show"`
Pinned *bool `json:"pinned"`
Popup *bool `json:"popup"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
UserSubscribe {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
OrderId int64 `json:"order_id"`
SubscribeId int64 `json:"subscribe_id"`
Subscribe Subscribe `json:"subscribe"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
FinishedAt int64 `json:"finished_at"`
ResetTime int64 `json:"reset_time"`
Traffic int64 `json:"traffic"`
Download int64 `json:"download"`
Upload int64 `json:"upload"`
Token string `json:"token"`
Status uint8 `json:"status"`
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"`
RegisteredAt int64 `json:"registered_at"`
Enable bool `json:"enable"`
}
SortItem {
Id int64 `json:"id" validate:"required"`
Sort int64 `json:"sort" validate:"required"`
}
TimePeriod {
StartTime string `json:"start_time"`
EndTime string `json:"end_time"`
Multiplier float32 `json:"multiplier"`
}
NodeRelay {
Host string `json:"host"`
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"`
Identifier string `json:"identifier"`
UserAgent string `json:"user_agent"`
Online bool `json:"online"`
Enabled bool `json:"enabled"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
UserAuthMethod {
AuthType string `json:"auth_type"`
AuthIdentifier string `json:"auth_identifier"`
Verified bool `json:"verified"`
}
AuthMethodConfig {
Id int64 `json:"id"`
Method string `json:"method"`
Config interface{} `json:"config"`
Enabled bool `json:"enabled"`
}
TrafficLog {
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"`
}
ServerRuleGroup {
Id int64 `json:"id"`
Icon string `json:"icon"`
Name string `json:"name" validate:"required"`
Tags []string `json:"tags"`
Rules string `json:"rules"`
Enable bool `json:"enable"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
UserSubscribeLog {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
UserSubscribeId int64 `json:"user_subscribe_id"`
Token string `json:"token"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
CreatedAt int64 `json:"created_at"`
}
UserLoginLog {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
LoginIP string `json:"login_ip"`
UserAgent string `json:"user_agent"`
Success bool `json:"success"`
CreatedAt int64 `json:"created_at"`
}
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"`
}
Ads {
Id int `json:"id"`
Title string `json:"title"`
Type string `json:"type"`
Content string `json:"content"`
Description string `json:"description"`
TargetURL string `json:"target_url"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
Status int `json:"status"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
//public order
PurchaseOrderRequest {
SubscribeId int64 `json:"subscribe_id"`
Quantity int64 `json:"quantity" validate:"required,gt=0"`
Payment int64 `json:"payment,omitempty"`
Coupon string `json:"coupon,omitempty"`
}
PreOrderResponse {
Price int64 `json:"price"`
Amount int64 `json:"amount"`
Discount int64 `json:"discount"`
GiftAmount int64 `json:"gift_amount"`
Coupon string `json:"coupon"`
CouponDiscount int64 `json:"coupon_discount"`
FeeAmount int64 `json:"fee_amount"`
}
PurchaseOrderResponse {
OrderNo string `json:"order_no"`
}
RenewalOrderRequest {
UserSubscribeID int64 `json:"user_subscribe_id"`
Quantity int64 `json:"quantity"`
Payment int64 `json:"payment"`
Coupon string `json:"coupon,omitempty"`
}
RenewalOrderResponse {
OrderNo string `json:"order_no"`
}
ResetTrafficOrderRequest {
UserSubscribeID int64 `json:"user_subscribe_id"`
Payment int64 `json:"payment"`
}
ResetTrafficOrderResponse {
OrderNo string `json:"order_no"`
}
RechargeOrderRequest {
Amount int64 `json:"amount"`
Payment int64 `json:"payment"`
}
RechargeOrderResponse {
OrderNo string `json:"order_no"`
}
PreRenewalOrderResponse {
OrderNo string `json:"orderNo"`
}
CloseOrderRequest {
OrderNo string `json:"orderNo" validate:"required"`
}
QueryOrderDetailRequest {
OrderNo string `form:"order_no" validate:"required"`
}
StripePayment {
Method string `json:"method"`
ClientSecret string `json:"client_secret"`
PublishableKey string `json:"publishable_key"`
}
QueryOrderListRequest {
Page int `form:"page" validate:"required"`
Size int `form:"size" validate:"required"`
}
QueryOrderListResponse {
Total int64 `json:"total"`
List []OrderDetail `json:"list"`
}
//public document
QueryDocumentListResponse {
Total int64 `json:"total"`
List []Document `json:"list"`
}
QueryDocumentDetailRequest {
Id int64 `form:"id" validate:"required"`
}
// public payment
GetAvailablePaymentMethodsResponse {
List []PaymentMethod `json:"list"`
}
// public announcement
QueryAnnouncementRequest {
Page int `form:"page"`
Size int `form:"size"`
Pinned *bool `form:"pinned"`
Popup *bool `form:"popup"`
}
QueryAnnouncementResponse {
Total int64 `json:"total"`
List []Announcement `json:"announcements"`
}
//subscribe
QuerySubscribeListResponse {
List []Subscribe `json:"list"`
Total int64 `json:"total"`
}
QuerySubscribeGroupListResponse {
List []SubscribeGroup `json:"list"`
Total int64 `json:"total"`
}
GetUserSubscribeTrafficLogsRequest {
Page int `form:"page"`
Size int `form:"size"`
UserId int64 `form:"user_id"`
SubscribeId int64 `form:"subscribe_id"`
StartTime int64 `form:"start_time"`
EndTime int64 `form:"end_time"`
}
GetUserSubscribeTrafficLogsResponse {
List []TrafficLog `json:"list"`
Total int64 `json:"total"`
}
QueryUserAffiliateListResponse {
List []UserAffiliate `json:"list"`
Total int64 `json:"total"`
}
PlatformResponse {
List []PlatformInfo `json:"list"`
}
PlatformInfo {
Platform string `json:"platform"`
PlatformUrl string `json:"platform_url"`
PlatformFieldDescription map[string]string `json:"platform_field_description"`
}
AlipayNotifyResponse {
ReturnCode string `json:"return_code"`
}
EPayNotifyRequest {
Pid int64 `json:"pid" form:"pid"`
TradeNo string `json:"trade_no" form:"trade_no"`
OutTradeNo string `json:"out_trade_no" form:"out_trade_no"`
Type string `json:"type" form:"type"`
Name string `json:"name" form:"name"`
Money string `json:"money" form:"money"`
TradeStatus string `json:"trade_status" form:"trade_status"`
Param string `json:"param" form:"param"`
Sign string `json:"sign" form:"sign"`
SignType string `json:"sign_type" form:"sign_type"`
}
QueryUserAffiliateListRequest {
Page int `form:"page"`
Size int `form:"size"`
}
CheckoutOrderRequest {
OrderNo string `json:"orderNo"`
ReturnUrl string `json:"returnUrl,omitempty"`
}
CheckoutOrderResponse {
Type string `json:"type"`
CheckoutUrl string `json:"checkout_url,omitempty"`
Stripe *StripePayment `json:"stripe,omitempty"`
}
SiteCustomDataContacts {
Email string `json:"email"`
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"`
}
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"`
}
)

30
cmd/root.go Normal file
View File

@ -0,0 +1,30 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(startCmd)
rootCmd.AddCommand(versionCmd)
}
var rootCmd = &cobra.Command{
Use: "PPanel",
Short: "PPanel is a modern multi-user agent panel.",
Long: `[ PPanel is a pure, professional, and perfect open-source proxy panel tool, designed to be your ideal choice for learning and practical use.]
[ Simple and easy to operate.]`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("args:", args)
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

155
cmd/run.go Normal file
View File

@ -0,0 +1,155 @@
package cmd
import (
"context"
"fmt"
"github.com/perfect-panel/ppanel-server/pkg/constant"
"log"
"os"
"os/signal"
"syscall"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/perfect-panel/ppanel-server/initialize"
"github.com/perfect-panel/ppanel-server/internal"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/conf"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/orm"
"github.com/perfect-panel/ppanel-server/pkg/service"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/queue"
"github.com/perfect-panel/ppanel-server/scheduler"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)
func init() {
startCmd.Flags().StringVar(&startConfigPath, "config", "etc/ppanel.yaml", "ppanel.yaml directory to read from")
}
var (
startConfigPath string
)
var startCmd = &cobra.Command{
Use: "run",
Short: "start PPanel",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("[PPanel version] v" + fmt.Sprintf("%s(%s)", constant.Version, constant.BuildNumber))
run()
},
}
func run() {
services := getServers()
defer services.Stop()
go services.Start()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
<-quit
}
func getServers() *service.Group {
var c config.Config
// check config file is exist
if _, err := os.Stat(startConfigPath); os.IsNotExist(err) {
// check directory is existed
if _, err := os.Stat("etc"); os.IsNotExist(err) {
logger.Errorf("Directory %s does not exist. Creating it...\n", "etc")
if err = os.MkdirAll("etc", os.ModePerm); err != nil {
log.Fatalf("Please create the directory %s and place the configuration file %s in it.\n", "etc", startConfigPath)
}
}
// create new config file
if _, err := os.Create(startConfigPath); err != nil {
logger.Errorf("Please create the configuration file %s in the directory %s.\n", startConfigPath, "etc")
panic(fmt.Sprintf("Please create the configuration file %s in the directory %s.\n", startConfigPath, "etc"))
}
}
// check config file is empty, if empty, start init web server
if initConfig(&c) {
status, server := initialize.Config(startConfigPath)
<-status
if err := server.Shutdown(context.TODO()); err != nil {
log.Printf("Init Server Shutdown: %s\n", err.Error())
}
}
conf.MustLoad(startConfigPath, &c)
if !c.Debug {
gin.SetMode(gin.ReleaseMode)
}
// init logger
if err := logger.SetUp(c.Logger); err != nil {
logger.Errorf("Logger setup failed: %v", err.Error())
}
// init service context
ctx := svc.NewServiceContext(c)
services := service.NewServiceGroup()
services.Add(internal.NewService(ctx))
services.Add(queue.NewService(ctx))
services.Add(scheduler.NewService(ctx))
return services
}
func initConfig(c *config.Config) bool {
// load config
conf.MustLoad(startConfigPath, c)
// check custom config
if startConfigPath != "etc/ppanel.yaml" && c.MySQL.Addr == "" {
return true
}
// check access secret
if c.JwtAuth.AccessSecret == "" && startConfigPath == "etc/ppanel.yaml" {
c.JwtAuth.AccessSecret = uuid.New().String()
// Get environment variables
dsn := os.Getenv("PPANEL_DB")
if dsn == "" {
return true
}
cfg := orm.ParseDSN(dsn)
if cfg == nil {
return true
} else {
c.MySQL = *cfg
}
// Get environment variables
uri := os.Getenv("PPANEL_REDIS")
if uri == "" {
return true
}
addr, pass, db, err := tool.ParseRedisURI(uri)
if err != nil {
return true
} else {
c.Redis.Host = addr
c.Redis.Pass = pass
c.Redis.DB = db
}
// save yaml file
newConfig := config.File{
Host: c.Host,
Port: c.Port,
Debug: c.Debug,
JwtAuth: c.JwtAuth,
Logger: c.Logger,
MySQL: c.MySQL,
Redis: c.Redis,
}
fileData, err := yaml.Marshal(newConfig)
if err != nil {
panic(err.Error())
}
// write to file
if err := os.WriteFile(startConfigPath, fileData, 0644); err != nil {
panic(err.Error())
}
}
return false
}

16
cmd/version.go Normal file
View File

@ -0,0 +1,16 @@
package cmd
import (
"fmt"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "PPanel version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("[PPanel version] " + config.Version)
},
}

161
doc/config-zh.md Normal file
View File

@ -0,0 +1,161 @@
# PPanel 配置指南
本文件为 PPanel 应用程序的配置文件提供全面指南。配置文件采用 YAML 格式定义了服务器、日志、数据库、Redis 和管理员访问的相关设置。
## 1. 配置文件概述
- **默认路径**`./etc/ppanel.yaml`
- **自定义路径**:通过启动参数 `--config` 指定配置文件路径。
- **格式**YAML 格式,支持注释,文件名需以 `.yaml` 结尾。
## 2. 配置文件结构
以下是配置文件示例,包含默认值和说明:
```yaml
# PPanel 配置文件
Host: "0.0.0.0" # 服务监听地址
Port: 8080 # 服务监听端口
Debug: false # 是否开启调试模式(禁用后台日志)
JwtAuth: # JWT 认证配置
AccessSecret: "" # 访问令牌密钥(为空时随机生成)
AccessExpire: 604800 # 访问令牌过期时间(秒)
Logger: # 日志配置
ServiceName: "" # 日志服务标识名称
Mode: "console" # 日志输出模式console、file、volume
Encoding: "json" # 日志格式json、plain
TimeFormat: "2006-01-02T15:04:05.000Z07:00" # 自定义时间格式
Path: "logs" # 日志文件目录
Level: "info" # 日志级别info、error、severe
Compress: false # 是否压缩日志文件
KeepDays: 7 # 日志保留天数
StackCooldownMillis: 100 # 堆栈日志冷却时间(毫秒)
MaxBackups: 3 # 最大日志备份数
MaxSize: 50 # 最大日志文件大小MB
Rotation: "daily" # 日志轮转策略daily、size
MySQL: # MySQL 数据库配置
Addr: "" # MySQL 地址(必填)
Username: "" # MySQL 用户名(必填)
Password: "" # MySQL 密码(必填)
Dbname: "" # MySQL 数据库名(必填)
Config: "charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai" # MySQL 连接参数
MaxIdleConns: 10 # 最大空闲连接数
MaxOpenConns: 100 # 最大打开连接数
LogMode: "info" # 日志级别debug、error、warn、info
LogZap: true # 是否使用 Zap 记录 SQL 日志
SlowThreshold: 1000 # 慢查询阈值(毫秒)
Redis: # Redis 配置
Host: "localhost:6379" # Redis 地址
Pass: "" # Redis 密码
DB: 0 # Redis 数据库索引
Administer: # 管理员登录配置
Email: "admin@ppanel.dev" # 管理员登录邮箱
Password: "password" # 管理员登录密码
```
## 3. 配置项说明
### 3.1 服务器设置
- **`Host`**:服务监听的地址。
- 默认:`0.0.0.0`(监听所有网络接口)。
- **`Port`**:服务监听的端口。
- 默认:`8080`
- **`Debug`**:是否开启调试模式,开启后禁用后台日志功能。
- 默认:`false`
### 3.2 JWT 认证 (`JwtAuth`)
- **`AccessSecret`**:访问令牌的密钥。
- 默认:为空时随机生成。
- **`AccessExpire`**:令牌过期时间(秒)。
- 默认:`604800`7天
### 3.3 日志配置 (`Logger`)
- **`ServiceName`**:日志的服务标识名称,在 `volume` 模式下用作日志文件名。
- 默认:`""`
- **`Mode`**:日志输出方式。
- 选项:`console`(标准输出/错误输出)、`file`(写入指定目录)、`volume`Docker 卷)。
- 默认:`console`
- **`Encoding`**:日志格式。
- 选项:`json`(结构化 JSON`plain`(纯文本,带颜色)。
- 默认:`json`
- **`TimeFormat`**:日志时间格式。
- 默认:`2006-01-02T15:04:05.000Z07:00`
- **`Path`**:日志文件存储目录。
- 默认:`logs`
- **`Level`**:日志过滤级别。
- 选项:`info`(记录所有日志)、`error`(仅错误和严重日志)、`severe`(仅严重日志)。
- 默认:`info`
- **`Compress`**:是否压缩日志文件(仅在 `file` 模式下生效)。
- 默认:`false`
- **`KeepDays`**:日志文件保留天数。
- 默认:`7`
- **`StackCooldownMillis`**:堆栈日志冷却时间(毫秒),防止日志过多。
- 默认:`100`
- **`MaxBackups`**:最大日志备份数量(仅在 `size` 轮转时生效)。
- 默认:`3`
- **`MaxSize`**日志文件最大大小MB仅在 `size` 轮转时生效)。
- 默认:`50`
- **`Rotation`**:日志轮转策略。
- 选项:`daily`(按天轮转)、`size`(按大小轮转)。
- 默认:`daily`
### 3.4 MySQL 数据库 (`MySQL`)
- **`Addr`**MySQL 服务器地址。
- 必填。
- **`Username`**MySQL 用户名。
- 必填。
- **`Password`**MySQL 密码。
- 必填。
- **`Dbname`**MySQL 数据库名。
- 必填。
- **`Config`**MySQL 连接参数。
- 默认:`charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai`
- **`MaxIdleConns`**:最大空闲连接数。
- 默认:`10`
- **`MaxOpenConns`**:最大打开连接数。
- 默认:`100`
- **`LogMode`**SQL 日志级别。
- 选项:`debug``error``warn``info`
- 默认:`info`
- **`LogZap`**:是否使用 Zap 记录 SQL 日志。
- 默认:`true`
- **`SlowThreshold`**:慢查询阈值(毫秒)。
- 默认:`1000`
### 3.5 Redis 配置 (`Redis`)
- **`Host`**Redis 服务器地址。
- 默认:`localhost:6379`
- **`Pass`**Redis 密码。
- 默认:`""`(无密码)。
- **`DB`**Redis 数据库索引。
- 默认:`0`
### 3.6 管理员登录 (`Administer`)
- **`Email`**:管理员登录邮箱。
- 默认:`admin@ppanel.dev`
- **`Password`**:管理员登录密码。
- 默认:`password`
## 4. 环境变量
以下环境变量可用于覆盖配置文件中的设置:
| 环境变量 | 配置项 | 示例值 |
|----------------|----------|----------------------------------------------|
| `PPANEL_DB` | MySQL 配置 | `root:password@tcp(localhost:3306)/vpnboard` |
| `PPANEL_REDIS` | Redis 配置 | `redis://localhost:6379` |
## 5. 最佳实践
- **安全性**:生产环境中避免使用默认的 `Administer` 凭据,更新 `Email``Password` 为安全值。
- **日志**:生产环境中建议使用 `file``volume` 模式持久化日志,将 `Level` 设置为 `error``severe` 以减少日志量。
- **数据库**:确保 `MySQL``Redis` 凭据安全,避免在版本控制中暴露。
- **JWT**:为 `JwtAuth``AccessSecret` 设置强密钥以增强安全性。
如需进一步帮助,请参考 PPanel 官方文档或联系支持团队。

164
doc/config.md Normal file
View File

@ -0,0 +1,164 @@
# PPanel Configuration Guide
This document provides a comprehensive guide to the configuration file for the PPanel application. The configuration
file is in YAML format and defines settings for the server, logging, database, Redis, and admin access.
## 1. Configuration File Overview
- **Default Path**: `./etc/ppanel.yaml`
- **Custom Path**: Specify a custom path using the `--config` startup parameter.
- **Format**: YAML, supports comments, and must be named with a `.yaml` extension.
## 2. Configuration File Structure
Below is an example of the configuration file with default values and explanations:
```yaml
# PPanel Configuration
Host: "0.0.0.0" # Server listening address
Port: 8080 # Server listening port
Debug: false # Enable debug mode (disables background logging)
JwtAuth: # JWT authentication settings
AccessSecret: "" # Access token secret (randomly generated if empty)
AccessExpire: 604800 # Access token expiration (seconds)
Logger: # Logging configuration
ServiceName: "" # Service name for log identification
Mode: "console" # Log output mode (console, file, volume)
Encoding: "json" # Log format (json, plain)
TimeFormat: "2006-01-02T15:04:05.000Z07:00" # Custom time format
Path: "logs" # Log file directory
Level: "info" # Log level (info, error, severe)
Compress: false # Enable log compression
KeepDays: 7 # Log retention period (days)
StackCooldownMillis: 100 # Stack trace cooldown (milliseconds)
MaxBackups: 3 # Maximum number of log backups
MaxSize: 50 # Maximum log file size (MB)
Rotation: "daily" # Log rotation strategy (daily, size)
MySQL: # MySQL database configuration
Addr: "" # MySQL address (required)
Username: "" # MySQL username (required)
Password: "" # MySQL password (required)
Dbname: "" # MySQL database name (required)
Config: "charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai" # MySQL connection parameters
MaxIdleConns: 10 # Maximum idle connections
MaxOpenConns: 100 # Maximum open connections
LogMode: "info" # Log level (debug, error, warn, info)
LogZap: true # Enable Zap logging for SQL
SlowThreshold: 1000 # Slow query threshold (milliseconds)
Redis: # Redis configuration
Host: "localhost:6379" # Redis address
Pass: "" # Redis password
DB: 0 # Redis database index
Administer: # Admin login configuration
Email: "admin@ppanel.dev" # Admin login email
Password: "password" # Admin login password
```
## 3. Configuration Details
### 3.1 Server Settings
- **`Host`**: Address the server listens on.
- Default: `0.0.0.0` (all network interfaces).
- **`Port`**: Port the server listens on.
- Default: `8080`.
- **`Debug`**: Enables debug mode, disabling background logging.
- Default: `false`.
### 3.2 JWT Authentication (`JwtAuth`)
- **`AccessSecret`**: Secret key for access tokens.
- Default: Randomly generated if not specified.
- **`AccessExpire`**: Token expiration time in seconds.
- Default: `604800` (7 days).
### 3.3 Logging (`Logger`)
- **`ServiceName`**: Identifier for logs, used as the log filename in `volume` mode.
- Default: `""`.
- **`Mode`**: Log output destination.
- Options: `console` (stdout/stderr), `file` (to a directory), `volume` (Docker volume).
- Default: `console`.
- **`Encoding`**: Log format.
- Options: `json` (structured JSON), `plain` (plain text with colors).
- Default: `json`.
- **`TimeFormat`**: Custom time format for logs.
- Default: `2006-01-02T15:04:05.000Z07:00`.
- **`Path`**: Directory for log files.
- Default: `logs`.
- **`Level`**: Log filtering level.
- Options: `info` (all logs), `error` (error and severe), `severe` (severe only).
- Default: `info`.
- **`Compress`**: Enable compression for log files (only in `file` mode).
- Default: `false`.
- **`KeepDays`**: Retention period for log files (in days).
- Default: `7`.
- **`StackCooldownMillis`**: Cooldown for stack trace logging to prevent log flooding.
- Default: `100`.
- **`MaxBackups`**: Maximum number of log backups (for `size` rotation).
- Default: `3`.
- **`MaxSize`**: Maximum log file size in MB (for `size` rotation).
- Default: `50`.
- **`Rotation`**: Log rotation strategy.
- Options: `daily` (rotate daily), `size` (rotate by size).
- Default: `daily`.
### 3.4 MySQL Database (`MySQL`)
- **`Addr`**: MySQL server address.
- Required.
- **`Username`**: MySQL username.
- Required.
- **`Password`**: MySQL password.
- Required.
- **`Dbname`**: MySQL database name.
- Required.
- **`Config`**: MySQL connection parameters.
- Default: `charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai`.
- **`MaxIdleConns`**: Maximum idle connections.
- Default: `10`.
- **`MaxOpenConns`**: Maximum open connections.
- Default: `100`.
- **`LogMode`**: SQL logging level.
- Options: `debug`, `error`, `warn`, `info`.
- Default: `info`.
- **`LogZap`**: Enable Zap logging for SQL queries.
- Default: `true`.
- **`SlowThreshold`**: Threshold for slow query logging (in milliseconds).
- Default: `1000`.
### 3.5 Redis (`Redis`)
- **`Host`**: Redis server address.
- Default: `localhost:6379`.
- **`Pass`**: Redis password.
- Default: `""` (no password).
- **`DB`**: Redis database index.
- Default: `0`.
### 3.6 Admin Login (`Administer`)
- **`Email`**: Admin login email.
- Default: `admin@ppanel.dev`.
- **`Password`**: Admin login password.
- Default: `password`.
## 4. Environment Variables
The following environment variables can be used to override configuration settings:
| Environment Variable | Configuration Section | Example Value |
|----------------------|-----------------------|----------------------------------------------|
| `PPANEL_DB` | MySQL | `root:password@tcp(localhost:3306)/vpnboard` |
| `PPANEL_REDIS` | Redis | `redis://localhost:6379` |
## 5. Best Practices
- **Security**: Avoid using default `Administer` credentials in production. Update `Email` and `Password` to secure
values.
- **Logging**: Use `file` or `volume` mode for production to persist logs. Adjust `Level` to `error` or `severe` to
reduce log volume.
- **Database**: Ensure `MySQL` and `Redis` credentials are secure and not exposed in version control.
- **JWT**: Specify a strong `AccessSecret` for `JwtAuth` to enhance security.
For further assistance, refer to the official PPanel documentation or contact support.

133
doc/install-zh.md Normal file
View File

@ -0,0 +1,133 @@
### 安装说明
#### 前置系统要求
- Mysql 5.7+ (推荐使用8.0)
- Redis 6.0+ (推荐使用7.0)
#### 二进制安装
1.确定系统架构,并下载对应的二进制文件
下载地址:`https://github.com/perfect-panel/ppanel/releases`
示例说明系统Linux amd64用户root当前目录/root
- 下载二进制文件
```shell
$ wget https://github.com/perfect-panel/ppanel/releases/download/v0.1.0/ppanel-server-linux-amd64.tar.gz
```
- 解压二进制文件
```shell
$ tar -zxvf ppanel-server-linux-amd64.tar.gz
```
- 进入解压后的目录
```shell
$ cd ppanel-server-linux-amd64
```
- 赋予二进制文件执行权限
```shell
$ chmod +x ppanel-server
```
- 创建 systemd 服务文件
```shell
$ cat > /etc/systemd/system/ppanel.service <<EOF
[Unit]
Description=PPANEL Server
After=network.target
[Service]
ExecStart=/root/ppanel-server-linux-amd64/ppanel-server
Restart=always
User=root
WorkingDirectory=/root/ppanel-server-linux-amd64
[Install]
WantedBy=multi-user.target
EOF
```
- 重新加载 systemd 服务
```shell
$ systemctl daemon-reload
```
- 启动服务
```shell
$ systemctl start ppanel
```
##### 其他说明
1. 安装路径:二进制文件将解压到 /root/ppanel-server-linux-amd64 目录下
2. systemd 服务:
- 服务名称ppanel
- 服务配置文件:/etc/systemd/system/ppanel.service
- 服务启动命令systemctl start ppanel
- 服务停止命令systemctl stop ppanel
- 服务重启命令systemctl restart ppanel
- 服务状态命令systemctl status ppanel
- 服务开机自启systemctl enable ppanel
3. 设置开机自启可通过以下命令开机自启
```shell
$ systemctl enable ppanel
```
4. 服务日志:服务日志默认输出到 /root/ppanel-server-linux-amd64/ppanel.log 文件中
5. 可通过 `journalctl -u ppanel -f` 查看服务日志
6. 当配置文件为空或者不存在的情况下,服务会使用默认配置启动,配置文件路径为:`./etc/ppanel.yaml`
请通过`http://服务器地址:8080/init` 初始化系统配置
#### NGINX 反向代理配置
以下是反向代理配置示例,将 `ppanel` 服务代理到 `api.ppanel.dev` 域名下
```nginx
server {
listen 80;
server_name ppanel.dev;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
add_header X-Cache $upstream_cache_status;
#Set Nginx Cache
set $static_filezbsQiET1 0;
if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" )
{
set $static_filezbsQiET1 1;
expires 1m;
}
if ( $static_filezbsQiET1 = 0 )
{
add_header Cache-Control no-cache;
}
}
}
```
如果使用cloudflare代理服务需要获取到用户真实访问IP。请在Nginx配置文件中Http段落中加入:
- 需要依赖:**ngx_http_realip_module**模块, 使用nginx -V命令查看nginx是否已经编译该模块没有的话需要自己编译。
```nginx
# cloudflare Start
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# cloudflare END
```

142
doc/install.md Normal file
View File

@ -0,0 +1,142 @@
### Installation Instructions
#### Prerequisites
- MySQL 5.7+ (recommended: 8.0)
- Redis 6.0+ (recommended: 7.0)
#### Binary Installation
1. Determine your system architecture and download the corresponding binary file.
Download URL: `https://github.com/perfect-panel/ppanel/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
```
- Extract the binary file:
```shell
$ tar -zxvf ppanel-server-linux-amd64.tar.gz
```
- Navigate to the extracted directory:
```shell
$ cd ppanel-server-linux-amd64
```
- Grant execution permissions to the binary:
```shell
$ chmod +x ppanel
```
- Create a systemd service file:
```shell
$ cat > /etc/systemd/system/ppanel.service <<EOF
[Unit]
Description=PPANEL Server
After=network.target
[Service]
ExecStart=/root/ppanel-server-linux-amd64/ppanel
Restart=always
User=root
WorkingDirectory=/root/ppanel-server-linux-amd64
[Install]
WantedBy=multi-user.target
EOF
```
- Reload the systemd service configuration:
```shell
$ systemctl daemon-reload
```
- Start the service:
```shell
$ systemctl start ppanel
```
#### Additional Notes
1. Installation Path: The binary files will be extracted to /root/ppanel-server-linux-amd64.
2. systemd Service:
- Service Name: ppanel
- Service Configuration File: /etc/systemd/system/ppanel.service
- Service Commands:
- Start: systemctl start ppanel
- Stop: systemctl stop ppanel
- Restart: systemctl restart ppanel
- Status: systemctl status ppanel
- Enable on Boot: systemctl enable ppanel
3. Enable Auto-start: Use the following command to enable the service on boot:
```shell
$ systemctl enable ppanel
```
4. Service Logs: By default, logs are output to `/root/ppanel-server-linux-amd64/ppanel.log`.
5. You can view service logs using: `journalctl -u ppanel -f`
6. If the configuration file is missing or empty, the service will start with default settings. The configuration file path is `./etc/ppanel.yaml`. Access `http://<server_address>:8080/init` to **initialize the system configuration**.
#### NGINX Reverse Proxy Configuration
Below is an example configuration to proxy the ppanel service to the domain api.ppanel.dev:
```nginx
server {
listen 80;
server_name ppanel.dev;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
add_header X-Cache $upstream_cache_status;
# Set Nginx Cache
set $static_file_cache 0;
if ($uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$") {
set $static_file_cache 1;
expires 1m;
}
if ($static_file_cache = 0) {
add_header Cache-Control no-cache;
}
}
}
```
If using Cloudflare as a proxy service, you need to retrieve the user's real IP address. Add the following to the http section of the NGINX configuration file:
- Dependency: `ngx_http_realip_module`. Check if your NGINX build includes this module by running `nginx -V`. If not, you will need to recompile NGINX with this module.
```nginx
# Cloudflare Start
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Cloudflare End
```

13
docker-compose.yml Normal file
View File

@ -0,0 +1,13 @@
version: '3'
services:
ppanel:
container_name: ppanel-server
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
volumes:
- ./etc/ppanel.yaml:/app/etc/ppanel.yaml
restart: always

BIN
generate/gopure-amd64.exe Executable file

Binary file not shown.

BIN
generate/gopure-arm64.exe Executable file

Binary file not shown.

BIN
generate/gopure-darwin-amd64 Executable file

Binary file not shown.

BIN
generate/gopure-darwin-arm64 Executable file

Binary file not shown.

BIN
generate/gopure-linux-amd64 Executable file

Binary file not shown.

BIN
generate/gopure-linux-arm64 Executable file

Binary file not shown.

139
go.mod Normal file
View File

@ -0,0 +1,139 @@
module github.com/perfect-panel/ppanel-server
go 1.23.3
require (
github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f
github.com/alibabacloud-go/darabonba-openapi v0.1.18
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18
github.com/alibabacloud-go/tea v1.2.2
github.com/alicebob/miniredis/v2 v2.34.0
github.com/anaskhan96/go-password-encoder v0.0.0-20201010210601-c765b799fd72
github.com/andybalholm/brotli v1.1.1
github.com/forgoer/openssl v1.6.0
github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.24.0
github.com/go-resty/resty/v2 v2.15.3
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/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/hibiken/asynq v0.24.1
github.com/jinzhu/copier v0.4.0
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/smartwalle/alipay/v3 v3.2.23
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0
github.com/stripe/stripe-go/v81 v81.1.0
github.com/twilio/twilio-go v1.23.11
go.opentelemetry.io/otel v1.29.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0
go.opentelemetry.io/otel/exporters/zipkin v1.24.0
go.opentelemetry.io/otel/sdk v1.29.0
go.opentelemetry.io/otel/trace v1.29.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.32.0
golang.org/x/oauth2 v0.25.0
golang.org/x/time v0.6.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12
gorm.io/plugin/soft_delete v1.2.1
k8s.io/apimachinery v0.31.1
)
require (
github.com/fatih/color v1.18.0
github.com/goccy/go-json v0.10.4
github.com/golang-migrate/migrate/v4 v4.18.2
github.com/spaolacci/murmur3 v1.1.0
google.golang.org/grpc v1.64.1
google.golang.org/protobuf v1.36.3
)
require (
cloud.google.com/go/compute/metadata v0.6.0 // indirect
filippo.io/edwards25519 v1.1.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
github.com/alibabacloud-go/openapi-util v0.1.1 // indirect
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
github.com/aliyun/credentials-go v1.3.10 // indirect
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
github.com/bytedance/sonic v1.12.7 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.5.6 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.2.2 // indirect
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/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
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/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/smartwalle/ncrypto v1.0.4 // indirect
github.com/smartwalle/ngx v1.0.9 // indirect
github.com/smartwalle/nsign v1.0.9 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.13.0 // indirect
golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

544
go.sum Normal file
View File

@ -0,0 +1,544 @@
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=
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=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
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/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=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
github.com/alibabacloud-go/darabonba-openapi v0.1.18 h1:3eUVmAr7WCJp7fgIvmCd9ZUyuwtJYbtUqJIed5eXCmk=
github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc=
github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18 h1:hfZA4cgIl6frNdsRmAyj8sn9J1bihQpYbzIVv2T/+Cs=
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18/go.mod h1:di54xjBFHvKiQQo7st3TUmiMy0ywne5TOHup786Rhes=
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28=
github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA=
github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/anaskhan96/go-password-encoder v0.0.0-20201010210601-c765b799fd72 h1:a93gW7OBt55SksMQVibqPWdu4Ly73KM4d3zoIUUX3cs=
github.com/anaskhan96/go-password-encoder v0.0.0-20201010210601-c765b799fd72/go.mod h1:PsJICrlruG9QcJDYuZ0dO/2KtMDALzRbony8NkxZ2nE=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q=
github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g=
github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dhui/dktest v0.4.4 h1:+I4s6JRE1yGuqflzwqG+aIaMdgXIorCf5P98JnaAWa8=
github.com/dhui/dktest v0.4.4/go.mod h1:4+22R4lgsdAXrDyaH4Nqx2JEz2hLp49MqQmm9HLCQhM=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4=
github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/forgoer/openssl v1.6.0 h1:IueL+UfH0hKo99xFPojHLlO3QzRBQqFY+Cht0WwtOC0=
github.com/forgoer/openssl v1.6.0/go.mod h1:9DZ4yOsQmveP0aXC/BpQ++Y5TKaz5yR9+emcxmIZNZs=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
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-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=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
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/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=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q=
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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/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=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nyaruka/phonenumbers v1.5.0 h1:0M+Gd9zl53QC4Nl5z1Yj1O/zPk2XXBUwR/vlzdXSJv4=
github.com/nyaruka/phonenumbers v1.5.0/go.mod h1:gv+CtldaFz+G3vHHnasBSirAi3O2XLqZzVWz4V1pl2E=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA=
github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
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/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/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=
github.com/smartwalle/ncrypto v1.0.4/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk=
github.com/smartwalle/ngx v1.0.9 h1:pUXDvWRZJIHVrCKA1uZ15YwNti+5P4GuJGbpJ4WvpMw=
github.com/smartwalle/ngx v1.0.9/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0=
github.com/smartwalle/nsign v1.0.9 h1:8poAgG7zBd8HkZy9RQDwasC6XZvJpDGQWSjzL2FZL6E=
github.com/smartwalle/nsign v1.0.9/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stripe/stripe-go/v81 v81.1.0 h1:OlpGPO2vhS2raLR/NuvHKeRUZ57FTkdZBTcd5Hhoyos=
github.com/stripe/stripe-go/v81 v81.1.0/go.mod h1:C/F4jlmnGNacvYtBp/LUHCvVUJEZffFQCobkzwY1WOo=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/twilio/twilio-go v1.23.11 h1:Q532m0rgWF1AzzF4Z4ejzTk5XeORWT+zLGzlklSk/iU=
github.com/twilio/twilio-go v1.23.11/go.mod h1:zRkMjudW7v7MqQ3cWNZmSoZJ7EBjPZ4OpNh2zm7Q6ko=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d h1:N0hmiNbwsSNwHBAvR3QB5w25pUwH4tK0Y/RltD1j1h4=
golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No=
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gorm.io/plugin/soft_delete v1.2.1 h1:qx9D/c4Xu6w5KT8LviX8DgLcB9hkKl6JC9f44Tj7cGU=
gorm.io/plugin/soft_delete v1.2.1/go.mod h1:Zv7vQctOJTGOsJ/bWgrN1n3od0GBAZgnLjEx+cApLGk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

273
initialize/config.go Normal file
View File

@ -0,0 +1,273 @@
package initialize
import (
"database/sql"
"embed"
"fmt"
"html/template"
"log"
"net/http"
"os"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"gorm.io/driver/mysql"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/perfect-panel/ppanel-server/initialize/migrate"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/pkg/conf"
"github.com/perfect-panel/ppanel-server/pkg/orm"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
"gorm.io/gorm"
)
//go:embed templates/*.html
var templateFS embed.FS
var initStatus = make(chan bool)
var configPath string
func Config(path string) (chan bool, *http.Server) {
// Set the configuration file path
configPath = path
// Create a new Gin instance
r := gin.Default()
// Create a new HTTP server
server := &http.Server{
Addr: ":8080",
Handler: r,
}
// Load templates
tmpl := template.Must(template.ParseFS(templateFS, "templates/*.html"))
r.SetHTMLTemplate(tmpl)
r.GET("/init", handleInit)
r.POST("/init/config", handleInitConfig)
r.POST("/init/mysql/test", HandleMySQLTest)
r.POST("/init/redis/test", HandleRedisTest)
// Handle 404
r.NoRoute(func(c *gin.Context) {
c.Redirect(http.StatusFound, "/init")
})
go func(server *http.Server) {
// Start the server
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listen: %s\n", err)
}
}(server)
return initStatus, server
}
func handleInit(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
}
func handleInitConfig(c *gin.Context) {
// Load configuration file
var cfg config.File
conf.MustLoad(configPath, &cfg)
var request struct {
AdminEmail string `json:"adminEmail"`
AdminPassword string `json:"adminPassword"`
MysqlHost string `json:"mysqlHost"`
MysqlPort string `json:"mysqlPort"`
MysqlDatabase string `json:"mysqlDatabase"`
MysqlUser string `json:"mysqlUser"`
MysqlPassword string `json:"mysqlPassword"`
RedisHost string `json:"redisHost"`
RedisPort string `json:"redisPort"`
RedisPassword string `json:"redisPassword"`
}
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"msg": "Invalid request",
"data": nil,
})
c.Abort()
return
}
cfg.Debug = false
// jwt secret
cfg.JwtAuth.AccessSecret = uuid.New().String()
// mysql
cfg.MySQL.Addr = fmt.Sprintf("%s:%s", request.MysqlHost, request.MysqlPort)
cfg.MySQL.Dbname = request.MysqlDatabase
cfg.MySQL.Username = request.MysqlUser
cfg.MySQL.Password = request.MysqlPassword
// redis
cfg.Redis.Host = fmt.Sprintf("%s:%s", request.RedisHost, request.RedisPort)
cfg.Redis.Pass = request.RedisPassword
// save config
fileData, err := yaml.Marshal(cfg)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "Configuration initialization failed",
"data": nil,
})
c.Abort()
return
}
// create mysql connection
db, err := orm.ConnectMysql(orm.Mysql{
Config: orm.Config{
Addr: fmt.Sprintf("%s:%s", request.MysqlHost, request.MysqlPort),
Username: request.MysqlUser,
Password: request.MysqlPassword,
Dbname: request.MysqlDatabase,
Config: "charset%3Dutf8mb4%26parseTime%3Dtrue%26loc%3DLocal",
MaxIdleConns: 10,
MaxOpenConns: 10,
SlowThreshold: 1000,
},
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "MySQL connection failed",
"data": nil,
})
c.Abort()
return
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", request.MysqlUser, request.MysqlPassword, request.MysqlHost, request.MysqlPort, request.MysqlDatabase)
// migrate database
if err = migrate.Migrate(dsn).Up(); err != nil {
logger.Errorf("[Init Mysql] Migrate failed: %v", err.Error())
c.JSON(http.StatusOK, gin.H{
"code": 500,
"msg": "Database migration failed",
"data": nil,
})
c.Abort()
return
}
// create admin user
if err = migrate.CreateAdminUser(request.AdminEmail, request.AdminPassword, db); err != nil {
logger.Errorf("[Init Mysql] Create admin user failed: %v", err.Error())
c.JSON(http.StatusOK, gin.H{
"code": 500,
"msg": "Admin user creation failed",
"data": nil,
})
c.Abort()
return
}
// write to file
if err = os.WriteFile(configPath, fileData, 0644); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "Configuration initialization failed",
"data": nil,
})
c.Abort()
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": "Configuration initialized",
"status": true,
})
initStatus <- true
}
func HandleMySQLTest(c *gin.Context) {
var request struct {
Host string `json:"host"`
Port string `json:"port"`
Database string `json:"database"`
User string `json:"user"`
Password string `json:"password"`
}
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"msg": "Invalid request",
"data": nil,
})
c.Abort()
return
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", request.User, request.Password, request.Host, request.Port, request.Database)
var status = true
var message string
var tx *sql.DB
var tables []string
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
logger.Errorf("connect mysql failed, err: %v\n", err.Error())
status = false
message = "MySQL connection failed"
goto result
}
tx, _ = db.DB()
if err := tx.Ping(); err != nil {
logger.Errorf("ping mysql failed, err: %v\n", err.Error())
status = false
message = "MySQL connection failed"
}
tables, err = db.Migrator().GetTables()
if err != nil {
logger.Errorf("database table check failed, err: %v\n", err.Error())
status = false
message = "Database table check failed"
goto result
}
if len(tables) > 0 {
status = false
message = "The database contains existing data. Please clear it before proceeding with the installation."
goto result
}
result:
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": message,
"status": status,
})
}
func HandleRedisTest(c *gin.Context) {
var request struct {
Host string `json:"host"`
Port string `json:"port"`
Password string `json:"password"`
}
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"msg": "Invalid request",
"data": nil,
})
c.Abort()
return
}
if err := tool.RedisPing(fmt.Sprintf("%s:%s", request.Host, request.Port), request.Password, 0); err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": nil,
"status": false,
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": nil,
"status": true,
})
}

33
initialize/email.go Normal file
View File

@ -0,0 +1,33 @@
package initialize
import (
"context"
"encoding/json"
"fmt"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/model/auth"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
// Email get email smtp config
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()))
}
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()))
}
tool.DeepCopy(&cfg, emailConfig)
cfg.Enable = *method.Enabled
value, _ := json.Marshal(emailConfig.PlatformConfig)
cfg.PlatformConfig = string(value)
ctx.Config.Email = cfg
}

22
initialize/init.go Normal file
View File

@ -0,0 +1,22 @@
package initialize
import (
"github.com/perfect-panel/ppanel-server/internal/svc"
)
func StartInitSystemConfig(svc *svc.ServiceContext) {
Migrate(svc)
Site(svc)
Node(svc)
Email(svc)
Invite(svc)
Verify(svc)
Subscribe(svc)
Register(svc)
Mobile(svc)
TrafficDataToRedis(svc)
if !svc.Config.Debug {
Telegram(svc)
}
}

23
initialize/invite.go Normal file
View File

@ -0,0 +1,23 @@
package initialize
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
func Invite(ctx *svc.ServiceContext) {
// Initialize the system configuration
logger.Debug("Register config initialization")
configs, err := ctx.SystemModel.GetInviteConfig(context.Background())
if err != nil {
logger.Error("[Init Invite Config] Get Invite Config Error: ", logger.Field("error", err.Error()))
return
}
var inviteConfig config.InviteConfig
tool.SystemConfigSliceReflectToStruct(configs, &inviteConfig)
ctx.Config.Invite = inviteConfig
}

View File

@ -0,0 +1,36 @@
-- 000001_init_schema.down.sql
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `user_subscribe_log`;
DROP TABLE IF EXISTS `user_subscribe`;
DROP TABLE IF EXISTS `user_login_log`;
DROP TABLE IF EXISTS `user_gift_amount_log`;
DROP TABLE IF EXISTS `user_device`;
DROP TABLE IF EXISTS `user_commission_log`;
DROP TABLE IF EXISTS `user_balance_log`;
DROP TABLE IF EXISTS `user_auth_methods`;
DROP TABLE IF EXISTS `user`;
DROP TABLE IF EXISTS `traffic_log`;
DROP TABLE IF EXISTS `ticket_follow`;
DROP TABLE IF EXISTS `ticket`;
DROP TABLE IF EXISTS `system`;
DROP TABLE IF EXISTS `subscribe_type`;
DROP TABLE IF EXISTS `subscribe_group`;
DROP TABLE IF EXISTS `subscribe`;
DROP TABLE IF EXISTS `sms`;
DROP TABLE IF EXISTS `server_rule_group`;
DROP TABLE IF EXISTS `server_group`;
DROP TABLE IF EXISTS `server`;
DROP TABLE IF EXISTS `payment`;
DROP TABLE IF EXISTS `order`;
DROP TABLE IF EXISTS `message_log`;
DROP TABLE IF EXISTS `document`;
DROP TABLE IF EXISTS `coupon`;
DROP TABLE IF EXISTS `auth_method`;
DROP TABLE IF EXISTS `application_version`;
DROP TABLE IF EXISTS `application_config`;
DROP TABLE IF EXISTS `application`;
DROP TABLE IF EXISTS `announcement`;
DROP TABLE IF EXISTS `ads`;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,559 @@
-- 000001_init_schema.up.sql
SET FOREIGN_KEY_CHECKS = 0;
CREATE TABLE IF NOT EXISTS `ads`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Ads title',
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Ads type',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Ads content',
`target_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Ads target url',
`start_time` datetime DEFAULT NULL COMMENT 'Ads start time',
`end_time` datetime DEFAULT NULL COMMENT 'Ads end time',
`status` tinyint(1) DEFAULT '0' COMMENT 'Ads status,0 disable,1 enable',
`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;
CREATE TABLE IF NOT EXISTS `announcement`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Title',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Content',
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Show',
`pinned` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Pinned',
`popup` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Popup',
`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;
CREATE TABLE IF NOT EXISTS `application`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用名称',
`icon` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '应用图标',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '更新描述',
`subscribe_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '订阅类型',
`created_at` datetime(3) DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `application_config`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`app_id` bigint NOT NULL DEFAULT '0' COMMENT 'App id',
`encryption_key` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Encryption Key',
`encryption_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Encryption Method',
`domains` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`startup_picture` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`startup_picture_skip_time` bigint NOT NULL DEFAULT '0' COMMENT 'Startup Picture Skip Time',
`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;
CREATE TABLE IF NOT EXISTS `application_version`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用地址',
`version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用版本',
`platform` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用平台',
`is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '默认版本',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '更新描述',
`application_id` bigint DEFAULT NULL COMMENT '所属应用',
`created_at` datetime(3) DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `fk_application_application_versions` (`application_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `auth_method`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'method',
`config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'OAuth Configuration',
`enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Enabled',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_auth_method` (`method`)
) ENGINE = InnoDB
AUTO_INCREMENT = 9
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `coupon`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Coupon Name',
`code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Coupon Code',
`count` bigint NOT NULL DEFAULT '0' COMMENT 'Count Limit',
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Coupon Type: 1: Percentage 2: Fixed Amount',
`discount` bigint NOT NULL DEFAULT '0' COMMENT 'Coupon Discount',
`start_time` bigint NOT NULL DEFAULT '0' COMMENT 'Start Time',
`expire_time` bigint NOT NULL DEFAULT '0' COMMENT 'Expire Time',
`user_limit` bigint NOT NULL DEFAULT '0' COMMENT 'User Limit',
`subscribe` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Subscribe Limit',
`used_count` bigint NOT NULL DEFAULT '0' COMMENT 'Used Count',
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Enable',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_coupon_code` (`code`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `document`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Document Title',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Document Content',
`tags` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Document Tags',
`show` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Show',
`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;
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;
CREATE TABLE IF NOT EXISTS `order`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`parent_id` bigint DEFAULT NULL COMMENT 'Parent Order Id',
`user_id` bigint NOT NULL DEFAULT '0' COMMENT 'User Id',
`order_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Order No',
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Order Type: 1: Subscribe, 2: Renewal, 3: ResetTraffic, 4: Recharge',
`quantity` bigint NOT NULL DEFAULT '1' COMMENT 'Quantity',
`price` bigint NOT NULL DEFAULT '0' COMMENT 'Original price',
`amount` bigint NOT NULL DEFAULT '0' COMMENT 'Order Amount',
`gift_amount` bigint NOT NULL DEFAULT '0' COMMENT 'User Gift Amount',
`discount` bigint NOT NULL DEFAULT '0' COMMENT 'Discount Amount',
`coupon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Coupon',
`coupon_discount` bigint NOT NULL DEFAULT '0' COMMENT 'Coupon Discount Amount',
`commission` bigint NOT NULL DEFAULT '0' COMMENT 'Order Commission',
`payment_id` bigint NOT NULL DEFAULT '-1' COMMENT 'Payment Id',
`method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Payment Method',
`fee_amount` bigint NOT NULL DEFAULT '0' COMMENT 'Fee Amount',
`trade_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Trade No',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Order Status: 1: Pending, 2: Paid, 3:Close, 4: Failed, 5:Finished',
`subscribe_id` bigint NOT NULL DEFAULT '0' COMMENT 'Subscribe Id',
`subscribe_token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Renewal Subscribe Token',
`is_new` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is New Order',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_order_order_no` (`order_no`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `payment`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Payment Name',
`platform` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Payment Platform',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Payment Description',
`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Payment Icon',
`domain` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Notification Domain',
`config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Payment Configuration',
`fee_mode` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Fee Mode: 0: No Fee 1: Percentage 2: Fixed Amount 3: Percentage + Fixed Amount',
`fee_percent` bigint DEFAULT '0' COMMENT 'Fee Percentage',
`fee_amount` bigint DEFAULT '0' COMMENT 'Fixed Fee Amount',
`enable` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Enabled',
`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Payment Token',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_payment_token` (`token`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `server`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Node Name',
`tags` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Tags',
`country` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Country',
`city` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'City',
`latitude` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'latitude',
`longitude` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'longitude',
`server_addr` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Server Address',
`relay_mode` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'none' COMMENT 'Relay Mode',
`relay_node` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Relay Node',
`speed_limit` bigint NOT NULL DEFAULT '0' COMMENT 'Speed Limit',
`traffic_ratio` decimal(4, 2) NOT NULL DEFAULT '0.00' COMMENT 'Traffic Ratio',
`group_id` bigint DEFAULT NULL COMMENT 'Group ID',
`protocol` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Protocol',
`config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Config',
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Enabled',
`sort` bigint NOT NULL DEFAULT '0' COMMENT 'Sort',
`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`),
KEY `idx_group_id` (`group_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `server_group`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Group Name',
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Group Description',
`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;
-- if `sms` not exist, create it
CREATE TABLE IF NOT EXISTS `sms`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`platform` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`area_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`telephone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`status` tinyint(1) DEFAULT '1',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `subscribe`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Subscribe Name',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Subscribe Description',
`unit_price` bigint NOT NULL DEFAULT '0' COMMENT 'Unit Price',
`unit_time` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Unit Time',
`discount` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Discount',
`replacement` bigint NOT NULL DEFAULT '0' COMMENT 'Replacement',
`inventory` bigint NOT NULL DEFAULT '0' COMMENT 'Inventory',
`traffic` bigint NOT NULL DEFAULT '0' COMMENT 'Traffic',
`speed_limit` bigint NOT NULL DEFAULT '0' COMMENT 'Speed Limit',
`device_limit` bigint NOT NULL DEFAULT '0' COMMENT 'Device Limit',
`quota` bigint NOT NULL DEFAULT '0' COMMENT 'Quota',
`group_id` bigint DEFAULT NULL COMMENT 'Group Id',
`server_group` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Server Group',
`server` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Server',
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Show portal page',
`sell` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Sell',
`sort` bigint NOT NULL DEFAULT '0' COMMENT 'Sort',
`deduction_ratio` bigint DEFAULT '0' COMMENT 'Deduction Ratio',
`allow_deduction` tinyint(1) DEFAULT '1' COMMENT 'Allow deduction',
`reset_cycle` bigint DEFAULT '0' COMMENT 'Reset Cycle: 0: No Reset, 1: 1st, 2: Monthly, 3: Yearly',
`renewal_reset` tinyint(1) DEFAULT '0' COMMENT 'Renew Reset',
`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;
CREATE TABLE IF NOT EXISTS `subscribe_group`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Group Name',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Group Description',
`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;
CREATE TABLE IF NOT EXISTS `subscribe_type`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '订阅类型',
`mark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '订阅标识',
`created_at` datetime(3) DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 15
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `system`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`category` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Category',
`key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Key Name',
`value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Key Value',
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Type',
`desc` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Description',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_system_key` (`key`),
KEY `index_key` (`key`)
) ENGINE = InnoDB
AUTO_INCREMENT = 42
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `ticket`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Title',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Description',
`user_id` bigint NOT NULL DEFAULT '0' COMMENT 'UserId',
`status` tinyint(1) NOT NULL DEFAULT '1' 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;
CREATE TABLE IF NOT EXISTS `ticket_follow`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`ticket_id` bigint NOT NULL DEFAULT '0' COMMENT 'TicketId',
`from` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'From',
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Type: 1 text, 2 image',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Content',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `traffic_log`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`server_id` bigint NOT NULL COMMENT 'Server ID',
`user_id` bigint NOT NULL COMMENT 'User ID',
`subscribe_id` bigint NOT NULL COMMENT 'Subscription ID',
`download` bigint DEFAULT '0' COMMENT 'Download Traffic',
`upload` bigint DEFAULT '0' COMMENT 'Upload Traffic',
`timestamp` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Traffic Log Time',
PRIMARY KEY (`id`),
KEY `idx_subscribe_id` (`subscribe_id`),
KEY `idx_server_id` (`server_id`),
KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'User Password',
`avatar` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'User Avatar',
`balance` bigint DEFAULT '0' COMMENT 'User Balance',
`telegram` bigint DEFAULT NULL COMMENT 'Telegram Account',
`refer_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Referral Code',
`referer_id` bigint DEFAULT NULL COMMENT 'Referrer ID',
`commission` bigint DEFAULT '0' COMMENT 'Commission',
`gift_amount` bigint DEFAULT '0' COMMENT 'User Gift Amount',
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Is Account Enabled',
`is_admin` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Admin',
`valid_email` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Email Verified',
`enable_email_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Email Notifications',
`enable_telegram_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Telegram Notifications',
`enable_balance_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Balance Change Notifications',
`enable_login_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Login Notifications',
`enable_subscribe_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Subscription Notifications',
`enable_trade_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Trade Notifications',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
`deleted_at` datetime(3) DEFAULT NULL COMMENT 'Deletion Time',
`is_del` bigint unsigned DEFAULT NULL COMMENT '1: Normal 0: Deleted',
PRIMARY KEY (`id`),
KEY `idx_referer` (`referer_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user_auth_methods`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`auth_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Auth Type 1: apple 2: google 3: github 4: facebook 5: telegram 6: email 7: phone',
`auth_identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Auth Identifier',
`verified` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Verified',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`),
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;
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_device`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`subscribe_id` bigint DEFAULT NULL COMMENT 'Subscribe ID',
`ip` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Ip.',
`Identifier` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Identifier.',
`user_agent` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device User Agent.',
`online` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Online',
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'EnableDeviceNumber',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update 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_subscribe`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT 'User ID',
`order_id` bigint NOT NULL COMMENT 'Order ID',
`subscribe_id` bigint NOT NULL COMMENT 'Subscription ID',
`start_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Subscription Start Time',
`expire_time` datetime(3) DEFAULT NULL COMMENT 'Subscription Expire Time',
`traffic` bigint DEFAULT '0' COMMENT 'Traffic',
`download` bigint DEFAULT '0' COMMENT 'Download Traffic',
`upload` bigint DEFAULT '0' COMMENT 'Upload Traffic',
`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Token',
`uuid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'UUID',
`status` tinyint(1) DEFAULT '0' COMMENT 'Subscription Status: 0: Pending 1: Active 2: Finished 3: Expired 4: Deducted',
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_user_subscribe_token` (`token`),
UNIQUE KEY `uni_user_subscribe_uuid` (`uuid`),
KEY `idx_user_id` (`user_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_subscribe_id` (`subscribe_id`),
KEY `idx_token` (`token`),
KEY `idx_uuid` (`uuid`)
) 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 `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',
`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',
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_name` (`name`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,21 @@
-- 000002_init_data.down.sql
SET
FOREIGN_KEY_CHECKS = 0;
DELETE
FROM `auth_method`
WHERE `id` IN (1, 2, 3, 4, 5, 6, 7, 8);
DELETE
FROM `payment`
WHERE `id` = -1;
DELETE
FROM `subscribe_type`
WHERE `id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
DELETE
FROM `system`
WHERE `id` IN
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41);
SET
FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,127 @@
-- 000002_init_data.up.sql
SET FOREIGN_KEY_CHECKS = 0;
-- auth_method
INSERT IGNORE INTO `auth_method` (`id`, `method`, `config`, `enabled`, `created_at`, `updated_at`)
VALUES (1, 'email',
'{"platform":"smtp","platform_config":{"host":"","port":0,"user":"","pass":"","from":"","ssl":false},"enable_verify":false,"enable_notify":false,"enable_domain_suffix":false,"domain_suffix_list":"","verify_email_template":"","expiration_email_template":"","maintenance_email_template":"","traffic_exceed_email_template":""}',
1, '2025-04-22 14:25:16.642', '2025-04-22 14:25:16.642'),
(2, 'mobile',
'{"platform":"AlibabaCloud","platform_config":{"access":"","secret":"","sign_name":"","endpoint":"","template_code":""},"enable_whitelist":false,"whitelist":[]}',
0, '2025-04-22 14:25:16.642', '2025-04-22 14:25:16.642'),
(3, 'apple', '{"team_id":"","key_id":"","client_id":"","client_secret":"","redirect_url":""}', 0,
'2025-04-22 14:25:16.642', '2025-04-22 14:25:16.642'),
(4, 'google', '{"client_id":"","client_secret":"","redirect_url":""}', 0, '2025-04-22 14:25:16.642',
'2025-04-22 14:25:16.642'),
(5, 'github', '{"client_id":"","client_secret":"","redirect_url":""}', 0, '2025-04-22 14:25:16.642',
'2025-04-22 14:25:16.642'),
(6, 'facebook', '{"client_id":"","client_secret":"","redirect_url":""}', 0, '2025-04-22 14:25:16.642',
'2025-04-22 14:25:16.642'),
(7, 'telegram', '{"bot_token":"","enable_notify":false,"webhook_domain":""}', 0, '2025-04-22 14:25:16.642',
'2025-04-22 14:25:16.642'),
(8, 'device', '{"show_ads":false,"only_real_device":false,"enable_security":false,"security_secret":""}', 0,
'2025-04-22 14:25:16.642', '2025-04-22 14:25:16.642');
-- payment
INSERT IGNORE INTO `payment` (`id`, `name`, `platform`, `description`, `icon`, `domain`, `config`, `fee_mode`,
`fee_percent`, `fee_amount`, `enable`, `token`)
VALUES (-1, 'Balance', 'balance', '', '', '', '', 0, 0, 0, 1, '');
-- subscribe_type
INSERT IGNORE INTO `subscribe_type` (`id`, `name`, `mark`, `created_at`, `updated_at`)
VALUES (1, 'Clash', 'Clash', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(2, 'Hiddify', 'Hiddify', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(3, 'Loon', 'Loon', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(4, 'NekoBox', 'NekoBox', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(5, 'NekoRay', 'NekoRay', '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'),
(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'),
(13, 'V2rayN', 'V2rayN', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648'),
(14, 'V2rayNg', 'V2rayNg', '2025-04-22 14:25:16.648', '2025-04-22 14:25:16.648');
-- system
INSERT IGNORE INTO `system` (`id`, `category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`)
VALUES (1, 'site', 'SiteLogo', '/favicon.svg', 'string', 'Site Logo', '2025-04-22 14:25:16.637',
'2025-04-22 14:25:16.637'),
(2, 'site', 'SiteName', 'Perfect Panel', 'string', 'Site Name', '2025-04-22 14:25:16.637',
'2025-04-22 14:25:16.637'),
(3, 'site', 'SiteDesc',
'PPanel is a pure, professional, and perfect open-source proxy panel tool, designed to be your ideal choice for learning and practical use.',
'string', 'Site Description', '2025-04-22 14:25:16.637', '2025-04-22 14:25:16.637'),
(4, 'site', 'Host', '', 'string', 'Site Host', '2025-04-22 14:25:16.637', '2025-04-22 14:25:16.637'),
(5, 'site', 'Keywords', 'Perfect Panel,PPanel', 'string', 'Site Keywords', '2025-04-22 14:25:16.637',
'2025-04-22 14:25:16.637'),
(6, 'site', 'CustomHTML', '', 'string', 'Custom HTML', '2025-04-22 14:25:16.637', '2025-04-22 14:25:16.637'),
(7, 'tos', 'TosContent', 'Welcome to use Perfect Panel', 'string', 'Terms of Service', '2025-04-22 14:25:16.637',
'2025-04-22 14:25:16.637'),
(8, 'tos', 'PrivacyPolicy', '', 'string', 'PrivacyPolicy', '2025-04-22 14:25:16.637', '2025-04-22 14:25:16.637'),
(9, 'ad', 'WebAD', 'false', 'bool', 'Display ad on the web', '2025-04-22 14:25:16.637',
'2025-04-22 14:25:16.637'),
(10, 'subscribe', 'SingleModel', 'false', 'bool', '是否单订阅模式', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(11, 'subscribe', 'SubscribePath', '/api/subscribe', 'string', '订阅路径', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(12, 'subscribe', 'SubscribeDomain', '', 'string', '订阅域名', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(13, 'subscribe', 'PanDomain', 'false', 'bool', '是否使用泛域名', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(14, 'verify', 'TurnstileSiteKey', '', 'string', 'TurnstileSiteKey', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(15, 'verify', 'TurnstileSecret', '', 'string', 'TurnstileSecret', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(16, 'verify', 'EnableLoginVerify', 'false', 'bool', 'is enable login verify', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(17, 'verify', 'EnableRegisterVerify', 'false', 'bool', 'is enable register verify', '2025-04-22 14:25:16.639',
'2025-04-22 14:25:16.639'),
(18, 'verify', 'EnableResetPasswordVerify', 'false', 'bool', 'is enable reset password verify',
'2025-04-22 14:25:16.639', '2025-04-22 14:25:16.639'),
(19, 'server', 'NodeSecret', '12345678', 'string', 'node secret', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(20, 'server', 'NodePullInterval', '10', 'int', 'node pull interval', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(21, 'server', 'NodePushInterval', '60', 'int', 'node push interval', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(22, 'server', 'NodeMultiplierConfig', '[]', 'string', 'node multiplier config', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(23, 'invite', 'ForcedInvite', 'false', 'bool', 'Forced invite', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(24, 'invite', 'ReferralPercentage', '20', 'int', 'Referral percentage', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(25, 'invite', 'OnlyFirstPurchase', 'false', 'bool', 'Only first purchase', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(26, 'register', 'StopRegister', 'false', 'bool', 'is stop register', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(27, 'register', 'EnableTrial', 'false', 'bool', 'is enable trial', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(28, 'register', 'TrialSubscribe', '', 'int', 'Trial subscription', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(29, 'register', 'TrialTime', '24', 'int', 'Trial time', '2025-04-22 14:25:16.640', '2025-04-22 14:25:16.640'),
(30, 'register', 'TrialTimeUnit', 'Hour', 'string', 'Trial time unit', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(31, 'register', 'EnableIpRegisterLimit', 'false', 'bool', 'is enable IP register limit',
'2025-04-22 14:25:16.640', '2025-04-22 14:25:16.640'),
(32, 'register', 'IpRegisterLimit', '3', 'int', 'IP Register Limit', '2025-04-22 14:25:16.640',
'2025-04-22 14:25:16.640'),
(33, 'register', 'IpRegisterLimitDuration', '64', 'int', 'IP Register Limit Duration (minutes)',
'2025-04-22 14:25:16.640', '2025-04-22 14:25:16.640'),
(34, 'currency', 'Currency', 'USD', 'string', 'Currency', '2025-04-22 14:25:16.641', '2025-04-22 14:25:16.641'),
(35, 'currency', 'CurrencySymbol', '$', 'string', 'Currency Symbol', '2025-04-22 14:25:16.641',
'2025-04-22 14:25:16.641'),
(36, 'currency', 'CurrencyUnit', 'USD', 'string', 'Currency Unit', '2025-04-22 14:25:16.641',
'2025-04-22 14:25:16.641'),
(37, 'currency', 'AccessKey', '', 'string', 'Exchangerate Access Key', '2025-04-22 14:25:16.641',
'2025-04-22 14:25:16.641'),
(38, 'verify_code', 'VerifyCodeExpireTime', '300', 'int', 'Verify code expire time', '2025-04-22 14:25:16.641',
'2025-04-22 14:25:16.641'),
(39, 'verify_code', 'VerifyCodeLimit', '15', 'int', 'limits of verify code', '2025-04-22 14:25:16.641',
'2025-04-22 14:25:16.641'),
(40, 'verify_code', 'VerifyCodeInterval', '60', 'int', 'Interval of verify code', '2025-04-22 14:25:16.641',
'2025-04-22 14:25:16.641'),
(41, 'system', 'Version', '0.2.0(02002)', 'string', 'System Version', '2025-04-22 14:25:16.642',
'2025-04-22 14:25:16.642');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,72 @@
-- migrations/02003_update_payment.down.sql
-- Purpose: Revert updates to payment and order tables
-- Author: PPanel Team, 2025-04-21
SET FOREIGN_KEY_CHECKS = 0;
-- Drop payment_id column from order table (if exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'order'
AND COLUMN_NAME = 'payment_id');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `order` DROP COLUMN `payment_id`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Drop platform column from payment table (if exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'platform');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `payment` DROP COLUMN `platform`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Drop description column from payment table (if exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'description');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `payment` DROP COLUMN `description`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Drop token column from payment table (if exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'token');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `payment` DROP COLUMN `token`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Optionally restore mark column (if needed, adjust definition as per original schema)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'mark');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `payment` ADD COLUMN `mark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT \'Payment Mark\' AFTER `name`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,72 @@
-- 2025-04-22 16:16:00
-- Purpose: Update payment table
-- Author: PPanel Team, 2025-04-21
SET FOREIGN_KEY_CHECKS = 0;
-- Alter the order table to add a payment_id column (if not exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'order'
AND COLUMN_NAME = 'payment_id');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `order` ADD COLUMN `payment_id` bigint NOT NULL DEFAULT \'-1\' COMMENT \'Payment Id\' AFTER `commission`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Alter the payment table to add a platform column (if not exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'platform');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `payment` ADD COLUMN `platform` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT \'Payment Platform\' AFTER `name`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Drop the mark column from the payment table (only if exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'mark');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `payment` DROP COLUMN `mark`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Alter the payment table to add a description column (if not exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'description');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `payment` ADD COLUMN `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT \'Payment Description\' AFTER `platform`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Alter the payment table to add a token column (if not exists)
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'payment'
AND COLUMN_NAME = 'token');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `payment` ADD COLUMN `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT \'Payment Token\' AFTER `description`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,4 @@
-- migrations/02003_rebuild_rule.up.sql
-- Purpose: Back rebuilding server rule table
-- Author: PPanel Team, 2025-04-21
DROP TABLE IF EXISTS server_rule_group;

View File

@ -0,0 +1,22 @@
-- migrations/02003_rebuild_rule.up.sql
-- Purpose: rebuilding server rule table
-- Author: PPanel Team, 2025-04-21
DROP TABLE IF EXISTS `server_rule_group`;
CREATE TABLE `server_rule_group`
(
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Rule Group Name',
`icon` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Rule Group Icon',
`tags` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Selected Node Tags',
`rules` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Rules',
`enable` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Rule Group Enable',
`created_at` DATETIME(3) COMMENT 'Creation Time',
`updated_at` DATETIME(3) COMMENT 'Update Time',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_server_rule_group_name` (`name`),
INDEX `idx_enable` (`enable`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;

View File

@ -0,0 +1,52 @@
-- migrations/02004_create_user_device_online_record.down.sql
-- Purpose: Drop user device online record table
-- Author: PPanel Team, 2025-04-22
DROP TABLE IF EXISTS `user_device_online_record`;
-- User subscribe table migration for removing finished_at column
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'user_subscribe'
AND COLUMN_NAME = 'finished_at');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `user_subscribe` DROP COLUMN `finished_at`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Application config table migration for removing invitation_link column
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'application_config'
AND COLUMN_NAME = 'invitation_link');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `application_config` DROP COLUMN `invitation_link`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Application config table migration for removing kr_website_id column
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'application_config'
AND COLUMN_NAME = 'kr_website_id');
SET @sql = IF(@column_exists > 0,
'ALTER TABLE `application_config` DROP COLUMN `kr_website_id`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

View File

@ -0,0 +1,69 @@
-- migrations/02005_create_user_device_online_record.up.sql
-- Purpose: Create table for tracking user device online records
-- Author: PPanel Team, 2025-04-22
CREATE TABLE IF NOT EXISTS `user_device_online_record`
(
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_id` BIGINT NOT NULL COMMENT 'User ID',
`identifier` VARCHAR(255) NOT NULL COMMENT 'Device Identifier',
`online_time` DATETIME COMMENT 'Online Time',
`offline_time` DATETIME COMMENT 'Offline Time',
`online_seconds` BIGINT COMMENT 'Offline Seconds',
`duration_days` BIGINT COMMENT 'Duration Days',
`created_at` DATETIME COMMENT 'Creation Time'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci;
-- User subscribe table migration for adding finished_at column
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'user_subscribe'
AND COLUMN_NAME = 'finished_at');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `user_subscribe` ADD COLUMN `finished_at` DATETIME NULL COMMENT ''Subscribe Finished Time'' AFTER `expire_time`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Application config table migration for adding Link column
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'application_config'
AND COLUMN_NAME = 'invitation_link');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `application_config` ADD COLUMN `invitation_link` TEXT NULL DEFAULT NULL COMMENT ''Invitation Link'' AFTER `startup_picture_skip_time`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Application config table migration for adding kr_website_id column
SET @column_exists = (SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'application_config'
AND COLUMN_NAME = 'kr_website_id');
SET @sql = IF(@column_exists = 0,
'ALTER TABLE `application_config` ADD COLUMN `kr_website_id` VARCHAR(255) NULL DEFAULT NULL COMMENT ''KR Website ID'' AFTER `invitation_link`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

View File

@ -0,0 +1,5 @@
-- migrations/02008_create_user_reset_subscribe_log.down.sql
-- Purpose: Drop user_reset_subscribe_log table
-- Author: PPanel Team, 2025-04-22
DROP TABLE IF EXISTS `user_reset_subscribe_log`;

View File

@ -0,0 +1,17 @@
-- migrations/02008_create_user_reset_subscribe_log.up.sql
-- Purpose: Create user_reset_subscribe_log table
-- Author: PPanel Team, 2025-04-22
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;

View File

@ -0,0 +1,42 @@
package migrate
import (
"time"
"github.com/perfect-panel/ppanel-server/internal/model/user"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/uuidx"
"gorm.io/gorm"
)
// CreateAdminUser create admin user
func CreateAdminUser(email, password string, tx *gorm.DB) error {
enable := true
return tx.Transaction(func(tx *gorm.DB) error {
// Prevent duplicate creation
if tx.Model(&user.User{}).Find(&user.User{}).RowsAffected != 0 {
logger.Info("User already exists, skip creating administrator account")
return nil
}
u := user.User{
Password: tool.EncodePassWord(password),
IsAdmin: &enable,
ReferCode: uuidx.UserInviteCode(time.Now().Unix()),
}
if err := tx.Model(&user.User{}).Save(&u).Error; err != nil {
return err
}
method := user.AuthMethods{
UserId: u.Id,
AuthType: "email",
AuthIdentifier: email,
Verified: true,
}
if err := tx.Model(&user.AuthMethods{}).Save(&method).Error; err != nil {
return err
}
return nil
})
}

View File

@ -0,0 +1 @@
package migrate

View File

@ -0,0 +1,29 @@
package migrate
import (
"embed"
"fmt"
"github.com/golang-migrate/migrate/v4"
_ "github.com/golang-migrate/migrate/v4/database/mysql"
"github.com/golang-migrate/migrate/v4/source/iofs"
"github.com/perfect-panel/ppanel-server/pkg/logger"
)
//go:embed database/*.sql
var sqlFiles embed.FS
var NoChange = migrate.ErrNoChange
func Migrate(dsn string) *migrate.Migrate {
d, err := iofs.New(sqlFiles, "database")
if err != nil {
logger.Errorf("[Migrate] iofs.New error: %v", err.Error())
panic(err)
}
client, err := migrate.NewWithSourceInstance("iofs", d, fmt.Sprintf("mysql://%s", dsn))
if err != nil {
logger.Errorf("[Migrate] NewWithSourceInstance error: %v", err.Error())
panic(err)
}
return client
}

View File

@ -0,0 +1,32 @@
package migrate
import (
"testing"
"github.com/perfect-panel/ppanel-server/pkg/orm"
)
func getDSN() string {
cfg := orm.Config{
Addr: "127.0.0.1",
Username: "root",
Password: "mylove520",
Dbname: "vpnboard",
}
mc := orm.Mysql{
Config: cfg,
}
return mc.Dsn()
}
func TestMigrate(t *testing.T) {
t.Skipf("skip test")
m := Migrate(getDSN())
err := m.Migrate(2004)
if err != nil {
t.Errorf("failed to migrate: %v", err)
} else {
t.Log("migrate success")
}
}

101
initialize/migrate/tool.go Normal file
View File

@ -0,0 +1,101 @@
package migrate
import (
"fmt"
"strings"
"gorm.io/gorm"
)
// ExecuteSQLFile 执行嵌入的 SQL 文件,去除注释
func ExecuteSQLFile(tx *gorm.DB, path string) error {
// 读取 SQL 文件内容
sqlContent, err := sqlFiles.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read embedded SQL file: %v", err)
}
// 清除注释内容
cleanedSQL := removeComments(string(sqlContent))
// 将清除注释后的内容按分号分割成多个 SQL 语句
sqlStatements := splitSQLStatements(cleanedSQL)
// 遍历 SQL 语句并执行
for _, stmt := range sqlStatements {
stmt = strings.TrimSpace(stmt)
if stmt == "" {
continue
}
// 执行 SQL 语句
if err := tx.Exec(stmt).Error; err != nil {
return fmt.Errorf("failed to execute SQL statement: %v \nSQL: %s", err.Error(), stmt)
}
}
return nil
}
// removeComments 去除 SQL 代码中的注释
func removeComments(sql string) string {
var result strings.Builder
inSingleLineComment := false
inMultiLineComment := false
length := len(sql)
for i := 0; i < length; i++ {
// 处理 -- 单行注释
if !inMultiLineComment && !inSingleLineComment && i+1 < length && sql[i] == '-' && sql[i+1] == '-' {
inSingleLineComment = true
i++ // 跳过 '-'
continue
}
// 结束单行注释(支持 \r\n 和 \n
if inSingleLineComment && (sql[i] == '\n' || sql[i] == '\r') {
inSingleLineComment = false
}
// 处理 /* 多行注释 */
if !inSingleLineComment && !inMultiLineComment && i+1 < length && sql[i] == '/' && sql[i+1] == '*' {
inMultiLineComment = true
i++ // 跳过 '*'
continue
}
// 结束多行注释
if inMultiLineComment && i+1 < length && sql[i] == '*' && sql[i+1] == '/' {
inMultiLineComment = false
i++ // 跳过 '/'
continue
}
// 不是注释内容时,将字符添加到结果中
if !inSingleLineComment && !inMultiLineComment {
result.WriteByte(sql[i])
}
}
// 去除多余的空白行
lines := strings.Split(result.String(), "\n")
var cleanedLines []string
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed != "" {
cleanedLines = append(cleanedLines, trimmed)
}
}
return strings.Join(cleanedLines, "\n")
}
// splitSQLStatements 更安全地分割 SQL 语句
func splitSQLStatements(sql string) []string {
statements := strings.Split(sql, ";")
var results []string
for _, stmt := range statements {
trimmed := strings.TrimSpace(stmt)
if trimmed != "" {
results = append(results, trimmed)
}
}
return results
}

32
initialize/mobile.go Normal file
View File

@ -0,0 +1,32 @@
package initialize
import (
"context"
"encoding/json"
"fmt"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/model/auth"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
func Mobile(ctx *svc.ServiceContext) {
logger.Debug("Mobile config initialization")
method, err := ctx.AuthModel.FindOneByMethod(context.Background(), "mobile")
if err != nil {
panic(err)
}
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()))
}
tool.DeepCopy(&cfg, mobileConfig)
cfg.Enable = *method.Enabled
value, _ := json.Marshal(mobileConfig.PlatformConfig)
cfg.PlatformConfig = string(value)
ctx.Config.Mobile = cfg
}

1
initialize/mysql.go Normal file
View File

@ -0,0 +1 @@
package initialize

51
initialize/node.go Normal file
View File

@ -0,0 +1,51 @@
package initialize
import (
"context"
"encoding/json"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/model/system"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/nodeMultiplier"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
func Node(ctx *svc.ServiceContext) {
logger.Debug("Node config initialization")
configs, err := ctx.SystemModel.GetNodeConfig(context.Background())
if err != nil {
panic(err)
}
var nodeConfig config.NodeConfig
tool.SystemConfigSliceReflectToStruct(configs, &nodeConfig)
ctx.Config.Node = nodeConfig
// Manager initialization
if ctx.DB.Model(&system.System{}).Where("`key` = ?", "NodeMultiplierConfig").Find(&system.System{}).RowsAffected == 0 {
if err := ctx.DB.Model(&system.System{}).Create(&system.System{
Key: "NodeMultiplierConfig",
Value: "[]",
Type: "string",
Desc: "Node Multiplier Config",
Category: "server",
}).Error; err != nil {
logger.Errorf("Create Node Multiplier Config Error: %s", err.Error())
}
return
}
nodeMultiplierData, err := ctx.SystemModel.FindNodeMultiplierConfig(context.Background())
if err != nil {
logger.Error("Get Node Multiplier Config Error: ", logger.Field("error", err.Error()))
return
}
var periods []nodeMultiplier.TimePeriod
if err := json.Unmarshal([]byte(nodeMultiplierData.Value), &periods); err != nil {
logger.Error("Unmarshal Node Multiplier Config Error: ", logger.Field("error", err.Error()), logger.Field("value", nodeMultiplierData.Value))
}
ctx.NodeMultiplierManager = nodeMultiplier.NewNodeMultiplierManager(periods)
}

11
initialize/oauth.go Normal file
View File

@ -0,0 +1,11 @@
package initialize
import (
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/logger"
)
func OAuth(svc *svc.ServiceContext) {
logger.Debug("OAuth config initialization")
}

23
initialize/register.go Normal file
View File

@ -0,0 +1,23 @@
package initialize
import (
"context"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
func Register(ctx *svc.ServiceContext) {
logger.Debug("Register config initialization")
configs, err := ctx.SystemModel.GetRegisterConfig(context.Background())
if err != nil {
logger.Errorf("[Init Register Config] Get Register Config Error: %s", err.Error())
return
}
var registerConfig config.RegisterConfig
tool.SystemConfigSliceReflectToStruct(configs, &registerConfig)
ctx.Config.Register = registerConfig
}

22
initialize/site.go Normal file
View File

@ -0,0 +1,22 @@
package initialize
import (
"context"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
func Site(ctx *svc.ServiceContext) {
logger.Debug("initialize site config")
configs, err := ctx.SystemModel.GetSiteConfig(context.Background())
if err != nil {
panic(err)
}
var siteConfig config.SiteConfig
tool.SystemConfigSliceReflectToStruct(configs, &siteConfig)
ctx.Config.Site = siteConfig
}

57
initialize/statistics.go Normal file
View File

@ -0,0 +1,57 @@
package initialize
import (
"context"
"time"
"github.com/perfect-panel/ppanel-server/internal/model/cache"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-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("初始化昨天的流量数据到缓存成功")
}

24
initialize/subscribe.go Normal file
View File

@ -0,0 +1,24 @@
package initialize
import (
"context"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
func Subscribe(svc *svc.ServiceContext) {
logger.Debug("Subscribe config initialization")
configs, err := svc.SystemModel.GetSubscribeConfig(context.Background())
if err != nil {
logger.Error("[Init Subscribe Config] Get Subscribe Config Error: ", logger.Field("error", err.Error()))
return
}
var subscribeConfig config.SubscribeConfig
tool.SystemConfigSliceReflectToStruct(configs, &subscribeConfig)
svc.Config.Subscribe = subscribeConfig
}

82
initialize/telegram.go Normal file
View File

@ -0,0 +1,82 @@
package initialize
import (
"context"
"fmt"
"github.com/perfect-panel/ppanel-server/pkg/logger"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/logic/telegram"
"github.com/perfect-panel/ppanel-server/internal/model/auth"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/pkg/tool"
)
func Telegram(svc *svc.ServiceContext) {
method, err := svc.AuthModel.FindOneByMethod(context.Background(), "telegram")
if err != nil {
logger.Errorf("[Init Telegram Config] Get Telegram Config Error: %s", err.Error())
return
}
var tg config.Telegram
tgConfig := new(auth.TelegramAuthConfig)
if err = tgConfig.Unmarshal(method.Config); err != nil {
logger.Errorf("[Init Telegram Config] Unmarshal Telegram Config Error: %s", err.Error())
return
}
if tgConfig.BotToken == "" {
logger.Debug("[Init Telegram Config] Telegram Token is empty")
return
}
bot, err := tgbotapi.NewBotAPI(tg.BotToken)
if err != nil {
logger.Error("[Init Telegram Config] New Bot API Error: ", logger.Field("error", err.Error()))
return
}
if tgConfig.WebHookDomain == "" || svc.Config.Debug {
// set Long Polling mode
updateConfig := tgbotapi.NewUpdate(0)
updateConfig.Timeout = 60
updates := bot.GetUpdatesChan(updateConfig)
go func() {
for update := range updates {
if update.Message != nil {
ctx := context.Background()
l := telegram.NewTelegramLogic(ctx, svc)
l.TelegramLogic(&update)
}
}
}()
} else {
wh, err := tgbotapi.NewWebhook(fmt.Sprintf("%s/v1/telegram/webhook?secret=%s", tgConfig.WebHookDomain, tool.Md5Encode(tgConfig.BotToken, false)))
if err != nil {
logger.Errorf("[Init Telegram Config] New Webhook Error: %s", err.Error())
return
}
_, err = bot.Request(wh)
if err != nil {
logger.Errorf("[Init Telegram Config] Request Webhook Error: %s", err.Error())
return
}
}
user, err := bot.GetMe()
if err != nil {
logger.Error("[Init Telegram Config] Get Bot Info Error: ", logger.Field("error", err.Error()))
return
}
svc.Config.Telegram.BotID = user.ID
svc.Config.Telegram.BotName = user.UserName
svc.Config.Telegram.EnableNotify = tg.EnableNotify
svc.Config.Telegram.WebHookDomain = tg.WebHookDomain
svc.TelegramBot = bot
logger.Info("[Init Telegram Config] Webhook set success")
}

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