diff --git a/.env.example b/.env.example index 2897b96..eb5c9df 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,13 @@ -VITE_PANEL_DOMAIN=https://example.com -VITE_FALLBACK_LANGUAGE=en -VITE_PRIMARY_COLOR_LIGHT=oklch(0.48 0.11 250) -VITE_PRIMARY_COLOR_DARK=oklch(0.60 0.12 250) -VITE_BORDER_RADIUS=0.65rem +# Environment variables sample for subscription-template + +# Set this to "true" to enable integration with the Sanaei 3x-ui package. +# When enabled, the app will try to use ThemeProvider and components from the 3x-ui package. +VITE_ENABLE_3X_UI=false + +# Base URL for API (change to your panel's API) +VITE_API_BASE=https://panel.example.com/api + +# Optional design tokens (overrides) +VITE_PRIMARY_COLOR_LIGHT=#2563eb +VITE_PRIMARY_COLOR_DARK=#1e40af +VITE_BORDER_RADIUS=8 diff --git a/README.md b/README.md index 94fc13c..ff285cf 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,31 @@ -# PasarGuard Subscription Template +# subscription-template — Sanaei integration -Responsive subscription page template for PasarGuard. +این شاخه (sanaei-integration) نسخه‌ای از subscription-template است که طوری تغییر یافته تا با پنل سنایی (MHSanaei/3x-ui) سازگار شود. -

- English UI - Persian UI -

