Skip to content

Commit 5f854c3

Browse files
committed
feat: 批量转移服务器给其他用户
1 parent bf7c42e commit 5f854c3

File tree

6 files changed

+141
-19
lines changed

6 files changed

+141
-19
lines changed

src/api/server.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
ModelBatchMoveServerForm,
23
ModelServer,
34
ModelServerConfigForm,
45
ModelServerForm,
@@ -15,6 +16,10 @@ export const deleteServer = async (id: number[]): Promise<void> => {
1516
return fetcher<void>(FetcherMethod.POST, "/api/v1/batch-delete/server", id)
1617
}
1718

19+
export const batchMoveServer = async (data: ModelBatchMoveServerForm): Promise<void> => {
20+
return fetcher<void>(FetcherMethod.POST, "/api/v1/batch-move/server", data)
21+
}
22+
1823
export const forceUpdateServer = async (id: number[]): Promise<ModelServerTaskResponse> => {
1924
return fetcher<ModelServerTaskResponse>(FetcherMethod.POST, "/api/v1/force-update/server", id)
2025
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { batchMoveServer } from "@/api/server"
2+
import { Button, ButtonProps } from "@/components/ui/button"
3+
import {
4+
Dialog,
5+
DialogClose,
6+
DialogContent,
7+
DialogDescription,
8+
DialogFooter,
9+
DialogHeader,
10+
DialogTitle,
11+
DialogTrigger,
12+
} from "@/components/ui/dialog"
13+
import { Input } from "@/components/ui/input"
14+
import { Label } from "@/components/ui/label"
15+
import { ScrollArea } from "@/components/ui/scroll-area"
16+
import { IconButton } from "@/components/xui/icon-button"
17+
import { useState } from "react"
18+
import { useTranslation } from "react-i18next"
19+
import { toast } from "sonner"
20+
import { Textarea } from "./ui/textarea"
21+
22+
interface BatchMoveServerIconProps extends ButtonProps {
23+
serverIds: number[]
24+
}
25+
26+
export const BatchMoveServerIcon: React.FC<BatchMoveServerIconProps> = ({ serverIds, ...props }) => {
27+
const { t } = useTranslation()
28+
const [open, setOpen] = useState(false)
29+
const [toUserId, setToUserId] = useState<number | undefined>(undefined)
30+
31+
const onSubmit = async () => {
32+
try {
33+
await batchMoveServer({
34+
ids: serverIds,
35+
to_user: toUserId!
36+
})
37+
} catch (e) {
38+
console.error(e)
39+
toast(t("Error"), {
40+
description: t("Results.UnExpectedError"),
41+
})
42+
return
43+
}
44+
toast(t("Done"))
45+
setOpen(false)
46+
}
47+
48+
return serverIds.length < 1 ? (
49+
<IconButton
50+
{...props}
51+
icon="user-pen"
52+
onClick={() => {
53+
toast(t("Error"), {
54+
description: t("Results.NoRowsAreSelected"),
55+
})
56+
}}
57+
/>
58+
) : (
59+
<Dialog open={open} onOpenChange={setOpen}>
60+
<DialogTrigger asChild>
61+
<IconButton {...props} icon="user-pen" />
62+
</DialogTrigger>
63+
<DialogContent className="sm:max-w-xl">
64+
<ScrollArea className="max-h-[calc(100dvh-5rem)] p-3">
65+
<div className="items-center mx-1">
66+
<DialogHeader>
67+
<DialogTitle>{t("BatchMoveServer")}</DialogTitle>
68+
<DialogDescription />
69+
</DialogHeader>
70+
<div className="flex flex-col gap-3 mt-4">
71+
<Label>{t("Servers")}</Label>
72+
<Textarea disabled>
73+
{serverIds.join(", ")}
74+
</Textarea>
75+
<Label>{t("ToUser")}</Label>
76+
<Input
77+
type="number"
78+
placeholder="User ID"
79+
value={toUserId}
80+
onChange={(e) => {
81+
setToUserId(parseInt(e.target.value, 10))
82+
}}
83+
/>
84+
<DialogFooter className="justify-end">
85+
<DialogClose asChild>
86+
<Button type="button" className="my-2" variant="secondary">
87+
{t("Cancel")}
88+
</Button>
89+
</DialogClose>
90+
<Button disabled={!toUserId || toUserId == 0} type="submit" className="my-2" onClick={onSubmit}>
91+
{t("Move")}
92+
</Button>
93+
</DialogFooter>
94+
</div>
95+
</div>
96+
</ScrollArea>
97+
</DialogContent>
98+
</Dialog>
99+
)
100+
}

src/components/xui/icon-button.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
Terminal,
1717
Trash2,
1818
Upload,
19+
UserPen,
1920
} from "lucide-react"
2021
import { forwardRef } from "react"
2122

