feat: v0.4.0 — Apache service stack, shell access, log viewer, changelog

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
This commit is contained in:
NixReaper
2026-04-11 05:17:49 -05:00
parent 10e31482b0
commit 8ff00c544c
65 changed files with 11223 additions and 1259 deletions
+7 -13
View File
@@ -1,11 +1,10 @@
import type { FastifyInstance } from 'fastify'
import { z } from 'zod'
import path from 'path'
import prisma from '../../db/client.js'
import { requireAdminOrReseller } from '../../middleware/auth.js'
import { exec } from '../../core/exec.js'
import { enableVhost } from '../../core/nginx.js'
import { config } from '../../config.js'
import { enableVhost } from '../../core/apache.js'
import { getCertPaths } from '../../core/ssl.js'
export default async function sslRoutes(fastify: FastifyInstance) {
const preHandler = [requireAdminOrReseller]
@@ -47,23 +46,19 @@ export default async function sslRoutes(fastify: FastifyInstance) {
if (includeWww) domains.push(`www.${domain}`)
const certbotArgs = [
'certonly', '--webroot',
'-w', config.paths.certbotWebroot,
'certonly', '--apache',
'--non-interactive',
'--agree-tos',
'--email', account.email,
...domains.flatMap(d => ['-d', d]),
]
const result = await exec('certbot', certbotArgs)
const result = await exec('certbot', certbotArgs, { timeout: 120_000 })
if (result.exitCode !== 0) {
return reply.code(500).send({ success: false, error: `certbot failed: ${result.stderr}` })
}
const certDir = `/etc/letsencrypt/live/${domain}`
const certPath = `${certDir}/fullchain.pem`
const keyPath = `${certDir}/privkey.pem`
const chainPath = `${certDir}/chain.pem`
const { certPath, keyPath, chainPath } = getCertPaths(domain)
const issuedAt = new Date()
const expiresAt = new Date()
@@ -82,7 +77,7 @@ export default async function sslRoutes(fastify: FastifyInstance) {
})
}
// Update nginx vhost with SSL
// Regenerate Apache vhost with SSL enabled
try {
await enableVhost({
domain,
@@ -91,10 +86,9 @@ export default async function sslRoutes(fastify: FastifyInstance) {
sslEnabled: true,
certPath,
keyPath,
chainPath,
})
} catch (e) {
console.warn(`Nginx SSL update failed for ${domain}:`, e)
console.warn(`Apache SSL vhost update failed for ${domain}:`, e)
}
return reply.send({ success: true, data: cert })