mirror of
				https://github.com/gethomepage/homepage.git
				synced 2025-11-03 19:17:03 -05:00 
			
		
		
		
	Feature: Support pi-hole v6 (#3207)
This commit is contained in:
		
							parent
							
								
									b0d57866a0
								
							
						
					
					
						commit
						29ac7bfea7
					
				@ -15,6 +15,7 @@ Note: by default the "blocked" and "blocked_percent" fields are merged e.g. "1,2
 | 
				
			|||||||
widget:
 | 
					widget:
 | 
				
			||||||
  type: pihole
 | 
					  type: pihole
 | 
				
			||||||
  url: http://pi.hole.or.ip
 | 
					  url: http://pi.hole.or.ip
 | 
				
			||||||
 | 
					  version: 6 # required if running v6 or higher, defaults to 5
 | 
				
			||||||
  key: yourpiholeapikey # optional
 | 
					  key: yourpiholeapikey # optional
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -393,8 +393,10 @@ export function cleanServiceGroups(groups) {
 | 
				
			|||||||
          enableBlocks,
 | 
					          enableBlocks,
 | 
				
			||||||
          enableNowPlaying,
 | 
					          enableNowPlaying,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // glances
 | 
					          // glances, pihole
 | 
				
			||||||
          version,
 | 
					          version,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // glances
 | 
				
			||||||
          chart,
 | 
					          chart,
 | 
				
			||||||
          metric,
 | 
					          metric,
 | 
				
			||||||
          pointsLimit,
 | 
					          pointsLimit,
 | 
				
			||||||
@ -528,8 +530,10 @@ export function cleanServiceGroups(groups) {
 | 
				
			|||||||
          if (snapshotHost) cleanedService.widget.snapshotHost = snapshotHost;
 | 
					          if (snapshotHost) cleanedService.widget.snapshotHost = snapshotHost;
 | 
				
			||||||
          if (snapshotPath) cleanedService.widget.snapshotPath = snapshotPath;
 | 
					          if (snapshotPath) cleanedService.widget.snapshotPath = snapshotPath;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (type === "glances") {
 | 
					        if (["glances", "pihole"].includes(type)) {
 | 
				
			||||||
          if (version) cleanedService.widget.version = version;
 | 
					          if (version) cleanedService.widget.version = version;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (type === "glances") {
 | 
				
			||||||
          if (metric) cleanedService.widget.metric = metric;
 | 
					          if (metric) cleanedService.widget.metric = metric;
 | 
				
			||||||
          if (chart !== undefined) {
 | 
					          if (chart !== undefined) {
 | 
				
			||||||
            cleanedService.widget.chart = chart;
 | 
					            cleanedService.widget.chart = chart;
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ export default function Component({ service }) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const { widget } = service;
 | 
					  const { widget } = service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { data: piholeData, error: piholeError } = useWidgetAPI(widget, "summaryRaw");
 | 
					  const { data: piholeData, error: piholeError } = useWidgetAPI(widget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (piholeError) {
 | 
					  if (piholeError) {
 | 
				
			||||||
    return <Container service={service} error={piholeError} />;
 | 
					    return <Container service={service} error={piholeError} />;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										95
									
								
								src/widgets/pihole/proxy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								src/widgets/pihole/proxy.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					import cache from "memory-cache";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { httpProxy } from "utils/proxy/http";
 | 
				
			||||||
 | 
					import { formatApiCall } from "utils/proxy/api-helpers";
 | 
				
			||||||
 | 
					import getServiceWidget from "utils/config/service-helpers";
 | 
				
			||||||
 | 
					import createLogger from "utils/logger";
 | 
				
			||||||
 | 
					import widgets from "widgets/widgets";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const proxyName = "piholeProxyHandler";
 | 
				
			||||||
 | 
					const logger = createLogger(proxyName);
 | 
				
			||||||
 | 
					const sessionSIDCacheKey = `${proxyName}__sessionSID`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function login(widget, service) {
 | 
				
			||||||
 | 
					  const url = formatApiCall(widgets[widget.type].api, { ...widget, endpoint: "auth" });
 | 
				
			||||||
 | 
					  const [status, , data] = await httpProxy(url, {
 | 
				
			||||||
 | 
					    method: "POST",
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      "Content-Type": "application/json",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    body: JSON.stringify({
 | 
				
			||||||
 | 
					      password: widget.key,
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const dataParsed = JSON.parse(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (status !== 200 || !dataParsed.session) {
 | 
				
			||||||
 | 
					    logger.error("Failed to login to Pi-Hole API, status: %d", status);
 | 
				
			||||||
 | 
					    cache.del(`${sessionSIDCacheKey}.${service}`);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    cache.put(`${sessionSIDCacheKey}.${service}`, dataParsed.session.sid, dataParsed.session.validity);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default async function piholeProxyHandler(req, res) {
 | 
				
			||||||
 | 
					  const { group, service } = req.query;
 | 
				
			||||||
 | 
					  let endpoint = "stats/summary";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!group || !service) {
 | 
				
			||||||
 | 
					    logger.error("Invalid or missing service '%s' or group '%s'", service, group);
 | 
				
			||||||
 | 
					    return res.status(400).json({ error: "Invalid proxy service type" });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const widget = await getServiceWidget(group, service);
 | 
				
			||||||
 | 
					  if (!widget) {
 | 
				
			||||||
 | 
					    logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group);
 | 
				
			||||||
 | 
					    return res.status(400).json({ error: "Invalid widget configuration" });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let status;
 | 
				
			||||||
 | 
					  let data;
 | 
				
			||||||
 | 
					  if (!widget.version || widget.version < 6) {
 | 
				
			||||||
 | 
					    // pihole v5
 | 
				
			||||||
 | 
					    endpoint = "summaryRaw";
 | 
				
			||||||
 | 
					    [status, , data] = await httpProxy(formatApiCall(widgets[widget.type].apiv5, { ...widget, endpoint }));
 | 
				
			||||||
 | 
					    return res.status(status).send(data);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // pihole v6
 | 
				
			||||||
 | 
					  if (!cache.get(`${sessionSIDCacheKey}.${service}`)) {
 | 
				
			||||||
 | 
					    await login(widget, service);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const sid = cache.get(`${sessionSIDCacheKey}.${service}`);
 | 
				
			||||||
 | 
					  if (!sid) {
 | 
				
			||||||
 | 
					    return res.status(500).json({ error: "Failed to authenticate with Pi-hole" });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    logger.debug("Calling Pi-hole API endpoint: %s", endpoint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [status, , data] = await httpProxy(formatApiCall(widgets[widget.type].api, { ...widget, endpoint }), {
 | 
				
			||||||
 | 
					      headers: {
 | 
				
			||||||
 | 
					        "Content-Type": "application/json",
 | 
				
			||||||
 | 
					        "X-FTL-SID": sid,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (status !== 200) {
 | 
				
			||||||
 | 
					      logger.error("Error calling Pi-Hole API: %d. Data: %s", status, data);
 | 
				
			||||||
 | 
					      return res.status(status).json({ error: "Pi-Hole API Error", data });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const dataParsed = JSON.parse(data);
 | 
				
			||||||
 | 
					    return res.status(status).json({
 | 
				
			||||||
 | 
					      domains_being_blocked: dataParsed.gravity.domains_being_blocked,
 | 
				
			||||||
 | 
					      ads_blocked_today: dataParsed.queries.blocked,
 | 
				
			||||||
 | 
					      ads_percentage_today: dataParsed.queries.percent_blocked,
 | 
				
			||||||
 | 
					      dns_queries_today: dataParsed.queries.total,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    logger.error("Exception calling Pi-Hole API: %s", error.message);
 | 
				
			||||||
 | 
					    return res.status(500).json({ error: "Pi-Hole API Error", message: error.message });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,15 +1,9 @@
 | 
				
			|||||||
import genericProxyHandler from "utils/proxy/handlers/generic";
 | 
					import piholeProxyHandler from "./proxy";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const widget = {
 | 
					const widget = {
 | 
				
			||||||
  api: "{url}/admin/api.php?{endpoint}&auth={key}",
 | 
					  api: "{url}/api/{endpoint}",
 | 
				
			||||||
  proxyHandler: genericProxyHandler,
 | 
					  apiv5: "{url}/admin/api.php?{endpoint}&auth={key}",
 | 
				
			||||||
 | 
					  proxyHandler: piholeProxyHandler,
 | 
				
			||||||
  mappings: {
 | 
					 | 
				
			||||||
    summaryRaw: {
 | 
					 | 
				
			||||||
      endpoint: "summaryRaw",
 | 
					 | 
				
			||||||
      validate: ["dns_queries_today", "ads_blocked_today", "ads_percentage_today", "domains_being_blocked"],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default widget;
 | 
					export default widget;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user