fix: nixclient SSL calls correct user-scoped route, not admin endpoint

- Add /api/nixclient/ssl (GET) — returns certs for authenticated user only
- Add /api/nixclient/ssl/:id/renew (POST) — validates cert belongs to user
- Register sslRoutes in nixclient index.ts
- nixclient SSL.tsx was calling /nixserver/ssl (requireAdminOrReseller)
  which would always 401 for regular users — fixed to /nixclient/ssl
This commit is contained in:
NixReaper
2026-04-11 05:39:01 -05:00
parent 8ff00c544c
commit d235d27fe3
3 changed files with 50 additions and 2 deletions
+2 -2
View File
@@ -10,14 +10,14 @@ export default function SSL() {
const fetch = async () => {
setLoading(true)
try { const r = await api.get('/nixserver/ssl'); setCerts(r.data.data) }
try { const r = await api.get('/nixclient/ssl'); setCerts(r.data.data) }
finally { setLoading(false) }
}
useEffect(() => { fetch() }, [])
const renew = async (id: number) => {
try { await api.post(`/nixserver/ssl/${id}/renew`); fetch() }
try { await api.post(`/nixclient/ssl/${id}/renew`); fetch() }
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Renewal failed') }
}
+2
View File
@@ -6,6 +6,7 @@ import domainRoutes from './domains.js'
import ftpRoutes from './ftp.js'
import cronRoutes from './cron.js'
import statsRoutes from './stats.js'
import sslRoutes from './ssl.js'
export default async function nixclientRoutes(fastify: FastifyInstance) {
fastify.register(fileRoutes, { prefix: '/files' })
@@ -15,4 +16,5 @@ export default async function nixclientRoutes(fastify: FastifyInstance) {
fastify.register(ftpRoutes, { prefix: '/ftp' })
fastify.register(cronRoutes, { prefix: '/cron' })
fastify.register(statsRoutes, { prefix: '/stats' })
fastify.register(sslRoutes, { prefix: '/ssl' })
}
+46
View File
@@ -0,0 +1,46 @@
import type { FastifyInstance } from 'fastify'
import prisma from '../../db/client.js'
import { requireAuth } from '../../middleware/auth.js'
import { exec } from '../../core/exec.js'
export default async function sslRoutes(fastify: FastifyInstance) {
const preHandler = [requireAuth]
// GET /api/nixclient/ssl — certs for the authenticated user's account only
fastify.get('/', { preHandler }, async (request, reply) => {
const user = request.user!
const accountId = user.sub
const certs = await prisma.sslCertificate.findMany({
where: { accountId },
orderBy: { createdAt: 'desc' },
})
return reply.send({ success: true, data: certs })
})
// POST /api/nixclient/ssl/:id/renew
fastify.post('/:id/renew', { preHandler }, async (request, reply) => {
const { id } = request.params as { id: string }
const user = request.user!
const cert = await prisma.sslCertificate.findFirst({
where: { id: parseInt(id, 10), accountId: user.sub },
})
if (!cert) return reply.code(404).send({ success: false, error: 'Certificate not found' })
const result = await exec('certbot', ['renew', '--cert-name', cert.domain, '--non-interactive'])
if (result.exitCode !== 0) {
return reply.code(500).send({ success: false, error: `Renewal failed: ${result.stderr}` })
}
const expiresAt = new Date()
expiresAt.setDate(expiresAt.getDate() + 90)
const updated = await prisma.sslCertificate.update({
where: { id: cert.id },
data: { status: 'active', issuedAt: new Date(), expiresAt },
})
return reply.send({ success: true, data: updated })
})
}