Files
dsh_shenxian/scripts/storage-report.cjs
T
admin 452924d89c feat(config): 涉密内容外置到配置目录(档案 140)
把散落在代码里的真实部署值统一收进 config/,代码改为引用配置,
使仓库副本/开源导出不再带出生产域名、IP、内网路径与凭据。

新增 config/:platform.env.example(模板)· load.sh(shell 加载器)·
index.cjs(node 加载器)· README.md(键一览与优先级)。
真实值放 config/platform.env —— 已 .gitignore 排除,不入库、不进导出。

TS 侧新增 src/platform-paths.ts 作部署路径的唯一解析处(零副作用):
platformDir/stateDir/backupDir/artifactDir/installDir/scriptPath。
config.ts 接入这些字段;内置中继种子由生产 URL 改为空(改由
DSHS_OVERLAY_BOOTSTRAP_SEEDS 提供)。修掉 5 处硬编码绝对路径,
src/** 注释中性化 116 行/53 文件。

scripts/** 36 个内部运维脚本:真令牌/PG 口令/隧道目标/主机号/路径
一律改从配置取;web/wake.html 的注册域白名单改为运行时从
location.hostname 推导;test/** 夹具 119 行/13 文件改 RFC 2606/5737
保留值,并把「内置种子必须为空」固化为回归断言。

取证:tsc 0 错;npm test 373/375(唯一失败 lease 属既有);
全仓扫描(大小写不敏感)代码面涉密标识 = 0;已部署 47 并零回归
(/opt/dsh/* 未搬家,/var/lib/dshs/platform 未被误建)。
2026-09-19 15:12:19 +08:00

64 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
const cfg = require('../config/index.cjs')
/**
* storage-report.cjs —— 每用户存储用量上报(档案 28)
* 输出:/var/run/dsh-storage-report.json(供门户 GET /api/admin/storage 直接读取,避免每次请求都 du)
* cron:每小时一次。
*/
const { execFileSync } = require('node:child_process')
const { existsSync, writeFileSync } = require('node:fs')
const { join } = require('node:path')
const Database = require('better-sqlite3')
const OUT = process.env.DSH_STORAGE_REPORT ?? '/var/run/dsh-storage-report.json'
const WS_MB = Number(process.env.DSH_WS_THRESHOLD_MB ?? 2048)
const SESS_MB = Number(process.env.DSH_SESSIONS_THRESHOLD_MB ?? 1024)
const duKB = (p) => { try { return Number(execFileSync('du', ['-sk', p], { encoding: 'utf8' }).split(/\s+/)[0]) * 1024 } catch { return 0 } }
const db = new Database(cfg.dbFile(), { readonly: true })
const users = db.prepare('SELECT username, home_dir FROM users ORDER BY username').all()
const rows = users.map((u) => {
const ws = join(u.home_dir, '..', 'ws'), sessions = join(u.home_dir, 'sessions'), trash = join(u.home_dir, '..', 'trash')
const t1 = [], t2 = []
// 仅统计顶层候选(与 ws-cleanup 口径一致),供管理员判断
if (existsSync(ws)) {
const { readdirSync, statSync } = require('node:fs')
const T1_DIRS = ['.local', '.cache', '.poc-backup', 'poc', '__pycache__', '.pytest_cache', 'node_modules', '.ipynb_checkpoints']
const T1_SUFFIX = ['.tgz', '.tmp', '.log', '.bak', '.part', '.crdownload']
const T2_EXT = new Set(['.py', '.js', '.mjs', '.cjs', '.sh', '.bash', '.ps1', '.psm1', '.bat', '.cmd', '.ts', '.ipynb', '.sql'])
const CUTOFF = Date.now() - 90 * 86400_000
for (const e of readdirSync(ws)) {
const p = join(ws, e)
let st; try { st = statSync(p) } catch { continue }
if (st.isDirectory()) { if (T1_DIRS.includes(e)) t1.push({ name: e, size: duKB(p) }); continue }
const dot = e.lastIndexOf('.')
const ext = dot >= 0 ? e.slice(dot).toLowerCase() : ''
if (T1_SUFFIX.includes(ext)) t1.push({ name: e, size: st.size })
else if (T2_EXT.has(ext) && st.mtimeMs < CUTOFF) t2.push({ name: e, size: st.size })
}
}
const wsB = existsSync(ws) ? duKB(ws) : 0
const sessB = existsSync(sessions) ? duKB(sessions) : 0
const trashB = existsSync(trash) ? duKB(trash) : 0
return {
username: u.username,
ws: wsB, sessions: sessB, trash: trashB, total: wsB + sessB + trashB,
wsOver: wsB >= WS_MB * 1048576, sessionsOver: sessB >= SESS_MB * 1048576,
cleanableT1: t1.length, cleanableT2: t2.length,
cleanableBytes: [...t1, ...t2].reduce((a, c) => a + c.size, 0),
topCleanable: [...t1, ...t2].sort((a, b) => b.size - a.size).slice(0, 5).map((c) => `${c.name} (${(c.size / 1048576).toFixed(1)}MB)`),
}
})
db.close()
const report = {
generatedAt: new Date().toISOString(),
thresholds: { wsMB: WS_MB, sessionsMB: SESS_MB, keepDays: { ws: 90, sessions: 365 }, trashKeepDays: 30 },
totals: { ws: rows.reduce((a, r) => a + r.ws, 0), sessions: rows.reduce((a, r) => a + r.sessions, 0), trash: rows.reduce((a, r) => a + r.trash, 0) },
users: rows,
}
writeFileSync(OUT, JSON.stringify(report, null, 2) + '\n')
const fmt = (b) => (b >= 1073741824 ? (b / 1073741824).toFixed(2) + 'G' : (b / 1048576).toFixed(1) + 'M')
for (const r of rows) console.log(` ${r.username}: ws=${fmt(r.ws)} sessions=${fmt(r.sessions)} trash=${fmt(r.trash)} | 可清 ${r.cleanableT1 + r.cleanableT2} 项/${fmt(r.cleanableBytes)}`)
console.log(` → 已写入 ${OUT}`)