Skip to content

Commit

Permalink
feat: add Proxmox integration/widget (#1903)
Browse files Browse the repository at this point in the history
  • Loading branch information
dslatt committed Mar 23, 2024
1 parent 4a8b737 commit 0677271
Show file tree
Hide file tree
Showing 13 changed files with 1,182 additions and 218 deletions.
157 changes: 130 additions & 27 deletions public/locales/en/modules/health-monitoring.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,141 @@
{
"descriptor": {
"name": "System Health Monitoring",
"description": "Information about your NAS",
"settings": {
"title": "System Health Monitoring",
"fahrenheit": {
"label": "Fahrenheit"
}
}
},
"descriptor": {
"name": "System Health Monitoring",
"description": "Displays information showing the health and status of your system(s).",
"settings": {
"title": "Settings for system health monitoring",
"fahrenheit": {
"label": "CPU Temp in Fahrenheit"
},
"cpu": {
"label": "CPU",
"label": "Show CPU Info",
"load": "Load Average",
"minute": "{{minute}} minute",
"minutes": "{{minutes}} minutes"
},
"memory": {
"label": "Memory",
"totalMem": "Total memory: {{total}}GB",
"available": "Available: {{available}}GB - {{percentage}}%"
"label": "Show Memory Info"
},
"fileSystem": {
"label": "File System",
"available": "Available: {{available}} - {{percentage}}%"
},
"info": {
"uptime": "Uptime",
"updates": "Updates",
"reboot": "Reboot"
},
"errors": {
"general": {
"title": "Unable to find your NAS",
"text": "There was a problem connecting to your NAS. Please verify your configuration/integration(s)."
"label": "Show Filesystem Info"
},
"node": {
"label": "Filter by node name",
"info": "Enter your Proxmox node name to only show metrics for that node. By default, the entire cluster is shown."
},
"defaultViewState": {
"label": "Section open by default",
"data": {
"none": "None",
"node": "Nodes",
"vm": "VMs",
"lxc": "LXCs",
"storage": "Storage"
}
},
"defaultTabState": {
"label": "Tab open by default",
"info": "Tab open by default. Only used when multiple integrations are available.",
"data": {
"system": "System",
"cluster": "Cluster"
}
},
"summary": {
"label": "Show summary section"
},
"showNode": {
"label": "Show nodes section"
},
"showVM": {
"label": "Show VMs section"
},
"showLXCs": {
"label": "Show LXCs section"
},
"showStorage": {
"label": "Show storage section"
},
"sectionIndicatorColor": {
"label": "Requirement for section status indicator to be 'OK'",
"info": "'All' requires that all items be online for the indicator to be green. 'Any' requires at least one item to be online.",
"data": {
"any": "Any Active",
"all": "All Active"
}
},
"ignoreCert": {
"label": "Ignore Certificate Errors",
"info": "If enabled, the widget will ignore certificate errors when accessing the Proxmox API. This can be helpful when accessing Proxmox through HTTPS."
}
}
}
},
"cpu": {
"label": "CPU",
"load": "Load Average",
"minute": "{{minute}} minute"
},
"memory": {
"label": "Memory",
"totalMem": "Total memory: {{total}}GB",
"available": "Available: {{available}}GB - {{percentage}}%"
},
"fileSystem": {
"label": "File System",
"available": "Available: {{available}} - {{percentage}}%"
},
"info": {
"uptime": "Uptime",
"uptimeFormat": "{{days}} days, {{hours}} hours",
"updates": "Updates Available",
"reboot": "Reboot"
},
"errors": {
"general": {
"title": "Unable to find your system(s).",
"text": "There was a problem connecting to your system. Please verify your configuration/integration(s)."
}
},
"headings": {
"system": "System",
"cluster": "Cluster"
},
"cluster": {
"summary": {
"cpu": "CPU",
"ram": "RAM"
},
"accordion": {
"title": {
"nodes": "Nodes",
"vms": "VMs",
"lxcs": "LXCs",
"storage": "Storage"
}
},
"table": {
"header": {
"name": "Name",
"cpu": "CPU",
"ram": "RAM",
"node": "Node"
}
},
"popover": {
"node": "Node",
"vmid": "VMID",
"details": "Details",
"cores": "Cores - {{maxCpu}}",
"memSize": "Memory - {{maxMem}}",
"memRatio": "Memory - {{usedMem}} / {{maxMem}}",
"diskSize": "Disk - {{maxDisk}}",
"diskRatio": "Disk - {{usedDisk}} / {{maxDisk}}",
"uptime": "Uptime - {{uptime}}",
"plugin": "Plugin",
"ha": "HA State - {{haState}}",
"sharedStorage": "Shared Storage",
"localStorage": "Local Storage",
"na": "N/A"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,9 @@ export const availableIntegrations = [
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/openmediavault.png',
label: 'OpenMediaVault',
},
{
value: 'proxmox',
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/proxmox.png',
label: 'Proxmox',
}
] as const satisfies Readonly<SelectItem[]>;
4 changes: 2 additions & 2 deletions src/server/api/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import { dashDotRouter } from './routers/dash-dot';
import { dnsHoleRouter } from './routers/dns-hole/router';
import { dockerRouter } from './routers/docker/router';
import { downloadRouter } from './routers/download';
import { healthMonitoringRouter } from './routers/health-monitoring/router';
import { iconRouter } from './routers/icon';
import { indexerManagerRouter } from './routers/indexer-manager';
import { inviteRouter } from './routers/invite/invite-router';
import { mediaRequestsRouter } from './routers/media-request';
import { mediaServerRouter } from './routers/media-server';
import { notebookRouter } from './routers/notebook';
import { openmediavaultRouter } from './routers/openmediavault';
import { overseerrRouter } from './routers/overseerr';
import { passwordRouter } from './routers/password';
import { rssRouter } from './routers/rss';
Expand Down Expand Up @@ -50,7 +50,7 @@ export const rootRouter = createTRPCRouter({
password: passwordRouter,
notebook: notebookRouter,
smartHomeEntityState: smartHomeEntityStateRouter,
openmediavault: openmediavaultRouter,
healthMonitoring: healthMonitoringRouter,
});

// export type definition of API
Expand Down
127 changes: 127 additions & 0 deletions src/server/api/routers/health-monitoring/openmediavault.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import axios from 'axios';
import Consola from 'consola';
import { checkIntegrationsType, findAppProperty } from '~/tools/client/app-properties';
import { getConfig } from '~/tools/config/getConfig';
import { ConfigAppType } from '~/types/app';

let sessionId: string | null = null;
let loginToken: string | null = null;

async function makeOpenMediaVaultRPCCall(
serviceName: string,
method: string,
params: Record<string, any>,
headers: Record<string, string>,
input: { configName: string }
) {
const config = getConfig(input.configName);
const app = config.apps.find((app) => checkIntegrationsType(app.integration, ['openmediavault']));

if (!app) {
Consola.error(`App 'openmediavault' not found for configName '${input.configName}'`);
return null;
}

const appUrl = new URL(app.url);
const response = await axios.post(
`${appUrl.origin}/rpc.php`,
{
service: serviceName,
method: method,
params: params,
},
{
headers: {
'Content-Type': 'application/json',
...headers,
},
}
);
return response;
}

export async function makeOpenMediaVaultCalls(app: ConfigAppType, input: any) {
let authResponse: any = null;

if (!sessionId || !loginToken) {
if (!app) {
Consola.error(
`Failed to process request to app 'openmediavault'. Please check username & password`
);
return null;
}

authResponse = await makeOpenMediaVaultRPCCall(
'session',
'login',
{
username: findAppProperty(app, 'username'),
password: findAppProperty(app, 'password'),
},
{},
input
);

if (authResponse.data.response.sessionid) {
sessionId = authResponse.data.response.sessionid;
} else {
const cookies = authResponse.headers['set-cookie'] || [];
sessionId = cookies
.find((cookie: any) => cookie.includes('X-OPENMEDIAVAULT-SESSIONID'))
?.split(';')[0];

loginToken = cookies
.find((cookie: any) => cookie.includes('X-OPENMEDIAVAULT-LOGIN'))
?.split(';')[0];
}

const responses = await Promise.allSettled([
makeOpenMediaVaultRPCCall(
'system',
'getInformation',
{},
loginToken
? { Cookie: `${loginToken};${sessionId}` }
: { 'X-OPENMEDIAVAULT-SESSIONID': sessionId as string },
input
),
makeOpenMediaVaultRPCCall(
'filesystemmgmt',
'enumerateMountedFilesystems',
{ includeroot: true },
loginToken
? { Cookie: `${loginToken};${sessionId}` }
: { 'X-OPENMEDIAVAULT-SESSIONID': sessionId as string },
input
),
makeOpenMediaVaultRPCCall(
'cputemp',
'get',
{},
loginToken
? { Cookie: `${loginToken};${sessionId}` }
: { 'X-OPENMEDIAVAULT-SESSIONID': sessionId as string },
input
),
]);

const systemInfoResponse =
responses[0].status === 'fulfilled' && responses[0].value
? responses[0].value.data?.response
: null;
const fileSystemResponse =
responses[1].status === 'fulfilled' && responses[1].value
? responses[1].value.data?.response
: null;
const cpuTempResponse =
responses[2].status === 'fulfilled' && responses[2].value
? responses[2].value.data?.response
: null;

return {
systemInfo: systemInfoResponse,
fileSystem: fileSystemResponse,
cpuTemp: cpuTempResponse,
};
}
}
Loading

0 comments on commit 0677271

Please sign in to comment.