🎉 feat: initialization

This commit is contained in:
web
2025-11-26 19:56:16 -08:00
commit a801849fb2
553 changed files with 213088 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"projectName": "apps/user",
"mode": "file-router",
"typescript": true,
"tailwind": true,
"packageManager": "bun",
"addOnOptions": {},
"git": true,
"version": 1,
"framework": "react-cra",
"chosenAddOns": ["biome", "tanstack-query", "table", "form", "shadcn"]
}
+10
View File
@@ -0,0 +1,10 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
count.txt
.env
.nitro
.tanstack
.wrangler
+35
View File
@@ -0,0 +1,35 @@
{
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
},
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[css]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit"
}
}
+141
View File
@@ -0,0 +1,141 @@
<a name="readme-top"></a>
<div align="center">
<img width="160" src="https://raw.githubusercontent.com/perfect-panel/ppanel-assets/refs/heads/main/logo.svg">
<h1>PPanel admin web</h1>
This is a PPanel admin web powered by PPanel
English
·
[Chinese](./README.zh-CN.md)
·
[Changelog](../../CHANGELOG.md)
·
[Report Bug][issues-link]
·
[Request Feature][issues-link]
<!-- SHIELD GROUP -->
[![][github-release-shield]][github-release-link]
[![][github-releasedate-shield]][github-releasedate-link]
[![][github-action-test-shield]][github-action-test-link]
[![][github-action-release-shield]][github-action-release-link]<br/>
[![][github-contributors-shield]][github-contributors-link]
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
[![][github-license-shield]][github-license-link]
![](https://urlscan.io/liveshot/?width=1920&height=1080&url=https://admin.ppanel.dev)
</div>
<details>
<summary><kbd>Table of contents</kbd></summary>
#### TOC
- [⌨️ Local Development](#-local-development)
- [🚀 Deploy on Vercel](#-deploy-on-vercel)
- [🤝 Contributing](#-contributing)
- [📝 License](#-license)
####
</details>
## ⌨️ Local Development
You can use Github Codespaces for online development:
[![][codespaces-shield]][codespaces-link]
You can use Gitpod for online development:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][gitpod-link]
or clone it for local development:
```bash
git clone https://github.com/perfect-panel/ppanel-web.git
cd ppanel-web
# Install dependencies
bun install
# Run the development server
cd apps/admin
bun dev
```
Open <http://localhost:3000> with your browser to see the result.
## 🚀 Deploy on Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fadmin.ppanel.dev&demo-title=PPanel%20Admin%20Web&demo-url=https%3A%2F%2Fadmin.ppanel.dev%2F&from=.&project-name=ppanel-admin-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fadmin&skippable-integrations=1)
The easiest way to deploy your Next.js app is to use the
[Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme)
from the creators of Next.js.
Check out our
[Next.js deployment documentation](https://nextjs.org/docs/deployment)
for more details.
## 🤝 Contributing
Contributions of all types are more than welcome,
if you're interested in contributing code, feel free to check out our GitHub
[Issues][github-issues-link] to get stuck in to show us what youre made of.
[![][pr-welcome-shield]][pr-welcome-link]
[![][contributors-contrib]][contributors-url]
<div align="right">
[![][back-to-top]](#readme-top)
</div>
---
## 📝 License
Copyright © 2024 [PPanel][profile-link]. <br />
This project is [GNU](../../LICENSE) licensed.
<!-- LINK GROUP -->
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square
[codespaces-link]: https://codespaces.new/perfect-panel/ppanel-web
[codespaces-shield]: https://github.com/codespaces/badge.svg
[contributors-contrib]: https://contrib.rocks/image?repo=perfect-panel/ppanel-web
[contributors-url]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-action-release-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/release.yml
[github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-action-test-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/test.yml
[github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-contributors-link]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-contributors-shield]: https://img.shields.io/github/contributors/perfect-panel/ppanel-web?color=c4f042&labelColor=black&style=flat-square
[github-forks-link]: https://github.com/perfect-panel/ppanel-web/network/members
[github-forks-shield]: https://img.shields.io/github/forks/perfect-panel/ppanel-web?color=8ae8ff&labelColor=black&style=flat-square
[github-issues-link]: https://github.com/perfect-panel/ppanel-web/issues
[github-issues-shield]: https://img.shields.io/github/issues/perfect-panel/ppanel-web?color=ff80eb&labelColor=black&style=flat-square
[github-license-link]: https://github.com/perfect-panel/ppanel-web/blob/master/LICENSE
[github-license-shield]: https://img.shields.io/github/license/perfect-panel/ppanel-web?color=white&labelColor=black&style=flat-square
[github-release-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-release-shield]: https://img.shields.io/github/v/release/perfect-panel/ppanel-web?style=flat-square&sort=semver&logo=github
[github-releasedate-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/perfect-panel/ppanel-web?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/perfect-panel/ppanel-web/network/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/perfect-panel/ppanel-web?color=ffcb47&labelColor=black&style=flat-square
[gitpod-link]: https://gitpod.io/#https://github.com/perfect-panel/ppanel-web
[issues-link]: https://github.com/perfect-panel/ppanel-web/issues/new/choose
[pr-welcome-link]: https://github.com/perfect-panel/ppanel-web/pulls
[pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/perfect-panel
+141
View File
@@ -0,0 +1,141 @@
<a name="readme-top"></a>
<div align="center">
<img width="160" src="https://raw.githubusercontent.com/perfect-panel/ppanel-assets/refs/heads/main/logo.svg">
<h1>PPanel 管理后台</h1>
这是由 PPanel 提供支持的 PPanel 管理后台
[英文](./README.md)
·
中文
·
[更新日志](../../CHANGELOG.md)
·
[报告问题][issues-link]
·
[请求功能][issues-link]
<!-- SHIELD GROUP -->
[![][github-release-shield]][github-release-link]
[![][github-releasedate-shield]][github-releasedate-link]
[![][github-action-test-shield]][github-action-test-link]
[![][github-action-release-shield]][github-action-release-link]<br/>
[![][github-contributors-shield]][github-contributors-link]
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
[![][github-license-shield]][github-license-link]
![](https://urlscan.io/liveshot/?width=1920&height=1080&url=https://admin.ppanel.dev)
</div>
<details>
<summary><kbd>目录</kbd></summary>
#### 目录
- [⌨️ 本地开发](#-本地开发)
- [🚀 在 Vercel 上部署](#-在-vercel-上部署)
- [🤝 贡献](#-贡献)
- [📝 许可证](#-许可证)
####
</details>
## ⌨️ 本地开发
您可以使用 Github Codespaces 进行在线开发:
[![][codespaces-shield]][codespaces-link]
您可以使用 Gitpod 进行在线开发:
[![在 Gitpod 中打开](https://gitpod.io/button/open-in-gitpod.svg)][gitpod-link]
或者克隆项目进行本地开发:
```bash
git clone https://github.com/perfect-panel/ppanel-web.git
cd ppanel-web
# 安装依赖
bun install
# 运行开发服务器
cd apps/admin
bun dev
```
在浏览器中打开 <http://localhost:3000> 查看结果。
## 🚀 在 Vercel 上部署
[![使用 Vercel 部署](https://vercel.com/button)](https://vercel.com/new/clone?demo-description=PPanel%20is%20a%20pure%2C%20professional%2C%20and%20perfect%20open-source%20proxy%20panel%20tool%2C%20designed%20to%20be%20your%20ideal%20choice%20for%20learning%20and%20practical%20use&demo-image=https%3A%2F%2Furlscan.io%2Fliveshot%2F%3Fwidth%3D1920%26height%3D1080%26url%3Dhttps%3A%2F%2Fadmin.ppanel.dev&demo-title=PPanel%20Admin%20Web&demo-url=https%3A%2F%2Fadmin.ppanel.dev%2F&from=.&project-name=ppanel-admin-web&repository-name=ppanel-web&repository-url=https%3A%2F%2Fgithub.com%2Fperfect-panel%2Fppanel-web&root-directory=apps%2Fadmin&skippable-integrations=1)
部署 Next.js 应用的最简单方式是使用
[ Vercel 平台](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme)
由 Next.js 的创建者提供支持。
查看我们的
[Next.js 部署文档](https://nextjs.org/docs/deployment)
获取更多详情。
## 🤝 贡献
欢迎各种类型的贡献,
如果您有兴趣贡献代码,请随时查看我们的 GitHub
[问题][github-issues-link] 来展示您的能力。
[![][pr-welcome-shield]][pr-welcome-link]
[![][contributors-contrib]][contributors-url]
<div align="right">
[![][back-to-top]](#readme-top)
</div>
---
## 📝 许可证
版权所有 © 2024 [PPanel][profile-link]。<br />
本项目使用 [GNU](./LICENSE) 许可证。
<!-- LINK GROUP -->
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square
[codespaces-link]: https://codespaces.new/perfect-panel/ppanel-web
[codespaces-shield]: https://github.com/codespaces/badge.svg
[contributors-contrib]: https://contrib.rocks/image?repo=perfect-panel/ppanel-web
[contributors-url]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-action-release-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/release.yml
[github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-action-test-link]: https://github.com/perfect-panel/ppanel-web/actions/workflows/test.yml
[github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/perfect-panel/ppanel-web/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-contributors-link]: https://github.com/perfect-panel/ppanel-web/graphs/contributors
[github-contributors-shield]: https://img.shields.io/github/contributors/perfect-panel/ppanel-web?color=c4f042&labelColor=black&style=flat-square
[github-forks-link]: https://github.com/perfect-panel/ppanel-web/network/members
[github-forks-shield]: https://img.shields.io/github/forks/perfect-panel/ppanel-web?color=8ae8ff&labelColor=black&style=flat-square
[github-issues-link]: https://github.com/perfect-panel/ppanel-web/issues
[github-issues-shield]: https://img.shields.io/github/issues/perfect-panel/ppanel-web?color=ff80eb&labelColor=black&style=flat-square
[github-license-link]: https://github.com/perfect-panel/ppanel-web/blob/master/LICENSE
[github-license-shield]: https://img.shields.io/github/license/perfect-panel/ppanel-web?color=white&labelColor=black&style=flat-square
[github-release-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-release-shield]: https://img.shields.io/github/v/release/perfect-panel/ppanel-web?style=flat-square&sort=semver&logo=github
[github-releasedate-link]: https://github.com/perfect-panel/ppanel-web/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/perfect-panel/ppanel-web?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/perfect-panel/ppanel-web/network/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/perfect-panel/ppanel-web?color=ffcb47&labelColor=black&style=flat-square
[gitpod-link]: https://gitpod.io/#https://github.com/perfect-panel/ppanel-web
[issues-link]: https://github.com/perfect-panel/ppanel-web/issues/new/choose
[pr-welcome-link]: https://github.com/perfect-panel/ppanel-web/pulls
[pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/perfect-panel
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "../../packages/ui/src/styles/globals.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@workspace/ui/components",
"utils": "@workspace/ui/lib/utils",
"hooks": "@workspace/ui/hooks",
"lib": "@workspace/ui/lib",
"ui": "@workspace/ui/components"
},
"iconLibrary": "lucide"
}
+23
View File
@@ -0,0 +1,23 @@
import { defineConfig } from "i18next-cli";
/**
* i18next CLI configuration
*
* Used for extracting translation keys and managing translation files.
* Inherits language settings from the shared i18n configuration.
*
* @see https://github.com/i18next/i18next-cli
*/
export default defineConfig({
// Use supported languages from shared config
locales: ["en-US", "zh-CN"],
// Extraction configuration
extract: {
input: [
"src/**/*.{js,jsx,ts,tsx}",
"../../packages/ui/src/**/*.{js,jsx,ts,tsx}",
], // Source files to scan
output: "public/locales/{{language}}/{{namespace}}.json", // Output path template
},
});
+35
View File
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicons -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<!-- PWA -->
<link rel="manifest" href="/site.webmanifest">
<meta
name="theme-color"
content="#FFFFFF"
media="(prefers-color-scheme: light)"
>
<meta
name="theme-color"
content="#000000"
media="(prefers-color-scheme: dark)"
>
<!-- SEO -->
<meta name="description" content="Loading...">
<meta name="keywords" content="">
<title>Loading...</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+52
View File
@@ -0,0 +1,52 @@
{
"name": "ppanel-admin-web",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3001",
"build": "vite build && tsc",
"serve": "vite preview",
"lint": "biome lint",
"check": "biome check",
"i18n:extract": "i18next-cli extract",
"i18n:sync": "i18next-cli sync",
"i18n:status": "i18next-cli status"
},
"dependencies": {
"@faker-js/faker": "^10.0.0",
"@lottiefiles/dotlottie-react": "^0.17.7",
"@noble/curves": "^2.0.1",
"@stripe/react-stripe-js": "^5.4.0",
"@stripe/stripe-js": "^8.5.2",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/react-devtools": "^0.7.0",
"@tanstack/react-form": "^1.0.0",
"@tanstack/react-router-devtools": "^1.132.0",
"@tanstack/react-table": "^8.21.2",
"@tanstack/router-plugin": "^1.132.0",
"@workspace/typescript-config": "workspace:*",
"@workspace/ui": "workspace:*",
"ahooks": "^3.9.6",
"date-fns": "^4.1.0",
"framer-motion": "^12.23.24",
"mlkem-wasm": "^0.0.7",
"motion": "^12.23.24",
"qrcode.react": "^4.2.0",
"radash": "^12.1.1",
"react-copy-to-clipboard": "^5.1.0",
"react-helmet-async": "^2.0.5",
"react-turnstile": "^1.1.4",
"zustand": "^5.0.8"
},
"devDependencies": {
"@tanstack/devtools-vite": "^0.3.11",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.2.0",
"@vitejs/plugin-react": "^5.0.4",
"jsdom": "^27.0.0",
"vite": "^7.1.7",
"vitest": "^3.0.5",
"web-vitals": "^5.1.0"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

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

After

Width:  |  Height:  |  Size: 1.1 KiB

+39
View File
@@ -0,0 +1,39 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"create": "Create",
"createAds": "Create Ad",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteSuccess": "Deleted successfully",
"deleteWarning": "Are you sure you want to delete this ad? This action cannot be undone.",
"disabled": "Disabled",
"edit": "Edit",
"editAds": "Edit Ad",
"enabled": "Enabled",
"form": {
"cancel": "Cancel",
"confirm": "Confirm",
"content": "Content",
"description": "Description",
"endTime": "End Time",
"enterDescription": "Enter description",
"enterEndTime": "Select end time",
"enterStartTime": "Select start time",
"enterTargetUrl": "Enter target URL",
"enterTitle": "Enter title",
"startTime": "Start Time",
"targetUrl": "Target URL",
"title": "Title",
"type": "Type",
"typeImage": "Image",
"typeVideo": "Video"
},
"status": "Status",
"targetUrl": "Target URL",
"title": "Title",
"type": "Type",
"updateSuccess": "Updated successfully",
"validityPeriod": "Validity Period"
}
@@ -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"
}
@@ -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
View File
@@ -0,0 +1,38 @@
{
"get": "Get Code",
"login": {
"email": "Please enter a valid email address",
"emailPlaceholder": "Enter your email...",
"forgotPassword": "Forgot Password?",
"passwordPlaceholder": "Enter your password...",
"registerAccount": "Register Account",
"success": "Login successful!",
"title": "Login"
},
"logout": "Logout",
"register": {
"codePlaceholder": "Enter code...",
"email": "Please enter a valid email address",
"emailPlaceholder": "Enter your email...",
"existingAccount": "Already have an account?",
"invite": "Invitation Code (Optional)",
"message": "Registration is currently disabled",
"passwordMismatch": "Passwords do not match",
"passwordPlaceholder": "Enter your password...",
"repeatPasswordPlaceholder": "Enter password again...",
"success": "Registration successful!",
"switchToLogin": "Login",
"title": "Register",
"whitelist": "This email domain is not in the whitelist"
},
"reset": {
"codePlaceholder": "Enter code...",
"email": "Please enter a valid email address",
"emailPlaceholder": "Enter your email...",
"existingAccount": "Remember your password?",
"passwordPlaceholder": "Enter your new password...",
"success": "Password reset successful!",
"switchToLogin": "Login",
"title": "Reset Password"
}
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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
View File
@@ -0,0 +1,66 @@
{
"column": {
"amount": "Amount",
"balance": "Balance",
"content": "Content",
"date": "Date",
"download": "Download",
"identifier": "Identifier",
"ip": "IP",
"orderNo": "Order No.",
"platform": "Platform",
"remark": "Remark",
"server": "Server",
"serverId": "Server ID",
"status": "Status",
"subject": "Subject",
"subscribe": "Subscribe",
"subscribeId": "Subscribe ID",
"success": "Success",
"time": "Time",
"to": "To",
"total": "Total",
"type": "Type",
"upload": "Upload",
"user": "User",
"userAgent": "User Agent",
"userId": "User ID"
},
"detail": "Detail",
"failed": "Failed",
"sent": "Sent",
"success": "Success",
"title": {
"balance": "Balance Log",
"commission": "Commission Log",
"email": "Email Log",
"gift": "Gift Log",
"login": "Login Log",
"mobile": "SMS Log",
"register": "Register Log",
"resetSubscribe": "Reset Subscribe Log",
"serverTraffic": "Server Traffic Log",
"subscribe": "Subscribe Log",
"subscribeTraffic": "Subscribe Traffic Log",
"trafficDetails": "Traffic Details"
},
"type": {
"231": "Auto Reset",
"232": "Advance Reset",
"233": "Paid Reset",
"321": "Recharge",
"322": "Withdraw",
"323": "Payment",
"324": "Refund",
"325": "Reward",
"326": "Admin Adjust",
"331": "Purchase",
"332": "Renewal",
"333": "Refund",
"334": "Withdraw",
"335": "Admin Adjust",
"341": "Increase",
"342": "Reduce"
},
"unknown": "Unknown"
}
@@ -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
View File
@@ -0,0 +1,35 @@
{
"ADS Config": "ADS Config",
"Announcement Management": "Announcement Management",
"Auth Control": "Auth Control",
"Balance": "Balance",
"Commerce": "Commerce",
"Commission": "Commission",
"Coupon Management": "Coupon Management",
"Dashboard": "Dashboard",
"Document Management": "Document Management",
"Email": "Email",
"Gift": "Gift",
"Login": "Login",
"Logs & Analytics": "Logs & Analytics",
"Maintenance": "Maintenance",
"Marketing Management": "Marketing Management",
"Mobile": "Mobile",
"Node Management": "Node Management",
"Order Management": "Order Management",
"Payment Config": "Payment Config",
"Product Management": "Product Management",
"Register": "Register",
"Reset Subscribe": "Reset Subscribe",
"Server Management": "Server Management",
"Server Traffic": "Server Traffic",
"Subscribe": "Subscribe",
"Subscribe Config": "Subscribe Config",
"Subscribe Traffic": "Subscribe Traffic",
"System": "System",
"System Config": "System Config",
"Ticket Management": "Ticket Management",
"Traffic Details": "Traffic Details",
"User Management": "User Management",
"Users & Support": "Users & Support"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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
View File
@@ -0,0 +1,136 @@
{
"basicSettings": "Basic Settings",
"common": {
"cancel": "Cancel",
"save": "Save Settings",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful"
},
"currency": {
"accessKey": "API Key",
"accessKeyDescription": "Free exchange rate API key provided by {{url}}",
"accessKeyPlaceholder": "Enter API key",
"currencySymbol": "Currency Symbol",
"currencySymbolDescription": "Used for display purposes only; changing this will affect all currency units in the system",
"currencySymbolPlaceholder": "$",
"currencyUnit": "Currency Unit",
"currencyUnitDescription": "Used for display purposes only; changing this will affect all currency units in the system",
"currencyUnitPlaceholder": "USD",
"description": "Configure currency units, symbols, and exchange rate API settings",
"title": "Currency Configuration"
},
"invite": {
"description": "Configure user invitation and referral reward settings",
"forcedInvite": "Require Invitation to Register",
"forcedInviteDescription": "When enabled, users must register through an invitation link",
"inputPlaceholder": "Please enter",
"onlyFirstPurchase": "First Purchase Reward Only",
"onlyFirstPurchaseDescription": "When enabled, referrers only receive rewards for the first purchase by referred users",
"referralPercentage": "Referral Reward Percentage",
"referralPercentageDescription": "Percentage of reward given to referrers",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"title": "Invitation Settings"
},
"logCleanup": {
"autoClear": "Enable Auto Cleanup",
"autoClearDescription": "When enabled, the system will automatically clear expired log records",
"clearDays": "Retention Days",
"clearDaysDescription": "Number of days to retain logs; logs older than this will be cleaned up",
"clearDaysPlaceholder": "Enter retention days",
"description": "Configure automatic log cleanup rules and retention period",
"title": "Log Cleanup Settings"
},
"logSettings": "Log Settings",
"privacyPolicy": {
"description": "Edit and manage privacy policy content",
"title": "Privacy Policy"
},
"register": {
"day": "Day(s)",
"description": "Configure user registration related settings",
"enableTrial": "Enable Trial",
"enableTrialDescription": "When enabled, new users will receive a trial subscription upon registration",
"hour": "Hour(s)",
"inputPlaceholder": "Please enter",
"ipRegistrationLimit": "IP Registration Limit",
"ipRegistrationLimitDescription": "Limit the number of registrations from a single IP address",
"minute": "Minute(s)",
"month": "Month(s)",
"none": "None",
"registrationLimitCount": "Registration Limit Count",
"registrationLimitCountDescription": "Number of registrations allowed per IP within the limit period",
"registrationLimitExpire": "Limit Period",
"registrationLimitExpireDescription": "Duration for IP registration limit (minutes)",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"selectPlaceholder": "Please select",
"stopNewUserRegistration": "Stop New User Registration",
"stopNewUserRegistrationDescription": "When enabled, new user registration will be disabled",
"title": "Registration Settings",
"trialConfig": "Trial Configuration",
"trialConfigDescription": "Configure trial subscription, duration and time unit for new users upon registration",
"year": "Year(s)"
},
"site": {
"customData": "Custom Data",
"customDataDescription": "Custom data for website customization",
"customHtml": "Custom HTML",
"customHtmlDescription": "Custom HTML code to be injected into the bottom of the site's body tag",
"description": "Configure basic site information, logo, domain and other settings",
"keywords": "Keywords",
"keywordsDescription": "Used for SEO purposes",
"keywordsPlaceholder": "keyword1, keyword2, keyword3",
"logo": "Site Logo",
"logoDescription": "Used for displaying the logo in designated locations",
"logoPlaceholder": "Enter the URL of the logo, without ending with '/'",
"siteDesc": "Site Description",
"siteDescDescription": "Used for displaying the site description in designated locations",
"siteDescPlaceholder": "Enter site description",
"siteDomain": "Site Domain",
"siteDomainDescription": "Domain address of the current website, e.g., used in emails",
"siteDomainPlaceholder": "Please enter the domain address. For multiple domains, please enter one per line.",
"siteName": "Site Name",
"siteNameDescription": "Used for displaying the site name in designated locations",
"siteNamePlaceholder": "Enter site name",
"title": "Site Configuration"
},
"tos": {
"description": "Edit and manage terms of service content",
"title": "Terms of Service"
},
"userSecuritySettings": "User & Security",
"verify": {
"description": "Configure Turnstile CAPTCHA and verification settings",
"enableLoginVerify": "Enable Verification on Login",
"enableLoginVerifyDescription": "When enabled, users must pass human verification during login",
"enablePasswordVerify": "Enable Verification on Password Reset",
"enablePasswordVerifyDescription": "When enabled, users must pass human verification during password reset",
"enableRegisterVerify": "Enable Verification on Registration",
"enableRegisterVerifyDescription": "When enabled, users must pass human verification during registration",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"title": "Security Verification",
"turnstileSecret": "Turnstile Secret Key",
"turnstileSecretDescription": "Cloudflare Turnstile secret key for backend verification",
"turnstileSecretPlaceholder": "Enter Turnstile secret key",
"turnstileSiteKey": "Turnstile Site Key",
"turnstileSiteKeyDescription": "Cloudflare Turnstile site key for frontend verification",
"turnstileSiteKeyPlaceholder": "Enter Turnstile site key"
},
"verifyCode": {
"dailyLimit": "Daily Sending Limit",
"dailyLimitDescription": "Maximum number of verification codes each user can send per day",
"description": "Configure email verification code sending rules and limits",
"expireTime": "Verification Code Validity",
"expireTimeDescription": "Validity period of verification codes (seconds)",
"inputPlaceholder": "Please enter",
"interval": "Sending Interval",
"intervalDescription": "Minimum interval between two verification code sends (seconds)",
"saveFailed": "Save Failed",
"saveSuccess": "Save Successful",
"seconds": "seconds",
"times": "time(s)",
"title": "Verification Code Settings"
}
}
@@ -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
View File
@@ -0,0 +1,14 @@
{
"cancel": "Cancel",
"confirmReboot": "Confirm Reboot",
"confirmSystemReboot": "Confirm System Reboot",
"newVersionAvailable": "New Version Available",
"rebootDescription": "Are you sure you want to reboot the system? This action cannot be undone.",
"rebooting": "Rebooting...",
"refreshLogs": "Refresh Logs",
"serverVersion": "Server Version",
"systemLogs": "System Logs",
"systemReboot": "System Reboot",
"systemServices": "System Services",
"webVersion": "Web Version"
}
@@ -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
View File
@@ -0,0 +1,108 @@
{
"accountEnable": "Account Enable",
"add": "Add",
"administrator": "Administrator",
"areaCodePlaceholder": "Area code",
"authMethodsTitle": "Auth Methods",
"avatar": "Avatar",
"balance": "Balance",
"balanceLogs": "Balance Logs",
"balanceNotifications": "Balance Notifications",
"balancePlaceholder": "Enter balance",
"basicInfoTitle": "Basic Info",
"cancel": "Cancel",
"commission": "Commission",
"commissionLogs": "Commission Logs",
"commissionPlaceholder": "Enter commission",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"confirmOffline": "Confirm Offline",
"copySubscription": "Copy Subscription",
"copySuccess": "Copied successfully",
"create": "Create",
"createdAt": "Created At",
"createSubscription": "Create Subscription",
"createSuccess": "Created successfully",
"createUser": "Create User",
"delete": "Delete",
"deleteDescription": "This action cannot be undone.",
"deleteSubscriptionDescription": "This action cannot be undone.",
"deleteSuccess": "Deleted successfully",
"deviceLimit": "Device Limit",
"download": "Download",
"downloadTraffic": "Download Traffic",
"edit": "Edit",
"editSubscription": "Edit Subscription",
"enable": "Enable",
"expiredAt": "Expired At",
"expireTime": "expireTime",
"giftAmount": "Gift Amount",
"giftAmountPlaceholder": "Enter gift amount",
"giftLogs": "Gift Logs",
"invalidEmailFormat": "Invalid email format",
"inviteCode": "Invite Code",
"inviteCodePlaceholder": "Enter invite code",
"kickOfflineConfirm": "kickOfflineConfirm",
"kickOfflineSuccess": "Device kicked offline",
"lastSeen": "Last Seen",
"loading": "Loading...",
"loginLogs": "Login Logs",
"loginNotifications": "Login Notifications",
"loginStatus": "Login Status",
"manager": "Administrator",
"more": "More",
"notifySettingsTitle": "Notify Settings",
"offline": "Offline",
"online": "Online",
"onlineDevices": "Online Devices",
"onlyFirstPurchase": "First Purchase Only",
"orderList": "Order List",
"password": "Password",
"passwordPlaceholder": "Enter password",
"permanent": "Permanent",
"pleaseEnterEmail": "Enter email",
"referer": "Referer",
"refererId": "Referer ID",
"refererIdPlaceholder": "Enter referer ID",
"referralCode": "Referral Code",
"referralPercentage": "Referral Percentage",
"referralPercentagePlaceholder": "Enter percentage",
"referrerUserId": "Referrer User ID",
"remove": "Remove",
"resetLogs": "Reset Logs",
"resetTime": "Reset Time",
"save": "Save",
"speedLimit": "Speed Limit",
"startTime": "startTime",
"subscription": "Subscription",
"subscriptionId": "subscriptionId",
"subscriptionInfo": "subscriptionInfo",
"subscriptionList": "Subscription List",
"subscriptionLogs": "Subscription Logs",
"subscriptionName": "subscriptionName",
"subscriptionNotifications": "Subscription Notifications",
"telephone": "Phone",
"telephonePlaceholder": "Enter phone number",
"token": "token",
"totalTraffic": "Total Traffic",
"tradeNotifications": "Trade Notifications",
"trafficDetails": "Traffic Details",
"trafficLimit": "Traffic Limit",
"trafficStats": "Traffic Stats",
"trafficUsage": "trafficUsage",
"unlimited": "unlimited",
"unverified": "Unverified",
"update": "Update",
"updateSuccess": "Updated successfully",
"upload": "Upload",
"uploadTraffic": "Upload Traffic",
"userAgent": "User Agent",
"userEmail": "Email",
"userEmailPlaceholder": "Enter email",
"userId": "userId",
"userInfo": "userInfo",
"userList": "User List",
"userName": "Username",
"userProfile": "User Profile",
"verified": "Verified"
}
+39
View File
@@ -0,0 +1,39 @@
{
"cancel": "取消",
"confirm": "确认",
"confirmDelete": "确认删除",
"create": "创建",
"createAds": "创建广告",
"createSuccess": "创建成功",
"delete": "删除",
"deleteSuccess": "删除成功",
"deleteWarning": "确定要删除此广告吗?此操作无法撤销。",
"disabled": "已禁用",
"edit": "编辑",
"editAds": "编辑广告",
"enabled": "已启用",
"form": {
"cancel": "取消",
"confirm": "确认",
"content": "内容",
"description": "描述",
"endTime": "结束时间",
"enterDescription": "输入描述",
"enterEndTime": "选择结束时间",
"enterStartTime": "选择开始时间",
"enterTargetUrl": "输入目标链接",
"enterTitle": "输入标题",
"startTime": "开始时间",
"targetUrl": "目标链接",
"title": "标题",
"type": "类型",
"typeImage": "图片",
"typeVideo": "视频"
},
"status": "状态",
"targetUrl": "目标链接",
"title": "标题",
"type": "类型",
"updateSuccess": "更新成功",
"validityPeriod": "有效期"
}
@@ -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": "更新成功"
}
@@ -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
View File
@@ -0,0 +1,38 @@
{
"get": "获取验证码",
"login": {
"email": "请输入有效的邮箱地址",
"emailPlaceholder": "输入您的邮箱...",
"forgotPassword": "忘记密码?",
"passwordPlaceholder": "输入您的密码...",
"registerAccount": "注册账号",
"success": "登录成功!",
"title": "登录"
},
"logout": "退出登录",
"register": {
"codePlaceholder": "输入验证码...",
"email": "请输入有效的邮箱地址",
"emailPlaceholder": "输入您的邮箱...",
"existingAccount": "已有账号?",
"invite": "邀请码(可选)",
"message": "注册功能暂时不可用",
"passwordMismatch": "两次密码输入不一致",
"passwordPlaceholder": "输入您的密码...",
"repeatPasswordPlaceholder": "再次输入密码...",
"success": "注册成功!",
"switchToLogin": "登录",
"title": "注册",
"whitelist": "该邮箱域名不在白名单中"
},
"reset": {
"codePlaceholder": "输入验证码...",
"email": "请输入有效的邮箱地址",
"emailPlaceholder": "输入您的邮箱...",
"existingAccount": "记得密码了?",
"passwordPlaceholder": "输入您的新密码...",
"success": "密码重置成功!",
"switchToLogin": "登录",
"title": "重置密码"
}
}
@@ -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": "无限制"
}
@@ -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": "有效期"
}
@@ -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": "昨日"
}
@@ -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
View File
@@ -0,0 +1,66 @@
{
"column": {
"amount": "金额",
"balance": "余额",
"content": "内容",
"date": "日期",
"download": "下载",
"identifier": "标识符",
"ip": "IP",
"orderNo": "订单号",
"platform": "平台",
"remark": "备注",
"server": "服务器",
"serverId": "服务器 ID",
"status": "状态",
"subject": "主题",
"subscribe": "订阅",
"subscribeId": "订阅 ID",
"success": "成功",
"time": "时间",
"to": "收件人",
"total": "总计",
"type": "类型",
"upload": "上传",
"user": "用户",
"userAgent": "用户代理",
"userId": "用户 ID"
},
"detail": "详情",
"failed": "失败",
"sent": "已发送",
"success": "成功",
"title": {
"balance": "余额日志",
"commission": "佣金日志",
"email": "邮件日志",
"gift": "赠送日志",
"login": "登录日志",
"mobile": "短信日志",
"register": "注册日志",
"resetSubscribe": "重置订阅日志",
"serverTraffic": "服务器流量日志",
"subscribe": "订阅日志",
"subscribeTraffic": "订阅流量日志",
"trafficDetails": "流量详情"
},
"type": {
"231": "自动重置",
"232": "提前重置",
"233": "付费重置",
"321": "充值",
"322": "提现",
"323": "支付",
"324": "退款",
"325": "奖励",
"326": "管理员调整",
"331": "购买",
"332": "续费",
"333": "退款",
"334": "提现",
"335": "管理员调整",
"341": "增加",
"342": "减少"
},
"unknown": "未知"
}
@@ -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
View File
@@ -0,0 +1,35 @@
{
"ADS Config": "广告配置",
"Announcement Management": "公告管理",
"Auth Control": "认证控制",
"Balance": "余额",
"Commerce": "商业",
"Commission": "佣金",
"Coupon Management": "优惠券管理",
"Dashboard": "仪表盘",
"Document Management": "文档管理",
"Email": "邮件",
"Gift": "赠送",
"Login": "登录",
"Logs & Analytics": "日志与分析",
"Maintenance": "维护",
"Marketing Management": "营销管理",
"Mobile": "短信",
"Node Management": "节点管理",
"Order Management": "订单管理",
"Payment Config": "支付配置",
"Product Management": "商品管理",
"Register": "注册",
"Reset Subscribe": "重置订阅",
"Server Management": "服务器管理",
"Server Traffic": "服务器流量",
"Subscribe": "订阅",
"Subscribe Config": "订阅配置",
"Subscribe Traffic": "订阅流量",
"System": "系统",
"System Config": "系统配置",
"Ticket Management": "工单管理",
"Traffic Details": "流量详情",
"User Management": "用户管理",
"Users & Support": "用户与支持"
}
@@ -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": "已更新"
}
@@ -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": "用户"
}
@@ -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": "更新成功"
}
@@ -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": "更新成功"
}
@@ -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": "用户"
}
@@ -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
View File
@@ -0,0 +1,136 @@
{
"basicSettings": "基本设置",
"common": {
"cancel": "取消",
"save": "保存设置",
"saveFailed": "保存失败",
"saveSuccess": "保存成功"
},
"currency": {
"accessKey": "API 密钥",
"accessKeyDescription": "由 {{url}} 提供的免费汇率 API 密钥",
"accessKeyPlaceholder": "输入 API 密钥",
"currencySymbol": "货币符号",
"currencySymbolDescription": "仅用于显示;更改此项将影响系统中所有货币单位",
"currencySymbolPlaceholder": "¥",
"currencyUnit": "货币单位",
"currencyUnitDescription": "仅用于显示;更改此项将影响系统中所有货币单位",
"currencyUnitPlaceholder": "CNY",
"description": "配置货币单位、符号和汇率 API 设置",
"title": "货币配置"
},
"invite": {
"description": "配置用户邀请和推荐奖励设置",
"forcedInvite": "强制邀请注册",
"forcedInviteDescription": "启用后,用户必须通过邀请链接注册",
"inputPlaceholder": "请输入",
"onlyFirstPurchase": "仅首次购买奖励",
"onlyFirstPurchaseDescription": "启用后,推荐人仅在被推荐用户首次购买时获得奖励",
"referralPercentage": "推荐奖励百分比",
"referralPercentageDescription": "给予推荐人的奖励百分比",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"title": "邀请设置"
},
"logCleanup": {
"autoClear": "启用自动清理",
"autoClearDescription": "启用后,系统将自动清理过期的日志记录",
"clearDays": "保留天数",
"clearDaysDescription": "日志保留的天数;超过此时间的日志将被清理",
"clearDaysPlaceholder": "输入保留天数",
"description": "配置自动日志清理规则和保留期限",
"title": "日志清理设置"
},
"logSettings": "日志设置",
"privacyPolicy": {
"description": "编辑和管理隐私政策内容",
"title": "隐私政策"
},
"register": {
"day": "天",
"description": "配置用户注册相关设置",
"enableTrial": "启用试用",
"enableTrialDescription": "启用后,新用户注册时将获得试用订阅",
"hour": "小时",
"inputPlaceholder": "请输入",
"ipRegistrationLimit": "IP 注册限制",
"ipRegistrationLimitDescription": "限制单个 IP 地址的注册次数",
"minute": "分钟",
"month": "月",
"none": "无",
"registrationLimitCount": "注册限制次数",
"registrationLimitCountDescription": "限制期内每个 IP 允许的注册次数",
"registrationLimitExpire": "限制期",
"registrationLimitExpireDescription": "IP 注册限制的持续时间(分钟)",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"selectPlaceholder": "请选择",
"stopNewUserRegistration": "停止新用户注册",
"stopNewUserRegistrationDescription": "启用后,将禁止新用户注册",
"title": "注册设置",
"trialConfig": "试用配置",
"trialConfigDescription": "配置新用户注册时的试用订阅、时长和时间单位",
"year": "年"
},
"site": {
"customData": "自定义数据",
"customDataDescription": "用于网站定制的自定义数据",
"customHtml": "自定义 HTML",
"customHtmlDescription": "注入到站点 body 标签底部的自定义 HTML 代码",
"description": "配置基本站点信息、Logo、域名和其他设置",
"keywords": "关键词",
"keywordsDescription": "用于 SEO 目的",
"keywordsPlaceholder": "关键词1, 关键词2, 关键词3",
"logo": "站点 Logo",
"logoDescription": "用于在指定位置显示 Logo",
"logoPlaceholder": "输入 Logo 的 URL,不要以 '/' 结尾",
"siteDesc": "站点描述",
"siteDescDescription": "用于在指定位置显示站点描述",
"siteDescPlaceholder": "输入站点描述",
"siteDomain": "站点域名",
"siteDomainDescription": "当前网站的域名地址,例如用于邮件中",
"siteDomainPlaceholder": "请输入域名地址。多个域名请每行输入一个。",
"siteName": "站点名称",
"siteNameDescription": "用于在指定位置显示站点名称",
"siteNamePlaceholder": "输入站点名称",
"title": "站点配置"
},
"tos": {
"description": "编辑和管理服务条款内容",
"title": "服务条款"
},
"userSecuritySettings": "用户与安全",
"verify": {
"description": "配置 Turnstile 验证码和验证设置",
"enableLoginVerify": "登录验证",
"enableLoginVerifyDescription": "启用后,用户登录时必须通过人机验证",
"enablePasswordVerify": "密码重置验证",
"enablePasswordVerifyDescription": "启用后,用户重置密码时必须通过人机验证",
"enableRegisterVerify": "注册验证",
"enableRegisterVerifyDescription": "启用后,用户注册时必须通过人机验证",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"title": "安全验证",
"turnstileSecret": "Turnstile 密钥",
"turnstileSecretDescription": "用于后端验证的 Cloudflare Turnstile 密钥",
"turnstileSecretPlaceholder": "输入 Turnstile 密钥",
"turnstileSiteKey": "Turnstile 站点密钥",
"turnstileSiteKeyDescription": "用于前端验证的 Cloudflare Turnstile 站点密钥",
"turnstileSiteKeyPlaceholder": "输入 Turnstile 站点密钥"
},
"verifyCode": {
"dailyLimit": "每日发送限制",
"dailyLimitDescription": "每个用户每天可发送的最大验证码数量",
"description": "配置邮件验证码发送规则和限制",
"expireTime": "验证码有效期",
"expireTimeDescription": "验证码的有效期(秒)",
"inputPlaceholder": "请输入",
"interval": "发送间隔",
"intervalDescription": "两次验证码发送之间的最小间隔(秒)",
"saveFailed": "保存失败",
"saveSuccess": "保存成功",
"seconds": "秒",
"times": "次",
"title": "验证码设置"
}
}
@@ -0,0 +1,22 @@
{
"cancel": "取消",
"check": "查看",
"close": "关闭",
"closeSuccess": "关闭成功",
"closeWarning": "一旦关闭,工单将无法操作,请谨慎操作。",
"confirm": "确认",
"confirmClose": "确定要关闭吗?",
"inputPlaceholder": "请输入您的问题,我们会尽快回复。",
"reply": "回复",
"status": {
"0": "状态",
"1": "待跟进",
"2": "待回复",
"3": "已解决",
"4": "已关闭"
},
"ticketList": "工单列表",
"title": "标题",
"updatedAt": "更新时间",
"user": "用户"
}
+14
View File
@@ -0,0 +1,14 @@
{
"cancel": "取消",
"confirmReboot": "确认重启",
"confirmSystemReboot": "确认系统重启",
"newVersionAvailable": "有新版本可用",
"rebootDescription": "确定要重启系统吗?此操作无法撤销。",
"rebooting": "重启中...",
"refreshLogs": "刷新日志",
"serverVersion": "服务器版本",
"systemLogs": "系统日志",
"systemReboot": "系统重启",
"systemServices": "系统服务",
"webVersion": "前端版本"
}
@@ -0,0 +1,16 @@
{
"errors": {
"nameRequired": "请输入名称",
"portRange": "端口必须在 1 到 65535 之间",
"protocolRequired": "请选择协议",
"serverAddrRequired": "请输入入口地址",
"serverRequired": "请选择服务器"
},
"form": {
"validation": {
"nameRequired": "客户端名称必填",
"userAgentRequiredSuffix": "是必填项"
}
},
"tos": "服务条款"
}
+108
View File
@@ -0,0 +1,108 @@
{
"accountEnable": "账户启用",
"add": "添加",
"administrator": "管理员",
"areaCodePlaceholder": "区号",
"authMethodsTitle": "认证方式",
"avatar": "头像",
"balance": "余额",
"balanceLogs": "余额日志",
"balanceNotifications": "余额通知",
"balancePlaceholder": "输入余额",
"basicInfoTitle": "基本信息",
"cancel": "取消",
"commission": "佣金",
"commissionLogs": "佣金日志",
"commissionPlaceholder": "输入佣金",
"confirm": "确认",
"confirmDelete": "确认删除",
"confirmOffline": "确认下线",
"copySubscription": "复制订阅",
"copySuccess": "复制成功",
"create": "创建",
"createdAt": "创建时间",
"createSubscription": "创建订阅",
"createSuccess": "创建成功",
"createUser": "创建用户",
"delete": "删除",
"deleteDescription": "此操作无法撤销。",
"deleteSubscriptionDescription": "此操作无法撤销。",
"deleteSuccess": "删除成功",
"deviceLimit": "设备限制",
"download": "下载",
"downloadTraffic": "下载流量",
"edit": "编辑",
"editSubscription": "编辑订阅",
"enable": "启用",
"expiredAt": "过期时间",
"expireTime": "过期时间",
"giftAmount": "赠送金额",
"giftAmountPlaceholder": "输入赠送金额",
"giftLogs": "赠送日志",
"invalidEmailFormat": "邮箱格式无效",
"inviteCode": "邀请码",
"inviteCodePlaceholder": "输入邀请码",
"kickOfflineConfirm": "确认踢下线",
"kickOfflineSuccess": "设备已踢下线",
"lastSeen": "最后上线",
"loading": "加载中...",
"loginLogs": "登录日志",
"loginNotifications": "登录通知",
"loginStatus": "登录状态",
"manager": "管理员",
"more": "更多",
"notifySettingsTitle": "通知设置",
"offline": "离线",
"online": "在线",
"onlineDevices": "在线设备",
"onlyFirstPurchase": "仅首次购买",
"orderList": "订单列表",
"password": "密码",
"passwordPlaceholder": "输入密码",
"permanent": "永久",
"pleaseEnterEmail": "输入邮箱",
"referer": "推荐人",
"refererId": "推荐人 ID",
"refererIdPlaceholder": "输入推荐人 ID",
"referralCode": "推荐码",
"referralPercentage": "推荐百分比",
"referralPercentagePlaceholder": "输入百分比",
"referrerUserId": "推荐人用户 ID",
"remove": "移除",
"resetLogs": "重置日志",
"resetTime": "重置时间",
"save": "保存",
"speedLimit": "速度限制",
"startTime": "开始时间",
"subscription": "订阅",
"subscriptionId": "订阅 ID",
"subscriptionInfo": "订阅信息",
"subscriptionList": "订阅列表",
"subscriptionLogs": "订阅日志",
"subscriptionName": "订阅名称",
"subscriptionNotifications": "订阅通知",
"telephone": "电话",
"telephonePlaceholder": "输入电话号码",
"token": "令牌",
"totalTraffic": "总流量",
"tradeNotifications": "交易通知",
"trafficDetails": "流量详情",
"trafficLimit": "流量限制",
"trafficStats": "流量统计",
"trafficUsage": "流量使用",
"unlimited": "无限制",
"unverified": "未验证",
"update": "更新",
"updateSuccess": "更新成功",
"upload": "上传",
"uploadTraffic": "上传流量",
"userAgent": "用户代理",
"userEmail": "邮箱",
"userEmailPlaceholder": "输入邮箱",
"userId": "用户 ID",
"userInfo": "用户信息",
"userList": "用户列表",
"userName": "用户名",
"userProfile": "用户资料",
"verified": "已验证"
}
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#0071cd" d="M0.038 23.657c0.003 1.309 1.066 2.367 2.375 2.364 0 0 24.875-0.056 24.875-0.056 1.42-0.003 2.575 1.132 2.606 2.545 0-0.018 0.003-0.035 0.003-0.053 0 0-0.048-21.313-0.048-21.313-0.003-1.438-1.171-2.601-2.609-2.597 0 0-24.875 0.056-24.875 0.056-1.309 0.003-2.367 1.066-2.364 2.375 0 0 0.038 16.68 0.038 16.68z"></path>
<path fill="#1e9b5f" d="M29.298 16.714c0 0-21.592 9.368-21.592 9.368-0.456 0.198-0.987-0.063-1.187-0.582 0 0-5.232-13.621-5.232-13.621-0.2-0.519 0.008-1.101 0.464-1.299 0 0 21.592-9.368 21.592-9.368 0.456-0.198 0.987 0.063 1.187 0.582 0 0 5.232 13.621 5.232 13.621 0.2 0.519-0.008 1.101-0.464 1.299z"></path>
<path fill="#22b573" d="M29.768 20.094c0 0-24.591 5.455-24.591 5.455-0.519 0.115-1.028-0.237-1.138-0.787 0 0-2.859-14.411-2.859-14.411-0.109-0.55 0.223-1.089 0.743-1.204 0 0 24.591-5.455 24.591-5.455 0.519-0.115 1.028 0.237 1.138 0.787 0 0 2.859 14.411 2.859 14.411 0.109 0.55-0.223 1.089-0.743 1.204z"></path>
<path fill="#008cff" d="M31.432 11.74c-0.003-1.309-1.066-2.367-2.375-2.364 0 0-26.448 0.060-26.448 0.060-1.42 0.003-2.575-1.132-2.606-2.545-0 0.018-0.003 0.035-0.003 0.053 0 0 0.048 21.313 0.048 21.313 0.003 1.438 1.171 2.601 2.609 2.597 0 0 26.448-0.060 26.448-0.060 1.309-0.003 2.367-1.066 2.364-2.375 0 0-0.038-16.68-0.038-16.68z"></path>
<path fill="#0070cd" d="M26.236 22.693c0 0 5.237-0.012 5.237-0.012 0.292-0.001 0.528-0.238 0.527-0.53 0 0-0.009-3.803-0.009-3.803-0.001-0.292-0.238-0.528-0.53-0.528 0 0-5.237 0.012-5.237 0.012-1.342 0.003-2.428 1.094-2.425 2.436 0 0 0 0 0 0 0.003 1.342 1.093 2.428 2.436 2.425z"></path>
<path fill="#ddd" d="M27.349 20.26c0.001 0.618-0.498 1.12-1.116 1.121s-1.12-0.498-1.121-1.116c-0.001-0.618 0.498-1.12 1.116-1.121s1.12 0.498 1.121 1.116z"></path>
<path fill="#fff" d="M26.315 19.817c0 0.208-0.168 0.377-0.376 0.377s-0.377-0.168-0.377-0.376c-0-0.208 0.168-0.377 0.376-0.377s0.377 0.168 0.377 0.376z"></path>
<path fill="#fff" d="M11.717 22.215l-0.152-0.173c-0.747-0.851-2.045-2.149-2.893-2.894l-0.173-0.152c-0.431-0.379-1.007-0.588-1.62-0.588s-1.189 0.209-1.621 0.588l-0.173 0.152c-0.849 0.745-2.147 2.043-2.893 2.894l-0.152 0.173c-0.783 0.893-0.783 2.345 0 3.238l0.152 0.173c0.746 0.85 2.044 2.149 2.893 2.895l0.173 0.152c0.432 0.379 1.007 0.588 1.621 0.588s1.189-0.209 1.621-0.588l0.273-0.24c0.269-0.235 0.391-0.343 0.768-0.712l0.089-0.089-0.014-0.015c0.075-0.151 0.027-0.334-0.113-0.431s-0.331-0.079-0.449 0.043c-0.036 0.034-0.534 0.502-0.842 0.773l-0.173 0.152c-0.305 0.267-0.717 0.414-1.16 0.414s-0.855-0.147-1.16-0.415l-0.173-0.152c-0.831-0.729-2.101-1.999-2.83-2.83l-0.152-0.173c-0.266-0.304-0.413-0.717-0.413-1.163s0.147-0.859 0.413-1.163l0.152-0.173c0.729-0.831 1.998-2.101 2.83-2.831l0.173-0.152c0.305-0.267 0.717-0.415 1.16-0.415s0.855 0.147 1.16 0.415l0.173 0.152c0.831 0.73 2.101 2 2.83 2.83l0.152 0.173c0.554 0.631 0.555 1.738 0.003 2.322l-0.088 0.093c-0.249 0.265-0.4 0.425-0.559 0.473-0.151 0.045-0.342-0.003-0.705-0.176-0.607-0.289-1.466-0.774-1.764-0.943 0.121-0.237 0.398-0.817 0.528-1.398l0.035-0.156h-1.49v-0.182h1.896v-0.694h-1.896v-0.875h-0.697v0.875h-1.896v0.694h1.896v0.182h-1.459v0.693h2.627c-0.057 0.176-0.161 0.394-0.235 0.536-0.271-0.106-0.989-0.355-1.727-0.355-0.465 0-0.857 0.124-1.133 0.359-0.274 0.233-0.419 0.556-0.419 0.936s0.139 0.705 0.401 0.94c0.268 0.24 0.649 0.368 1.103 0.368 0.524 0 1.067-0.23 1.57-0.662 0.196-0.169 0.375-0.357 0.534-0.56 0.281 0.154 1.103 0.604 1.869 1.009 0.5 0.264 0.851 0.317 1.174 0.175 0.307-0.135 0.577-0.442 0.949-0.867l0.006-0.006c0.783-0.893 0.783-2.345-0.001-3.238zM5.602 25.648c-0.737 0-0.794-0.452-0.794-0.59 0.002-0.14 0.055-0.274 0.149-0.379 0.136-0.147 0.348-0.222 0.633-0.222 0.612 0 1.164 0.208 1.414 0.319-0.227 0.265-0.811 0.873-1.401 0.873z"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

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

After

Width:  |  Height:  |  Size: 3.8 KiB

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

After

Width:  |  Height:  |  Size: 3.8 KiB

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

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -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

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

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
+34
View File
@@ -0,0 +1,34 @@
{
"name": "PPanel",
"short_name": "PPanel",
"icons": [
{
"src": "/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/pwa-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#FFFFFF",
"theme_color": "#FFFFFF"
}
+49
View File
@@ -0,0 +1,49 @@
import { formatBytes } from "@workspace/ui/utils/formatting";
import { unitConversion } from "@workspace/ui/utils/unit-conversions";
import { useTranslation } from "react-i18next";
import { useGlobalStore } from "@/stores/global";
type DisplayType = "currency" | "traffic" | "number" | "trafficSpeed";
interface DisplayProps<T> {
value?: T;
unlimited?: boolean;
type?: DisplayType;
}
export function Display<T extends number | undefined | null>({
value = 0,
unlimited = false,
type = "number",
}: DisplayProps<T>): string {
const { t } = useTranslation("components");
const { common } = useGlobalStore();
const { currency } = common;
if (type === "currency") {
const formattedValue = `${currency?.currency_symbol ?? ""}${unitConversion("centsToDollars", value as number)?.toFixed(2) ?? "0.00"}`;
return formattedValue;
}
if (
["traffic", "trafficSpeed", "number"].includes(type) &&
unlimited &&
!value
) {
return t("unlimited");
}
if (type === "traffic") {
return value ? formatBytes(value) : "0";
}
if (type === "trafficSpeed") {
return value ? `${formatBytes(value).replace("B", "b")}ps` : "0";
}
if (type === "number") {
return value ? value.toString() : "0";
}
return "0";
}
+32
View File
@@ -0,0 +1,32 @@
"use client";
import { ExternalLink } from "lucide-react";
import type React from "react";
interface IpLinkProps {
ip: string;
children?: React.ReactNode;
className?: string;
target?: "_blank" | "_self";
}
export function IpLink({
ip,
children,
className = "",
target = "_blank",
}: IpLinkProps) {
const url = `https://ipinfo.io/${ip}`;
return (
<a
className={`inline-flex items-center gap-1 font-mono text-primary transition-colors hover:text-primary/80 hover:underline ${className}`}
href={url}
rel={target === "_blank" ? "noopener noreferrer" : undefined}
target={target}
>
{children || ip}
<ExternalLink className="h-3 w-3" />
</a>
);
}
+19
View File
@@ -0,0 +1,19 @@
import { Link } from "@tanstack/react-router";
import { Button } from "@workspace/ui/components/button";
interface OrderLinkProps {
orderId?: string | number;
className?: string;
}
export function OrderLink({ orderId, className }: OrderLinkProps) {
if (!orderId) return <span>--</span>;
return (
<Button asChild className={`p-0 ${className || ""}`} variant="link">
<Link search={{ search: orderId }} to="/dashboard/order">
{orderId}
</Link>
</Button>
);
}
+10
View File
@@ -0,0 +1,10 @@
export const fallbackLng = "en-US";
export const supportedLngs = ["en-US", "zh-CN"];
export const CDN_URL =
import.meta.env.VITE_CDN_URL || "https://cdn.jsdmirror.com";
export const TUTORIAL_DOCUMENT =
import.meta.env.VITE_TUTORIAL_DOCUMENT || "true";
export const USER_EMAIL = import.meta.env.VITE_USER_EMAIL;
export const USER_PASSWORD = import.meta.env.VITE_USER_PASSWORD;
+56
View File
@@ -0,0 +1,56 @@
import { Link, useLocation } from "@tanstack/react-router";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@workspace/ui/components/breadcrumb";
import { Separator } from "@workspace/ui/components/separator";
import { SidebarTrigger } from "@workspace/ui/components/sidebar";
import { LanguageSwitch } from "@workspace/ui/composed/language-switch";
import { ThemeSwitch } from "@workspace/ui/composed/theme-switch";
import { Fragment, useMemo } from "react";
import { findNavByUrl, useNavs } from "./navs";
import TimezoneSwitch from "./timezone-switch";
import { UserNav } from "./user-nav";
export function Header() {
const pathname = useLocation({ select: (location) => location.pathname });
const navs = useNavs();
const items = useMemo(() => findNavByUrl(navs, pathname), [pathname]);
return (
<header className="sticky top-0 z-50 flex h-14 shrink-0 items-center gap-2 bg-background">
<div className="flex flex-1 items-center gap-2 px-3">
<SidebarTrigger />
<Separator className="mr-2 h-4" orientation="vertical" />
<Breadcrumb>
<BreadcrumbList>
{items.map((item, index) => (
<Fragment key={item?.title}>
{index !== items.length - 1 && (
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link to={item?.url || "/dashboard"}>{item?.title}</Link>
</BreadcrumbLink>
</BreadcrumbItem>
)}
{index < items.length - 1 && <BreadcrumbSeparator />}
{index === items.length - 1 && (
<BreadcrumbPage>{item?.title}</BreadcrumbPage>
)}
</Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
</div>
<div className="flex items-center gap-2 px-3">
<LanguageSwitch />
<TimezoneSwitch />
<ThemeSwitch />
<UserNav />
</div>
</header>
);
}
+32
View File
@@ -0,0 +1,32 @@
import { Outlet } from "@tanstack/react-router";
import {
SidebarInset,
SidebarProvider,
} from "@workspace/ui/components/sidebar";
import { getCookie } from "@workspace/ui/lib/cookies";
import { useEffect, useState } from "react";
import { Header } from "@/layout/header";
import { SidebarLeft } from "./sidebar-left";
export default function DashboardLayout() {
const [open, setOpen] = useState(true);
useEffect(() => {
const sidebarState = getCookie("sidebar_state");
if (sidebarState !== undefined) {
setOpen(sidebarState === "true");
}
}, []);
return (
<SidebarProvider defaultOpen={open}>
<SidebarLeft />
<SidebarInset className="relative flex-grow overflow-hidden">
<Header />
<div className="h-[calc(100vh-56px)] flex-grow gap-4 overflow-auto p-4">
<Outlet />
</div>
</SidebarInset>
</SidebarProvider>
);
}
+226
View File
@@ -0,0 +1,226 @@
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
export interface NavItem {
title: string;
url?: string;
icon?: string;
items?: NavItem[];
defaultOpen?: boolean;
}
export function useNavs() {
const { t } = useTranslation("menu");
const navs: NavItem[] = useMemo(
() => [
{
title: t("Dashboard", "Dashboard"),
url: "/dashboard",
icon: "flat-color-icons:globe",
},
{
title: t("Maintenance", "Maintenance"),
icon: "flat-color-icons:data-protection",
items: [
{
title: t("Server Management", "Server Management"),
url: "/dashboard/servers",
icon: "flat-color-icons:data-protection",
},
{
title: t("Node Management", "Node Management"),
url: "/dashboard/nodes",
icon: "flat-color-icons:mind-map",
},
{
title: t("Subscribe Config", "Subscribe Config"),
url: "/dashboard/subscribe",
icon: "flat-color-icons:ruler",
},
{
title: t("Product Management", "Product Management"),
url: "/dashboard/product",
icon: "flat-color-icons:shop",
},
],
},
{
title: t("Commerce", "Commerce"),
icon: "flat-color-icons:sales-performance",
items: [
{
title: t("Order Management", "Order Management"),
url: "/dashboard/order",
icon: "flat-color-icons:todo-list",
},
{
title: t("Coupon Management", "Coupon Management"),
url: "/dashboard/coupon",
icon: "flat-color-icons:bookmark",
},
{
title: t("Marketing Management", "Marketing Management"),
url: "/dashboard/marketing",
icon: "flat-color-icons:bullish",
},
{
title: t("Announcement Management", "Announcement Management"),
url: "/dashboard/announcement",
icon: "flat-color-icons:advertising",
},
],
},
{
title: t("Users & Support", "Users & Support"),
icon: "flat-color-icons:collaboration",
items: [
{
title: t("User Management", "User Management"),
url: "/dashboard/user",
icon: "flat-color-icons:conference-call",
},
{
title: t("Ticket Management", "Ticket Management"),
url: "/dashboard/ticket",
icon: "flat-color-icons:collaboration",
},
{
title: t("Document Management", "Document Management"),
url: "/dashboard/document",
icon: "flat-color-icons:document",
},
],
},
{
defaultOpen: false,
title: t("System", "System"),
icon: "flat-color-icons:services",
items: [
{
title: t("System Config", "System Config"),
url: "/dashboard/system",
icon: "flat-color-icons:services",
},
{
title: t("Auth Control", "Auth Control"),
url: "/dashboard/auth-control",
icon: "flat-color-icons:lock-portrait",
},
{
title: t("Payment Config", "Payment Config"),
url: "/dashboard/payment",
icon: "flat-color-icons:currency-exchange",
},
{
title: t("ADS Config", "ADS Config"),
url: "/dashboard/ads",
icon: "flat-color-icons:electrical-sensor",
},
],
},
{
defaultOpen: false,
title: t("Logs & Analytics", "Logs & Analytics"),
icon: "flat-color-icons:statistics",
items: [
{
title: t("Login", "Login"),
url: "/dashboard/log/login",
icon: "flat-color-icons:unlock",
},
{
title: t("Register", "Register"),
url: "/dashboard/log/register",
icon: "flat-color-icons:contacts",
},
{
title: t("Email", "Email"),
url: "/dashboard/log/email",
icon: "flat-color-icons:feedback",
},
{
title: t("Mobile", "Mobile"),
url: "/dashboard/log/mobile",
icon: "flat-color-icons:sms",
},
{
title: t("Subscribe", "Subscribe"),
url: "/dashboard/log/subscribe",
icon: "flat-color-icons:workflow",
},
{
title: t("Reset Subscribe", "Reset Subscribe"),
url: "/dashboard/log/reset-subscribe",
icon: "flat-color-icons:refresh",
},
{
title: t("Subscribe Traffic", "Subscribe Traffic"),
url: "/dashboard/log/subscribe-traffic",
icon: "flat-color-icons:statistics",
},
{
title: t("Server Traffic", "Server Traffic"),
url: "/dashboard/log/server-traffic",
icon: "flat-color-icons:statistics",
},
{
title: t("Traffic Details", "Traffic Details"),
url: "/dashboard/log/traffic-details",
icon: "flat-color-icons:combo-chart",
},
{
title: t("Balance", "Balance"),
url: "/dashboard/log/balance",
icon: "flat-color-icons:sales-performance",
},
{
title: t("Commission", "Commission"),
url: "/dashboard/log/commission",
icon: "flat-color-icons:debt",
},
{
title: t("Gift", "Gift"),
url: "/dashboard/log/gift",
icon: "flat-color-icons:donate",
},
],
},
],
[t]
);
return navs;
}
export function findNavByUrl(navs: NavItem[], url: string) {
function matchDynamicRoute(pattern: string, path: string): boolean {
const regexPattern = pattern
.replace(/:[^/]+/g, "[^/]+")
.replace(/\//g, "\\/");
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(path);
}
function findNav(
items: NavItem[],
url: string,
path: NavItem[] = []
): NavItem[] {
for (const item of items) {
if (item.url === url || (item.url && matchDynamicRoute(item.url, url))) {
return [...path, item];
}
if (item.items) {
const result = findNav(item.items, url, [...path, item]);
if (result.length) return result;
}
}
return [];
}
return findNav(navs, url);
}
+294
View File
@@ -0,0 +1,294 @@
import { Link, useLocation } from "@tanstack/react-router";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@workspace/ui/components/hover-card";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@workspace/ui/components/sidebar";
import { Icon } from "@workspace/ui/composed/icon";
import { cn } from "@workspace/ui/lib/utils";
import React, { useState } from "react";
import { useGlobalStore } from "@/stores/global";
import { type NavItem, useNavs } from "./navs";
function hasChildren(obj: any): obj is { items: any[] } {
return (
obj && Array.isArray((obj as any).items) && (obj as any).items.length > 0
);
}
export function SidebarLeft({
...props
}: React.ComponentProps<typeof Sidebar>) {
const { common } = useGlobalStore();
const { site } = common;
const navs = useNavs();
const pathname = useLocation({ select: (location) => location.pathname });
const { state, isMobile } = useSidebar();
const [openGroups, setOpenGroups] = useState<Record<string, boolean>>({});
React.useEffect(() => {
setOpenGroups((prev) => {
const next: Record<string, boolean> = { ...prev };
navs.forEach((nav) => {
if (hasChildren(nav) && next[nav.title] === undefined) {
next[nav.title] = nav.defaultOpen ?? true;
}
});
return next;
});
}, [navs]);
const handleToggleGroup = (title: string) => {
setOpenGroups((prev) => ({ ...prev, [title]: !prev[title] }));
};
const normalize = (p: string) =>
p.endsWith("/") && p !== "/" ? p.replace(/\/+$/, "") : p;
const isActiveUrl = (url: string) => {
const path = normalize(pathname);
const target = normalize(url);
if (target === "/dashboard") return path === target;
if (path === target) return true;
return path.startsWith(`${target}/`);
};
const isGroupActive = (nav: NavItem) =>
(hasChildren(nav) && nav.items?.some((i: any) => isActiveUrl(i.url))) ||
("url" in nav && nav.url ? isActiveUrl(nav.url as string) : false);
React.useEffect(() => {
setOpenGroups((prev) => {
const next: Record<string, boolean> = { ...prev };
navs.forEach((nav) => {
if (hasChildren(nav) && isGroupActive(nav)) next[nav.title] = true;
});
return next;
});
}, [pathname, navs]);
const renderCollapsedFlyout = (nav: NavItem) => {
const ParentButton = (
<SidebarMenuButton
aria-label={nav.title}
className="h-8 justify-center"
isActive={false}
size="sm"
>
{"url" in nav && nav.url ? (
<Link to={nav.url as string}>
{"icon" in nav && (nav as any).icon ? (
<Icon className="size-4" icon={(nav as any).icon} />
) : null}
</Link>
) : "icon" in nav && (nav as any).icon ? (
<Icon className="size-4" icon={(nav as any).icon} />
) : null}
</SidebarMenuButton>
);
if (!hasChildren(nav)) return ParentButton;
return (
<HoverCard closeDelay={200} openDelay={40}>
<HoverCardTrigger asChild>{ParentButton}</HoverCardTrigger>
<HoverCardContent
align="start"
avoidCollisions
className="z-[9999] w-64 p-0"
collisionPadding={8}
side="right"
sideOffset={10}
>
<div className="flex items-center gap-2 border-b px-3 py-2">
{"icon" in nav && (nav as any).icon ? (
<Icon className="size-4" icon={(nav as any).icon} />
) : null}
<span className="truncate font-medium text-muted-foreground text-xs">
{nav.title}
</span>
</div>
<ul className="p-1">
{nav.items?.map((item: any) => (
<li key={item.title}>
<Link
className={[
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm",
isActiveUrl(item.url)
? "bg-accent text-accent-foreground"
: "hover:bg-accent/60",
].join(" ")}
to={item.url}
>
{item.icon && <Icon className="size-4" icon={item.icon} />}
<span className="truncate">{item.title}</span>
</Link>
</li>
))}
</ul>
</HoverCardContent>
</HoverCard>
);
};
return (
<Sidebar className="border-r-0" collapsible="icon" {...props}>
<SidebarHeader className="p-2">
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild className="h-10" size="sm">
<Link to="/">
<div className="flex aspect-square size-6 items-center justify-center rounded-lg">
<img
alt="logo"
className="size-full"
height={24}
src={site.site_logo || "/favicon.svg"}
width={24}
/>
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold text-xs">
{site.site_name}
</span>
<span className="truncate text-xs opacity-70">
{site.site_desc}
</span>
</div>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent className="py-2">
<SidebarMenu>
{!isMobile && state === "collapsed"
? navs.map((nav) => (
<SidebarMenuItem className="mx-auto" key={nav.title}>
{renderCollapsedFlyout(nav)}
</SidebarMenuItem>
))
: navs.map((nav) => {
if (hasChildren(nav)) {
const isOpen = openGroups[nav.title] ?? false;
return (
<SidebarGroup className={cn("py-1")} key={nav.title}>
<SidebarMenuButton
className={cn(
"mb-2 flex h-8 w-full items-center justify-between hover:bg-accent/60 hover:text-accent-foreground"
)}
isActive={false}
onClick={() => handleToggleGroup(nav.title)}
size="sm"
style={{ fontWeight: 500 }}
tabIndex={0}
>
<span className="flex min-w-0 items-center gap-2">
{"icon" in nav && (nav as any).icon ? (
<Icon
className="size-4 shrink-0"
icon={(nav as any).icon}
/>
) : null}
<span className="truncate text-sm">{nav.title}</span>
</span>
<Icon
className={`ml-2 size-4 transition-transform ${isOpen ? "" : "-rotate-90"}`}
icon="mdi:chevron-down"
/>
</SidebarMenuButton>
{isOpen && (
<SidebarGroupContent className="px-4">
<SidebarMenu>
{nav.items?.map((item: any) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton
asChild
className="h-8"
isActive={isActiveUrl(item.url)}
size="sm"
tooltip={item.title}
>
<Link to={item.url}>
{item.icon && (
<Icon
className="size-4"
icon={item.icon}
/>
)}
<span className="text-sm">
{item.title}
</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
)}
</SidebarGroup>
);
}
return (
<SidebarGroup className="py-1" key={nav.title}>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
asChild={"url" in nav && !!(nav as any).url}
className="h-8"
isActive={
"url" in nav && (nav as any).url
? isActiveUrl((nav as any).url)
: false
}
size="sm"
tooltip={nav.title}
>
{"url" in nav && (nav as any).url ? (
<Link to={(nav as any).url}>
{"icon" in nav && (nav as any).icon ? (
<Icon
className="size-4"
icon={(nav as any).icon}
/>
) : null}
<span className="text-sm">{nav.title}</span>
</Link>
) : (
<>
{"icon" in nav && (nav as any).icon ? (
<Icon
className="size-4"
icon={(nav as any).icon}
/>
) : null}
<span className="text-sm">{nav.title}</span>
</>
)}
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
})}
</SidebarMenu>
</SidebarContent>
</Sidebar>
);
}
+315
View File
@@ -0,0 +1,315 @@
import { Button } from "@workspace/ui/components/button";
import {
Command,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@workspace/ui/components/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@workspace/ui/components/popover";
import { Icon } from "@workspace/ui/composed/icon";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
interface TimezoneOption {
value: string;
label: string;
timezone: string;
}
function getCurrentTime(timezone: string): string {
try {
const now = new Date();
return now.toLocaleTimeString("en-US", {
timeZone: timezone,
hour12: false,
hour: "2-digit",
minute: "2-digit",
});
} catch {
return "--:--";
}
}
function getAllTimezones(locale = "en-US"): TimezoneOption[] {
try {
const timeZones = Intl.supportedValuesOf("timeZone");
const processed = timeZones
.map((tz) => {
try {
return {
value: tz,
label: tz,
timezone: getTimezoneOffset(tz),
};
} catch {
return {
value: tz,
label: tz,
timezone: "UTC+00:00",
};
}
})
.filter(Boolean)
.sort((a, b) => a.label.localeCompare(b.label, locale));
const hasUTC = processed.some((tz) => tz.value === "UTC");
if (!hasUTC) {
processed.unshift({
value: "UTC",
label: "UTC",
timezone: "UTC+00:00",
});
}
return processed;
} catch {
return [
{
value: "UTC",
label: "UTC",
timezone: "UTC+00:00",
},
];
}
}
function getServerTimezones(): string[] {
return ["UTC"];
}
function getRecommendedTimezones(): string[] {
try {
const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
if (browserTimezone.startsWith("Asia/")) {
return [
"Asia/Shanghai",
"Asia/Tokyo",
"Asia/Kolkata",
"Asia/Singapore",
"Asia/Seoul",
];
}
if (browserTimezone.startsWith("Europe/")) {
return [
"Europe/London",
"Europe/Paris",
"Europe/Berlin",
"Europe/Rome",
"Europe/Madrid",
];
}
if (browserTimezone.startsWith("America/")) {
return [
"America/New_York",
"America/Los_Angeles",
"America/Chicago",
"America/Denver",
"America/Toronto",
];
}
if (browserTimezone.startsWith("Australia/")) {
return [
"Australia/Sydney",
"Australia/Melbourne",
"Australia/Perth",
"Australia/Brisbane",
];
}
return [
"America/New_York",
"Europe/London",
"Asia/Shanghai",
"Asia/Tokyo",
"Australia/Sydney",
];
} catch {
return [
"America/New_York",
"Europe/London",
"Asia/Shanghai",
"Asia/Tokyo",
"Australia/Sydney",
];
}
}
function getTimezoneOffset(timezone: string): string {
try {
const now = new Date();
const utc = new Date(now.getTime() + now.getTimezoneOffset() * 60_000);
const targetTime = new Date(
utc.toLocaleString("en-US", { timeZone: timezone })
);
const offset = (targetTime.getTime() - utc.getTime()) / (1000 * 60 * 60);
const sign = offset >= 0 ? "+" : "-";
const hours = Math.floor(Math.abs(offset));
const minutes = Math.floor((Math.abs(offset) - hours) * 60);
return `UTC${sign}${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
} catch {
return "UTC+00:00";
}
}
export default function TimezoneSwitch() {
const { i18n } = useTranslation();
const locale = i18n.language;
const [timezone, setTimezone] = useState<string>("UTC");
const [open, setOpen] = useState(false);
const timezoneOptions = useMemo(() => getAllTimezones(locale), [locale]);
useEffect(() => {
const savedTimezone = localStorage.getItem("timezone");
if (savedTimezone) {
setTimezone(savedTimezone);
} else {
try {
const browserTimezone =
Intl.DateTimeFormat().resolvedOptions().timeZone;
setTimezone(browserTimezone);
localStorage.setItem("timezone", browserTimezone);
} catch {
setTimezone("UTC");
}
}
}, []);
const handleTimezoneChange = (newTimezone: string) => {
setTimezone(newTimezone);
localStorage.setItem("timezone", newTimezone);
setOpen(false);
window.dispatchEvent(
new CustomEvent("timezoneChanged", {
detail: { timezone: newTimezone },
})
);
};
const serverTimezones = timezoneOptions.filter(
(option) =>
getServerTimezones().includes(option.value) && option.value !== timezone
);
return (
<Popover onOpenChange={setOpen} open={open}>
<PopoverTrigger asChild>
<Button className="p-0" size="icon" variant="ghost">
<Icon className="!size-6" icon="flat-color-icons:overtime" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-80 p-0">
<Command>
<CommandInput placeholder="Search..." />
<CommandList>
<CommandGroup heading="Current">
{timezoneOptions
.filter((option) => option.value === timezone)
.map((option) => (
<CommandItem
className="bg-primary/10"
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-100" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
{serverTimezones.length > 0 && (
<CommandGroup heading="Server">
{serverTimezones.map((option) => (
<CommandItem
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-0" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
)}
<CommandGroup heading="Recommended">
{timezoneOptions
.filter(
(option) =>
getRecommendedTimezones().includes(option.value) &&
option.value !== timezone
)
.map((option) => (
<CommandItem
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-0" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
<CommandGroup heading="All">
{timezoneOptions
.filter(
(option) =>
!(
getServerTimezones().includes(option.value) ||
getRecommendedTimezones().includes(option.value)
) && option.value !== timezone
)
.map((option) => (
<CommandItem
key={option.value}
onSelect={() => handleTimezoneChange(option.value)}
value={`${option.label} ${option.value}`}
>
<div className="flex w-full items-center gap-3">
<div className="flex flex-1 flex-col">
<span className="font-medium">{option.value}</span>
<span className="text-muted-foreground text-xs">
{option.timezone} {getCurrentTime(option.value)}
</span>
</div>
<Icon className="h-4 w-4 opacity-0" icon="uil:check" />
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}
+73
View File
@@ -0,0 +1,73 @@
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@workspace/ui/components/avatar";
import { Button } from "@workspace/ui/components/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@workspace/ui/components/dropdown-menu";
import { useTranslation } from "react-i18next";
import { useGlobalStore } from "@/stores/global";
import { Logout } from "@/utils/common";
export function UserNav() {
const { t } = useTranslation("auth");
const { user } = useGlobalStore();
if (user) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="default">
<Avatar className="size-8">
<AvatarImage alt={user?.avatar ?? ""} src={user?.avatar ?? ""} />
<AvatarFallback className="rounded-none bg-transparent">
{user?.auth_methods?.[0]?.auth_identifier
.toUpperCase()
.charAt(0)}
</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56" forceMount>
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="font-medium text-sm leading-none">
{user?.auth_methods?.[0]?.auth_identifier}
</p>
{/* <p className='text-xs leading-none text-muted-foreground'>ID: {user?.id}</p> */}
</div>
</DropdownMenuLabel>
{/* <DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
Profile
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Billing
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Settings
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>New Team</DropdownMenuItem>
</DropdownMenuGroup> */}
<DropdownMenuSeparator />
<DropdownMenuItem onClick={Logout}>
{t("logout", "Logout")}
<DropdownMenuShortcut>Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
}
+95
View File
@@ -0,0 +1,95 @@
import { createRouter, RouterProvider } from "@tanstack/react-router";
import {
TanStackQueryContext,
TanStackQueryProvider,
} from "@workspace/ui/integrations/tanstack-query";
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
// Import the generated route tree
import { routeTree } from "./routeTree.gen";
// Styles
import "@workspace/ui/globals.css";
import { DirectionProvider } from "@workspace/ui/integrations/direction";
import { LanguageProvider } from "@workspace/ui/integrations/language";
import { ThemeProvider } from "@workspace/ui/integrations/theme";
import { initializeI18n } from "@workspace/ui/lib/i18n";
import { fallbackLng, supportedLngs } from "./config/index.ts";
// Report web vitals
import reportWebVitals from "./reportWebVitals.ts";
// Common utilities
import { Logout } from "./utils/common.ts";
initializeI18n({
supportedLngs,
fallbackLng,
ns: [
"ads",
"announcement",
"auth-control",
"auth",
"components",
"coupon",
"dashboard",
"document",
"log",
"marketing",
"menu",
"nodes",
"order",
"payment",
"product",
"servers",
"subscribe",
"system",
"ticket",
"tool",
"translation",
"user",
],
});
window.logout = Logout;
// Create a new router instance
const TanStackQueryProviderContext = TanStackQueryContext();
const router = createRouter({
routeTree,
context: {
...TanStackQueryProviderContext,
},
defaultPreload: "intent",
scrollRestoration: true,
defaultStructuralSharing: true,
defaultPreloadStaleTime: 0,
});
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
// Render the app
const rootElement = document.getElementById("app");
if (rootElement && !rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<TanStackQueryProvider {...TanStackQueryProviderContext}>
<LanguageProvider supportedLanguages={supportedLngs}>
<ThemeProvider>
<DirectionProvider>
<RouterProvider router={router} />
</DirectionProvider>
</ThemeProvider>
</LanguageProvider>
</TanStackQueryProvider>
</StrictMode>
);
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
+13
View File
@@ -0,0 +1,13 @@
const reportWebVitals = (onPerfEntry?: () => void) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import("web-vitals").then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
onCLS(onPerfEntry);
onINP(onPerfEntry);
onFCP(onPerfEntry);
onLCP(onPerfEntry);
onTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
+799
View File
@@ -0,0 +1,799 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { createFileRoute } from '@tanstack/react-router'
import { Route as rootRouteImport } from './routes/__root'
const DashboardRouteLazyRouteImport = createFileRoute('/dashboard')()
const IndexLazyRouteImport = createFileRoute('/')()
const DashboardIndexLazyRouteImport = createFileRoute('/dashboard/')()
const DashboardServersLazyRouteImport = createFileRoute('/dashboard/servers')()
const DashboardNodesLazyRouteImport = createFileRoute('/dashboard/nodes')()
const DashboardUserIndexLazyRouteImport = createFileRoute('/dashboard/user/')()
const DashboardTicketIndexLazyRouteImport =
createFileRoute('/dashboard/ticket/')()
const DashboardSystemIndexLazyRouteImport =
createFileRoute('/dashboard/system/')()
const DashboardSubscribeIndexLazyRouteImport = createFileRoute(
'/dashboard/subscribe/',
)()
const DashboardProductIndexLazyRouteImport = createFileRoute(
'/dashboard/product/',
)()
const DashboardPaymentIndexLazyRouteImport = createFileRoute(
'/dashboard/payment/',
)()
const DashboardOrderIndexLazyRouteImport =
createFileRoute('/dashboard/order/')()
const DashboardMarketingIndexLazyRouteImport = createFileRoute(
'/dashboard/marketing/',
)()
const DashboardDocumentIndexLazyRouteImport = createFileRoute(
'/dashboard/document/',
)()
const DashboardCouponIndexLazyRouteImport =
createFileRoute('/dashboard/coupon/')()
const DashboardAuthControlIndexLazyRouteImport = createFileRoute(
'/dashboard/auth-control/',
)()
const DashboardAnnouncementIndexLazyRouteImport = createFileRoute(
'/dashboard/announcement/',
)()
const DashboardAdsIndexLazyRouteImport = createFileRoute('/dashboard/ads/')()
const DashboardLogTrafficDetailsLazyRouteImport = createFileRoute(
'/dashboard/log/traffic-details',
)()
const DashboardLogSubscribeTrafficLazyRouteImport = createFileRoute(
'/dashboard/log/subscribe-traffic',
)()
const DashboardLogSubscribeLazyRouteImport = createFileRoute(
'/dashboard/log/subscribe',
)()
const DashboardLogServerTrafficLazyRouteImport = createFileRoute(
'/dashboard/log/server-traffic',
)()
const DashboardLogResetSubscribeLazyRouteImport = createFileRoute(
'/dashboard/log/reset-subscribe',
)()
const DashboardLogRegisterLazyRouteImport = createFileRoute(
'/dashboard/log/register',
)()
const DashboardLogMobileLazyRouteImport = createFileRoute(
'/dashboard/log/mobile',
)()
const DashboardLogLoginLazyRouteImport = createFileRoute(
'/dashboard/log/login',
)()
const DashboardLogGiftLazyRouteImport = createFileRoute('/dashboard/log/gift')()
const DashboardLogEmailLazyRouteImport = createFileRoute(
'/dashboard/log/email',
)()
const DashboardLogCommissionLazyRouteImport = createFileRoute(
'/dashboard/log/commission',
)()
const DashboardLogBalanceLazyRouteImport = createFileRoute(
'/dashboard/log/balance',
)()
const DashboardRouteLazyRoute = DashboardRouteLazyRouteImport.update({
id: '/dashboard',
path: '/dashboard',
getParentRoute: () => rootRouteImport,
} as any).lazy(() =>
import('./routes/dashboard/route.lazy').then((d) => d.Route),
)
const IndexLazyRoute = IndexLazyRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route))
const DashboardIndexLazyRoute = DashboardIndexLazyRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/index.lazy').then((d) => d.Route),
)
const DashboardServersLazyRoute = DashboardServersLazyRouteImport.update({
id: '/servers',
path: '/servers',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/servers.lazy').then((d) => d.Route),
)
const DashboardNodesLazyRoute = DashboardNodesLazyRouteImport.update({
id: '/nodes',
path: '/nodes',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/nodes.lazy').then((d) => d.Route),
)
const DashboardUserIndexLazyRoute = DashboardUserIndexLazyRouteImport.update({
id: '/user/',
path: '/user/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/user/index.lazy').then((d) => d.Route),
)
const DashboardTicketIndexLazyRoute =
DashboardTicketIndexLazyRouteImport.update({
id: '/ticket/',
path: '/ticket/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/ticket/index.lazy').then((d) => d.Route),
)
const DashboardSystemIndexLazyRoute =
DashboardSystemIndexLazyRouteImport.update({
id: '/system/',
path: '/system/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/system/index.lazy').then((d) => d.Route),
)
const DashboardSubscribeIndexLazyRoute =
DashboardSubscribeIndexLazyRouteImport.update({
id: '/subscribe/',
path: '/subscribe/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/subscribe/index.lazy').then((d) => d.Route),
)
const DashboardProductIndexLazyRoute =
DashboardProductIndexLazyRouteImport.update({
id: '/product/',
path: '/product/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/product/index.lazy').then((d) => d.Route),
)
const DashboardPaymentIndexLazyRoute =
DashboardPaymentIndexLazyRouteImport.update({
id: '/payment/',
path: '/payment/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/payment/index.lazy').then((d) => d.Route),
)
const DashboardOrderIndexLazyRoute = DashboardOrderIndexLazyRouteImport.update({
id: '/order/',
path: '/order/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/order/index.lazy').then((d) => d.Route),
)
const DashboardMarketingIndexLazyRoute =
DashboardMarketingIndexLazyRouteImport.update({
id: '/marketing/',
path: '/marketing/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/marketing/index.lazy').then((d) => d.Route),
)
const DashboardDocumentIndexLazyRoute =
DashboardDocumentIndexLazyRouteImport.update({
id: '/document/',
path: '/document/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/document/index.lazy').then((d) => d.Route),
)
const DashboardCouponIndexLazyRoute =
DashboardCouponIndexLazyRouteImport.update({
id: '/coupon/',
path: '/coupon/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/coupon/index.lazy').then((d) => d.Route),
)
const DashboardAuthControlIndexLazyRoute =
DashboardAuthControlIndexLazyRouteImport.update({
id: '/auth-control/',
path: '/auth-control/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/auth-control/index.lazy').then((d) => d.Route),
)
const DashboardAnnouncementIndexLazyRoute =
DashboardAnnouncementIndexLazyRouteImport.update({
id: '/announcement/',
path: '/announcement/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/announcement/index.lazy').then((d) => d.Route),
)
const DashboardAdsIndexLazyRoute = DashboardAdsIndexLazyRouteImport.update({
id: '/ads/',
path: '/ads/',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/ads/index.lazy').then((d) => d.Route),
)
const DashboardLogTrafficDetailsLazyRoute =
DashboardLogTrafficDetailsLazyRouteImport.update({
id: '/log/traffic-details',
path: '/log/traffic-details',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/traffic-details.lazy').then((d) => d.Route),
)
const DashboardLogSubscribeTrafficLazyRoute =
DashboardLogSubscribeTrafficLazyRouteImport.update({
id: '/log/subscribe-traffic',
path: '/log/subscribe-traffic',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/subscribe-traffic.lazy').then(
(d) => d.Route,
),
)
const DashboardLogSubscribeLazyRoute =
DashboardLogSubscribeLazyRouteImport.update({
id: '/log/subscribe',
path: '/log/subscribe',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/subscribe.lazy').then((d) => d.Route),
)
const DashboardLogServerTrafficLazyRoute =
DashboardLogServerTrafficLazyRouteImport.update({
id: '/log/server-traffic',
path: '/log/server-traffic',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/server-traffic.lazy').then((d) => d.Route),
)
const DashboardLogResetSubscribeLazyRoute =
DashboardLogResetSubscribeLazyRouteImport.update({
id: '/log/reset-subscribe',
path: '/log/reset-subscribe',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/reset-subscribe.lazy').then((d) => d.Route),
)
const DashboardLogRegisterLazyRoute =
DashboardLogRegisterLazyRouteImport.update({
id: '/log/register',
path: '/log/register',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/register.lazy').then((d) => d.Route),
)
const DashboardLogMobileLazyRoute = DashboardLogMobileLazyRouteImport.update({
id: '/log/mobile',
path: '/log/mobile',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/mobile.lazy').then((d) => d.Route),
)
const DashboardLogLoginLazyRoute = DashboardLogLoginLazyRouteImport.update({
id: '/log/login',
path: '/log/login',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/login.lazy').then((d) => d.Route),
)
const DashboardLogGiftLazyRoute = DashboardLogGiftLazyRouteImport.update({
id: '/log/gift',
path: '/log/gift',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/gift.lazy').then((d) => d.Route),
)
const DashboardLogEmailLazyRoute = DashboardLogEmailLazyRouteImport.update({
id: '/log/email',
path: '/log/email',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/email.lazy').then((d) => d.Route),
)
const DashboardLogCommissionLazyRoute =
DashboardLogCommissionLazyRouteImport.update({
id: '/log/commission',
path: '/log/commission',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/commission.lazy').then((d) => d.Route),
)
const DashboardLogBalanceLazyRoute = DashboardLogBalanceLazyRouteImport.update({
id: '/log/balance',
path: '/log/balance',
getParentRoute: () => DashboardRouteLazyRoute,
} as any).lazy(() =>
import('./routes/dashboard/log/balance.lazy').then((d) => d.Route),
)
export interface FileRoutesByFullPath {
'/': typeof IndexLazyRoute
'/dashboard': typeof DashboardRouteLazyRouteWithChildren
'/dashboard/nodes': typeof DashboardNodesLazyRoute
'/dashboard/servers': typeof DashboardServersLazyRoute
'/dashboard/': typeof DashboardIndexLazyRoute
'/dashboard/log/balance': typeof DashboardLogBalanceLazyRoute
'/dashboard/log/commission': typeof DashboardLogCommissionLazyRoute
'/dashboard/log/email': typeof DashboardLogEmailLazyRoute
'/dashboard/log/gift': typeof DashboardLogGiftLazyRoute
'/dashboard/log/login': typeof DashboardLogLoginLazyRoute
'/dashboard/log/mobile': typeof DashboardLogMobileLazyRoute
'/dashboard/log/register': typeof DashboardLogRegisterLazyRoute
'/dashboard/log/reset-subscribe': typeof DashboardLogResetSubscribeLazyRoute
'/dashboard/log/server-traffic': typeof DashboardLogServerTrafficLazyRoute
'/dashboard/log/subscribe': typeof DashboardLogSubscribeLazyRoute
'/dashboard/log/subscribe-traffic': typeof DashboardLogSubscribeTrafficLazyRoute
'/dashboard/log/traffic-details': typeof DashboardLogTrafficDetailsLazyRoute
'/dashboard/ads': typeof DashboardAdsIndexLazyRoute
'/dashboard/announcement': typeof DashboardAnnouncementIndexLazyRoute
'/dashboard/auth-control': typeof DashboardAuthControlIndexLazyRoute
'/dashboard/coupon': typeof DashboardCouponIndexLazyRoute
'/dashboard/document': typeof DashboardDocumentIndexLazyRoute
'/dashboard/marketing': typeof DashboardMarketingIndexLazyRoute
'/dashboard/order': typeof DashboardOrderIndexLazyRoute
'/dashboard/payment': typeof DashboardPaymentIndexLazyRoute
'/dashboard/product': typeof DashboardProductIndexLazyRoute
'/dashboard/subscribe': typeof DashboardSubscribeIndexLazyRoute
'/dashboard/system': typeof DashboardSystemIndexLazyRoute
'/dashboard/ticket': typeof DashboardTicketIndexLazyRoute
'/dashboard/user': typeof DashboardUserIndexLazyRoute
}
export interface FileRoutesByTo {
'/': typeof IndexLazyRoute
'/dashboard/nodes': typeof DashboardNodesLazyRoute
'/dashboard/servers': typeof DashboardServersLazyRoute
'/dashboard': typeof DashboardIndexLazyRoute
'/dashboard/log/balance': typeof DashboardLogBalanceLazyRoute
'/dashboard/log/commission': typeof DashboardLogCommissionLazyRoute
'/dashboard/log/email': typeof DashboardLogEmailLazyRoute
'/dashboard/log/gift': typeof DashboardLogGiftLazyRoute
'/dashboard/log/login': typeof DashboardLogLoginLazyRoute
'/dashboard/log/mobile': typeof DashboardLogMobileLazyRoute
'/dashboard/log/register': typeof DashboardLogRegisterLazyRoute
'/dashboard/log/reset-subscribe': typeof DashboardLogResetSubscribeLazyRoute
'/dashboard/log/server-traffic': typeof DashboardLogServerTrafficLazyRoute
'/dashboard/log/subscribe': typeof DashboardLogSubscribeLazyRoute
'/dashboard/log/subscribe-traffic': typeof DashboardLogSubscribeTrafficLazyRoute
'/dashboard/log/traffic-details': typeof DashboardLogTrafficDetailsLazyRoute
'/dashboard/ads': typeof DashboardAdsIndexLazyRoute
'/dashboard/announcement': typeof DashboardAnnouncementIndexLazyRoute
'/dashboard/auth-control': typeof DashboardAuthControlIndexLazyRoute
'/dashboard/coupon': typeof DashboardCouponIndexLazyRoute
'/dashboard/document': typeof DashboardDocumentIndexLazyRoute
'/dashboard/marketing': typeof DashboardMarketingIndexLazyRoute
'/dashboard/order': typeof DashboardOrderIndexLazyRoute
'/dashboard/payment': typeof DashboardPaymentIndexLazyRoute
'/dashboard/product': typeof DashboardProductIndexLazyRoute
'/dashboard/subscribe': typeof DashboardSubscribeIndexLazyRoute
'/dashboard/system': typeof DashboardSystemIndexLazyRoute
'/dashboard/ticket': typeof DashboardTicketIndexLazyRoute
'/dashboard/user': typeof DashboardUserIndexLazyRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexLazyRoute
'/dashboard': typeof DashboardRouteLazyRouteWithChildren
'/dashboard/nodes': typeof DashboardNodesLazyRoute
'/dashboard/servers': typeof DashboardServersLazyRoute
'/dashboard/': typeof DashboardIndexLazyRoute
'/dashboard/log/balance': typeof DashboardLogBalanceLazyRoute
'/dashboard/log/commission': typeof DashboardLogCommissionLazyRoute
'/dashboard/log/email': typeof DashboardLogEmailLazyRoute
'/dashboard/log/gift': typeof DashboardLogGiftLazyRoute
'/dashboard/log/login': typeof DashboardLogLoginLazyRoute
'/dashboard/log/mobile': typeof DashboardLogMobileLazyRoute
'/dashboard/log/register': typeof DashboardLogRegisterLazyRoute
'/dashboard/log/reset-subscribe': typeof DashboardLogResetSubscribeLazyRoute
'/dashboard/log/server-traffic': typeof DashboardLogServerTrafficLazyRoute
'/dashboard/log/subscribe': typeof DashboardLogSubscribeLazyRoute
'/dashboard/log/subscribe-traffic': typeof DashboardLogSubscribeTrafficLazyRoute
'/dashboard/log/traffic-details': typeof DashboardLogTrafficDetailsLazyRoute
'/dashboard/ads/': typeof DashboardAdsIndexLazyRoute
'/dashboard/announcement/': typeof DashboardAnnouncementIndexLazyRoute
'/dashboard/auth-control/': typeof DashboardAuthControlIndexLazyRoute
'/dashboard/coupon/': typeof DashboardCouponIndexLazyRoute
'/dashboard/document/': typeof DashboardDocumentIndexLazyRoute
'/dashboard/marketing/': typeof DashboardMarketingIndexLazyRoute
'/dashboard/order/': typeof DashboardOrderIndexLazyRoute
'/dashboard/payment/': typeof DashboardPaymentIndexLazyRoute
'/dashboard/product/': typeof DashboardProductIndexLazyRoute
'/dashboard/subscribe/': typeof DashboardSubscribeIndexLazyRoute
'/dashboard/system/': typeof DashboardSystemIndexLazyRoute
'/dashboard/ticket/': typeof DashboardTicketIndexLazyRoute
'/dashboard/user/': typeof DashboardUserIndexLazyRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths:
| '/'
| '/dashboard'
| '/dashboard/nodes'
| '/dashboard/servers'
| '/dashboard/'
| '/dashboard/log/balance'
| '/dashboard/log/commission'
| '/dashboard/log/email'
| '/dashboard/log/gift'
| '/dashboard/log/login'
| '/dashboard/log/mobile'
| '/dashboard/log/register'
| '/dashboard/log/reset-subscribe'
| '/dashboard/log/server-traffic'
| '/dashboard/log/subscribe'
| '/dashboard/log/subscribe-traffic'
| '/dashboard/log/traffic-details'
| '/dashboard/ads'
| '/dashboard/announcement'
| '/dashboard/auth-control'
| '/dashboard/coupon'
| '/dashboard/document'
| '/dashboard/marketing'
| '/dashboard/order'
| '/dashboard/payment'
| '/dashboard/product'
| '/dashboard/subscribe'
| '/dashboard/system'
| '/dashboard/ticket'
| '/dashboard/user'
fileRoutesByTo: FileRoutesByTo
to:
| '/'
| '/dashboard/nodes'
| '/dashboard/servers'
| '/dashboard'
| '/dashboard/log/balance'
| '/dashboard/log/commission'
| '/dashboard/log/email'
| '/dashboard/log/gift'
| '/dashboard/log/login'
| '/dashboard/log/mobile'
| '/dashboard/log/register'
| '/dashboard/log/reset-subscribe'
| '/dashboard/log/server-traffic'
| '/dashboard/log/subscribe'
| '/dashboard/log/subscribe-traffic'
| '/dashboard/log/traffic-details'
| '/dashboard/ads'
| '/dashboard/announcement'
| '/dashboard/auth-control'
| '/dashboard/coupon'
| '/dashboard/document'
| '/dashboard/marketing'
| '/dashboard/order'
| '/dashboard/payment'
| '/dashboard/product'
| '/dashboard/subscribe'
| '/dashboard/system'
| '/dashboard/ticket'
| '/dashboard/user'
id:
| '__root__'
| '/'
| '/dashboard'
| '/dashboard/nodes'
| '/dashboard/servers'
| '/dashboard/'
| '/dashboard/log/balance'
| '/dashboard/log/commission'
| '/dashboard/log/email'
| '/dashboard/log/gift'
| '/dashboard/log/login'
| '/dashboard/log/mobile'
| '/dashboard/log/register'
| '/dashboard/log/reset-subscribe'
| '/dashboard/log/server-traffic'
| '/dashboard/log/subscribe'
| '/dashboard/log/subscribe-traffic'
| '/dashboard/log/traffic-details'
| '/dashboard/ads/'
| '/dashboard/announcement/'
| '/dashboard/auth-control/'
| '/dashboard/coupon/'
| '/dashboard/document/'
| '/dashboard/marketing/'
| '/dashboard/order/'
| '/dashboard/payment/'
| '/dashboard/product/'
| '/dashboard/subscribe/'
| '/dashboard/system/'
| '/dashboard/ticket/'
| '/dashboard/user/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexLazyRoute: typeof IndexLazyRoute
DashboardRouteLazyRoute: typeof DashboardRouteLazyRouteWithChildren
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/dashboard': {
id: '/dashboard'
path: '/dashboard'
fullPath: '/dashboard'
preLoaderRoute: typeof DashboardRouteLazyRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexLazyRouteImport
parentRoute: typeof rootRouteImport
}
'/dashboard/': {
id: '/dashboard/'
path: '/'
fullPath: '/dashboard/'
preLoaderRoute: typeof DashboardIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/servers': {
id: '/dashboard/servers'
path: '/servers'
fullPath: '/dashboard/servers'
preLoaderRoute: typeof DashboardServersLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/nodes': {
id: '/dashboard/nodes'
path: '/nodes'
fullPath: '/dashboard/nodes'
preLoaderRoute: typeof DashboardNodesLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/user/': {
id: '/dashboard/user/'
path: '/user'
fullPath: '/dashboard/user'
preLoaderRoute: typeof DashboardUserIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/ticket/': {
id: '/dashboard/ticket/'
path: '/ticket'
fullPath: '/dashboard/ticket'
preLoaderRoute: typeof DashboardTicketIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/system/': {
id: '/dashboard/system/'
path: '/system'
fullPath: '/dashboard/system'
preLoaderRoute: typeof DashboardSystemIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/subscribe/': {
id: '/dashboard/subscribe/'
path: '/subscribe'
fullPath: '/dashboard/subscribe'
preLoaderRoute: typeof DashboardSubscribeIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/product/': {
id: '/dashboard/product/'
path: '/product'
fullPath: '/dashboard/product'
preLoaderRoute: typeof DashboardProductIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/payment/': {
id: '/dashboard/payment/'
path: '/payment'
fullPath: '/dashboard/payment'
preLoaderRoute: typeof DashboardPaymentIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/order/': {
id: '/dashboard/order/'
path: '/order'
fullPath: '/dashboard/order'
preLoaderRoute: typeof DashboardOrderIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/marketing/': {
id: '/dashboard/marketing/'
path: '/marketing'
fullPath: '/dashboard/marketing'
preLoaderRoute: typeof DashboardMarketingIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/document/': {
id: '/dashboard/document/'
path: '/document'
fullPath: '/dashboard/document'
preLoaderRoute: typeof DashboardDocumentIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/coupon/': {
id: '/dashboard/coupon/'
path: '/coupon'
fullPath: '/dashboard/coupon'
preLoaderRoute: typeof DashboardCouponIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/auth-control/': {
id: '/dashboard/auth-control/'
path: '/auth-control'
fullPath: '/dashboard/auth-control'
preLoaderRoute: typeof DashboardAuthControlIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/announcement/': {
id: '/dashboard/announcement/'
path: '/announcement'
fullPath: '/dashboard/announcement'
preLoaderRoute: typeof DashboardAnnouncementIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/ads/': {
id: '/dashboard/ads/'
path: '/ads'
fullPath: '/dashboard/ads'
preLoaderRoute: typeof DashboardAdsIndexLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/traffic-details': {
id: '/dashboard/log/traffic-details'
path: '/log/traffic-details'
fullPath: '/dashboard/log/traffic-details'
preLoaderRoute: typeof DashboardLogTrafficDetailsLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/subscribe-traffic': {
id: '/dashboard/log/subscribe-traffic'
path: '/log/subscribe-traffic'
fullPath: '/dashboard/log/subscribe-traffic'
preLoaderRoute: typeof DashboardLogSubscribeTrafficLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/subscribe': {
id: '/dashboard/log/subscribe'
path: '/log/subscribe'
fullPath: '/dashboard/log/subscribe'
preLoaderRoute: typeof DashboardLogSubscribeLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/server-traffic': {
id: '/dashboard/log/server-traffic'
path: '/log/server-traffic'
fullPath: '/dashboard/log/server-traffic'
preLoaderRoute: typeof DashboardLogServerTrafficLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/reset-subscribe': {
id: '/dashboard/log/reset-subscribe'
path: '/log/reset-subscribe'
fullPath: '/dashboard/log/reset-subscribe'
preLoaderRoute: typeof DashboardLogResetSubscribeLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/register': {
id: '/dashboard/log/register'
path: '/log/register'
fullPath: '/dashboard/log/register'
preLoaderRoute: typeof DashboardLogRegisterLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/mobile': {
id: '/dashboard/log/mobile'
path: '/log/mobile'
fullPath: '/dashboard/log/mobile'
preLoaderRoute: typeof DashboardLogMobileLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/login': {
id: '/dashboard/log/login'
path: '/log/login'
fullPath: '/dashboard/log/login'
preLoaderRoute: typeof DashboardLogLoginLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/gift': {
id: '/dashboard/log/gift'
path: '/log/gift'
fullPath: '/dashboard/log/gift'
preLoaderRoute: typeof DashboardLogGiftLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/email': {
id: '/dashboard/log/email'
path: '/log/email'
fullPath: '/dashboard/log/email'
preLoaderRoute: typeof DashboardLogEmailLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/commission': {
id: '/dashboard/log/commission'
path: '/log/commission'
fullPath: '/dashboard/log/commission'
preLoaderRoute: typeof DashboardLogCommissionLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
'/dashboard/log/balance': {
id: '/dashboard/log/balance'
path: '/log/balance'
fullPath: '/dashboard/log/balance'
preLoaderRoute: typeof DashboardLogBalanceLazyRouteImport
parentRoute: typeof DashboardRouteLazyRoute
}
}
}
interface DashboardRouteLazyRouteChildren {
DashboardNodesLazyRoute: typeof DashboardNodesLazyRoute
DashboardServersLazyRoute: typeof DashboardServersLazyRoute
DashboardIndexLazyRoute: typeof DashboardIndexLazyRoute
DashboardLogBalanceLazyRoute: typeof DashboardLogBalanceLazyRoute
DashboardLogCommissionLazyRoute: typeof DashboardLogCommissionLazyRoute
DashboardLogEmailLazyRoute: typeof DashboardLogEmailLazyRoute
DashboardLogGiftLazyRoute: typeof DashboardLogGiftLazyRoute
DashboardLogLoginLazyRoute: typeof DashboardLogLoginLazyRoute
DashboardLogMobileLazyRoute: typeof DashboardLogMobileLazyRoute
DashboardLogRegisterLazyRoute: typeof DashboardLogRegisterLazyRoute
DashboardLogResetSubscribeLazyRoute: typeof DashboardLogResetSubscribeLazyRoute
DashboardLogServerTrafficLazyRoute: typeof DashboardLogServerTrafficLazyRoute
DashboardLogSubscribeLazyRoute: typeof DashboardLogSubscribeLazyRoute
DashboardLogSubscribeTrafficLazyRoute: typeof DashboardLogSubscribeTrafficLazyRoute
DashboardLogTrafficDetailsLazyRoute: typeof DashboardLogTrafficDetailsLazyRoute
DashboardAdsIndexLazyRoute: typeof DashboardAdsIndexLazyRoute
DashboardAnnouncementIndexLazyRoute: typeof DashboardAnnouncementIndexLazyRoute
DashboardAuthControlIndexLazyRoute: typeof DashboardAuthControlIndexLazyRoute
DashboardCouponIndexLazyRoute: typeof DashboardCouponIndexLazyRoute
DashboardDocumentIndexLazyRoute: typeof DashboardDocumentIndexLazyRoute
DashboardMarketingIndexLazyRoute: typeof DashboardMarketingIndexLazyRoute
DashboardOrderIndexLazyRoute: typeof DashboardOrderIndexLazyRoute
DashboardPaymentIndexLazyRoute: typeof DashboardPaymentIndexLazyRoute
DashboardProductIndexLazyRoute: typeof DashboardProductIndexLazyRoute
DashboardSubscribeIndexLazyRoute: typeof DashboardSubscribeIndexLazyRoute
DashboardSystemIndexLazyRoute: typeof DashboardSystemIndexLazyRoute
DashboardTicketIndexLazyRoute: typeof DashboardTicketIndexLazyRoute
DashboardUserIndexLazyRoute: typeof DashboardUserIndexLazyRoute
}
const DashboardRouteLazyRouteChildren: DashboardRouteLazyRouteChildren = {
DashboardNodesLazyRoute: DashboardNodesLazyRoute,
DashboardServersLazyRoute: DashboardServersLazyRoute,
DashboardIndexLazyRoute: DashboardIndexLazyRoute,
DashboardLogBalanceLazyRoute: DashboardLogBalanceLazyRoute,
DashboardLogCommissionLazyRoute: DashboardLogCommissionLazyRoute,
DashboardLogEmailLazyRoute: DashboardLogEmailLazyRoute,
DashboardLogGiftLazyRoute: DashboardLogGiftLazyRoute,
DashboardLogLoginLazyRoute: DashboardLogLoginLazyRoute,
DashboardLogMobileLazyRoute: DashboardLogMobileLazyRoute,
DashboardLogRegisterLazyRoute: DashboardLogRegisterLazyRoute,
DashboardLogResetSubscribeLazyRoute: DashboardLogResetSubscribeLazyRoute,
DashboardLogServerTrafficLazyRoute: DashboardLogServerTrafficLazyRoute,
DashboardLogSubscribeLazyRoute: DashboardLogSubscribeLazyRoute,
DashboardLogSubscribeTrafficLazyRoute: DashboardLogSubscribeTrafficLazyRoute,
DashboardLogTrafficDetailsLazyRoute: DashboardLogTrafficDetailsLazyRoute,
DashboardAdsIndexLazyRoute: DashboardAdsIndexLazyRoute,
DashboardAnnouncementIndexLazyRoute: DashboardAnnouncementIndexLazyRoute,
DashboardAuthControlIndexLazyRoute: DashboardAuthControlIndexLazyRoute,
DashboardCouponIndexLazyRoute: DashboardCouponIndexLazyRoute,
DashboardDocumentIndexLazyRoute: DashboardDocumentIndexLazyRoute,
DashboardMarketingIndexLazyRoute: DashboardMarketingIndexLazyRoute,
DashboardOrderIndexLazyRoute: DashboardOrderIndexLazyRoute,
DashboardPaymentIndexLazyRoute: DashboardPaymentIndexLazyRoute,
DashboardProductIndexLazyRoute: DashboardProductIndexLazyRoute,
DashboardSubscribeIndexLazyRoute: DashboardSubscribeIndexLazyRoute,
DashboardSystemIndexLazyRoute: DashboardSystemIndexLazyRoute,
DashboardTicketIndexLazyRoute: DashboardTicketIndexLazyRoute,
DashboardUserIndexLazyRoute: DashboardUserIndexLazyRoute,
}
const DashboardRouteLazyRouteWithChildren =
DashboardRouteLazyRoute._addFileChildren(DashboardRouteLazyRouteChildren)
const rootRouteChildren: RootRouteChildren = {
IndexLazyRoute: IndexLazyRoute,
DashboardRouteLazyRoute: DashboardRouteLazyRouteWithChildren,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
+79
View File
@@ -0,0 +1,79 @@
import { TanStackDevtools } from "@tanstack/react-devtools";
import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
import { Toaster } from "@workspace/ui/components/sonner";
import { NavigationProgress } from "@workspace/ui/composed/navigation-progress";
import { TanStackQueryDevtools } from "@workspace/ui/integrations/tanstack-query-devtools";
import { getCookie } from "@workspace/ui/lib/cookies";
import { getGlobalConfig } from "@workspace/ui/services/common/common";
import { isBrowser } from "@workspace/ui/utils/index";
import { useEffect } from "react";
import { Helmet, HelmetProvider } from "react-helmet-async";
import { useGlobalStore } from "@/stores/global";
export const Route = createRootRouteWithContext()({
component: () => {
const { common, setCommon, getUserInfo } = useGlobalStore();
useEffect(() => {
const initializeApp = async () => {
try {
const configResponse = await getGlobalConfig();
if (configResponse.data?.data) {
setCommon(configResponse.data.data);
}
try {
if (getCookie("Authorization")) {
await getUserInfo();
}
} catch {
/* empty */
}
} catch (error) {
console.error("Failed to initialize app:", error);
}
};
initializeApp();
}, []);
const { site } = common;
const title = site.site_name || "Loading...";
const description = site.site_desc || "";
const keywords = site.keywords || "";
const logo = site.site_logo || "";
const url = isBrowser() ? window.location.href : "";
return (
<HelmetProvider>
<Helmet>
<title>{title}</title>
<meta content={description} name="description" />
<meta content={keywords} name="keywords" />
<link href={url} rel="canonical" />
<link href={logo} rel="icon" type="image/svg+xml" />
<link href={logo} rel="apple-touch-icon" sizes="180x180" />
<link href="/site.webmanifest" rel="manifest" />
</Helmet>
<NavigationProgress />
<Outlet />
<Toaster closeButton richColors />
<div
dangerouslySetInnerHTML={{ __html: common?.site.custom_html || "" }}
id="custom_html"
/>
<TanStackDevtools
config={{
position: "bottom-right",
}}
plugins={[
{
name: "Tanstack Router",
render: <TanStackRouterDevtoolsPanel />,
},
TanStackQueryDevtools,
]}
/>
</HelmetProvider>
);
},
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import Ads from "@/sections/ads";
export const Route = createLazyFileRoute("/dashboard/ads/")({
component: Ads,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import Announcement from "@/sections/announcement";
export const Route = createLazyFileRoute("/dashboard/announcement/")({
component: Announcement,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import AuthControl from "@/sections/auth-control";
export const Route = createLazyFileRoute("/dashboard/auth-control/")({
component: AuthControl,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import Coupon from "@/sections/coupon";
export const Route = createLazyFileRoute("/dashboard/coupon/")({
component: Coupon,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import Document from "@/sections/document";
export const Route = createLazyFileRoute("/dashboard/document/")({
component: Document,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import Dashboard from "@/sections/dashboard";
export const Route = createLazyFileRoute("/dashboard/")({
component: Dashboard,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import BalanceLogPage from "@/sections/log/balance";
export const Route = createLazyFileRoute("/dashboard/log/balance")({
component: BalanceLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import CommissionLogPage from "@/sections/log/commission";
export const Route = createLazyFileRoute("/dashboard/log/commission")({
component: CommissionLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import EmailLogPage from "@/sections/log/email";
export const Route = createLazyFileRoute("/dashboard/log/email")({
component: EmailLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import GiftLogPage from "@/sections/log/gift";
export const Route = createLazyFileRoute("/dashboard/log/gift")({
component: GiftLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import LoginLogPage from "@/sections/log/login";
export const Route = createLazyFileRoute("/dashboard/log/login")({
component: LoginLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import MobileLogPage from "@/sections/log/mobile";
export const Route = createLazyFileRoute("/dashboard/log/mobile")({
component: MobileLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import RegisterLogPage from "@/sections/log/register";
export const Route = createLazyFileRoute("/dashboard/log/register")({
component: RegisterLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import ResetSubscribeLogPage from "@/sections/log/reset-subscribe";
export const Route = createLazyFileRoute("/dashboard/log/reset-subscribe")({
component: ResetSubscribeLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import ServerTrafficLogPage from "@/sections/log/server-traffic";
export const Route = createLazyFileRoute("/dashboard/log/server-traffic")({
component: ServerTrafficLogPage,
});
@@ -0,0 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import SubscribeTrafficLogPage from "@/sections/log/subscribe-traffic";
export const Route = createLazyFileRoute("/dashboard/log/subscribe-traffic")({
component: SubscribeTrafficLogPage,
});

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