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:
@@ -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);
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user