blob: 92a30a272f08625c526eff294d7a5465a7358c5c [file] [log] [blame]
giob77cb932025-05-19 09:37:14 +00001import { z } from "zod";
2import { accessSchema, useEnv } from "./lib/state";
3import { Copy, Globe, Terminal, Network, Database, Check } from "lucide-react";
4import { Button } from "./components/ui/button";
5import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/ui/tooltip";
6import { useCallback, useState } from "react";
7
8export function Gateways() {
9 const env = useEnv();
10 const groupedAccess = env.access.reduce((acc, curr) => {
11 if (!acc.has(curr.name)) {
12 acc.set(curr.name, []);
13 }
14 acc.get(curr.name)!.push(curr);
15 return acc;
16 }, new Map<string, typeof env.access>());
17 return (
18 <ul>
19 {Array.from(groupedAccess.entries()).map(([name, access]) => (
gio40370782025-05-19 11:04:52 +000020 <>
giob77cb932025-05-19 09:37:14 +000021 {access.map((a) => (
gio40370782025-05-19 11:04:52 +000022 <li key={name}>
23 <Gateway g={a} />
24 </li>
giob77cb932025-05-19 09:37:14 +000025 ))}
gio40370782025-05-19 11:04:52 +000026 </>
giob77cb932025-05-19 09:37:14 +000027 ))}
28 </ul>
29 );
30}
31
32function Gateway({ g }: { g: z.infer<typeof accessSchema> }) {
33 const [hidden, content] = (() => {
34 switch (g.type) {
35 case "https":
36 return [g.address, g.address];
37 case "ssh":
38 case "tcp":
39 case "udp":
40 return [`${g.host}:${g.port}`, `${g.host}:${g.port}`];
41 case "postgresql":
42 return [
43 `postgresql://${g.username}:*****@${g.host}:${g.port}/${g.database}`,
44 `postgresql://${g.username}:${g.password}@${g.host}:${g.port}/${g.database}`,
45 ];
46 case "mongodb":
47 return [
48 `mongodb://${g.username}:*****@${g.host}:${g.port}/${g.database}`,
49 `mongodb://${g.username}:${g.password}@${g.host}:${g.port}/${g.database}`,
50 ];
51 }
52 })();
53 const [clicked, setClicked] = useState(false);
54 const [open, setOpen] = useState(false);
55 const copy = useCallback(() => {
56 navigator.clipboard.writeText(content);
57 setClicked(true);
58 setOpen(true);
59 setTimeout(() => {
60 setClicked(false);
61 setOpen(false);
62 }, 1000);
63 }, [content, setClicked, setOpen]);
64 return (
65 <TooltipProvider>
66 <Tooltip delayDuration={100} open={open} onOpenChange={setOpen}>
67 <TooltipTrigger asChild>
68 <Button variant="ghost" onClick={copy}>
69 <AccessType type={g.type} className="w-4 h-4" />
70 <div className="hover:bg-gray-200 p-x-1">{hidden}</div>
71 </Button>
72 </TooltipTrigger>
73 <TooltipContent side="right" className="!bg-transparent cursor-pointer !p-0" sideOffset={1}>
74 {!clicked && <Copy className="w-4 h-4 !bg-transparent" color="black" />}
75 {clicked && <Check className="w-4 h-4 !bg-transparent" color="black" />}
76 </TooltipContent>
77 </Tooltip>
78 </TooltipProvider>
79 );
80}
81
82function AccessType({ type, className }: { type: z.infer<typeof accessSchema>["type"]; className?: string }) {
83 switch (type) {
84 case "https":
85 return <Globe className={className} />;
86 case "ssh":
87 return <Terminal className={className} />;
88 case "tcp":
89 case "udp":
90 return <Network className={className} />;
91 case "postgresql":
92 case "mongodb":
93 return <Database className={className} />;
94 }
95}