mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-02-03 20:20:11 +00:00
feat: batch set server config (#114)
* feat: batch set server config * make every field optional * chore: auto-fix linting and formatting issues * update * [WIP] improve batch edit ux * chore: auto-fix linting and formatting issues
This commit is contained in:
96
package.json
96
package.json
@@ -13,68 +13,68 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
"@radix-ui/react-checkbox": "^1.1.2",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.1",
|
||||
"@radix-ui/react-popover": "^1.1.2",
|
||||
"@radix-ui/react-scroll-area": "^1.2.1",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.5",
|
||||
"@radix-ui/react-avatar": "^1.1.2",
|
||||
"@radix-ui/react-checkbox": "^1.1.3",
|
||||
"@radix-ui/react-dialog": "^1.1.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.5",
|
||||
"@radix-ui/react-label": "^2.1.1",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.4",
|
||||
"@radix-ui/react-popover": "^1.1.5",
|
||||
"@radix-ui/react-scroll-area": "^1.2.2",
|
||||
"@radix-ui/react-select": "^2.1.5",
|
||||
"@radix-ui/react-separator": "^1.1.1",
|
||||
"@radix-ui/react-slot": "^1.1.1",
|
||||
"@radix-ui/react-tabs": "^1.1.1",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.0",
|
||||
"@radix-ui/react-tabs": "^1.1.2",
|
||||
"@tanstack/react-table": "^8.20.6",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@xterm/addon-attach": "^0.11.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
"cmdk": "^1.0.4",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"framer-motion": "^11.14.1",
|
||||
"i18next": "^24.0.2",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"framer-motion": "^11.18.2",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"jotai-zustand": "^0.6.0",
|
||||
"lucide-react": "^0.454.0",
|
||||
"luxon": "^3.5.0",
|
||||
"next-themes": "^0.3.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.9",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.53.1",
|
||||
"react-i18next": "^15.1.2",
|
||||
"react-router-dom": "^6.27.0",
|
||||
"react-virtuoso": "^4.12.0",
|
||||
"sonner": "^1.6.1",
|
||||
"swr": "^2.2.5",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-i18next": "^15.4.0",
|
||||
"react-router-dom": "^7.1.5",
|
||||
"react-virtuoso": "^4.12.3",
|
||||
"sonner": "^1.7.4",
|
||||
"swr": "^2.3.0",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^1.1.1",
|
||||
"zod": "^3.23.8",
|
||||
"zustand": "^5.0.1"
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.24.1",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^4.3.3",
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@types/node": "^22.13.0",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.13.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.14",
|
||||
"globals": "^15.11.0",
|
||||
"postcss": "^8.4.47",
|
||||
"swagger-typescript-api": "^13.0.22",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.11.0",
|
||||
"vite": "^5.4.10"
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.18",
|
||||
"globals": "^15.14.0",
|
||||
"postcss": "^8.5.1",
|
||||
"swagger-typescript-api": "^13.0.23",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.3",
|
||||
"typescript-eslint": "^8.22.0",
|
||||
"vite": "^6.0.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ModelForceUpdateResponse, ModelServer, ModelServerForm } from "@/types"
|
||||
import {
|
||||
ModelServer,
|
||||
ModelServerConfigForm,
|
||||
ModelServerForm,
|
||||
ModelServerTaskResponse,
|
||||
} from "@/types"
|
||||
|
||||
import { FetcherMethod, fetcher } from "./api"
|
||||
|
||||
@@ -10,8 +15,8 @@ export const deleteServer = async (id: number[]): Promise<void> => {
|
||||
return fetcher<void>(FetcherMethod.POST, "/api/v1/batch-delete/server", id)
|
||||
}
|
||||
|
||||
export const forceUpdateServer = async (id: number[]): Promise<ModelForceUpdateResponse> => {
|
||||
return fetcher<ModelForceUpdateResponse>(FetcherMethod.POST, "/api/v1/force-update/server", id)
|
||||
export const forceUpdateServer = async (id: number[]): Promise<ModelServerTaskResponse> => {
|
||||
return fetcher<ModelServerTaskResponse>(FetcherMethod.POST, "/api/v1/force-update/server", id)
|
||||
}
|
||||
|
||||
export const getServers = async (): Promise<ModelServer[]> => {
|
||||
@@ -19,9 +24,11 @@ export const getServers = async (): Promise<ModelServer[]> => {
|
||||
}
|
||||
|
||||
export const getServerConfig = async (id: number): Promise<string> => {
|
||||
return fetcher<string>(FetcherMethod.GET, `/api/v1/server/${id}/config`, null)
|
||||
return fetcher<string>(FetcherMethod.GET, `/api/v1/server/config/${id}`, null)
|
||||
}
|
||||
|
||||
export const setServerConfig = async (id: number, data: string): Promise<void> => {
|
||||
return fetcher<void>(FetcherMethod.POST, `/api/v1/server/${id}/config`, data)
|
||||
export const setServerConfig = async (
|
||||
data: ModelServerConfigForm,
|
||||
): Promise<ModelServerTaskResponse> => {
|
||||
return fetcher<ModelServerTaskResponse>(FetcherMethod.POST, `/api/v1/server/config`, data)
|
||||
}
|
||||
|
||||
104
src/components/server-config-batch.tsx
Normal file
104
src/components/server-config-batch.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import { setServerConfig } from "@/api/server"
|
||||
import { Button, ButtonProps } from "@/components/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { IconButton } from "@/components/xui/icon-button"
|
||||
import { ModelServerTaskResponse } from "@/types"
|
||||
import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { Pusher } from "./xui/pusher"
|
||||
|
||||
interface ServerConfigCardBatchProps extends ButtonProps {
|
||||
sid: number[]
|
||||
}
|
||||
|
||||
export const ServerConfigCardBatch: React.FC<ServerConfigCardBatchProps> = ({ sid, ...props }) => {
|
||||
const { t } = useTranslation()
|
||||
const [data, setData] = useState<Record<string, any>>({})
|
||||
const [open, setOpen] = useState(false)
|
||||
const [currentKey, setCurrentKey] = useState<string>("")
|
||||
const [currentVal, setCurrentVal] = useState<string>("")
|
||||
|
||||
const onSubmit = async () => {
|
||||
let resp: ModelServerTaskResponse = {}
|
||||
try {
|
||||
resp = await setServerConfig({ config: JSON.stringify(data), servers: sid })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast(t("Error"), {
|
||||
description: t("Results.UnExpectedError"),
|
||||
})
|
||||
return
|
||||
}
|
||||
toast(t("Done"), {
|
||||
description:
|
||||
t("Results.ForceUpdate") +
|
||||
(resp.success?.length ? t(`Success`) + ` [${resp.success.join(",")}]` : "") +
|
||||
(resp.failure?.length ? t(`Failure`) + ` [${resp.failure.join(",")}]` : "") +
|
||||
(resp.offline?.length ? t(`Offline`) + ` [${resp.offline.join(",")}]` : ""),
|
||||
})
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<IconButton {...props} icon="cog" />
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-xl">
|
||||
<ScrollArea className="max-h-[calc(100dvh-5rem)] p-3">
|
||||
<div className="items-center mx-1">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("EditServerConfig")}</DialogTitle>
|
||||
<DialogDescription />
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-3 mt-4">
|
||||
<Label>Option</Label>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="option"
|
||||
value={currentKey}
|
||||
onChange={(e) => {
|
||||
setCurrentKey(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<Label>Value</Label>
|
||||
<Textarea
|
||||
className="resize-y"
|
||||
value={currentVal}
|
||||
onChange={(e) => {
|
||||
setCurrentVal(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<Pusher property={[currentKey, currentVal]} setData={setData} />
|
||||
<DialogFooter className="justify-end">
|
||||
<DialogClose asChild>
|
||||
<Button type="button" className="my-2" variant="secondary">
|
||||
{t("Close")}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button type="submit" className="my-2" onClick={onSubmit}>
|
||||
{t("Submit")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getServerConfig, setServerConfig } from "@/api/server"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Button, ButtonProps } from "@/components/ui/button"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import {
|
||||
Dialog,
|
||||
@@ -25,6 +25,7 @@ import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { IconButton } from "@/components/xui/icon-button"
|
||||
import { asOptionalField } from "@/lib/utils"
|
||||
import { ModelServerTaskResponse } from "@/types"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
@@ -56,7 +57,7 @@ const agentConfigSchema = z.object({
|
||||
},
|
||||
),
|
||||
),
|
||||
ip_report_period: z.coerce.number().int().min(30),
|
||||
ip_report_period: asOptionalField(z.coerce.number().int().min(30)),
|
||||
nic_allowlist: asOptionalField(z.record(z.boolean())),
|
||||
nic_allowlist_raw: asOptionalField(
|
||||
z.string().refine(
|
||||
@@ -73,7 +74,7 @@ const agentConfigSchema = z.object({
|
||||
},
|
||||
),
|
||||
),
|
||||
report_delay: z.coerce.number().int().min(1).max(4),
|
||||
report_delay: asOptionalField(z.coerce.number().int().min(1).max(4)),
|
||||
skip_connection_count: asOptionalField(z.boolean()),
|
||||
skip_procs_count: asOptionalField(z.boolean()),
|
||||
temperature: asOptionalField(z.boolean()),
|
||||
@@ -99,7 +100,11 @@ for (let i = 0; i < boolFields.length; i += 2) {
|
||||
groupedBoolFields.push(boolFields.slice(i, i + 2))
|
||||
}
|
||||
|
||||
export const ServerConfigCard = ({ id }: { id: number }) => {
|
||||
interface ServerConfigCardProps extends ButtonProps {
|
||||
sid: number[]
|
||||
}
|
||||
|
||||
export const ServerConfigCard = ({ sid, ...props }: ServerConfigCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [data, setData] = useState<AgentConfig | undefined>(undefined)
|
||||
const [loading, setLoading] = useState(true)
|
||||
@@ -108,7 +113,11 @@ export const ServerConfigCard = ({ id }: { id: number }) => {
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const result = await getServerConfig(id)
|
||||
if (sid.length > 1) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
const result = await getServerConfig(sid[0])
|
||||
setData(JSON.parse(result))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -151,6 +160,7 @@ export const ServerConfigCard = ({ id }: { id: number }) => {
|
||||
}, [data, form])
|
||||
|
||||
const onSubmit = async (values: AgentConfig) => {
|
||||
let resp: ModelServerTaskResponse = {}
|
||||
try {
|
||||
values.nic_allowlist = values.nic_allowlist_raw
|
||||
? JSON.parse(values.nic_allowlist_raw)
|
||||
@@ -158,7 +168,7 @@ export const ServerConfigCard = ({ id }: { id: number }) => {
|
||||
values.hard_drive_partition_allowlist = values.hard_drive_partition_allowlist_raw
|
||||
? JSON.parse(values.hard_drive_partition_allowlist_raw)
|
||||
: undefined
|
||||
await setServerConfig(id, JSON.stringify(values))
|
||||
resp = await setServerConfig({ config: JSON.stringify(values), servers: sid })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast(t("Error"), {
|
||||
@@ -166,14 +176,31 @@ export const ServerConfigCard = ({ id }: { id: number }) => {
|
||||
})
|
||||
return
|
||||
}
|
||||
toast(t("Done"), {
|
||||
description:
|
||||
t("Results.ForceUpdate") +
|
||||
(resp.success?.length ? t(`Success`) + ` [${resp.success.join(",")}]` : "") +
|
||||
(resp.failure?.length ? t(`Failure`) + ` [${resp.failure.join(",")}]` : "") +
|
||||
(resp.offline?.length ? t(`Offline`) + ` [${resp.offline.join(",")}]` : ""),
|
||||
})
|
||||
setOpen(false)
|
||||
form.reset()
|
||||
}
|
||||
|
||||
return (
|
||||
return sid.length < 1 ? (
|
||||
<IconButton
|
||||
{...props}
|
||||
icon="cog"
|
||||
onClick={() => {
|
||||
toast(t("Error"), {
|
||||
description: t("Results.NoRowsAreSelected"),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<IconButton variant="outline" icon="cog" />
|
||||
<IconButton {...props} icon="cog" />
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-xl">
|
||||
{loading ? (
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Expand,
|
||||
FolderClosed,
|
||||
Menu,
|
||||
Minus,
|
||||
Play,
|
||||
Plus,
|
||||
Terminal,
|
||||
@@ -35,6 +36,7 @@ export interface IconButtonProps extends ButtonProps {
|
||||
| "ban"
|
||||
| "expand"
|
||||
| "cog"
|
||||
| "minus"
|
||||
}
|
||||
|
||||
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props, ref) => {
|
||||
@@ -92,6 +94,9 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props,
|
||||
case "cog": {
|
||||
return <CogIcon />
|
||||
}
|
||||
case "minus": {
|
||||
return <Minus />
|
||||
}
|
||||
}
|
||||
})()}
|
||||
</Button>
|
||||
|
||||
50
src/components/xui/pusher.tsx
Normal file
50
src/components/xui/pusher.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
|
||||
import { Label } from "../ui/label"
|
||||
import { Textarea } from "../ui/textarea"
|
||||
import { IconButton } from "./icon-button"
|
||||
|
||||
interface PusherProps {
|
||||
property: [string, string]
|
||||
setData: React.Dispatch<React.SetStateAction<Record<string, any>>>
|
||||
}
|
||||
|
||||
export const Pusher: React.FC<PusherProps> = ({ property, setData }) => {
|
||||
const [cData, setCData] = useState<Record<string, any>>({})
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex gap-2 ml-auto">
|
||||
<IconButton
|
||||
icon="plus"
|
||||
onClick={() => {
|
||||
const [k, v] = property
|
||||
if (k && v) {
|
||||
const temp = { ...cData }
|
||||
temp[k] = JSON.parse(v)
|
||||
setCData(temp)
|
||||
setData(cData)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
icon="minus"
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
const [k] = property
|
||||
if (k) {
|
||||
const temp = { ...cData }
|
||||
temp[k] = undefined
|
||||
setCData(temp)
|
||||
setData(cData)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Label>Preview</Label>
|
||||
<Textarea value={JSON.stringify(cData, null, 2)} readOnly />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { InstallCommandsMenu } from "@/components/install-commands"
|
||||
import { NoteMenu } from "@/components/note-menu"
|
||||
import { ServerCard } from "@/components/server"
|
||||
import { ServerConfigCard } from "@/components/server-config"
|
||||
import { ServerConfigCardBatch } from "@/components/server-config-batch"
|
||||
import { TerminalButton } from "@/components/terminal"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import {
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
import { IconButton } from "@/components/xui/icon-button"
|
||||
import { useServer } from "@/hooks/useServer"
|
||||
import { joinIP } from "@/lib/utils"
|
||||
import { ModelForceUpdateResponse, ModelServer as Server } from "@/types"
|
||||
import { ModelServerTaskResponse, ModelServer as Server } from "@/types"
|
||||
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"
|
||||
import { useEffect, useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
@@ -144,7 +145,7 @@ export default function ServerPage() {
|
||||
<>
|
||||
<TerminalButton id={s.id} />
|
||||
<ServerCard mutate={mutate} data={s} />
|
||||
<ServerConfigCard id={s.id} />
|
||||
<ServerConfigCard sid={[s.id]} variant="outline" />
|
||||
</>
|
||||
</ActionButtonGroup>
|
||||
)
|
||||
@@ -187,7 +188,7 @@ export default function ServerPage() {
|
||||
return
|
||||
}
|
||||
|
||||
let resp: ModelForceUpdateResponse = {}
|
||||
let resp: ModelServerTaskResponse = {}
|
||||
try {
|
||||
resp = await forceUpdateServer(id)
|
||||
} catch (e) {
|
||||
@@ -212,6 +213,10 @@ export default function ServerPage() {
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<ServerConfigCardBatch
|
||||
sid={selectedRows.map((r) => r.original.id)}
|
||||
className="shadow-[inset_0_1px_0_rgba(255,255,255,0.2)] bg-yellow-600 text-white hover:bg-yellow-500 dark:hover:bg-yellow-700 rounded-lg"
|
||||
/>
|
||||
<InstallCommandsMenu className="shadow-[inset_0_1px_0_rgba(255,255,255,0.2)] bg-blue-700 text-white hover:bg-blue-600 dark:hover:bg-blue-800 rounded-lg" />
|
||||
</HeaderButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -99,12 +99,6 @@ export interface GithubComNezhahqNezhaModelCommonResponseGithubComNezhahqNezhaMo
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface GithubComNezhahqNezhaModelCommonResponseModelForceUpdateResponse {
|
||||
data: ModelForceUpdateResponse
|
||||
error: string
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface GithubComNezhahqNezhaModelCommonResponseModelLoginResponse {
|
||||
data: ModelLoginResponse
|
||||
error: string
|
||||
@@ -117,6 +111,12 @@ export interface GithubComNezhahqNezhaModelCommonResponseModelProfile {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface GithubComNezhahqNezhaModelCommonResponseModelServerTaskResponse {
|
||||
data: ModelServerTaskResponse
|
||||
error: string
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface GithubComNezhahqNezhaModelCommonResponseModelServiceResponse {
|
||||
data: ModelServiceResponse
|
||||
error: string
|
||||
@@ -336,12 +336,6 @@ export interface ModelDDNSProfile {
|
||||
webhook_url: string
|
||||
}
|
||||
|
||||
export interface ModelForceUpdateResponse {
|
||||
failure?: number[]
|
||||
offline?: number[]
|
||||
success?: number[]
|
||||
}
|
||||
|
||||
export interface ModelFrontendTemplate {
|
||||
author: string
|
||||
is_admin: boolean
|
||||
@@ -577,6 +571,11 @@ export interface ModelServer {
|
||||
uuid: string
|
||||
}
|
||||
|
||||
export interface ModelServerConfigForm {
|
||||
config: string
|
||||
servers: number[]
|
||||
}
|
||||
|
||||
export interface ModelServerForm {
|
||||
/** DDNS配置 */
|
||||
ddns_profiles?: number[]
|
||||
@@ -615,6 +614,12 @@ export interface ModelServerGroupResponseItem {
|
||||
servers: number[]
|
||||
}
|
||||
|
||||
export interface ModelServerTaskResponse {
|
||||
failure?: number[]
|
||||
offline?: number[]
|
||||
success?: number[]
|
||||
}
|
||||
|
||||
export interface ModelService {
|
||||
cover: number
|
||||
created_at: string
|
||||
|
||||
69
src/types/server.ts
Normal file
69
src/types/server.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { asOptionalField } from "@/lib/utils"
|
||||
import { z } from "zod"
|
||||
|
||||
export const AgentConfigSchema = z.object({
|
||||
debug: asOptionalField(z.boolean()),
|
||||
disable_auto_update: asOptionalField(z.boolean()),
|
||||
disable_command_execute: asOptionalField(z.boolean()),
|
||||
disable_force_update: asOptionalField(z.boolean()),
|
||||
disable_nat: asOptionalField(z.boolean()),
|
||||
disable_send_query: asOptionalField(z.boolean()),
|
||||
gpu: asOptionalField(z.boolean()),
|
||||
hard_drive_partition_allowlist: asOptionalField(z.array(z.string())),
|
||||
hard_drive_partition_allowlist_raw: asOptionalField(
|
||||
z.string().refine(
|
||||
(val) => {
|
||||
try {
|
||||
JSON.parse(val)
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
{
|
||||
message: "Invalid JSON string",
|
||||
},
|
||||
),
|
||||
),
|
||||
ip_report_period: asOptionalField(z.coerce.number().int().min(30)),
|
||||
nic_allowlist: asOptionalField(z.record(z.boolean())),
|
||||
nic_allowlist_raw: asOptionalField(
|
||||
z.string().refine(
|
||||
(val) => {
|
||||
try {
|
||||
JSON.parse(val)
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
{
|
||||
message: "Invalid JSON string",
|
||||
},
|
||||
),
|
||||
),
|
||||
report_delay: asOptionalField(z.coerce.number().int().min(1).max(4)),
|
||||
skip_connection_count: asOptionalField(z.boolean()),
|
||||
skip_procs_count: asOptionalField(z.boolean()),
|
||||
temperature: asOptionalField(z.boolean()),
|
||||
})
|
||||
|
||||
type AgentConfig = z.infer<typeof AgentConfigSchema>
|
||||
|
||||
const boolFields: (keyof AgentConfig)[] = [
|
||||
"disable_auto_update",
|
||||
"disable_command_execute",
|
||||
"disable_force_update",
|
||||
"disable_nat",
|
||||
"disable_send_query",
|
||||
"gpu",
|
||||
"temperature",
|
||||
"skip_connection_count",
|
||||
"skip_procs_count",
|
||||
"debug",
|
||||
]
|
||||
|
||||
export const GroupedBoolFields: (keyof AgentConfig)[][] = []
|
||||
for (let i = 0; i < boolFields.length; i += 2) {
|
||||
GroupedBoolFields.push(boolFields.slice(i, i + 2))
|
||||
}
|
||||
Reference in New Issue
Block a user