🎉 feat: initialization

This commit is contained in:
web 2025-11-26 19:56:16 -08:00
commit a801849fb2
553 changed files with 213088 additions and 0 deletions

45
.github/ISSUE_TEMPLATE/1_bug_report.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: '🐛 反馈缺陷 Bug Report'
description: '反馈一个问题缺陷 | Report an bug'
title: '[Bug] '
labels: '🐛 Bug'
body:
- type: dropdown
attributes:
label: '💻 系统环境 | Operating System'
options:
- Windows
- macOS
- Ubuntu
- Other Linux
- Other
validations:
required: true
- type: dropdown
attributes:
label: '🌐 浏览器 | Browser'
options:
- Chrome
- Edge
- Safari
- Firefox
- Other
validations:
required: true
- type: textarea
attributes:
label: '🐛 问题描述 | Bug Description'
description: A clear and concise description of the bug.
validations:
required: true
- type: textarea
attributes:
label: '🚦 期望结果 | Expected Behavior'
description: A clear and concise description of what you expected to happen.
- type: textarea
attributes:
label: '📷 复现步骤 | Recurrence Steps'
description: A clear and concise description of how to recurrence.
- type: textarea
attributes:
label: '📝 补充信息 | Additional Information'
description: If your problem needs further explanation, or if the issue you're seeing cannot be reproduced in a gist, please add more information here.

View File

@ -0,0 +1,21 @@
name: '🌠 功能需求 Feature Request'
description: '需求或建议 | Suggest an idea'
title: '[Request] '
labels: '🌠 Feature Request'
body:
- type: textarea
attributes:
label: '🥰 需求描述 | Feature Description'
description: Please add a clear and concise description of the problem you are seeking to solve with this feature request.
validations:
required: true
- type: textarea
attributes:
label: '🧐 解决方案 | Proposed Solution'
description: Describe the solution you'd like in a clear and concise manner.
validations:
required: true
- type: textarea
attributes:
label: '📝 补充信息 | Additional Information'
description: Add any other context about the problem here.

15
.github/ISSUE_TEMPLATE/3_question.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: '😇 疑问或帮助 Help Wanted'
description: '疑问或需要帮助 | Need help'
title: '[Question] '
labels: '😇 Help Wanted'
body:
- type: textarea
attributes:
label: '🧐 问题描述 | Proposed Solution'
description: A clear and concise description of the proplem.
validations:
required: true
- type: textarea
attributes:
label: '📝 补充信息 | Additional Information'
description: Add any other context about the problem here.

7
.github/ISSUE_TEMPLATE/4_other.md vendored Normal file
View File

@ -0,0 +1,7 @@
---
name: '📝 其他 Other'
about: '其他问题 | Other issues'
title: ''
labels: ''
assignees: ''
---

17
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,17 @@
#### 💻 变更类型 | Change Type
<!-- For change type, change [ ] to [x]. -->
- \[ ] ✨ feat
- \[ ] 🐛 fix
- \[ ] 💄 style
- \[ ] 🔨 chore
- \[ ] 📝 docs
#### 🔀 变更说明 | Description of Change
<!-- Thank you for your Pull Request. Please provide a description above. -->
#### 📝 补充信息 | Additional Information
<!-- Add any other context about the Pull Request here. -->

30
.github/workflows/auto-merge.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Dependabot Auto Merge
on:
pull_request_target:
types: [labeled, edited]
jobs:
merge:
if: contains(github.event.pull_request.labels.*.name, 'dependencies')
name: Dependabot Auto Merge
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install deps
run: pnpm install
- name: Merge
uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
command: merge
target: minor
github-token: ${{ secrets.GH_TOKEN }}

View File

@ -0,0 +1,22 @@
name: Issue Check Inactive
on:
schedule:
- cron: '0 0 */15 * *'
permissions:
contents: read
jobs:
issue-check-inactive:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: check-inactive
uses: actions-cool/issues-helper@v3
with:
actions: 'check-inactive'
inactive-label: 'Inactive'
inactive-day: 30

View File

