undo loki stuff

This commit is contained in:
fuomag9
2025-11-12 23:22:30 +01:00
parent fa65dc1b2c
commit 58ec7e321e
6 changed files with 8 additions and 282 deletions

View File

@@ -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<string, string>;
} | 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
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="h6" fontWeight={600} gutterBottom>
Logging
</Typography>
<Typography color="text.secondary" variant="body2" sx={{ mb: 2 }}>
Enable comprehensive request logging to Loki for debugging and monitoring.
You must deploy your own Loki instance and provide its URL.
</Typography>
{logging?.hasPassword && (
<Alert severity="info" sx={{ mb: 2 }}>
A Loki password is already configured. Leave the password field blank to keep it, or enter a new password to update it.
</Alert>
)}
<Stack component="form" action={loggingFormAction} spacing={2}>
{loggingState?.message && (
<Alert severity={loggingState.success ? "success" : "error"}>
{loggingState.message}
</Alert>
)}
<FormControlLabel
control={<Checkbox name="enabled" defaultChecked={logging?.enabled ?? false} />}
label="Enable request logging"
/>
<TextField
name="lokiUrl"
label="Loki URL"
defaultValue={logging?.lokiUrl ?? ""}
placeholder="http://loki:3100"
helperText="Base URL of your Loki instance (e.g., http://loki:3100). The /loki/api/v1/push path will be added automatically."
fullWidth
/>
<TextField
name="lokiUsername"
label="Loki Username (optional)"
defaultValue={logging?.lokiUsername ?? ""}
helperText="Leave empty if your Loki instance doesn't require authentication"
fullWidth
/>
<TextField
name="lokiPassword"
label="Loki Password (optional)"
type="password"
autoComplete="new-password"
placeholder={logging?.hasPassword ? "Enter new password to update" : "Enter password"}
helperText="Leave empty to keep existing password, or enter new password to update"
fullWidth
/>
<FormControlLabel
control={<Checkbox name="clearPassword" />}
label="Remove existing password"
disabled={!logging?.hasPassword}
/>
<TextField
name="labels"
label="Custom Labels (optional)"
defaultValue={logging?.labels ? JSON.stringify(logging.labels) : ""}
placeholder='{"environment":"production","service":"caddy"}'
helperText="Optional JSON object of custom labels to add to logs"
fullWidth
multiline
rows={2}
/>
<Alert severity="info">
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.
</Alert>
<Box sx={{ display: "flex", justifyContent: "flex-end" }}>
<Button type="submit" variant="contained">
Save logging settings
</Button>
</Box>
</Stack>
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="h6" fontWeight={600} gutterBottom>

View File

@@ -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<ActionResult> {
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<string, string> | 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" };
}
}

View File

@@ -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}
/>
);
}

View File

@@ -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

View File

@@ -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<Awaited<ReturnType<typeof getLoggingSettings>>>) {
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<string, unknown> = {
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<string, unknown> = {};
// 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 } : {})

View File

@@ -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<string, string>; // Optional custom labels for logs
};
export async function getSetting<T>(key: string): Promise<SettingValue<T>> {
const setting = await db.query.settings.findFirst({
where: (table, { eq }) => eq(table.key, key)
@@ -102,11 +94,3 @@ export async function getMetricsSettings(): Promise<MetricsSettings | null> {
export async function saveMetricsSettings(settings: MetricsSettings): Promise<void> {
await setSetting("metrics", settings);
}
export async function getLoggingSettings(): Promise<LoggingSettings | null> {
return await getSetting<LoggingSettings>("logging");
}
export async function saveLoggingSettings(settings: LoggingSettings): Promise<void> {
await setSetting("logging", settings);
}