fix: resolve domain.ts type errors and SWR fetching bugs; fix vps config unmarshal issue

This commit is contained in:
Bot committed 2026-04-26 22:07:27 +08:00
1 parent 825bcb08f4
commit 1e4fae5306
16 files changed
+2513 -2057

No files matched your search

+15 -17
View File
@@ -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() {
</NavigationMenuItem>
{/* ^^^^^^^^^^^ 2. 在这里为桌面端菜单添加新链接 ^^^^^^^^^^^ */}
{/* ======================================================= */}
<NavigationMenuItem>
<NzNavigationMenuLink
asChild
@@ -428,4 +427,3 @@ function Overview() {
</section>
)
}
+45 -15
View File
@@ -177,11 +177,21 @@ export const NotifierCard: React.FC<NotifierCardProps> = ({ data, mutate }) => {
render={({ field }) => (
<FormItem>
<FormLabel>
{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"}
</FormLabel>
<FormControl>
<Input {...field} placeholder={form.watch("type") == 3 ? "123456:ABC-DEF" : ""} />
<Input
{...field}
placeholder={
form.watch("type") == 3
? "123456:ABC-DEF"
: ""
}
/>
</FormControl>
<FormMessage />
</FormItem>
@@ -234,11 +244,13 @@ export const NotifierCard: React.FC<NotifierCardProps> = ({ data, mutate }) => {
</SelectTrigger>
</FormControl>
<SelectContent>
{Object.entries(nrequestTypes).map(([k, v]) => (
<SelectItem key={k} value={k}>
{v}
</SelectItem>
))}
{Object.entries(nrequestTypes).map(
([k, v]) => (
<SelectItem key={k} value={k}>
{v}
</SelectItem>
),
)}
</SelectContent>
</Select>
<FormMessage />
@@ -253,15 +265,21 @@ export const NotifierCard: React.FC<NotifierCardProps> = ({ data, mutate }) => {
render={({ field }) => (
<FormItem>
<FormLabel>
{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")}
</FormLabel>
<FormControl>
<Textarea
className="resize-y"
placeholder={
form.watch("type") == 2 ? "user:pass" :
form.watch("type") == 3 ? "123456789" : '{"User-Agent":"Nezha-Agent"}'
form.watch("type") == 2
? "user:pass"
: form.watch("type") == 3
? "123456789"
: '{"User-Agent":"Nezha-Agent"}'
}
{...field}
/>
@@ -276,11 +294,23 @@ export const NotifierCard: React.FC<NotifierCardProps> = ({ data, mutate }) => {
name="request_body"
render={({ field }) => (
<FormItem>
<FormLabel>{form.watch("type") == 2 ? "Recipient Email" : t("RequestBody")}</FormLabel>
<FormLabel>
{form.watch("type") == 2
? "Recipient Email"
: t("RequestBody")}
</FormLabel>
<FormControl>
<Textarea
className={form.watch("type") == 2 ? "resize-y" : "resize-y h-[240px]"}
placeholder={form.watch("type") == 2 ? "target@example.com" : '...'}
className={
form.watch("type") == 2
? "resize-y"
: "resize-y h-[240px]"
}
placeholder={
form.watch("type") == 2
? "target@example.com"
: "..."
}
{...field}
/>
</FormControl>
+10 -6
View File
@@ -160,13 +160,17 @@ export const ServerConfigCard = ({ sid, menuItem = false, ...props }: ServerConf
const onSubmit = async (values: any) => {
let resp: ModelServerTaskResponse = {}
try {
values.nic_allowlist = values.nic_allowlist_raw
? JSON.parse(values.nic_allowlist_raw)
const submitValues = { ...values }
submitValues.nic_allowlist = submitValues.nic_allowlist_raw
? JSON.parse(submitValues.nic_allowlist_raw)
: undefined
values.hard_drive_partition_allowlist = values.hard_drive_partition_allowlist_raw
? JSON.parse(values.hard_drive_partition_allowlist_raw)
: undefined
resp = await setServerConfig({ config: JSON.stringify(values), servers: [sid] })
submitValues.hard_drive_partition_allowlist =
submitValues.hard_drive_partition_allowlist_raw
? JSON.parse(submitValues.hard_drive_partition_allowlist_raw)
: undefined
delete submitValues.nic_allowlist_raw
delete submitValues.hard_drive_partition_allowlist_raw
resp = await setServerConfig({ config: JSON.stringify(submitValues), servers: [sid] })
} catch (e) {
console.error(e)
toast(t("Error"), {
+59 -29
View File
@@ -28,7 +28,7 @@ import { conv } from "@/lib/utils"
import { asOptionalField } from "@/lib/utils"
import { ModelServer } from "@/types"
import { zodResolver } from "@hookform/resolvers/zod"
import { useState, useEffect } from "react"
import { useEffect, useState } from "react"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { toast } from "sonner"
@@ -65,11 +65,13 @@ const serverFormSchema = z.object({
},
),
),
billing_data: z.object({
registrar: asOptionalField(z.string()),
endDate: asOptionalField(z.string()),
notes: asOptionalField(z.string()),
}).optional(),
billing_data: z
.object({
registrar: asOptionalField(z.string()),
endDate: asOptionalField(z.string()),
notes: asOptionalField(z.string()),
})
.optional(),
})
export const ServerCard: React.FC<ServerCardProps> = ({ data, mutate }) => {
@@ -92,16 +94,16 @@ export const ServerCard: React.FC<ServerCardProps> = ({ data, mutate }) => {
useEffect(() => {
const handleMessage = (e: MessageEvent) => {
if (e.data?.type === 'NZCFG_JSON') {
if (e.data.target === 'public_note') {
form.setValue('public_note', e.data.payload);
toast(t("Success"), { description: "配置已通过可视化构建器自动填入" });
if (e.data?.type === "NZCFG_JSON") {
if (e.data.target === "public_note") {
form.setValue("public_note", e.data.payload)
toast(t("Success"), { description: "配置已通过可视化构建器自动填入" })
}
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, [form, t]);
}
window.addEventListener("message", handleMessage)
return () => window.removeEventListener("message", handleMessage)
}, [form, t])
const onSubmit = async (values: any) => {
try {
@@ -255,7 +257,9 @@ export const ServerCard: React.FC<ServerCardProps> = ({ data, mutate }) => {
)}
/>
<div className="p-3 border rounded-md border-dashed space-y-2">
<Label className="text-xs text-muted-foreground uppercase font-bold">Billing & Expiry</Label>
<Label className="text-xs text-muted-foreground uppercase font-bold">
Billing & Expiry
</Label>
<FormField
control={form.control as any}
name="billing_data.registrar"
@@ -263,7 +267,10 @@ export const ServerCard: React.FC<ServerCardProps> = ({ data, mutate }) => {
<FormItem>
<FormLabel>Registrar</FormLabel>
<FormControl>
<Input placeholder="AWS / Azure /阿里云" {...field} />
<Input
placeholder="AWS / Azure /阿里云"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
@@ -276,7 +283,20 @@ export const ServerCard: React.FC<ServerCardProps> = ({ data, mutate }) => {
<FormItem>
<FormLabel>Expiry Date</FormLabel>
<FormControl>
<Input type="date" {...field} value={field.value?.split('T')[0] || ''} onChange={(e) => field.onChange(e.target.value ? new Date(e.target.value).toISOString() : '')} />
<Input
type="date"
{...field}
value={field.value?.split("T")[0] || ""}
onChange={(e) =>
field.onChange(
e.target.value
? new Date(
e.target.value,
).toISOString()
: "",
)
}
/>
</FormControl>
<FormMessage />
</FormItem>
@@ -290,18 +310,28 @@ export const ServerCard: React.FC<ServerCardProps> = ({ data, mutate }) => {
<FormItem>
<FormLabel className="flex justify-between items-center w-full">
<span>{t("Public") + t("Note")}</span>
<a href="/dashboard/nzcfg.html" target="_blank" className="text-blue-500 hover:text-blue-700 text-xs flex items-center gap-1" onClick={(e) => {
e.preventDefault();
const popup = window.open('/dashboard/nzcfg.html', 'nzcfg', 'width=1000,height=800');
if(popup) {
const timer = setInterval(() => {
if(popup.closed) {
clearInterval(timer);
}
}, 500);
}
}}>
可视化管理配置 <i className="fa-solid fa-up-right-from-square"></i>
<a
href="/dashboard/nzcfg.html"
target="_blank"
className="text-blue-500 hover:text-blue-700 text-xs flex items-center gap-1"
onClick={(e) => {
e.preventDefault()
const popup = window.open(
"/dashboard/nzcfg.html",
"nzcfg",
"width=1000,height=800",
)
if (popup) {
const timer = setInterval(() => {
if (popup.closed) {
clearInterval(timer)
}
}, 500)
}
}}
>
可视化管理配置{" "}
<i className="fa-solid fa-up-right-from-square"></i>
</a>
</FormLabel>
<FormControl>
+1 -2
View File
@@ -23,8 +23,7 @@ const badgeVariants = cva(
)
export interface BadgeProps
extends HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return <div className={cn(badgeVariants({ variant }), className)} {...props} />
+1 -2
View File
@@ -31,8 +31,7 @@ const buttonVariants = cva(
)
export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
asChild?: boolean
}
+2 -1
View File
@@ -67,7 +67,8 @@ const multiSelectVariants = cva(
* Props for MultiSelect component
*/
interface MultiSelectProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
extends
React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof multiSelectVariants> {
/**
* An array of option objects to be displayed in the multi-select component.
+2 -1
View File
@@ -37,7 +37,8 @@ const sheetVariants = cva(
)
interface SheetContentProps
extends ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
extends
ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {
setOpen: Dispatch<SetStateAction<boolean>>
}