@ -0,0 +1,46 @@
name: Issue Close Require
on:
schedule:
- cron: '0 0 * * *'
permissions:
contents: read
jobs:
issue-close-require:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: '✅ Fixed'
inactive-day: 3
body: |
Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
由于该 issue 被标记为已修复,同时 3 天未收到回应。现关闭 issue若有任何问题可评论回复。
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: '🤔 Need Reproduce'
inactive-day: 3
body: |
Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
由于该 issue 被标记为需要更多信息,却 3 天未收到回应。现关闭 issue若有任何问题可评论回复。
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: "🙅🏻‍♀️ WON'T DO"
inactive-day: 3
body: |
Since the issue was labeled with `🙅🏻‍♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
由于该 issue 被标记为暂不处理,同时 3 天未收到回应。现关闭 issue若有任何问题可评论回复。

View File

@ -0,0 +1,25 @@
name: Issue Remove Inactive
on:
issues:
types: [edited]
issue_comment:
types: [created, edited]
permissions:
contents: read
jobs:
issue-remove-inactive:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: remove inactive
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login
uses: actions-cool/issues-helper@v3
with:
actions: 'remove-labels'
issue-number: ${{ github.event.issue.number }}
labels: 'Inactive'

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

@ -0,0 +1,42 @@
name: Build and Release
on:
push:
branches: [main, next, beta]
permissions:
contents: write
jobs:
release:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 'latest'
- name: Cache Bun dependencies
uses: actions/cache@v3
with:
path: |
~/.bun
node_modules
key: ${{ runner.os }}-bun-cache-${{ hashFiles('**/bun.lockb') }}
restore-keys: |
${{ runner.os }}-bun-cache-
- name: Install deps
run: bun install
- name: Build
run: bun run build
- name: Release
id: release
run: bun run release
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Dependencies
node_modules
.pnp
.pnp.js
# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Testing
coverage
# Turbo
.turbo
# Vercel
.vercel
# Build Outputs
.next/
out/
build
dist
# Debug
npm-debug.log*
# Misc
.DS_Store
*.pem

70
.husky/pre-commit Normal file
View File

@ -0,0 +1,70 @@
npm test
#!/bin/sh
# Exit on any error
set -e
# Check if there are any staged files
if [ -z "$(git diff --cached --name-only)" ]; then
echo "No staged files to format"
exit 0
fi
# Store the hash of staged changes to detect modifications
STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)
# Save list of staged files (handling all file states)
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
PARTIALLY_STAGED=$(git diff --name-only)
# Stash unstaged changes to preserve working directory
# --keep-index keeps staged changes in working tree
git stash push --quiet --keep-index --message "pre-commit-stash" || true
STASHED=$?
# Run formatter on the staged files
bun x ultracite fix
FORMAT_EXIT_CODE=$?
# Restore working directory state
if [ $STASHED -eq 0 ]; then
# Re-stage the formatted files
if [ -n "$STAGED_FILES" ]; then
echo "$STAGED_FILES" | while IFS= read -r file; do
if [ -f "$file" ]; then
git add "$file"
fi
done
fi
# Restore unstaged changes
git stash pop --quiet || true
# Restore partial staging if files were partially staged
if [ -n "$PARTIALLY_STAGED" ]; then
for file in $PARTIALLY_STAGED; do
if [ -f "$file" ] && echo "$STAGED_FILES" | grep -q "^$file$"; then
# File was partially staged - need to unstage the unstaged parts
git restore --staged "$file" 2>/dev/null || true
git add -p "$file" < /dev/null 2>/dev/null || git add "$file"
fi
done
fi
else
# No stash was created, just re-add the formatted files
if [ -n "$STAGED_FILES" ]; then
echo "$STAGED_FILES" | while IFS= read -r file; do
if [ -f "$file" ]; then
git add "$file"
fi
done
fi
fi
# Check if staged files actually changed
NEW_STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)
if [ "$STAGED_HASH" != "$NEW_STAGED_HASH" ]; then
echo "✨ Files formatted by Ultracite"
fi
exit $FORMAT_EXIT_CODE

157
.releaserc.json Normal file
View File

@ -0,0 +1,157 @@
{
"repositoryUrl": "https://github.com/perfect-panel/frontend",
"branches": [
"main",
{
"name": "beta",
"prerelease": true
},
{
"name": "alpha",
"prerelease": true
},
{
"name": "develop",
"prerelease": "dev"
},
{
"name": "next",
"prerelease": true
}
],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits",
"releaseRules": [
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "perf", "release": "patch" },
{ "type": "revert", "release": "patch" },
{ "type": "docs", "release": false },
{ "type": "style", "release": false },
{ "type": "chore", "release": false },
{ "type": "refactor", "release": "patch" },
{ "type": "test", "release": false },
{ "type": "build", "release": false },
{ "type": "ci", "release": false },
{ "release": "major", "breaking": true },
{ "release": "patch", "subject": "*force patch*" },
{ "release": "minor", "subject": "*force minor*" },
{ "release": "major", "subject": "*force major*" },
{ "release": false, "subject": "*skip release*" },
{ "release": false, "subject": "*no release*" },
{ "release": "patch", "revert": true },
{ "release": "patch", "type": "security" },
{ "release": "minor", "type": "deprecate" }
],
"parserOpts": {
"headerPattern": "^(?:(\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff])\\s*)?(\\w*)(?:\\((.*)\\))?!?:\\s*(.*)$",
"headerCorrespondence": ["emoji", "type", "scope", "subject"],
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"],
"revertPattern": "^(?:Revert|revert:)\\s\"?([\\s\\S]*)\"?\\s*This reverts commit (\\w*)\\.?",
"revertCorrespondence": ["header", "hash"]
}
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits",
"presetConfig": {
"types": [
{
"type": "feat",
"section": "✨ Features / 新功能"
},
{
"type": "fix",
"section": "🐛 Bug Fixes / 问题修复"
},
{
"type": "docs",
"section": "📚 Documentation / 文档更新"
},
{
"type": "style",
"section": "🎨 Code Style / 代码格式"
},
{
"type": "refactor",
"section": "♻️ Code Refactoring / 代码重构"
},
{
"type": "perf",
"section": "⚡️ Performance Improvements / 性能优化"
},
{
"type": "test",
"section": "🧪 Tests / 测试相关"
},
{
"type": "build",
"section": "🔨 Build System / 构建系统"
},
{
"type": "ci",
"section": "👷 Continuous Integration / CI/CD"
},
{
"type": "chore",
"section": "🔧 Chores / 其他变更"
}
]
},
"parserOpts": {
"headerPattern": "^(?:(\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff])\\s*)?(\\w*)(?:\\((.*)\\))?!?:\\s*(.*)$",
"headerCorrespondence": ["emoji", "type", "scope", "subject"],
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"],
"revertPattern": "^(?:Revert|revert:)\\s\"?([\\s\\S]*)\"?\\s*This reverts commit (\\w*)\\.?",
"revertCorrespondence": ["header", "hash"]
}
}
],
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md",
"changelogTitle": "# 📋 Changelog / 更新日志\n\nThis document records all notable changes to ShadCN Admin.\n本文档记录了 ShadCN Admin 的所有重要变更。\n\n## Version Guide / 版本说明\n- 🔥 **Breaking Changes / 重大变更**: Contains breaking updates / 包含破坏性更新\n- ✨ **Features / 新功能**: New features added / 添加的新特性\n- 🐛 **Bug Fixes / 问题修复**: Fixed bugs / 修复的bug\n- 📚 **Documentation / 文档**: Documentation updates / 文档相关更新\n- 🎨 **Style / 样式**: Code formatting and style changes / 代码格式化、样式调整\n- ♻️ **Refactoring / 重构**: Code refactoring / 代码重构\n- ⚡️ **Performance / 性能**: Performance improvements / 性能优化\n- 🧪 **Tests / 测试**: Test related changes / 测试相关\n- 🔨 **Build / 构建**: Build system changes / 构建系统相关\n- 👷 **CI/CD**: Continuous integration changes / CI/CD相关\n- 🔧 **Chores / 杂项**: Other changes / 其他变更\n\n---\n"
}
],
[
"@semantic-release/npm",
{
"npmPublish": false
}
],
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md", "package.json"],
"message": "🚀 chore(release): Release ${nextRelease.version} / 发布版本 ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
[
"@semantic-release/exec",
{
"prepareCmd": "tar -czf admin.tar.gz -C apps/admin dist/ && tar -czf user.tar.gz -C apps/user dist/"
}
],
[
"@semantic-release/github",
{
"assets": [
{
"path": "admin.tar.gz",
"name": "ppanel-admin-${nextRelease.version}.tar.gz"
},
{
"path": "user.tar.gz",
"name": "ppanel-user-${nextRelease.version}.tar.gz"
}
]
}
]
]
}

44
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,44 @@
{
"tailwindCSS.experimental.configFile": "packages/ui/src/styles/globals.css",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[css]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[graphql]": {
"editor.defaultFormatter": "biomejs.biome"
},
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"emmet.showExpandedAbbreviation": "never",
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.ts": "${capture}.js",
"*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts",
"*.jsx": "${capture}.js",
"*.tsx": "${capture}.ts",
"README.md": "*.md, LICENSE"
}
}

128
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<web@ppanel.dev>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
For answers to common questions about this code of conduct, see the FAQ at
<https://www.contributor-covenant.org/faq>. Translations are available at
<https://www.contributor-covenant.org/translations>.
[homepage]: https://www.contributor-covenant.org

674
LICENSE Normal file
View File

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

138
README.md Normal file
View File

@ -0,0 +1,138 @@
<a name="readme-top"></a>
<div align="center">
<img width="160" src="https://raw.githubusercontent.com/perfect-panel/ppanel-assets/refs/heads/main/logo.svg">
<h1>PPanel web</h1>
This is a PPanel web powered by PPanel
English
·
[Chinese](./README.zh-CN.md)
·
[Changelog](../../CHANGELOG.md)
·
[Report Bug][issues-link]
·
[Request Feature][issues-link]
<!-- SHIELD GROUP -->
[![][github-release-shield]][github-release-link]
[![][github-releasedate-shield]][github-releasedate-link]
[![][github-action-test-shield]][github-action-test-link]
[![][github-action-release-shield]][github-action-release-link]<br/>
[![][github-contributors-shield]][github-contributors-link]
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
[![][github-license-shield]][github-license-link]
![][split]
</div>
> **Article 1.**
> All human beings are born free and equal in dignity and rights.
> They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.
>
> **Article 12.**
> No one shall be subjected to arbitrary interference with his privacy, family, home or correspondence, nor to attacks upon his honour and reputation.
> Everyone has the right to the protection of the law against such interference or attacks.
>
> **Article 19.**
> Everyone has the right to freedom of opinion and expression; this right includes freedom to hold opinions without interference and to seek, receive and impart information and ideas through any media and regardless of frontiers.
>
> _Source: [United Nations Universal Declaration of Human Rights (UN.org)](https://www.un.org/sites/un2.un.org/files/2021/03/udhr.pdf)_
## 📦 Application List
| 📦 Application | 🖼️ Preview |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- |
| [**PPanel User Web**][ppanel-user-web-github]<br/>Developed with modern frontend technologies (Next.js, TypeScript, TailwindCSS), providing basic user features with support for multiple languages and themes.<br/>[![One-Click Deploy](https://img.shields.io/badge/Deploy%20with-Vercel-blue?style=for-the-badge)][ppanel-user-web-deploy] | [![Preview][ppanel-user-web-cover]][ppanel-user-web-github] |
| [**PPanel Admin Web**][ppanel-admin-web-github]<br/>Developed with modern frontend technologies, this admin web provides basic data management features with support for multiple languages and themes.<br/>[![One-Click Deploy](https://img.shields.io/badge/Deploy%20with-Vercel-blue?style=for-the-badge)][ppanel-admin-web-deploy] | [![Preview][ppanel-admin-web-cover]][ppanel-admin-web-preview] |
## ⌨️ Local Development
You can use Github Codespaces for online development:
[![][codespaces-shield]][codespaces-link]
You can use Gitpod for online development:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][gitpod-link]
or clone it for local development:
```bash
git clone https://github.com/perfect-panel/ppanel-web.git
cd ppanel-web
# Install dependencies
bun install
```
## 🤝 Contributing
Contributions of all types are more than welcome,
if you're interested in contributing code, feel free to check out our GitHub
[Issues][github-issues-link] to get stuck in to show us what youre made of.
[![][pr-welcome-shield]][pr-welcome-link]
[![][contributors-contrib]][contributors-url]
<div align="right">
[![][back-to-top]](#readme-top)
</div>
---
## 📝 License
Copyright © 2024 [PPanel][profile-link]. <br />
This project is [GNU](../../LICENSE) licensed.
<!-- LINK GROUP -->
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square
[codespaces-link]: https://codespaces.new/perfect-panel/ppanel-web
[codespaces-shield]: https://github.com/codespaces/badge.svg
[contributors-contrib]: https://contrib.rocks/image?repo=perfect-panel/ppanel-web
[contributors-url]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-action-release-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/release.yml
[github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-action-test-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/test.yml
[github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-contributors-link]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-contributors-shield]: https://img.shields.io/github/contributors/perfect-panel/ppanel-web?color=c4f042&labelColor=black&style=flat-square
[github-forks-link]: https://github.com/perfect-panel/ppanel-web/network/members
[github-forks-shield]: https://img.shields.io/github/forks/perfect-panel/ppanel-web?color=8ae8ff&labelColor=black&style=flat-square
[github-issues-link]: https://github.com/perfect-panel/ppanel-web/issues
[github-issues-shield]: https://img.shields.io/github/issues/perfect-panel/ppanel-web?color=ff80eb&labelColor=black&style=flat-square
[github-license-link]: https://github.com/perfect-panel/ppanel-web/blob/master/LICENSE
[github-license-shield]: https://img.shields.io/github/license/perfect-panel/ppanel-web?color=white&labelColor=black&style=flat-square
[github-release-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-release-shield]: https://img.shields.io/github/v/release/perfect-panel/ppanel-web?style=flat-square&sort=semver&logo=github
[github-releasedate-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/perfect-panel/ppanel-web?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/perfect-panel/ppanel-web/network/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/perfect-panel/ppanel-web?color=ffcb47&labelColor=black&style=flat-square
[gitpod-link]: https://gitpod.io/#https://github.com/perfect-panel/ppanel-web
[issues-link]: https://github.com/perfect-panel/ppanel-web/issues/new/choose
[pr-welcome-link]: https://github.com/perfect-panel/ppanel-web/pulls
[pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/perfect-panel
[split]: https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png
[ppanel-user-web-github]: https://github.com/perfect-panel/ppanel-web/tree/main/apps/user
[ppanel-user-web-cover]: https://urlscan.io/liveshot/?width=1920&height=1080&url=https://user.ppanel.dev
[ppanel-user-web-preview]: https://user.ppanel.dev
[ppanel-user-web-deploy]: https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fuser.ppanel.dev&demo-title=PPanel%20User%20Web&demo-url=https%3A%2F%2Fuser.ppanel.dev%2F&from=.&project-name=ppanel-user-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fuser&skippable-integrations=1
[ppanel-admin-web-github]: https://github.com/perfect-panel/ppanel-web/tree/main/apps/admin
[ppanel-admin-web-cover]: https://urlscan.io/liveshot/?width=1920&height=1080&url=https://admin.ppanel.dev
[ppanel-admin-web-preview]: https://admin.ppanel.dev
[ppanel-admin-web-deploy]: https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fadmin.ppanel.dev&demo-title=PPanel%20Admin%20Web&demo-url=https%3A%2F%2Fadmin.ppanel.dev%2F&from=.&project-name=ppanel-admin-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fadmin&skippable-integrations=1

138
README.zh-CN.md Normal file
View File

@ -0,0 +1,138 @@
<a name="readme-top"></a>
<div align="center">
<img width="160" src="https://raw.githubusercontent.com/perfect-panel/ppanel-assets/refs/heads/main/logo.svg">
<h1>PPanel 前端</h1>
这是由 PPanel 提供支持的前端
[英文](./README.md)
·
中文
·
[更新日志](./CHANGELOG.md)
·
[报告问题][issues-link]
·
[请求功能][issues-link]
<!-- SHIELD GROUP -->
[![][github-release-shield]][github-release-link]
[![][github-releasedate-shield]][github-releasedate-link]
[![][github-action-test-shield]][github-action-test-link]
[![][github-action-release-shield]][github-action-release-link]<br/>
[![][github-contributors-shield]][github-contributors-link]
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
[![][github-license-shield]][github-license-link]
![][split]
</div>
> **第一条**
> 人人生而自由,在尊严与权利上一律平等。
> 他们赋有理性与良知,应当以兄弟般的精神彼此相待。
>
> **第十二条**
> 任何人的隐私、家庭、住宅和通信不得任意干涉,其名誉与荣誉不得加以攻击。
> 人人有权受到法律的保护,以免遭受这种干涉或攻击。
>
> **第十九条**
> 人人有思想与表达的自由;此项自由包括持有主张而不受干预,以及通过任何媒介、无论国界,自由寻求、接受和传播信息与思想。
>
> _来源 [United Nations Universal Declaration of Human Rights (UN.org)](https://www.un.org/sites/un2.un.org/files/2021/03/udhr.pdf)_
## 📦 Application List
| 📦 Application | 🖼️ Preview |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- |
| [**PPanel User Web**][ppanel-user-web-github]<br/>Developed with modern frontend technologies (Next.js, TypeScript, TailwindCSS), providing basic user features with support for multiple languages and themes.<br/>[![One-Click Deploy](https://img.shields.io/badge/Deploy%20with-Vercel-blue?style=for-the-badge)][ppanel-user-web-deploy] | [![Preview][ppanel-user-web-cover]][ppanel-user-web-github] |
| [**PPanel Admin Web**][ppanel-admin-web-github]<br/>Developed with modern frontend technologies, this admin web provides basic data management features with support for multiple languages and themes.<br/>[![One-Click Deploy](https://img.shields.io/badge/Deploy%20with-Vercel-blue?style=for-the-badge)][ppanel-admin-web-deploy] | [![Preview][ppanel-admin-web-cover]][ppanel-admin-web-preview] |
## ⌨️ 本地开发
您可以使用 Github Codespaces 进行在线开发:
[![][codespaces-shield]][codespaces-link]
您可以使用 Gitpod 进行在线开发:
[![在 Gitpod 中打开](https://gitpod.io/button/open-in-gitpod.svg)][gitpod-link]
或者克隆项目进行本地开发:
```bash
git clone https://github.com/perfect-panel/ppanel-web.git
cd ppanel-web
# 安装依赖
bun install
```
## 🤝 贡献
欢迎各种类型的贡献,
如果您有兴趣贡献代码,请随时查看我们的 GitHub
[问题][github-issues-link] 来展示您的能力。
[![][pr-welcome-shield]][pr-welcome-link]
[![][contributors-contrib]][contributors-url]
<div align="right">
[![][back-to-top]](#readme-top)
</div>
---
## 📝 许可证
版权所有 © 2024 [PPanel][profile-link]。<br />
本项目使用 [GNU](./LICENSE) 许可证。
<!-- LINK GROUP -->
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square
[codespaces-link]: https://codespaces.new/perfect-panel/ppanel-web
[codespaces-shield]: https://github.com/codespaces/badge.svg
[contributors-contrib]: https://contrib.rocks/image?repo=perfect-panel/ppanel-web
[contributors-url]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-action-release-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/release.yml
[github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-action-test-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/test.yml
[github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-contributors-link]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-contributors-shield]: https://img.shields.io/github/contributors/perfect-panel/ppanel-web?color=c4f042&labelColor=black&style=flat-square
[github-forks-link]: https://github.com/perfect-panel/ppanel-web/network/members
[github-forks-shield]: https://img.shields.io/github/forks/perfect-panel/ppanel-web?color=8ae8ff&labelColor=black&style=flat-square
[github-issues-link]: https://github.com/perfect-panel/ppanel-web/issues
[github-issues-shield]: https://img.shields.io/github/issues/perfect-panel/ppanel-web?color=ff80eb&labelColor=black&style=flat-square
[github-license-link]: https://github.com/perfect-panel/ppanel-web/blob/master/LICENSE
[github-license-shield]: https://img.shields.io/github/license/perfect-panel/ppanel-web?color=white&labelColor=black&style=flat-square
[github-release-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-release-shield]: https://img.shields.io/github/v/release/perfect-panel/ppanel-web?style=flat-square&sort=semver&logo=github
[github-releasedate-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/perfect-panel/ppanel-web?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/perfect-panel/ppanel-web/network/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/perfect-panel/ppanel-web?color=ffcb47&labelColor=black&style=flat-square
[gitpod-link]: https://gitpod.io/#https://github.com/perfect-panel/ppanel-web
[issues-link]: https://github.com/perfect-panel/ppanel-web/issues/new/choose
[pr-welcome-link]: https://github.com/perfect-panel/ppanel-web/pulls
[pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/perfect-panel
[split]: https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png
[ppanel-user-web-github]: https://github.com/perfect-panel/ppanel-web/tree/main/apps/user
[ppanel-user-web-cover]: https://urlscan.io/liveshot/?width=1920&height=1080&url=https://user.ppanel.dev
[ppanel-user-web-preview]: https://user.ppanel.dev
[ppanel-user-web-deploy]: https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fuser.ppanel.dev&demo-title=PPanel%20User%20Web&demo-url=https%3A%2F%2Fuser.ppanel.dev%2F&from=.&project-name=ppanel-user-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fuser&skippable-integrations=1
[ppanel-admin-web-github]: https://github.com/perfect-panel/ppanel-web/tree/main/apps/admin
[ppanel-admin-web-cover]: https://urlscan.io/liveshot/?width=1920&height=1080&url=https://admin.ppanel.dev
[ppanel-admin-web-preview]: https://admin.ppanel.dev
[ppanel-admin-web-deploy]: https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fadmin.ppanel.dev&demo-title=PPanel%20Admin%20Web&demo-url=https%3A%2F%2Fadmin.ppanel.dev%2F&from=.&project-name=ppanel-admin-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fadmin&skippable-integrations=1

12
apps/admin/.cta.json Normal file
View File

@ -0,0 +1,12 @@
{
"projectName": "apps/user",
"mode": "file-router",
"typescript": true,
"tailwind": true,
"packageManager": "bun",
"addOnOptions": {},
"git": true,
"version": 1,
"framework": "react-cra",
"chosenAddOns": ["biome", "tanstack-query", "table", "form", "shadcn"]
}

10
apps/admin/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
count.txt
.env
.nitro
.tanstack
.wrangler

35
apps/admin/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,35 @@
{
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
},
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[css]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit"
}
}

141
apps/admin/README.md Normal file
View File

@ -0,0 +1,141 @@
<a name="readme-top"></a>
<div align="center">
<img width="160" src="https://raw.githubusercontent.com/perfect-panel/ppanel-assets/refs/heads/main/logo.svg">
<h1>PPanel admin web</h1>
This is a PPanel admin web powered by PPanel
English
·
[Chinese](./README.zh-CN.md)
·
[Changelog](../../CHANGELOG.md)
·
[Report Bug][issues-link]
·
[Request Feature][issues-link]
<!-- SHIELD GROUP -->
[![][github-release-shield]][github-release-link]
[![][github-releasedate-shield]][github-releasedate-link]
[![][github-action-test-shield]][github-action-test-link]
[![][github-action-release-shield]][github-action-release-link]<br/>
[![][github-contributors-shield]][github-contributors-link]
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
[![][github-license-shield]][github-license-link]
![](https://urlscan.io/liveshot/?width=1920&height=1080&url=https://admin.ppanel.dev)
</div>
<details>
<summary><kbd>Table of contents</kbd></summary>
#### TOC
- [⌨️ Local Development](#-local-development)
- [🚀 Deploy on Vercel](#-deploy-on-vercel)
- [🤝 Contributing](#-contributing)
- [📝 License](#-license)
####
</details>
## ⌨️ Local Development
You can use Github Codespaces for online development:
[![][codespaces-shield]][codespaces-link]
You can use Gitpod for online development:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][gitpod-link]
or clone it for local development:
```bash
git clone https://github.com/perfect-panel/ppanel-web.git
cd ppanel-web
# Install dependencies
bun install
# Run the development server
cd apps/admin
bun dev
```
Open <http://localhost:3000> with your browser to see the result.
## 🚀 Deploy on Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fadmin.ppanel.dev&demo-title=PPanel%20Admin%20Web&demo-url=https%3A%2F%2Fadmin.ppanel.dev%2F&from=.&project-name=ppanel-admin-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fadmin&skippable-integrations=1)
The easiest way to deploy your Next.js app is to use the
[Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme)
from the creators of Next.js.
Check out our
[Next.js deployment documentation](https://nextjs.org/docs/deployment)
for more details.
## 🤝 Contributing
Contributions of all types are more than welcome,
if you're interested in contributing code, feel free to check out our GitHub
[Issues][github-issues-link] to get stuck in to show us what youre made of.
[![][pr-welcome-shield]][pr-welcome-link]
[![][contributors-contrib]][contributors-url]
<div align="right">
[![][back-to-top]](#readme-top)
</div>
---
## 📝 License
Copyright © 2024 [PPanel][profile-link]. <br />
This project is [GNU](../../LICENSE) licensed.
<!-- LINK GROUP -->
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square
[codespaces-link]: https://codespaces.new/perfect-panel/ppanel-web
[codespaces-shield]: https://github.com/codespaces/badge.svg
[contributors-contrib]: https://contrib.rocks/image?repo=perfect-panel/ppanel-web
[contributors-url]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-action-release-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/release.yml
[github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-action-test-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/test.yml
[github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-contributors-link]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-contributors-shield]: https://img.shields.io/github/contributors/perfect-panel/ppanel-web?color=c4f042&labelColor=black&style=flat-square
[github-forks-link]: https://github.com/perfect-panel/ppanel-web/network/members
[github-forks-shield]: https://img.shields.io/github/forks/perfect-panel/ppanel-web?color=8ae8ff&labelColor=black&style=flat-square
[github-issues-link]: https://github.com/perfect-panel/ppanel-web/issues
[github-issues-shield]: https://img.shields.io/github/issues/perfect-panel/ppanel-web?color=ff80eb&labelColor=black&style=flat-square
[github-license-link]: https://github.com/perfect-panel/ppanel-web/blob/master/LICENSE
[github-license-shield]: https://img.shields.io/github/license/perfect-panel/ppanel-web?color=white&labelColor=black&style=flat-square
[github-release-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-release-shield]: https://img.shields.io/github/v/release/perfect-panel/ppanel-web?style=flat-square&sort=semver&logo=github
[github-releasedate-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/perfect-panel/ppanel-web?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/perfect-panel/ppanel-web/network/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/perfect-panel/ppanel-web?color=ffcb47&labelColor=black&style=flat-square
[gitpod-link]: https://gitpod.io/#https://github.com/perfect-panel/ppanel-web
[issues-link]: https://github.com/perfect-panel/ppanel-web/issues/new/choose
[pr-welcome-link]: https://github.com/perfect-panel/ppanel-web/pulls
[pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/perfect-panel

141
apps/admin/README.zh-CN.md Normal file
View File

@ -0,0 +1,141 @@
<a name="readme-top"></a>
<div align="center">
<img width="160" src="https://raw.githubusercontent.com/perfect-panel/ppanel-assets/refs/heads/main/logo.svg">
<h1>PPanel 管理后台</h1>
这是由 PPanel 提供支持的 PPanel 管理后台
[英文](./README.md)
·
中文
·
[更新日志](../../CHANGELOG.md)
·
[报告问题][issues-link]
·
[请求功能][issues-link]
<!-- SHIELD GROUP -->
[![][github-release-shield]][github-release-link]
[![][github-releasedate-shield]][github-releasedate-link]
[![][github-action-test-shield]][github-action-test-link]
[![][github-action-release-shield]][github-action-release-link]<br/>
[![][github-contributors-shield]][github-contributors-link]
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
[![][github-license-shield]][github-license-link]
![](https://urlscan.io/liveshot/?width=1920&height=1080&url=https://admin.ppanel.dev)
</div>
<details>
<summary><kbd>目录</kbd></summary>
#### 目录
- [⌨️ 本地开发](#-本地开发)
- [🚀 在 Vercel 上部署](#-在-vercel-上部署)
- [🤝 贡献](#-贡献)
- [📝 许可证](#-许可证)
####
</details>
## ⌨️ 本地开发
您可以使用 Github Codespaces 进行在线开发:
[![][codespaces-shield]][codespaces-link]
您可以使用 Gitpod 进行在线开发:
[![在 Gitpod 中打开](https://gitpod.io/button/open-in-gitpod.svg)][gitpod-link]
或者克隆项目进行本地开发:
```bash
git clone https://github.com/perfect-panel/ppanel-web.git
cd ppanel-web
# 安装依赖
bun install
# 运行开发服务器
cd apps/admin
bun dev
```
在浏览器中打开 <http://localhost:3000> 查看结果。
## 🚀 在 Vercel 上部署
[![使用 Vercel 部署](https://vercel.com/button)](https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fadmin.ppanel.dev&demo-title=PPanel%20Admin%20Web&demo-url=https%3A%2F%2Fadmin.ppanel.dev%2F&from=.&project-name=ppanel-admin-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fadmin&skippable-integrations=1)
部署 Next.js 应用的最简单方式是使用
[ Vercel 平台](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme)
由 Next.js 的创建者提供支持。
查看我们的
[Next.js 部署文档](https://nextjs.org/docs/deployment)
获取更多详情。
## 🤝 贡献
欢迎各种类型的贡献,
如果您有兴趣贡献代码,请随时查看我们的 GitHub
[问题][github-issues-link] 来展示您的能力。
[![][pr-welcome-shield]][pr-welcome-link]
[![][contributors-contrib]][contributors-url]
<div align="right">
[![][back-to-top]](#readme-top)
</div>
---
## 📝 许可证
版权所有 © 2024 [PPanel][profile-link]。<br />
本项目使用 [GNU](./LICENSE) 许可证。
<!-- LINK GROUP -->
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square
[codespaces-link]: https://codespaces.new/perfect-panel/ppanel-web
[codespaces-shield]: https://github.com/codespaces/badge.svg
[contributors-contrib]: https://contrib.rocks/image?repo=perfect-panel/ppanel-web
[contributors-url]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-action-release-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/release.yml
[github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-action-test-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/test.yml
[github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-contributors-link]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-contributors-shield]: https://img.shields.io/github/contributors/perfect-panel/ppanel-web?color=c4f042&labelColor=black&style=flat-square
[github-forks-link]: https://github.com/perfect-panel/ppanel-web/network/members
[github-forks-shield]: https://img.shields.io/github/forks/perfect-panel/ppanel-web?color=8ae8ff&labelColor=black&style=flat-square
[github-issues-link]: https://github.com/perfect-panel/ppanel-web/issues
[github-issues-shield]: https://img.shields.io/github/issues/perfect-panel/ppanel-web?color=ff80eb&labelColor=black&style=flat-square
[github-license-link]: https://github.com/perfect-panel/ppanel-web/blob/master/LICENSE
[github-license-shield]: https://img.shields.io/github/license/perfect-panel/ppanel-web?color=white&labelColor=black&style=flat-square
[github-release-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-release-shield]: https://img.shields.io/github/v/release/perfect-panel/ppanel-web?style=flat-square&sort=semver&logo=github
[github-releasedate-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/perfect-panel/ppanel-web?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/perfect-panel/ppanel-web/network/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/perfect-panel/ppanel-web?color=ffcb47&labelColor=black&style=flat-square
[gitpod-link]: https://gitpod.io/#https://github.com/perfect-panel/ppanel-web
[issues-link]: https://github.com/perfect-panel/ppanel-web/issues/new/choose
[pr-welcome-link]: https://github.com/perfect-panel/ppanel-web/pulls
[pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/perfect-panel

View File

@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "../../packages/ui/src/styles/globals.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@workspace/ui/components",
"utils": "@workspace/ui/lib/utils",
"hooks": "@workspace/ui/hooks",
"lib": "@workspace/ui/lib",
"ui": "@workspace/ui/components"
},
"iconLibrary": "lucide"
}

View File

@ -0,0 +1,23 @@
import { defineConfig } from "i18next-cli";
/**
* i18next CLI configuration
*
* Used for extracting translation keys and managing translation files.
* Inherits language settings from the shared i18n configuration.
*
* @see https://github.com/i18next/i18next-cli
*/
export default defineConfig({
// Use supported languages from shared config
locales: ["en-US", "zh-CN"],
// Extraction configuration
extract: {
input: [
"src/**/*.{js,jsx,ts,tsx}",
"../../packages/ui/src/**/*.{js,jsx,ts,tsx}",
], // Source files to scan
output: "public/locales/{{language}}/{{namespace}}.json", // Output path template
},
});

35
apps/admin/index.html Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicons -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<!-- PWA -->
<link rel="manifest" href="/site.webmanifest">
<meta
name="theme-color"
content="#FFFFFF"
media="(prefers-color-scheme: light)"
>
<meta
name="theme-color"
content="#000000"
media="(prefers-color-scheme: dark)"
>
<!-- SEO -->
<meta name="description" content="Loading...">
<meta name="keywords" content="">
<title>Loading...</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

52
apps/admin/package.json Normal file
View File

@ -0,0 +1,52 @@
{
"name": "ppanel-admin-web",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3001",
"build": "vite build && tsc",
"serve": "vite preview",
"lint": "biome lint",
"check": "biome check",
"i18n:extract": "i18next-cli extract",
"i18n:sync": "i18next-cli sync",
"i18n:status": "i18next-cli status"
},
"dependencies": {
"@faker-js/faker": "^10.0.0",
"@lottiefiles/dotlottie-react": "^0.17.7",
"@noble/curves": "^2.0.1",
"@stripe/react-stripe-js": "^5.4.0",
"@stripe/stripe-js": "^8.5.2",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/react-devtools": "^0.7.0",
"@tanstack/react-form": "^1.0.0",
"@tanstack/react-router-devtools": "^1.132.0",
"@tanstack/react-table": "^8.21.2",
"@tanstack/router-plugin": "^1.132.0",
"@workspace/typescript-config": "workspace:*",
"@workspace/ui": "workspace:*",
"ahooks": "^3.9.6",
"date-fns": "^4.1.0",
"framer-motion": "^12.23.24",
"mlkem-wasm": "^0.0.7",
"motion": "^12.23.24",
"qrcode.react": "^4.2.0",
"radash": "^12.1.1",
"react-copy-to-clipboard": "^5.1.0",
"react-helmet-async": "^2.0.5",
"react-turnstile": "^1.1.4",
"zustand": "^5.0.8"
},
"devDependencies": {
"@tanstack/devtools-vite": "^0.3.11",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.2.0",
"@vitejs/plugin-react": "^5.0.4",
"jsdom": "^27.0.0",
"vite": "^7.1.7",
"vitest": "^3.0.5",
"web-vitals": "^5.1.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" fill="none"><path stroke="url(#a)" stroke-linecap="round" stroke-width="25" d="M162.779 118.828v105.346"/><path fill="url(#b)" fill-rule="evenodd" d="M175.279 38.443v97.01l39.384-16.222c6.384-2.629 13.69.414 16.319 6.797 2.629 6.383-.414 13.689-6.797 16.319l-29.869 12.303 39.758 21.079c6.099 3.234 8.422 10.8 5.188 16.899-3.234 6.099-10.8 8.422-16.899 5.188l-58.083-30.794-24.537 10.107c-.308.127-.617.24-.928.341L32.554 221.278l-17.265 7.118V86.468l-.007-.017 142.683-59.543 17.314-7.226v18.761Zm-39.745 113.338-95.245 39.267v-87.943l109.99-45.9v74.098l-35.34-18.737c-6.1-3.234-13.666-.911-16.9 5.188-3.233 6.1-.91 13.666 5.189 16.899l32.306 17.128Z" clip-rule="evenodd"/><defs><linearGradient id="a" x1="163.279" x2="163.279" y1="118.828" y2="224.174" gradientUnits="userSpaceOnUse"><stop stop-color="#2E58FF"/><stop offset="1" stop-color="#26D0FF"/></linearGradient><linearGradient id="b" x1="128.003" x2="105.241" y1="19.688" y2="214.91" gradientUnits="userSpaceOnUse"><stop stop-color="#02F"/><stop offset=".48" stop-color="#5EE7FF"/><stop offset=".975" stop-color="#FCD8FF"/></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,39 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"create": "Create",
"createAds": "Create Ad",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteSuccess": "Deleted successfully",
"deleteWarning": "Are you sure you want to delete this ad? This action cannot be undone.",
"disabled": "Disabled",
"edit": "Edit",
"editAds": "Edit Ad",
"enabled": "Enabled",
"form": {
"cancel": "Cancel",
"confirm": "Confirm",
"content": "Content",
"description": "Description",
"endTime": "End Time",
"enterDescription": "Enter description",
"enterEndTime": "Select end time",
"enterStartTime": "Select start time",
"enterTargetUrl": "Enter target URL",
"enterTitle": "Enter title",
"startTime": "Start Time",
"targetUrl": "Target URL",
"title": "Title",
"type": "Type",
"typeImage": "Image",
"typeVideo": "Video"
},
"status": "Status",
"targetUrl": "Target URL",
"title": "Title",
"type": "Type",
"updateSuccess": "Updated successfully",
"validityPeriod": "Validity Period"
}

View File

@ -0,0 +1,30 @@
{
"announcementList": "Announcement List",
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"content": "Content",
"create": "Create",
"createAnnouncement": "Create Announcement",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteDescription": "This action cannot be undone.",
"deleteSuccess": "Deleted successfully",
"edit": "Edit",
"editAnnouncement": "Edit Announcement",
"enable": "Enable",
"form": {
"cancel": "Cancel",
"confirm": "Confirm",
"content": "Content",
"title": "Title",
"titlePlaceholder": "Enter title"
},
"hide": "Hide",
"pinned": "Pinned",
"popup": "Popup",
"show": "Show",
"title": "Title",
"updatedAt": "Updated At",
"updateSuccess": "Updated successfully"
}

View File

@ -0,0 +1,179 @@
{
"apple": {
"clientId": "Service ID",
"clientIdDescription": "Apple Service ID, available from Apple Developer Portal",
"clientSecret": "Private Key",
"clientSecretDescription": "Private key content (.p8 file) for authenticating with Apple",
"description": "Authenticate users with Apple accounts",
"enable": "Enable",
"enableDescription": "When enabled, users can sign in with their Apple ID",
"keyId": "Key ID",
"keyIdDescription": "Your private key ID from Apple Developer Portal",
"redirectUri": "Redirect URL",
"redirectUriDescription": "API address for redirect URL after successful Apple authentication. Do not end with /",
"teamId": "Team ID",
"teamIdDescription": "Apple Developer Team ID",
"title": "Apple Sign-In"
},
"common": {
"cancel": "Cancel",
"save": "Save",
"saveFailed": "Save failed",
"saveSuccess": "Saved successfully"
},
"communicationMethods": "Communication Methods",
"device": {
"blockVirtualMachine": "Block Virtual Machine",
"blockVirtualMachineDescription": "Block virtual machine login, only allow real device",
"communicationKey": "Communication Key",
"communicationKeyDescription": "The key used for secure communication between application and server",
"description": "Authenticate users with device",
"enable": "Enable",
"enableDescription": "When enabled, users can sign in with device",
"enableSecurity": "Enable Security",
"enableSecurityDescription": "When enabled, application requests must carry communication key",
"showAds": "Show Ads",
"showAdsDescription": "When enabled, ads will be shown",
"title": "Device Sign-In"
},
"deviceAuthMethods": "Device Authentication Methods",
"email": {
"basicSettings": "Basic Settings",
"description": "Configure email authentication and templates",
"emailSuffixWhitelist": "Email Suffix Whitelist",
"emailSuffixWhitelistDescription": "Only allow emails from whitelisted domains",
"emailVerification": "Email Verification",
"emailVerificationDescription": "Require email verification for new users",
"enable": "Enable",
"enableDescription": "When enabled, users can sign in with email",
"expirationEmailTemplate": "Expiration Email Template",
"expirationTemplate": "Expiration Template",
"inputPlaceholder": "Please enter",
"maintenanceEmailTemplate": "Maintenance Email Template",
"maintenanceTemplate": "Maintenance Template",
"senderAddress": "Sender Address",
"senderAddressDescription": "The email address that appears in the From field",
"sendFailure": "Email send failed",
"sendSuccess": "Email sent successfully",
"sendTestEmail": "Send Test Email",
"sendTestEmailDescription": "Send a test email to verify your SMTP configuration",
"smtpAccount": "SMTP Account",
"smtpAccountDescription": "The SMTP authentication username",
"smtpEncryptionMethod": "SSL/TLS Encryption",
"smtpEncryptionMethodDescription": "Enable SSL/TLS encryption for SMTP connection",
"smtpPassword": "SMTP Password",
"smtpPasswordDescription": "The SMTP authentication password",
"smtpServerAddress": "SMTP Server Address",
"smtpServerAddressDescription": "The SMTP server hostname",
"smtpServerPort": "SMTP Server Port",
"smtpServerPortDescription": "The SMTP server port (usually 25, 465, or 587)",
"smtpSettings": "SMTP Settings",
"templateVariables": {
"code": {
"description": "Verification code"
},
"expire": {
"description": "Code expiration time"
},
"expireDate": {
"description": "Subscription expiration date"
},
"maintenanceDate": {
"description": "Maintenance date"
},
"maintenanceTime": {
"description": "Maintenance time"
},
"siteLogo": {
"description": "Site logo URL"
},
"siteName": {
"description": "Site name"
},
"title": "Template Variables",
"type": {
"conditionalSyntax": "Use conditional syntax to display different content",
"description": "Email type (1: Register, 2: Reset Password)"
}
},
"title": "Email Settings",
"trafficExceedEmailTemplate": "Traffic Exceed Email Template",
"trafficTemplate": "Traffic Template",
"verifyEmailTemplate": "Verify Email Template",
"verifyTemplate": "Verify Template",
"whitelistSuffixes": "Whitelist Suffixes",
"whitelistSuffixesDescription": "One domain suffix per line",
"whitelistSuffixesPlaceholder": "gmail.com, outlook.com"
},
"facebook": {
"clientId": "App ID",
"clientIdDescription": "Facebook App ID, available from Facebook Developer Portal",
"clientSecret": "App Secret",
"clientSecretDescription": "Facebook App Secret, available from Facebook Developer Portal",
"description": "Authenticate users with Facebook accounts",
"enable": "Enable",
"enableDescription": "When enabled, users can sign in with their Facebook account",
"title": "Facebook Sign-In"
},
"github": {
"clientId": "Client ID",
"clientIdDescription": "GitHub OAuth App Client ID, available from GitHub Developer Settings",
"clientSecret": "Client Secret",
"clientSecretDescription": "GitHub OAuth App Client Secret, available from GitHub Developer Settings",
"description": "Authenticate users with GitHub accounts",
"enable": "Enable",
"enableDescription": "When enabled, users can sign in with their GitHub account",
"title": "GitHub Sign-In"
},
"google": {
"clientId": "Client ID",
"clientIdDescription": "Google OAuth Client ID, available from Google Cloud Console",
"clientSecret": "Client Secret",
"clientSecretDescription": "Google OAuth Client Secret, available from Google Cloud Console",
"description": "Authenticate users with Google accounts",
"enable": "Enable",
"enableDescription": "When enabled, users can sign in with their Google account",
"title": "Google Sign-In"
},
"phone": {
"accessLabel": "Access Key",
"applyPlatform": "Apply",
"description": "Configure SMS authentication",
"enable": "Enable",
"enableTip": "When enabled, users can sign in with their phone number",
"endpointLabel": "Endpoint",
"phoneNumberLabel": "Phone Number",
"placeholders": {
"template": "Use {{code}} for verification code"
},
"platform": "SMS Platform",
"platformConfigTip": "Please enter {{key}}",
"platformTip": "Select SMS service provider",
"secretLabel": "Secret Key",
"sendFailed": "SMS send failed",
"sendSuccess": "SMS sent successfully",
"signNameLabel": "Sign Name",
"template": "Template",
"templateCodeLabel": "Template Code",
"templateTip": "Use {{code}} variable for the verification code",
"testSms": "Test SMS",
"testSmsPhone": "Phone number",
"testSmsTip": "Send a test SMS to verify configuration",
"title": "SMS Settings",
"whitelistAreaCode": "Whitelist Area Codes",
"whitelistAreaCodeTip": "Enter area codes separated by commas",
"whitelistValidation": "Whitelist Validation",
"whitelistValidationTip": "Only allow phone numbers with whitelisted area codes"
},
"socialAuthMethods": "Social Authentication Methods",
"telegram": {
"clientId": "Bot ID",
"clientIdDescription": "Telegram Bot ID, available from @BotFather",
"clientSecret": "Bot Token",
"clientSecretDescription": "Telegram Bot Token, available from @BotFather",
"description": "Authenticate users with Telegram accounts",
"enable": "Enable",
"enableDescription": "When enabled, users can sign in with their Telegram account",
"title": "Telegram Sign-In"
}
}

View File

@ -0,0 +1,38 @@
{
"get": "Get Code",
"login": {
"email": "Please enter a valid email address",
"emailPlaceholder": "Enter your email...",
"forgotPassword": "Forgot Password?",
"passwordPlaceholder": "Enter your password...",
"registerAccount": "Register Account",
"success": "Login successful!",
"title": "Login"
},
"logout": "Logout",
"register": {
"codePlaceholder": "Enter code...",
"email": "Please enter a valid email address",
"emailPlaceholder": "Enter your email...",
"existingAccount": "Already have an account?",
"invite": "Invitation Code (Optional)",
"message": "Registration is currently disabled",
"passwordMismatch": "Passwords do not match",
"passwordPlaceholder": "Enter your password...",
"repeatPasswordPlaceholder": "Enter password again...",
"success": "Registration successful!",
"switchToLogin": "Login",
"title": "Register",
"whitelist": "This email domain is not in the whitelist"
},
"reset": {
"codePlaceholder": "Enter code...",
"email": "Please enter a valid email address",
"emailPlaceholder": "Enter your email...",
"existingAccount": "Remember your password?",
"passwordPlaceholder": "Enter your new password...",
"success": "Password reset successful!",
"switchToLogin": "Login",
"title": "Reset Password"
}
}

View File

@ -0,0 +1,65 @@
{
"empty": {
"tips": {
"0": "Imagine this space filled with exciting content! For now, you'll have to use your imagination...",
"1": "This area mysteriously disappeared, but we're summoning it back!",
"2": "Oh no, nothing happened... Feel free to fill in the blank!",
"3": "It's like discovering an empty stage at a concert... Why not go up and perform?",
"4": "You've found a blank canvas! How about building a house?",
"5": "This area is currently empty, but creativity starts here!",
"6": "Nothing here... but don't worry, it's just the beginning!",
"7": "This place was supposed to have a big surprise, but the surprise slipped away!",
"8": "There's nothing here for now, like an empty snack cabinet.",
"9": "This empty space is waiting for its protagonist to take the stage!"
}
},
"error": {
"400": "The request parameters are incorrect, please check and resubmit.",
"401": "Request is too frequent, please try again later.",
"500": "The server is having some issues, please try again later.",
"10001": "Query was not successful, please try again later or check your conditions.",
"10002": "Update operation was not successful, please try again later.",
"10003": "Insert operation cannot be completed at the moment, please try again later.",
"10004": "Deletion operation could not be completed, please try again later.",
"20001": "The user information already exists, please retry with different information.",
"20002": "User not found, please check the information and try again.",
"20003": "Incorrect password, please re-enter.",
"20004": "The user is disabled, please contact customer service if you have questions.",
"20005": "Insufficient balance, please recharge and try again.",
"20006": "The registration function is temporarily unavailable, please try again later.",
"20008": "User information is incorrect, please check and try again.",
"30001": "The node already exists, please do not add it again.",
"30002": "Related node not found, please check and try again.",
"30003": "Group already exists, please try using a different name.",
"30004": "Group not found, please verify the information and try again.",
"30005": "There is still content in the group, please clear it and try again.",
"40002": "Valid Token not found, please log in before retrying.",
"40003": "Current Token is invalid, please reacquire before trying again.",
"40004": "Token has expired, please log in again.",
"40005": "You do not have access permission, please contact the administrator if you have any questions.",
"50001": "Corresponding coupon information not found, please check and try again.",
"50002": "The coupon has been used, cannot be used again.",
"60001": "Subscription has expired, please renew before using.",
"60002": "Unable to use the subscription at the moment, please try again later.",
"60003": "An existing subscription is detected. Please cancel it before proceeding.",
"60004": "Unable to delete at the moment as the subscription has active users.",
"60005": "Single subscription mode has exceeded user limit",
"70001": "Incorrect verification code, please re-enter.",
"80001": "Task was not successfully queued, please try again later.",
"90001": "Please disable DEBUG mode and try again.",
"90015": "This account has reached the limit of sending times today, please try again tomorrow.",
"unknown": "An error occurred in the system, please try again later."
},
"language": "Language",
"pagination": {
"pageInfo": "Page {{page}} of {{total}}",
"rowsPerPage": "Rows per page"
},
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"toggle": "Toggle theme"
},
"unlimited": "unlimited"
}

View File

@ -0,0 +1,47 @@
{
"amount": "Amount",
"cancel": "Cancel",
"code": "Code",
"confirm": "Confirm",
"confirmDelete": "Are you sure you want to delete?",
"count": "Count",
"create": "Create",
"createCoupon": "Create Coupon",
"createSuccess": "Create Success",
"delete": "Delete",
"deleteSuccess": "Delete Success",
"deleteWarning": "Once deleted, data cannot be recovered. Please proceed with caution.",
"discount": "Discount",
"edit": "Edit",
"editCoupon": "Edit Coupon",
"enable": "Enable",
"form": {
"amountDiscount": "Amount Discount",
"cancel": "Cancel",
"confirm": "Confirm",
"count": "Max Usage Count",
"countPlaceholder": "Max Usage Count (leave blank for no limit)",
"customCouponCode": "Custom Coupon Code",
"customCouponCodePlaceholder": "Custom Coupon Code (leave blank for auto-generation)",
"enterCouponName": "Enter Coupon Name",
"enterValue": "Enter Value",
"expireTime": "Expire Time",
"name": "Name",
"percentageDiscount": "Percentage Discount",
"selectServer": "Select Subscription",
"specifiedServer": "Specified Subscription",
"startTime": "Start Time",
"type": "Coupon Type",
"userLimit": "Max Usage Count per User",
"userLimitPlaceholder": "Max Usage Count per User (leave blank for no limit)"
},
"name": "Name",
"percentage": "Percentage",
"remainingTimes": "Remaining",
"subscribe": "Subscribe",
"type": "Type",
"unlimited": "Unlimited",
"updateSuccess": "Update Success",
"usedTimes": "Usage Times",
"validityPeriod": "Validity Period"
}

View File

@ -0,0 +1,33 @@
{
"billing": {
"description": "Sponsoring helps PPanel to continue releasing updates!",
"title": "Sponsor"
},
"currentlyOnline": "Currently Online",
"month": "Month",
"monthTraffic": "Month Traffic",
"newPurchase": "New Purchase",
"nodes": "Nodes",
"nodeTraffic": "Node Traffic",
"offline": "Offline",
"online": "Online",
"onlineUsersCount": "Online Users",
"pending": "Pending",
"pendingTickets": "Pending Tickets",
"register": "Register",
"repurchase": "Repurchase",
"revenueTitle": "Revenue Statistics",
"selectTypePlaceholder": "Select Type",
"today": "Today",
"todayTraffic": "Today Traffic",
"total": "Total",
"totalIncome": "Total Income",
"totalServers": "Total Servers",
"traffic": "Traffic",
"trafficRank": "Traffic Rank",
"type": "Type",
"users": "Users",
"userTitle": "User Statistics",
"userTraffic": "User Traffic",
"yesterday": "Yesterday"
}

View File

@ -0,0 +1,28 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"create": "Create",
"createDocument": "Create Document",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteDescription": "Are you sure you want to delete this document? This action cannot be undone.",
"deleteSuccess": "Deleted successfully",
"DocumentList": "Document List",
"edit": "Edit",
"editDocument": "Edit Document",
"form": {
"cancel": "Cancel",
"confirm": "Confirm",
"content": "Content",
"tags": "Tags",
"tagsPlaceholder": "Enter tags",
"title": "Title",
"titlePlaceholder": "Enter document title"
},
"show": "Show",
"tags": "Tags",
"title": "Title",
"updatedAt": "Updated At",
"updateSuccess": "Updated successfully"
}

View File

@ -0,0 +1,66 @@
{
"column": {
"amount": "Amount",
"balance": "Balance",
"content": "Content",
"date": "Date",
"download": "Download",
"identifier": "Identifier",
"ip": "IP",
"orderNo": "Order No.",
"platform": "Platform",
"remark": "Remark",
"server": "Server",
"serverId": "Server ID",
"status": "Status",
"subject": "Subject",
"subscribe": "Subscribe",
"subscribeId": "Subscribe ID",
"success": "Success",
"time": "Time",
"to": "To",
"total": "Total",
"type": "Type",
"upload": "Upload",
"user": "User",
"userAgent": "User Agent",
"userId": "User ID"
},
"detail": "Detail",
"failed": "Failed",
"sent": "Sent",
"success": "Success",
"title": {
"balance": "Balance Log",
"commission": "Commission Log",
"email": "Email Log",
"gift": "Gift Log",
"login": "Login Log",
"mobile": "SMS Log",
"register": "Register Log",
"resetSubscribe": "Reset Subscribe Log",
"serverTraffic": "Server Traffic Log",
"subscribe": "Subscribe Log",
"subscribeTraffic": "Subscribe Traffic Log",
"trafficDetails": "Traffic Details"
},
"type": {
"231": "Auto Reset",
"232": "Advance Reset",
"233": "Paid Reset",
"321": "Recharge",
"322": "Withdraw",
"323": "Payment",
"324": "Refund",
"325": "Reward",
"326": "Admin Adjust",
"331": "Purchase",
"332": "Renewal",
"333": "Refund",
"334": "Withdraw",
"335": "Admin Adjust",
"341": "Increase",
"342": "Reduce"
},
"unknown": "Unknown"
}

View File

@ -0,0 +1,107 @@
{
"additional": "Additional",
"additionalRecipientEmails": "Additional recipient emails",
"additionalRecipients": "Additional Recipients",
"additionalRecipientsDescription": "These emails will receive the broadcast in addition to the user filter above",
"allUsers": "All Users",
"cancel": "Cancel",
"cannotBeEmpty": "cannot be empty",
"completed": "Completed",
"content": "Email Content",
"createAndSendQuotaTasks": "Create and Distribute Quota Tasks",
"createBroadcast": "Create Broadcast",
"createdAt": "Created At",
"createNewEmailBroadcastCampaign": "Create new email broadcast campaign",
"createQuotaTask": "Create Quota Task",
"dailyLimit": "Daily limit must be at least 1",
"dailySendLimit": "Daily Send Limit",
"days": "Days",
"emailAddedToScheduledQueue": "Email added to scheduled send queue",
"emailBroadcast": "Email Broadcast",
"emailBroadcastTaskCreatedSuccessfully": "Email broadcast task created successfully",
"emailBroadcastTasks": "Email Broadcast Tasks",
"emailContent": "Email Content",
"emailInterval": "Email Interval (seconds)",
"emailIntervalMinimum": "Email interval must be at least 0.1 seconds",
"emailMarketing": "Email Marketing",
"emailTaskManager": "Email Task Manager",
"endTime": "End Time",
"enterAmount": "Enter amount",
"enterPercentage": "Enter percentage",
"estimatedRecipients": "Estimated recipients",
"expiredSubscriptionUsersOnly": "Expired subscription users only",
"expiredUsers": "Expired Users",
"failedToCalculateRecipients": "Failed to calculate recipients",
"failedToCreateQuotaTask": "Failed to create quota task",
"failedToStopTask": "Failed to stop task",
"fixedAmount": "Fixed Amount",
"giftAmount": "Gift Amount",
"giftType": "Gift Amount Type",
"includeSubscriptionsValidAfter": "Include subscriptions valid on or after this date",
"includeSubscriptionsValidBefore": "Include subscriptions valid on or before this date",
"includeUsersRegisteredAfter": "Include users registered on or after this date",
"includeUsersRegisteredBefore": "Include users registered on or before this date",
"inProgress": "In Progress",
"intervalTimeBetweenEmails": "Interval time between each email",
"leaveEmptyForImmediateSend": "Leave empty for immediate send",
"maximumNumberPerDay": "Maximum number of emails to send per day",
"no": "No",
"nonSubscribers": "Non-subscribers",
"noSubscriptions": "No Subscriptions",
"noSubscriptionUsersOnly": "No subscription users only",
"noTimeLimit": "No Time Limit",
"notStarted": "Not Started",
"numberOfDaysForTheQuota": "Number of days to extend subscription expiration",
"onePerLine": "one per line",
"percentageAmount": "Percentage Amount",
"percentageAmountDescription": "Gift percentage amount based on current package price",
"pleaseEnter": "Please enter",
"pleaseEnterValidEmailAddresses": "Please enter valid email addresses, one per line",
"pleaseSelectSubscribers": "Please select packages",
"processing": "Processing...",
"progress": "Progress",
"quotaBroadcast": "Quota Distribution",
"quotaDays": "Extend Expiration Days",
"quotaService": "Quota Service",
"quotaTaskCreatedSuccessfully": "Quota task created successfully",
"quotaTaskManager": "Quota Task Manager",
"quotaTasks": "Quota Tasks",
"recipientType": "Recipient Type",
"registrationEndDate": "Registration End Date",
"registrationStartDate": "Registration Start Date",
"resetTraffic": "Reset Traffic",
"resetTrafficDescription": "Whether to reset subscription used traffic",
"scheduledSend": "Schedule Send",
"scheduledSendTimeMustBeLater": "Scheduled send time must be later than current time",
"scheduleSend": "Schedule Send",
"scope": "Send Scope",
"selectSendScope": "Select send scope",
"selectSendTime": "Select send time, leave empty for immediate send",
"selectValidSubscriptionsOnly": "Select currently valid subscriptions only",
"sendFailed": "Send failed, please try again",
"sendNow": "Send Now",
"sendScope": "Send Scope",
"sendScopeDescription": "Choose the user scope for email sending. Select \"Additional emails only\" to send only to the email addresses filled below",
"sendSettings": "Send Settings",
"sendTime": "Send Time",
"specificUsers": "Specific Users",
"specificUsersOnly": "Additional emails only (skip platform users)",
"startTime": "Start Time",
"status": "Status",
"stop": "Stop",
"subject": "Email Subject",
"subscribedUsers": "Subscribed Users",
"subscribedUsersOnly": "Subscribed users only",
"subscribers": "Packages",
"subscriptionCount": "Subscription Count",
"subscriptionValidityEndDate": "Subscription Validity End Date",
"subscriptionValidityStartDate": "Subscription Validity Start Date",
"taskStoppedSuccessfully": "Task stopped successfully",
"timeRange": "Time Range",
"useMarkdownEditor": "Use Markdown editor to write email content with preview functionality",
"users": "users",
"validOnly": "Valid Only",
"viewAndManageEmailBroadcastTasks": "View and manage email broadcast tasks",
"viewAndManageQuotaTasks": "View and manage quota tasks",
"yes": "Yes"
}

View File

@ -0,0 +1,35 @@
{
"ADS Config": "ADS Config",
"Announcement Management": "Announcement Management",
"Auth Control": "Auth Control",
"Balance": "Balance",
"Commerce": "Commerce",
"Commission": "Commission",
"Coupon Management": "Coupon Management",
"Dashboard": "Dashboard",
"Document Management": "Document Management",
"Email": "Email",
"Gift": "Gift",
"Login": "Login",
"Logs & Analytics": "Logs & Analytics",
"Maintenance": "Maintenance",
"Marketing Management": "Marketing Management",
"Mobile": "Mobile",
"Node Management": "Node Management",
"Order Management": "Order Management",
"Payment Config": "Payment Config",
"Product Management": "Product Management",
"Register": "Register",
"Reset Subscribe": "Reset Subscribe",
"Server Management": "Server Management",
"Server Traffic": "Server Traffic",
"Subscribe": "Subscribe",
"Subscribe Config": "Subscribe Config",
"Subscribe Traffic": "Subscribe Traffic",
"System": "System",
"System Config": "System Config",
"Ticket Management": "Ticket Management",
"Traffic Details": "Traffic Details",
"User Management": "User Management",
"Users & Support": "Users & Support"
}

View File

@ -0,0 +1,31 @@
{
"address": "Address",
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDeleteDesc": "This action cannot be undone.",
"confirmDeleteTitle": "Delete this node?",
"copied": "Copied",
"copy": "Copy",
"create": "Create",
"created": "Created",
"delete": "Delete",
"deleted": "Deleted",
"drawerCreateTitle": "Create Node",
"drawerEditTitle": "Edit Node",
"edit": "Edit",
"enabled": "Enabled",
"enabled_off": "Disabled",
"enabled_on": "Enabled",
"name": "Name",
"pageTitle": "Nodes",
"port": "Port",
"protocol": "Protocol",
"select_protocol": "Select protocol…",
"select_server": "Select server…",
"server": "Server",
"sorted_success": "Sorted successfully",
"tags": "Tags",
"tags_description": "Permission grouping tag (incl. plan binding and delivery policies).",
"tags_placeholder": "Use Enter or comma (,) to add multiple tags",
"updated": "Updated"
}

View File

@ -0,0 +1,29 @@
{
"amount": "Amount",
"couponDiscount": "Coupon Discount",
"discount": "Discount Amount",
"feeAmount": "Fee Amount",
"method": "Payment Method",
"orderNumber": "Order Number",
"status": {
"0": "Status",
"1": "Pending",
"2": "Paid",
"3": "Cancelled",
"4": "Closed",
"5": "Completed"
},
"subscribe": "Subscribe",
"subscribePrice": "Subscription Price",
"total": "Total",
"tradeNo": "Transaction Number",
"type": {
"0": "Type",
"1": "New Purchase",
"2": "Renewal",
"3": "Reset Traffic",
"4": "Recharge"
},
"updateTime": "Update Time",
"user": "User"
}

View File

@ -0,0 +1,39 @@
{
"applyForPayment": "Apply for Payment",
"batchDelete": "Batch Delete",
"cancel": "Cancel",
"configPlaceholder": "Please fill in the provided {{field}} configuration",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"copy": "Copy",
"copySuccess": "Copied successfully",
"create": "Add Payment Method",
"createPayment": "Add Payment Method",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteSuccess": "Deleted successfully",
"deleteWarning": "Are you sure you want to delete this payment method? This action cannot be undone.",
"description": "Description",
"domain": "Domain",
"edit": "Edit",
"editPayment": "Edit Payment Method",
"enable": "Enable",
"feeAmount": "Fixed Amount",
"feePercent": "Fee Percentage",
"fixedFee": "Fixed Amount",
"handlingFee": "Handling Fee",
"icon": "Icon",
"iconPlaceholder": "Enter icon URL",
"name": "Name",
"namePlaceholder": "Enter payment method name",
"nameRequired": "Name is required",
"noFee": "No Fee",
"notify_url": "Notify URL",
"paymentManagement": "Payment Management",
"percentFee": "Percentage",
"platform": "Platform",
"searchPlaceholder": "Enter search terms",
"selectPlatform": "Select Platform",
"submit": "Submit",
"updateSuccess": "Updated successfully"
}

View File

@ -0,0 +1,73 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Are you sure you want to delete?",
"copy": "Copy",
"copySuccess": "Copied successfully",
"create": "Create",
"createSubscribe": "Create Subscription",
"createSuccess": "Create Successful",
"delete": "Delete",
"deleteSuccess": "Delete Successful",
"deleteWarning": "Data cannot be recovered after deletion. Please proceed with caution.",
"deviceLimit": "Device Limit/Unit",
"edit": "Edit",
"editSubscribe": "Edit Subscription",
"form": {
"annualReset": "Annual Reset",
"basic": "Basic",
"cancel": "Cancel",
"confirm": "Confirm",
"Day": "Day",
"deductionRatio": "Automatic/Manual Deduction Configuration",
"deductionRatioDescription": "Used for deduction. By default, the system adopts an automatic calculation algorithm. When a manual ratio is provided, the system calculates proportions based on the time and traffic ratio, ensuring the total equals 100%.",
"description": "Description",
"deviceLimit": "Device Limit",
"discount": "Discount",
"discount_price": "Discount Price",
"discountDescription": "Set discount based on unit price",
"discountPercent": "Discount Percentage",
"Hour": "Hour",
"inventory": "Subscription Limit",
"language": "Language",
"languageDescription": "Leave empty for default without language restriction",
"languagePlaceholder": "Language identifier for the subscription, e.g., en-US, zh-CN",
"Minute": "Minute",
"Month": "Month",
"monthlyReset": "Monthly Reset",
"name": "Name",
"node": "Node",
"nodeGroup": "Node Group",
"nodes": "Nodes",
"noLimit": "No Limit",
"NoLimit": "No Limit",
"noReset": "No Reset",
"pricing": "Pricing",
"purchaseWithDiscount": "Allow Deduction",
"purchaseWithDiscountDescription": "Enable or disable unsubscribe functionality. After activation, the system will perform deduction processing according to the configured rules and proportions, and the remaining value will be returned to the balance",
"quota": "Purchase Limit",
"renewalReset": "Renewal Reset",
"renewalResetDescription": "Reset cycle upon renewal",
"replacement": "Reset Price (per time)",
"resetCycle": "Reset Cycle",
"resetOn1st": "Reset on the 1st",
"selectResetCycle": "Please select a reset cycle",
"selectUnitTime": "Please select a unit of time",
"speedLimit": "Speed Limit ",
"traffic": "Traffic",
"unitPrice": "Unit Price",
"unitTime": "Unit Time",
"Year": "Year"
},
"inventory": "Subscription Limit",
"language": "Language",
"name": "Name",
"quota": "Purchase Limit/Time",
"replacement": "Reset Price/Time",
"sell": "Sell",
"show": "Display",
"sold": "Subscription Count",
"traffic": "Traffic",
"unitPrice": "Unit Price",
"updateSuccess": "Update Successful"
}

View File

@ -0,0 +1,156 @@
{
"actions": {
"cancel": "Cancel",
"save": "Save"
},
"address": "Address",
"address_placeholder": "Server address",
"apiHost": "API Host",
"apiHostPlaceholder": "http(s)://example.com",
"bandwidth_placeholder": "Enter bandwidth, leave empty for BBR",
"basic": "Basic Configuration",
"cancel": "Cancel",
"cert_dns_env": "DNS Environment Variables",
"cert_dns_provider": "DNS Provider",
"cert_mode": "Certificate Mode",
"cipher": "Encryption Algorithm",
"city": "City",
"close": "Close",
"confirm": "Confirm",
"confirmDeleteDesc": "This action cannot be undone.",
"confirmDeleteTitle": "Delete this server?",
"congestion_controller": "Congestion Controller",
"connect": "Connect",
"copied": "Copied",
"copy": "Copy",
"copyAndClose": "Copy and Close",
"copyFailed": "Copy failed",
"country": "Country",
"cpu": "CPU",
"create": "Create",
"created": "Created",
"delete": "Delete",
"deleted": "Deleted",
"disable_sni": "Disable SNI",
"disabled": "Disabled",
"disk": "Disk",
"down_mbps": "Download Bandwidth",
"drawerCreateTitle": "Create Server",
"drawerEditTitle": "Edit Server",
"edit": "Edit",
"enabled": "Enabled",
"encryption": "Encryption",
"encryption_client_padding": "Client Padding",
"encryption_mode": "Mode",
"encryption_password": "Password",
"encryption_password_placeholder": "Leave empty for auto-generation",
"encryption_private_key": "Private Key",
"encryption_private_key_placeholder": "Leave empty for auto-generation",
"encryption_rtt": "RTT",
"encryption_server_padding": "Server Padding",
"encryption_ticket": "Ticket Time",
"expired": "Expired",
"expireTime": "Expire Time",
"extra": "Extra",
"flow": "Flow",
"generate_quantum_resistant_key": "Generate Quantum-Resistant Key",
"generate_standard_encryption_key": "Generate Standard Key",
"hop_interval": "Hop Interval",
"hop_ports": "Hop Ports",
"hop_ports_placeholder": "e.g. 1-65535",
"host": "Host",
"id": "ID",
"installCommand": "Install command",
"ipAddresses": "IP Addresses",
"memory": "Memory",
"mode": "Mode",
"multiplex": "Multiplex",
"name": "Name",
"notAvailable": "Not Available",
"obfs": "Obfuscation",
"obfs_password": "Obfuscation Password",
"obfs_password_placeholder": "Enter obfuscation password",
"offline": "Offline",
"oneClickInstall": "One-click Install",
"online": "Online",
"onlineUsers": "Online Users",
"padding_scheme": "Padding Scheme",
"padding_scheme_placeholder": "One padding rule per line, format: stop=8, 0=30-30",
"pageTitle": "Servers",
"path": "Path",
"port": "Port",
"protocol_configurations": "Protocol Configurations",
"protocol_configurations_desc": "Enable and configure the required protocol types",
"protocols": "Protocols",
"reality": "Reality",
"reduce_rtt": "Reduce RTT",
"security": "Security",
"security_allow_insecure": "Allow Insecure",
"security_fingerprint": "Fingerprint",
"security_private_key": "Reality Private Key",
"security_private_key_placeholder": "Enter private key",
"security_public_key": "Reality Public Key",
"security_public_key_placeholder": "Enter public key",
"security_server_address": "Reality Server Address",
"security_server_address_placeholder": "e.g. 1.2.3.4 or domain",
"security_server_port": "Reality Server Port",
"security_short_id": "Reality Short ID",
"security_sni": "SNI",
"server_config": {
"description": "Manage node communication keys, pull/push intervals.",
"dynamic_multiplier": "Dynamic multiplier",
"dynamic_multiplier_desc": "Define time slots and multipliers to adjust traffic accounting.",
"fields": {
"block_rules_placeholder": "One domain rule per line",
"communication_key": "Communication key",
"communication_key_desc": "Used for node authentication.",
"communication_key_placeholder": "Please enter",
"dns_config": "DNS Configuration",
"dns_domains_placeholder": "One domain rule per line",
"dns_proto_placeholder": "Select type",
"end_time": "End time",
"ip_strategy": "IP Strategy",
"ip_strategy_desc": "Choose IP version preference for network connections",
"ip_strategy_ipv4": "Prefer IPv4",
"ip_strategy_ipv6": "Prefer IPv6",
"ip_strategy_placeholder": "Select IP strategy",
"multiplier": "Multiplier",
"node_pull_interval": "Node pull interval",
"node_pull_interval_desc": "How often the node pulls configuration (seconds).",
"node_push_interval": "Node push interval",
"node_push_interval_desc": "How often the node pushes stats (seconds).",
"outbound_address_placeholder": "Server address",
"outbound_name_placeholder": "Configuration name",
"outbound_password_placeholder": "Password (optional)",
"outbound_port_placeholder": "Port number",
"outbound_protocol_placeholder": "Select protocol",
"outbound_rules_placeholder": "One rule per line",
"reset": "Reset",
"start_time": "Start time",
"traffic_report_threshold": "Traffic Report Threshold",
"traffic_report_threshold_desc": "Set the minimum threshold for traffic reporting."
},
"saveSuccess": "Saved successfully",
"tabs": {
"basic": "Basic Configuration",
"block": "Block Rules",
"dns": "DNS Configuration",
"outbound": "Outbound Rules"
},
"title": "Node configuration"
},
"server_key": "Server Key",
"service_name": "Service Name",
"sorted_success": "Sorted successfully",
"status": "Status",
"subscribeId": "Subscribe ID",
"subscription": "Subscription",
"traffic": "Traffic",
"traffic_ratio": "Ratio",
"transport": "Transport",
"udp_relay_mode": "UDP Relay Mode",
"unlimited": "Unlimited",
"up_mbps": "Upload Bandwidth",
"updated": "Updated",
"user": "User"
}

View File

@ -0,0 +1,121 @@
{
"actions": {
"add": "Add",
"batchDelete": "Batch Delete",
"batchDeleteSuccess_one": "Successfully deleted {count} clients",
"batchDeleteSuccess_other": "Successfully deleted {count} clients",
"batchDeleteWarning_one": "Are you sure you want to delete the selected {count} clients?",
"batchDeleteWarning_other": "Are you sure you want to delete the selected {count} clients?",
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteFailed": "Delete failed",
"deleteSuccess": "Deleted successfully",
"deleteWarning": "This operation cannot be undone. Are you sure you want to delete this client?",
"edit": "Edit",
"save": "Save",
"saveFailed": "Save failed",
"update": "Update",
"updateSuccess": "Updated successfully"
},
"config": {
"description": "Manage subscription system settings",
"singleSubscriptionMode": "Single Subscription Mode",
"singleSubscriptionModeDescription": "Limit users to one active subscription. Existing subscriptions unaffected",
"subscriptionDomain": "Subscription Domain",
"subscriptionDomainDescription": "Custom domain for subscription links",
"subscriptionDomainPlaceholder": "Enter subscription domain, one per line",
"subscriptionPath": "Subscription Path",
"subscriptionPathDescription": "Custom path for subscription endpoints (better performance after system restart)",
"subscriptionPathPlaceholder": "Enter subscription path",
"title": "Subscription Configuration",
"updateError": "Update failed",
"updateSuccess": "Settings updated successfully",
"userAgentLimit": "{userAgent} Restriction",
"userAgentLimitDescription": "Enable access restrictions based on {userAgent}",
"userAgentList": "{userAgent} Whitelist",
"userAgentListDescription": "Allowed {userAgent} for subscription access, one per line. Configured application {userAgent} will be automatically included",
"userAgentListPlaceholder": "Enter allowed {userAgent}, one per line",
"wildcardResolution": "Wildcard Resolution",
"wildcardResolutionDescription": "Enable wildcard domain resolution for subscriptions"
},
"form": {
"addTitle": "Add Client",
"descriptions": {
"description": "Detailed client description",
"downloadLink": "platform download URL",
"icon": "Icon URL or base64 encoding",
"name": "Client display name",
"outputFormat": "Subscription configuration file format",
"scheme": {
"base64Encoding": "Base64 encoding",
"functions": "Supports functions:",
"jsonStringify": "JSON object to string",
"nameVariable": "site name",
"title": "URL Scheme template",
"urlEncoding": "URL encoding",
"urlVariable": "subscription URL",
"variables": "Supports variables:"
},
"template": {
"functions": "Template functions:",
"if": "conditional statements",
"nodes": "proxy nodes list",
"range": "iterate arrays",
"siteName": "site name",
"sprig": "Sprig function library (string processing, dates, etc.)",
"subscribeName": "subscription name",
"title": "Go Template Syntax",
"userInfo": "user info (traffic, expiry, etc.)",
"variables": "Available variables:"
},
"userAgentPrefix": "Client identifier for distinguishing different clients"
},
"editTitle": "Edit Client",
"fields": {
"description": "Description",
"icon": "Icon",
"name": "Name",
"outputFormat": "Output Format",
"scheme": "URL Scheme",
"template": "Subscription File Template"
},
"tabs": {
"basic": "Basic Info",
"download": "Downloads",
"template": "Templates"
}
},
"outputFormats": {
"base64": "Base64",
"conf": "CONF",
"json": "JSON",
"plain": "Plain Text",
"yaml": "YAML"
},
"protocol": {
"title": "Client Management"
},
"table": {
"columns": {
"default": "Default",
"description": "Description",
"name": "Client Name",
"outputFormat": "Output Format",
"supportedPlatforms": "Supported Platforms"
}
},
"templatePreview": {
"base64": {
"decodedContent": "Decoded Content",
"decodeError": "Base64 decode error",
"originalContent": "Original Content"
},
"failed": "Failed to load template",
"loading": "Loading...",
"preview": "Preview",
"title": "Template Preview"
}
}

View File

@ -0,0 +1,136 @@
{
"basicSettings": "Basic Settings",
"common": {
"cancel": "Cancel",
"save": "Save Settings",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful"
},
"currency": {
"accessKey": "API Key",
"accessKeyDescription": "Free exchange rate API key provided by {{url}}",
"accessKeyPlaceholder": "Enter API key",
"currencySymbol": "Currency Symbol",
"currencySymbolDescription": "Used for display purposes only; changing this will affect all currency units in the system",
"currencySymbolPlaceholder": "$",
"currencyUnit": "Currency Unit",
"currencyUnitDescription": "Used for display purposes only; changing this will affect all currency units in the system",
"currencyUnitPlaceholder": "USD",
"description": "Configure currency units, symbols, and exchange rate API settings",
"title": "Currency Configuration"
},
"invite": {
"description": "Configure user invitation and referral reward settings",
"forcedInvite": "Require Invitation to Register",
"forcedInviteDescription": "When enabled, users must register through an invitation link",
"inputPlaceholder": "Please enter",
"onlyFirstPurchase": "First Purchase Reward Only",
"onlyFirstPurchaseDescription": "When enabled, referrers only receive rewards for the first purchase by referred users",
"referralPercentage": "Referral Reward Percentage",
"referralPercentageDescription": "Percentage of reward given to referrers",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"title": "Invitation Settings"
},
"logCleanup": {
"autoClear": "Enable Auto Cleanup",
"autoClearDescription": "When enabled, the system will automatically clear expired log records",
"clearDays": "Retention Days",
"clearDaysDescription": "Number of days to retain logs; logs older than this will be cleaned up",
"clearDaysPlaceholder": "Enter retention days",
"description": "Configure automatic log cleanup rules and retention period",
"title": "Log Cleanup Settings"
},
"logSettings": "Log Settings",
"privacyPolicy": {
"description": "Edit and manage privacy policy content",
"title": "Privacy Policy"
},
"register": {
"day": "Day(s)",
"description": "Configure user registration related settings",
"enableTrial": "Enable Trial",
"enableTrialDescription": "When enabled, new users will receive a trial subscription upon registration",
"hour": "Hour(s)",
"inputPlaceholder": "Please enter",
"ipRegistrationLimit": "IP Registration Limit",
"ipRegistrationLimitDescription": "Limit the number of registrations from a single IP address",
"minute": "Minute(s)",
"month": "Month(s)",
"none": "None",
"registrationLimitCount": "Registration Limit Count",
"registrationLimitCountDescription": "Number of registrations allowed per IP within the limit period",
"registrationLimitExpire": "Limit Period",
"registrationLimitExpireDescription": "Duration for IP registration limit (minutes)",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"selectPlaceholder": "Please select",
"stopNewUserRegistration": "Stop New User Registration",
"stopNewUserRegistrationDescription": "When enabled, new user registration will be disabled",
"title": "Registration Settings",
"trialConfig": "Trial Configuration",
"trialConfigDescription": "Configure trial subscription, duration and time unit for new users upon registration",
"year": "Year(s)"
},
"site": {
"customData": "Custom Data",
"customDataDescription": "Custom data for website customization",
"customHtml": "Custom HTML",
"customHtmlDescription": "Custom HTML code to be injected into the bottom of the site's body tag",
"description": "Configure basic site information, logo, domain and other settings",
"keywords": "Keywords",
"keywordsDescription": "Used for SEO purposes",
"keywordsPlaceholder": "keyword1, keyword2, keyword3",
"logo": "Site Logo",
"logoDescription": "Used for displaying the logo in designated locations",
"logoPlaceholder": "Enter the URL of the logo, without ending with '/'",
"siteDesc": "Site Description",
"siteDescDescription": "Used for displaying the site description in designated locations",
"siteDescPlaceholder": "Enter site description",
"siteDomain": "Site Domain",
"siteDomainDescription": "Domain address of the current website, e.g., used in emails",
"siteDomainPlaceholder": "Please enter the domain address. For multiple domains, please enter one per line.",
"siteName": "Site Name",
"siteNameDescription": "Used for displaying the site name in designated locations",
"siteNamePlaceholder": "Enter site name",
"title": "Site Configuration"
},
"tos": {
"description": "Edit and manage terms of service content",
"title": "Terms of Service"
},
"userSecuritySettings": "User & Security",
"verify": {
"description": "Configure Turnstile CAPTCHA and verification settings",
"enableLoginVerify": "Enable Verification on Login",
"enableLoginVerifyDescription": "When enabled, users must pass human verification during login",
"enablePasswordVerify": "Enable Verification on Password Reset",
"enablePasswordVerifyDescription": "When enabled, users must pass human verification during password reset",
"enableRegisterVerify": "Enable Verification on Registration",
"enableRegisterVerifyDescription": "When enabled, users must pass human verification during registration",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"title": "Security Verification",
"turnstileSecret": "Turnstile Secret Key",
"turnstileSecretDescription": "Cloudflare Turnstile secret key for backend verification",
"turnstileSecretPlaceholder": "Enter Turnstile secret key",
"turnstileSiteKey": "Turnstile Site Key",
"turnstileSiteKeyDescription": "Cloudflare Turnstile site key for frontend verification",
"turnstileSiteKeyPlaceholder": "Enter Turnstile site key"
},
"verifyCode": {
"dailyLimit": "Daily Sending Limit",
"dailyLimitDescription": "Maximum number of verification codes each user can send per day",
"description": "Configure email verification code sending rules and limits",
"expireTime": "Verification Code Validity",
"expireTimeDescription": "Validity period of verification codes (seconds)",
"inputPlaceholder": "Please enter",
"interval": "Sending Interval",
"intervalDescription": "Minimum interval between two verification code sends (seconds)",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"seconds": "seconds",
"times": "time(s)",
"title": "Verification Code Settings"
}
}

View File

@ -0,0 +1,22 @@
{
"cancel": "Cancel",
"check": "Check",
"close": "Close",
"closeSuccess": "Closed successfully",
"closeWarning": "Once closed, the ticket cannot be operated on. Please proceed with caution.",
"confirm": "Confirm",
"confirmClose": "Are you sure you want to close?",
"inputPlaceholder": "Please enter your question, we will reply as soon as possible.",
"reply": "Reply",
"status": {
"0": "Status",
"1": "Pending Follow-up",
"2": "Pending Reply",
"3": "Resolved",
"4": "Closed"
},
"ticketList": "Ticket List",
"title": "Title",
"updatedAt": "Updated At",
"user": "User"
}

View File

@ -0,0 +1,14 @@
{
"cancel": "Cancel",
"confirmReboot": "Confirm Reboot",
"confirmSystemReboot": "Confirm System Reboot",
"newVersionAvailable": "New Version Available",
"rebootDescription": "Are you sure you want to reboot the system? This action cannot be undone.",
"rebooting": "Rebooting...",
"refreshLogs": "Refresh Logs",
"serverVersion": "Server Version",
"systemLogs": "System Logs",
"systemReboot": "System Reboot",
"systemServices": "System Services",
"webVersion": "Web Version"
}

View File

@ -0,0 +1,16 @@
{
"errors": {
"nameRequired": "Please enter a name",
"portRange": "Port must be between 1 and 65535",
"protocolRequired": "Please select a protocol",
"serverAddrRequired": "Please enter an entry address",
"serverRequired": "Please select a server"
},
"form": {
"validation": {
"nameRequired": "Client name is required",
"userAgentRequiredSuffix": "is required"
}
},
"tos": "tos"
}

View File

@ -0,0 +1,108 @@
{
"accountEnable": "Account Enable",
"add": "Add",
"administrator": "Administrator",
"areaCodePlaceholder": "Area code",
"authMethodsTitle": "Auth Methods",
"avatar": "Avatar",
"balance": "Balance",
"balanceLogs": "Balance Logs",
"balanceNotifications": "Balance Notifications",
"balancePlaceholder": "Enter balance",
"basicInfoTitle": "Basic Info",
"cancel": "Cancel",
"commission": "Commission",
"commissionLogs": "Commission Logs",
"commissionPlaceholder": "Enter commission",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"confirmOffline": "Confirm Offline",
"copySubscription": "Copy Subscription",
"copySuccess": "Copied successfully",
"create": "Create",
"createdAt": "Created At",
"createSubscription": "Create Subscription",
"createSuccess": "Created successfully",
"createUser": "Create User",
"delete": "Delete",
"deleteDescription": "This action cannot be undone.",
"deleteSubscriptionDescription": "This action cannot be undone.",
"deleteSuccess": "Deleted successfully",
"deviceLimit": "Device Limit",
"download": "Download",
"downloadTraffic": "Download Traffic",
"edit": "Edit",
"editSubscription": "Edit Subscription",
"enable": "Enable",
"expiredAt": "Expired At",
"expireTime": "expireTime",
"giftAmount": "Gift Amount",
"giftAmountPlaceholder": "Enter gift amount",
"giftLogs": "Gift Logs",
"invalidEmailFormat": "Invalid email format",
"inviteCode": "Invite Code",
"inviteCodePlaceholder": "Enter invite code",
"kickOfflineConfirm": "kickOfflineConfirm",
"kickOfflineSuccess": "Device kicked offline",
"lastSeen": "Last Seen",
"loading": "Loading...",
"loginLogs": "Login Logs",
"loginNotifications": "Login Notifications",
"loginStatus": "Login Status",
"manager": "Administrator",
"more": "More",
"notifySettingsTitle": "Notify Settings",
"offline": "Offline",
"online": "Online",
"onlineDevices": "Online Devices",
"onlyFirstPurchase": "First Purchase Only",
"orderList": "Order List",
"password": "Password",
"passwordPlaceholder": "Enter password",
"permanent": "Permanent",
"pleaseEnterEmail": "Enter email",
"referer": "Referer",
"refererId": "Referer ID",
"refererIdPlaceholder": "Enter referer ID",
"referralCode": "Referral Code",
"referralPercentage": "Referral Percentage",
"referralPercentagePlaceholder": "Enter percentage",
"referrerUserId": "Referrer User ID",
"remove": "Remove",
"resetLogs": "Reset Logs",
"resetTime": "Reset Time",
"save": "Save",
"speedLimit": "Speed Limit",
"startTime": "startTime",
"subscription": "Subscription",
"subscriptionId": "subscriptionId",
"subscriptionInfo": "subscriptionInfo",
"subscriptionList": "Subscription List",
"subscriptionLogs": "Subscription Logs",
"subscriptionName": "subscriptionName",
"subscriptionNotifications": "Subscription Notifications",
"telephone": "Phone",
"telephonePlaceholder": "Enter phone number",
"token": "token",
"totalTraffic": "Total Traffic",
"tradeNotifications": "Trade Notifications",
"trafficDetails": "Traffic Details",
"trafficLimit": "Traffic Limit",
"trafficStats": "Traffic Stats",
"trafficUsage": "trafficUsage",
"unlimited": "unlimited",
"unverified": "Unverified",
"update": "Update",
"updateSuccess": "Updated successfully",
"upload": "Upload",
"uploadTraffic": "Upload Traffic",
"userAgent": "User Agent",
"userEmail": "Email",
"userEmailPlaceholder": "Enter email",
"userId": "userId",
"userInfo": "userInfo",
"userList": "User List",
"userName": "Username",
"userProfile": "User Profile",
"verified": "Verified"
}

View File

@ -0,0 +1,39 @@
{
"cancel": "取消",
"confirm": "确认",
"confirmDelete": "确认删除",
"create": "创建",
"createAds": "创建广告",
"createSuccess": "创建成功",
"delete": "删除",
"deleteSuccess": "删除成功",
"deleteWarning": "确定要删除此广告吗?此操作无法撤销。",
"disabled": "已禁用",
"edit": "编辑",
"editAds": "编辑广告",
"enabled": "已启用",
"form": {
"cancel": "取消",
"confirm": "确认",
"content": "内容",
"description": "描述",
"endTime": "结束时间",
"enterDescription": "输入描述",
"enterEndTime": "选择结束时间",
"enterStartTime": "选择开始时间",
"enterTargetUrl": "输入目标链接",
"enterTitle": "输入标题",
"startTime": "开始时间",
"targetUrl": "目标链接",
"title": "标题",
"type": "类型",
"typeImage": "图片",
"typeVideo": "视频"
},
"status": "状态",
"targetUrl": "目标链接",
"title": "标题",
"type": "类型",
"updateSuccess": "更新成功",
"validityPeriod": "有效期"
}

View File

@ -0,0 +1,30 @@
{
"announcementList": "公告列表",
"cancel": "取消",
"confirm": "确认",
"confirmDelete": "确认删除",
"content": "内容",
"create": "创建",
"createAnnouncement": "创建公告",
"createSuccess": "创建成功",
"delete": "删除",
"deleteDescription": "此操作无法撤销。",
"deleteSuccess": "删除成功",
"edit": "编辑",
"editAnnouncement": "编辑公告",
"enable": "启用",
"form": {
"cancel": "取消",
"confirm": "确认",
"content": "内容",
"title": "标题",
"titlePlaceholder": "输入标题"
},
"hide": "隐藏",
"pinned": "置顶",
"popup": "弹窗",
"show": "显示",
"title": "标题",
"updatedAt": "更新时间",
"updateSuccess": "更新成功"
}

View File

@ -0,0 +1,179 @@
{
"apple": {
"clientId": "服务 ID",
"clientIdDescription": "Apple 服务 ID可从 Apple 开发者门户获取",
"clientSecret": "私钥",
"clientSecretDescription": "用于 Apple 身份验证的私钥内容(.p8 文件)",
"description": "使用 Apple 账户验证用户身份",
"enable": "启用",
"enableDescription": "启用后,用户可以使用 Apple ID 登录",
"keyId": "密钥 ID",
"keyIdDescription": "Apple 开发者门户中的私钥 ID",
"redirectUri": "重定向 URL",
"redirectUriDescription": "Apple 认证成功后重定向 URL 的 API 地址,不要以 / 结尾",
"teamId": "团队 ID",
"teamIdDescription": "Apple 开发者团队 ID",
"title": "Apple 登录"
},
"common": {
"cancel": "取消",
"save": "保存",
"saveFailed": "保存失败",
"saveSuccess": "保存成功"
},
"communicationMethods": "通讯方式",
"device": {
"blockVirtualMachine": "阻止虚拟机",
"blockVirtualMachineDescription": "阻止虚拟机登录,只允许真实设备",
"communicationKey": "通讯密钥",
"communicationKeyDescription": "用于应用程序与服务器之间安全通信的密钥",
"description": "使用设备验证用户身份",
"enable": "启用",
"enableDescription": "启用后,用户可以使用设备登录",
"enableSecurity": "启用安全验证",
"enableSecurityDescription": "启用后,应用请求必须携带通讯密钥",
"showAds": "显示广告",
"showAdsDescription": "启用后,将显示广告",
"title": "设备登录"
},
"deviceAuthMethods": "设备认证方式",
"email": {
"basicSettings": "基本设置",
"description": "配置邮箱认证和模板",
"emailSuffixWhitelist": "邮箱后缀白名单",
"emailSuffixWhitelistDescription": "只允许来自白名单域名的邮箱",
"emailVerification": "邮箱验证",
"emailVerificationDescription": "新用户需要邮箱验证",
"enable": "启用",
"enableDescription": "启用后,用户可以使用邮箱登录",
"expirationEmailTemplate": "到期邮件模板",
"expirationTemplate": "到期模板",
"inputPlaceholder": "请输入",
"maintenanceEmailTemplate": "维护邮件模板",
"maintenanceTemplate": "维护模板",
"senderAddress": "发件人地址",
"senderAddressDescription": "显示在发件人字段中的邮箱地址",
"sendFailure": "邮件发送失败",
"sendSuccess": "邮件发送成功",
"sendTestEmail": "发送测试邮件",
"sendTestEmailDescription": "发送测试邮件以验证您的 SMTP 配置",
"smtpAccount": "SMTP 账户",
"smtpAccountDescription": "SMTP 认证用户名",
"smtpEncryptionMethod": "SSL/TLS 加密",
"smtpEncryptionMethodDescription": "为 SMTP 连接启用 SSL/TLS 加密",
"smtpPassword": "SMTP 密码",
"smtpPasswordDescription": "SMTP 认证密码",
"smtpServerAddress": "SMTP 服务器地址",
"smtpServerAddressDescription": "SMTP 服务器主机名",
"smtpServerPort": "SMTP 服务器端口",
"smtpServerPortDescription": "SMTP 服务器端口(通常为 25、465 或 587",
"smtpSettings": "SMTP 设置",
"templateVariables": {
"code": {
"description": "验证码"
},
"expire": {
"description": "验证码过期时间"
},
"expireDate": {
"description": "订阅到期日期"
},
"maintenanceDate": {
"description": "维护日期"
},
"maintenanceTime": {
"description": "维护时间"
},
"siteLogo": {
"description": "网站 Logo URL"
},
"siteName": {
"description": "网站名称"
},
"title": "模板变量",
"type": {
"conditionalSyntax": "使用条件语法显示不同内容",
"description": "邮件类型1注册2重置密码"
}
},
"title": "邮箱设置",
"trafficExceedEmailTemplate": "流量超额邮件模板",
"trafficTemplate": "流量模板",
"verifyEmailTemplate": "验证邮件模板",
"verifyTemplate": "验证模板",
"whitelistSuffixes": "白名单后缀",
"whitelistSuffixesDescription": "每行一个域名后缀",
"whitelistSuffixesPlaceholder": "gmail.com, outlook.com"
},
"facebook": {
"clientId": "应用 ID",
"clientIdDescription": "Facebook 应用 ID可从 Facebook 开发者门户获取",
"clientSecret": "应用密钥",
"clientSecretDescription": "Facebook 应用密钥,可从 Facebook 开发者门户获取",
"description": "使用 Facebook 账户验证用户身份",
"enable": "启用",
"enableDescription": "启用后,用户可以使用 Facebook 账户登录",
"title": "Facebook 登录"
},
"github": {
"clientId": "客户端 ID",
"clientIdDescription": "GitHub OAuth 应用客户端 ID可从 GitHub 开发者设置获取",
"clientSecret": "客户端密钥",
"clientSecretDescription": "GitHub OAuth 应用客户端密钥,可从 GitHub 开发者设置获取",
"description": "使用 GitHub 账户验证用户身份",
"enable": "启用",
"enableDescription": "启用后,用户可以使用 GitHub 账户登录",
"title": "GitHub 登录"
},
"google": {
"clientId": "客户端 ID",
"clientIdDescription": "Google OAuth 客户端 ID可从 Google Cloud 控制台获取",
"clientSecret": "客户端密钥",
"clientSecretDescription": "Google OAuth 客户端密钥,可从 Google Cloud 控制台获取",
"description": "使用 Google 账户验证用户身份",
"enable": "启用",
"enableDescription": "启用后,用户可以使用 Google 账户登录",
"title": "Google 登录"
},
"phone": {
"accessLabel": "访问密钥",
"applyPlatform": "申请",
"description": "配置短信认证",
"enable": "启用",
"enableTip": "启用后,用户可以使用手机号登录",
"endpointLabel": "端点",
"phoneNumberLabel": "手机号",
"placeholders": {
"template": "使用 {{code}} 表示验证码"
},
"platform": "短信平台",
"platformConfigTip": "请输入 {{key}}",
"platformTip": "选择短信服务提供商",
"secretLabel": "密钥",
"sendFailed": "短信发送失败",
"sendSuccess": "短信发送成功",
"signNameLabel": "签名",
"template": "模板",
"templateCodeLabel": "模板代码",
"templateTip": "使用 {{code}} 变量表示验证码",
"testSms": "测试短信",
"testSmsPhone": "手机号",
"testSmsTip": "发送测试短信以验证配置",
"title": "短信设置",
"whitelistAreaCode": "区号白名单",
"whitelistAreaCodeTip": "输入区号,用逗号分隔",
"whitelistValidation": "白名单验证",
"whitelistValidationTip": "只允许白名单区号的手机号"
},
"socialAuthMethods": "社交账号认证方式",
"telegram": {
"clientId": "机器人 ID",
"clientIdDescription": "Telegram 机器人 ID可从 @BotFather 获取",
"clientSecret": "机器人令牌",
"clientSecretDescription": "Telegram 机器人令牌,可从 @BotFather 获取",
"description": "使用 Telegram 账户验证用户身份",
"enable": "启用",
"enableDescription": "启用后,用户可以使用 Telegram 账户登录",
"title": "Telegram 登录"
}
}

View File

@ -0,0 +1,38 @@
{
"get": "获取验证码",
"login": {
"email": "请输入有效的邮箱地址",
"emailPlaceholder": "输入您的邮箱...",
"forgotPassword": "忘记密码?",
"passwordPlaceholder": "输入您的密码...",
"registerAccount": "注册账号",
"success": "登录成功!",
"title": "登录"
},
"logout": "退出登录",
"register": {
"codePlaceholder": "输入验证码...",
"email": "请输入有效的邮箱地址",
"emailPlaceholder": "输入您的邮箱...",
"existingAccount": "已有账号?",
"invite": "邀请码(可选)",
"message": "注册功能暂时不可用",
"passwordMismatch": "两次密码输入不一致",
"passwordPlaceholder": "输入您的密码...",
"repeatPasswordPlaceholder": "再次输入密码...",
"success": "注册成功!",
"switchToLogin": "登录",
"title": "注册",
"whitelist": "该邮箱域名不在白名单中"
},
"reset": {
"codePlaceholder": "输入验证码...",
"email": "请输入有效的邮箱地址",
"emailPlaceholder": "输入您的邮箱...",
"existingAccount": "记得密码了?",
"passwordPlaceholder": "输入您的新密码...",
"success": "密码重置成功!",
"switchToLogin": "登录",
"title": "重置密码"
}
}

View File

@ -0,0 +1,65 @@
{
"empty": {
"tips": {
"0": "想象一下这里充满了精彩的内容!现在,你只能发挥你的想象力了...",
"1": "这个区域神秘消失了,但我们正在召唤它回来!",
"2": "哦不,什么都没发生... 随意填补空白吧!",
"3": "就像在音乐会上发现一个空舞台... 为什么不上去表演呢?",
"4": "你发现了一块空白画布!建个房子怎么样?",
"5": "这个区域目前是空的,但创造力从这里开始!",
"6": "这里什么都没有... 但别担心,这只是开始!",
"7": "这个地方本该有个大惊喜,但惊喜溜走了!",
"8": "暂时这里什么都没有,就像一个空的零食柜。",
"9": "这个空荡荡的空间正在等待它的主角登场!"
}
},
"error": {
"400": "请求参数不正确,请检查后重新提交。",
"401": "请求过于频繁,请稍后再试。",
"500": "服务器出现了一些问题,请稍后再试。",
"10001": "查询不成功,请稍后再试或检查查询条件。",
"10002": "更新操作不成功,请稍后再试。",
"10003": "插入操作暂时无法完成,请稍后再试。",
"10004": "删除操作无法完成,请稍后再试。",
"20001": "用户信息已存在,请使用不同的信息重试。",
"20002": "找不到用户,请检查信息后重试。",
"20003": "密码不正确,请重新输入。",
"20004": "用户已被禁用,如有疑问请联系客服。",
"20005": "余额不足,请充值后再试。",
"20006": "注册功能暂时不可用,请稍后再试。",
"20008": "用户信息不正确,请检查后重试。",
"30001": "该节点已存在,请勿重复添加。",
"30002": "找不到相关节点,请检查后重试。",
"30003": "分组已存在,请尝试使用不同的名称。",
"30004": "找不到分组,请验证信息后重试。",
"30005": "分组中还有内容,请清空后再试。",
"40002": "未找到有效的 Token请登录后再试。",
"40003": "当前 Token 无效,请重新获取后再试。",
"40004": "Token 已过期,请重新登录。",
"40005": "您没有访问权限,如有疑问请联系管理员。",
"50001": "找不到对应的优惠券信息,请检查后重试。",
"50002": "该优惠券已被使用,无法再次使用。",
"60001": "订阅已过期,请续费后使用。",
"60002": "暂时无法使用该订阅,请稍后再试。",
"60003": "检测到现有订阅,请先取消后再继续。",
"60004": "由于订阅有活跃用户,暂时无法删除。",
"60005": "单一订阅模式已超过用户限制",
"70001": "验证码不正确,请重新输入。",
"80001": "任务未成功加入队列,请稍后再试。",
"90001": "请禁用 DEBUG 模式后再试。",
"90015": "该账户今日已达到发送次数限制,请明天再试。",
"unknown": "系统发生错误,请稍后再试。"
},
"language": "语言",
"pagination": {
"pageInfo": "第 {{page}} 页,共 {{total}} 页",
"rowsPerPage": "每页行数"
},
"theme": {
"dark": "深色",
"light": "浅色",
"system": "系统",
"toggle": "切换主题"
},
"unlimited": "无限制"
}

View File

@ -0,0 +1,47 @@
{
"amount": "金额",
"cancel": "取消",
"code": "优惠码",
"confirm": "确认",
"confirmDelete": "确定要删除吗?",
"count": "数量",
"create": "创建",
"createCoupon": "创建优惠券",
"createSuccess": "创建成功",
"delete": "删除",
"deleteSuccess": "删除成功",
"deleteWarning": "删除后数据无法恢复,请谨慎操作。",
"discount": "折扣",
"edit": "编辑",
"editCoupon": "编辑优惠券",
"enable": "启用",
"form": {
"amountDiscount": "金额折扣",
"cancel": "取消",
"confirm": "确认",
"count": "最大使用次数",
"countPlaceholder": "最大使用次数(留空不限制)",
"customCouponCode": "自定义优惠码",
"customCouponCodePlaceholder": "自定义优惠码(留空自动生成)",
"enterCouponName": "输入优惠券名称",
"enterValue": "输入值",
"expireTime": "过期时间",
"name": "名称",
"percentageDiscount": "百分比折扣",
"selectServer": "选择订阅",
"specifiedServer": "指定订阅",
"startTime": "开始时间",
"type": "优惠券类型",
"userLimit": "每用户最大使用次数",
"userLimitPlaceholder": "每用户最大使用次数(留空不限制)"
},
"name": "名称",
"percentage": "百分比",
"remainingTimes": "剩余次数",
"subscribe": "订阅",
"type": "类型",
"unlimited": "无限制",
"updateSuccess": "更新成功",
"usedTimes": "使用次数",
"validityPeriod": "有效期"
}

View File

@ -0,0 +1,33 @@
{
"billing": {
"description": "赞助帮助 PPanel 继续发布更新!",
"title": "赞助"
},
"currentlyOnline": "当前在线",
"month": "月",
"monthTraffic": "本月流量",
"newPurchase": "新购",
"nodes": "节点",
"nodeTraffic": "节点流量",
"offline": "离线",
"online": "在线",
"onlineUsersCount": "在线用户数",
"pending": "待处理",
"pendingTickets": "待处理工单",
"register": "注册",
"repurchase": "续费",
"revenueTitle": "收入统计",
"selectTypePlaceholder": "选择类型",
"today": "今日",
"todayTraffic": "今日流量",
"total": "总计",
"totalIncome": "总收入",
"totalServers": "服务器总数",
"traffic": "流量",
"trafficRank": "流量排行",
"type": "类型",
"users": "用户",
"userTitle": "用户统计",
"userTraffic": "用户流量",
"yesterday": "昨日"
}

View File

@ -0,0 +1,28 @@
{
"cancel": "取消",
"confirm": "确认",
"confirmDelete": "确认删除",
"create": "创建",
"createDocument": "创建文档",
"createSuccess": "创建成功",
"delete": "删除",
"deleteDescription": "确定要删除此文档吗?此操作无法撤销。",
"deleteSuccess": "删除成功",
"DocumentList": "文档列表",
"edit": "编辑",
"editDocument": "编辑文档",
"form": {
"cancel": "取消",
"confirm": "确认",
"content": "内容",
"tags": "标签",
"tagsPlaceholder": "输入标签",
"title": "标题",
"titlePlaceholder": "输入文档标题"
},
"show": "显示",
"tags": "标签",
"title": "标题",
"updatedAt": "更新时间",
"updateSuccess": "更新成功"
}

View File

@ -0,0 +1,66 @@
{
"column": {
"amount": "金额",
"balance": "余额",
"content": "内容",
"date": "日期",
"download": "下载",
"identifier": "标识符",
"ip": "IP",
"orderNo": "订单号",
"platform": "平台",
"remark": "备注",
"server": "服务器",
"serverId": "服务器 ID",
"status": "状态",
"subject": "主题",
"subscribe": "订阅",
"subscribeId": "订阅 ID",
"success": "成功",
"time": "时间",
"to": "收件人",
"total": "总计",
"type": "类型",
"upload": "上传",
"user": "用户",
"userAgent": "用户代理",
"userId": "用户 ID"
},
"detail": "详情",
"failed": "失败",
"sent": "已发送",
"success": "成功",
"title": {
"balance": "余额日志",
"commission": "佣金日志",
"email": "邮件日志",
"gift": "赠送日志",
"login": "登录日志",
"mobile": "短信日志",
"register": "注册日志",
"resetSubscribe": "重置订阅日志",
"serverTraffic": "服务器流量日志",
"subscribe": "订阅日志",
"subscribeTraffic": "订阅流量日志",
"trafficDetails": "流量详情"
},
"type": {
"231": "自动重置",
"232": "提前重置",
"233": "付费重置",
"321": "充值",
"322": "提现",
"323": "支付",
"324": "退款",
"325": "奖励",
"326": "管理员调整",
"331": "购买",
"332": "续费",
"333": "退款",
"334": "提现",
"335": "管理员调整",
"341": "增加",
"342": "减少"
},
"unknown": "未知"
}

View File

@ -0,0 +1,107 @@
{
"additional": "附加",
"additionalRecipientEmails": "附加收件人邮箱",
"additionalRecipients": "附加收件人",
"additionalRecipientsDescription": "除上述用户筛选外,这些邮箱也将收到广播",
"allUsers": "所有用户",
"cancel": "取消",
"cannotBeEmpty": "不能为空",
"completed": "已完成",
"content": "邮件内容",
"createAndSendQuotaTasks": "创建并分发配额任务",
"createBroadcast": "创建广播",
"createdAt": "创建时间",
"createNewEmailBroadcastCampaign": "创建新的邮件广播活动",
"createQuotaTask": "创建配额任务",
"dailyLimit": "每日限制必须至少为 1",
"dailySendLimit": "每日发送限制",
"days": "天",
"emailAddedToScheduledQueue": "邮件已添加到定时发送队列",
"emailBroadcast": "邮件广播",
"emailBroadcastTaskCreatedSuccessfully": "邮件广播任务创建成功",
"emailBroadcastTasks": "邮件广播任务",
"emailContent": "邮件内容",
"emailInterval": "邮件间隔(秒)",
"emailIntervalMinimum": "邮件间隔必须至少为 0.1 秒",
"emailMarketing": "邮件营销",
"emailTaskManager": "邮件任务管理器",
"endTime": "结束时间",
"enterAmount": "输入金额",
"enterPercentage": "输入百分比",
"estimatedRecipients": "预计收件人",
"expiredSubscriptionUsersOnly": "仅订阅已过期用户",
"expiredUsers": "过期用户",
"failedToCalculateRecipients": "计算收件人失败",
"failedToCreateQuotaTask": "创建配额任务失败",
"failedToStopTask": "停止任务失败",
"fixedAmount": "固定金额",
"giftAmount": "赠送金额",
"giftType": "赠送金额类型",
"includeSubscriptionsValidAfter": "包含此日期及之后有效的订阅",
"includeSubscriptionsValidBefore": "包含此日期及之前有效的订阅",
"includeUsersRegisteredAfter": "包含此日期及之后注册的用户",
"includeUsersRegisteredBefore": "包含此日期及之前注册的用户",
"inProgress": "进行中",
"intervalTimeBetweenEmails": "每封邮件之间的间隔时间",
"leaveEmptyForImmediateSend": "留空立即发送",
"maximumNumberPerDay": "每日最大发送邮件数",
"no": "否",
"nonSubscribers": "未订阅用户",
"noSubscriptions": "无订阅",
"noSubscriptionUsersOnly": "仅无订阅用户",
"noTimeLimit": "无时间限制",
"notStarted": "未开始",
"numberOfDaysForTheQuota": "延长订阅到期的天数",
"onePerLine": "每行一个",
"percentageAmount": "百分比金额",
"percentageAmountDescription": "根据当前套餐价格计算赠送百分比金额",
"pleaseEnter": "请输入",
"pleaseEnterValidEmailAddresses": "请输入有效的邮箱地址,每行一个",
"pleaseSelectSubscribers": "请选择套餐",
"processing": "处理中...",
"progress": "进度",
"quotaBroadcast": "配额分发",
"quotaDays": "延长到期天数",
"quotaService": "配额服务",
"quotaTaskCreatedSuccessfully": "配额任务创建成功",
"quotaTaskManager": "配额任务管理器",
"quotaTasks": "配额任务",
"recipientType": "收件人类型",
"registrationEndDate": "注册结束日期",
"registrationStartDate": "注册开始日期",
"resetTraffic": "重置流量",
"resetTrafficDescription": "是否重置订阅已用流量",
"scheduledSend": "定时发送",
"scheduledSendTimeMustBeLater": "定时发送时间必须晚于当前时间",
"scheduleSend": "定时发送",
"scope": "发送范围",
"selectSendScope": "选择发送范围",
"selectSendTime": "选择发送时间,留空立即发送",
"selectValidSubscriptionsOnly": "仅选择当前有效订阅",
"sendFailed": "发送失败,请重试",
"sendNow": "立即发送",
"sendScope": "发送范围",
"sendScopeDescription": "选择邮件发送的用户范围。选择\"仅附加邮箱\"将只发送到下方填写的邮箱地址",
"sendSettings": "发送设置",
"sendTime": "发送时间",
"specificUsers": "特定用户",
"specificUsersOnly": "仅附加邮箱(跳过平台用户)",
"startTime": "开始时间",
"status": "状态",
"stop": "停止",
"subject": "邮件主题",
"subscribedUsers": "已订阅用户",
"subscribedUsersOnly": "仅已订阅用户",
"subscribers": "套餐",
"subscriptionCount": "订阅数量",
"subscriptionValidityEndDate": "订阅有效期结束日期",
"subscriptionValidityStartDate": "订阅有效期开始日期",
"taskStoppedSuccessfully": "任务停止成功",
"timeRange": "时间范围",
"useMarkdownEditor": "使用 Markdown 编辑器编写邮件内容,支持预览功能",
"users": "用户",
"validOnly": "仅有效",
"viewAndManageEmailBroadcastTasks": "查看和管理邮件广播任务",
"viewAndManageQuotaTasks": "查看和管理配额任务",
"yes": "是"
}

View File

@ -0,0 +1,35 @@
{
"ADS Config": "广告配置",
"Announcement Management": "公告管理",
"Auth Control": "认证控制",
"Balance": "余额",
"Commerce": "商业",
"Commission": "佣金",
"Coupon Management": "优惠券管理",
"Dashboard": "仪表盘",
"Document Management": "文档管理",
"Email": "邮件",
"Gift": "赠送",
"Login": "登录",
"Logs & Analytics": "日志与分析",
"Maintenance": "维护",
"Marketing Management": "营销管理",
"Mobile": "短信",
"Node Management": "节点管理",
"Order Management": "订单管理",
"Payment Config": "支付配置",
"Product Management": "商品管理",
"Register": "注册",
"Reset Subscribe": "重置订阅",
"Server Management": "服务器管理",
"Server Traffic": "服务器流量",
"Subscribe": "订阅",
"Subscribe Config": "订阅配置",
"Subscribe Traffic": "订阅流量",
"System": "系统",
"System Config": "系统配置",
"Ticket Management": "工单管理",
"Traffic Details": "流量详情",
"User Management": "用户管理",
"Users & Support": "用户与支持"
}

View File

@ -0,0 +1,31 @@
{
"address": "地址",
"cancel": "取消",
"confirm": "确认",
"confirmDeleteDesc": "此操作无法撤销。",
"confirmDeleteTitle": "删除此节点?",
"copied": "已复制",
"copy": "复制",
"create": "创建",
"created": "已创建",
"delete": "删除",
"deleted": "已删除",
"drawerCreateTitle": "创建节点",
"drawerEditTitle": "编辑节点",
"edit": "编辑",
"enabled": "已启用",
"enabled_off": "已禁用",
"enabled_on": "已启用",
"name": "名称",
"pageTitle": "节点",
"port": "端口",
"protocol": "协议",
"select_protocol": "选择协议…",
"select_server": "选择服务器…",
"server": "服务器",
"sorted_success": "排序成功",
"tags": "标签",
"tags_description": "权限分组标签(包含计划绑定和投递策略)。",
"tags_placeholder": "使用回车或逗号 (,) 添加多个标签",
"updated": "已更新"
}

View File

@ -0,0 +1,29 @@
{
"amount": "金额",
"couponDiscount": "优惠券折扣",
"discount": "折扣金额",
"feeAmount": "手续费",
"method": "支付方式",
"orderNumber": "订单编号",
"status": {
"0": "状态",
"1": "待支付",
"2": "已支付",
"3": "已取消",
"4": "已关闭",
"5": "已完成"
},
"subscribe": "订阅",
"subscribePrice": "订阅价格",
"total": "总计",
"tradeNo": "交易号",
"type": {
"0": "类型",
"1": "新购",
"2": "续费",
"3": "重置流量",
"4": "充值"
},
"updateTime": "更新时间",
"user": "用户"
}

View File

@ -0,0 +1,39 @@
{
"applyForPayment": "申请支付",
"batchDelete": "批量删除",
"cancel": "取消",
"configPlaceholder": "请填写提供的 {{field}} 配置",
"confirm": "确认",
"confirmDelete": "确认删除",
"copy": "复制",
"copySuccess": "复制成功",
"create": "添加支付方式",
"createPayment": "添加支付方式",
"createSuccess": "创建成功",
"delete": "删除",
"deleteSuccess": "删除成功",
"deleteWarning": "确定要删除此支付方式吗?此操作无法撤销。",
"description": "描述",
"domain": "域名",
"edit": "编辑",
"editPayment": "编辑支付方式",
"enable": "启用",
"feeAmount": "固定金额",
"feePercent": "手续费百分比",
"fixedFee": "固定金额",
"handlingFee": "手续费",
"icon": "图标",
"iconPlaceholder": "输入图标 URL",
"name": "名称",
"namePlaceholder": "输入支付方式名称",
"nameRequired": "名称为必填项",
"noFee": "无手续费",
"notify_url": "回调 URL",
"paymentManagement": "支付管理",
"percentFee": "百分比",
"platform": "平台",
"searchPlaceholder": "输入搜索内容",
"selectPlatform": "选择平台",
"submit": "提交",
"updateSuccess": "更新成功"
}

View File

@ -0,0 +1,73 @@
{
"cancel": "取消",
"confirm": "确认",
"confirmDelete": "确定要删除吗?",
"copy": "复制",
"copySuccess": "复制成功",
"create": "创建",
"createSubscribe": "创建订阅",
"createSuccess": "创建成功",
"delete": "删除",
"deleteSuccess": "删除成功",
"deleteWarning": "删除后数据无法恢复,请谨慎操作。",
"deviceLimit": "设备限制/单位",
"edit": "编辑",
"editSubscribe": "编辑订阅",
"form": {
"annualReset": "年度重置",
"basic": "基本",
"cancel": "取消",
"confirm": "确认",
"Day": "天",
"deductionRatio": "自动/手动扣减配置",
"deductionRatioDescription": "用于扣减。默认情况下,系统采用自动计算算法。当提供手动比例时,系统根据时间和流量比例计算比例,确保总和为 100%。",
"description": "描述",
"deviceLimit": "设备限制",
"discount": "折扣",
"discount_price": "折扣价格",
"discountDescription": "根据单价设置折扣",
"discountPercent": "折扣百分比",
"Hour": "小时",
"inventory": "订阅库存",
"language": "语言",
"languageDescription": "留空为默认无语言限制",
"languagePlaceholder": "订阅的语言标识符,例如 en-US、zh-CN",
"Minute": "分钟",
"Month": "月",
"monthlyReset": "每月重置",
"name": "名称",
"node": "节点",
"nodeGroup": "节点组",
"nodes": "节点",
"noLimit": "无限制",
"NoLimit": "无限制",
"noReset": "不重置",
"pricing": "定价",
"purchaseWithDiscount": "允许扣减",
"purchaseWithDiscountDescription": "启用或禁用退订功能。激活后,系统将根据配置的规则和比例进行扣减处理,剩余价值将返还到余额",
"quota": "购买限制",
"renewalReset": "续费重置",
"renewalResetDescription": "续费时重置周期",
"replacement": "重置价格(每次)",
"resetCycle": "重置周期",
"resetOn1st": "每月1日重置",
"selectResetCycle": "请选择重置周期",
"selectUnitTime": "请选择时间单位",
"speedLimit": "速度限制",
"traffic": "流量",
"unitPrice": "单价",
"unitTime": "时间单位",
"Year": "年"
},
"inventory": "订阅库存",
"language": "语言",
"name": "名称",
"quota": "购买限制/次",
"replacement": "重置价格/次",
"sell": "销售",
"show": "显示",
"sold": "订阅数量",
"traffic": "流量",
"unitPrice": "单价",
"updateSuccess": "更新成功"
}

View File

@ -0,0 +1,156 @@
{
"actions": {
"cancel": "取消",
"save": "保存"
},
"address": "地址",
"address_placeholder": "服务器地址",
"apiHost": "API 主机",
"apiHostPlaceholder": "http(s)://example.com",
"bandwidth_placeholder": "输入带宽,留空使用 BBR",
"basic": "基本配置",
"cancel": "取消",
"cert_dns_env": "DNS 环境变量",
"cert_dns_provider": "DNS 提供商",
"cert_mode": "证书模式",
"cipher": "加密算法",
"city": "城市",
"close": "关闭",
"confirm": "确认",
"confirmDeleteDesc": "此操作无法撤销。",
"confirmDeleteTitle": "删除此服务器?",
"congestion_controller": "拥塞控制器",
"connect": "连接",
"copied": "已复制",
"copy": "复制",
"copyAndClose": "复制并关闭",
"copyFailed": "复制失败",
"country": "国家",
"cpu": "CPU",
"create": "创建",
"created": "已创建",
"delete": "删除",
"deleted": "已删除",
"disable_sni": "禁用 SNI",
"disabled": "已禁用",
"disk": "磁盘",
"down_mbps": "下载带宽",
"drawerCreateTitle": "创建服务器",
"drawerEditTitle": "编辑服务器",
"edit": "编辑",
"enabled": "已启用",
"encryption": "加密",
"encryption_client_padding": "客户端填充",
"encryption_mode": "模式",
"encryption_password": "密码",
"encryption_password_placeholder": "留空自动生成",
"encryption_private_key": "私钥",
"encryption_private_key_placeholder": "留空自动生成",
"encryption_rtt": "RTT",
"encryption_server_padding": "服务器填充",
"encryption_ticket": "票据时间",
"expired": "已过期",
"expireTime": "过期时间",
"extra": "额外",
"flow": "流控",
"generate_quantum_resistant_key": "生成量子抗性密钥",
"generate_standard_encryption_key": "生成标准密钥",
"hop_interval": "跳跃间隔",
"hop_ports": "跳跃端口",
"hop_ports_placeholder": "例如 1-65535",
"host": "主机",
"id": "ID",
"installCommand": "安装命令",
"ipAddresses": "IP 地址",
"memory": "内存",
"mode": "模式",
"multiplex": "多路复用",
"name": "名称",
"notAvailable": "不可用",
"obfs": "混淆",
"obfs_password": "混淆密码",
"obfs_password_placeholder": "输入混淆密码",
"offline": "离线",
"oneClickInstall": "一键安装",
"online": "在线",
"onlineUsers": "在线用户",
"padding_scheme": "填充方案",
"padding_scheme_placeholder": "每行一条填充规则格式stop=8, 0=30-30",
"pageTitle": "服务器",
"path": "路径",
"port": "端口",
"protocol_configurations": "协议配置",
"protocol_configurations_desc": "启用并配置所需的协议类型",
"protocols": "协议",
"reality": "Reality",
"reduce_rtt": "减少 RTT",
"security": "安全",
"security_allow_insecure": "允许不安全",
"security_fingerprint": "指纹",
"security_private_key": "Reality 私钥",
"security_private_key_placeholder": "输入私钥",
"security_public_key": "Reality 公钥",
"security_public_key_placeholder": "输入公钥",
"security_server_address": "Reality 服务器地址",
"security_server_address_placeholder": "例如 1.2.3.4 或域名",
"security_server_port": "Reality 服务器端口",
"security_short_id": "Reality Short ID",
"security_sni": "SNI",
"server_config": {
"description": "管理节点通信密钥、拉取/推送间隔。",
"dynamic_multiplier": "动态倍率",
"dynamic_multiplier_desc": "定义时间段和倍率来调整流量计费。",
"fields": {
"block_rules_placeholder": "每行一条域名规则",
"communication_key": "通信密钥",
"communication_key_desc": "用于节点认证。",
"communication_key_placeholder": "请输入",
"dns_config": "DNS 配置",
"dns_domains_placeholder": "每行一条域名规则",
"dns_proto_placeholder": "选择类型",
"end_time": "结束时间",
"ip_strategy": "IP 策略",
"ip_strategy_desc": "选择网络连接的 IP 版本偏好",
"ip_strategy_ipv4": "优先 IPv4",
"ip_strategy_ipv6": "优先 IPv6",
"ip_strategy_placeholder": "选择 IP 策略",
"multiplier": "倍率",
"node_pull_interval": "节点拉取间隔",
"node_pull_interval_desc": "节点拉取配置的频率(秒)。",
"node_push_interval": "节点推送间隔",
"node_push_interval_desc": "节点推送统计的频率(秒)。",
"outbound_address_placeholder": "服务器地址",
"outbound_name_placeholder": "配置名称",
"outbound_password_placeholder": "密码(可选)",
"outbound_port_placeholder": "端口号",
"outbound_protocol_placeholder": "选择协议",
"outbound_rules_placeholder": "每行一条规则",
"reset": "重置",
"start_time": "开始时间",
"traffic_report_threshold": "流量上报阈值",
"traffic_report_threshold_desc": "设置流量上报的最小阈值。"
},
"saveSuccess": "保存成功",
"tabs": {
"basic": "基本配置",
"block": "屏蔽规则",
"dns": "DNS 配置",
"outbound": "出站规则"
},
"title": "节点配置"
},
"server_key": "服务器密钥",
"service_name": "服务名称",
"sorted_success": "排序成功",
"status": "状态",
"subscribeId": "订阅 ID",
"subscription": "订阅",
"traffic": "流量",
"traffic_ratio": "倍率",
"transport": "传输",
"udp_relay_mode": "UDP 中继模式",
"unlimited": "无限制",
"up_mbps": "上传带宽",
"updated": "已更新",
"user": "用户"
}

View File

@ -0,0 +1,119 @@
{
"actions": {
"add": "添加",
"batchDelete": "批量删除",
"batchDeleteSuccess_other": "成功删除 {count} 个客户端",
"batchDeleteWarning_other": "确定要删除选中的 {count} 个客户端吗?",
"cancel": "取消",
"confirm": "确认",
"confirmDelete": "确认删除",
"createSuccess": "创建成功",
"delete": "删除",
"deleteFailed": "删除失败",
"deleteSuccess": "删除成功",
"deleteWarning": "此操作无法撤销。确定要删除此客户端吗?",
"edit": "编辑",
"save": "保存",
"saveFailed": "保存失败",
"update": "更新",
"updateSuccess": "更新成功"
},
"config": {
"description": "管理订阅系统设置",
"singleSubscriptionMode": "单一订阅模式",
"singleSubscriptionModeDescription": "限制用户只能有一个活跃订阅。现有订阅不受影响",
"subscriptionDomain": "订阅域名",
"subscriptionDomainDescription": "订阅链接的自定义域名",
"subscriptionDomainPlaceholder": "输入订阅域名,每行一个",
"subscriptionPath": "订阅路径",
"subscriptionPathDescription": "订阅端点的自定义路径(系统重启后性能更佳)",
"subscriptionPathPlaceholder": "输入订阅路径",
"title": "订阅配置",
"updateError": "更新失败",
"updateSuccess": "设置更新成功",
"userAgentLimit": "{userAgent} 限制",
"userAgentLimitDescription": "基于 {userAgent} 启用访问限制",
"userAgentList": "{userAgent} 白名单",
"userAgentListDescription": "允许访问订阅的 {userAgent},每行一个。已配置的应用程序 {userAgent} 将自动包含",
"userAgentListPlaceholder": "输入允许的 {userAgent},每行一个",
"wildcardResolution": "泛域名解析",
"wildcardResolutionDescription": "为订阅启用泛域名解析"
},
"form": {
"addTitle": "添加客户端",
"descriptions": {
"description": "详细的客户端描述",
"downloadLink": "平台下载 URL",
"icon": "图标 URL 或 base64 编码",
"name": "客户端显示名称",
"outputFormat": "订阅配置文件格式",
"scheme": {
"base64Encoding": "Base64 编码",
"functions": "支持函数:",
"jsonStringify": "JSON 对象转字符串",
"nameVariable": "站点名称",
"title": "URL Scheme 模板",
"urlEncoding": "URL 编码",
"urlVariable": "订阅 URL",
"variables": "支持变量:"
},
"template": {
"functions": "模板函数:",
"if": "条件语句",
"nodes": "代理节点列表",
"range": "遍历数组",
"siteName": "站点名称",
"sprig": "Sprig 函数库(字符串处理、日期等)",
"subscribeName": "订阅名称",
"title": "Go 模板语法",
"userInfo": "用户信息(流量、到期等)",
"variables": "可用变量:"
},
"userAgentPrefix": "用于区分不同客户端的客户端标识符"
},
"editTitle": "编辑客户端",
"fields": {
"description": "描述",
"icon": "图标",
"name": "名称",
"outputFormat": "输出格式",
"scheme": "URL Scheme",
"template": "订阅文件模板"
},
"tabs": {
"basic": "基本信息",
"download": "下载",
"template": "模板"
}
},
"outputFormats": {
"base64": "Base64",
"conf": "CONF",
"json": "JSON",
"plain": "纯文本",
"yaml": "YAML"
},
"protocol": {
"title": "客户端管理"
},
"table": {
"columns": {
"default": "默认",
"description": "描述",
"name": "客户端名称",
"outputFormat": "输出格式",
"supportedPlatforms": "支持的平台"
}
},
"templatePreview": {
"base64": {
"decodedContent": "解码内容",
"decodeError": "Base64 解码错误",
"originalContent": "原始内容"
},
"failed": "加载模板失败",
"loading": "加载中...",
"preview": "预览",
"title": "模板预览"
}
}

View File

@ -0,0 +1,136 @@
{
"basicSettings": "基本设置",
"common": {
"cancel": "取消",
"save": "保存设置",
"saveFailed": "保存失败",
"saveSuccess": "保存成功"
},
"currency": {
"accessKey": "API 密钥",
"accessKeyDescription": "由 {{url}} 提供的免费汇率 API 密钥",
"accessKeyPlaceholder": "输入 API 密钥",
"currencySymbol": "货币符号",
"currencySymbolDescription": "仅用于显示;更改此项将影响系统中所有货币单位",
"currencySymbolPlaceholder": "¥",
"currencyUnit": "货币单位",
"currencyUnitDescription": "仅用于显示;更改此项将影响系统中所有货币单位",
"currencyUnitPlaceholder": "CNY",
"description": "配置货币单位、符号和汇率 API 设置",
"title": "货币配置"
},
"invite": {
"description": "配置用户邀请和推荐奖励设置",
"forcedInvite": "强制邀请注册",
"forcedInviteDescription": "启用后,用户必须通过邀请链接注册",
"inputPlaceholder": "请输入",
"onlyFirstPurchase": "仅首次购买奖励",
"onlyFirstPurchaseDescription": "启用后,推荐人仅在被推荐用户首次购买时获得奖励",
"referralPercentage": "推荐奖励百分比",
"referralPercentageDescription": "给予推荐人的奖励百分比",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"title": "邀请设置"
},
"logCleanup": {
"autoClear": "启用自动清理",
"autoClearDescription": "启用后,系统将自动清理过期的日志记录",
"clearDays": "保留天数",
"clearDaysDescription": "日志保留的天数;超过此时间的日志将被清理",
"clearDaysPlaceholder": "输入保留天数",
"description": "配置自动日志清理规则和保留期限",
"title": "日志清理设置"
},
"logSettings": "日志设置",
"privacyPolicy": {
"description": "编辑和管理隐私政策内容",
"title": "隐私政策"
},
"register": {
"day": "天",
"description": "配置用户注册相关设置",
"enableTrial": "启用试用",
"enableTrialDescription": "启用后,新用户注册时将获得试用订阅",
"hour": "小时",
"inputPlaceholder": "请输入",
"ipRegistrationLimit": "IP 注册限制",
"ipRegistrationLimitDescription": "限制单个 IP 地址的注册次数",
"minute": "分钟",
"month": "月",
"none": "无",
"registrationLimitCount": "注册限制次数",
"registrationLimitCountDescription": "限制期内每个 IP 允许的注册次数",
"registrationLimitExpire": "限制期",
"registrationLimitExpireDescription": "IP 注册限制的持续时间(分钟)",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"selectPlaceholder": "请选择",
"stopNewUserRegistration": "停止新用户注册",
"stopNewUserRegistrationDescription": "启用后,将禁止新用户注册",
"title": "注册设置",
"trialConfig": "试用配置",
"trialConfigDescription": "配置新用户注册时的试用订阅、时长和时间单位",
"year": "年"
},
"site": {
"customData": "自定义数据",
"customDataDescription": "用于网站定制的自定义数据",
"customHtml": "自定义 HTML",
"customHtmlDescription": "注入到站点 body 标签底部的自定义 HTML 代码",
"description": "配置基本站点信息、Logo、域名和其他设置",
"keywords": "关键词",
"keywordsDescription": "用于 SEO 目的",
"keywordsPlaceholder": "关键词1, 关键词2, 关键词3",
"logo": "站点 Logo",
"logoDescription": "用于在指定位置显示 Logo",
"logoPlaceholder": "输入 Logo 的 URL不要以 '/' 结尾",
"siteDesc": "站点描述",
"siteDescDescription": "用于在指定位置显示站点描述",
"siteDescPlaceholder": "输入站点描述",
"siteDomain": "站点域名",
"siteDomainDescription": "当前网站的域名地址,例如用于邮件中",
"siteDomainPlaceholder": "请输入域名地址。多个域名请每行输入一个。",
"siteName": "站点名称",
"siteNameDescription": "用于在指定位置显示站点名称",
"siteNamePlaceholder": "输入站点名称",
"title": "站点配置"
},
"tos": {
"description": "编辑和管理服务条款内容",
"title": "服务条款"
},
"userSecuritySettings": "用户与安全",
"verify": {
"description": "配置 Turnstile 验证码和验证设置",
"enableLoginVerify": "登录验证",
"enableLoginVerifyDescription": "启用后,用户登录时必须通过人机验证",
"enablePasswordVerify": "密码重置验证",
"enablePasswordVerifyDescription": "启用后,用户重置密码时必须通过人机验证",
"enableRegisterVerify": "注册验证",
"enableRegisterVerifyDescription": "启用后,用户注册时必须通过人机验证",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"title": "安全验证",
"turnstileSecret": "Turnstile 密钥",
"turnstileSecretDescription": "用于后端验证的 Cloudflare Turnstile 密钥",
"turnstileSecretPlaceholder": "输入 Turnstile 密钥",
"turnstileSiteKey": "Turnstile 站点密钥",
"turnstileSiteKeyDescription": "用于前端验证的 Cloudflare Turnstile 站点密钥",
"turnstileSiteKeyPlaceholder": "输入 Turnstile 站点密钥"
},
"verifyCode": {
"dailyLimit": "每日发送限制",
"dailyLimitDescription": "每个用户每天可发送的最大验证码数量",
"description": "配置邮件验证码发送规则和限制",
"expireTime": "验证码有效期",
"expireTimeDescription": "验证码的有效期(秒)",
"inputPlaceholder": "请输入",
"interval": "发送间隔",
"intervalDescription": "两次验证码发送之间的最小间隔(秒)",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"seconds": "秒",
"times": "次",
"title": "验证码设置"
}
}

View File

@ -0,0 +1,22 @@
{
"cancel": "取消",
"check": "查看",
"close": "关闭",
"closeSuccess": "关闭成功",
"closeWarning": "一旦关闭,工单将无法操作,请谨慎操作。",
"confirm": "确认",
"confirmClose": "确定要关闭吗?",
"inputPlaceholder": "请输入您的问题,我们会尽快回复。",
"reply": "回复",
"status": {
"0": "状态",
"1": "待跟进",
"2": "待回复",
"3": "已解决",
"4": "已关闭"
},
"ticketList": "工单列表",
"title": "标题",
"updatedAt": "更新时间",
"user": "用户"
}

View File

@ -0,0 +1,14 @@
{
"cancel": "取消",
"confirmReboot": "确认重启",
"confirmSystemReboot": "确认系统重启",
"newVersionAvailable": "有新版本可用",
"rebootDescription": "确定要重启系统吗?此操作无法撤销。",
"rebooting": "重启中...",
"refreshLogs": "刷新日志",
"serverVersion": "服务器版本",
"systemLogs": "系统日志",
"systemReboot": "系统重启",
"systemServices": "系统服务",
"webVersion": "前端版本"
}

View File

@ -0,0 +1,16 @@
{
"errors": {
"nameRequired": "请输入名称",
"portRange": "端口必须在 1 到 65535 之间",
"protocolRequired": "请选择协议",
"serverAddrRequired": "请输入入口地址",
"serverRequired": "请选择服务器"
},
"form": {
"validation": {
"nameRequired": "客户端名称必填",
"userAgentRequiredSuffix": "是必填项"
}
},
"tos": "服务条款"
}

View File

@ -0,0 +1,108 @@
{
"accountEnable": "账户启用",
"add": "添加",
"administrator": "管理员",
"areaCodePlaceholder": "区号",
"authMethodsTitle": "认证方式",
"avatar": "头像",
"balance": "余额",
"balanceLogs": "余额日志",
"balanceNotifications": "余额通知",
"balancePlaceholder": "输入余额",
"basicInfoTitle": "基本信息",
"cancel": "取消",
"commission": "佣金",
"commissionLogs": "佣金日志",
"commissionPlaceholder": "输入佣金",
"confirm": "确认",
"confirmDelete": "确认删除",
"confirmOffline": "确认下线",
"copySubscription": "复制订阅",
"copySuccess": "复制成功",
"create": "创建",
"createdAt": "创建时间",
"createSubscription": "创建订阅",
"createSuccess": "创建成功",
"createUser": "创建用户",
"delete": "删除",
"deleteDescription": "此操作无法撤销。",
"deleteSubscriptionDescription": "此操作无法撤销。",
"deleteSuccess": "删除成功",
"deviceLimit": "设备限制",
"download": "下载",
"downloadTraffic": "下载流量",
"edit": "编辑",
"editSubscription": "编辑订阅",
"enable": "启用",
"expiredAt": "过期时间",
"expireTime": "过期时间",
"giftAmount": "赠送金额",
"giftAmountPlaceholder": "输入赠送金额",
"giftLogs": "赠送日志",
"invalidEmailFormat": "邮箱格式无效",
"inviteCode": "邀请码",
"inviteCodePlaceholder": "输入邀请码",
"kickOfflineConfirm": "确认踢下线",
"kickOfflineSuccess": "设备已踢下线",
"lastSeen": "最后上线",
"loading": "加载中...",
"loginLogs": "登录日志",
"loginNotifications": "登录通知",
"loginStatus": "登录状态",
"manager": "管理员",
"more": "更多",
"notifySettingsTitle": "通知设置",
"offline": "离线",
"online": "在线",
"onlineDevices": "在线设备",
"onlyFirstPurchase": "仅首次购买",
"orderList": "订单列表",
"password": "密码",
"passwordPlaceholder": "输入密码",
"permanent": "永久",
"pleaseEnterEmail": "输入邮箱",
"referer": "推荐人",
"refererId": "推荐人 ID",
"refererIdPlaceholder": "输入推荐人 ID",
"referralCode": "推荐码",
"referralPercentage": "推荐百分比",
"referralPercentagePlaceholder": "输入百分比",
"referrerUserId": "推荐人用户 ID",
"remove": "移除",
"resetLogs": "重置日志",
"resetTime": "重置时间",
"save": "保存",
"speedLimit": "速度限制",
"startTime": "开始时间",
"subscription": "订阅",
"subscriptionId": "订阅 ID",
"subscriptionInfo": "订阅信息",
"subscriptionList": "订阅列表",
"subscriptionLogs": "订阅日志",
"subscriptionName": "订阅名称",
"subscriptionNotifications": "订阅通知",
"telephone": "电话",
"telephonePlaceholder": "输入电话号码",
"token": "令牌",
"totalTraffic": "总流量",
"tradeNotifications": "交易通知",
"trafficDetails": "流量详情",
"trafficLimit": "流量限制",
"trafficStats": "流量统计",
"trafficUsage": "流量使用",
"unlimited": "无限制",
"unverified": "未验证",
"update": "更新",
"updateSuccess": "更新成功",
"upload": "上传",
"uploadTraffic": "上传流量",
"userAgent": "用户代理",
"userEmail": "邮箱",
"userEmailPlaceholder": "输入邮箱",
"userId": "用户 ID",
"userInfo": "用户信息",
"userList": "用户列表",
"userName": "用户名",
"userProfile": "用户资料",
"verified": "已验证"
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#0071cd" d="M0.038 23.657c0.003 1.309 1.066 2.367 2.375 2.364 0 0 24.875-0.056 24.875-0.056 1.42-0.003 2.575 1.132 2.606 2.545 0-0.018 0.003-0.035 0.003-0.053 0 0-0.048-21.313-0.048-21.313-0.003-1.438-1.171-2.601-2.609-2.597 0 0-24.875 0.056-24.875 0.056-1.309 0.003-2.367 1.066-2.364 2.375 0 0 0.038 16.68 0.038 16.68z"></path>
<path fill="#1e9b5f" d="M29.298 16.714c0 0-21.592 9.368-21.592 9.368-0.456 0.198-0.987-0.063-1.187-0.582 0 0-5.232-13.621-5.232-13.621-0.2-0.519 0.008-1.101 0.464-1.299 0 0 21.592-9.368 21.592-9.368 0.456-0.198 0.987 0.063 1.187 0.582 0 0 5.232 13.621 5.232 13.621 0.2 0.519-0.008 1.101-0.464 1.299z"></path>
<path fill="#22b573" d="M29.768 20.094c0 0-24.591 5.455-24.591 5.455-0.519 0.115-1.028-0.237-1.138-0.787 0 0-2.859-14.411-2.859-14.411-0.109-0.55 0.223-1.089 0.743-1.204 0 0 24.591-5.455 24.591-5.455 0.519-0.115 1.028 0.237 1.138 0.787 0 0 2.859 14.411 2.859 14.411 0.109 0.55-0.223 1.089-0.743 1.204z"></path>
<path fill="#008cff" d="M31.432 11.74c-0.003-1.309-1.066-2.367-2.375-2.364 0 0-26.448 0.060-26.448 0.060-1.42 0.003-2.575-1.132-2.606-2.545-0 0.018-0.003 0.035-0.003 0.053 0 0 0.048 21.313 0.048 21.313 0.003 1.438 1.171 2.601 2.609 2.597 0 0 26.448-0.060 26.448-0.060 1.309-0.003 2.367-1.066 2.364-2.375 0 0-0.038-16.68-0.038-16.68z"></path>
<path fill="#0070cd" d="M26.236 22.693c0 0 5.237-0.012 5.237-0.012 0.292-0.001 0.528-0.238 0.527-0.53 0 0-0.009-3.803-0.009-3.803-0.001-0.292-0.238-0.528-0.53-0.528 0 0-5.237 0.012-5.237 0.012-1.342 0.003-2.428 1.094-2.425 2.436 0 0 0 0 0 0 0.003 1.342 1.093 2.428 2.436 2.425z"></path>
<path fill="#ddd" d="M27.349 20.26c0.001 0.618-0.498 1.12-1.116 1.121s-1.12-0.498-1.121-1.116c-0.001-0.618 0.498-1.12 1.116-1.121s1.12 0.498 1.121 1.116z"></path>
<path fill="#fff" d="M26.315 19.817c0 0.208-0.168 0.377-0.376 0.377s-0.377-0.168-0.377-0.376c-0-0.208 0.168-0.377 0.376-0.377s0.377 0.168 0.377 0.376z"></path>
<path fill="#fff" d="M11.717 22.215l-0.152-0.173c-0.747-0.851-2.045-2.149-2.893-2.894l-0.173-0.152c-0.431-0.379-1.007-0.588-1.62-0.588s-1.189 0.209-1.621 0.588l-0.173 0.152c-0.849 0.745-2.147 2.043-2.893 2.894l-0.152 0.173c-0.783 0.893-0.783 2.345 0 3.238l0.152 0.173c0.746 0.85 2.044 2.149 2.893 2.895l0.173 0.152c0.432 0.379 1.007 0.588 1.621 0.588s1.189-0.209 1.621-0.588l0.273-0.24c0.269-0.235 0.391-0.343 0.768-0.712l0.089-0.089-0.014-0.015c0.075-0.151 0.027-0.334-0.113-0.431s-0.331-0.079-0.449 0.043c-0.036 0.034-0.534 0.502-0.842 0.773l-0.173 0.152c-0.305 0.267-0.717 0.414-1.16 0.414s-0.855-0.147-1.16-0.415l-0.173-0.152c-0.831-0.729-2.101-1.999-2.83-2.83l-0.152-0.173c-0.266-0.304-0.413-0.717-0.413-1.163s0.147-0.859 0.413-1.163l0.152-0.173c0.729-0.831 1.998-2.101 2.83-2.831l0.173-0.152c0.305-0.267 0.717-0.415 1.16-0.415s0.855 0.147 1.16 0.415l0.173 0.152c0.831 0.73 2.101 2 2.83 2.83l0.152 0.173c0.554 0.631 0.555 1.738 0.003 2.322l-0.088 0.093c-0.249 0.265-0.4 0.425-0.559 0.473-0.151 0.045-0.342-0.003-0.705-0.176-0.607-0.289-1.466-0.774-1.764-0.943 0.121-0.237 0.398-0.817 0.528-1.398l0.035-0.156h-1.49v-0.182h1.896v-0.694h-1.896v-0.875h-0.697v0.875h-1.896v0.694h1.896v0.182h-1.459v0.693h2.627c-0.057 0.176-0.161 0.394-0.235 0.536-0.271-0.106-0.989-0.355-1.727-0.355-0.465 0-0.857 0.124-1.133 0.359-0.274 0.233-0.419 0.556-0.419 0.936s0.139 0.705 0.401 0.94c0.268 0.24 0.649 0.368 1.103 0.368 0.524 0 1.067-0.23 1.57-0.662 0.196-0.169 0.375-0.357 0.534-0.56 0.281 0.154 1.103 0.604 1.869 1.009 0.5 0.264 0.851 0.317 1.174 0.175 0.307-0.135 0.577-0.442 0.949-0.867l0.006-0.006c0.783-0.893 0.783-2.345-0.001-3.238zM5.602 25.648c-0.737 0-0.794-0.452-0.794-0.59 0.002-0.14 0.055-0.274 0.149-0.379 0.136-0.147 0.348-0.222 0.633-0.222 0.612 0 1.164 0.208 1.414 0.319-0.227 0.265-0.811 0.873-1.401 0.873z"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,11 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#0071cd" d="M0.114 24.238c0.003 1.309 1.066 2.367 2.375 2.364 0 0 24.875-0.056 24.875-0.056 1.42-0.003 2.575 1.132 2.606 2.545 0-0.018 0.003-0.035 0.003-0.053 0 0-0.048-21.313-0.048-21.313-0.003-1.438-1.171-2.601-2.609-2.597 0 0-24.875 0.056-24.875 0.056-1.309 0.003-2.367 1.066-2.364 2.375 0 0 0.038 16.68 0.038 16.68z"></path>
<path fill="#1e9b5f" d="M29.374 17.295c0 0-21.592 9.368-21.592 9.368-0.456 0.198-0.987-0.063-1.187-0.582 0 0-5.232-13.621-5.232-13.621-0.2-0.519 0.008-1.101 0.464-1.299 0 0 21.592-9.368 21.592-9.368 0.456-0.198 0.987 0.063 1.187 0.582 0 0 5.232 13.621 5.232 13.621 0.2 0.519-0.008 1.101-0.464 1.299z"></path>
<path fill="#22b573" d="M29.844 20.675c0 0-24.591 5.455-24.591 5.455-0.519 0.115-1.028-0.237-1.138-0.787 0 0-2.859-14.411-2.859-14.411-0.109-0.55 0.223-1.089 0.743-1.204 0 0 24.591-5.455 24.591-5.455 0.519-0.115 1.028 0.237 1.138 0.787 0 0 2.859 14.411 2.859 14.411 0.109 0.55-0.223 1.089-0.743 1.204z"></path>
<path fill="#008cff" d="M31.509 12.321c-0.003-1.309-1.066-2.367-2.375-2.364 0 0-26.448 0.060-26.448 0.060-1.42 0.003-2.575-1.132-2.606-2.545-0 0.018-0.003 0.035-0.003 0.053 0 0 0.048 21.313 0.048 21.313 0.003 1.438 1.171 2.601 2.609 2.597 0 0 26.448-0.060 26.448-0.060 1.309-0.003 2.367-1.066 2.364-2.375 0 0-0.038-16.68-0.038-16.68z"></path>
<path fill="#0070cd" d="M26.312 23.274c0 0 5.237-0.012 5.237-0.012 0.292-0.001 0.528-0.238 0.527-0.53 0 0-0.009-3.803-0.009-3.803-0.001-0.292-0.238-0.528-0.53-0.528 0 0-5.237 0.012-5.237 0.012-1.342 0.003-2.428 1.094-2.425 2.436 0 0 0 0 0 0 0.003 1.342 1.093 2.428 2.436 2.425z"></path>
<path fill="#ddd" d="M27.425 20.841c0.001 0.618-0.498 1.12-1.116 1.121s-1.12-0.498-1.121-1.116c-0.001-0.618 0.498-1.12 1.116-1.121s1.12 0.498 1.121 1.116z"></path>
<path fill="#fff" d="M26.392 20.398c0 0.208-0.168 0.377-0.376 0.377s-0.377-0.168-0.377-0.376c-0-0.208 0.168-0.377 0.376-0.377s0.377 0.168 0.377 0.376z"></path>
<path fill="#fff" d="M11.794 22.796l-0.152-0.173c-0.747-0.851-2.045-2.149-2.893-2.894l-0.173-0.152c-0.431-0.379-1.007-0.588-1.62-0.588s-1.189 0.209-1.621 0.588l-0.173 0.152c-0.849 0.745-2.147 2.043-2.893 2.894l-0.152 0.173c-0.783 0.893-0.783 2.345 0 3.238l0.152 0.173c0.746 0.85 2.044 2.149 2.893 2.895l0.173 0.152c0.432 0.379 1.007 0.588 1.621 0.588s1.189-0.209 1.621-0.588l0.273-0.24c0.269-0.235 0.391-0.343 0.768-0.712l0.089-0.089-0.014-0.015c0.075-0.151 0.027-0.334-0.113-0.431s-0.331-0.079-0.449 0.043c-0.036 0.034-0.534 0.502-0.842 0.773l-0.173 0.152c-0.305 0.267-0.717 0.414-1.16 0.414s-0.855-0.147-1.16-0.415l-0.173-0.152c-0.831-0.729-2.101-1.999-2.83-2.83l-0.152-0.173c-0.266-0.304-0.413-0.717-0.413-1.163s0.147-0.859 0.413-1.163l0.152-0.173c0.729-0.831 1.998-2.101 2.83-2.831l0.173-0.152c0.305-0.267 0.717-0.415 1.16-0.415s0.855 0.147 1.16 0.415l0.173 0.152c0.831 0.73 2.101 2 2.83 2.83l0.152 0.173c0.554 0.631 0.555 1.738 0.003 2.322l-0.088 0.093c-0.249 0.265-0.4 0.425-0.559 0.473-0.151 0.045-0.342-0.003-0.705-0.176-0.607-0.289-1.466-0.774-1.764-0.943 0.121-0.237 0.398-0.817 0.528-1.398l0.035-0.156h-1.49v-0.182h1.896v-0.694h-1.896v-0.875h-0.697v0.875h-1.896v0.694h1.896v0.182h-1.459v0.693h2.627c-0.057 0.176-0.161 0.394-0.235 0.536-0.271-0.106-0.989-0.355-1.727-0.355-0.465 0-0.857 0.124-1.133 0.359-0.274 0.233-0.419 0.556-0.419 0.936s0.139 0.705 0.401 0.94c0.268 0.24 0.649 0.368 1.103 0.368 0.524 0 1.067-0.23 1.57-0.662 0.196-0.169 0.375-0.357 0.534-0.56 0.281 0.154 1.103 0.604 1.869 1.009 0.5 0.264 0.851 0.317 1.174 0.175 0.307-0.135 0.577-0.442 0.949-0.867l0.006-0.006c0.783-0.893 0.783-2.345-0.001-3.238zM5.679 26.229c-0.737 0-0.794-0.452-0.794-0.59 0.002-0.14 0.055-0.274 0.149-0.379 0.136-0.147 0.348-0.222 0.633-0.222 0.612 0 1.164 0.208 1.414 0.319-0.227 0.265-0.811 0.873-1.401 0.873z"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,14 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#1a1a1a" d="M0.038 23.657c0.003 1.309 1.066 2.367 2.375 2.364 0 0 24.875-0.056 24.875-0.056 1.42-0.003 2.575 1.132 2.606 2.545 0-0.018 0.003-0.035 0.003-0.053 0 0-0.048-21.313-0.048-21.313-0.003-1.438-1.171-2.601-2.609-2.597 0 0-24.875 0.056-24.875 0.056-1.309 0.003-2.367 1.066-2.364 2.375 0 0 0.038 16.68 0.038 16.68z"></path>
<path fill="#1e9b5f" d="M29.298 16.714c0 0-21.592 9.368-21.592 9.368-0.456 0.198-0.987-0.063-1.187-0.582 0 0-5.232-13.621-5.232-13.621-0.2-0.519 0.008-1.101 0.464-1.299 0 0 21.592-9.368 21.592-9.368 0.456-0.198 0.987 0.063 1.187 0.582 0 0 5.232 13.621 5.232 13.621 0.2 0.519-0.008 1.101-0.464 1.299z"></path>
<path fill="#22b573" d="M29.768 20.094c0 0-24.591 5.455-24.591 5.455-0.519 0.115-1.028-0.237-1.138-0.787 0 0-2.859-14.411-2.859-14.411-0.109-0.55 0.223-1.089 0.743-1.204 0 0 24.591-5.455 24.591-5.455 0.519-0.115 1.028 0.237 1.138 0.787 0 0 2.859 14.411 2.859 14.411 0.109 0.55-0.223 1.089-0.743 1.204z"></path>
<path fill="#434343" d="M31.432 11.74c-0.003-1.309-1.066-2.367-2.375-2.364 0 0-26.448 0.060-26.448 0.060-1.42 0.003-2.575-1.132-2.606-2.545-0 0.018-0.003 0.035-0.003 0.053 0 0 0.048 21.313 0.048 21.313 0.003 1.438 1.171 2.601 2.609 2.597 0 0 26.448-0.060 26.448-0.060 1.309-0.003 2.367-1.066 2.364-2.375 0 0-0.038-16.68-0.038-16.68z"></path>
<path fill="#1a1a1a" d="M26.236 22.693c0 0 5.237-0.012 5.237-0.012 0.292-0.001 0.528-0.238 0.527-0.53 0 0-0.009-3.803-0.009-3.803-0.001-0.292-0.238-0.528-0.53-0.528 0 0-5.237 0.012-5.237 0.012-1.342 0.003-2.428 1.094-2.425 2.436 0 0 0 0 0 0 0.003 1.342 1.093 2.428 2.436 2.425z"></path>
<path fill="#ddd" d="M27.349 20.26c0.001 0.618-0.498 1.12-1.116 1.121s-1.12-0.498-1.121-1.116c-0.001-0.618 0.498-1.12 1.116-1.121s1.12 0.498 1.121 1.116z"></path>
<path fill="#fff" d="M26.315 19.817c0 0.208-0.168 0.377-0.376 0.377s-0.377-0.168-0.377-0.376c-0-0.208 0.168-0.377 0.376-0.377s0.377 0.168 0.377 0.376z"></path>
<path fill="#fff" d="M5.778 26.32c-1.082 0-1.96-0.493-1.966-1.101 0 0.233-0.001 0.436-0.001 0.436 0 0.611 0.88 1.107 1.966 1.107s1.966-0.495 1.966-1.107v-0.442c0.001 0.611-0.879 1.107-1.965 1.107zM5.778 27.089c-1.082 0-1.96-0.492-1.966-1.101 0 0.233-0.001 0.436-0.001 0.436 0 0.611 0.88 1.107 1.966 1.107s1.966-0.495 1.966-1.107v-0.441c0.001 0.611-0.879 1.106-1.965 1.106zM8.294 25.807c-0.063 0-0.125-0.002-0.187-0.005-0.002 0.019-0.002 0.039-0.005 0.058 0.013 0.039 0.020 0.081 0.020 0.124v0.26c0.057 0.003 0.115 0.005 0.173 0.005 1.086 0 1.966-0.495 1.966-1.107v-0.441c0 0.611-0.88 1.107-1.967 1.107z"></path>
<path fill="#fff" d="M8.294 25.217c-0.063 0-0.125-0.002-0.187-0.005-0.002 0.019-0.002 0.038-0.005 0.056 0.013 0.038 0.020 0.078 0.020 0.12v0.251c0.057 0.003 0.115 0.005 0.173 0.005 1.086 0 1.965-0.479 1.965-1.071v-0.427c0 0.592-0.88 1.071-1.966 1.071zM8.294 23.726c-1.082 0-1.96-0.477-1.965-1.066 0 0.225-0.001 0.422-0.001 0.422 0 0.253 0.162 0.485 0.43 0.668 0.272 0.073 0.514 0.176 0.715 0.305 0.25 0.062 0.527 0.098 0.82 0.098 1.086 0 1.965-0.479 1.965-1.071v-0.427c0.002 0.593-0.879 1.071-1.965 1.071z"></path>
<path fill="#fff" d="M8.294 24.288c-0.132 0-0.261-0.008-0.386-0.021 0.1 0.136 0.167 0.284 0.195 0.443 0.063 0.004 0.126 0.005 0.19 0.005 1.087 0 1.966-0.48 1.966-1.072v-0.427c0.002 0.592-0.879 1.072-1.966 1.072zM8.294 21.082c-1.086 0-1.966 0.48-1.966 1.072s0.88 1.072 1.966 1.072c1.087 0 1.966-0.48 1.966-1.072s-0.88-1.072-1.966-1.072zM5.778 23.815c-1.086 0-1.966 0.48-1.966 1.072s0.88 1.072 1.966 1.072c1.087 0 1.966-0.48 1.966-1.072s-0.88-1.072-1.966-1.072z"></path>
<path fill="#fff" d="M7.037 18.722c-2.999 0-5.428 2.43-5.428 5.427s2.429 5.427 5.428 5.427c2.996 0 5.425-2.43 5.425-5.427s-2.429-5.427-5.425-5.427v0zM7.037 28.897c-2.624 0-4.749-2.126-4.749-4.748s2.125-4.748 4.749-4.748c2.621 0 4.747 2.126 4.747 4.748s-2.126 4.748-4.747 4.748v0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,12 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#549f24" d="M0.038 23.657c0.003 1.309 1.066 2.367 2.375 2.364 0 0 24.875-0.056 24.875-0.056 1.42-0.003 2.575 1.132 2.606 2.545 0-0.018 0.003-0.035 0.003-0.053 0 0-0.048-21.313-0.048-21.313-0.003-1.438-1.171-2.601-2.609-2.597 0 0-24.875 0.056-24.875 0.056-1.309 0.003-2.367 1.066-2.364 2.375 0 0 0.038 16.68 0.038 16.68z"></path>
<path fill="#1e9b5f" d="M29.298 16.714c0 0-21.592 9.368-21.592 9.368-0.456 0.198-0.987-0.063-1.187-0.582 0 0-5.232-13.621-5.232-13.621-0.2-0.519 0.008-1.101 0.464-1.299 0 0 21.592-9.368 21.592-9.368 0.456-0.198 0.987 0.063 1.187 0.582 0 0 5.232 13.621 5.232 13.621 0.2 0.519-0.008 1.101-0.464 1.299z"></path>
<path fill="#22b573" d="M29.768 20.094c0 0-24.591 5.455-24.591 5.455-0.519 0.115-1.028-0.237-1.138-0.787 0 0-2.859-14.411-2.859-14.411-0.109-0.55 0.223-1.089 0.743-1.204 0 0 24.591-5.455 24.591-5.455 0.519-0.115 1.028 0.237 1.138 0.787 0 0 2.859 14.411 2.859 14.411 0.109 0.55-0.223 1.089-0.743 1.204z"></path>
<path fill="#85c65c" d="M31.432 11.74c-0.003-1.309-1.066-2.367-2.375-2.364 0 0-26.448 0.060-26.448 0.060-1.42 0.003-2.575-1.132-2.606-2.545-0 0.018-0.003 0.035-0.003 0.053 0 0 0.048 21.313 0.048 21.313 0.003 1.438 1.171 2.601 2.609 2.597 0 0 26.448-0.060 26.448-0.060 1.309-0.003 2.367-1.066 2.364-2.375 0 0-0.038-16.68-0.038-16.68z"></path>
<path fill="#539e24" d="M26.236 22.693c0 0 5.237-0.012 5.237-0.012 0.292-0.001 0.528-0.238 0.527-0.53 0 0-0.009-3.803-0.009-3.803-0.001-0.292-0.238-0.528-0.53-0.528 0 0-5.237 0.012-5.237 0.012-1.342 0.003-2.428 1.094-2.425 2.436 0 0 0 0 0 0 0.003 1.342 1.093 2.428 2.436 2.425z"></path>
<path fill="#ddd" d="M27.349 20.26c0.001 0.618-0.498 1.12-1.116 1.121s-1.12-0.498-1.121-1.116c-0.001-0.618 0.498-1.12 1.116-1.121s1.12 0.498 1.121 1.116z"></path>
<path fill="#fff" d="M26.315 19.817c0 0.208-0.168 0.377-0.376 0.377s-0.377-0.168-0.377-0.376c-0-0.208 0.168-0.377 0.376-0.377s0.377 0.168 0.377 0.376z"></path>
<path fill="#fff" d="M6.88 18.722c-2.999 0-5.428 2.43-5.428 5.427s2.429 5.427 5.428 5.427c2.996 0 5.425-2.43 5.425-5.427s-2.429-5.427-5.425-5.427v0zM6.88 28.897c-2.624 0-4.749-2.126-4.749-4.748s2.125-4.748 4.749-4.748c2.621 0 4.747 2.126 4.747 4.748s-2.126 4.748-4.747 4.748v0z"></path>
<path fill="#fff" d="M7.316 22.976c0.623 0 1.283-0.002 1.92 0.002 0.656 0.003 0.741-0.063 0.868-0.785-1.813-0.003-3.079-0.005-3.914-0.010-1.251-0.008-1.499 0.233-1.789 1.506-0.022 0.096-0.030 0.142-0.046 0.24h-0.857l0.671 0.819c-0.020 0.131-0.048 0.274-0.076 0.404-0.138 0.64 0.028 1.278 0.792 1.278 1.299 0 2.646-0.005 3.916 0 0.291 0 0.376-0.137 0.423-0.312 0.026-0.094 0.105-0.473 0.105-0.473h-2.254c-0.196 0-0.377 0.001-0.546 0-0.248-0.001-0.449-0.103-0.539-0.231-0.195-0.27 0.046-0.661 0.441-0.661l2.589-0.002c0 0 0.314 0.010 0.413-0.055 0.083-0.056 0.143-0.139 0.166-0.234l0.125-0.531h-2.796c-0.633-0.040-0.629-0.455-0.629-0.455 0.014-0.379 0.328-0.503 0.519-0.498 0.163 0.004 0.203 0 0.499 0h-0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,12 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#0071cd" d="M0.038 23.769c0.003 1.328 1.082 2.401 2.409 2.399 0 0 25.24-0.057 25.24-0.057 1.441-0.003 2.612 1.148 2.644 2.582 0-0.018 0.003-0.035 0.003-0.053 0 0-0.049-21.626-0.049-21.626-0.003-1.459-1.188-2.639-2.647-2.635 0 0-25.24 0.057-25.24 0.057-1.328 0.003-2.401 1.082-2.399 2.409 0 0 0.038 16.924 0.038 16.924z"></path>
<path fill="#1e9b5f" d="M29.727 16.724c0 0-21.908 9.505-21.908 9.505-0.462 0.201-1.002-0.064-1.204-0.591 0 0-5.309-13.82-5.309-13.82-0.203-0.527 0.008-1.117 0.471-1.318 0 0 21.908-9.505 21.908-9.505 0.462-0.201 1.002 0.064 1.204 0.591 0 0 5.309 13.82 5.309 13.82 0.203 0.527-0.008 1.117-0.471 1.318z"></path>
<path fill="#22b573" d="M30.204 20.154c0 0-24.952 5.535-24.952 5.535-0.527 0.117-1.043-0.24-1.154-0.798 0 0-2.901-14.622-2.901-14.622-0.111-0.558 0.227-1.105 0.753-1.221 0 0 24.952-5.535 24.952-5.535 0.527-0.117 1.043 0.241 1.154 0.798 0 0 2.901 14.622 2.901 14.622 0.111 0.558-0.227 1.105-0.753 1.221z"></path>
<path fill="#008cff" d="M31.892 11.678c-0.003-1.328-1.082-2.402-2.409-2.399 0 0-26.836 0.060-26.836 0.060-1.441 0.003-2.612-1.148-2.644-2.582-0 0.018-0.003 0.035-0.003 0.053 0 0 0.049 21.626 0.049 21.626 0.003 1.459 1.188 2.639 2.647 2.635 0 0 26.836-0.060 26.836-0.060 1.328-0.003 2.401-1.082 2.399-2.409 0 0-0.038-16.924-0.038-16.924z"></path>
<path fill="#0070cd" d="M26.62 22.791c0 0 5.314-0.012 5.314-0.012 0.296-0.001 0.536-0.241 0.535-0.538 0 0-0.009-3.859-0.009-3.859-0.001-0.296-0.241-0.536-0.538-0.535 0 0-5.314 0.012-5.314 0.012-1.362 0.003-2.463 1.11-2.46 2.472 0 0 0 0 0 0 0.003 1.362 1.11 2.463 2.471 2.46z"></path>
<path fill="#ddd" d="M27.749 20.323c0.001 0.627-0.506 1.136-1.132 1.137s-1.136-0.506-1.137-1.132c-0.001-0.627 0.506-1.136 1.132-1.137s1.136 0.506 1.137 1.132z"></path>
<path fill="#fff" d="M26.7 19.872c0 0.211-0.17 0.382-0.381 0.383s-0.382-0.17-0.383-0.381c-0-0.211 0.17-0.382 0.381-0.383s0.382 0.17 0.383 0.381z"></path>
<path fill="#fff" d="M29.944 19.956c-0.243-0.091-0.375-0.161-0.375-0.272 0-0.094 0.076-0.148 0.212-0.148 0.249 0 0.505 0.097 0.681 0.184l0.099-0.621c-0.14-0.068-0.425-0.178-0.82-0.178-0.279 0-0.512 0.074-0.678 0.212-0.173 0.144-0.262 0.353-0.262 0.604 0 0.457 0.276 0.651 0.724 0.816 0.289 0.104 0.385 0.178 0.385 0.292 0 0.111-0.094 0.175-0.263 0.175-0.21 0-0.555-0.104-0.781-0.238l-0.101 0.628c0.195 0.111 0.553 0.225 0.926 0.225 0.295 0 0.541-0.071 0.707-0.205 0.186-0.148 0.282-0.366 0.282-0.648 0-0.467-0.282-0.661-0.737-0.826h0z"></path>
<path fill="#fff" d="M11.889 22.306l-0.154-0.176c-0.758-0.864-2.075-2.181-2.936-2.936l-0.176-0.154c-0.438-0.385-1.022-0.597-1.644-0.597s-1.207 0.212-1.645 0.597l-0.176 0.154c-0.861 0.756-2.178 2.073-2.936 2.937l-0.154 0.176c-0.794 0.906-0.794 2.38 0 3.286l0.154 0.176c0.757 0.863 2.074 2.18 2.936 2.937l0.176 0.154c0.438 0.385 1.022 0.597 1.645 0.597s1.206-0.212 1.645-0.597l0.277-0.243c0.273-0.239 0.397-0.348 0.779-0.723l0.090-0.090-0.014-0.015c0.076-0.154 0.028-0.339-0.114-0.438s-0.335-0.080-0.456 0.043c-0.036 0.034-0.542 0.509-0.854 0.784l-0.175 0.154c-0.309 0.271-0.728 0.421-1.177 0.421s-0.868-0.149-1.177-0.421l-0.176-0.154c-0.843-0.74-2.131-2.029-2.871-2.872l-0.154-0.176c-0.27-0.308-0.419-0.727-0.419-1.18s0.149-0.872 0.419-1.18l0.154-0.176c0.74-0.843 2.028-2.132 2.871-2.872l0.175-0.154c0.309-0.271 0.727-0.421 1.177-0.421s0.868 0.149 1.177 0.421l0.176 0.154c0.843 0.741 2.132 2.029 2.871 2.872l0.154 0.176c0.562 0.64 0.564 1.763 0.003 2.356l-0.089 0.094c-0.253 0.269-0.406 0.432-0.567 0.48-0.153 0.046-0.347-0.003-0.715-0.178-0.616-0.294-1.487-0.785-1.79-0.957 0.123-0.241 0.403-0.829 0.536-1.418l0.036-0.158h-1.511v-0.185h1.924v-0.704h-1.924v-0.888h-0.707v0.888h-1.924v0.704h1.924v0.185h-1.48v0.704h2.666c-0.058 0.178-0.164 0.4-0.239 0.544-0.275-0.107-1.004-0.36-1.753-0.36-0.472 0-0.869 0.126-1.15 0.364-0.278 0.236-0.425 0.564-0.425 0.95s0.141 0.715 0.407 0.954c0.272 0.244 0.659 0.373 1.119 0.373 0.531 0 1.082-0.233 1.593-0.672 0.199-0.172 0.381-0.362 0.542-0.569 0.285 0.156 1.119 0.613 1.897 1.024 0.507 0.268 0.864 0.321 1.192 0.177 0.312-0.137 0.585-0.448 0.963-0.879l0.006-0.007c0.794-0.906 0.794-2.38-0.001-3.286zM5.684 25.789c-0.748 0-0.806-0.459-0.806-0.599 0.002-0.142 0.056-0.279 0.151-0.385 0.137-0.15 0.354-0.226 0.642-0.226 0.621 0 1.181 0.211 1.434 0.323-0.23 0.269-0.823 0.886-1.422 0.886z"></path>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,14 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#006924" d="M0.038 23.657c0.003 1.309 1.066 2.367 2.375 2.364 0 0 24.875-0.056 24.875-0.056 1.42-0.003 2.575 1.132 2.606 2.545 0-0.018 0.003-0.035 0.003-0.053 0 0-0.048-21.313-0.048-21.313-0.003-1.438-1.171-2.601-2.609-2.597 0 0-24.875 0.056-24.875 0.056-1.309 0.003-2.367 1.066-2.364 2.375 0 0 0.038 16.68 0.038 16.68z"></path>
<path fill="#1e9b5f" d="M29.298 16.714c0 0-21.592 9.368-21.592 9.368-0.456 0.198-0.987-0.063-1.187-0.582 0 0-5.232-13.621-5.232-13.621-0.2-0.519 0.008-1.101 0.464-1.299 0 0 21.592-9.368 21.592-9.368 0.456-0.198 0.987 0.063 1.187 0.582 0 0 5.232 13.621 5.232 13.621 0.2 0.519-0.008 1.101-0.464 1.299z"></path>
<path fill="#22b573" d="M29.768 20.094c0 0-24.591 5.455-24.591 5.455-0.519 0.115-1.028-0.237-1.138-0.787 0 0-2.859-14.411-2.859-14.411-0.109-0.55 0.223-1.089 0.743-1.204 0 0 24.591-5.455 24.591-5.455 0.519-0.115 1.028 0.237 1.138 0.787 0 0 2.859 14.411 2.859 14.411 0.109 0.55-0.223 1.089-0.743 1.204z"></path>
<path fill="#01cd47" d="M31.432 11.74c-0.003-1.309-1.066-2.367-2.375-2.364 0 0-26.448 0.060-26.448 0.060-1.42 0.003-2.575-1.132-2.606-2.545-0 0.018-0.003 0.035-0.003 0.053 0 0 0.048 21.313 0.048 21.313 0.003 1.438 1.171 2.601 2.609 2.597 0 0 26.448-0.060 26.448-0.060 1.309-0.003 2.367-1.066 2.364-2.375 0 0-0.038-16.68-0.038-16.68z"></path>
<path fill="#006924" d="M26.236 22.693c0 0 5.237-0.012 5.237-0.012 0.292-0.001 0.528-0.238 0.527-0.53 0 0-0.009-3.803-0.009-3.803-0.001-0.292-0.238-0.528-0.53-0.528 0 0-5.237 0.012-5.237 0.012-1.342 0.003-2.428 1.094-2.425 2.436 0 0 0 0 0 0 0.003 1.342 1.093 2.428 2.436 2.425z"></path>
<path fill="#ddd" d="M27.349 20.26c0.001 0.618-0.498 1.12-1.116 1.121s-1.12-0.498-1.121-1.116c-0.001-0.618 0.498-1.12 1.116-1.121s1.12 0.498 1.121 1.116z"></path>
<path fill="#fff" d="M26.315 19.817c0 0.208-0.168 0.377-0.376 0.377s-0.377-0.168-0.377-0.376c-0-0.208 0.168-0.377 0.376-0.377s0.377 0.168 0.377 0.376z"></path>
<path fill="#fff" d="M10.874 24.652c0-0.935-0.977-1.694-2.184-1.694-1.206 0-2.184 0.758-2.184 1.694 0 0.93 0.967 1.684 2.164 1.693 0.521 0.871 0.998 0.704 0.998 0.704s-0.372-0.246-0.411-0.762c0.931-0.194 1.617-0.852 1.617-1.635v0zM7.964 24.388c-0.141 0-0.255-0.114-0.255-0.254s0.115-0.254 0.255-0.254c0.141 0 0.255 0.114 0.255 0.254s-0.115 0.254-0.255 0.254v0zM9.363 24.388c-0.142 0-0.255-0.114-0.255-0.254s0.113-0.254 0.255-0.254c0.141 0 0.254 0.114 0.254 0.254s-0.113 0.254-0.254 0.254v0z"></path>
<path fill="#fff" d="M8.36 22.6c-0.363-0.793-1.347-1.363-2.505-1.363-1.467 0-2.657 0.914-2.657 2.041 0 0.966 0.874 1.775 2.046 1.986-0.022 0.668-0.504 0.987-0.504 0.987s0.607 0.214 1.258-0.932c0 0-0.003-0.001-0.009-0.002 0.123-0.006 0.244-0.017 0.362-0.034-0.157-0.248-0.246-0.527-0.246-0.822-0-0.991 0.996-1.801 2.254-1.861v0zM5.062 22.937c-0.186 0-0.335-0.149-0.335-0.332 0-0.185 0.15-0.333 0.335-0.333 0.184 0 0.334 0.149 0.334 0.333 0 0.183-0.149 0.332-0.334 0.332v0zM6.413 22.605c0-0.185 0.15-0.333 0.335-0.333 0.184 0 0.334 0.149 0.334 0.333 0 0.184-0.15 0.332-0.334 0.332-0.186 0-0.335-0.149-0.335-0.332v0z"></path>
<path fill="#fff" d="M7.037 18.722c-2.999 0-5.428 2.43-5.428 5.427s2.429 5.427 5.428 5.427c2.996 0 5.425-2.43 5.425-5.427s-2.429-5.427-5.425-5.427v0zM7.037 28.897c-2.624 0-4.749-2.126-4.749-4.748s2.125-4.748 4.749-4.748c2.621 0 4.747 2.126 4.747 4.748s-2.126 4.748-4.747 4.748v0z"></path>
<path fill="#fff" d="M29.354 19.899c-0.239-0.090-0.37-0.159-0.37-0.268 0-0.093 0.075-0.145 0.209-0.145 0.245 0 0.497 0.096 0.671 0.182l0.098-0.612c-0.138-0.067-0.419-0.175-0.808-0.175-0.275 0-0.504 0.073-0.668 0.209-0.17 0.142-0.259 0.348-0.259 0.596 0 0.45 0.272 0.642 0.713 0.804 0.285 0.103 0.38 0.175 0.38 0.288 0 0.109-0.093 0.172-0.259 0.172-0.207 0-0.547-0.103-0.77-0.235l-0.099 0.619c0.192 0.109 0.545 0.222 0.912 0.222 0.291 0 0.534-0.070 0.697-0.202 0.183-0.145 0.278-0.361 0.278-0.639 0-0.46-0.278-0.652-0.726-0.814h0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,13 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#006924" d="M0.038 23.657c0.003 1.309 1.066 2.367 2.375 2.364 0 0 24.875-0.056 24.875-0.056 1.42-0.003 2.575 1.132 2.606 2.545 0-0.018 0.003-0.035 0.003-0.053 0 0-0.048-21.313-0.048-21.313-0.003-1.438-1.171-2.601-2.609-2.597 0 0-24.875 0.056-24.875 0.056-1.309 0.003-2.367 1.066-2.364 2.375 0 0 0.038 16.68 0.038 16.68z"></path>
<path fill="#1e9b5f" d="M29.298 16.714c0 0-21.592 9.368-21.592 9.368-0.456 0.198-0.987-0.063-1.187-0.582 0 0-5.232-13.621-5.232-13.621-0.2-0.519 0.008-1.101 0.464-1.299 0 0 21.592-9.368 21.592-9.368 0.456-0.198 0.987 0.063 1.187 0.582 0 0 5.232 13.621 5.232 13.621 0.2 0.519-0.008 1.101-0.464 1.299z"></path>
<path fill="#22b573" d="M29.768 20.094c0 0-24.591 5.455-24.591 5.455-0.519 0.115-1.028-0.237-1.138-0.787 0 0-2.859-14.411-2.859-14.411-0.109-0.55 0.223-1.089 0.743-1.204 0 0 24.591-5.455 24.591-5.455 0.519-0.115 1.028 0.237 1.138 0.787 0 0 2.859 14.411 2.859 14.411 0.109 0.55-0.223 1.089-0.743 1.204z"></path>
<path fill="#01cd47" d="M31.432 11.74c-0.003-1.309-1.066-2.367-2.375-2.364 0 0-26.448 0.060-26.448 0.060-1.42 0.003-2.575-1.132-2.606-2.545-0 0.018-0.003 0.035-0.003 0.053 0 0 0.048 21.313 0.048 21.313 0.003 1.438 1.171 2.601 2.609 2.597 0 0 26.448-0.060 26.448-0.060 1.309-0.003 2.367-1.066 2.364-2.375 0 0-0.038-16.68-0.038-16.68z"></path>
<path fill="#006924" d="M26.236 22.693c0 0 5.237-0.012 5.237-0.012 0.292-0.001 0.528-0.238 0.527-0.53 0 0-0.009-3.803-0.009-3.803-0.001-0.292-0.238-0.528-0.53-0.528 0 0-5.237 0.012-5.237 0.012-1.342 0.003-2.428 1.094-2.425 2.436 0 0 0 0 0 0 0.003 1.342 1.093 2.428 2.436 2.425z"></path>
<path fill="#ddd" d="M27.349 20.26c0.001 0.618-0.498 1.12-1.116 1.121s-1.12-0.498-1.121-1.116c-0.001-0.618 0.498-1.12 1.116-1.121s1.12 0.498 1.121 1.116z"></path>
<path fill="#fff" d="M26.315 19.817c0 0.208-0.168 0.377-0.376 0.377s-0.377-0.168-0.377-0.376c-0-0.208 0.168-0.377 0.376-0.377s0.377 0.168 0.377 0.376z"></path>
<path fill="#fff" d="M10.874 24.652c0-0.935-0.977-1.694-2.184-1.694-1.206 0-2.184 0.758-2.184 1.694 0 0.93 0.967 1.684 2.164 1.693 0.521 0.871 0.998 0.704 0.998 0.704s-0.372-0.246-0.411-0.762c0.931-0.194 1.617-0.852 1.617-1.635v0zM7.964 24.388c-0.141 0-0.255-0.114-0.255-0.254s0.115-0.254 0.255-0.254c0.141 0 0.255 0.114 0.255 0.254s-0.115 0.254-0.255 0.254v0zM9.363 24.388c-0.142 0-0.255-0.114-0.255-0.254s0.113-0.254 0.255-0.254c0.141 0 0.254 0.114 0.254 0.254s-0.113 0.254-0.254 0.254v0z"></path>
<path fill="#fff" d="M8.36 22.6c-0.363-0.793-1.347-1.363-2.505-1.363-1.467 0-2.657 0.914-2.657 2.041 0 0.966 0.874 1.775 2.046 1.986-0.022 0.668-0.504 0.987-0.504 0.987s0.607 0.214 1.258-0.932c0 0-0.003-0.001-0.009-0.002 0.123-0.006 0.244-0.017 0.362-0.034-0.157-0.248-0.246-0.527-0.246-0.822-0-0.991 0.996-1.801 2.254-1.861v0zM5.062 22.937c-0.186 0-0.335-0.149-0.335-0.332 0-0.185 0.15-0.333 0.335-0.333 0.184 0 0.334 0.149 0.334 0.333 0 0.183-0.149 0.332-0.334 0.332v0zM6.413 22.605c0-0.185 0.15-0.333 0.335-0.333 0.184 0 0.334 0.149 0.334 0.333 0 0.184-0.15 0.332-0.334 0.332-0.186 0-0.335-0.149-0.335-0.332v0z"></path>
<path fill="#fff" d="M7.037 18.722c-2.999 0-5.428 2.43-5.428 5.427s2.429 5.427 5.428 5.427c2.996 0 5.425-2.43 5.425-5.427s-2.429-5.427-5.425-5.427v0zM7.037 28.897c-2.624 0-4.749-2.126-4.749-4.748s2.125-4.748 4.749-4.748c2.621 0 4.747 2.126 4.747 4.748s-2.126 4.748-4.747 4.748v0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,34 @@
{
"name": "PPanel",
"short_name": "PPanel",
"icons": [
{
"src": "/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/pwa-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#FFFFFF",
"theme_color": "#FFFFFF"
}

View File

@ -0,0 +1,49 @@
import { formatBytes } from "@workspace/ui/utils/formatting";
import { unitConversion } from "@workspace/ui/utils/unit-conversions";
import { useTranslation } from "react-i18next";
import { useGlobalStore } from "@/stores/global";
type DisplayType = "currency" | "traffic" | "number" | "trafficSpeed";
interface DisplayProps<T> {
value?: T;
unlimited?: boolean;
type?: DisplayType;
}
export function Display<T extends number | undefined | null>({
value = 0,
unlimited = false,
type = "number",
}: DisplayProps<T>): string {
const { t } = useTranslation("components");
const { common } = useGlobalStore();
const { currency } = common;
if (type === "currency") {
const formattedValue = `${currency?.currency_symbol ?? ""}${unitConversion("centsToDollars", value as number)?.toFixed(2) ?? "0.00"}`;
return formattedValue;
}
if (
["traffic", "trafficSpeed", "number"].includes(type) &&
unlimited &&
!value
) {
return t("unlimited");
}
if (type === "traffic") {
return value ? formatBytes(value) : "0";
}
if (type === "trafficSpeed") {
return value ? `${formatBytes(value).replace("B", "b")}ps` : "0";
}
if (type === "number") {
return value ? value.toString() : "0";
}
return "0";
}

View File

@ -0,0 +1,32 @@
"use client";
import { ExternalLink } from "lucide-react";
import type React from "react";
interface IpLinkProps {
ip: string;
children?: React.ReactNode;
className?: string;
target?: "_blank" | "_self";
}
export function IpLink({
ip,
children,
className = "",
target = "_blank",
}: IpLinkProps) {
const url = `https://ipinfo.io/${ip}`;
return (
<a
className={`inline-flex items-center gap-1 font-mono text-primary transition-colors hover:text-primary/80 hover:underline ${className}`}
href={url}
rel={target === "_blank" ? "noopener noreferrer" : undefined}
target={target}
>
{children || ip}
<ExternalLink className="h-3 w-3" />
</a>
);
}

