mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-09-19 09:40:13 +00:00
chore: merge upstream/main into admin-frontend-domain preserving custom domain and branding features
This commit is contained in:
+97
-43
@@ -7,6 +7,7 @@ import { Combobox } from "@/components/ui/combobox"
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
@@ -25,13 +26,13 @@ import { Textarea } from "@/components/ui/textarea"
|
||||
import { useAuth } from "@/hooks/useAuth"
|
||||
import { useNotification } from "@/hooks/useNotfication"
|
||||
import useSetting from "@/hooks/useSetting"
|
||||
import { asOptionalField } from "@/lib/utils"
|
||||
import { asOptionalField, safeExternalHref } from "@/lib/utils"
|
||||
import { nezhaLang, settingCoverageTypes } from "@/types"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useEffect } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { Navigate } from "react-router-dom"
|
||||
import { toast } from "sonner"
|
||||
import { z } from "zod"
|
||||
|
||||
@@ -44,6 +45,8 @@ const settingFormSchema = z.object({
|
||||
language: z.string().min(2),
|
||||
user_template: z.string().min(1),
|
||||
install_host: asOptionalField(z.string()),
|
||||
dashboard_host: asOptionalField(z.string()),
|
||||
reserved_hosts: asOptionalField(z.string()),
|
||||
custom_code: asOptionalField(z.string()),
|
||||
custom_code_dashboard: asOptionalField(z.string()),
|
||||
web_real_ip_header: asOptionalField(z.string()),
|
||||
@@ -53,6 +56,7 @@ const settingFormSchema = z.object({
|
||||
enable_ip_change_notification: asOptionalField(z.boolean()),
|
||||
enable_plain_ip_in_notification: asOptionalField(z.boolean()),
|
||||
custom_logo: asOptionalField(z.string()),
|
||||
|
||||
custom_description: asOptionalField(z.string()),
|
||||
custom_links: asOptionalField(z.string()),
|
||||
background_image_day: asOptionalField(z.string()),
|
||||
@@ -66,13 +70,14 @@ const settingFormSchema = z.object({
|
||||
domain_expiry_notification_days: asOptionalField(z.string()),
|
||||
server_expiry_notification_days: asOptionalField(z.string()),
|
||||
expiry_notification_group_id: z.coerce.number().int().min(0),
|
||||
enable_mcp: asOptionalField(z.boolean()),
|
||||
|
||||
})
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { t, i18n } = useTranslation()
|
||||
const { data: config, mutate } = useSetting()
|
||||
const { profile } = useAuth()
|
||||
const navigate = useNavigate()
|
||||
const { profile, loading: authLoading } = useAuth()
|
||||
|
||||
const { notifierGroup } = useNotification()
|
||||
const ngroupList = notifierGroup?.map((ng) => ({
|
||||
@@ -82,12 +87,10 @@ export default function SettingsPage() {
|
||||
|
||||
const isAdmin = profile?.role === 0
|
||||
|
||||
if (!isAdmin) {
|
||||
navigate("/dashboard/settings/online-user")
|
||||
}
|
||||
|
||||
const form = useForm({
|
||||
// 所有 hooks 必须在条件 return 之前调用,否则违反 rules-of-hooks。
|
||||
const form = useForm<z.infer<typeof settingFormSchema>>({
|
||||
resolver: zodResolver(settingFormSchema) as any,
|
||||
|
||||
defaultValues: config
|
||||
? {
|
||||
...config.config,
|
||||
@@ -117,6 +120,13 @@ export default function SettingsPage() {
|
||||
}
|
||||
}, [config?.config, form])
|
||||
|
||||
if (authLoading) {
|
||||
return null
|
||||
}
|
||||
if (!isAdmin) {
|
||||
return <Navigate to="/dashboard/settings/api-tokens" replace />
|
||||
}
|
||||
|
||||
const onSubmit = async (values: any) => {
|
||||
try {
|
||||
await updateSettings(values)
|
||||
@@ -129,19 +139,19 @@ export default function SettingsPage() {
|
||||
}),
|
||||
})
|
||||
return
|
||||
} finally {
|
||||
if (values.language != i18n.language) {
|
||||
i18n.changeLanguage(values.language)
|
||||
}
|
||||
toast(t("Success"))
|
||||
}
|
||||
if (values.language != i18n.language) {
|
||||
i18n.changeLanguage(values.language)
|
||||
}
|
||||
toast(t("Success"))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="px-3">
|
||||
<SettingsTab className="mt-6 mb-4 w-full" />
|
||||
<div>
|
||||
<Form {...form}>
|
||||
<Form {...(form as any)}>
|
||||
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2 my-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -390,14 +400,24 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</SelectItem>
|
||||
<div className="px-8 py-1">
|
||||
<a
|
||||
href={template.repository}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-blue-600 hover:text-blue-800 hover:underline"
|
||||
>
|
||||
{template.repository}
|
||||
</a>
|
||||
{safeExternalHref(
|
||||
template.repository,
|
||||
) ? (
|
||||
<a
|
||||
href={safeExternalHref(
|
||||
template.repository,
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-blue-600 hover:text-blue-800 hover:underline"
|
||||
>
|
||||
{template.repository}
|
||||
</a>
|
||||
) : (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{template.repository}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -459,6 +479,34 @@ export default function SettingsPage() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="dashboard_host"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("DashboardHost")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>{t("DashboardHostHint")}</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="reserved_hosts"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("ReservedHosts")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>{t("ReservedHostsHint")}</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="tls"
|
||||
@@ -512,16 +560,10 @@ export default function SettingsPage() {
|
||||
checked={field.value == "NZ::Use-Peer-IP"}
|
||||
className="ml-2"
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
field.disabled = true
|
||||
form.setValue(
|
||||
"web_real_ip_header",
|
||||
"NZ::Use-Peer-IP",
|
||||
)
|
||||
} else {
|
||||
field.disabled = false
|
||||
form.setValue("web_real_ip_header", "")
|
||||
}
|
||||
form.setValue(
|
||||
"web_real_ip_header",
|
||||
checked ? "NZ::Use-Peer-IP" : "",
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<FormLabel className="font-normal ml-2">
|
||||
@@ -551,16 +593,10 @@ export default function SettingsPage() {
|
||||
checked={field.value == "NZ::Use-Peer-IP"}
|
||||
className="ml-2"
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
field.disabled = true
|
||||
form.setValue(
|
||||
"agent_real_ip_header",
|
||||
"NZ::Use-Peer-IP",
|
||||
)
|
||||
} else {
|
||||
field.disabled = false
|
||||
form.setValue("agent_real_ip_header", "")
|
||||
}
|
||||
form.setValue(
|
||||
"agent_real_ip_header",
|
||||
checked ? "NZ::Use-Peer-IP" : "",
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<FormLabel className="font-normal ml-2">
|
||||
@@ -749,6 +785,24 @@ export default function SettingsPage() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="enable_mcp"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2">
|
||||
<FormControl>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
<Label className="text-sm">{t("EnableMCP")}</Label>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit">{t("Confirm")}</Button>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
Reference in New Issue
Block a user