@@ -37,6 +38,7 @@ export interface IconButtonProps extends ButtonProps {
3738
| "expand"
3839
| "cog"
3940
| "minus"
41+
| "user-pen"
4042
}
4143

4244
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props, ref) => {
@@ -97,6 +99,9 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props,
9799
case "minus": {
98100
return <Minus />
99101
}
102+
case "user-pen": {
103+
return <UserPen />
104+
}
100105
}
101106
})()}
102107
</Button>

src/routes/server.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { swrFetcher } from "@/api/api"
22
import { deleteServer, forceUpdateServer } from "@/api/server"
33
import { ActionButtonGroup } from "@/components/action-button-group"
4+
import { BatchMoveServerIcon } from "@/components/batch-move-server-icon"
45
import { CopyButton } from "@/components/copy-button"
56
import { HeaderButtonGroup } from "@/components/header-button-group"
67
import { InstallCommandsMenu } from "@/components/install-commands"
@@ -213,6 +214,7 @@ export default function ServerPage() {
213214
})
214215
}}
215216
/>
217+
<BatchMoveServerIcon serverIds={selectedRows.map((r) => r.original.id)} />
216218
<ServerConfigCardBatch
217219
sid={selectedRows.map((r) => r.original.id)}
218220
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"

src/routes/user.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ export default function UserPage() {
7474
return row.role === 1 ? t("User") : t("Admin")
7575
},
7676
},
77+
{
78+
header: t("LastLogin"),
79+
accessorKey: "updated_at",
80+
accessorFn: (row) => row.updated_at ? new Date(row.updated_at).toLocaleString() : t("Never"),
81+
},
7782
{
7883
id: "actions",
7984
header: t("Actions"),

src/types/api.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
/* eslint-disable */
2-
/* tslint:disable */
3-
/*
4-
* ---------------------------------------------------------------
5-
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
6-
* ## ##
7-
* ## AUTHOR: acacode ##
8-
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9-
* ---------------------------------------------------------------
10-
*/
11-
1+
/* eslint-disable */
2+
/* tslint:disable */
3+
/*
4+
* ---------------------------------------------------------------
5+
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
6+
* ## ##
7+
* ## AUTHOR: acacode ##
8+
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9+
* ---------------------------------------------------------------
10+
*/
11+
1212
export interface GithubComNezhahqNezhaModelCommonResponseAny {
1313
data: any
1414
error: string
@@ -188,6 +188,11 @@ export interface ModelAlertRuleForm {
188188
trigger_mode: number
189189
}
190190

191+
export interface ModelBatchMoveServerForm {
192+
ids: number[]
193+
to_user: number
194+
}
195+
191196
export interface ModelCreateFMResponse {
192197
session_id: string
193198
}
@@ -631,6 +636,8 @@ export interface ModelServiceResponseItem {
631636

632637
export interface ModelSetting {
633638
admin_template: string
639+
/** Agent真实IP */
640+
agent_real_ip_header: string
634641
/** 覆盖范围(0:提醒未被 IgnoredIPNotification 包含的所有服务器; 1:仅提醒被 IgnoredIPNotification 包含的服务器;) */
635642
cover: number
636643
custom_code: string
@@ -648,17 +655,17 @@ export interface ModelSetting {
648655
/** 系统语言,默认 zh_CN */
649656
language: string
650657
oauth2_providers: string[]
651-
/** 前端真实IP */
652-
web_real_ip_header: string
653-
/** Agent真实IP */
654-
agent_real_ip_header: string
655658
site_name: string
656659
/** 用于前端判断生成的安装命令是否启用 TLS */
657660
tls: boolean
658661
user_template: string
662+
/** 前端真实IP */
663+
web_real_ip_header: string
659664
}
660665

661666
export interface ModelSettingForm {
667+
/** Agent真实IP */
668+
agent_real_ip_header?: string
662669
cover: number
663670
custom_code?: string
664671
custom_code_dashboard?: string
@@ -671,14 +678,12 @@ export interface ModelSettingForm {
671678
ip_change_notification_group_id: number
672679
/** @minLength 2 */
673680
language: string
674-
/** 前端真实IP */
675-
web_real_ip_header?: string
676-
/** Agent真实IP */
677-
agent_real_ip_header?: string
678681
/** @minLength 1 */
679682
site_name: string
680683
tls?: boolean
681684
user_template?: string
685+
/** 前端真实IP */
686+
web_real_ip_header?: string
682687
}
683688

684689
export interface ModelSettingResponse {

0 commit comments

Comments
 (0)