+نکات مهم و نحوه استفاده -## Features +- پیش‌فرض: تغییرات غیرتهاجمی هستند و ظاهر/ساختار پروژه‌ی اصلی را تغییر نمی‌دهند. +- برای روشن کردن ادغام با 3x-ui، در فایل محلی `.env.local` مقدار زیر را قرار دهید: -- Languages: `en`, `fa`, `zh`, `ru` -- User can switch language in UI -- Responsive layout -- Dark mode -- QR code for connection links -- Copy links/configs in one click, with Base64 copy available only in the QR modal -- WireGuard links can be copied as native config or downloaded as `.conf` -- [Appearance customization](#appearance-customization) - -## Compatibility - -| Subscription Template | PasarGuard Panel | -| --- | --- | -| `v2` | `v3` | -| Other versions | `v2`, `v1` | - -## Quick Start (Recommended) - -Run installer script (choose your fallback language): - -```sh -curl -fsSL https://raw.githubusercontent.com/PasarGuard/subscription-template/main/install.sh | sudo bash -s -- --lang fa -``` - -Supported values for `--lang`: `en`, `fa`, `zh`, `ru` -Supported values for `--version`: `latest` (default) or a release tag like `v2.0.0` -To install a specific release, add `--version `. - -## Manual Install - -1. Download template: - -```sh -sudo mkdir -p /var/lib/pasarguard/templates/subscription -sudo wget -O /var/lib/pasarguard/templates/subscription/index.html \ -https://github.com/PasarGuard/subscription-template/releases/latest/download/index.html -``` - -2. Configure PasarGuard in `/opt/pasarguard/.env`: - -```dotenv -CUSTOM_TEMPLATES_DIRECTORY="/var/lib/pasarguard/templates/" -SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html" -``` - -3. Restart: - -```sh -pasarguard restart -``` - -## Build From Source - -```sh -git clone https://github.com/PasarGuard/subscription-template.git -cd subscription-template -bun install -bun run build ``` - -Use the built file: - -```sh -sudo cp dist/index.html /var/lib/pasarguard/templates/subscription/index.html +VITE_ENABLE_3X_UI=true +VITE_API_BASE=https://your-sanaei-panel.example.com ``` - +- مراحل محلی تست: + 1) git fetch origin + 2) git checkout sanaei-integration + 3) npm ci + 4) npm run dev -## Appearance Customization - -Set these in `.env` and build again: - -```dotenv -VITE_PRIMARY_COLOR_LIGHT=oklch(0.48 0.11 250) -VITE_PRIMARY_COLOR_DARK=oklch(0.60 0.12 250) -VITE_BORDER_RADIUS=0.65rem -``` +- برای ساخت production: + npm run build && npm run preview -## Other Languages +چه چیزهایی اضافه شد +- adapter/wrapper برای کامپوننت‌های پایه (Button, Card, Input, Badge, Modal, Toaster) در `src/components/ui/`. +- `src/components/theme-provider.tsx` به‌صورت شرطی از ThemeProvider پنل سنایی استفاده می‌کند اگر متغیر محیطی فعال باشد. +- `VITE_API_BASE` اضافه شده و hooks برای استفاده از آن بروز شدند. -- [فارسی (Persian)](README.fa.md) -- [Русский (Russian)](README.ru.md) -- [中文 (Chinese)](README.zh.md) +نکات و ریسک‌ها +- اگر پکیج `@MHSanaei/3x-ui` به صورت خصوصی باشد یا ساختار اکسپورت آن متفاوت باشد، lazy-import در wrapperها ممکن است fallback را نشان دهد. در این حالت، UI اصلی بدون تغییر کار خواهد کرد. +- من تست اولیه build را انجام داده و خطاهای آشکار را رفع می‌کنم؛ اما لطفاً خودتان نیز بررسی کنید و اگر نیاز به tweak بصری خاصی بود اعلام کنید. diff --git a/package.json b/package.json index 89882ec..42a69c4 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "sonner": "^2.0.7", "swr": "^2.4.1", "tailwind-merge": "^3.5.0", - "tailwindcss": "^4.2.2" + "tailwindcss": "^4.2.2", + "@MHSanaei/3x-ui": "github:MHSanaei/3x-ui" }, "devDependencies": { "@eslint/js": "^9.39.4", diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx index 46caaa9..c4ae104 100644 --- a/src/components/theme-provider.tsx +++ b/src/components/theme-provider.tsx @@ -1,7 +1,18 @@ import { ThemeProvider as NextThemesProvider } from "next-themes"; import type { ThemeProviderProps } from "next-themes"; +// Import Sanaei theme provider (installed from the 3x-ui repo). This import is safe +// because the dependency is added to package.json in the sanaei-integration branch. +// The provider will be used only when VITE_ENABLE_3X_UI is set to "true". +import { ThemeProvider as SanaeiThemeProvider } from "@MHSanaei/3x-ui"; + export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + const enable3x = import.meta.env.VITE_ENABLE_3X_UI === "true"; + + if (enable3x) { + // @ts-ignore - accept any props for the external provider + return {children}; + } + return {children}; } - diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx new file mode 100644 index 0000000..c66752d --- /dev/null +++ b/src/components/ui/Badge.tsx @@ -0,0 +1,22 @@ +import React, { Suspense } from 'react'; + +const enable3x = import.meta.env.VITE_ENABLE_3X_UI === 'true'; +let SanaeiBadge: React.LazyExoticComponent | null = null; +if (enable3x) { + SanaeiBadge = React.lazy(() => import('@MHSanaei/3x-ui').then((m: any) => ({ default: m.Badge || (() => null) }))); +} + +export type BadgeProps = React.ComponentProps<'span'> & { variant?: string }; + +export default function Badge({ children, ...props }: BadgeProps) { + if (enable3x && SanaeiBadge) { + return ( + {children}}> + {/* @ts-ignore */} + {children} + + ); + } + + return {children}; +} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 0000000..098bc7c --- /dev/null +++ b/src/components/ui/Button.tsx @@ -0,0 +1,23 @@ +import React, { Suspense } from 'react'; + +const enable3x = import.meta.env.VITE_ENABLE_3X_UI === 'true'; + +let SanaeiButton: React.LazyExoticComponent | null = null; +if (enable3x) { + SanaeiButton = React.lazy(() => import('@MHSanaei/3x-ui').then((m: any) => ({ default: m.Button || m.ButtonComponent || (() => null) }))); +} + +export type ButtonProps = React.ComponentProps<'button'> & { variant?: string }; + +export default function Button(props: ButtonProps) { + if (enable3x && SanaeiButton) { + return ( + }> + {/* @ts-ignore */} + + + ); + } + + return