mirror of
https://github.com/mblanke/Dashboard.git
synced 2026-03-01 12:10:20 -05:00
fix: rewrite Synology API to match actual DSM field names
- Volume sizes are in vol.size.total / vol.size.used (nested object)
- Added disk info parsing (name, model, status, temp, isSsd)
- Added SYNO.Core.System.Utilization for CPU/memory stats
- Response now returns {volumes, disks, utilization} matching widget interface
This commit is contained in:
@@ -10,6 +10,8 @@ const SYNOLOGY_PASSWORD = process.env.SYNOLOGY_PASSWORD;
|
|||||||
// Port 5000 = HTTP, 5001+ = HTTPS (Synology convention)
|
// Port 5000 = HTTP, 5001+ = HTTPS (Synology convention)
|
||||||
const protocol = SYNOLOGY_PORT === "5000" ? "http" : "https";
|
const protocol = SYNOLOGY_PORT === "5000" ? "http" : "https";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
if (!SYNOLOGY_HOST || !SYNOLOGY_USERNAME || !SYNOLOGY_PASSWORD) {
|
if (!SYNOLOGY_HOST || !SYNOLOGY_USERNAME || !SYNOLOGY_PASSWORD) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
@@ -22,12 +24,10 @@ export async function GET() {
|
|||||||
const baseUrl = `${protocol}://${SYNOLOGY_HOST}:${SYNOLOGY_PORT}`;
|
const baseUrl = `${protocol}://${SYNOLOGY_HOST}:${SYNOLOGY_PORT}`;
|
||||||
const httpsConfig =
|
const httpsConfig =
|
||||||
protocol === "https"
|
protocol === "https"
|
||||||
? {
|
? { httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
|
||||||
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
|
|
||||||
}
|
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
// Login to Synology
|
// Login
|
||||||
const loginResponse = await axios.get(`${baseUrl}/webapi/auth.cgi`, {
|
const loginResponse = await axios.get(`${baseUrl}/webapi/auth.cgi`, {
|
||||||
params: {
|
params: {
|
||||||
api: "SYNO.API.Auth",
|
api: "SYNO.API.Auth",
|
||||||
@@ -43,30 +43,77 @@ export async function GET() {
|
|||||||
|
|
||||||
const sid = loginResponse.data.data.sid;
|
const sid = loginResponse.data.data.sid;
|
||||||
|
|
||||||
// Get storage info
|
// Fetch storage + utilization in parallel
|
||||||
const storageResponse = await axios.get(`${baseUrl}/webapi/entry.cgi`, {
|
const [storageResponse, utilizationResponse] = await Promise.all([
|
||||||
params: {
|
axios.get(`${baseUrl}/webapi/entry.cgi`, {
|
||||||
api: "SYNO.Storage.CGI.Storage",
|
params: {
|
||||||
version: 1,
|
api: "SYNO.Storage.CGI.Storage",
|
||||||
method: "load_info",
|
version: 1,
|
||||||
_sid: sid,
|
method: "load_info",
|
||||||
},
|
_sid: sid,
|
||||||
...httpsConfig,
|
},
|
||||||
|
...httpsConfig,
|
||||||
|
}),
|
||||||
|
axios.get(`${baseUrl}/webapi/entry.cgi`, {
|
||||||
|
params: {
|
||||||
|
api: "SYNO.Core.System.Utilization",
|
||||||
|
version: 1,
|
||||||
|
method: "get",
|
||||||
|
_sid: sid,
|
||||||
|
},
|
||||||
|
...httpsConfig,
|
||||||
|
}).catch(() => null),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const storageData = storageResponse.data.data;
|
||||||
|
|
||||||
|
// Parse volumes — size info is nested: vol.size.total, vol.size.used
|
||||||
|
const rawVolumes = storageData.volumes || [];
|
||||||
|
const volumes = rawVolumes.map((vol: any) => {
|
||||||
|
const sizeObj = vol.size || {};
|
||||||
|
const total = parseInt(sizeObj.total, 10) || 0;
|
||||||
|
const used = parseInt(sizeObj.used, 10) || 0;
|
||||||
|
const available = total - used;
|
||||||
|
const pct = total > 0 ? ((used / total) * 100).toFixed(2) : "0";
|
||||||
|
return {
|
||||||
|
volume: vol.vol_path || vol.volume_path || "",
|
||||||
|
id: vol.id || "",
|
||||||
|
size: total,
|
||||||
|
used: used,
|
||||||
|
available: available,
|
||||||
|
percentUsed: pct,
|
||||||
|
status: vol.status || "unknown",
|
||||||
|
fsType: vol.fs_type || "",
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const volumes = storageResponse.data.data.volumes.map((vol: any) => ({
|
// Parse disks
|
||||||
volume: vol.volume_path,
|
const rawDisks = storageData.disks || [];
|
||||||
size: vol.size_total_byte,
|
const disks = rawDisks.map((d: any) => ({
|
||||||
used: vol.size_used_byte,
|
name: d.name || d.longName || "Unknown",
|
||||||
available: vol.size_free_byte,
|
model: d.model || "",
|
||||||
percentUsed: ((vol.size_used_byte / vol.size_total_byte) * 100).toFixed(
|
status: d.smart_status || d.status || "unknown",
|
||||||
2
|
isSsd: d.isSsd ?? false,
|
||||||
),
|
temp: typeof d.temp === "number" ? d.temp : null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return NextResponse.json(volumes);
|
// Parse utilization
|
||||||
|
let utilization: { cpu: number | null; memory: number | null } | null = null;
|
||||||
|
if (utilizationResponse?.data?.data) {
|
||||||
|
const util = utilizationResponse.data.data;
|
||||||
|
const cpu = util.cpu
|
||||||
|
? (util.cpu.user_load || 0) + (util.cpu.system_load || 0)
|
||||||
|
: null;
|
||||||
|
const memory = util.memory?.real_usage ?? null;
|
||||||
|
utilization = { cpu, memory };
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ volumes, disks, utilization });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Synology API error:", error instanceof Error ? error.message : error);
|
console.error(
|
||||||
|
"Synology API error:",
|
||||||
|
error instanceof Error ? error.message : error
|
||||||
|
);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Failed to fetch Synology storage" },
|
{ error: "Failed to fetch Synology storage" },
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
|
|||||||
Reference in New Issue
Block a user