🎉 feat: initialization
45
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
Normal 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.
|
||||||
21
.github/ISSUE_TEMPLATE/2_feature_request.yml
vendored
Normal 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
@ -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
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
name: '📝 其他 Other'
|
||||||
|
about: '其他问题 | Other issues'
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal 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
@ -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 }}
|
||||||
22
.github/workflows/issue-check-inactive.yml
vendored
Normal 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
|
||||||
46
.github/workflows/issue-close-require.yml
vendored
Normal 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,若有任何问题,可评论回复。
|
||||||
25
.github/workflows/issue-remove-inactive.yml
vendored
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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/>[][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/>[][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:
|
||||||
|
|
||||||
|
[][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 you’re 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
@ -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/>[][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/>[][ppanel-admin-web-deploy] | [![Preview][ppanel-admin-web-cover]][ppanel-admin-web-preview] |
|
||||||
|
|
||||||
|
## ⌨️ 本地开发
|
||||||
|
|
||||||
|
您可以使用 Github Codespaces 进行在线开发:
|
||||||
|
|
||||||
|
[![][codespaces-shield]][codespaces-link]
|
||||||
|
|
||||||
|
您可以使用 Gitpod 进行在线开发:
|
||||||
|
|
||||||
|
[][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
@ -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
@ -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
@ -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
@ -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]
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</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:
|
||||||
|
|
||||||
|
[][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
|
||||||
|
|
||||||
|
[](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 you’re 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
@ -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]
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><kbd>目录</kbd></summary>
|
||||||
|
|
||||||
|
#### 目录
|
||||||
|
|
||||||
|
- [⌨️ 本地开发](#️-本地开发)
|
||||||
|
- [🚀 在 Vercel 上部署](#-在-vercel-上部署)
|
||||||
|
- [🤝 贡献](#-贡献)
|
||||||
|
- [📝 许可证](#-许可证)
|
||||||
|
|
||||||
|
####
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## ⌨️ 本地开发
|
||||||
|
|
||||||
|
您可以使用 Github Codespaces 进行在线开发:
|
||||||
|
|
||||||
|
[![][codespaces-shield]][codespaces-link]
|
||||||
|
|
||||||
|
您可以使用 Gitpod 进行在线开发:
|
||||||
|
|
||||||
|
[][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 上部署
|
||||||
|
|
||||||
|
[](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
|
||||||
21
apps/admin/components.json
Normal 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"
|
||||||
|
}
|
||||||
23
apps/admin/i18next.config.ts
Normal 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
@ -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
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
apps/admin/public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
apps/admin/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
1
apps/admin/public/favicon.svg
Normal 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 |
39
apps/admin/public/locales/en-US/ads.json
Normal 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"
|
||||||
|
}
|
||||||
30
apps/admin/public/locales/en-US/announcement.json
Normal 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"
|
||||||
|
}
|
||||||
179
apps/admin/public/locales/en-US/auth-control.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
38
apps/admin/public/locales/en-US/auth.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
65
apps/admin/public/locales/en-US/components.json
Normal 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"
|
||||||
|
}
|
||||||
47
apps/admin/public/locales/en-US/coupon.json
Normal 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"
|
||||||
|
}
|
||||||
33
apps/admin/public/locales/en-US/dashboard.json
Normal 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"
|
||||||
|
}
|
||||||
28
apps/admin/public/locales/en-US/document.json
Normal 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"
|
||||||
|
}
|
||||||
66
apps/admin/public/locales/en-US/log.json
Normal 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"
|
||||||
|
}
|
||||||
107
apps/admin/public/locales/en-US/marketing.json
Normal 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"
|
||||||
|
}
|
||||||
35
apps/admin/public/locales/en-US/menu.json
Normal 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"
|
||||||
|
}
|
||||||
31
apps/admin/public/locales/en-US/nodes.json
Normal 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"
|
||||||
|
}
|
||||||
29
apps/admin/public/locales/en-US/order.json
Normal 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"
|
||||||
|
}
|
||||||
39
apps/admin/public/locales/en-US/payment.json
Normal 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"
|
||||||
|
}
|
||||||
73
apps/admin/public/locales/en-US/product.json
Normal 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"
|
||||||
|
}
|
||||||
156
apps/admin/public/locales/en-US/servers.json
Normal 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"
|
||||||
|
}
|
||||||
121
apps/admin/public/locales/en-US/subscribe.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
136
apps/admin/public/locales/en-US/system.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
apps/admin/public/locales/en-US/ticket.json
Normal 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"
|
||||||
|
}
|
||||||
14
apps/admin/public/locales/en-US/tool.json
Normal 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"
|
||||||
|
}
|
||||||
16
apps/admin/public/locales/en-US/translation.json
Normal 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"
|
||||||
|
}
|
||||||
108
apps/admin/public/locales/en-US/user.json
Normal 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"
|
||||||
|
}
|
||||||
39
apps/admin/public/locales/zh-CN/ads.json
Normal 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": "有效期"
|
||||||
|
}
|
||||||
30
apps/admin/public/locales/zh-CN/announcement.json
Normal 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": "更新成功"
|
||||||
|
}
|
||||||
179
apps/admin/public/locales/zh-CN/auth-control.json
Normal 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 登录"
|
||||||
|
}
|
||||||
|
}
|
||||||
38
apps/admin/public/locales/zh-CN/auth.json
Normal 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": "重置密码"
|
||||||
|
}
|
||||||
|
}
|
||||||
65
apps/admin/public/locales/zh-CN/components.json
Normal 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": "无限制"
|
||||||
|
}
|
||||||
47
apps/admin/public/locales/zh-CN/coupon.json
Normal 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": "有效期"
|
||||||
|
}
|
||||||
33
apps/admin/public/locales/zh-CN/dashboard.json
Normal 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": "昨日"
|
||||||
|
}
|
||||||
28
apps/admin/public/locales/zh-CN/document.json
Normal 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": "更新成功"
|
||||||
|
}
|
||||||
66
apps/admin/public/locales/zh-CN/log.json
Normal 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": "未知"
|
||||||
|
}
|
||||||
107
apps/admin/public/locales/zh-CN/marketing.json
Normal 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": "是"
|
||||||
|
}
|
||||||
35
apps/admin/public/locales/zh-CN/menu.json
Normal 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": "用户与支持"
|
||||||
|
}
|
||||||
31
apps/admin/public/locales/zh-CN/nodes.json
Normal 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": "已更新"
|
||||||
|
}
|
||||||
29
apps/admin/public/locales/zh-CN/order.json
Normal 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": "用户"
|
||||||
|
}
|
||||||
39
apps/admin/public/locales/zh-CN/payment.json
Normal 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": "更新成功"
|
||||||
|
}
|
||||||
73
apps/admin/public/locales/zh-CN/product.json
Normal 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": "更新成功"
|
||||||
|
}
|
||||||
156
apps/admin/public/locales/zh-CN/servers.json
Normal 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": "用户"
|
||||||
|
}
|
||||||
119
apps/admin/public/locales/zh-CN/subscribe.json
Normal 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": "模板预览"
|
||||||
|
}
|
||||||
|
}
|
||||||
136
apps/admin/public/locales/zh-CN/system.json
Normal 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": "验证码设置"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
apps/admin/public/locales/zh-CN/ticket.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"cancel": "取消",
|
||||||
|
"check": "查看",
|
||||||
|
"close": "关闭",
|
||||||
|
"closeSuccess": "关闭成功",
|
||||||
|
"closeWarning": "一旦关闭,工单将无法操作,请谨慎操作。",
|
||||||
|
"confirm": "确认",
|
||||||
|
"confirmClose": "确定要关闭吗?",
|
||||||
|
"inputPlaceholder": "请输入您的问题,我们会尽快回复。",
|
||||||
|
"reply": "回复",
|
||||||
|
"status": {
|
||||||
|
"0": "状态",
|
||||||
|
"1": "待跟进",
|
||||||
|
"2": "待回复",
|
||||||
|
"3": "已解决",
|
||||||
|
"4": "已关闭"
|
||||||
|
},
|
||||||
|
"ticketList": "工单列表",
|
||||||
|
"title": "标题",
|
||||||
|
"updatedAt": "更新时间",
|
||||||
|
"user": "用户"
|
||||||
|
}
|
||||||
14
apps/admin/public/locales/zh-CN/tool.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"cancel": "取消",
|
||||||
|
"confirmReboot": "确认重启",
|
||||||
|
"confirmSystemReboot": "确认系统重启",
|
||||||
|
"newVersionAvailable": "有新版本可用",
|
||||||
|
"rebootDescription": "确定要重启系统吗?此操作无法撤销。",
|
||||||
|
"rebooting": "重启中...",
|
||||||
|
"refreshLogs": "刷新日志",
|
||||||
|
"serverVersion": "服务器版本",
|
||||||
|
"systemLogs": "系统日志",
|
||||||
|
"systemReboot": "系统重启",
|
||||||
|
"systemServices": "系统服务",
|
||||||
|
"webVersion": "前端版本"
|
||||||
|
}
|
||||||
16
apps/admin/public/locales/zh-CN/translation.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"errors": {
|
||||||
|
"nameRequired": "请输入名称",
|
||||||
|
"portRange": "端口必须在 1 到 65535 之间",
|
||||||
|
"protocolRequired": "请选择协议",
|
||||||
|
"serverAddrRequired": "请输入入口地址",
|
||||||
|
"serverRequired": "请选择服务器"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"validation": {
|
||||||
|
"nameRequired": "客户端名称必填",
|
||||||
|
"userAgentRequiredSuffix": "是必填项"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tos": "服务条款"
|
||||||
|
}
|
||||||
108
apps/admin/public/locales/zh-CN/user.json
Normal 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": "已验证"
|
||||||
|
}
|
||||||
19241
apps/admin/public/lotties/login.json
Normal file
11
apps/admin/public/payment/alipay.svg
Normal 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 |
11
apps/admin/public/payment/alipay_f2f.svg
Normal 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 |
14
apps/admin/public/payment/balance.svg
Normal 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 |
12
apps/admin/public/payment/epay.svg
Normal 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 |
12
apps/admin/public/payment/stripe_alipay.svg
Normal 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 |
14
apps/admin/public/payment/stripe_wechat_pay.svg
Normal 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 |
13
apps/admin/public/payment/wechat_pay.svg
Normal 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 |
BIN
apps/admin/public/pwa-192x192.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
apps/admin/public/pwa-512x512.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
apps/admin/public/pwa-maskable-192x192.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
apps/admin/public/pwa-maskable-512x512.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
3
apps/admin/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
34
apps/admin/public/site.webmanifest
Normal 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"
|
||||||
|
}
|
||||||
49
apps/admin/src/components/display.tsx
Normal 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";
|
||||||
|
}
|
||||||
32
apps/admin/src/components/ip-link.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
apps/admin/src/components/order-link.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
10
apps/admin/src/config/index.ts
Normal 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;
|
||||||
56
apps/admin/src/layout/header.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
32
apps/admin/src/layout/index.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
226
apps/admin/src/layout/navs.ts
Normal 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);
|
||||||
|
}
|
||||||
294
apps/admin/src/layout/sidebar-left.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
315
apps/admin/src/layout/timezone-switch.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
73
apps/admin/src/layout/user-nav.tsx
Normal 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
@ -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();
|
||||||
13
apps/admin/src/reportWebVitals.ts
Normal 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;
|
||||||