init commit
This commit is contained in:
commit
7eee7bcd3b
3
.commitlintrc.json
Normal file
3
.commitlintrc.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
.idea/
|
||||
.vscode/
|
||||
*-dev.yaml
|
||||
*.local.yaml
|
||||
/test/
|
||||
*.log
|
||||
.DS_Store
|
||||
*_test_config.go
|
||||
/build/
|
||||
etc/ppanel.yaml
|
||||
*.p8
|
||||
*.crt
|
||||
*.key
|
||||
node_modules
|
||||
package-lock.json
|
||||
package.json
|
||||
66
.goreleaser.yaml
Normal file
66
.goreleaser.yaml
Normal 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
|
||||
40
CONTRIBUTING.md
Normal file
40
CONTRIBUTING.md
Normal 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
44
CONTRIBUTING_ZH.md
Normal file
@ -0,0 +1,44 @@
|
||||
# Pull Request 提交须知
|
||||
|
||||
为了确保代码库的质量和项目的可维护性,在提交 Pull Request(PR)之前,请务必遵循以下准则:
|
||||
|
||||
## 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
44
Dockerfile
Normal 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
674
LICENSE
Normal 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
79
apis/admin/ads.api
Normal 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)
|
||||
}
|
||||
|
||||
76
apis/admin/announcement.api
Normal file
76
apis/admin/announcement.api
Normal 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
76
apis/admin/auth.api
Normal 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
88
apis/admin/console.api
Normal 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
85
apis/admin/coupon.api
Normal 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
13
apis/admin/device.api
Normal 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
78
apis/admin/document.api
Normal 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
40
apis/admin/log.api
Normal 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
68
apis/admin/order.api
Normal 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
81
apis/admin/payment.api
Normal 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
190
apis/admin/server.api
Normal 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
164
apis/admin/subscribe.api
Normal 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
205
apis/admin/system.api
Normal 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
62
apis/admin/ticket.api
Normal 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
33
apis/admin/tool.api
Normal 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
278
apis/admin/user.api
Normal 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
24
apis/app/announcement.api
Normal 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)
|
||||
}
|
||||
|
||||
105
apis/app/auth.api
Normal file
105
apis/app/auth.api
Normal file
@ -0,0 +1,105 @@
|
||||
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"`
|
||||
VersionReview string `json:"version_review"`
|
||||
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
27
apis/app/document.api
Normal 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
49
apis/app/node.api
Normal 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
58
apis/app/order.api
Normal 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
23
apis/app/payment.api
Normal 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
75
apis/app/subscribe.api
Normal 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
90
apis/app/user.api
Normal 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
23
apis/app/ws.api
Normal 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
166
apis/auth/auth.api
Normal 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
125
apis/common.api
Normal 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
121
apis/node/node.api
Normal 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)
|
||||
}
|
||||
|
||||
24
apis/public/announcement.api
Normal file
24
apis/public/announcement.api
Normal 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
28
apis/public/document.api
Normal 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
51
apis/public/order.api
Normal 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
24
apis/public/payment.api
Normal 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)
|
||||
}
|
||||
|
||||
95
apis/public/portal.api
Normal file
95
apis/public/portal.api
Normal file
@ -0,0 +1,95 @@
|
||||
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"`
|
||||
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
31
apis/public/subscribe.api
Normal 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
70
apis/public/ticket.api
Normal 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
211
apis/public/user.api
Normal 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
26
apis/swagger_admin.api
Normal 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
21
apis/swagger_app.api
Normal 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
14
apis/swagger_common.api
Normal 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
11
apis/swagger_node.api
Normal 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
19
apis/swagger_user.api
Normal 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"
|
||||
)
|
||||
752
apis/types.api
Normal file
752
apis/types.api
Normal file
@ -0,0 +1,752 @@
|
||||
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"`
|
||||
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"`
|
||||
LatitudeCountry string `json:"latitudeCountry"`
|
||||
LongitudeCountry string `json:"longitudeCountry"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
Download int64 `json:"download"`
|
||||
}
|
||||
)
|
||||
|
||||
30
cmd/root.go
Normal file
30
cmd/root.go
Normal 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
155
cmd/run.go
Normal file
@ -0,0 +1,155 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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/constant"
|
||||
"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" + constant.Version)
|
||||
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
16
cmd/version.go
Normal 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)
|
||||
},
|
||||
}
|
||||
88
doc/config-zh.md
Normal file
88
doc/config-zh.md
Normal file
@ -0,0 +1,88 @@
|
||||
### 配置文件说明
|
||||
|
||||
#### 1. 配置文件路径
|
||||
|
||||
配置文件默认路径为:`./etc/ppanel.yaml`,可通过启动参数 `--config` 指定配置文件路径。
|
||||
|
||||
#### 2. 配置文件格式
|
||||
- 配置文件为yaml格式,支持注释,命名为xxx.yaml。
|
||||
|
||||
```yaml
|
||||
# 配置文件示例
|
||||
Host: # 服务监听地址,默认: 0.0.0.0
|
||||
Port: # 服务监听端口,默认: 8080
|
||||
Debug: # 是否开启调试模式,开启后无法使用后台日志功能, 默认: false
|
||||
JwtAuth: # JWT认证配置
|
||||
AccessSecret: # 访问令牌密钥, 默认: 随机生成
|
||||
AccessExpire: # 访问令牌过期时间,单位秒, 默认: 604800
|
||||
Logger: # 日志配置
|
||||
FilePath: # 日志文件路径, 默认: ./ppanel.log
|
||||
MaxSize: # 日志文件最大大小,单位MB, 默认: 50
|
||||
MaxBackup: # 日志文件最大备份数, 默认: 3
|
||||
MaxAge: # 日志文件最大保存时间,单位天, 默认: 30
|
||||
Compress: # 是否压缩日志文件, 默认: true
|
||||
Level: # 日志级别, 默认: info, 可选: debug, info, warn, error, panic, panic, fatal
|
||||
MySQL:
|
||||
Addr: # MySQL地址, 必填
|
||||
Username: # MySQL用户名, 必填
|
||||
Password: # MySQL密码, 必填
|
||||
Dbname: # MySQL数据库名, 必填
|
||||
Config: # Mysql配置默认值 charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||
MaxIdleConns: # 最大空闲连接数, 默认: 10
|
||||
MaxOpenConns: # 最大打开连接数, 默认: 100
|
||||
LogMode: # 日志级别, 默认: info, 可选: debug, error, warn, info
|
||||
LogZap: # 是否使用zap日志记录sql, 默认: true
|
||||
SlowThreshold: # 慢查询阈值,单位毫秒, 默认: 1000
|
||||
Redis:
|
||||
Host: # Redis地址, 默认:localhost:6379
|
||||
Pass: # Redis密码, 默认: ""
|
||||
DB: # Redis数据库, 默认: 0
|
||||
|
||||
Administer:
|
||||
Email: # 后台登录邮箱, 默认: admin@ppanel.dev
|
||||
Password: # 后台登录密码, 默认: password
|
||||
|
||||
```
|
||||
|
||||
#### 3. 配置文件说明
|
||||
|
||||
- `Host`: 服务监听地址,默认: **0.0.0.0**
|
||||
- `Port`: 服务监听端口,默认: **8080**
|
||||
- `Debug`: 是否开启调试模式,开启后无法使用后台日志功能, 默认: **false**
|
||||
- `JwtAuth`: JWT认证配置
|
||||
- `AccessSecret`: 访问令牌密钥, 默认: **随机生成**
|
||||
- `AccessExpire`: 访问令牌过期时间,单位秒, 默认: **604800**
|
||||
- `Logger`: 日志配置
|
||||
- `FilePath`: 日志文件路径, 默认: **./ppanel.log**
|
||||
- `MaxSize`: 日志文件最大大小,单位MB, 默认: **50**
|
||||
- `MaxBackup`: 日志文件最大备份数, 默认: **3**
|
||||
- `MaxAge`: 日志文件最大保存时间,单位天, 默认: **30**
|
||||
- `Compress`: 是否压缩日志文件, 默认: **true**
|
||||
- `Level`: 日志级别, 默认: **info**, 可选: **debug, info, warn, error, panic, panic, fatal**
|
||||
- `MySQL`: MySQL配置
|
||||
- `Addr`: MySQL地址, 必填
|
||||
- `Username`: MySQL用户名, 必填
|
||||
- `Password`: MySQL密码, 必填
|
||||
- `Dbname`: MySQL数据库名, 必填
|
||||
- `Config`: Mysql配置默认值 charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||
- `MaxIdleConns`: 最大空闲连接数, 默认: **10**
|
||||
- `MaxOpenConns`: 最大打开连接数, 默认: **100**
|
||||
- `LogMode`: 日志级别, 默认: **info**, 可选: **debug, error, warn, info**
|
||||
- `LogZap`: 是否使用zap日志记录sql, 默认: **true**
|
||||
- `SlowThreshold`: 慢查询阈值,单位毫秒, 默认: **1000**
|
||||
- `Redis`: Redis配置
|
||||
- `Host`: Redis地址, 默认: **localhost:6379**
|
||||
- `Pass`: Redis密码, 默认: **""**
|
||||
- `DB`: Redis数据库, 默认: **0**
|
||||
- `Administer`: 后台登录配置
|
||||
- `Email`: 后台登录邮箱, 默认: **admin@ppanel.dev**
|
||||
- `Password`: 后台登录密码, 默认: **password**
|
||||
|
||||
#### 4. 环境变量
|
||||
|
||||
支持的环境变量如下:
|
||||
|
||||
| 环境变量 | 配置项 | 示例 |
|
||||
|--------------|---------|:-------------------------------------------|
|
||||
| PPANEL_DB | MySQL配置 | root:password@tcp(localhost:3306)/vpnboard |
|
||||
| PPANEL_REDIS | Redis配置 | redis://localhost:6379" |
|
||||
116
doc/config.md
Normal file
116
doc/config.md
Normal file
@ -0,0 +1,116 @@
|
||||
### Configuration File Instructions
|
||||
#### Configuration File Path
|
||||
|
||||
The default configuration file path is ./etc/ppanel.yaml. You can specify a custom path using the --config startup parameter.
|
||||
|
||||
#### Configuration File Format
|
||||
|
||||
The configuration file uses the YAML format, supports comments, and should be named xxx.yaml.
|
||||
|
||||
```yaml
|
||||
# Sample Configuration File
|
||||
Host: # Service listening address, default: 0.0.0.0
|
||||
Port: # Service listening port, default: 8080
|
||||
Debug: # Enable debug mode; disables backend logging when enabled, default: false
|
||||
JwtAuth: # JWT authentication settings
|
||||
AccessSecret: # Access token secret, default: randomly generated
|
||||
AccessExpire: # Access token expiration time in seconds, default: 604800
|
||||
Logger: # Logging configuration
|
||||
FilePath: # Log file path, default: ./ppanel.log
|
||||
MaxSize: # Maximum log file size in MB, default: 50
|
||||
MaxBackup: # Maximum number of log file backups, default: 3
|
||||
MaxAge: # Maximum log file retention time in days, default: 30
|
||||
Compress: # Whether to compress log files, default: true
|
||||
Level: # Logging level, default: info; options: debug, info, warn, error, panic, fatal
|
||||
MySQL:
|
||||
Addr: # MySQL address, required
|
||||
Username: # MySQL username, required
|
||||
Password: # MySQL password, required
|
||||
Dbname: # MySQL database name, required
|
||||
Config: # MySQL configuration, default: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||
MaxIdleConns: # Maximum idle connections, default: 10
|
||||
MaxOpenConns: # Maximum open connections, default: 100
|
||||
LogMode: # Log level, default: info; options: debug, error, warn, info
|
||||
LogZap: # Whether to use zap for SQL logging, default: true
|
||||
SlowThreshold: # Slow query threshold in milliseconds, default: 1000
|
||||
Redis:
|
||||
Host: # Redis address, default: localhost:6379
|
||||
Pass: # Redis password, default: ""
|
||||
DB: # Redis database, default: 0
|
||||
|
||||
Administer:
|
||||
Email: # Admin login email, default: admin@ppanel.dev
|
||||
Password: # Admin login password, default: password
|
||||
```
|
||||
|
||||
#### 3.Configuration Descriptions
|
||||
|
||||
- Host: Service listening address, default: 0.0.0.0
|
||||
|
||||
- Port: Service listening port, default: 8080
|
||||
- Debug: Enable debug mode; disables backend logging when enabled, default: false
|
||||
|
||||
- JwtAuth: JWT authentication settings
|
||||
|
||||
- AccessSecret: Access token secret, default: randomly generated
|
||||
|
||||
- AccessExpire: Access token expiration time in seconds, default: 604800
|
||||
|
||||
- Logger: Logging configuration
|
||||
|
||||
- FilePath: Log file path, default: ./ppanel.log
|
||||
|
||||
- MaxSize: Maximum log file size in MB, default: 50
|
||||
|
||||
- MaxBackup: Maximum number of log file backups, default: 3
|
||||
|
||||
- MaxAge: Maximum log file retention time in days, default: 30
|
||||
|
||||
- Compress: Whether to compress log files, default: true
|
||||
|
||||
- Level: Logging level, default: info; options: debug, info, warn, error, panic, fatal
|
||||
|
||||
- MySQL: MySQL configuration
|
||||
|
||||
- Addr: MySQL address, required
|
||||
|
||||
- Username: MySQL username, required
|
||||
|
||||
- Password: MySQL password, required
|
||||
|
||||
- Dbname: MySQL database name, required
|
||||
|
||||
- Config: MySQL configuration, default: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||
|
||||
- MaxIdleConns: Maximum idle connections, default: 10
|
||||
|
||||
- MaxOpenConns: Maximum open connections, default: 100
|
||||
|
||||
- LogMode: Log level, default: info; options: debug, error, warn, info
|
||||
|
||||
- LogZap: Whether to use zap for SQL logging, default: true
|
||||
|
||||
- SlowThreshold: Slow query threshold in milliseconds, default: 1000
|
||||
|
||||
- Redis: Redis configuration
|
||||
|
||||
- Host: Redis address, default: localhost:6379
|
||||
|
||||
- Pass: Redis password, default: ""
|
||||
|
||||
- DB: Redis database, default: 0
|
||||
|
||||
- Administer: Admin login configuration
|
||||
|
||||
- Email: Admin login email, default: admin@ppanel.dev
|
||||
|
||||
- Password: Admin login password, default: password
|
||||
|
||||
#### 4. Environment Variables
|
||||
|
||||
Supported environment variables are as follows:
|
||||
|
||||
| Environment Variable | Configuration | Example |
|
||||
|-----------------------|---------------|:-------------------------------------------|
|
||||
| PPANEL_DB | MySQL config | root:password@tcp(localhost:3306)/vpnboard |
|
||||
| PPANEL_REDIS | Redis config | redis://localhost:6379" |
|
||||
133
doc/install-zh.md
Normal file
133
doc/install-zh.md
Normal 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
142
doc/install.md
Normal 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
13
docker-compose.yml
Normal 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
Normal file
BIN
generate/gopure-amd64.exe
Normal file
Binary file not shown.
BIN
generate/gopure-arm64.exe
Normal file
BIN
generate/gopure-arm64.exe
Normal file
Binary file not shown.
BIN
generate/gopure-darwin-amd64
Normal file
BIN
generate/gopure-darwin-amd64
Normal file
Binary file not shown.
BIN
generate/gopure-darwin-arm64
Normal file
BIN
generate/gopure-darwin-arm64
Normal file
Binary file not shown.
BIN
generate/gopure-linux-amd64
Normal file
BIN
generate/gopure-linux-amd64
Normal file
Binary file not shown.
BIN
generate/gopure-linux-arm64
Normal file
BIN
generate/gopure-linux-arm64
Normal file
Binary file not shown.
135
go.mod
Normal file
135
go.mod
Normal file
@ -0,0 +1,135 @@
|
||||
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.24.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.24.0
|
||||
go.opentelemetry.io/otel/trace v1.24.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/spaolacci/murmur3 v1.1.0
|
||||
google.golang.org/grpc v1.61.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.1.2 // 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.19.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
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.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.1.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-20240102182953-50ed04b92917 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
505
go.sum
Normal file
505
go.sum
Normal file
@ -0,0 +1,505 @@
|
||||
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/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/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/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/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/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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
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.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
||||
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/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.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
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/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/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/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
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.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
|
||||
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.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
|
||||
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 v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
||||
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.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
|
||||
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
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=
|
||||
277
initialize/config.go
Normal file
277
initialize/config.go
Normal file
@ -0,0 +1,277 @@
|
||||
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
|
||||
}
|
||||
|
||||
// init
|
||||
if err := initMysql(db, request.AdminEmail, request.AdminPassword); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "MySQL initialization 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,
|
||||
})
|
||||
}
|
||||
|
||||
func initMysql(tx *gorm.DB, email, password string) error {
|
||||
tables, err := tx.Migrator().GetTables()
|
||||
if err != nil {
|
||||
return fmt.Errorf("database table validation failed: %w", err)
|
||||
}
|
||||
if len(tables) > 0 {
|
||||
return errors.New("the database contains existing data. Please clear it before proceeding with the installation")
|
||||
}
|
||||
if err := migrate.InitPPanelSQL(tx); err != nil {
|
||||
return fmt.Errorf("failed to initialize database: %w", err)
|
||||
}
|
||||
if err := migrate.CreateAdminUser(email, password, tx); err != nil {
|
||||
return fmt.Errorf("failed to create admin user: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
33
initialize/email.go
Normal file
33
initialize/email.go
Normal 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
22
initialize/init.go
Normal file
@ -0,0 +1,22 @@
|
||||
package initialize
|
||||
|
||||
import "github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
|
||||
func StartInitSystemConfig(svc *svc.ServiceContext) {
|
||||
// Initialize the system configuration
|
||||
Mysql(svc)
|
||||
VerifyVersion(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
23
initialize/invite.go
Normal 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
|
||||
}
|
||||
54
initialize/migrate/database/01200-patch.sql
Normal file
54
initialize/migrate/database/01200-patch.sql
Normal file
@ -0,0 +1,54 @@
|
||||
-- 先检查 `email` 列是否存在,再删除
|
||||
SELECT COUNT(*) INTO @col_exists FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'user' AND column_name = 'email';
|
||||
|
||||
SET @sql = IF(@col_exists > 0, 'ALTER TABLE `user` DROP COLUMN `email`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 先检查 `telephone` 列是否存在,再删除
|
||||
SELECT COUNT(*) INTO @col_exists FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'user' AND column_name = 'telephone';
|
||||
|
||||
SET @sql = IF(@col_exists > 0, 'ALTER TABLE `user` DROP COLUMN `telephone`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 先检查 `telephone_area_code` 列是否存在,再删除
|
||||
SELECT COUNT(*) INTO @col_exists FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'user' AND column_name = 'telephone_area_code';
|
||||
|
||||
SET @sql = IF(@col_exists > 0, 'ALTER TABLE `user` DROP COLUMN `telephone_area_code`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
|
||||
-- 先检查 `idx_email` 索引是否存在,再删除
|
||||
SELECT COUNT(*) INTO @idx_exists FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE() AND table_name = 'user' AND index_name = 'idx_email';
|
||||
|
||||
SET @sql = IF(@idx_exists > 0, 'ALTER TABLE `user` DROP INDEX `idx_email`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 先检查 `idx_telephone` 索引是否存在,再删除
|
||||
SELECT COUNT(*) INTO @idx_exists FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE() AND table_name = 'user' AND index_name = 'idx_telephone';
|
||||
|
||||
SET @sql = IF(@idx_exists > 0, 'ALTER TABLE `user` DROP INDEX `idx_telephone`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 先检查 `idx_telephone_area_code` 索引是否存在,再删除
|
||||
SELECT COUNT(*) INTO @idx_exists FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE() AND table_name = 'user' AND index_name = 'idx_telephone_area_code';
|
||||
|
||||
SET @sql = IF(@idx_exists > 0, 'ALTER TABLE `user` DROP INDEX `idx_telephone_area_code`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
118
initialize/migrate/database/01201-patch.sql
Normal file
118
initialize/migrate/database/01201-patch.sql
Normal file
@ -0,0 +1,118 @@
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- 检查表是否存在,如果存在则跳过创建
|
||||
CREATE TABLE IF NOT EXISTS `oauth_config` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`platform` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'platform',
|
||||
`config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'OAuth Configuration',
|
||||
`redirect` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Redirect URL',
|
||||
`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_oauth_config_platform` (`platform`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- 插入记录时忽略重复记录
|
||||
BEGIN;
|
||||
INSERT IGNORE INTO `oauth_config` (`id`, `platform`, `config`, `redirect`, `enabled`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'apple', '{\"team_id\":\"\",\"key_id\":\"\",\"client_id\":\"\",\"client_secret\":\"\"}', '', 0, '2025-01-26 20:11:15.292', '2025-01-26 20:11:15.292'),
|
||||
(2, 'google', '{\"client_id\":\"\",\"client_secret\":\"\"}', '', 0, '2025-01-26 20:11:15.292', '2025-01-26 20:11:15.292'),
|
||||
(3, 'github', '{\"client_id\":\"\",\"client_secret\":\"\"}', '', 0, '2025-01-26 20:11:15.292', '2025-01-26 20:11:15.292'),
|
||||
(4, 'facebook', '{\"client_id\":\"\",\"client_secret\":\"\"}', '', 0, '2025-01-26 20:11:15.292', '2025-01-26 20:11:15.292'),
|
||||
(5, 'telegram', '{\"bot\":\"\",\"bot_token\":\"\"}', '', 0, '2025-01-26 20:11:15.292', '2025-01-26 20:11:15.292');
|
||||
COMMIT;
|
||||
|
||||
-- 检测更新设置表
|
||||
BEGIN;
|
||||
INSERT IGNORE INTO `system` (`category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`) VALUES
|
||||
('sms', 'SmsEnabled', 'false', 'bool', '是否启用短信功能', NOW(), NOW()),
|
||||
('sms', 'SmsKey', 'your-key', 'string', '短信服务用户名或Key',NOW(), NOW()),
|
||||
('sms', 'SmsSecret', 'your-secret', 'string', '短信服务密码或Secret', NOW(), NOW()),
|
||||
('sms', 'SmsSign', 'your-sign', 'string', '短信签名', NOW(), NOW()),
|
||||
('sms', 'SmsTemplate', 'your-template', 'string', '短信模板ID', NOW(), NOW()),
|
||||
('sms', 'SmsRegion', 'cn-hangzhou', 'string', '短信服务所在区域(适用于阿里云)', NOW(), NOW()),
|
||||
('sms', 'SmsTemplate', '您的验证码是{{.Code}},请在5分钟内使用。', 'string', '自定义短信模板', NOW(), NOW()),
|
||||
('sms', 'SmsTemplateCode', 'SMS_12345678', 'string', '阿里云国内短信模板代码',NOW(),NOW()),
|
||||
('sms', 'SmsTemplateParam', '{\"code\":{{.Code}}}', 'string', '短信模板参数', NOW(), NOW()),
|
||||
('sms', 'SmsPlatform', 'smsbao', 'string', '当前使用的短信平台', NOW(), NOW()),
|
||||
('sms', 'SmsLimit', '10', 'int64', '可以发送的短信最大数量', NOW(), NOW()),
|
||||
('sms', 'SmsInterval', '60', 'int64', '发送短信的时间间隔(单位:秒)',NOW(), NOW()),
|
||||
('sms', 'SmsExpireTime', '300', 'int64', '短信验证码的过期时间(单位:秒)',NOW(), NOW()),
|
||||
('email', 'EmailEnabled', 'true', 'bool', '启用邮箱登陆',NOW(), NOW()),
|
||||
('email', 'EmailSmtpHost', '', 'string', '邮箱服务器地址', NOW(), NOW()),
|
||||
('email', 'EmailSmtpPort', '465', 'int', '邮箱服务器端口',NOW(), NOW()),
|
||||
('email', 'EmailSmtpUser', 'domain@f1shyu.com', 'string', '邮箱服务器用户名', NOW(), NOW()),
|
||||
('email', 'EmailSmtpPass', 'password', 'string', '邮箱服务器密码', NOW(), NOW()),
|
||||
('email', 'EmailSmtpFrom', 'domain@f1shyu.com', 'string', '发送邮件的邮箱',NOW(), NOW()),
|
||||
('email', 'EmailSmtpSSL', 'true', 'bool', '邮箱服务器加密方式',NOW(), NOW()),
|
||||
('email', 'EmailTemplate', '%s', 'string', '邮件模板',NOW(), NOW()),
|
||||
('email', 'VerifyEmailTemplate', '', 'string', 'Verify Email template',NOW(), NOW()),
|
||||
('email', 'MaintenanceEmailTemplate', '', 'string', 'Maintenance Email template',NOW(), NOW()),
|
||||
('email', 'ExpirationEmailTemplate', '', 'string', 'Expiration Email template', NOW(), NOW()),
|
||||
('email', 'EmailEnableVerify', 'true', 'bool', '是否开启邮箱验证', NOW(), NOW()),
|
||||
('email', 'EmailEnableDomainSuffix', 'false', 'bool', '是否开启邮箱域名后缀限制',NOW(), NOW()),
|
||||
('email', 'EmailDomainSuffixList', 'qq.com', 'string', '邮箱域名后缀列表',NOW(), NOW());
|
||||
COMMIT;
|
||||
|
||||
-- User Device
|
||||
CREATE TABLE IF NOT EXISTS `user_device` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint NOT NULL COMMENT 'User ID',
|
||||
`device_number` varchar(191) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Number.',
|
||||
`online` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Online',
|
||||
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'EnableDeviceNumber',
|
||||
`last_online` datetime(3) DEFAULT NULL COMMENT 'Last Online',
|
||||
`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`),
|
||||
CONSTRAINT `fk_user_user_devices` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Mobile
|
||||
CREATE TABLE IF NOT EXISTS `sms` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`content` text COLLATE utf8mb4_general_ci,
|
||||
`platform` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`area_code` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`telephone` varchar(64) 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;
|
||||
|
||||
-- Application Config
|
||||
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 COLLATE utf8mb4_general_ci COMMENT 'Encryption Key',
|
||||
`encryption_method` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Encryption Method',
|
||||
`domains` text COLLATE utf8mb4_general_ci,
|
||||
`startup_picture` text 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;
|
||||
|
||||
-- Application Version
|
||||
CREATE TABLE IF NOT EXISTS `application_version` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`url` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用地址',
|
||||
`version` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用版本',
|
||||
`platform` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用平台',
|
||||
`is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '默认版本',
|
||||
`description` text 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`),
|
||||
CONSTRAINT `fk_application_application_versions` FOREIGN KEY (`application_id`) REFERENCES `application` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
UPDATE `subscribe` SET `unit_time`='Month' WHERE unit_time = '';
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
44
initialize/migrate/database/01202-patch.sql
Normal file
44
initialize/migrate/database/01202-patch.sql
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
DROP TABLE IF EXISTS `user_device`;
|
||||
-- User Device
|
||||
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) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Ip.',
|
||||
`Identifier` varchar(191) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Identifier.',
|
||||
`user_agent` varchar(64) 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`),
|
||||
CONSTRAINT `fk_user_user_devices` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for server_rule_group
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `server_rule_group`;
|
||||
CREATE TABLE `server_rule_group` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Rule Group Name',
|
||||
`icon` text COLLATE utf8mb4_general_ci COMMENT 'Rule Group Icon',
|
||||
`description` varchar(255) 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`) -- Add unique constraint to `name`
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
-- ----------------------------
|
||||
-- Records of server_rule_group
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
COMMIT;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
562
initialize/migrate/database/ppanel.sql
Normal file
562
initialize/migrate/database/ppanel.sql
Normal file
@ -0,0 +1,562 @@
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ads
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `ads` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(255) COLLATE utf8mb4_german2_ci NOT NULL DEFAULT '' COMMENT 'Ads title',
|
||||
`type` varchar(255) COLLATE utf8mb4_german2_ci NOT NULL DEFAULT '' COMMENT 'Ads type',
|
||||
`content` text COLLATE utf8mb4_german2_ci COMMENT 'Ads content',
|
||||
`target_url` varchar(512) COLLATE utf8mb4_german2_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_german2_ci;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for announcement
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `announcement` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Title',
|
||||
`content` text 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for application
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `application` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用名称',
|
||||
`icon` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '应用图标',
|
||||
`description` text COLLATE utf8mb4_general_ci COMMENT '更新描述',
|
||||
`subscribe_type` varchar(50) 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for application_config
|
||||
-- ----------------------------
|
||||
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 COLLATE utf8mb4_general_ci COMMENT 'Encryption Key',
|
||||
`encryption_method` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Encryption Method',
|
||||
`domains` text COLLATE utf8mb4_general_ci,
|
||||
`startup_picture` text COLLATE utf8mb4_general_ci,
|
||||
`startup_picture_skip_time` bigint NOT NULL DEFAULT '0' COMMENT 'Startup Picture Skip Time',
|
||||
`invitation_link` text COLLATE utf8mb4_general_ci COMMENT 'Invitation Link',
|
||||
`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;
|
||||
-- ----------------------------
|
||||
-- Table structure for application_version
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `application_version` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`url` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用地址',
|
||||
`version` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用版本',
|
||||
`platform` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '应用平台',
|
||||
`is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '默认版本',
|
||||
`description` text 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for coupon
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `coupon` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Coupon Name',
|
||||
`code` varchar(255) 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) 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for document
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `document` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Document Title',
|
||||
`content` text COLLATE utf8mb4_general_ci COMMENT 'Document Content',
|
||||
`tags` varchar(255) 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for auth_method
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `auth_method` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`method` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'method',
|
||||
`config` text 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 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for order
|
||||
-- ----------------------------
|
||||
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) 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) 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) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Payment Method',
|
||||
`fee_amount` bigint NOT NULL DEFAULT '0' COMMENT 'Fee Amount',
|
||||
`trade_no` varchar(255) 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) 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for payment
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `payment` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Payment Name',
|
||||
`platform` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Payment Platform',
|
||||
`description` text COLLATE utf8mb4_general_ci COMMENT 'Payment Description',
|
||||
`icon` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Payment Icon',
|
||||
`domain` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Notification Domain',
|
||||
`config` text 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) 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for server
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `server` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Node Name',
|
||||
`tags` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Tags',
|
||||
`country` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Country',
|
||||
`city` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'City',
|
||||
`latitude` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'latitude',
|
||||
`longitude` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'longitude',
|
||||
`server_addr` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Server Address',
|
||||
`relay_mode` varchar(20) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'none' COMMENT 'Relay Mode',
|
||||
`relay_node` text 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) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Protocol',
|
||||
`config` text 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for server_group
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `server_group` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Group Name',
|
||||
`description` varchar(255) 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for sms
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `sms` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`content` text COLLATE utf8mb4_general_ci,
|
||||
`platform` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`area_code` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`telephone` varchar(64) 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for subscribe
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `subscribe` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Subscribe Name',
|
||||
`description` text COLLATE utf8mb4_general_ci COMMENT 'Subscribe Description',
|
||||
`unit_price` bigint NOT NULL DEFAULT '0' COMMENT 'Unit Price',
|
||||
`unit_time` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Unit Time',
|
||||
`discount` text 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) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Server Group',
|
||||
`server` varchar(255) 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for subscribe_group
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `subscribe_group` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Group Name',
|
||||
`description` text 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;
|
||||
-- ----------------------------
|
||||
-- Table structure for subscribe_type
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `subscribe_type` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '订阅类型',
|
||||
`mark` varchar(255) 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `system` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`category` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Category',
|
||||
`key` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Key Name',
|
||||
`value` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Key Value',
|
||||
`type` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Type',
|
||||
`desc` text 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 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ticket
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `ticket` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Title',
|
||||
`description` text 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ticket_follow
|
||||
-- ----------------------------
|
||||
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) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'From',
|
||||
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Type: 1 text, 2 image',
|
||||
`content` text 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for traffic_log
|
||||
-- ----------------------------
|
||||
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;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `user` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`password` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'User Password',
|
||||
`avatar` text 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) 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 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_auth_methods
|
||||
-- ----------------------------
|
||||
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) 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) 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`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
UNIQUE KEY `idx_auth_identifier` (`auth_identifier`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_balance_log
|
||||
-- ----------------------------
|
||||
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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_commission_log
|
||||
-- ----------------------------
|
||||
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) 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_device
|
||||
-- ----------------------------
|
||||
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) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Ip.',
|
||||
`Identifier` varchar(191) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Identifier.',
|
||||
`user_agent` varchar(64) 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_gift_amount_log
|
||||
-- ----------------------------
|
||||
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) 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) 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_subscribe
|
||||
-- ----------------------------
|
||||
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) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Token',
|
||||
`uuid` varchar(255) 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',
|
||||
`finished_at` datetime(3) DEFAULT NULL COMMENT 'Finished At',
|
||||
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 `server_rule_group` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Rule Group Name',
|
||||
`icon` text COLLATE utf8mb4_general_ci COMMENT 'Rule Group Icon',
|
||||
`description` varchar(255) 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`) -- Add unique constraint to `name`
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_login_log
|
||||
-- ----------------------------
|
||||
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) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Login IP',
|
||||
`user_agent` text 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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_subscribe_log
|
||||
-- ----------------------------
|
||||
|
||||
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) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Token',
|
||||
`ip` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'IP',
|
||||
`user_agent` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'UserAgent',
|
||||
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_user_subscribe_id` (`user_subscribe_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `message_log` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'email' COMMENT 'Message Type',
|
||||
`platform` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'smtp' COMMENT 'Platform',
|
||||
`to` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'To',
|
||||
`subject` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Subject',
|
||||
`content` text 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;
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_device_online_record
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `user_device_online_record` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint NULL DEFAULT NULL COMMENT 'User ID',
|
||||
`identifier` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'Device Identifier',
|
||||
`online_time` datetime(3) NULL DEFAULT NULL COMMENT 'Online Time',
|
||||
`offline_time` datetime(3) NULL DEFAULT NULL COMMENT 'Offline Time',
|
||||
`online_seconds` bigint NOT NULL DEFAULT '0' COMMENT 'Online Seconds ',
|
||||
`duration_days` bigint NOT NULL DEFAULT '0' COMMENT 'Duration Days ',
|
||||
`created_at` datetime(3) NULL DEFAULT NULL COMMENT 'Creation Time',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
644
initialize/migrate/init.go
Normal file
644
initialize/migrate/init.go
Normal file
@ -0,0 +1,644 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/auth"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/payment"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/subscribeType"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/system"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/user"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/constant"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/email"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/sms"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/uuidx"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func InitPPanelSQL(db *gorm.DB) error {
|
||||
logger.Info("PPanel SQL initialization started")
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
logger.Info("PPanel SQL initialization completed", logger.Field("duration", time.Since(startTime).String()))
|
||||
|
||||
}()
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
var err error
|
||||
defer func() {
|
||||
// If an error occurs, delete all tables
|
||||
if err != nil {
|
||||
logger.Debugf("PPanel SQL initialization completed, err: %v", err.Error())
|
||||
tables, _ := tx.Migrator().GetTables()
|
||||
for _, table := range tables {
|
||||
tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table))
|
||||
}
|
||||
}
|
||||
}()
|
||||
// init ppanel.sql file
|
||||
if err = ExecuteSQLFile(tx, "database/ppanel.sql"); err != nil {
|
||||
return err
|
||||
}
|
||||
//Insert basic system data
|
||||
if err = insertBasicSystemData(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
// insert into OAuth config
|
||||
if err = insertAuthMethodConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
// insert into Payment config
|
||||
if err = insertPaymentConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
// insert into SubscribeType
|
||||
if err = insertSubscribeType(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func insertBasicSystemData(tx *gorm.DB) error {
|
||||
if err := insertSiteConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertSubscribeConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertVerifyConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertSeverConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertInviteConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertRegisterConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertCurrencyConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertVerifyCodeConfig(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := system.System{
|
||||
Category: "system",
|
||||
Key: "Version",
|
||||
Value: constant.Version,
|
||||
Type: "string",
|
||||
Desc: "System Version",
|
||||
}
|
||||
if err := tx.Model(&system.System{}).Save(&version).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// insertSiteConfig
|
||||
func insertSiteConfig(tx *gorm.DB) error {
|
||||
siteConfig := []system.System{
|
||||
{
|
||||
Category: "site",
|
||||
Key: "SiteLogo",
|
||||
Value: "/favicon.svg",
|
||||
Type: "string",
|
||||
Desc: "Site Logo",
|
||||
},
|
||||
{
|
||||
Category: "site",
|
||||
Key: "SiteName",
|
||||
Value: "Perfect Panel",
|
||||
Type: "string",
|
||||
Desc: "Site Name",
|
||||
},
|
||||
{
|
||||
Category: "site",
|
||||
Key: "SiteDesc",
|
||||
Value: "PPanel is a pure, professional, and perfect open-source proxy panel tool, designed to be your ideal choice for learning and practical use.",
|
||||
Type: "string",
|
||||
Desc: "Site Description",
|
||||
},
|
||||
{
|
||||
Category: "site",
|
||||
Key: "Host",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "Site Host",
|
||||
},
|
||||
{
|
||||
Category: "site",
|
||||
Key: "Keywords",
|
||||
Value: "Perfect Panel,PPanel",
|
||||
Type: "string",
|
||||
Desc: "Site Keywords",
|
||||
},
|
||||
{
|
||||
Category: "site",
|
||||
Key: "CustomHTML",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "Custom HTML",
|
||||
},
|
||||
{
|
||||
Category: "site",
|
||||
Key: "CustomData",
|
||||
Value: "{\"website\":\"\",\"contacts\":{\"email\":\"\",\"telephone\":\"\",\"address\":\"\"},\"community\":{\"telegram\":\"\",\"twitter\":\"\",\"discord\":\"\",\"instagram\":\"\",\"linkedin\":\"\",\"facebook\":\"\",\"github\":\"\"}}",
|
||||
Type: "string",
|
||||
Desc: "Custom data",
|
||||
},
|
||||
{
|
||||
Category: "tos",
|
||||
Key: "TosContent",
|
||||
Value: "Welcome to use Perfect Panel",
|
||||
Type: "string",
|
||||
Desc: "Terms of Service",
|
||||
},
|
||||
{
|
||||
Category: "tos",
|
||||
Key: "PrivacyPolicy",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "PrivacyPolicy",
|
||||
},
|
||||
{
|
||||
Category: "ad",
|
||||
Key: "WebAD",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "Display ad on the web",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(&siteConfig).Error
|
||||
}
|
||||
|
||||
// insertSubscribeConfig
|
||||
func insertSubscribeConfig(tx *gorm.DB) error {
|
||||
subscribeConfig := []system.System{
|
||||
{
|
||||
Category: "subscribe",
|
||||
Key: "SingleModel",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "是否单订阅模式",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
Category: "subscribe",
|
||||
Key: "SubscribePath",
|
||||
Value: "/api/subscribe",
|
||||
Type: "string",
|
||||
Desc: "订阅路径",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
Category: "subscribe",
|
||||
Key: "SubscribeDomain",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "订阅域名",
|
||||
},
|
||||
{
|
||||
Category: "subscribe",
|
||||
Key: "PanDomain",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "是否使用泛域名",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(&subscribeConfig).Error
|
||||
}
|
||||
|
||||
// insertVerifyConfig
|
||||
func insertVerifyConfig(tx *gorm.DB) error {
|
||||
verifyConfig := []system.System{
|
||||
{
|
||||
Category: "verify",
|
||||
Key: "TurnstileSiteKey",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "TurnstileSiteKey",
|
||||
},
|
||||
{
|
||||
Category: "verify",
|
||||
Key: "TurnstileSecret",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "TurnstileSecret",
|
||||
},
|
||||
{
|
||||
Category: "verify",
|
||||
Key: "EnableLoginVerify",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "is enable login verify",
|
||||
},
|
||||
{
|
||||
Category: "verify",
|
||||
Key: "EnableRegisterVerify",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "is enable register verify",
|
||||
},
|
||||
{
|
||||
Category: "verify",
|
||||
Key: "EnableResetPasswordVerify",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "is enable reset password verify",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(&verifyConfig).Error
|
||||
}
|
||||
|
||||
// insertSeverConfig
|
||||
func insertSeverConfig(tx *gorm.DB) error {
|
||||
serverConfig := []system.System{
|
||||
{
|
||||
Category: "server",
|
||||
Key: "NodeSecret",
|
||||
Value: "12345678",
|
||||
Type: "string",
|
||||
Desc: "node secret",
|
||||
},
|
||||
{
|
||||
Category: "server",
|
||||
Key: "NodePullInterval",
|
||||
Value: "10",
|
||||
Type: "int",
|
||||
Desc: "node pull interval",
|
||||
},
|
||||
{
|
||||
Category: "server",
|
||||
Key: "NodePushInterval",
|
||||
Value: "60",
|
||||
Type: "int",
|
||||
Desc: "node push interval",
|
||||
},
|
||||
{
|
||||
Category: "server",
|
||||
Key: "NodeMultiplierConfig",
|
||||
Value: "[]",
|
||||
Type: "string",
|
||||
Desc: "node multiplier config",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(&serverConfig).Error
|
||||
}
|
||||
|
||||
// insertInviteConfig
|
||||
func insertInviteConfig(tx *gorm.DB) error {
|
||||
inviteConfig := []system.System{
|
||||
{
|
||||
Category: "invite",
|
||||
Key: "ForcedInvite",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "Forced invite",
|
||||
},
|
||||
{
|
||||
Category: "invite",
|
||||
Key: "ReferralPercentage",
|
||||
Value: "20",
|
||||
Type: "int",
|
||||
Desc: "Referral percentage",
|
||||
},
|
||||
{
|
||||
Category: "invite",
|
||||
Key: "OnlyFirstPurchase",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "Only first purchase",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(&inviteConfig).Error
|
||||
}
|
||||
|
||||
// insertRegisterConfig
|
||||
func insertRegisterConfig(tx *gorm.DB) error {
|
||||
registerConfig := []system.System{
|
||||
{
|
||||
Category: "register",
|
||||
Key: "StopRegister",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "is stop register",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "EnableTrial",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "is enable trial",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "TrialSubscribe",
|
||||
Value: "",
|
||||
Type: "int",
|
||||
Desc: "Trial subscription",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "TrialTime",
|
||||
Value: "24",
|
||||
Type: "int",
|
||||
Desc: "Trial time",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "TrialTimeUnit",
|
||||
Value: "Hour",
|
||||
Type: "string",
|
||||
Desc: "Trial time unit",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "EnableIpRegisterLimit",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "is enable IP register limit",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "IpRegisterLimit",
|
||||
Value: "3",
|
||||
Type: "int",
|
||||
Desc: "IP Register Limit",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "IpRegisterLimitDuration",
|
||||
Value: "64",
|
||||
Type: "int",
|
||||
Desc: "IP Register Limit Duration (minutes)",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(®isterConfig).Error
|
||||
}
|
||||
|
||||
// insertAuthMethodConfig
|
||||
func insertAuthMethodConfig(tx *gorm.DB) error {
|
||||
// insert into OAuth config
|
||||
var methods []auth.Auth
|
||||
methods = append(methods, []auth.Auth{
|
||||
initEmailConfig(),
|
||||
initMobileConfig(),
|
||||
{
|
||||
Method: "apple",
|
||||
Config: new(auth.AppleAuthConfig).Marshal(),
|
||||
},
|
||||
{
|
||||
Method: "google",
|
||||
Config: new(auth.GoogleAuthConfig).Marshal(),
|
||||
},
|
||||
{
|
||||
Method: "github",
|
||||
Config: new(auth.GithubAuthConfig).Marshal(),
|
||||
},
|
||||
{
|
||||
Method: "facebook",
|
||||
Config: new(auth.FacebookAuthConfig).Marshal(),
|
||||
},
|
||||
{
|
||||
|
||||
Method: "telegram",
|
||||
Config: new(auth.TelegramAuthConfig).Marshal(),
|
||||
},
|
||||
{
|
||||
Method: "device",
|
||||
Config: new(auth.DeviceConfig).Marshal(),
|
||||
},
|
||||
}...)
|
||||
return tx.Model(&auth.Auth{}).Save(&methods).Error
|
||||
}
|
||||
|
||||
// insertPaymentConfig
|
||||
func insertPaymentConfig(tx *gorm.DB) error {
|
||||
enable := true
|
||||
payments := []payment.Payment{
|
||||
{
|
||||
Id: -1,
|
||||
Name: "Balance",
|
||||
Platform: "balance",
|
||||
Icon: "",
|
||||
Domain: "",
|
||||
Config: "",
|
||||
FeeMode: 0,
|
||||
FeePercent: 0,
|
||||
FeeAmount: 0,
|
||||
Enable: &enable,
|
||||
},
|
||||
}
|
||||
// reset auto increment
|
||||
if err := tx.Exec("ALTER TABLE `payment` AUTO_INCREMENT = 1").Error; err != nil {
|
||||
logger.Errorw("Reset auto increment failed", logger.Field("error", err))
|
||||
return err
|
||||
}
|
||||
return tx.Model(&payment.Payment{}).Save(&payments).Error
|
||||
}
|
||||
|
||||
// insertSubscribeType
|
||||
func insertSubscribeType(tx *gorm.DB) error {
|
||||
// insert into subscribe type
|
||||
var subscribeTypes []subscribeType.SubscribeType
|
||||
subscribeTypes = append(subscribeTypes, []subscribeType.SubscribeType{
|
||||
{
|
||||
Name: "Clash",
|
||||
Mark: "Clash",
|
||||
},
|
||||
{
|
||||
Name: "Hiddify",
|
||||
Mark: "Hiddify",
|
||||
},
|
||||
{
|
||||
Name: "Loon",
|
||||
Mark: "Loon",
|
||||
},
|
||||
{
|
||||
Name: "NekoBox",
|
||||
Mark: "NekoBox",
|
||||
},
|
||||
{
|
||||
Name: "NekoRay",
|
||||
Mark: "NekoRay",
|
||||
},
|
||||
{
|
||||
Name: "Netch",
|
||||
Mark: "Netch",
|
||||
},
|
||||
{
|
||||
Name: "Quantumult",
|
||||
Mark: "Quantumult",
|
||||
},
|
||||
{
|
||||
Name: "Shadowrocket",
|
||||
Mark: "Shadowrocket",
|
||||
},
|
||||
{
|
||||
Name: "Singbox",
|
||||
Mark: "Singbox",
|
||||
},
|
||||
{
|
||||
Name: "Surfboard",
|
||||
Mark: "Surfboard",
|
||||
},
|
||||
{
|
||||
Name: "Surge",
|
||||
Mark: "Surge",
|
||||
},
|
||||
{
|
||||
Name: "V2box",
|
||||
Mark: "V2box",
|
||||
},
|
||||
{
|
||||
Name: "V2rayN",
|
||||
Mark: "V2rayN",
|
||||
},
|
||||
{
|
||||
Name: "V2rayNg",
|
||||
Mark: "V2rayNg",
|
||||
},
|
||||
}...)
|
||||
// insert into payment
|
||||
return tx.Save(&subscribeTypes).Error
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
}
|
||||
|
||||
func initEmailConfig() auth.Auth {
|
||||
enable := true
|
||||
smtpConfig := new(auth.SMTPConfig)
|
||||
emailConfig := auth.EmailAuthConfig{
|
||||
Platform: "smtp",
|
||||
PlatformConfig: smtpConfig,
|
||||
EnableVerify: false,
|
||||
EnableDomainSuffix: false,
|
||||
DomainSuffixList: "",
|
||||
VerifyEmailTemplate: email.DefaultEmailVerifyTemplate,
|
||||
ExpirationEmailTemplate: email.DefaultExpirationEmailTemplate,
|
||||
MaintenanceEmailTemplate: email.DefaultMaintenanceEmailTemplate,
|
||||
TrafficExceedEmailTemplate: email.DefaultTrafficExceedEmailTemplate,
|
||||
}
|
||||
authMethod := auth.Auth{
|
||||
Method: "email",
|
||||
Config: emailConfig.Marshal(),
|
||||
Enabled: &enable,
|
||||
}
|
||||
return authMethod
|
||||
}
|
||||
|
||||
func initMobileConfig() auth.Auth {
|
||||
cfg := new(auth.AlibabaCloudConfig)
|
||||
mobileConfig := auth.MobileAuthConfig{
|
||||
Platform: sms.AlibabaCloud.String(),
|
||||
PlatformConfig: cfg,
|
||||
EnableWhitelist: false,
|
||||
Whitelist: make([]string, 0),
|
||||
}
|
||||
authMethod := auth.Auth{
|
||||
Method: "mobile",
|
||||
Config: mobileConfig.Marshal(),
|
||||
}
|
||||
return authMethod
|
||||
}
|
||||
|
||||
// insert into currency config
|
||||
func insertCurrencyConfig(tx *gorm.DB) error {
|
||||
currencyConfig := []system.System{
|
||||
{
|
||||
Category: "currency",
|
||||
Key: "Currency",
|
||||
Value: "USD",
|
||||
Type: "string",
|
||||
Desc: "Currency",
|
||||
},
|
||||
{
|
||||
Category: "currency",
|
||||
Key: "CurrencySymbol",
|
||||
Value: "$",
|
||||
Type: "string",
|
||||
Desc: "Currency Symbol",
|
||||
},
|
||||
{
|
||||
Category: "currency",
|
||||
Key: "CurrencyUnit",
|
||||
Value: "USD",
|
||||
Type: "string",
|
||||
Desc: "Currency Unit",
|
||||
},
|
||||
{
|
||||
Category: "currency",
|
||||
Key: "AccessKey",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "Exchangerate Access Key",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(¤cyConfig).Error
|
||||
}
|
||||
|
||||
// insert into verify code config
|
||||
func insertVerifyCodeConfig(tx *gorm.DB) error {
|
||||
verifyCodeConfig := []system.System{
|
||||
{
|
||||
Category: "verify_code",
|
||||
Key: "VerifyCodeExpireTime",
|
||||
Value: "300",
|
||||
Type: "int",
|
||||
Desc: "Verify code expire time",
|
||||
},
|
||||
{
|
||||
Category: "verify_code",
|
||||
Key: "VerifyCodeLimit",
|
||||
Value: "15",
|
||||
Type: "int",
|
||||
Desc: "limits of verify code",
|
||||
},
|
||||
{
|
||||
Category: "verify_code",
|
||||
Key: "VerifyCodeInterval",
|
||||
Value: "60",
|
||||
Type: "int",
|
||||
Desc: "Interval of verify code",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(&verifyCodeConfig).Error
|
||||
}
|
||||
37
initialize/migrate/init_test.go
Normal file
37
initialize/migrate/init_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/orm"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func connMySQL() *gorm.DB {
|
||||
|
||||
cfg := orm.Config{
|
||||
Addr: "127.0.0.1",
|
||||
Username: "root",
|
||||
Password: "mylove520",
|
||||
Dbname: "ppanel",
|
||||
}
|
||||
db, err := orm.ConnectMysql(orm.Mysql{
|
||||
Config: cfg,
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return db
|
||||
}
|
||||
func TestInitPPanelSQL(t *testing.T) {
|
||||
t.Skipf("Skip TestInitPPanelSQL")
|
||||
db := connMySQL()
|
||||
if db == nil {
|
||||
t.Error("connect mysql failed")
|
||||
return
|
||||
}
|
||||
if err := InitPPanelSQL(db); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Logf("InitPPanelSQL success")
|
||||
}
|
||||
34
initialize/migrate/migrate.go
Normal file
34
initialize/migrate/migrate.go
Normal file
@ -0,0 +1,34 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/system"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
)
|
||||
|
||||
//go:embed database/*.sql
|
||||
var sqlFiles embed.FS
|
||||
|
||||
func Migrate(ctx *svc.ServiceContext) {
|
||||
logger.Debug("SQL Migrate started")
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
logger.WithDuration(time.Since(startTime)).Debug("PPanel SQL Migrate completed")
|
||||
}()
|
||||
db := ctx.DB
|
||||
if !db.Migrator().HasTable(&system.System{}) {
|
||||
if err := InitPPanelSQL(db); err != nil {
|
||||
logger.Error("SQL Migrate failed", logger.Field("err", err.Error()))
|
||||
panic(err)
|
||||
}
|
||||
// create admin user
|
||||
if err := CreateAdminUser(ctx.Config.Administrator.Email, ctx.Config.Administrator.Password, db); err != nil {
|
||||
logger.Error("Create admin User failed", logger.Field("err", err.Error()))
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
456
initialize/migrate/patch/01703.go
Normal file
456
initialize/migrate/patch/01703.go
Normal file
@ -0,0 +1,456 @@
|
||||
package patch
|
||||
|
||||
import (
|
||||
"github.com/perfect-panel/ppanel-server/initialize/migrate"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/application"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/auth"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/log"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/system"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/user"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/email"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/sms"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Migrate01200(db *gorm.DB) error {
|
||||
var version = "0.1.2(01200)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if exists := db.Migrator().HasColumn(&user.OldUser{}, "email"); !exists {
|
||||
logger.Debug("Migrate 01200 skipped", logger.Field("reason", "old user table not exists"))
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Debug("Migrate 01200 started", logger.Field("step", "1"), logger.Field("action", "migrate old user to user auth methods"))
|
||||
var users []*user.OldUser
|
||||
if err := tx.Model(&user.OldUser{}).Find(&users).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Migrator().AutoMigrate(&user.AuthMethods{}); err != nil {
|
||||
logger.Errorw("Migrate 01200 failed", logger.Field("step", "1"), logger.Field("action", "create user auth methods table"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
err := tx.Transaction(func(tx *gorm.DB) error {
|
||||
for _, oldUser := range users {
|
||||
if oldUser.Email == "" {
|
||||
continue
|
||||
}
|
||||
// create user auth method
|
||||
authMethod := &user.AuthMethods{
|
||||
UserId: oldUser.Id,
|
||||
AuthType: "email",
|
||||
AuthIdentifier: oldUser.Email,
|
||||
Verified: false,
|
||||
}
|
||||
if err := tx.Create(authMethod).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logger.Errorw("Migrate 01200 failed", logger.Field("step", "1"), logger.Field("action", "migrate old user to user auth methods"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Debug("Migrate 01200 completed", logger.Field("step", "1"), logger.Field("action", "migrate old user to user auth methods"))
|
||||
|
||||
logger.Debug("Migrate 01200 started", logger.Field("step", "2"), logger.Field("action", "exclude sql files"))
|
||||
// exclude sql files
|
||||
if err := migrate.ExecuteSQLFile(tx, "database/01200-patch.sql"); err != nil {
|
||||
logger.Errorw("Migrate 01200 failed", logger.Field("step", "2"), logger.Field("action", "exclude sql files"), logger.Field("file", "database/01200-patch.sql"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Debug("Migrate 01200 completed", logger.Field("step", "2"), logger.Field("action", "exclude sql files"))
|
||||
|
||||
logger.Debug("Migrate 01200 started", logger.Field("step", "3"), logger.Field("action", "update system config"))
|
||||
versionConfig := &system.System{
|
||||
Category: "system",
|
||||
Key: "Version",
|
||||
Value: version,
|
||||
Type: "string",
|
||||
Desc: "Version of the system, eg: 1.0.0(10000)",
|
||||
}
|
||||
// update system config
|
||||
if err := tx.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Save(&versionConfig).Error; err != nil {
|
||||
logger.Errorw("Migrate 01200 failed", logger.Field("step", "3"), logger.Field("action", "update system config"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate01201(db *gorm.DB) error {
|
||||
version := "0.1.2(01201)"
|
||||
// exclude sql files
|
||||
if err := migrate.ExecuteSQLFile(db, "database/01201-patch.sql"); err != nil {
|
||||
logger.Errorw("Migrate 01201 failed", logger.Field("step", "1"), logger.Field("action", "exclude sql files"), logger.Field("file", "database/01200-patch.sql"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// update system config
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate 01201 failed", logger.Field("step", "2"), logger.Field("action", "update system config"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Migrate01202(db *gorm.DB) error {
|
||||
version := "0.1.2(01202)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
// migrate email config to system config
|
||||
if err := db.Migrator().AutoMigrate(&auth.Auth{}); err != nil {
|
||||
logger.Errorw("Migrate01202: AutoMigrate Auth failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
if db.Migrator().HasColumn("oauth_config", "platform") {
|
||||
if err := db.Migrator().RenameColumn("oauth_config", "platform", "method"); err != nil {
|
||||
logger.Errorw("Migrate01202: RenameColumn platform to method failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// init email config
|
||||
if err := initEmailConfig(db); err != nil {
|
||||
logger.Errorw("Migrate01202: initEmailConfig failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// init mobile config
|
||||
if err := initMobileConfig(db); err != nil {
|
||||
logger.Errorw("Migrate01202: initMobileConfig failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// drop oauth_config table
|
||||
err := db.Migrator().DropTable("oauth_config")
|
||||
if err != nil {
|
||||
logger.Debug("Migrate01202: DropTable oauth_config failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
}
|
||||
// exclude sql files
|
||||
if err := migrate.ExecuteSQLFile(db, "database/01202-patch.sql"); err != nil {
|
||||
logger.Errorw("Migrate 01202 failed", logger.Field("action", "exclude sql files"), logger.Field("file", "database/012002-patch.sql"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
// update system config
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate 01202 failed", logger.Field("step", "2"), logger.Field("action", "update system config"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func Migrate01203(db *gorm.DB) error {
|
||||
version := "0.1.2(01203)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := db.AutoMigrate(&user.LoginLog{}, &user.SubscribeLog{}); err != nil {
|
||||
logger.Errorw("Migrate01203: AutoMigrate LoginLog/SubscribeLog failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// update version
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate01203: Update Version failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate01204(db *gorm.DB) error {
|
||||
version := "0.1.2(01204)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := db.AutoMigrate(&log.MessageLog{}); err != nil {
|
||||
logger.Errorw("Migrate01204: AutoMigrate MessageLog failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// Trial configuration
|
||||
if err := initTrialConfig(tx); err != nil {
|
||||
logger.Errorw("Migrate01204: initTrialConfig failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// Add auth method with device
|
||||
if err := addAuthMethodWithDevice(tx); err != nil {
|
||||
logger.Errorw("Migrate01204: Add auth method with device failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// update version
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate01204: Update Version failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate01205(db *gorm.DB) error {
|
||||
version := "0.1.2(01205)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
// Add VerifyCode public configuration
|
||||
configs := []system.System{
|
||||
{
|
||||
Category: "verify_code",
|
||||
Key: "VerifyCodeExpireTime",
|
||||
Value: "5",
|
||||
Type: "int",
|
||||
Desc: "Verify code expire time",
|
||||
},
|
||||
{
|
||||
Category: "verify_code",
|
||||
Key: "VerifyCodeLimit",
|
||||
Value: "15",
|
||||
Type: "int",
|
||||
Desc: "limits of verify code",
|
||||
},
|
||||
{
|
||||
Category: "verify_code",
|
||||
Key: "VerifyCodeInterval",
|
||||
Value: "60",
|
||||
Type: "int",
|
||||
Desc: "Interval of verify code",
|
||||
},
|
||||
}
|
||||
if err := tx.Model(&system.System{}).Save(&configs).Error; err != nil {
|
||||
logger.Errorw("Migrate01205: Save VerifyCode public configuration failed", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
// update version
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate01205: Update Version failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate01301(db *gorm.DB) error {
|
||||
version := "0.1.3(01301)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Migrator().AlterColumn(&application.Application{}, "icon")
|
||||
if err != nil {
|
||||
logger.Errorw("Migrate01301: AlterColumn failed", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// update version
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate01205: Update Version failed", logger.Field("version", version), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate01602(db *gorm.DB) error {
|
||||
version := "0.1.6(01602)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if tx.Model(&system.System{}).Where("`category` = 'tos' AND `key` = 'TosContent'").Find(&system.System{}).RowsAffected == 0 {
|
||||
if err := tx.Save(&system.System{
|
||||
Category: "tos",
|
||||
Key: "TosContent",
|
||||
Value: "Welcome to use Perfect Panel",
|
||||
Type: "string",
|
||||
Desc: "Terms of Service",
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// update version
|
||||
if err := tx.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func Migrate01701(db *gorm.DB) error {
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
version := "0.1.7(01701)"
|
||||
if err := db.Migrator().AlterColumn(&user.User{}, "Avatar"); err != nil {
|
||||
return err
|
||||
}
|
||||
// update version
|
||||
if err := tx.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Migrate01702(db *gorm.DB) error {
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
version := "0.1.7(01702)"
|
||||
|
||||
if tx.Model(&system.System{}).Where("`category` = 'site' AND `key` = 'Keywords'").Find(&system.System{}).RowsAffected == 0 {
|
||||
if err := tx.Save(&system.System{
|
||||
Category: "site",
|
||||
Key: "Keywords",
|
||||
Value: "Perfect Panel,PPanel",
|
||||
Type: "string",
|
||||
Desc: "Keywords",
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if tx.Model(&system.System{}).Where("`category` = 'site' AND `key` = 'CustomHTML'").Find(&system.System{}).RowsAffected == 0 {
|
||||
if err := tx.Save(&system.System{
|
||||
Category: "site",
|
||||
Key: "CustomHTML",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "Custom HTML",
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// update version
|
||||
if err := tx.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate01703(db *gorm.DB) error {
|
||||
version := "0.1.7(01703)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if tx.Model(&system.System{}).Where("`category` = 'tos' AND `key` = 'PrivacyPolicy'").Find(&system.System{}).RowsAffected == 0 {
|
||||
if err := tx.Save(&system.System{
|
||||
Category: "tos",
|
||||
Key: "PrivacyPolicy",
|
||||
Value: "",
|
||||
Type: "string",
|
||||
Desc: "Privacy Policy",
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error
|
||||
})
|
||||
}
|
||||
func Migrate01704(db *gorm.DB) error {
|
||||
version := "0.1.7(01704)"
|
||||
|
||||
// check server table latitude column exists, if not exists, create it
|
||||
if exists := db.Migrator().HasColumn(&server.Server{}, "latitude"); !exists {
|
||||
if err := db.Migrator().AddColumn(&server.Server{}, "latitude"); err != nil {
|
||||
logger.Errorw("Migrate 01704 failed", logger.Field("action", "add latitude column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Infow("Migrate 01704 success", logger.Field("action", "add latitude column"))
|
||||
}
|
||||
// check server table longitude column exists, if not exists, create it
|
||||
if exists := db.Migrator().HasColumn(&server.Server{}, "longitude"); !exists {
|
||||
if err := db.Migrator().AddColumn(&server.Server{}, "longitude"); err != nil {
|
||||
logger.Errorw("Migrate 01704 failed", logger.Field("action", "add longitude column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Infow("Migrate 01704 success", logger.Field("action", "add longitude column"))
|
||||
}
|
||||
// update system config
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate 01704 failed", logger.Field("step", "2"), logger.Field("action", "update system config"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Infow("Migrate 01704 success", logger.Field("action", "update system config"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func Migrate01705(db *gorm.DB) error {
|
||||
version := "0.1.7(01705)"
|
||||
// check user_device table exists, if not exists, create it
|
||||
if exists := db.Migrator().HasTable(&user.Device{}); !exists {
|
||||
if err := db.Migrator().CreateTable(&user.Device{}); err != nil {
|
||||
logger.Errorw("Migrate 01705 failed", logger.Field("action", "create user_device table"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Infow("Migrate 01705 success", logger.Field("action", "create user_device table"))
|
||||
}
|
||||
// check user_table exists and imei column exists, if exists, update imei column name to identifier
|
||||
if exists := db.Migrator().HasColumn(&user.Device{}, "imei"); exists {
|
||||
if err := db.Migrator().RenameColumn(&user.Device{}, "imei", "identifier"); err != nil {
|
||||
logger.Errorw("Migrate 01705 failed", logger.Field("action", "rename imei column to identifier"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Infow("Migrate 01705 success", logger.Field("action", "rename imei column to identifier"))
|
||||
}
|
||||
|
||||
// update system config
|
||||
if err := db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error; err != nil {
|
||||
logger.Errorw("Migrate 01705 failed", logger.Field("step", "2"), logger.Field("action", "update system config"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initMobileConfig(db *gorm.DB) error {
|
||||
cfg := new(auth.AlibabaCloudConfig)
|
||||
mobileConfig := auth.MobileAuthConfig{
|
||||
Platform: sms.AlibabaCloud.String(),
|
||||
PlatformConfig: cfg.Marshal(),
|
||||
EnableWhitelist: false,
|
||||
Whitelist: make([]string, 0),
|
||||
}
|
||||
authMethod := auth.Auth{
|
||||
Method: "mobile",
|
||||
Config: mobileConfig.Marshal(),
|
||||
}
|
||||
if err := db.Save(&authMethod).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initEmailConfig(db *gorm.DB) error {
|
||||
enable := true
|
||||
smtpConfig := new(auth.SMTPConfig)
|
||||
|
||||
emailConfig := auth.EmailAuthConfig{
|
||||
Platform: "smtp",
|
||||
PlatformConfig: smtpConfig.Marshal(),
|
||||
EnableVerify: false,
|
||||
EnableDomainSuffix: false,
|
||||
DomainSuffixList: "",
|
||||
VerifyEmailTemplate: email.DefaultEmailVerifyTemplate,
|
||||
ExpirationEmailTemplate: email.DefaultExpirationEmailTemplate,
|
||||
MaintenanceEmailTemplate: email.DefaultMaintenanceEmailTemplate,
|
||||
}
|
||||
authMethod := auth.Auth{
|
||||
Method: "email",
|
||||
Config: emailConfig.Marshal(),
|
||||
Enabled: &enable,
|
||||
}
|
||||
return db.Save(&authMethod).Error
|
||||
}
|
||||
|
||||
func initTrialConfig(tx *gorm.DB) error {
|
||||
configs := []system.System{
|
||||
{
|
||||
Category: "register",
|
||||
Key: "TrialSubscribe",
|
||||
Value: "",
|
||||
Type: "int",
|
||||
Desc: "Trial subscription",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "TrialTime",
|
||||
Value: "24",
|
||||
Type: "int",
|
||||
Desc: "Trial time",
|
||||
},
|
||||
{
|
||||
Category: "register",
|
||||
Key: "TrialTimeUnit",
|
||||
Value: "Hour",
|
||||
Type: "string",
|
||||
Desc: "Trial time unit",
|
||||
},
|
||||
}
|
||||
return tx.Model(&system.System{}).Save(&configs).Error
|
||||
}
|
||||
|
||||
func addAuthMethodWithDevice(tx *gorm.DB) error {
|
||||
return tx.Model(&auth.Auth{}).Save(&auth.Auth{
|
||||
Method: "device",
|
||||
}).Error
|
||||
}
|
||||
252
initialize/migrate/patch/02000.go
Normal file
252
initialize/migrate/patch/02000.go
Normal file
@ -0,0 +1,252 @@
|
||||
package patch
|
||||
|
||||
import (
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/ads"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/application"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/auth"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/order"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/payment"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/system"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/user"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Migrate02000(db *gorm.DB) error {
|
||||
version := "0.2.0(02000)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := initDeviceConfig(tx); err != nil {
|
||||
logMigrationError("Setting Device Config", err)
|
||||
return err
|
||||
}
|
||||
logMigrationSuccess("Setting Device Config")
|
||||
|
||||
if !tx.Migrator().HasTable(&ads.Ads{}) {
|
||||
if err := createAdsTable(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := updatePaymentTable(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Migrator().AutoMigrate(&order.Order{}); err != nil {
|
||||
logMigrationError("Auto Migrate Order", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02001(db *gorm.DB) error {
|
||||
version := "0.2.0(02001)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if tx.Model(&system.System{}).Where("`category` = 'site' AND `key` = 'CustomData'").Find(&system.System{}).RowsAffected == 0 {
|
||||
if err := tx.Save(&system.System{
|
||||
Category: "site",
|
||||
Key: "CustomData",
|
||||
Value: "{\"website\":\"\",\"contacts\":{\"email\":\"\",\"telephone\":\"\",\"address\":\"\"},\"community\":{\"telegram\":\"\",\"twitter\":\"\",\"discord\":\"\",\"instagram\":\"\",\"linkedin\":\"\",\"facebook\":\"\",\"github\":\"\"}}",
|
||||
Type: "string",
|
||||
Desc: "Custom data",
|
||||
}).Error; err != nil {
|
||||
logMigrationError("create custom data system config", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02002(db *gorm.DB) error {
|
||||
version := "0.2.0(02002)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&system.System{}).Where("`category` = 'site' AND `key` = 'CustomData'").UpdateColumn("type", "string").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02003(db *gorm.DB) error {
|
||||
version := "0.2.0(02003)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := addColumnIfNotExists(tx, &order.Order{}, "payment_id"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addColumnIfNotExists(tx, &payment.Payment{}, "platform"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dropColumnIfExists(tx, &payment.Payment{}, "mark"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addColumnIfNotExists(tx, &payment.Payment{}, "description"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addColumnIfNotExists(tx, &payment.Payment{}, "token"); err != nil {
|
||||
return err
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02007(db *gorm.DB) error {
|
||||
version := "0.2.0(02007)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := recreateTable(tx, &server.RuleGroup{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02008(db *gorm.DB) error {
|
||||
version := "0.2.0(02008)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if exists := tx.Migrator().HasColumn(&application.ApplicationConfig{}, "invitation_link"); !exists {
|
||||
if err := tx.Migrator().AddColumn(&application.ApplicationConfig{}, "invitation_link"); err != nil {
|
||||
logger.Errorw("Migrate 02008 failed", logger.Field("action", "add invitation_link column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
logger.Infow("Migrate 02008 success", logger.Field("action", "add invitation_link column"))
|
||||
}
|
||||
|
||||
if exists := tx.Migrator().HasTable(&user.DeviceOnlineRecord{}); !exists {
|
||||
if err := tx.Migrator().CreateTable(&user.DeviceOnlineRecord{}); err != nil {
|
||||
logger.Errorw("Migrate 02008 failed", logger.Field("action", "create device_online_record table"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02009(db *gorm.DB) error {
|
||||
version := "0.2.0(02009)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := addColumnIfNotExists(tx, &user.Subscribe{}, "finished_at"); err != nil {
|
||||
logger.Errorw("Migrate 02009 failed", logger.Field("action", "subscribe table add finished_at column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02010(db *gorm.DB) error {
|
||||
version := "0.2.0(02010)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := addColumnIfNotExists(tx, &application.ApplicationConfig{}, "kr_website_id"); err != nil {
|
||||
logger.Errorw("Migrate 02010 failed", logger.Field("action", "application_config table add kr_website_id column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate02011(db *gorm.DB) error {
|
||||
version := "0.2.0(02011)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := addColumnIfNotExists(tx, &user.Subscribe{}, "used_period"); err != nil {
|
||||
logger.Errorw("Migrate 02011 failed", logger.Field("action", "user.Subscribe table add used_period column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
if err := addColumnIfNotExists(tx, &user.Subscribe{}, "total_period"); err != nil {
|
||||
logger.Errorw("Migrate 02011 failed", logger.Field("action", "user.Subscribe table add total_period column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
|
||||
func initDeviceConfig(db *gorm.DB) error {
|
||||
cfg := new(auth.DeviceConfig)
|
||||
return db.Model(&auth.Auth{}).Where("method = ?", "device").Update("config", cfg.Marshal()).Error
|
||||
}
|
||||
|
||||
func createAdsTable(tx *gorm.DB) error {
|
||||
if err := tx.Migrator().CreateTable(&ads.Ads{}); err != nil {
|
||||
logMigrationError("Create Table Ads", err)
|
||||
return err
|
||||
}
|
||||
logMigrationSuccess("Create Table Ads")
|
||||
return tx.Model(&system.System{}).Save(&system.System{
|
||||
Category: "ad",
|
||||
Key: "WebAD",
|
||||
Value: "false",
|
||||
Type: "bool",
|
||||
Desc: "Display ad on the web",
|
||||
}).Error
|
||||
}
|
||||
|
||||
func updatePaymentTable(tx *gorm.DB) error {
|
||||
if err := tx.Exec("DROP TABLE IF EXISTS `payment`").Error; err != nil {
|
||||
logMigrationError("Drop Payment Table", err)
|
||||
}
|
||||
if err := tx.AutoMigrate(&payment.Payment{}); err != nil {
|
||||
logMigrationError("Auto Migrate Payment", err)
|
||||
return err
|
||||
}
|
||||
enable := true
|
||||
return tx.Model(&payment.Payment{}).Create(&payment.Payment{
|
||||
Id: -1,
|
||||
Name: "",
|
||||
Platform: "balance",
|
||||
Icon: "",
|
||||
Domain: "",
|
||||
Config: "",
|
||||
FeeMode: 0,
|
||||
FeePercent: 0,
|
||||
FeeAmount: 0,
|
||||
Enable: &enable,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func updateSystemVersion(tx *gorm.DB, version string) error {
|
||||
return tx.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").Update("value", version).Error
|
||||
}
|
||||
|
||||
func addColumnIfNotExists(tx *gorm.DB, model interface{}, columnName string) error {
|
||||
if exists := tx.Migrator().HasColumn(model, columnName); !exists {
|
||||
if err := tx.Migrator().AddColumn(model, columnName); err != nil {
|
||||
logMigrationError("add "+columnName+" column", err)
|
||||
return err
|
||||
}
|
||||
logMigrationSuccess("add " + columnName + " column")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dropColumnIfExists(tx *gorm.DB, model interface{}, columnName string) error {
|
||||
if exists := tx.Migrator().HasColumn(model, columnName); exists {
|
||||
if err := tx.Migrator().DropColumn(model, columnName); err != nil {
|
||||
logMigrationError("del "+columnName+" column", err)
|
||||
return err
|
||||
}
|
||||
logMigrationSuccess("del " + columnName + " column")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recreateTable(tx *gorm.DB, model interface{}) error {
|
||||
if exists := tx.Migrator().HasTable(model); exists {
|
||||
if err := tx.Migrator().DropTable(model); err != nil {
|
||||
logMigrationError("drop table", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tx.Migrator().CreateTable(model); err != nil {
|
||||
logMigrationError("create table", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func logMigrationError(action string, err error) {
|
||||
logger.Errorw("Migration failed", logger.Field("action", action), logger.Field("error", err.Error()))
|
||||
}
|
||||
|
||||
func logMigrationSuccess(action string) {
|
||||
logger.Infow("Migration success", logger.Field("action", action))
|
||||
}
|
||||
18
initialize/migrate/patch/03001.go
Normal file
18
initialize/migrate/patch/03001.go
Normal file
@ -0,0 +1,18 @@
|
||||
package patch
|
||||
|
||||
import (
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/user"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Migrate03001(db *gorm.DB) error {
|
||||
version := "0.3.0(1)"
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := addColumnIfNotExists(tx, &user.Subscribe{}, "finished_at"); err != nil {
|
||||
logger.Errorw("Migrate 03001 failed", logger.Field("action", "user.Subscribe table add finished_at column"), logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
return updateSystemVersion(tx, version)
|
||||
})
|
||||
}
|
||||
101
initialize/migrate/tool.go
Normal file
101
initialize/migrate/tool.go
Normal 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
32
initialize/mobile.go
Normal 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
|
||||
}
|
||||
10
initialize/mysql.go
Normal file
10
initialize/mysql.go
Normal file
@ -0,0 +1,10 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/perfect-panel/ppanel-server/initialize/migrate"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
)
|
||||
|
||||
func Mysql(ctx *svc.ServiceContext) {
|
||||
migrate.Migrate(ctx)
|
||||
}
|
||||
51
initialize/node.go
Normal file
51
initialize/node.go
Normal 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
11
initialize/oauth.go
Normal 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
23
initialize/register.go
Normal 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, ®isterConfig)
|
||||
ctx.Config.Register = registerConfig
|
||||
}
|
||||
22
initialize/site.go
Normal file
22
initialize/site.go
Normal 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
57
initialize/statistics.go
Normal 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
24
initialize/subscribe.go
Normal 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
82
initialize/telegram.go
Normal 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")
|
||||
}
|
||||
469
initialize/templates/index.html
Normal file
469
initialize/templates/index.html
Normal file
@ -0,0 +1,469 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- 头部内容 -->
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PPanel - Application Initialization</title>
|
||||
<!-- 引入 Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- Tailwind CSS 配置 -->
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: ["class"],
|
||||
theme: {
|
||||
container: { center: true, padding: "2rem" },
|
||||
extend: {
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))" },
|
||||
destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))" },
|
||||
},
|
||||
borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)" },
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<!-- 自定义样式 -->
|
||||
<style>
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 221.2 83.2% 53.3%;
|
||||
--radius: 0.5rem;
|
||||
--primary: 221.2 83.2% 53.3%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--destructive: 346.8 77.2% 49.8%;
|
||||
--destructive-foreground: 355.7 100% 97.3%;
|
||||
}
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
}
|
||||
body {
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen font-sans antialiased">
|
||||
<div id="appContainer" class="container mx-auto p-4 flex flex-col items-center min-h-screen">
|
||||
<div class="w-full max-w-2xl space-y-6">
|
||||
<!-- 设置卡片 -->
|
||||
<div id="setupCard" class="p-6 space-y-6 bg-white rounded-lg border">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 id="title" class="text-3xl font-bold">Welcome to PPanel Setup</h1>
|
||||
<button onclick="switchLanguage()" id="langSwitch" class="h-10 px-4 py-2 border rounded-md text-sm font-medium bg-background hover:bg-accent hover:text-accent-foreground">中文</button>
|
||||
</div>
|
||||
<p id="description" class="text-gray-600 !mt-0">Let's get your PPanel application up and running. Please provide the necessary information below.</p>
|
||||
|
||||
<form id="setupForm" class="space-y-6" onsubmit="handleSubmit(event)">
|
||||
<!-- 管理员详情 -->
|
||||
<div class="space-y-4">
|
||||
<h2 id="adminInfoTitle" class="font-bold text-xl">Administrator Details</h2>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="adminEmail" id="labelAdminEmail" class="text-sm font-medium">Administrator Email</label>
|
||||
<input type="email" id="adminEmail" name="adminEmail" required class="input-field mt-1 block w-full rounded-md border px-3 py-2" placeholder="Enter administrator email" data-placeholder-en="Enter administrator email" data-placeholder-zh="请输入管理员邮箱" oninput="validateInput(this)">
|
||||
<span id="adminEmailError" class="text-red-500 text-sm mt-1 hidden">Invalid email address.</span>
|
||||
</div>
|
||||
<div>
|
||||
<label for="adminPassword" id="labelAdminPassword" class="text-sm font-medium">Administrator Password</label>
|
||||
<input type="password" id="adminPassword" name="adminPassword" required minlength="6" class="input-field mt-1 block w-full rounded-md border px-3 py-2" placeholder="Enter administrator password" data-placeholder-en="Enter administrator password" data-placeholder-zh="请输入管理员密码" oninput="validateInput(this)">
|
||||
<span id="adminPasswordError" class="text-red-500 text-sm mt-1 hidden">Password must be at least 6 characters.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MySQL 数据库设置 -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 id="mysqlInfoTitle" class="font-bold text-xl">MySQL Database Setup</h2>
|
||||
<button type="button" onclick="testConnection('mysql')" id="testMySQLButton" class="h-10 px-4 py-2 border rounded-md text-sm font-medium bg-background hover:bg-accent hover:text-accent-foreground">Test MySQL Connection</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="mysqlHost" id="labelMysqlHost" class="text-sm font-medium">Database Host</label>
|
||||
<input type="text" id="mysqlHost" name="mysqlHost" required class="input-field mt-1 block w-full rounded-md border px-3 py-2" value="localhost" placeholder="Enter database host" data-placeholder-en="Enter database host" data-placeholder-zh="请输入数据库主机">
|
||||
<span id="mysqlHostError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid host.</span>
|
||||
</div>
|
||||
<div>
|
||||
<label for="mysqlPort" id="labelMysqlPort" class="text-sm font-medium">Database Port</label>
|
||||
<input type="number" id="mysqlPort" name="mysqlPort" required min="1" max="65535" class="input-field mt-1 block w-full rounded-md border px-3 py-2" value="3306" placeholder="Enter database port" data-placeholder-en="Enter database port" data-placeholder-zh="请输入数据库端口">
|
||||
<span id="mysqlPortError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid port number.</span>
|
||||
</div>
|
||||
<div>
|
||||
<label for="mysqlUser" id="labelMysqlUser" class="text-sm font-medium">Database User</label>
|
||||
<input type="text" id="mysqlUser" name="mysqlUser" required class="input-field mt-1 block w-full rounded-md border px-3 py-2" value="root" placeholder="Enter database user" data-placeholder-en="Enter database user" data-placeholder-zh="请输入数据库用户名">
|
||||
<span id="mysqlUserError" class="text-red-500 text-sm mt-1 hidden">Please enter a username.</span>
|
||||
</div>
|
||||
<div>
|
||||
<label for="mysqlPassword" id="labelMysqlPassword" class="text-sm font-medium">Database Password</label>
|
||||
<input type="password" id="mysqlPassword" name="mysqlPassword" class="input-field mt-1 block w-full rounded-md border px-3 py-2" placeholder="Enter database password" data-placeholder-en="Enter database password" data-placeholder-zh="请输入数据库密码">
|
||||
</div>
|
||||
<div>
|
||||
<label for="mysqlDatabase" id="labelMysqlDatabase" class="text-sm font-medium">Database Name</label>
|
||||
<input type="text" id="mysqlDatabase" name="mysqlDatabase" required class="input-field mt-1 block w-full rounded-md border px-3 py-2" value="ppanel_db" placeholder="Enter database name" data-placeholder-en="Enter database name" data-placeholder-zh="请输入数据库名称">
|
||||
<span id="mysqlDatabaseError" class="text-red-500 text-sm mt-1 hidden">Please enter a database name.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis 缓存设置 -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 id="redisInfoTitle" class="font-bold text-xl">Redis Cache Setup</h2>
|
||||
<button type="button" onclick="testConnection('redis')" id="testRedisButton" class="h-10 px-4 py-2 border rounded-md text-sm font-medium bg-background hover:bg-accent hover:text-accent-foreground">Test Redis Connection</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="redisHost" id="labelRedisHost" class="text-sm font-medium">Redis Host</label>
|
||||
<input type="text" id="redisHost" name="redisHost" required class="input-field mt-1 block w-full rounded-md border px-3 py-2" value="127.0.0.1" placeholder="Enter Redis host" data-placeholder-en="Enter Redis host" data-placeholder-zh="请输入 Redis 主机">
|
||||
<span id="redisHostError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid host.</span>
|
||||
</div>
|
||||
<div>
|
||||
<label for="redisPort" id="labelRedisPort" class="text-sm font-medium">Redis Port</label>
|
||||
<input type="number" id="redisPort" name="redisPort" required min="1" max="65535" class="input-field mt-1 block w-full rounded-md border px-3 py-2" value="6379" placeholder="Enter Redis port" data-placeholder-en="Enter Redis port" data-placeholder-zh="请输入 Redis 端口">
|
||||
<span id="redisPortError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid port number.</span>
|
||||
</div>
|
||||
<div>
|
||||
<label for="redisPassword" id="labelRedisPassword" class="text-sm font-medium">Redis Password</label>
|
||||
<input type="password" id="redisPassword" name="redisPassword" class="input-field mt-1 block w-full rounded-md border px-3 py-2" placeholder="Enter Redis password (optional)" data-placeholder-en="Enter Redis password (optional)" data-placeholder-zh="请输入 Redis 密码(可选)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<button type="submit" id="submitButton" disabled class="w-full h-10 px-4 py-2 bg-primary text-white rounded-md opacity-50 cursor-not-allowed">Start Initialization</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 安装中卡片 -->
|
||||
<div id="installingCard" class="fixed inset-0 flex items-center justify-center bg-white p-6 hidden">
|
||||
<div class="w-full max-w-md space-y-6 text-center">
|
||||
<h1 id="installingTitle" class="text-3xl font-bold">Installing...</h1>
|
||||
<p id="installingMessage">Please wait while we initialize your application.</p>
|
||||
<div class="flex justify-center">
|
||||
<svg class="animate-spin h-10 w-10 text-primary" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 安装完成卡片 -->
|
||||
<div id="installationCompleteCard" class="fixed inset-0 flex items-center justify-center bg-white p-6 hidden">
|
||||
<div class="w-full max-w-md space-y-6 text-center">
|
||||
<h1 id="installationCompleteTitle" class="text-3xl font-bold">Installation Complete</h1>
|
||||
<p id="installationCompleteMessage">You can now close this page.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已初始化页面 -->
|
||||
<div id="alreadyInitializedCard" class="fixed inset-0 flex items-center justify-center bg-white p-6 hidden">
|
||||
<div class="w-full max-w-md space-y-6 text-center">
|
||||
<h1 id="alreadyInitializedTitle" class="text-3xl font-bold">Already Initialized</h1>
|
||||
<p id="alreadyInitializedMessage">Your PPanel application has already been initialized.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对话框 -->
|
||||
<div id="dialog" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
|
||||
<div class="bg-white rounded-lg p-6 w-96">
|
||||
<h2 id="dialogTitle" class="text-xl font-bold mb-4"></h2>
|
||||
<p id="dialogMessage" class="mb-4"></p>
|
||||
<div class="flex justify-end">
|
||||
<button onclick="closeDialog()" class="h-10 px-4 py-2 bg-primary text-white rounded-md">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script>
|
||||
let currentLang = 'en';
|
||||
const translations = {
|
||||
en: {
|
||||
// 英文翻译内容
|
||||
title: 'Welcome to PPanel Setup',
|
||||
description: "Let's get your PPanel application up and running. Please provide the necessary information below.",
|
||||
adminInfoTitle: 'Administrator Details',
|
||||
mysqlInfoTitle: 'MySQL Database Setup',
|
||||
redisInfoTitle: 'Redis Cache Setup',
|
||||
adminEmail: 'Administrator Email',
|
||||
adminPassword: 'Administrator Password',
|
||||
mysqlHost: 'Database Host',
|
||||
mysqlPort: 'Database Port',
|
||||
mysqlUser: 'Database User',
|
||||
mysqlPassword: 'Database Password',
|
||||
mysqlDatabase: 'Database Name',
|
||||
redisHost: 'Redis Host',
|
||||
redisPort: 'Redis Port',
|
||||
redisPassword: 'Redis Password',
|
||||
submit: 'Start Initialization',
|
||||
switchLang: '中文',
|
||||
testMySQLConnection: 'Test MySQL Connection',
|
||||
testRedisConnection: 'Test Redis Connection',
|
||||
connectionSuccess: 'Connection Successful',
|
||||
connectionError: 'Connection Failed',
|
||||
initializationError: 'Initialization Error',
|
||||
initializationErrorMessage: 'An error occurred during initialization. Please try again.',
|
||||
installingTitle: 'Installing...',
|
||||
installingMessage: 'Please wait while we initialize your application.',
|
||||
installationCompleteTitle: 'Installation Complete',
|
||||
installationCompleteMessage: 'You can now close this page.',
|
||||
proceedButton: 'Go to Application',
|
||||
mysqlConnectionSuccess: 'Successfully connected to the MySQL database.',
|
||||
mysqlConnectionError: 'Failed to connect to the MySQL database. Please check your settings.',
|
||||
redisConnectionSuccess: 'Successfully connected to the Redis server.',
|
||||
redisConnectionError: 'Failed to connect to the Redis server. Please check your settings.',
|
||||
alreadyInitializedTitle: 'Already Initialized',
|
||||
alreadyInitializedMessage: 'Your PPanel application has already been initialized.',
|
||||
invalidEmail: 'Invalid email address.',
|
||||
invalidPassword: 'Password must be at least 6 characters.',
|
||||
},
|
||||
zh: {
|
||||
// 中文翻译内容
|
||||
title: '欢迎使用 PPanel 设置向导',
|
||||
description: '让我们一起启动您的 PPanel 应用程序。请提供以下必要信息。',
|
||||
adminInfoTitle: '管理员详情',
|
||||
mysqlInfoTitle: 'MySQL 数据库设置',
|
||||
redisInfoTitle: 'Redis 缓存设置',
|
||||
adminEmail: '管理员邮箱',
|
||||
adminPassword: '管理员密码',
|
||||
mysqlHost: '数据库主机',
|
||||
mysqlPort: '数据库端口',
|
||||
mysqlUser: '数据库用户',
|
||||
mysqlPassword: '数据库密码',
|
||||
mysqlDatabase: '数据库名称',
|
||||
redisHost: 'Redis 主机',
|
||||
redisPort: 'Redis 端口',
|
||||
redisPassword: 'Redis 密码',
|
||||
submit: '开始初始化',
|
||||
switchLang: 'English',
|
||||
testMySQLConnection: '测试 MySQL 连接',
|
||||
testRedisConnection: '测试 Redis 连接',
|
||||
connectionSuccess: '连接成功',
|
||||
connectionError: '连接失败',
|
||||
initializationError: '初始化错误',
|
||||
initializationErrorMessage: '初始化过程中发生错误。请重试。',
|
||||
installingTitle: '正在安装...',
|
||||
installingMessage: '请稍候,我们正在初始化您的应用程序。',
|
||||
installationCompleteTitle: '安装完成',
|
||||
installationCompleteMessage: '您现在可以关闭此页面。',
|
||||
proceedButton: '前往应用程序',
|
||||
mysqlConnectionSuccess: '成功连接到 MySQL 数据库。',
|
||||
mysqlConnectionError: '无法连接到 MySQL 数据库。请检查您的设置。',
|
||||
redisConnectionSuccess: '成功连接到 Redis 服务器。',
|
||||
redisConnectionError: '无法连接到 Redis 服务器。请检查您的设置。',
|
||||
alreadyInitializedTitle: '已初始化',
|
||||
alreadyInitializedMessage: '您的 PPanel 应用程序已被初始化。',
|
||||
invalidEmail: '无效的邮箱地址。',
|
||||
invalidPassword: '密码至少需要6个字符。',
|
||||
}
|
||||
};
|
||||
|
||||
function switchLanguage() {
|
||||
currentLang = currentLang === 'en' ? 'zh' : 'en';
|
||||
updateTexts();
|
||||
}
|
||||
|
||||
function updateTexts() {
|
||||
const t = translations[currentLang];
|
||||
document.getElementById('title').textContent = t.title;
|
||||
document.getElementById('description').textContent = t.description;
|
||||
document.getElementById('adminInfoTitle').textContent = t.adminInfoTitle;
|
||||
document.getElementById('mysqlInfoTitle').textContent = t.mysqlInfoTitle;
|
||||
document.getElementById('redisInfoTitle').textContent = t.redisInfoTitle;
|
||||
document.getElementById('labelAdminEmail').textContent = t.adminEmail;
|
||||
document.getElementById('labelAdminPassword').textContent = t.adminPassword;
|
||||
document.getElementById('labelMysqlHost').textContent = t.mysqlHost;
|
||||
document.getElementById('labelMysqlPort').textContent = t.mysqlPort;
|
||||
document.getElementById('labelMysqlUser').textContent = t.mysqlUser;
|
||||
document.getElementById('labelMysqlPassword').textContent = t.mysqlPassword;
|
||||
document.getElementById('labelMysqlDatabase').textContent = t.mysqlDatabase;
|
||||
document.getElementById('labelRedisHost').textContent = t.redisHost;
|
||||
document.getElementById('labelRedisPort').textContent = t.redisPort;
|
||||
document.getElementById('labelRedisPassword').textContent = t.redisPassword;
|
||||
document.getElementById('submitButton').textContent = t.submit;
|
||||
document.getElementById('langSwitch').textContent = t.switchLang;
|
||||
document.getElementById('testMySQLButton').textContent = t.testMySQLConnection;
|
||||
document.getElementById('testRedisButton').textContent = t.testRedisConnection;
|
||||
document.getElementById('installingTitle').textContent = t.installingTitle;
|
||||
document.getElementById('installingMessage').textContent = t.installingMessage;
|
||||
document.getElementById('installationCompleteTitle').textContent = t.installationCompleteTitle;
|
||||
document.getElementById('installationCompleteMessage').textContent = t.installationCompleteMessage;
|
||||
document.getElementById('alreadyInitializedTitle').textContent = t.alreadyInitializedTitle;
|
||||
document.getElementById('alreadyInitializedMessage').textContent = t.alreadyInitializedMessage;
|
||||
|
||||
// 更新 placeholder 文本
|
||||
const inputs = document.querySelectorAll('.input-field');
|
||||
inputs.forEach((input) => {
|
||||
const placeholder = input.getAttribute(`data-placeholder-${currentLang}`);
|
||||
if (placeholder) {
|
||||
input.setAttribute('placeholder', placeholder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkInitialization() {
|
||||
const isInitialized = false; // 需要从服务器获取实际状态
|
||||
if (isInitialized) {
|
||||
document.getElementById('appContainer').classList.add('hidden');
|
||||
document.getElementById('alreadyInitializedCard').classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function testConnection(type) {
|
||||
const t = translations[currentLang];
|
||||
let data = {};
|
||||
let url = '';
|
||||
if (type === 'mysql') {
|
||||
data = {
|
||||
host: document.getElementById('mysqlHost').value,
|
||||
port: document.getElementById('mysqlPort').value,
|
||||
user: document.getElementById('mysqlUser').value,
|
||||
password: document.getElementById('mysqlPassword').value,
|
||||
database: document.getElementById('mysqlDatabase').value,
|
||||
};
|
||||
url = '/init/mysql/test';
|
||||
} else if (type === 'redis') {
|
||||
data = {
|
||||
host: document.getElementById('redisHost').value,
|
||||
port: document.getElementById('redisPort').value,
|
||||
password: document.getElementById('redisPassword').value,
|
||||
};
|
||||
url = '/init/redis/test';
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
console.log(result);
|
||||
if (result.code === 200 && result.status) {
|
||||
showDialog(t.connectionSuccess, result.msg || (type === 'mysql' ? t.mysqlConnectionSuccess : t.redisConnectionSuccess));
|
||||
} else {
|
||||
showDialog(t.connectionError, result.msg || (type === 'mysql' ? t.mysqlConnectionError : t.redisConnectionError));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showDialog(t.connectionError, t.initializationErrorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
const form = document.getElementById('setupForm');
|
||||
if (!form.checkValidity()) {
|
||||
form.reportValidity();
|
||||
return;
|
||||
}
|
||||
const t = translations[currentLang];
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
|
||||
// 显示安装中界面
|
||||
document.getElementById('setupCard').classList.add('hidden');
|
||||
document.getElementById('installingCard').classList.remove('hidden');
|
||||
|
||||
// 发送初始化请求
|
||||
fetch('/init/config', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
document.getElementById('installingCard').classList.add('hidden');
|
||||
if (result.code === 200 && result.status) {
|
||||
document.getElementById('installationCompleteCard').classList.remove('hidden');
|
||||
} else {
|
||||
showDialog(t.initializationError, result.msg || t.initializationErrorMessage);
|
||||
document.getElementById('setupCard').classList.remove('hidden');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
document.getElementById('installingCard').classList.add('hidden');
|
||||
showDialog(t.initializationError, t.initializationErrorMessage);
|
||||
document.getElementById('setupCard').classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
function showDialog(title, message) {
|
||||
document.getElementById('dialogTitle').textContent = title;
|
||||
document.getElementById('dialogMessage').textContent = message;
|
||||
document.getElementById('dialog').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
document.getElementById('dialog').classList.add('hidden');
|
||||
}
|
||||
|
||||
// 实时表单校验
|
||||
function validateInput(input) {
|
||||
const errorSpan = document.getElementById(input.id + 'Error');
|
||||
|
||||
if (input.type === 'email') {
|
||||
// 邮箱格式验证
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(input.value)) {
|
||||
errorSpan.textContent = translations[currentLang].invalidEmail;
|
||||
errorSpan.classList.remove('hidden');
|
||||
input.setCustomValidity(translations[currentLang].invalidEmail);
|
||||
} else {
|
||||
input.setCustomValidity('');
|
||||
errorSpan.classList.add('hidden');
|
||||
}
|
||||
} else if (input.validity.valid) {
|
||||
errorSpan.classList.add('hidden');
|
||||
input.setCustomValidity('');
|
||||
} else {
|
||||
errorSpan.classList.remove('hidden');
|
||||
}
|
||||
|
||||
updateSubmitButtonState();
|
||||
}
|
||||
|
||||
function updateSubmitButtonState() {
|
||||
const form = document.getElementById('setupForm');
|
||||
const submitButton = document.getElementById('submitButton');
|
||||
if (form.checkValidity()) {
|
||||
submitButton.disabled = false;
|
||||
submitButton.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||
} else {
|
||||
submitButton.disabled = true;
|
||||
submitButton.classList.add('opacity-50', 'cursor-not-allowed');
|
||||
}
|
||||
}
|
||||
|
||||
// 添加事件监听器
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const inputs = document.querySelectorAll('.input-field');
|
||||
inputs.forEach((input) => {
|
||||
input.addEventListener('input', () => validateInput(input));
|
||||
});
|
||||
updateSubmitButtonState();
|
||||
updateTexts();
|
||||
checkInitialization();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
48
initialize/verify.go
Normal file
48
initialize/verify.go
Normal file
@ -0,0 +1,48 @@
|
||||
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"
|
||||
)
|
||||
|
||||
type verifyConfig struct {
|
||||
TurnstileSiteKey string
|
||||
TurnstileSecret string
|
||||
EnableLoginVerify bool
|
||||
EnableRegisterVerify bool
|
||||
EnableResetPasswordVerify bool
|
||||
}
|
||||
|
||||
func Verify(svc *svc.ServiceContext) {
|
||||
logger.Debug("Verify config initialization")
|
||||
configs, err := svc.SystemModel.GetVerifyConfig(context.Background())
|
||||
if err != nil {
|
||||
logger.Error("[Init Verify Config] Get Verify Config Error: ", logger.Field("error", err.Error()))
|
||||
return
|
||||
}
|
||||
var verify verifyConfig
|
||||
tool.SystemConfigSliceReflectToStruct(configs, &verify)
|
||||
svc.Config.Verify = config.Verify{
|
||||
TurnstileSiteKey: verify.TurnstileSiteKey,
|
||||
TurnstileSecret: verify.TurnstileSecret,
|
||||
LoginVerify: verify.EnableLoginVerify,
|
||||
RegisterVerify: verify.EnableRegisterVerify,
|
||||
ResetPasswordVerify: verify.EnableResetPasswordVerify,
|
||||
}
|
||||
|
||||
logger.Debug("Verify code config initialization")
|
||||
|
||||
var verifyCodeConfig config.VerifyCode
|
||||
cfg, err := svc.SystemModel.GetVerifyCodeConfig(context.Background())
|
||||
if err != nil {
|
||||
logger.Errorf("[Init Verify Config] Get Verify Code Config Error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
tool.SystemConfigSliceReflectToStruct(cfg, &verifyCodeConfig)
|
||||
svc.Config.VerifyCode = verifyCodeConfig
|
||||
}
|
||||
127
initialize/version.go
Normal file
127
initialize/version.go
Normal file
@ -0,0 +1,127 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/initialize/migrate/patch"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/system"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/constant"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
)
|
||||
|
||||
func VerifyVersion(ctx *svc.ServiceContext) {
|
||||
var configVersion system.System
|
||||
err := ctx.DB.Transaction(func(db *gorm.DB) error {
|
||||
db.Model(&system.System{}).Where("`category` = 'system' AND `key` = 'Version'").First(&configVersion)
|
||||
if configVersion.Value != constant.Version {
|
||||
// Version eg: 1.0.0(10000)
|
||||
current := tool.ExtractVersionNumber(constant.Version)
|
||||
sqlVersion := tool.ExtractVersionNumber(configVersion.Value)
|
||||
logger.Infof("Verify System Version, current version: %d, datebase version: %d", current, sqlVersion)
|
||||
if current > sqlVersion {
|
||||
// Migrate to Milestone Version
|
||||
//
|
||||
// Migrate SQL to 0.1.7(01703)
|
||||
if sqlVersion < 1705 {
|
||||
if err := migrate01701(db, sqlVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 重新执行2000版本的迁移
|
||||
if sqlVersion == 2002 {
|
||||
sqlVersion = 2000
|
||||
}
|
||||
// Migrate SQL to 0.2.0(02000)
|
||||
if sqlVersion < 2009 {
|
||||
if err := migrate02000(db, sqlVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Migrate SQL to 0.3.0(03000)
|
||||
if sqlVersion < 3001 {
|
||||
if err := migrate03000(db, sqlVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic("update system version error:" + err.Error())
|
||||
}
|
||||
}
|
||||
func migrate01701(db *gorm.DB, sqlVersion int) error {
|
||||
migrations := map[int]func(*gorm.DB) error{
|
||||
1200: patch.Migrate01200,
|
||||
1201: patch.Migrate01201,
|
||||
1202: patch.Migrate01202,
|
||||
1203: patch.Migrate01203,
|
||||
1204: patch.Migrate01204,
|
||||
1205: patch.Migrate01205,
|
||||
1301: patch.Migrate01301,
|
||||
1602: patch.Migrate01602,
|
||||
1701: patch.Migrate01701,
|
||||
1702: patch.Migrate01702,
|
||||
1703: patch.Migrate01703,
|
||||
1704: patch.Migrate01704,
|
||||
1705: patch.Migrate01705,
|
||||
}
|
||||
|
||||
for v, migrate := range migrations {
|
||||
if sqlVersion < v {
|
||||
if err := migrate(db); err != nil {
|
||||
return fmt.Errorf("migrator %d version error: %w", v, err)
|
||||
}
|
||||
logger.Infof(fmt.Sprintf("Migrate %d version success", v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrate02000(db *gorm.DB, sqlVersion int) error {
|
||||
migrations := map[int]func(*gorm.DB) error{
|
||||
2000: patch.Migrate02000,
|
||||
2001: patch.Migrate02001,
|
||||
2002: patch.Migrate02002,
|
||||
2003: patch.Migrate02003,
|
||||
2007: patch.Migrate02007,
|
||||
2008: patch.Migrate02008,
|
||||
2009: patch.Migrate02009,
|
||||
2010: patch.Migrate02010,
|
||||
2011: patch.Migrate02011,
|
||||
3001: patch.Migrate03001,
|
||||
}
|
||||
|
||||
for v, migrate := range migrations {
|
||||
if sqlVersion < v {
|
||||
if err := migrate(db); err != nil {
|
||||
return fmt.Errorf("migrator %d version error: %w", v, err)
|
||||
}
|
||||
logger.Infof(fmt.Sprintf("Migrate %d version success", v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrate03000(db *gorm.DB, sqlVersion int) error {
|
||||
migrations := map[int]func(*gorm.DB) error{
|
||||
3001: patch.Migrate03001,
|
||||
}
|
||||
|
||||
for v, migrate := range migrations {
|
||||
if sqlVersion < v {
|
||||
if err := migrate(db); err != nil {
|
||||
return fmt.Errorf("migrator %d version error: %w", v, err)
|
||||
}
|
||||
logger.Infof(fmt.Sprintf("Migrate %d version success", v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
78
internal/config/cacheKey.go
Normal file
78
internal/config/cacheKey.go
Normal file
@ -0,0 +1,78 @@
|
||||
package config
|
||||
|
||||
// CurrencyConfigKey Currency Config Key
|
||||
const CurrencyConfigKey = "system:currency_config"
|
||||
|
||||
// SmsConfigKey Mobile Config Key
|
||||
const SmsConfigKey = "system:sms_config"
|
||||
|
||||
// SiteConfigKey Site Config Key
|
||||
const SiteConfigKey = "system:site_config"
|
||||
|
||||
// SubscribeConfigKey Subscribe Config Key
|
||||
const SubscribeConfigKey = "system:subscribe_config"
|
||||
|
||||
// ApplicationKey Application Key
|
||||
const ApplicationKey = "system:application"
|
||||
|
||||
// RegisterConfigKey Register Config Key
|
||||
const RegisterConfigKey = "system:register_config"
|
||||
|
||||
// VerifyConfigKey Verify Config Key
|
||||
const VerifyConfigKey = "system:verify_config"
|
||||
|
||||
// EmailSmtpConfigKey Email Smtp Config Key
|
||||
const EmailSmtpConfigKey = "system:email_smtp_config"
|
||||
|
||||
// NodeConfigKey Node Config Key
|
||||
const NodeConfigKey = "system:node_config"
|
||||
|
||||
// InviteConfigKey Invite Config Key
|
||||
const InviteConfigKey = "system:invite_config"
|
||||
|
||||
// TelegramConfigKey Telegram Config Key
|
||||
const TelegramConfigKey = "system:telegram_config"
|
||||
|
||||
// TosConfigKey Tos配置
|
||||
const TosConfigKey = "system:tos_config"
|
||||
|
||||
// VerifyCodeConfigKey Verify Code Config Key
|
||||
const VerifyCodeConfigKey = "system:verify_code_config"
|
||||
|
||||
// SessionIdKey cache session key
|
||||
const SessionIdKey = "auth:session_id"
|
||||
|
||||
// GlobalConfigKey Global Config Key
|
||||
const GlobalConfigKey = "system:global_config"
|
||||
|
||||
// AuthCodeCacheKey Register Code Cache Key
|
||||
const AuthCodeCacheKey = "auth:verify:email"
|
||||
|
||||
// AuthCodeTelephoneCacheKey Register Code Cache Key
|
||||
const AuthCodeTelephoneCacheKey = "auth:verify:telephone"
|
||||
|
||||
// ServerUserListCacheKey Server User List Cache Key
|
||||
const ServerUserListCacheKey = "server:user_list:id:"
|
||||
|
||||
// ServerConfigCacheKey Server Config Cache Key
|
||||
const ServerConfigCacheKey = "server:config:id:"
|
||||
|
||||
// CommonStat Cache Key
|
||||
const CommonStatCacheKey = "common:stat"
|
||||
|
||||
// ServerStatusCacheKey Server Status Cache Key
|
||||
const ServerStatusCacheKey = "server:status:id:"
|
||||
|
||||
// ServerCountCacheKey Server Count Cache Key
|
||||
const ServerCountCacheKey = "server:count"
|
||||
|
||||
// UserBindTelegramCacheKey User Bind Telegram Cache Key
|
||||
const UserBindTelegramCacheKey = "user:bind:telegram:code:"
|
||||
|
||||
const CacheSmsCount = "cache:sms:count"
|
||||
|
||||
// SendIntervalKeyPrefix Auth Code Send Interval Key Prefix
|
||||
const SendIntervalKeyPrefix = "send:interval:"
|
||||
|
||||
// SendCountLimitKeyPrefix Send Count Limit Key Prefix eg. send:limit:register:email:xxx@ppanel.dev
|
||||
const SendCountLimitKeyPrefix = "send:limit:"
|
||||
146
internal/config/config.go
Normal file
146
internal/config/config.go
Normal file
@ -0,0 +1,146 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/orm"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Model string `yaml:"Model" default:"prod"`
|
||||
Host string `yaml:"Host" default:"0.0.0.0"`
|
||||
Port int `yaml:"Port" default:"8080"`
|
||||
Debug bool `yaml:"Debug" default:"false"`
|
||||
TLS TLS `yaml:"TLS"`
|
||||
JwtAuth JwtAuth `yaml:"JwtAuth"`
|
||||
Logger logger.LogConf `yaml:"Logger"`
|
||||
MySQL orm.Config `yaml:"MySQL"`
|
||||
Redis RedisConfig `yaml:"Redis"`
|
||||
Site SiteConfig `yaml:"Site"`
|
||||
Node NodeConfig `yaml:"Node"`
|
||||
Mobile MobileConfig `yaml:"Mobile"`
|
||||
Email EmailConfig `yaml:"Email"`
|
||||
Verify Verify `yaml:"Verify"`
|
||||
VerifyCode VerifyCode `yaml:"VerifyCode"`
|
||||
Register RegisterConfig `yaml:"Register"`
|
||||
Subscribe SubscribeConfig `yaml:"Subscribe"`
|
||||
Invite InviteConfig `yaml:"Invite"`
|
||||
Telegram Telegram `yaml:"Telegram"`
|
||||
Administrator struct {
|
||||
Email string `yaml:"Email" default:"admin@ppanel.dev"`
|
||||
Password string `yaml:"Password" default:"password"`
|
||||
} `yaml:"Administrator"`
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
Host string `yaml:"Host" default:"localhost:6379"`
|
||||
Pass string `yaml:"Pass" default:""`
|
||||
DB int `yaml:"DB" default:"0"`
|
||||
}
|
||||
|
||||
type JwtAuth struct {
|
||||
AccessSecret string `yaml:"AccessSecret"`
|
||||
AccessExpire int64 `yaml:"AccessExpire" default:"604800"`
|
||||
}
|
||||
|
||||
type Verify struct {
|
||||
TurnstileSiteKey string `yaml:"TurnstileSiteKey" default:""`
|
||||
TurnstileSecret string `yaml:"TurnstileSecret" default:""`
|
||||
LoginVerify bool `yaml:"LoginVerify" default:"false"`
|
||||
RegisterVerify bool `yaml:"RegisterVerify" default:"false"`
|
||||
ResetPasswordVerify bool `yaml:"ResetPasswordVerify" default:"false"`
|
||||
}
|
||||
|
||||
type SubscribeConfig struct {
|
||||
SingleModel bool `yaml:"SingleModel" default:"false"`
|
||||
SubscribePath string `yaml:"SubscribePath" default:"/api/subscribe"`
|
||||
SubscribeDomain string `yaml:"SubscribeDomain" default:""`
|
||||
PanDomain bool `yaml:"PanDomain" default:"false"`
|
||||
}
|
||||
|
||||
type RegisterConfig struct {
|
||||
StopRegister bool `yaml:"StopRegister" default:"false"`
|
||||
EnableTrial bool `yaml:"EnableTrial" default:"false"`
|
||||
TrialSubscribe int64 `yaml:"TrialSubscribe" default:"0"`
|
||||
TrialTime int64 `yaml:"TrialTime" default:"0"`
|
||||
TrialTimeUnit string `yaml:"TrialTimeUnit" default:""`
|
||||
IpRegisterLimit int64 `yaml:"IpRegisterLimit" default:"0"`
|
||||
IpRegisterLimitDuration int64 `yaml:"IpRegisterLimitDuration" default:"0"`
|
||||
EnableIpRegisterLimit bool `yaml:"EnableIpRegisterLimit" default:"false"`
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
Enable bool `yaml:"Enable" default:"true"`
|
||||
Platform string `yaml:"platform"`
|
||||
PlatformConfig string `yaml:"platform_config"`
|
||||
EnableVerify bool `yaml:"enable_verify"`
|
||||
EnableNotify bool `yaml:"enable_notify"`
|
||||
EnableDomainSuffix bool `yaml:"enable_domain_suffix"`
|
||||
DomainSuffixList string `yaml:"domain_suffix_list"`
|
||||
VerifyEmailTemplate string `yaml:"verify_email_template"`
|
||||
ExpirationEmailTemplate string `yaml:"expiration_email_template"`
|
||||
MaintenanceEmailTemplate string `yaml:"maintenance_email_template"`
|
||||
TrafficExceedEmailTemplate string `yaml:"traffic_exceed_email_template"`
|
||||
}
|
||||
|
||||
type MobileConfig struct {
|
||||
Enable bool `yaml:"Enable" default:"true"`
|
||||
Platform string `yaml:"platform"`
|
||||
PlatformConfig string `yaml:"platform_config"`
|
||||
EnableVerify bool `yaml:"enable_verify"`
|
||||
EnableWhitelist bool `yaml:"enable_whitelist"`
|
||||
Whitelist []string `yaml:"whitelist"`
|
||||
}
|
||||
|
||||
type SiteConfig struct {
|
||||
Host string `yaml:"Host" default:""`
|
||||
SiteName string `yaml:"SiteName" default:""`
|
||||
SiteDesc string `yaml:"SiteDesc" default:""`
|
||||
SiteLogo string `yaml:"SiteLogo" default:""`
|
||||
Keywords string `yaml:"Keywords" default:""`
|
||||
CustomHTML string `yaml:"CustomHTML" default:""`
|
||||
CustomData string `yaml:"CustomData" default:""`
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
NodeSecret string `yaml:"NodeSecret" default:""`
|
||||
NodePullInterval int64 `yaml:"NodePullInterval" default:"60"`
|
||||
NodePushInterval int64 `yaml:"NodePushInterval" default:"60"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Host string `yaml:"Host" default:"0.0.0.0"`
|
||||
Port int `yaml:"Port" default:"8080"`
|
||||
TLS TLS `yaml:"TLS"`
|
||||
Debug bool `yaml:"Debug" default:"true"`
|
||||
JwtAuth JwtAuth `yaml:"JwtAuth"`
|
||||
Logger logger.LogConf `yaml:"Logger"`
|
||||
MySQL orm.Config `yaml:"MySQL"`
|
||||
Redis RedisConfig `yaml:"Redis"`
|
||||
}
|
||||
|
||||
type InviteConfig struct {
|
||||
ForcedInvite bool `yaml:"ForcedInvite" default:"false"`
|
||||
ReferralPercentage int64 `yaml:"ReferralPercentage" default:"0"`
|
||||
OnlyFirstPurchase bool `yaml:"OnlyFirstPurchase" default:"false"`
|
||||
}
|
||||
|
||||
type Telegram struct {
|
||||
Enable bool `yaml:"Enable" default:"false"`
|
||||
BotID int64 `yaml:"BotID" default:""`
|
||||
BotName string `yaml:"BotName" default:""`
|
||||
BotToken string `yaml:"BotToken" default:""`
|
||||
EnableNotify bool `yaml:"EnableNotify" default:"false"`
|
||||
WebHookDomain string `yaml:"WebHookDomain" default:""`
|
||||
}
|
||||
|
||||
type TLS struct {
|
||||
Enable bool `yaml:"Enable" default:"false"`
|
||||
CertFile string `yaml:"CertFile" default:""`
|
||||
KeyFile string `yaml:"KeyFile" default:""`
|
||||
}
|
||||
|
||||
type VerifyCode struct {
|
||||
ExpireTime int64 `yaml:"ExpireTime" default:"300"`
|
||||
Limit int64 `yaml:"Limit" default:"15"`
|
||||
Interval int64 `yaml:"Interval" default:"60"`
|
||||
}
|
||||
5
internal/config/constant.go
Normal file
5
internal/config/constant.go
Normal file
@ -0,0 +1,5 @@
|
||||
package config
|
||||
|
||||
import "github.com/perfect-panel/ppanel-server/pkg/constant"
|
||||
|
||||
const Version = constant.Version
|
||||
10
internal/config/protocol.go
Normal file
10
internal/config/protocol.go
Normal file
@ -0,0 +1,10 @@
|
||||
package config
|
||||
|
||||
type Protocol string
|
||||
|
||||
const (
|
||||
Shadowsocks Protocol = "shadowsocks"
|
||||
Trojan Protocol = "trojan"
|
||||
Vmess Protocol = "vmess"
|
||||
Vless Protocol = "vless"
|
||||
)
|
||||
27
internal/handler/admin/ads/createAdsHandler.go
Normal file
27
internal/handler/admin/ads/createAdsHandler.go
Normal file
@ -0,0 +1,27 @@
|
||||
package ads
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/ppanel-server/internal/logic/admin/ads"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/result"
|
||||
)
|
||||
|
||||
// Create Ads
|
||||
func CreateAdsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.CreateAdsRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
l := ads.NewCreateAdsLogic(ctx, svcCtx)
|
||||
err := l.CreateAds(&req)
|
||||
result.HttpResult(c, nil, err)
|
||||
}
|
||||
}
|
||||
27
internal/handler/admin/ads/deleteAdsHandler.go
Normal file
27
internal/handler/admin/ads/deleteAdsHandler.go
Normal file
@ -0,0 +1,27 @@
|
||||
package ads
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/ppanel-server/internal/logic/admin/ads"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/result"
|
||||
)
|
||||
|
||||
// Delete Ads
|
||||
func DeleteAdsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.DeleteAdsRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
l := ads.NewDeleteAdsLogic(ctx, svcCtx)
|
||||
err := l.DeleteAds(&req)
|
||||
result.HttpResult(c, nil, err)
|
||||
}
|
||||
}
|
||||
27
internal/handler/admin/ads/getAdsDetailHandler.go
Normal file
27
internal/handler/admin/ads/getAdsDetailHandler.go
Normal file
@ -0,0 +1,27 @@
|
||||
package ads
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/ppanel-server/internal/logic/admin/ads"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/result"
|
||||
)
|
||||
|
||||
// Get Ads Detail
|
||||
func GetAdsDetailHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.GetAdsDetailRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
l := ads.NewGetAdsDetailLogic(ctx, svcCtx)
|
||||
resp, err := l.GetAdsDetail(&req)
|
||||
result.HttpResult(c, resp, err)
|
||||
}
|
||||
}
|
||||
27
internal/handler/admin/ads/getAdsListHandler.go
Normal file
27
internal/handler/admin/ads/getAdsListHandler.go
Normal file
@ -0,0 +1,27 @@
|
||||
package ads
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/ppanel-server/internal/logic/admin/ads"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/result"
|
||||
)
|
||||
|
||||
// Get Ads List
|
||||
func GetAdsListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.GetAdsListRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
l := ads.NewGetAdsListLogic(ctx, svcCtx)
|
||||
resp, err := l.GetAdsList(&req)
|
||||
result.HttpResult(c, resp, err)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user