fix: remove ACME cert scanning to eliminate caddy-data permission issue (#88)

Caddy's certmagic creates storage dirs with hardcoded 0700 permissions,
making the web container's supplementary group membership ineffective.
Rather than working around this with ACLs or chmod hacks, remove the
feature entirely — it was cosmetic (issuer/expiry display) for certs
that Caddy auto-manages anyway.

Also bump access list dropdown timeout from 5s to 10s to fix flaky E2E test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
fuomag9
2026-04-03 12:34:18 +02:00
parent 49b869f0ca
commit b9a88c4330
8 changed files with 9 additions and 144 deletions

View File

@@ -44,7 +44,6 @@ export default function CertificatesClient({
const [statusFilter, setStatusFilter] = useState<string | null>(null);
const allStatuses: (CertExpiryStatus | null)[] = [
...acmeHosts.map((h) => h.certExpiryStatus),
...importedCerts.map((c) => c.expiryStatus),
];
const { expired, expiringSoon, healthy } = countExpiry(allStatuses);

View File

@@ -5,7 +5,6 @@ import { Card, CardContent } from "@/components/ui/card";
import { DataTable } from "@/components/ui/DataTable";
import { StatusChip } from "@/components/ui/StatusChip";
import type { AcmeHost } from "../page";
import { RelativeTime } from "./RelativeTime";
type Props = {
acmeHosts: AcmeHost[];
@@ -40,18 +39,6 @@ const columns = [
</div>
),
},
{
id: "issuer",
label: "Issuer",
render: (r: AcmeHost) => (
<span className="text-xs text-muted-foreground font-mono">{r.certIssuer ?? "—"}</span>
),
},
{
id: "expiry",
label: "Expiry",
render: (r: AcmeHost) => <RelativeTime validTo={r.certValidTo} status={r.certExpiryStatus} />,
},
{
id: "status",
label: "Status",
@@ -71,7 +58,6 @@ function acmeMobileCard(r: AcmeHost) {
{r.domains[0]}{r.domains.length > 1 ? ` +${r.domains.length - 1}` : ""}
</p>
<div className="flex items-center gap-2 flex-wrap mt-1">
<RelativeTime validTo={r.certValidTo} status={r.certExpiryStatus} />
<StatusChip status={r.enabled ? "active" : "inactive"} />
</div>
</CardContent>
@@ -81,7 +67,7 @@ function acmeMobileCard(r: AcmeHost) {
export function AcmeTab({ acmeHosts, acmePagination, search, statusFilter }: Props) {
const filtered = acmeHosts.filter((h) => {
if (statusFilter && h.certExpiryStatus !== statusFilter) return false;
if (statusFilter) return false; // ACME hosts have no expiry status
if (search) {
const q = search.toLowerCase();
return (

View File

@@ -4,7 +4,6 @@ import { proxyHosts, certificates } from '@/src/lib/db/schema';
import { isNull, isNotNull, count } from 'drizzle-orm';
import { requireAdmin } from '@/src/lib/auth';
import CertificatesClient from './CertificatesClient';
import { scanAcmeCerts } from '@/src/lib/acme-certs';
import { listCaCertificates, type CaCertificate } from '@/src/lib/models/ca-certificates';
import { listIssuedClientCertificates, type IssuedClientCertificate } from '@/src/lib/models/issued-client-certificates';
@@ -23,10 +22,6 @@ export type AcmeHost = {
domains: string[];
ssl_forced: boolean;
enabled: boolean;
certValidTo: string | null;
certValidFrom: string | null;
certIssuer: string | null;
certExpiryStatus: CertExpiryStatus | null;
};
export type ImportedCertView = {
@@ -86,8 +81,6 @@ export default async function CertificatesPage({ searchParams }: PageProps) {
const { page: pageParam } = await searchParams;
const page = Math.max(1, parseInt(pageParam ?? "1", 10) || 1);
const offset = (page - 1) * PER_PAGE;
const acmeCertMap = scanAcmeCerts();
const [caCerts, issuedClientCerts] = await Promise.all([
listCaCertificates(),
listIssuedClientCertificates()
@@ -124,25 +117,13 @@ export default async function CertificatesPage({ searchParams }: PageProps) {
.where(isNotNull(proxyHosts.certificateId)),
]);
const acmeHosts: AcmeHost[] = acmeRows.map(r => {
const domains = JSON.parse(r.domains) as string[];
let certInfo = null;
for (const domain of domains) {
const info = acmeCertMap.get(domain.toLowerCase());
if (info) { certInfo = info; break; }
}
return {
id: r.id,
name: r.name,
domains,
ssl_forced: r.sslForced,
enabled: r.enabled,
certValidTo: certInfo?.validTo ?? null,
certValidFrom: certInfo?.validFrom ?? null,
certIssuer: certInfo?.issuer ?? null,
certExpiryStatus: certInfo?.validTo ? getExpiryStatus(certInfo.validTo) : null,
};
});
const acmeHosts: AcmeHost[] = acmeRows.map(r => ({
id: r.id,
name: r.name,
domains: JSON.parse(r.domains) as string[],
ssl_forced: r.sslForced,
enabled: r.enabled,
}));
const usageMap = new Map<number, { id: number; name: string; domains: string[] }[]>();
for (const u of usageRows) {