syco.me Homelab Dashboard
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user