mirror of
https://github.com/gethomepage/homepage.git
synced 2025-12-06 21:57:48 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af1695687a | ||
|
|
f0d7cf3ce6 | ||
|
|
36e77e1fe3 | ||
|
|
35dd7ec230 | ||
|
|
15cc1d98c5 | ||
|
|
9803ef70c6 | ||
|
|
ccc27142ef | ||
|
|
9a20982406 | ||
|
|
67a69a5878 |
@@ -98,6 +98,8 @@ When the Kubernetes cluster connection has been properly configured, this servic
|
||||
|
||||
If you are using multiple instances of homepage, an `instance` annotation can be specified to limit services to a specific instance. If no instance is provided, the service will be visible on all instances.
|
||||
|
||||
If you have a single service that needs to be shown on multiple specific instances of homepage (but not on all of them), the service can be annotated by multiple `instance.name` annotations, where `name` can be the names of your specific multiple homepage instances. For example, a service that is annotated with `gethomepage.dev/instance.public: ""` and `gethomepage.dev/instance.internal: ""` will be shown on `public` and `internal` homepage instances.
|
||||
|
||||
### Traefik IngressRoute support
|
||||
|
||||
Homepage can also read ingresses defined using the Traefik IngressRoute custom resource definition. Due to the complex nature of Traefik routing rules, it is required for the `gethomepage.dev/href` annotation to be set:
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
"connectionStatusPendingDisconnect": "Desconexión pendiente",
|
||||
"connectionStatusDisconnecting": "Desconectando",
|
||||
"connectionStatusDisconnected": "Desconectado",
|
||||
"connectionStatusConnected": "Connected",
|
||||
"connectionStatusConnected": "Conectado",
|
||||
"uptime": "Tiempo activo",
|
||||
"maxDown": "Descarga máxima",
|
||||
"maxUp": "Subida máxima",
|
||||
@@ -279,9 +279,9 @@
|
||||
},
|
||||
"netalertx": {
|
||||
"total": "Total",
|
||||
"connected": "Connected",
|
||||
"new_devices": "New Devices",
|
||||
"down_alerts": "Down Alerts"
|
||||
"connected": "Conectado",
|
||||
"new_devices": "Nuevos dispositivos",
|
||||
"down_alerts": "Alertas de caída"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Consultas",
|
||||
@@ -544,7 +544,7 @@
|
||||
"hdhomerun": {
|
||||
"channels": "Canales",
|
||||
"hd": "Alta definición",
|
||||
"tunerCount": "Tuners",
|
||||
"tunerCount": "Sintonizadores",
|
||||
"channelNumber": "Canal",
|
||||
"channelNetwork": "Red",
|
||||
"signalStrength": "Intensidad",
|
||||
@@ -827,7 +827,7 @@
|
||||
},
|
||||
"romm": {
|
||||
"platforms": "Plataformas",
|
||||
"totalRoms": "Total ROMs"
|
||||
"totalRoms": "ROMs totales"
|
||||
},
|
||||
"netdata": {
|
||||
"warnings": "Advertencias",
|
||||
@@ -835,38 +835,38 @@
|
||||
},
|
||||
"plantit": {
|
||||
"events": "Eventos",
|
||||
"plants": "Plants",
|
||||
"plants": "Plantas",
|
||||
"photos": "Fotos",
|
||||
"species": "Species"
|
||||
"species": "Especies"
|
||||
},
|
||||
"gitea": {
|
||||
"notifications": "Notificaciones",
|
||||
"issues": "Números",
|
||||
"pulls": "Pull Requests"
|
||||
"pulls": "Solicitudes de cambios"
|
||||
},
|
||||
"stash": {
|
||||
"scenes": "Scenes",
|
||||
"scenesPlayed": "Scenes Played",
|
||||
"playCount": "Total Plays",
|
||||
"playDuration": "Time Watched",
|
||||
"sceneSize": "Scenes Size",
|
||||
"sceneDuration": "Scenes Duration",
|
||||
"scenes": "Escenas",
|
||||
"scenesPlayed": "Escenas reproducidas",
|
||||
"playCount": "Reproducciones totales",
|
||||
"playDuration": "Tiempo visto",
|
||||
"sceneSize": "Tamaño de las escenas",
|
||||
"sceneDuration": "Duración de las escenas",
|
||||
"images": "Imágenes",
|
||||
"imageSize": "Tamaño de imagen",
|
||||
"galleries": "Galerías",
|
||||
"performers": "Performers",
|
||||
"studios": "Studios",
|
||||
"performers": "Intérpretes",
|
||||
"studios": "Estudios",
|
||||
"movies": "Películas",
|
||||
"tags": "Etiquetas",
|
||||
"oCount": "O Count"
|
||||
"oCount": "O cuenta"
|
||||
},
|
||||
"tandoor": {
|
||||
"users": "Usuarios",
|
||||
"recipes": "Recetas",
|
||||
"keywords": "Keywords"
|
||||
"keywords": "Palabras clave"
|
||||
},
|
||||
"homebox": {
|
||||
"items": "Items",
|
||||
"items": "Objetos",
|
||||
"totalWithWarranty": "Con Garantía",
|
||||
"locations": "Ubicaciones",
|
||||
"labels": "Etiquetas",
|
||||
@@ -875,10 +875,10 @@
|
||||
},
|
||||
"crowdsec": {
|
||||
"alerts": "Alertas",
|
||||
"bans": "Bans"
|
||||
"bans": "Baneos"
|
||||
},
|
||||
"wgeasy": {
|
||||
"connected": "Connected",
|
||||
"connected": "Conectado",
|
||||
"enabled": "Activado",
|
||||
"disabled": "Desactivado",
|
||||
"total": "Total"
|
||||
|
||||
@@ -884,9 +884,9 @@
|
||||
"total": "Total"
|
||||
},
|
||||
"swagdashboard": {
|
||||
"proxied": "Proxied",
|
||||
"auth": "With Auth",
|
||||
"outdated": "Outdated",
|
||||
"banned": "Banned"
|
||||
"proxied": "Par proxy",
|
||||
"auth": "Avec authentification",
|
||||
"outdated": "Obsolète",
|
||||
"banned": "Banni"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import cachedFetch from "utils/proxy/cached-fetch";
|
||||
import { getSettings } from "utils/config/config";
|
||||
import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { latitude, longitude, units, provider, cache, lang } = req.query;
|
||||
let { apiKey } = req.query;
|
||||
const { latitude, longitude, units, provider, cache, lang, index } = req.query;
|
||||
const privateWidgetOptions = await getPrivateWidgetOptions("openweathermap", index);
|
||||
let { apiKey } = privateWidgetOptions;
|
||||
|
||||
if (!apiKey && !provider) {
|
||||
return res.status(400).json({ error: "Missing API key or provider" });
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import cachedFetch from "utils/proxy/cached-fetch";
|
||||
import { getSettings } from "utils/config/config";
|
||||
import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { latitude, longitude, provider, cache, lang } = req.query;
|
||||
let { apiKey } = req.query;
|
||||
const { latitude, longitude, provider, cache, lang, index } = req.query;
|
||||
const privateWidgetOptions = await getPrivateWidgetOptions("weatherapi", index);
|
||||
let { apiKey } = privateWidgetOptions;
|
||||
|
||||
if (!apiKey && !provider) {
|
||||
return res.status(400).json({ error: "Missing API key or provider" });
|
||||
|
||||
@@ -254,7 +254,8 @@ export async function servicesFromKubernetes() {
|
||||
ingress.metadata.annotations &&
|
||||
ingress.metadata.annotations[`${ANNOTATION_BASE}/enabled`] === "true" &&
|
||||
(!ingress.metadata.annotations[`${ANNOTATION_BASE}/instance`] ||
|
||||
ingress.metadata.annotations[`${ANNOTATION_BASE}/instance`] === instanceName),
|
||||
ingress.metadata.annotations[`${ANNOTATION_BASE}/instance`] === instanceName ||
|
||||
`${ANNOTATION_BASE}/instance.${instanceName}` in ingress.metadata.annotations),
|
||||
)
|
||||
.map((ingress) => {
|
||||
let constructedService = {
|
||||
|
||||
@@ -32,7 +32,7 @@ export async function cleanWidgetGroups(widgets) {
|
||||
const optionKeys = Object.keys(sanitizedOptions);
|
||||
|
||||
// delete private options from the sanitized options
|
||||
["username", "password", "key"].forEach((pO) => {
|
||||
["username", "password", "key", "apiKey"].forEach((pO) => {
|
||||
if (optionKeys.includes(pO)) {
|
||||
delete sanitizedOptions[pO];
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export async function getPrivateWidgetOptions(type, widgetIndex) {
|
||||
const widgets = await widgetsFromConfig();
|
||||
|
||||
const privateOptions = widgets.map((widget) => {
|
||||
const { index, url, username, password, key } = widget.options;
|
||||
const { index, url, username, password, key, apiKey } = widget.options;
|
||||
|
||||
return {
|
||||
type: widget.type,
|
||||
@@ -67,6 +67,7 @@ export async function getPrivateWidgetOptions(type, widgetIndex) {
|
||||
username,
|
||||
password,
|
||||
key,
|
||||
apiKey,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -31,6 +31,10 @@ export async function sendJsonRpcRequest(url, method, params, username, password
|
||||
if (status === 200) {
|
||||
const json = JSON.parse(data.toString());
|
||||
|
||||
if (json.id === null) {
|
||||
json.id = 1;
|
||||
}
|
||||
|
||||
// in order to get access to the underlying error object in the JSON response
|
||||
// you must set `result` equal to undefined
|
||||
if (json.error && json.result === null) {
|
||||
|
||||
@@ -8,7 +8,7 @@ export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { widget } = service;
|
||||
const { data: resultData, error: resultError } = useWidgetAPI(widget, "result");
|
||||
const { data: resultData, error: resultError } = useWidgetAPI(widget, "upstreams");
|
||||
|
||||
if (resultError) {
|
||||
return <Container service={service} error={resultError} />;
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import genericProxyHandler from "utils/proxy/handlers/generic";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/reverse_proxy/upstreams",
|
||||
api: "{url}/{endpoint}",
|
||||
proxyHandler: genericProxyHandler,
|
||||
|
||||
mappings: {
|
||||
upstreams: {
|
||||
endpoint: "reverse_proxy/upstreams",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default widget;
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function Component({ service }) {
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<div className={classNames(service.description ? "-top-10" : "-top-8", "absolute right-1")}>
|
||||
<div className={classNames(service.description ? "-top-10" : "-top-8", "absolute right-1 z-20")}>
|
||||
<Dropdown options={dateRangeOptions} value={dateRange} setValue={setDateRange} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Component({ service }) {
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const { data: omadaData, error: omadaAPIError } = useWidgetAPI(widget, {
|
||||
const { data: omadaData, error: omadaAPIError } = useWidgetAPI(widget, "info", {
|
||||
refreshInterval: 5000,
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,12 @@ import omadaProxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
proxyHandler: omadaProxyHandler,
|
||||
|
||||
mappings: {
|
||||
info: {
|
||||
endpoint: "api/info",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default widget;
|
||||
|
||||
@@ -77,7 +77,7 @@ async function fetchSystem(url) {
|
||||
const systemResponse = JSON.parse(data.toString())[1];
|
||||
const response = {
|
||||
uptime: systemResponse.uptime,
|
||||
cpuLoad: systemResponse.load[1],
|
||||
cpuLoad: (systemResponse.load[1] / 65536.0).toFixed(2),
|
||||
};
|
||||
return [200, contentType, response];
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function Component({ service }) {
|
||||
|
||||
const enabled = infoData.filter((item) => item.enabled).length;
|
||||
const disabled = infoData.length - enabled;
|
||||
const connectionThreshold = widget.threshold ?? 2 * 60 * 1000;
|
||||
const connectionThreshold = (widget.threshold ?? 2) * 60 * 1000;
|
||||
const currentTime = new Date();
|
||||
const connected = infoData.filter(
|
||||
(item) => currentTime - new Date(item.latestHandshakeAt) < connectionThreshold,
|
||||
|
||||
@@ -21,14 +21,21 @@ async function login(widget, service) {
|
||||
});
|
||||
|
||||
try {
|
||||
const connectSidCookie = responseHeaders["set-cookie"]
|
||||
let connectSidCookie = responseHeaders["set-cookie"];
|
||||
if (!connectSidCookie) {
|
||||
const sid = cache.get(`${sessionSIDCacheKey}.${service}`);
|
||||
if (sid) {
|
||||
return sid;
|
||||
}
|
||||
}
|
||||
connectSidCookie = connectSidCookie
|
||||
.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`);
|
||||
logger.error(`Error logging into wg-easy, error: ${e}`);
|
||||
cache.del(`${sessionSIDCacheKey}.${service}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user