https://dev.kronoz.win/r/marzneshin.git
1
2import { FC, useEffect, useState } from "react";
3import {
4 DialogTitle,
5 DialogContent,
6 Dialog,
7 DialogHeader,
8 Form,
9 FormItem,
10 FormControl,
11 FormMessage,
12 FormLabel,
13 Input,
14 FormField,
15 Checkbox,
16 Button,
17 SelectItem,
18 SelectContent,
19 SelectValue,
20 Select,
21 SelectTrigger,
22 Alert,
23 AlertTitle,
24 AlertDescription
25} from '@marzneshin/components';
26import { useForm } from 'react-hook-form';
27import { zodResolver } from '@hookform/resolvers/zod';
28import { useTranslation } from 'react-i18next';
29import {
30 HostSchema,
31 HostType,
32 getDefaultValues,
33 useHostsCreationMutation,
34 useHostsUpdateMutation
35} from "@marzneshin/features/hosts";
36import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
37
38interface MutationDialogProps {
39 entity: HostType | null
40 open: boolean
41 inboundId: number
42 onOpenChange: (state: boolean) => void
43}
44
45
46export const HostsMutationDialog: FC<MutationDialogProps> = ({
47 inboundId,
48 entity,
49 open,
50 onOpenChange,
51}) => {
52 const form = useForm({
53 defaultValues: entity ? entity : getDefaultValues(),
54 resolver: zodResolver(HostSchema)
55 });
56 const updateMutation = useHostsUpdateMutation();
57 const createMutation = useHostsCreationMutation();
58 const { t } = useTranslation();
59 const security = form.watch().security
60 const [extraSecurity, setExtraSecurity] = useState<boolean>(["tls", "inbound_default"].includes(security))
61
62 useEffect(() => {
63 setExtraSecurity(["tls", "inbound_default"].includes(security))
64 }, [security, setExtraSecurity])
65
66 const submit = (values: HostType) => {
67 if (entity) {
68 updateMutation.mutate({ inboundId, host: values });
69 } else {
70 createMutation.mutate({ inboundId, host: values });
71 }
72 onOpenChange(false);
73 };
74
75 useEffect(() => {
76 if (entity) form.reset(entity);
77 else form.reset(getDefaultValues());
78 }, [entity, form]);
79
80 return (
81 <Dialog open={open} onOpenChange={onOpenChange} defaultOpen={true}>
82 <DialogContent>
83 <DialogHeader>
84 <DialogTitle className="text-primary">
85 {entity
86 ? t('page.hosts.dialogs.edition.title')
87 : t('page.hosts.dialogs.creation.title')}
88 </DialogTitle>
89 </DialogHeader>
90 <Form {...form}>
91 <form onSubmit={form.handleSubmit(submit)}>
92 <FormField
93 control={form.control}
94 name="remark"
95 render={({ field }) => (
96 <FormItem >
97 <FormLabel>{t('name')}</FormLabel>
98 <FormControl>
99 <Input {...field} />
100 </FormControl>
101 <FormMessage />
102 </FormItem>
103 )}
104 />
105 <div className="flex flex-row gap-1 items-center">
106 <FormField
107 control={form.control}
108 name="address"
109 render={({ field }) => (
110 <FormItem className="w-2/3">
111 <FormLabel>{t('address')}</FormLabel>
112 <FormControl>
113 <Input {...field} />
114 </FormControl>
115 <FormMessage />
116 </FormItem>
117 )}
118 />
119 <FormField
120 control={form.control}
121 name="port"
122 render={({ field }) => (
123 <FormItem className="w-1/3">
124 <FormLabel>{t('port')}</FormLabel>
125 <FormControl>
126 <Input type="number" {...field} />
127 </FormControl>
128 <FormMessage />
129 </FormItem>
130 )}
131 />
132 </div>
133 <FormField
134 control={form.control}
135 name="path"
136 render={({ field }) => (
137 <FormItem >
138 <FormLabel>{t('path')}</FormLabel>
139 <FormControl>
140 <Input {...field} />
141 </FormControl>
142 <FormMessage />
143 </FormItem>
144 )}
145 />
146
147 <FormField
148 control={form.control}
149 name="host"
150 render={({ field }) => (
151 <FormItem >
152 <FormLabel>{t('host')}</FormLabel>
153 <FormControl>
154 <Input {...field} />
155 </FormControl>
156 <FormMessage />
157 </FormItem>
158 )}
159 />
160
161 <FormField
162 control={form.control}
163 name="security"
164 render={({ field }) => (
165 <FormItem>
166 <FormLabel>{t('security')}</FormLabel>
167 <Select onValueChange={field.onChange} defaultValue={field.value}>
168 <FormControl>
169 <SelectTrigger>
170 <SelectValue placeholder="Select a verified email to display" />
171 </SelectTrigger>
172 </FormControl>
173 <SelectContent>
174 <SelectItem value="none">None</SelectItem>
175 <SelectItem value="tls">TLS</SelectItem>
176 <SelectItem value="inbound_default">Inbound Default</SelectItem>
177 </SelectContent>
178 </Select>
179 <FormMessage />
180 </FormItem>
181 )}
182 />
183 {extraSecurity &&
184 <>
185 <FormField
186 control={form.control}
187 name="sni"
188 render={({ field }) => (
189 <FormItem >
190 <FormLabel>{t('SNI')}</FormLabel>
191 <FormControl>
192 <Input {...field} />
193 </FormControl>
194 <FormMessage />
195 </FormItem>
196 )}
197 />
198
199 <FormField
200 control={form.control}
201 name="alpn"
202 render={({ field }) => (
203 <FormItem>
204 <FormLabel>{t('alpn')}</FormLabel>
205 <Select onValueChange={field.onChange} defaultValue={field.value}>
206 <FormControl>
207 <SelectTrigger>
208 <SelectValue placeholder="Select ALPN" />
209 </SelectTrigger>
210 </FormControl>
211 <SelectContent>
212 <SelectItem value="none">None</SelectItem>
213 <SelectItem value="h2">h2</SelectItem>
214 <SelectItem value="http/1.1">HTTP 1.1</SelectItem>
215 <SelectItem value="h2,http/1.1"> H2 HTTP 1.1</SelectItem>
216 </SelectContent>
217 </Select>
218 <FormMessage />
219 </FormItem>
220 )}
221 />
222
223 <FormField
224 control={form.control}
225 name="fingerprint"
226 render={({ field }) => (
227 <FormItem>
228 <FormLabel>{t('fingerprint')}</FormLabel>
229 <Select onValueChange={field.onChange} defaultValue={field.value}>
230 <FormControl>
231 <SelectTrigger>
232 <SelectValue placeholder="Select fingerprint" />
233 </SelectTrigger>
234 </FormControl>
235 <SelectContent>
236 <SelectItem value="none">None</SelectItem>
237 <SelectItem value="chrome">Chrome</SelectItem>
238 <SelectItem value="firefox">Firefox</SelectItem>
239 <SelectItem value="safari">Safari</SelectItem>
240 <SelectItem value="ios">iOS</SelectItem>
241 <SelectItem value="android">Android</SelectItem>
242 <SelectItem value="edge">Edge</SelectItem>
243 <SelectItem value="360">360</SelectItem>
244 <SelectItem value="qq">qq</SelectItem>
245 <SelectItem value="random">Random</SelectItem>
246 <SelectItem value="randomized">Randomized</SelectItem>
247 </SelectContent>
248 </Select>
249 <FormMessage />
250 </FormItem>
251 )}
252 />
253
254 <FormField
255 control={form.control}
256 name="allowinsecure"
257 render={({ field }) => (
258 <FormItem className="my-2">
259 <FormControl>
260 <div className="flex flex-row gap-1 items-center">
261 <Checkbox
262 checked={field.value}
263 onCheckedChange={field.onChange}
264 />
265 <FormLabel>
266 {t('page.hosts.allowinsecure')}
267 </FormLabel>
268 </div>
269 </FormControl>
270 <FormMessage />
271 {form.getValues().allowinsecure &&
272 <Alert variant="destructive">
273 <ExclamationTriangleIcon className="w-4 h-4" />
274 <AlertTitle>
275 Warning
276 </AlertTitle>
277 <AlertDescription>
278 {t('page.hosts.dialogs.mutation.allowinsecure.alert')}
279 </AlertDescription>
280 </Alert>}
281 </FormItem>
282 )}
283 />
284 </>
285 }
286 <Button
287 className="mt-3 w-full font-semibold"
288 type="submit"
289 disabled={form.formState.isSubmitting}
290 >
291 {t('submit')}
292 </Button>
293 </form>
294 </Form>
295 </DialogContent>
296 </Dialog>
297 );
298};