View File

@ -0,0 +1,19 @@
import { Link } from "@tanstack/react-router";
import { Button } from "@workspace/ui/components/button";
interface OrderLinkProps {
orderId?: string | number;
className?: string;
}
export function OrderLink({ orderId, className }: OrderLinkProps) {
if (!orderId) return <span>--</span>;
return (
<Button asChild className={`p-0 ${className || ""}`} variant="link">
<Link search={{ search: orderId }} to="/dashboard/order">
{orderId}
</Link>
</Button>
);
}

View File

@ -0,0 +1,10 @@
export const fallbackLng = "en-US";
export const supportedLngs = ["en-US", "zh-CN"];
export const CDN_URL =
import.meta.env.VITE_CDN_URL || "https://cdn.jsdmirror.com";
export const TUTORIAL_DOCUMENT =
import.meta.env.VITE_TUTORIAL_DOCUMENT || "true";
export const USER_EMAIL = import.meta.env.VITE_USER_EMAIL;
export const USER_PASSWORD = import.meta.env.VITE_USER_PASSWORD;

View File

@ -0,0 +1,56 @@
import { Link, useLocation } from "@tanstack/react-router";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@workspace/ui/components/breadcrumb";
import { Separator } from "@workspace/ui/components/separator";
import { SidebarTrigger } from "@workspace/ui/components/sidebar";
import { LanguageSwitch } from "@workspace/ui/composed/language-switch";
import { ThemeSwitch } from "@workspace/ui/composed/theme-switch";
import { Fragment, useMemo } from "react";
import { findNavByUrl, useNavs } from "./navs";
import TimezoneSwitch from "./timezone-switch";
import { UserNav } from "./user-nav";
export function Header() {
const pathname = useLocation({ select: (location) => location.pathname });
const navs = useNavs();
const items = useMemo(() => findNavByUrl(navs, pathname), [pathname]);
return (
<header className="sticky top-0 z-50 flex h-14 shrink-0 items-center gap-2 bg-background">
<div className="flex flex-1 items-center gap-2 px-3">
<SidebarTrigger />
<Separator className="mr-2 h-4" orientation="vertical" />
<Breadcrumb>
<BreadcrumbList>
{items.map((item, index) => (
<Fragment key={item?.title}>
{index !== items.length - 1 && (
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link to={item?.url || "/dashboard"}>{item?.title}</Link>
</BreadcrumbLink>
</BreadcrumbItem>
)}
{index < items.length - 1 && <BreadcrumbSeparator />}
{index === items.length - 1 && (
<BreadcrumbPage>{item?.title}</BreadcrumbPage>
)}
</Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
</div>
<div className="flex items-center gap-2 px-3">
<LanguageSwitch />
<TimezoneSwitch />
<ThemeSwitch />
<UserNav />
</div>
</header>
);
}

View File

@ -0,0 +1,32 @@
import { Outlet } from "@tanstack/react-router";
import {
SidebarInset,
SidebarProvider,
} from "@workspace/ui/components/sidebar";
import { getCookie } from "@workspace/ui/lib/cookies";
import { useEffect, useState } from "react";
import { Header } from "@/layout/header";
import { SidebarLeft } from "./sidebar-left";
export default function DashboardLayout() {
const [open, setOpen] = useState(true);
useEffect(() => {
const sidebarState = getCookie("sidebar_state");
if (sidebarState !== undefined) {
setOpen(sidebarState === "true");
}
}, []);
return (
<SidebarProvider defaultOpen={open}>
<SidebarLeft />
<SidebarInset className="relative flex-grow overflow-hidden">
<Header />
<div className="h-[calc(100vh-56px)] flex-grow gap-4 overflow-auto p-4">
<Outlet />
</div>
</SidebarInset>
</SidebarProvider>
);
}

View File

@ -0,0 +1,226 @@
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
export interface NavItem {
title: string;
url?: string;
icon?: string;
items?: NavItem[];
defaultOpen?: boolean;
}
export function useNavs() {
const { t } = useTranslation("menu");
const navs: NavItem[] = useMemo(
() => [
{
title: t("Dashboard", "Dashboard"),
url: "/dashboard",
icon: "flat-color-icons:globe",
},
{
title: t("Maintenance", "Maintenance"),
icon: "flat-color-icons:data-protection",
items: [
{
title: t("Server Management", "Server Management"),
url: "/dashboard/servers",
icon: "flat-color-icons:data-protection",
},
{
title: t("Node Management", "Node Management"),
url: "/dashboard/nodes",
icon: "flat-color-icons:mind-map",
},
{
title: t("Subscribe Config", "Subscribe Config"),
url: "/dashboard/subscribe",
icon: "flat-color-icons:ruler",
},
{
title: t("Product Management", "Product Management"),
url: "/dashboard/product",
icon: "flat-color-icons:shop",
},
],
},
{
title: t("Commerce", "Commerce"),
icon: "flat-color-icons:sales-performance",
items: [
{
title: t("Order Management", "Order Management"),
url: "/dashboard/order",
icon: "flat-color-icons:todo-list",
},
{
title: t("Coupon Management", "Coupon Management"),
url: "/dashboard/coupon",
icon: "flat-color-icons:bookmark",
},
{
title: t("Marketing Management", "Marketing Management"),
url: "/dashboard/marketing",
icon: "flat-color-icons:bullish",
},
{
title: t("Announcement Management", "Announcement Management"),
url: "/dashboard/announcement",
icon: "flat-color-icons:advertising",
},
],
},
{
title: t("Users & Support", "Users & Support"),
icon: "flat-color-icons:collaboration",
items: [
{
title: t("User Management", "User Management"),
url: "/dashboard/user",
icon: "flat-color-icons:conference-call",
},
{
title: t("Ticket Management", "Ticket Management"),
url: "/dashboard/ticket",
icon: "flat-color-icons:collaboration",
},
{
title: t("Document Management", "Document Management"),
url: "/dashboard/document",
icon: "flat-color-icons:document",
},
],
},
{
defaultOpen: false,
title: t("System", "System"),
icon: "flat-color-icons:services",
items: [
{
title: t("System Config", "System Config"),
url: "/dashboard/system",
icon: "flat-color-icons:services",
},
{
title: t("Auth Control", "Auth Control"),
url: "/dashboard/auth-control",
icon: "flat-color-icons:lock-portrait",
},
{
title: t("Payment Config", "Payment Config"),
url: "/dashboard/payment",
icon: "flat-color-icons:currency-exchange",
},
{
title: t("ADS Config", "ADS Config"),
url: "/dashboard/ads",
icon: "flat-color-icons:electrical-sensor",
},
],
},
{
defaultOpen: false,
title: t("Logs & Analytics", "Logs & Analytics"),
icon: "flat-color-icons:statistics",
items: [
{
title: t("Login", "Login"),
url: "/dashboard/log/login",
icon: "flat-color-icons:unlock",
},
{
title: t("Register", "Register"),
url: "/dashboard/log/register",
icon: "flat-color-icons:contacts",
},
{
title: t("Email", "Email"),
url: "/dashboard/log/email",
icon: "flat-color-icons:feedback",
},
{
title: t("Mobile", "Mobile"),
url: "/dashboard/log/mobile",
icon: "flat-color-icons:sms",
},
{
title: t("Subscribe", "Subscribe"),
url: "/dashboard/log/subscribe",
icon: "flat-color-icons:workflow",
},
{
title: t("Reset Subscribe", "Reset Subscribe"),
url: "/dashboard/log/reset-subscribe",
icon: "flat-color-icons:refresh",
},
{
title: t("Subscribe Traffic", "Subscribe Traffic"),
url: "/dashboard/log/subscribe-traffic",
icon: "flat-color-icons:statistics",
},
{
title: t("Server Traffic", "Server Traffic"),
url: "/dashboard/log/server-traffic",
icon: "flat-color-icons:statistics",
},
{
title: t("Traffic Details", "Traffic Details"),
url: "/dashboard/log/traffic-details",
icon: "flat-color-icons:combo-chart",
},
{
title: t("Balance", "Balance"),
url: "/dashboard/log/balance",
icon: "flat-color-icons:sales-performance",
},
{
title: t("Commission", "Commission"),
url: "/dashboard/log/commission",
icon: "flat-color-icons:debt",
},
{
title: t("Gift", "Gift"),
url: "/dashboard/log/gift",
icon: "flat-color-icons:donate",
},
],
},
],
[t]
);
return navs;
}
export function findNavByUrl(navs: NavItem[], url: string) {
function matchDynamicRoute(pattern: string, path: string): boolean {
const regexPattern = pattern
.replace(/:[^/]+/g, "[^/]+")
.replace(/\//g, "\\/");
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(path);
}
function findNav(
items: NavItem[],
url: string,
path: NavItem[] = []
): NavItem[] {
for (const item of items) {
if (item.url === url || (item.url && matchDynamicRoute(item.url, url))) {
return [...path, item];
}
if (item.items) {
const result = findNav(item.items, url, [...path, item]);
if (result.length) return result;
}
}
return [];
}
return findNav(navs, url);
}

View File

@ -0,0 +1,294 @@
import { Link, useLocation } from "@tanstack/react-router";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@workspace/ui/components/hover-card";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@workspace/ui/components/sidebar";
import { Icon } from "@workspace/ui/composed/icon";
import { cn } from "@workspace/ui/lib/utils";
import React, { useState } from "react";
import { useGlobalStore } from "@/stores/global";
import { type NavItem, useNavs } from "./navs";
function hasChildren(obj: any): obj is { items: any[] } {
return (
obj && Array.isArray((obj as any).items) && (obj as any).items.length > 0
);
}
export function SidebarLeft({
...props
}: React.ComponentProps<typeof Sidebar>) {
const { common } = useGlobalStore();
const { site } = common;
const navs = useNavs();
const pathname = useLocation({ select: (location) => location.pathname });
const { state, isMobile } = useSidebar();
const [openGroups, setOpenGroups] = useState<Record<string, boolean>>({});
React.useEffect(() => {
setOpenGroups((prev) => {
const next: Record<string, boolean> = { ...prev };
navs.forEach((nav) => {
if (hasChildren(nav) && next[nav.title] === undefined) {
next[nav.title] = nav.defaultOpen ?? true;
}
});
return next;
});
}, [navs]);
const handleToggleGroup = (title: string) => {
setOpenGroups((prev) => ({ ...prev, [title]: !prev[title] }));
};
const normalize = (p: string) =>
p.endsWith("/") && p !== "/" ? p.replace(/\/+$/, "") : p;
const isActiveUrl = (url: string) => {
const path = normalize(pathname);
const target = normalize(url);
if (target === "/dashboard") return path === target;
if (path === target) return true;
return path.startsWith(`${target}/`);
};
const isGroupActive = (nav: NavItem) =>
(hasChildren(nav) && nav.items?.some((i: any) => isActiveUrl(i.url))) ||
("url" in nav && nav.url ? isActiveUrl(nav.url as string) : false);
React.useEffect(() => {
setOpenGroups((prev) => {
const next: Record<string, boolean> = { ...prev };
navs.forEach((nav) => {
if (hasChildren(nav) && isGroupActive(nav)) next[nav.title] = true;
});
return next;
});
}, [pathname, navs]);
const renderCollapsedFlyout = (nav: NavItem) => {
const ParentButton = (
<SidebarMenuButton
aria-label={nav.title}
className="h-8 justify-center"
isActive={false}
size="sm"
>
{"url" in nav && nav.url ? (
<Link to={nav.url as string}>
{"icon" in nav && (nav as any).icon ? (
<Icon className="size-4" icon={(nav as any).icon} />
) : null}
</Link>
) : "icon" in nav && (nav as any).icon ? (
<Icon className="size-4" icon={(nav as any).icon} />
) : null}
</SidebarMenuButton>
);
if (!hasChildren(nav)) return ParentButton;
return (
<HoverCard closeDelay={200} openDelay={40}>
<HoverCardTrigger asChild>{ParentButton}</HoverCardTrigger>
<HoverCardContent
align="start"
avoidCollisions
className="z-[9999] w-64 p-0"
collisionPadding={8}
side="right"
sideOffset={10}
>
<div className="flex items-center gap-2 border-b px-3 py-2">
{"icon" in nav && (nav as any).icon ? (
<Icon className="size-4" icon={(nav as any).icon} />
) : null}
<span className="truncate font-medium text-muted-foreground text-xs">
{nav.title}
</span>
</div>
<ul className="p-1">
{nav.items?.map((item: any) => (
<li key={item.title}>
<Link
className={[
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm",
isActiveUrl(item.url)
? "bg-accent text-accent-foreground"
: "hover:bg-accent/60",
].join(" ")}
to={item.url}
>
{item.icon && <Icon className="size-4" icon={item.icon} />}
<span className="truncate">{item.title}</span>
</Link>
</li>
))}
</ul>
</HoverCardContent>
</HoverCard>
);
};
return (
<Sidebar className="border-r-0" collapsible="icon" {...props}>
<SidebarHeader className="p-2">
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild className="h-10" size="sm">
<Link to="/">
<div className="flex aspect-square size-6 items-center justify-center rounded-lg">
<img
alt="logo"
className="size-full"
height={24}
src={site.site_logo || "/favicon.svg"}
width={24}
/>
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold text-xs">
{site.site_name}
</span>
<span className="truncate text-xs opacity-70">
{site.site_desc}
</span>
</div>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent className="py-2">
<SidebarMenu>
{!isMobile && state === "collapsed"
? navs.map((nav) => (
<SidebarMenuItem className="mx-auto" key={nav.title}>
{renderCollapsedFlyout(nav)}
</SidebarMenuItem>
))
: navs.map((nav) => {
if (hasChildren(nav)) {
const isOpen = openGroups[nav.title] ?? false;
return (
<SidebarGroup className={cn("py-1")} key={nav.title}>
<SidebarMenuButton
className={cn(
"mb-2 flex h-8 w-full items-center justify-between hover:bg-accent/60 hover:text-accent-foreground"
)}
isActive={false}
onClick={() => handleToggleGroup(nav.title)}
size="sm"
style={{ fontWeight: 500 }}
tabIndex={0}
>
<span className="flex min-w-0 items-center gap-2">
{"icon" in nav && (nav as any).icon ? (
<Icon
className="size-4 shrink-0"
icon={(nav as any).icon}
/>
) : null}
<span className="truncate text-sm">{nav.title}</span>
</span>
<Icon
className={`ml-2 size-4 transition-transform ${isOpen ? "" : "-rotate-90"}`}
icon="mdi:chevron-down"
/>
</SidebarMenuButton>
{isOpen && (
<SidebarGroupContent className="px-4">
<SidebarMenu>
{nav.items?.map((item: any) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton
asChild
className="h-8"
isActive={isActiveUrl(item.url)}
size="sm"
tooltip={item.title}
>
<Link to={item.url}>
{item.icon && (
<Icon
className="size-4"
icon={item.icon}
/>
)}
<span className="text-sm">
{item.title}
</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
)}
</SidebarGroup>
);
}
return (
<SidebarGroup className="py-1" key={nav.title}>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
asChild={"url" in nav && !!(nav as any).url}
className="h-8"
isActive={
"url" in nav && (nav as any).url
? isActiveUrl((nav as any).url)
: false
}
size="sm"
tooltip={nav.title}
>
{"url" in nav && (nav as any).url ? (
<Link to={(nav as any).url}>
{"icon" in nav && (nav as any).icon ? (
<Icon
className="size-4"
icon={(nav as any).icon}
/>
) : null}
<span className="text-sm">{nav.title}</span>
</Link>
) : (
<>
{"icon" in nav && (nav as any).icon ? (
<Icon
className="size-4"
icon={(nav as any).icon}
/>
) : null}
<span className="text-sm">{nav.title}</span>
</>
)}
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
})}
</SidebarMenu>
</SidebarContent>
</Sidebar>
);
}

View File

@ -0,0 +1,315 @@
import { Button } from "@workspace/ui/components/button";
import {
Command,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@workspace/ui/components/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@workspace/ui/components/popover";
import { Icon } from "@workspace/ui/composed/icon";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
interface TimezoneOption {
value: string;
label: string;
timezone: string;
}
function getCurrentTime(timezone: string): string {
try {
const now = new Date();
return now.toLocaleTimeString("en-US", {
timeZone: timezone,
hour12: false,
hour: "2-digit",
minute: "2-digit",
});
} catch {
return "--:--";
}
}
function getAllTimezones(locale = "en-US"): TimezoneOption[] {
try {
const timeZones = Intl.supportedValuesOf("timeZone");
const processed = timeZones
.map((tz) => {
try {
return {
value: tz,
label: tz,
timezone: getTimezoneOffset(tz),
};
} catch {
return {
value: tz,
label: tz,
timezone: "UTC+00:00",
};
}
})
.filter(Boolean)
.sort((a, b) => a.label.localeCompare(b.label, locale));
const hasUTC = processed.some((tz) => tz.value === "UTC");
if (!hasUTC) {
processed.unshift({
value: "UTC",
label: "UTC",
timezone: "UTC+00:00",
});
}
return processed;
} catch {
return [
{
value: "UTC",
label: "UTC",
timezone: "UTC+00:00",
},
];
}
}
function getServerTimezones(): string[] {
return ["UTC"];
}
function getRecommendedTimezones(): string[] {
try {
const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
if (browserTimezone.startsWith("Asia/")) {
return [
"Asia/Shanghai",
"Asia/Tokyo",
"Asia/Kolkata",
"Asia/Singapore",
"Asia/Seoul",
];
}
if (browserTimezone.startsWith("Europe/")) {
return [
"Europe/London",
"Europe/Paris",
"Europe/Berlin",
"Europe/Rome",
"Europe/Madrid",
];
}
if (browserTimezone.startsWith("America/")) {
return [
"America/New_York",
"America/Los_Angeles",
"America/Chicago",
"America/Denver",
"America/Toronto",
];
}
if (browserTimezone.startsWith("Australia/")) {
return [
"Australia/Sydney",
"Australia/Melbourne",
"Australia/Perth",
"Australia/Brisbane",
];
}
return [
"America/New_York",
"Europe/London",
"Asia/Shanghai",
"Asia/Tokyo",
"Australia/Sydney",
];
} catch {
return [
"America/New_York",
"Europe/London",
"Asia/Shanghai",
"Asia/Tokyo",
"Australia/Sydney",
];
}
}
function getTimezoneOffset(timezone: string): string {
try {
const now = new Date();
const utc = new Date(now.getTime() + now.getTimezoneOffset() * 60_000);
const targetTime = new Date(
utc.toLocaleString("en-US", { timeZone: timezone })
);
const offset = (targetTime.getTime() - utc.getTime()) / (1000 * 60 * 60);
const sign = offset >= 0 ? "+" : "-";
const hours = Math.floor(Math.abs(offset));
const minutes = Math.floor((Math.abs(offset) - hours) * 60);
return `UTC${sign}${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
} catch {
return "UTC+00:00";
}
}
export default function TimezoneSwitch() {
const { i18n } = useTranslation();
const locale = i18n.language;
const [timezone, setTimezone] = useState<string>("UTC");
const [open, setOpen] = useState(false);
const timezoneOptions = useMemo(() => getAllTimezones(locale), [locale]);
useEffect(() => {
const savedTimezone = localStorage.getItem("timezone");
if (savedTimezone) {
setTimezone(savedTimezone);
} else {
try {
const browserTimezone =
Intl.DateTimeFormat().resolvedOptions().timeZone;
setTimezone(browserTimezone);
localStorage.setItem("timezone", browserTimezone);
} catch {
setTimezone("UTC");
}
}
}, []);
const handleTimezoneChange = (newTimezone: string) => {
setTimezone(newTimezone);
localStorage.setItem("timezone", newTimezone);
setOpen(false);
window.dispatchEvent(
new CustomEvent("timezoneChanged", {
detail: { timezone: newTimezone },
})
);
};
const serverTimezones = timezoneOptions.filter(
(option) =>
getServerTimezones().includes(option.value) && option.value !== timezone
);
return (
<Popover onOpenChange={setOpen} open={open}>
<PopoverTrigger asChild>
<Button className="p-0" size="icon" variant="ghost">
<Icon className="!size-6" icon="flat-color-icons:overtime" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-80 p-0">
<Command>
<CommandInput placeholder="Search..." />
<CommandList>
<CommandGroup heading="Current">
{timezoneOptions
.filter((option) => option.value === timezone)
.map((option) => (
<CommandItem
className="bg-primary/10"
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-100" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
{serverTimezones.length > 0 && (
<CommandGroup heading="Server">
{serverTimezones.map((option) => (
<CommandItem
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-0" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
)}
<CommandGroup heading="Recommended">
{timezoneOptions
.filter(
(option) =>
getRecommendedTimezones().includes(option.value) &&
option.value !== timezone
)
.map((option) => (
<CommandItem
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-0" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
<CommandGroup heading="All">
{timezoneOptions
.filter(
(option) =>
!(
getServerTimezones().includes(option.value) ||
getRecommendedTimezones().includes(option.value)
) && option.value !== timezone
)
.map((option) => (
<CommandItem
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-0" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View File

@ -0,0 +1,73 @@
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@workspace/ui/components/avatar";
import { Button } from "@workspace/ui/components/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@workspace/ui/components/dropdown-menu";
import { useTranslation } from "react-i18next";
import { useGlobalStore } from "@/stores/global";
import { Logout } from "@/utils/common";
export function UserNav() {
const { t } = useTranslation("auth");
const { user } = useGlobalStore();
if (user) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="default">
<Avatar className="size-8">
<AvatarImage alt={user?.avatar ?? ""} src={user?.avatar ?? ""} />
<AvatarFallback className="rounded-none bg-transparent">
{user?.auth_methods?.[0]?.auth_identifier
.toUpperCase()
.charAt(0)}
</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56" forceMount>
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="font-medium text-sm leading-none">
{user?.auth_methods?.[0]?.auth_identifier}
</p>
{/* <p className='text-xs leading-none text-muted-foreground'>ID: {user?.id}</p> */}
</div>
</DropdownMenuLabel>
{/* <DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
Profile
<DropdownMenuShortcut>P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Billing
<DropdownMenuShortcut>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Settings
<DropdownMenuShortcut>S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>New Team</DropdownMenuItem>
</DropdownMenuGroup> */}
<DropdownMenuSeparator />
<DropdownMenuItem onClick={Logout}>
{t("logout", "Logout")}
<DropdownMenuShortcut>Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
}

95
apps/admin/src/main.tsx Normal file
View File

@ -0,0 +1,95 @@
import { createRouter, RouterProvider } from "@tanstack/react-router";
import {
TanStackQueryContext,
TanStackQueryProvider,
} from "@workspace/ui/integrations/tanstack-query";
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
// Import the generated route tree
import { routeTree } from "./routeTree.gen";
// Styles
import "@workspace/ui/globals.css";
import { DirectionProvider } from "@workspace/ui/integrations/direction";
import { LanguageProvider } from "@workspace/ui/integrations/language";
import { ThemeProvider } from "@workspace/ui/integrations/theme";
import { initializeI18n } from "@workspace/ui/lib/i18n";
import { fallbackLng, supportedLngs } from "./config/index.ts";
// Report web vitals
import reportWebVitals from "./reportWebVitals.ts";
// Common utilities
import { Logout } from "./utils/common.ts";
initializeI18n({
supportedLngs,
fallbackLng,
ns: [
"ads",
"announcement",
"auth-control",
"auth",
"components",
"coupon",
"dashboard",
"document",
"log",
"marketing",
"menu",
"nodes",
"order",
"payment",
"product",
"servers",
"subscribe",
"system",
"ticket",
"tool",
"translation",
"user",
],
});
window.logout = Logout;
// Create a new router instance
const TanStackQueryProviderContext = TanStackQueryContext();
const router = createRouter({
routeTree,
context: {
...TanStackQueryProviderContext,
},
defaultPreload: "intent",
scrollRestoration: true,
defaultStructuralSharing: true,
defaultPreloadStaleTime: 0,
});
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
// Render the app
const rootElement = document.getElementById("app");
if (rootElement && !rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<TanStackQueryProvider {...TanStackQueryProviderContext}>
<LanguageProvider supportedLanguages={supportedLngs}>
<ThemeProvider>
<DirectionProvider>
<RouterProvider router={router} />
</DirectionProvider>
</ThemeProvider>
</LanguageProvider>
</TanStackQueryProvider>
</StrictMode>
);
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -0,0 +1,13 @@
const reportWebVitals = (onPerfEntry?: () => void) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import("web-vitals").then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
onCLS(onPerfEntry);
onINP(onPerfEntry);
onFCP(onPerfEntry);
onLCP(onPerfEntry);
onTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

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