From 830430645c3c61d91fc0fb94944e753f4eb34d03 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Thu, 19 Mar 2026 02:56:42 -0700 Subject: [PATCH] =?UTF-8?q?merge:=20=E5=90=8C=E6=AD=A5=20origin/main=20(v1?= =?UTF-8?q?.4.0)=20=E5=88=B0=E5=AE=9A=E5=88=B6=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 24 ++++++ apps/admin/package.json | 2 + apps/admin/src/sections/auth/turnstile.tsx | 23 +++--- .../sections/servers/form-schema/constants.ts | 9 +-- .../sections/servers/form-schema/defaults.ts | 5 ++ .../sections/servers/form-schema/schemas.ts | 5 ++ .../servers/form-schema/useProtocolFields.ts | 76 ++++++++++++++++++- apps/user/package.json | 2 + apps/user/src/sections/auth/turnstile.tsx | 23 +++--- bunfig.toml | 5 ++ functions/v1/[[path]].ts | 63 +++++++++++++++ package.json | 2 +- 12 files changed, 209 insertions(+), 30 deletions(-) create mode 100644 bunfig.toml create mode 100644 functions/v1/[[path]].ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7701026..e7ca6dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,30 @@ This document records all notable changes to ShadCN Admin. --- +## [1.4.0](https://github.com/perfect-panel/frontend/compare/v1.3.15...v1.4.0) (2026-03-13) + +### ✨ Features / 新功能 + +* **servers:** add Reality support for anytls; fix vless flow ([be24ba0](https://github.com/perfect-panel/frontend/commit/be24ba03f564181d2bd6bc611917004d24e21aee)) + +## [1.3.15](https://github.com/perfect-panel/frontend/compare/v1.3.14...v1.3.15) (2026-03-12) + +### 🐛 Bug Fixes / 问题修复 + +* **ci:** remove deprecated forwardRef usage in turnstile components ([89ece6e](https://github.com/perfect-panel/frontend/commit/89ece6e959892808c3a67ffb43612c9b90f1e38a)) + +## [1.3.14](https://github.com/perfect-panel/frontend/compare/v1.3.13...v1.3.14) (2026-03-11) + +### 🐛 Bug Fixes / 问题修复 + +* improve renewal button readability on subscription card ([#25](https://github.com/perfect-panel/frontend/issues/25)) ([ced5c1d](https://github.com/perfect-panel/frontend/commit/ced5c1d24e5af26ff8fdf700fd2b9b864706694a)) + +## [1.3.13](https://github.com/perfect-panel/frontend/compare/v1.3.12...v1.3.13) (2026-03-07) + +### 🐛 Bug Fixes / 问题修复 + +* **user:** show expire time & improve renewal dialog on mobile ([#22](https://github.com/perfect-panel/frontend/issues/22)) ([889dbf9](https://github.com/perfect-panel/frontend/commit/889dbf97736252d54d3e1d7ed0622e91ee4e8a1e)) + ## [1.3.12](https://github.com/perfect-panel/frontend/compare/v1.3.11...v1.3.12) (2026-02-26) ### 🐛 Bug Fixes / 问题修复 diff --git a/apps/admin/package.json b/apps/admin/package.json index e49cbdd..0e4115b 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -14,6 +14,8 @@ }, "dependencies": { "@faker-js/faker": "^10.0.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", "@lottiefiles/dotlottie-react": "^0.17.7", "@noble/curves": "^2.0.1", "@stripe/react-stripe-js": "^5.4.0", diff --git a/apps/admin/src/sections/auth/turnstile.tsx b/apps/admin/src/sections/auth/turnstile.tsx index 4b0f821..329b708 100644 --- a/apps/admin/src/sections/auth/turnstile.tsx +++ b/apps/admin/src/sections/auth/turnstile.tsx @@ -1,5 +1,5 @@ import { useTheme } from "next-themes"; -import { forwardRef, useEffect, useImperativeHandle } from "react"; +import { type RefObject, useEffect, useImperativeHandle } from "react"; import { useTranslation } from "react-i18next"; import Turnstile, { useTurnstile } from "react-turnstile"; @@ -9,14 +9,17 @@ export type TurnstileRef = { reset: () => void; }; -const CloudFlareTurnstile = forwardRef< - TurnstileRef, - { - id?: string; - value?: null | string; - onChange: (value?: string) => void; - } ->(function CloudFlareTurnstile({ id, value, onChange }, ref) { +const CloudFlareTurnstile = function CloudFlareTurnstile({ + id, + value, + onChange, + ref, +}: { + id?: string; + value?: null | string; + onChange: (value?: string) => void; + ref?: RefObject; +}) { const { common } = useGlobalStore(); const { verify } = common; const { resolvedTheme } = useTheme(); @@ -58,6 +61,6 @@ const CloudFlareTurnstile = forwardRef< /> ) ); -}); +}; export default CloudFlareTurnstile; diff --git a/apps/admin/src/sections/servers/form-schema/constants.ts b/apps/admin/src/sections/servers/form-schema/constants.ts index b6e2d8a..1645f9e 100644 --- a/apps/admin/src/sections/servers/form-schema/constants.ts +++ b/apps/admin/src/sections/servers/form-schema/constants.ts @@ -66,18 +66,13 @@ export const SECURITY = { trojan: ["tls"] as const, hysteria: ["tls"] as const, tuic: ["tls"] as const, - anytls: ["tls"] as const, + anytls: ["none", "tls", "reality"] as const, naive: ["none", "tls"] as const, http: ["none", "tls"] as const, } as const; export const FLOWS = { - vless: [ - "none", - "xtls-rprx-direct", - "xtls-rprx-splice", - "xtls-rprx-vision", - ] as const, + vless: ["none", "xtls-rprx-vision"] as const, } as const; export const TUIC_UDP_RELAY_MODES = ["native", "quic"] as const; diff --git a/apps/admin/src/sections/servers/form-schema/defaults.ts b/apps/admin/src/sections/servers/form-schema/defaults.ts index b5ebb99..6b6f93a 100644 --- a/apps/admin/src/sections/servers/form-schema/defaults.ts +++ b/apps/admin/src/sections/servers/form-schema/defaults.ts @@ -184,6 +184,11 @@ export function getProtocolDefaultConfig(proto: ProtocolType) { cert_mode: "none", cert_dns_provider: null, cert_dns_env: null, + reality_server_addr: null, + reality_server_port: null, + reality_private_key: null, + reality_public_key: null, + reality_short_id: null, ratio: 1, } as any; default: diff --git a/apps/admin/src/sections/servers/form-schema/schemas.ts b/apps/admin/src/sections/servers/form-schema/schemas.ts index fc48035..f54515a 100644 --- a/apps/admin/src/sections/servers/form-schema/schemas.ts +++ b/apps/admin/src/sections/servers/form-schema/schemas.ts @@ -156,6 +156,11 @@ const anytls = z.object({ cert_mode: z.enum(CERT_MODES).nullish(), cert_dns_provider: nullableString, cert_dns_env: nullableString, + reality_server_addr: nullableString, + reality_server_port: nullablePort, + reality_private_key: nullableString, + reality_public_key: nullableString, + reality_short_id: nullableString, }); const socks = z.object({ diff --git a/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts b/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts index 4a04611..cab2a7a 100644 --- a/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts +++ b/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts @@ -279,7 +279,10 @@ export function useProtocolFields() { options: FLOWS.vless, defaultValue: "none", group: "transport", - condition: (p) => p.transport === "tcp", + condition: (p) => + p.encryption === "mlkem768x25519plus" || + (p.transport === "tcp" && + (p.security === "tls" || p.security === "reality")), }, { name: "security", @@ -441,7 +444,7 @@ export function useProtocolFields() { name: "encryption_ticket", type: "input", label: t("encryption_ticket", "Ticket Time"), - placeholder: "e.g. 600s", + placeholder: "e.g. 600", group: "encryption", condition: (p) => p.encryption === "mlkem768x25519plus" && @@ -1093,6 +1096,14 @@ export function useProtocolFields() { placeholder: "1-65535", group: "basic", }, + { + name: "security", + type: "select", + label: t("security", "Security"), + options: SECURITY.anytls, + defaultValue: "tls", + group: "security", + }, { name: "padding_scheme", type: "textarea", @@ -1108,12 +1119,14 @@ export function useProtocolFields() { type: "input", label: t("security_sni", "SNI"), group: "security", + condition: (p) => p.security !== "none", }, { name: "allow_insecure", type: "switch", label: t("security_allow_insecure", "Allow Insecure"), group: "security", + condition: (p) => p.security !== "none", }, { name: "fingerprint", @@ -1122,6 +1135,7 @@ export function useProtocolFields() { options: FINGERPRINTS, defaultValue: "chrome", group: "security", + condition: (p) => p.security !== "none", }, { name: "cert_mode", @@ -1130,6 +1144,7 @@ export function useProtocolFields() { options: CERT_MODES, defaultValue: "none", group: "security", + condition: (p) => p.security === "tls", }, { name: "cert_dns_provider", @@ -1148,6 +1163,63 @@ export function useProtocolFields() { group: "security", condition: (p) => p.cert_mode === "dns", }, + { + name: "reality_server_addr", + type: "input", + label: t("security_server_address", "Reality Server Address"), + placeholder: t( + "security_server_address_placeholder", + "e.g. 1.2.3.4 or domain" + ), + group: "reality", + condition: (p) => p.security === "reality", + }, + { + name: "reality_server_port", + type: "number", + label: t("security_server_port", "Reality Server Port"), + min: 1, + max: 65_535, + placeholder: "1-65535", + group: "reality", + condition: (p) => p.security === "reality", + }, + { + name: "reality_private_key", + type: "input", + label: t("security_private_key", "Reality Private Key"), + placeholder: t( + "security_private_key_placeholder", + "Enter private key" + ), + group: "reality", + generate: { + function: generateRealityKeyPair, + updateFields: { + reality_private_key: "privateKey", + reality_public_key: "publicKey", + } as Record, + }, + condition: (p) => p.security === "reality", + }, + { + name: "reality_public_key", + type: "input", + label: t("security_public_key", "Reality Public Key"), + placeholder: t("security_public_key_placeholder", "Enter public key"), + group: "reality", + condition: (p) => p.security === "reality", + }, + { + name: "reality_short_id", + type: "input", + label: t("security_short_id", "Reality Short ID"), + group: "reality", + generate: { + function: generateRealityShortId, + }, + condition: (p) => p.security === "reality", + }, ], }), [t] diff --git a/apps/user/package.json b/apps/user/package.json index c6ce1dc..955b9df 100644 --- a/apps/user/package.json +++ b/apps/user/package.json @@ -14,6 +14,8 @@ }, "dependencies": { "@faker-js/faker": "^10.0.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", "@lottiefiles/dotlottie-react": "^0.17.7", "@stripe/react-stripe-js": "^5.4.0", "@stripe/stripe-js": "^8.5.2", diff --git a/apps/user/src/sections/auth/turnstile.tsx b/apps/user/src/sections/auth/turnstile.tsx index 591b3e5..107168c 100644 --- a/apps/user/src/sections/auth/turnstile.tsx +++ b/apps/user/src/sections/auth/turnstile.tsx @@ -1,7 +1,7 @@ "use client"; import { useTheme } from "next-themes"; -import { forwardRef, useEffect, useImperativeHandle } from "react"; +import { type RefObject, useEffect, useImperativeHandle } from "react"; import { useTranslation } from "react-i18next"; import Turnstile, { useTurnstile } from "react-turnstile"; import { useGlobalStore } from "@/stores/global"; @@ -10,14 +10,17 @@ export type TurnstileRef = { reset: () => void; }; -const CloudFlareTurnstile = forwardRef< - TurnstileRef, - { - id?: string; - value?: null | string; - onChange: (value?: string) => void; - } ->(function CloudFlareTurnstile({ id, value, onChange }, ref) { +const CloudFlareTurnstile = function CloudFlareTurnstile({ + id, + value, + onChange, + ref, +}: { + id?: string; + value?: null | string; + onChange: (value?: string) => void; + ref?: RefObject; +}) { const { common } = useGlobalStore(); const { verify } = common; const { resolvedTheme } = useTheme(); @@ -63,6 +66,6 @@ const CloudFlareTurnstile = forwardRef< /> ) ); -}); +}; export default CloudFlareTurnstile; diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..d67c70f --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,5 @@ +[install] +# Use hoisted (flat) node_modules layout instead of isolated installs. +# This ensures Vite/Rollup can resolve dependencies correctly in CI environments +# such as Cloudflare Pages. +linker = "hoisted" diff --git a/functions/v1/[[path]].ts b/functions/v1/[[path]].ts new file mode 100644 index 0000000..da2b192 --- /dev/null +++ b/functions/v1/[[path]].ts @@ -0,0 +1,63 @@ +interface Env { + API_BASE_URL: string; +} + +export const onRequest: PagesFunction = async (context) => { + const { request, env } = context; + + const apiBase = (env.API_BASE_URL || "https://api.ppanel.dev").replace( + /\/$/, + "", + ); + + const url = new URL(request.url); + const targetUrl = `${apiBase}${url.pathname}${url.search}`; + + const headers = new Headers(request.headers); + headers.set("Host", new URL(apiBase).host); + headers.delete("cf-connecting-ip"); + headers.delete("cf-ipcountry"); + headers.delete("cf-ray"); + headers.delete("cf-visitor"); + + const init: RequestInit = { + method: request.method, + headers, + redirect: "follow", + }; + + if ( + request.method !== "GET" && + request.method !== "HEAD" && + request.body !== null + ) { + init.body = request.body; + } + + const response = await fetch(targetUrl, init); + + const responseHeaders = new Headers(response.headers); + responseHeaders.delete("set-cookie"); + responseHeaders.set("Access-Control-Allow-Origin", url.origin); + responseHeaders.set( + "Access-Control-Allow-Methods", + "GET, POST, PUT, DELETE, PATCH, OPTIONS", + ); + responseHeaders.set( + "Access-Control-Allow-Headers", + "Content-Type, Authorization", + ); + + if (request.method === "OPTIONS") { + return new Response(null, { + status: 204, + headers: responseHeaders, + }); + } + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: responseHeaders, + }); +}; diff --git a/package.json b/package.json index 6f306ea..8d37ebf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "1.3.12", + "version": "1.4.0", "private": true, "homepage": "https://github.com/perfect-panel/frontend", "bugs": {