diff --git a/app/(dashboard)/settings/SettingsClient.tsx b/app/(dashboard)/settings/SettingsClient.tsx index b899b3b5..668e60e0 100644 --- a/app/(dashboard)/settings/SettingsClient.tsx +++ b/app/(dashboard)/settings/SettingsClient.tsx @@ -2,12 +2,11 @@ import { useFormState } from "react-dom"; import { Alert, Box, Button, Card, CardContent, Checkbox, FormControlLabel, Stack, TextField, Typography } from "@mui/material"; -import type { GeneralSettings, AuthentikSettings, LoggingSettings, MetricsSettings } from "@/src/lib/settings"; +import type { GeneralSettings, AuthentikSettings, MetricsSettings } from "@/src/lib/settings"; import { updateCloudflareSettingsAction, updateGeneralSettingsAction, updateAuthentikSettingsAction, - updateLoggingSettingsAction, updateMetricsSettingsAction } from "./actions"; @@ -19,21 +18,13 @@ type Props = { accountId?: string; }; authentik: AuthentikSettings | null; - logging: { - enabled: boolean; - lokiUrl?: string; - lokiUsername?: string; - hasPassword: boolean; - labels?: Record; - } | null; metrics: MetricsSettings | null; }; -export default function SettingsClient({ general, cloudflare, authentik, logging, metrics }: Props) { +export default function SettingsClient({ general, cloudflare, authentik, metrics }: Props) { const [generalState, generalFormAction] = useFormState(updateGeneralSettingsAction, null); const [cloudflareState, cloudflareFormAction] = useFormState(updateCloudflareSettingsAction, null); const [authentikState, authentikFormAction] = useFormState(updateAuthentikSettingsAction, null); - const [loggingState, loggingFormAction] = useFormState(updateLoggingSettingsAction, null); const [metricsState, metricsFormAction] = useFormState(updateMetricsSettingsAction, null); return ( @@ -171,82 +162,6 @@ export default function SettingsClient({ general, cloudflare, authentik, logging - - - - Logging - - - Enable comprehensive request logging to Loki for debugging and monitoring. - You must deploy your own Loki instance and provide its URL. - - {logging?.hasPassword && ( - - A Loki password is already configured. Leave the password field blank to keep it, or enter a new password to update it. - - )} - - {loggingState?.message && ( - - {loggingState.message} - - )} - } - label="Enable request logging" - /> - - - - } - label="Remove existing password" - disabled={!logging?.hasPassword} - /> - - - After enabling logging, all Caddy requests will be sent to your Loki instance. - You can query and visualize logs in Grafana using the Loki datasource. - - - - - - - - diff --git a/app/(dashboard)/settings/actions.ts b/app/(dashboard)/settings/actions.ts index 15f9c670..6eee178f 100644 --- a/app/(dashboard)/settings/actions.ts +++ b/app/(dashboard)/settings/actions.ts @@ -3,15 +3,7 @@ import { revalidatePath } from "next/cache"; import { requireAdmin } from "@/src/lib/auth"; import { applyCaddyConfig } from "@/src/lib/caddy"; -import { - getCloudflareSettings, - getLoggingSettings, - saveAuthentikSettings, - saveCloudflareSettings, - saveGeneralSettings, - saveLoggingSettings, - saveMetricsSettings -} from "@/src/lib/settings"; +import { getCloudflareSettings, saveCloudflareSettings, saveGeneralSettings, saveAuthentikSettings, saveMetricsSettings } from "@/src/lib/settings"; type ActionResult = { success: boolean; @@ -126,73 +118,3 @@ export async function updateMetricsSettingsAction(_prevState: ActionResult | nul return { success: false, message: error instanceof Error ? error.message : "Failed to save metrics settings" }; } } - -export async function updateLoggingSettingsAction(_prevState: ActionResult | null, formData: FormData): Promise { - try { - await requireAdmin(); - const enabled = formData.get("enabled") === "on"; - const lokiUrl = formData.get("lokiUrl") ? String(formData.get("lokiUrl")).trim() : undefined; - const lokiUsername = formData.get("lokiUsername") ? String(formData.get("lokiUsername")).trim() : undefined; - const rawPassword = formData.get("lokiPassword") ? String(formData.get("lokiPassword")).trim() : ""; - const clearPassword = formData.get("clearPassword") === "on"; - const labelsStr = formData.get("labels") ? String(formData.get("labels")).trim() : ""; - - // Get current settings to preserve existing password if needed - const current = await getLoggingSettings(); - - // Validate Loki URL if logging is enabled - if (enabled && !lokiUrl) { - return { success: false, message: "Loki URL is required when logging is enabled" }; - } - - if (enabled && lokiUrl) { - try { - new URL(lokiUrl); - } catch { - return { success: false, message: "Invalid Loki URL format. Must be a valid HTTP/HTTPS URL." }; - } - } - - // Parse labels JSON if provided - let labels: Record | undefined; - if (labelsStr && labelsStr.length > 0) { - try { - labels = JSON.parse(labelsStr); - if (typeof labels !== "object" || Array.isArray(labels)) { - return { success: false, message: "Labels must be a JSON object" }; - } - } catch { - return { success: false, message: "Invalid labels JSON format" }; - } - } - - // Handle password: clear if checkbox is checked, update if new password provided, otherwise keep existing - const lokiPassword = clearPassword ? "" : rawPassword || current?.lokiPassword || ""; - - await saveLoggingSettings({ - enabled, - lokiUrl, - lokiUsername: lokiUsername && lokiUsername.length > 0 ? lokiUsername : undefined, - lokiPassword: lokiPassword && lokiPassword.length > 0 ? lokiPassword : undefined, - labels - }); - - // Apply config to enable/disable logging - try { - await applyCaddyConfig(); - revalidatePath("/settings"); - return { success: true, message: "Logging settings saved and applied successfully" }; - } catch (error) { - console.error("Failed to apply Caddy config:", error); - revalidatePath("/settings"); - const errorMsg = error instanceof Error ? error.message : "Unknown error"; - return { - success: true, - message: `Settings saved, but could not apply to Caddy: ${errorMsg}` - }; - } - } catch (error) { - console.error("Failed to save logging settings:", error); - return { success: false, message: error instanceof Error ? error.message : "Failed to save logging settings" }; - } -} diff --git a/app/(dashboard)/settings/page.tsx b/app/(dashboard)/settings/page.tsx index 37f96d2a..9e3e8b75 100644 --- a/app/(dashboard)/settings/page.tsx +++ b/app/(dashboard)/settings/page.tsx @@ -1,22 +1,15 @@ import SettingsClient from "./SettingsClient"; -import { - getAuthentikSettings, - getCloudflareSettings, - getGeneralSettings, - getLoggingSettings, - getMetricsSettings -} from "@/src/lib/settings"; +import { getCloudflareSettings, getGeneralSettings, getAuthentikSettings, getMetricsSettings } from "@/src/lib/settings"; import { requireAdmin } from "@/src/lib/auth"; export default async function SettingsPage() { await requireAdmin(); - const [general, cloudflare, authentik, metrics, logging] = await Promise.all([ + const [general, cloudflare, authentik, metrics] = await Promise.all([ getGeneralSettings(), getCloudflareSettings(), getAuthentikSettings(), - getMetricsSettings(), - getLoggingSettings() + getMetricsSettings() ]); return ( @@ -29,13 +22,6 @@ export default async function SettingsPage() { }} authentik={authentik} metrics={metrics} - logging={logging ? { - enabled: logging.enabled, - lokiUrl: logging.lokiUrl, - lokiUsername: logging.lokiUsername, - hasPassword: Boolean(logging.lokiPassword), - labels: logging.labels - } : null} /> ); } diff --git a/docker/caddy/Dockerfile b/docker/caddy/Dockerfile index 32cab599..577b1579 100644 --- a/docker/caddy/Dockerfile +++ b/docker/caddy/Dockerfile @@ -9,7 +9,6 @@ RUN go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest RUN xcaddy build \ --with github.com/caddy-dns/cloudflare \ --with github.com/mholt/caddy-l4 \ - --with github.com/trea/caddy-loki-logger \ --output /usr/bin/caddy FROM ubuntu:24.04 diff --git a/src/lib/caddy.ts b/src/lib/caddy.ts index cde61924..1f7b9cca 100644 --- a/src/lib/caddy.ts +++ b/src/lib/caddy.ts @@ -3,13 +3,7 @@ import { join } from "node:path"; import crypto from "node:crypto"; import db, { nowIso } from "./db"; import { config } from "./config"; -import { - getCloudflareSettings, - getGeneralSettings, - getLoggingSettings, - getMetricsSettings, - setSetting -} from "./settings"; +import { getCloudflareSettings, getGeneralSettings, getMetricsSettings, setSetting } from "./settings"; import { accessListEntries, certificates, @@ -788,72 +782,6 @@ async function buildTlsAutomation( }; } -function buildLoggingApp(loggingSettings: NonNullable>>) { - if (!loggingSettings.lokiUrl) { - return null; - } - - // Ensure Loki URL has the push endpoint path - let lokiUrl = loggingSettings.lokiUrl; - if (!lokiUrl.includes("/loki/api/v1/push")) { - // Remove trailing slash if present - lokiUrl = lokiUrl.replace(/\/$/, ""); - lokiUrl = `${lokiUrl}/loki/api/v1/push`; - } - - // Build Loki endpoint URL with basic auth if provided - let lokiEndpoint = lokiUrl; - if (loggingSettings.lokiUsername && loggingSettings.lokiPassword) { - // Embed basic auth in URL: https://user:pass@host/path - const urlObj = new URL(lokiUrl); - urlObj.username = loggingSettings.lokiUsername; - urlObj.password = loggingSettings.lokiPassword; - lokiEndpoint = urlObj.toString(); - } - - const lokiWriterConfig: Record = { - output: "loki", - endpoint: lokiEndpoint - }; - - // Add custom labels if provided - if (loggingSettings.labels && Object.keys(loggingSettings.labels).length > 0) { - lokiWriterConfig.label = loggingSettings.labels; - } - - return { - logs: { - // Access log for all HTTP requests - access: { - encoder: { - format: "json", - message_key: "msg", - level_key: "level", - time_key: "ts", - name_key: "logger" - }, - writer: lokiWriterConfig, - level: "INFO", - include: ["http.log.access.*"] - }, - // Default log for errors and other messages - default: { - encoder: { - format: "json", - message_key: "msg", - level_key: "level", - time_key: "ts", - name_key: "logger", - caller_key: "caller", - stacktrace_key: "stacktrace" - }, - writer: lokiWriterConfig, - level: "WARN" - } - } - }; -} - async function buildCaddyDocument() { const [proxyHostRecords, redirectHostRecords, deadHostRecords, certRows, accessListEntryRecords] = await Promise.all([ db @@ -1002,11 +930,6 @@ async function buildCaddyDocument() { const metricsEnabled = metricsSettings?.enabled ?? false; const metricsPort = metricsSettings?.port ?? 9090; - // Check if logging should be enabled - const loggingSettings = await getLoggingSettings(); - const loggingEnabled = loggingSettings?.enabled ?? false; - const loggingApp = loggingEnabled && loggingSettings ? buildLoggingApp(loggingSettings) : null; - const servers: Record = {}; // Main HTTP/HTTPS server for proxy hosts @@ -1017,9 +940,7 @@ async function buildCaddyDocument() { // Only disable automatic HTTPS if we have TLS automation policies // This allows Caddy to handle HTTP-01 challenges for managed certificates ...(tlsApp ? {} : { automatic_https: { disable: true } }), - ...(hasTls ? { tls_connection_policies: tlsConnectionPolicies } : {}), - // Enable access logging if configured - ...(loggingEnabled ? { logs: { default_logger_name: "access" } } : {}) + ...(hasTls ? { tls_connection_policies: tlsConnectionPolicies } : {}) }; } @@ -1049,7 +970,6 @@ async function buildCaddyDocument() { admin: { listen: "0.0.0.0:2019" }, - ...(loggingApp ? { logging: loggingApp } : {}), apps: { ...httpApp, ...(tlsApp ? { tls: tlsApp } : {}) diff --git a/src/lib/settings.ts b/src/lib/settings.ts index 094507bf..4c271d2e 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -26,14 +26,6 @@ export type MetricsSettings = { port?: number; // Port to expose metrics on (default: 9090, separate from admin API) }; -export type LoggingSettings = { - enabled: boolean; - lokiUrl?: string; // URL of Loki instance (e.g., http://loki:3100) - lokiUsername?: string; // Optional username for Loki authentication - lokiPassword?: string; // Optional password for Loki authentication - labels?: Record; // Optional custom labels for logs -}; - export async function getSetting(key: string): Promise> { const setting = await db.query.settings.findFirst({ where: (table, { eq }) => eq(table.key, key) @@ -102,11 +94,3 @@ export async function getMetricsSettings(): Promise { export async function saveMetricsSettings(settings: MetricsSettings): Promise { await setSetting("metrics", settings); } - -export async function getLoggingSettings(): Promise { - return await getSetting("logging"); -} - -export async function saveLoggingSettings(settings: LoggingSettings): Promise { - await setSetting("logging", settings); -}