syco.me Homelab Dashboard

This commit is contained in:
2026-05-10 21:23:42 +02:00
parent 933e492d15
commit 90de2c1674
45 changed files with 6666 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
import { Router } from 'express'
import axios from 'axios'
const router = Router()
let cachedSid: string | null = null
let sidExpiry = 0
async function getSid(): Promise<string> {
if (cachedSid && Date.now() < sidExpiry) return cachedSid
const host = process.env.SYNOLOGY_HOST
const res = await axios.get(`${host}/webapi/auth.cgi`, {
params: {
api: 'SYNO.API.Auth',
version: 3,
method: 'login',
account: process.env.SYNOLOGY_USER,
passwd: process.env.SYNOLOGY_PASSWORD,
session: 'dashboard',
format: 'sid',
},
})
if (!res.data.success) throw new Error(`Synology login failed: ${JSON.stringify(res.data.error)}`)
cachedSid = res.data.data.sid as string
sidExpiry = Date.now() + 20 * 60 * 1000
return cachedSid
}
router.get('/storage', async (_req, res) => {
try {
const host = process.env.SYNOLOGY_HOST
if (!host) {
res.status(503).json({ error: 'SYNOLOGY_HOST not configured' })
return
}
const sid = await getSid()
const storageRes = await axios.get(`${host}/webapi/entry.cgi`, {
params: {
api: 'SYNO.Storage.CGI.Storage',
version: 1,
method: 'load_info',
_sid: sid,
},
})
if (!storageRes.data.success) {
cachedSid = null
throw new Error(`Synology storage error: ${JSON.stringify(storageRes.data.error)}`)
}
const d = storageRes.data.data
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const volumes: any[] = d?.volumes ?? d?.vol_info ?? d?.storage?.volumes ?? []
res.json({
volumes: volumes.map(v => ({
id: v.vol_path ?? v.volume_path ?? v.id,
label: v.vol_path ?? v.display_name ?? v.id,
// DSM 7: sizes live under v.size.{total,used}
used: Number(v.size?.used ?? v.size_used ?? 0),
total: Number(v.size?.total ?? v.size_total ?? 0),
})),
})
} catch (err: unknown) {
if (err instanceof Error && err.message.includes('login failed')) cachedSid = null
const msg = err instanceof Error ? err.message : 'Unknown error'
res.status(500).json({ error: msg })
}
})
router.get('/info', async (_req, res) => {
try {
const host = process.env.SYNOLOGY_HOST
if (!host) { res.status(503).json({ error: 'SYNOLOGY_HOST not configured' }); return }
const sid = await getSid()
const r = await axios.get(`${host}/webapi/entry.cgi`, {
params: { api: 'SYNO.DSM.Info', version: 2, method: 'getinfo', _sid: sid },
})
if (!r.data.success) throw new Error(`DSM info error: ${JSON.stringify(r.data.error)}`)
const d = r.data.data ?? {}
res.json({
model: d.model ?? '',
dsmVersion: d.version ?? '',
uptime: Number(d.uptime ?? 0),
temperature: d.temperature != null ? Number(d.temperature) : null,
})
} catch (err: unknown) {
if (err instanceof Error && err.message.includes('login failed')) cachedSid = null
const msg = err instanceof Error ? err.message : 'Unknown error'
res.status(500).json({ error: msg })
}
})
export default router