mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-11-03 16:01:00 +00:00 
			
		
		
		
	Feature: Wg-Easy Widget (#3476)
--------- Co-authored-by: ConnerWithAnE <46903591+ConnerWithAnE@users.noreply.github.com> Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									1144f4dfa0
								
							
						
					
					
						commit
						6ab6d6fd3a
					
				
							
								
								
									
										20
									
								
								docs/widgets/services/wgeasy.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								docs/widgets/services/wgeasy.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					title: Wg-Easy
 | 
				
			||||||
 | 
					description: Wg-Easy Widget Configuration
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Learn more about [Wg-Easy](https://github.com/wg-easy/wg-easy).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Allowed fields: `["connected", "enabled", "disabled", "total"]`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note: by default `["connected", "enabled", "total"]` are displayed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To detect if a device is connected the time since the last handshake is queried. `threshold` is the time to wait in minutes since the last handshake to consider a device connected. Default is 2 minutes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					widget:
 | 
				
			||||||
 | 
					  type: wgeasy
 | 
				
			||||||
 | 
					  url: http://wg.easy.or.ip
 | 
				
			||||||
 | 
					  password: yourwgeasypassword
 | 
				
			||||||
 | 
					  threshold: 2 # optional
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
@ -876,5 +876,11 @@
 | 
				
			|||||||
    "crowdsec": {
 | 
					    "crowdsec": {
 | 
				
			||||||
        "alerts": "Alerts",
 | 
					        "alerts": "Alerts",
 | 
				
			||||||
        "bans": "Bans"
 | 
					        "bans": "Bans"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "wgeasy": {
 | 
				
			||||||
 | 
					        "connected": "Connected",
 | 
				
			||||||
 | 
					        "enabled": "Enabled",
 | 
				
			||||||
 | 
					        "disabled": "Disabled",
 | 
				
			||||||
 | 
					        "total": "Total"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -462,6 +462,9 @@ export function cleanServiceGroups(groups) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          // unifi
 | 
					          // unifi
 | 
				
			||||||
          site,
 | 
					          site,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // wgeasy
 | 
				
			||||||
 | 
					          threshold,
 | 
				
			||||||
        } = cleanedService.widget;
 | 
					        } = cleanedService.widget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let fieldsList = fields;
 | 
					        let fieldsList = fields;
 | 
				
			||||||
@ -596,6 +599,9 @@ export function cleanServiceGroups(groups) {
 | 
				
			|||||||
            cleanedService.widget.bitratePrecision = parseInt(bitratePrecision, 10);
 | 
					            cleanedService.widget.bitratePrecision = parseInt(bitratePrecision, 10);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (type === "wgeasy") {
 | 
				
			||||||
 | 
					          if (threshold !== undefined) cleanedService.widget.threshold = parseInt(threshold, 10);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return cleanedService;
 | 
					      return cleanedService;
 | 
				
			||||||
 | 
				
			|||||||
@ -117,6 +117,7 @@ const components = {
 | 
				
			|||||||
  uptimerobot: dynamic(() => import("./uptimerobot/component")),
 | 
					  uptimerobot: dynamic(() => import("./uptimerobot/component")),
 | 
				
			||||||
  urbackup: dynamic(() => import("./urbackup/component")),
 | 
					  urbackup: dynamic(() => import("./urbackup/component")),
 | 
				
			||||||
  watchtower: dynamic(() => import("./watchtower/component")),
 | 
					  watchtower: dynamic(() => import("./watchtower/component")),
 | 
				
			||||||
 | 
					  wgeasy: dynamic(() => import("./wgeasy/component")),
 | 
				
			||||||
  whatsupdocker: dynamic(() => import("./whatsupdocker/component")),
 | 
					  whatsupdocker: dynamic(() => import("./whatsupdocker/component")),
 | 
				
			||||||
  xteve: dynamic(() => import("./xteve/component")),
 | 
					  xteve: dynamic(() => import("./xteve/component")),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										45
									
								
								src/widgets/wgeasy/component.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/widgets/wgeasy/component.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					import Container from "components/services/widget/container";
 | 
				
			||||||
 | 
					import Block from "components/services/widget/block";
 | 
				
			||||||
 | 
					import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Component({ service }) {
 | 
				
			||||||
 | 
					  const { widget } = service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { data: infoData, error: infoError } = useWidgetAPI(widget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!widget.fields) {
 | 
				
			||||||
 | 
					    widget.fields = ["connected", "enabled", "total"];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (infoError) {
 | 
				
			||||||
 | 
					    return <Container service={service} error={infoError} />;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!infoData) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Container service={service}>
 | 
				
			||||||
 | 
					        <Block label="wgeasy.connected" />
 | 
				
			||||||
 | 
					        <Block label="wgeasy.enabled" />
 | 
				
			||||||
 | 
					        <Block label="wgeasy.disabled" />
 | 
				
			||||||
 | 
					        <Block label="wgeasy.total" />
 | 
				
			||||||
 | 
					      </Container>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const enabled = infoData.filter((item) => item.enabled).length;
 | 
				
			||||||
 | 
					  const disabled = infoData.length - enabled;
 | 
				
			||||||
 | 
					  const connectionThreshold = widget.threshold ?? 2 * 60 * 1000;
 | 
				
			||||||
 | 
					  const currentTime = new Date();
 | 
				
			||||||
 | 
					  const connected = infoData.filter(
 | 
				
			||||||
 | 
					    (item) => currentTime - new Date(item.latestHandshakeAt) < connectionThreshold,
 | 
				
			||||||
 | 
					  ).length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Container service={service}>
 | 
				
			||||||
 | 
					      <Block label="wgeasy.connected" value={connected} />
 | 
				
			||||||
 | 
					      <Block label="wgeasy.enabled" value={enabled} />
 | 
				
			||||||
 | 
					      <Block label="wgeasy.diabled" value={disabled} />
 | 
				
			||||||
 | 
					      <Block label="wgeasy.total" value={infoData.length} />
 | 
				
			||||||
 | 
					    </Container>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										70
									
								
								src/widgets/wgeasy/proxy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/widgets/wgeasy/proxy.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					import cache from "memory-cache";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import getServiceWidget from "utils/config/service-helpers";
 | 
				
			||||||
 | 
					import { formatApiCall } from "utils/proxy/api-helpers";
 | 
				
			||||||
 | 
					import { httpProxy } from "utils/proxy/http";
 | 
				
			||||||
 | 
					import widgets from "widgets/widgets";
 | 
				
			||||||
 | 
					import createLogger from "utils/logger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const proxyName = "wgeasyProxyHandler";
 | 
				
			||||||
 | 
					const logger = createLogger(proxyName);
 | 
				
			||||||
 | 
					const sessionSIDCacheKey = `${proxyName}__sessionSID`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function login(widget, service) {
 | 
				
			||||||
 | 
					  const url = formatApiCall(widgets[widget.type].api, { ...widget, endpoint: "session" });
 | 
				
			||||||
 | 
					  const [, , , responseHeaders] = await httpProxy(url, {
 | 
				
			||||||
 | 
					    method: "POST",
 | 
				
			||||||
 | 
					    body: JSON.stringify({ password: widget.password }),
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      "Content-Type": "application/json",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const connectSidCookie = responseHeaders["set-cookie"]
 | 
				
			||||||
 | 
					      .find((cookie) => cookie.startsWith("connect.sid="))
 | 
				
			||||||
 | 
					      .split(";")[0]
 | 
				
			||||||
 | 
					      .replace("connect.sid=", "");
 | 
				
			||||||
 | 
					    cache.put(`${sessionSIDCacheKey}.${service}`, connectSidCookie);
 | 
				
			||||||
 | 
					    return connectSidCookie;
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    logger.error(`Error logging into wg-easy`);
 | 
				
			||||||
 | 
					    cache.del(`${sessionSIDCacheKey}.${service}`);
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default async function wgeasyProxyHandler(req, res) {
 | 
				
			||||||
 | 
					  const { group, service } = req.query;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (group && service) {
 | 
				
			||||||
 | 
					    const widget = await getServiceWidget(group, service);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!widgets?.[widget.type]?.api) {
 | 
				
			||||||
 | 
					      return res.status(403).json({ error: "Service does not support API calls" });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (widget) {
 | 
				
			||||||
 | 
					      let sid = cache.get(`${sessionSIDCacheKey}.${service}`);
 | 
				
			||||||
 | 
					      if (!sid) {
 | 
				
			||||||
 | 
					        sid = await login(widget, service);
 | 
				
			||||||
 | 
					        if (!sid) {
 | 
				
			||||||
 | 
					          return res.status(500).json({ error: "Failed to authenticate with Wg-Easy" });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      const [, , data] = await httpProxy(
 | 
				
			||||||
 | 
					        formatApiCall(widgets[widget.type].api, { ...widget, endpoint: "wireguard/client" }),
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          headers: {
 | 
				
			||||||
 | 
					            "Content-Type": "application/json",
 | 
				
			||||||
 | 
					            Cookie: `connect.sid=${sid}`,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return res.json(JSON.parse(data));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return res.status(400).json({ error: "Invalid proxy service type" });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/widgets/wgeasy/widget.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/widgets/wgeasy/widget.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					import wgeasyProxyHandler from "./proxy";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const widget = {
 | 
				
			||||||
 | 
					  api: "{url}/api/{endpoint}",
 | 
				
			||||||
 | 
					  proxyHandler: wgeasyProxyHandler,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default widget;
 | 
				
			||||||
@ -107,6 +107,7 @@ import unmanic from "./unmanic/widget";
 | 
				
			|||||||
import uptimekuma from "./uptimekuma/widget";
 | 
					import uptimekuma from "./uptimekuma/widget";
 | 
				
			||||||
import uptimerobot from "./uptimerobot/widget";
 | 
					import uptimerobot from "./uptimerobot/widget";
 | 
				
			||||||
import watchtower from "./watchtower/widget";
 | 
					import watchtower from "./watchtower/widget";
 | 
				
			||||||
 | 
					import wgeasy from "./wgeasy/widget";
 | 
				
			||||||
import whatsupdocker from "./whatsupdocker/widget";
 | 
					import whatsupdocker from "./whatsupdocker/widget";
 | 
				
			||||||
import xteve from "./xteve/widget";
 | 
					import xteve from "./xteve/widget";
 | 
				
			||||||
import urbackup from "./urbackup/widget";
 | 
					import urbackup from "./urbackup/widget";
 | 
				
			||||||
@ -227,6 +228,7 @@ const widgets = {
 | 
				
			|||||||
  uptimerobot,
 | 
					  uptimerobot,
 | 
				
			||||||
  urbackup,
 | 
					  urbackup,
 | 
				
			||||||
  watchtower,
 | 
					  watchtower,
 | 
				
			||||||
 | 
					  wgeasy,
 | 
				
			||||||
  whatsupdocker,
 | 
					  whatsupdocker,
 | 
				
			||||||
  xteve,
 | 
					  xteve,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user