104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
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
|