diff --git a/public/nzcfg.html b/public/nzcfg.html index 7fdde70..0dd188f 100644 --- a/public/nzcfg.html +++ b/public/nzcfg.html @@ -1,1209 +1,1348 @@ - + - - - - - - - NEZHA配置生成器 - - - - -
-
-
公开备注代码
-
流量监控代码
-
- -
- -
- - -
-
-
-

公开备注代码配置

- -
- - -
- -
- - -
- -
- - -
- - -
-
- -
- - -
- -
- - - -
- -
- -
- - -
-
- - - -
-
- -
- -
- - -
-
- - - -
-
- -
- -
- - -
-
- - - - - -
-
- -
- - -
- -
- - -
- -
- - -
- -
- - - -
-
- -
-
-
- 163 - CN2 - CN2GIA - CMI - CMIN2 - 4837 - 10099 - IEPL - IPLC -
-
- - -
- -
- - -
- - + + + + + + NEZHA配置生成器 + + + + +
+
+
公开备注代码
+
流量监控代码
+
+
-
- -
+ +
+ + +
+
+
+

公开备注代码配置

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ +
+ + +
+ +
+ + + +
+ +
+ +
+ + +
+
+ + + +
+
+ +
+ +
+ + +
+
+ + + +
+
+ +
+ +
+ + +
+
+ + + + + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + +
+
+ +
+
+
+ 163 + CN2 + CN2GIA + CMI + CMIN2 + 4837 + 10099 + IEPL + IPLC +
+
+ + +
+ +
+ + +
+ + +
+
+ +
+
+
+
+ + +
+
+
+

流量监控代码配置

+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+ + +
+ + +
+
+ +
+
- -
-
-
-

流量监控代码配置

