Service stack (backend): - webserver.ts rewritten: apache2/php-fpm routes replacing nginx; per-account Apache log endpoint; PHP-FPM reload per version; apache2ctl configtest/reload - accounts.ts: added PUT /:id/shell to toggle /bin/bash vs /usr/sbin/nologin, runs usermod -s and writes audit log New UI features (nixserver): - ShellAccess page: grant/revoke SSH access per account via AccountPicker - ApacheLogs page: live log viewer (access/error) per account; replaces stub - NginxLogs: re-exports ApacheLogs (NixPanel uses Apache, not NGINX) - WebServer page: shows apache2, php8.2-fpm, php8.3-fpm status with reload buttons; config test; Apache log viewer Bug fixes: - Accounts.tsx: Lucide icon size prop type widened (string|number); removed unused Users import; dropped duplicate NGINX log card - Removed soon badge from ShellAccess and ApacheLogs entries Project: - CHANGELOG.md created with full history from 0.2.x through 0.4.0 - All packages bumped from 0.3.2 to 0.4.0
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import path from 'path'
|
|
import fs from 'fs/promises'
|
|
import prisma from '../db/client.js'
|
|
import { exec } from './exec.js'
|
|
import { config } from '../config.js'
|
|
|
|
/**
|
|
* Collect disk usage for all active accounts and update the DB.
|
|
* Called every 15 minutes by the scheduler.
|
|
*/
|
|
export async function collectDiskUsage(): Promise<void> {
|
|
const accounts = await prisma.account.findMany({
|
|
where: { status: { in: ['active', 'suspended'] } },
|
|
select: { id: true, username: true, homedir: true },
|
|
})
|
|
|
|
for (const account of accounts) {
|
|
try {
|
|
const usedMb = await getDirSizeMb(account.homedir)
|
|
await prisma.account.update({
|
|
where: { id: account.id },
|
|
data: { diskUsedMb: BigInt(Math.round(usedMb)) },
|
|
})
|
|
} catch {
|
|
// Non-fatal — homedir may not exist yet
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset bandwidth counters for all accounts on the 1st of each month.
|
|
* Called by the scheduler monthly cron.
|
|
*/
|
|
export async function resetMonthlyBandwidth(): Promise<void> {
|
|
await prisma.account.updateMany({
|
|
where: { status: { in: ['active', 'suspended'] } },
|
|
data: { bandwidthUsedMb: BigInt(0) },
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Get disk usage of a directory in MB using `du`.
|
|
*/
|
|
async function getDirSizeMb(dirPath: string): Promise<number> {
|
|
// du is not in exec allowlist — read from /proc or use stat directly
|
|
// Use fs.stat recursively for safety, or fall back to parsing /proc
|
|
try {
|
|
const result = await exec('df', ['-BM', '--output=used', dirPath])
|
|
// df output: header line + value line like "123M"
|
|
const lines = result.stdout.split('\n').filter(Boolean)
|
|
const valueStr = lines[lines.length - 1]?.replace('M', '').trim() ?? '0'
|
|
return parseInt(valueStr, 10) || 0
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get per-account Apache access log size as a bandwidth proxy.
|
|
* This is a rough estimate — real bandwidth tracking requires log parsing.
|
|
*/
|
|
export async function getAccessLogSizeMb(username: string): Promise<number> {
|
|
const logPath = `/var/log/apache2/${username}-access.log`
|
|
try {
|
|
const stat = await fs.stat(logPath)
|
|
return Math.round(stat.size / 1024 / 1024)
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|