+ - - - + if (unlimitedEndDateCheckbox.checked) { + endDate = "0000-00-00T23:59:59" + tz // 设置为无限期 + } else { + endDate = formatDateWithTimezone(document.getElementById("endDate").value, tz) + } - \ No newline at end of file + const autoRenewal = document.getElementById("autoRenewal").value + const cycleSelect = document.getElementById("cycle") + const cycleCustom = document.getElementById("cycleCustom").value + const cycle = cycleSelect.value === "custom" ? cycleCustom : cycleSelect.value + + const freeamountCheckbox = document.getElementById("freeamount") + let amount = "0" + if (!freeamountCheckbox.checked) { + const amountValue = document.getElementById("amountValue").value + const amountCurrencySelect = document.getElementById("amountCurrency") + const amountCustom = document.getElementById("amountCustom").value + const amountCurrency = + amountCurrencySelect.value === "custom" + ? amountCustom + : amountCurrencySelect.value + amount = amountValue + amountCurrency + } + + const unlimitedBandwidthCheckbox = document.getElementById("unlimitedBandwidth") + let bandwidth = "无限" + if (!unlimitedBandwidthCheckbox.checked) { + const bandwidthValue = document.getElementById("bandwidthValue").value + const bandwidthUnitSelect = document.getElementById("bandwidthUnit") + const bandwidthCustom = document.getElementById("bandwidthCustom").value + const bandwidthUnit = + bandwidthUnitSelect.value === "custom" + ? bandwidthCustom + : bandwidthUnitSelect.value + bandwidth = bandwidthValue + bandwidthUnit + } + + const unlimitedTrafficCheckbox = document.getElementById("unlimitedTraffic") + let trafficVol = "无限" + if (!unlimitedTrafficCheckbox.checked) { + const trafficValue = document.getElementById("trafficValue").value + const trafficUnitSelect = document.getElementById("trafficUnit") + const trafficPeriodSelect = document.getElementById("trafficPeriod") + const trafficUnitCustom = document.getElementById("trafficUnitCustom").value + const trafficPeriodCustom = document.getElementById("trafficPeriodCustom").value + let trafficUnit = + trafficUnitSelect.value === "custom" + ? trafficUnitCustom + : trafficUnitSelect.value + let trafficPeriod = + trafficPeriodSelect.value === "custom" + ? trafficPeriodCustom + : trafficPeriodSelect.value + trafficVol = trafficValue + trafficUnit + trafficPeriod + } + + const trafficType = document.getElementById("trafficType").value + const ipv4 = document.getElementById("ipv4").value + const ipv6 = document.getElementById("ipv6").value + let networkRoute = "" + + const routeModeSelect = document.getElementById("routeMode") + if (routeModeSelect.value === "tags") { + const selectedTagsContainer = document.getElementById("selectedTagsContainer") + const selectedTags = Array.from( + selectedTagsContainer.querySelectorAll(".tag"), + ).map((tag) => tag.textContent) + networkRoute = selectedTags.join("|") + } else { + const telecomRoute1 = document.getElementById("telecomRoute1").value + const telecomRoute2 = document.getElementById("telecomRoute2").value + const mobileRoute1 = document.getElementById("mobileRoute1").value + const mobileRoute2 = document.getElementById("mobileRoute2").value + const unicomRoute1 = document.getElementById("unicomRoute1").value + const unicomRoute2 = document.getElementById("unicomRoute2").value + + const telecomRoutes = [telecomRoute1, telecomRoute2] + .filter((route) => route !== "") + .join("|") + const mobileRoutes = [mobileRoute1, mobileRoute2] + .filter((route) => route !== "") + .join("|") + const unicomRoutes = [unicomRoute1, unicomRoute2] + .filter((route) => route !== "") + .join("|") + + const routes = [] + if (telecomRoutes) routes.push(`电信${telecomRoutes}`) + if (mobileRoutes) routes.push(`移动${mobileRoutes}`) + if (unicomRoutes) routes.push(`联通${unicomRoutes}`) + + networkRoute = routes.join(" ") + } + + const extra = document.getElementById("extra").value + + const jsonOutput = document.getElementById("jsonOutput") + const json = { + billingDataMod: { + startDate: formatDateWithTimezone(startDate, tz), + endDate: endDate, // 使用经过处理的endDate + autoRenewal: autoRenewal, + cycle: cycle, + amount: amount, + }, + planDataMod: { + bandwidth: bandwidth, + trafficVol: trafficVol, + trafficType: trafficType, + IPv4: ipv4, + IPv6: ipv6, + networkRoute: networkRoute, + extra: extra, + }, + } + document.querySelector("#jsonOutput code").innerHTML = Prism.highlight( + JSON.stringify(json, null, 2), + Prism.languages.json, + "json", + ) + } + + function generateTrafficMonitorJSON() { + const tz = document.getElementById("timezone2").value + const monitorType = document.getElementById("monitorType").value + const maxTrafficValue = document.getElementById("maxTraffic").value + const maxTrafficUnit = document.getElementById("maxTrafficUnit").value + const cycleStart = document.getElementById("cycleStart").value + const cycleInterval = document.getElementById("cycleInterval").value + const cycleUnit = document.getElementById("cycleUnit").value + const cover = document.getElementById("cover").value + const ignore = document.getElementById("ignore").value + + let maxBytes = parseFloat(maxTrafficValue) + if (maxTrafficUnit === "GB") { + maxBytes *= 1024 * 1024 * 1024 + } else if (maxTrafficUnit === "TB") { + maxBytes *= 1024 * 1024 * 1024 * 1024 + } else { + // MB + maxBytes *= 1024 * 1024 + } + + const ignoreArray = ignore + .split(",") + .map((item) => item.trim()) + .filter((item) => item !== "") + const ignoreObject = {} + ignoreArray.forEach((item) => { + ignoreObject[item] = true + }) + + const jsonOutput2 = document.getElementById("jsonOutput2") + const json = [ + { + type: monitorType, + max: maxBytes, + cycle_start: formatDateWithTimezone(cycleStart, tz), + cycle_interval: parseInt(cycleInterval), + cycle_unit: cycleUnit, + cover: parseInt(cover), + ignore: ignoreObject, + }, + ] + document.querySelector("#jsonOutput2 code").innerHTML = Prism.highlight( + JSON.stringify(json, null, 2), + Prism.languages.json, + "json", + ) + } + + function toggleTheme() { + const body = document.body + const themeToggle = document.getElementById("themeToggle") + const currentTheme = themeToggle.getAttribute("data-theme") + let newTheme + + if (currentTheme === "system") { + newTheme = "light" + body.classList.remove("dark-mode") + themeToggle.innerHTML = '' + } else if (currentTheme === "light") { + newTheme = "dark" + body.classList.add("dark-mode") + themeToggle.innerHTML = '' + } else { + newTheme = "system" + themeToggle.innerHTML = '' + applySystemTheme() + } + + themeToggle.setAttribute("data-theme", newTheme) + localStorage.setItem("theme-preference", newTheme) + } + + function applySystemTheme() { + const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches + document.body.classList.toggle("dark-mode", isDarkMode) + } + + function setupSystemThemeListener() { + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)") + mediaQuery.addEventListener("change", (e) => { + const themeToggle = document.getElementById("themeToggle") + if (themeToggle.getAttribute("data-theme") === "system") { + document.body.classList.toggle("dark-mode", e.matches) + } + }) + } + + document.addEventListener("DOMContentLoaded", () => { + const themeToggle = document.getElementById("themeToggle") + const savedTheme = localStorage.getItem("theme-preference") || "system" + themeToggle.setAttribute("data-theme", savedTheme) + + if (savedTheme === "system") { + themeToggle.innerHTML = '' + applySystemTheme() + } else if (savedTheme === "dark") { + themeToggle.innerHTML = '' + document.body.classList.add("dark-mode") + } else { + themeToggle.innerHTML = '' + document.body.classList.remove("dark-mode") + } + + setupSystemThemeListener() + + // Initialize end date checkbox state + toggleEndDate() + }) + + + + + diff --git a/src/api/domain.ts b/src/api/domain.ts index b3a0cbb..ef3d08a 100644 --- a/src/api/domain.ts +++ b/src/api/domain.ts @@ -1,44 +1,53 @@ -import { fetcher, FetcherMethod, swrFetcher } from './api' // 导入正确的 fetcher 函数和方法枚举 -import type { Domain, BillingDataMod} from '@/types/api' +// 导入正确的 fetcher 函数和方法枚举 +import type { BillingDataMod, Domain } from "@/types/domain" + +import { FetcherMethod, fetcher } from "./api" // --- GET 请求 (用于 SWR) --- // 获取域名列表的函数,专门为 useSWR 设计 // swrFetcher 内部会调用 fetcher,这一部分是正确的 -export const useDomainList = () => { - return swrFetcher('/api/v1/domains') +export const useDomainList = (url: string) => { + return fetcher(FetcherMethod.GET, url) } - // --- POST, PUT, DELETE 请求 (使用 fetcher) --- // 添加一个新的域名 export const addDomain = (domain: string) => { - return fetcher(FetcherMethod.POST, '/api/v1/domains', { domain }) + return fetcher(FetcherMethod.POST, "/api/v1/domains", { domain }) } // 触发域名验证 export const verifyDomain = (id: number) => { - return fetcher<{ success: boolean; message: string }>(FetcherMethod.POST, `/api/v1/domains/${id}/verify`) + return fetcher<{ success: boolean; message: string }>( + FetcherMethod.POST, + `/api/v1/domains/${id}/verify`, + ) } // 更新域名的配置信息 export const updateDomainConfig = (id: number, billingData: BillingDataMod) => { - return fetcher(FetcherMethod.PUT, `/api/v1/domains/${id}`, { billing_data: billingData }) + return fetcher(FetcherMethod.PUT, `/api/v1/domains/${id}`, { + billing_data: billingData, + }) } // 删除一个域名 export const deleteDomain = (id: number) => { - // DELETE 请求通常没有响应体,所以 T 可以是 any 或 unknown - return fetcher(FetcherMethod.DELETE, `/api/v1/domains/${id}`) + // DELETE 请求通常没有响应体,所以 T 可以是 any 或 unknown + return fetcher(FetcherMethod.DELETE, `/api/v1/domains/${id}`) } // 更新一个域名(包括公开状态和配置信息) -export const updateDomain = (id: number, data: { is_public: boolean, billing_data: BillingDataMod }) => { +export const updateDomain = ( + id: number, + data: { is_public: boolean; billing_data: BillingDataMod }, +) => { return fetcher(FetcherMethod.PUT, `/api/v1/domains/${id}`, data) } // 同步 Whois 信息 export const syncDomainWHOIS = (id: number) => { - return fetcher(FetcherMethod.POST, `/api/v1/domains/${id}/sync`) -} \ No newline at end of file + return fetcher(FetcherMethod.POST, `/api/v1/domains/${id}/sync`) +} diff --git a/src/components/header.tsx b/src/components/header.tsx index 7b3d73c..f573ec9 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -1,4 +1,6 @@ import { ModeToggle } from "@/components/mode-toggle" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Button } from "@/components/ui/button" import { Drawer, DrawerClose, @@ -9,12 +11,24 @@ import { DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" import { NavigationMenu, NavigationMenuItem, NavigationMenuLink, navigationMenuTriggerStyle, } from "@/components/ui/navigation-menu" +import { IconButton } from "@/components/xui/icon-button" +import { NzNavigationMenuLink } from "@/components/xui/navigation-menu" import { useAuth } from "@/hooks/useAuth" import { useMainStore } from "@/hooks/useMainStore" import { useMediaQuery } from "@/hooks/useMediaQuery" @@ -26,21 +40,6 @@ import { useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import { Link, useLocation, useNavigate } from "react-router-dom" -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" -import { Button } from "@/components/ui/button" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { IconButton } from "@/components/xui/icon-button" -import { NzNavigationMenuLink } from "@/components/xui/navigation-menu" - // ======================================================= // vvvvvvvvvvv 1. 在这里为移动端菜单添加新页面 vvvvvvvvvvv const pages = [ @@ -252,7 +251,7 @@ export default function Header() { {/* ^^^^^^^^^^^ 2. 在这里为桌面端菜单添加新链接 ^^^^^^^^^^^ */} {/* ======================================================= */} - + ) } - diff --git a/src/components/notifier.tsx b/src/components/notifier.tsx index 53a7141..cbb130e 100644 --- a/src/components/notifier.tsx +++ b/src/components/notifier.tsx @@ -177,11 +177,21 @@ export const NotifierCard: React.FC = ({ data, mutate }) => { render={({ field }) => ( - {form.watch("type") == 2 ? "SMTP Server (host:port)" : - form.watch("type") == 3 ? "Bot Token" : "URL"} + {form.watch("type") == 2 + ? "SMTP Server (host:port)" + : form.watch("type") == 3 + ? "Bot Token" + : "URL"} - + @@ -234,11 +244,13 @@ export const NotifierCard: React.FC = ({ data, mutate }) => { - {Object.entries(nrequestTypes).map(([k, v]) => ( - - {v} - - ))} + {Object.entries(nrequestTypes).map( + ([k, v]) => ( + + {v} + + ), + )} @@ -253,15 +265,21 @@ export const NotifierCard: React.FC = ({ data, mutate }) => { render={({ field }) => ( - {form.watch("type") == 2 ? "SMTP User:Pass" : - form.watch("type") == 3 ? "Chat ID" : t("RequestHeader")} + {form.watch("type") == 2 + ? "SMTP User:Pass" + : form.watch("type") == 3 + ? "Chat ID" + : t("RequestHeader")}