The static response feature has been completely removed
This commit is contained in:
@@ -84,16 +84,10 @@ export default function ProxyHostsClient({ hosts, certificates, accessLists, aut
|
||||
id: "upstreams",
|
||||
label: "Target",
|
||||
render: (host: ProxyHost) => (
|
||||
host.response_mode === "static" ? (
|
||||
<Typography variant="body2" color="warning.main" sx={{ fontStyle: 'italic' }}>
|
||||
Static Response ({host.static_status_code ?? 503})
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
||||
{host.upstreams[0]}
|
||||
{host.upstreams.length > 1 && ` +${host.upstreams.length - 1} more`}
|
||||
</Typography>
|
||||
)
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
||||
{host.upstreams[0]}
|
||||
{host.upstreams.length > 1 && ` +${host.upstreams.length - 1} more`}
|
||||
</Typography>
|
||||
)
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { requireAdmin } from "@/src/lib/auth";
|
||||
import { actionError, actionSuccess, INITIAL_ACTION_STATE, type ActionState } from "@/src/lib/actions";
|
||||
import { createProxyHost, deleteProxyHost, updateProxyHost, type ProxyHostAuthentikInput, type LoadBalancerInput, type LoadBalancingPolicy, type DnsResolverInput, type ResponseMode } from "@/src/lib/models/proxy-hosts";
|
||||
import { createProxyHost, deleteProxyHost, updateProxyHost, type ProxyHostAuthentikInput, type LoadBalancerInput, type LoadBalancingPolicy, type DnsResolverInput } from "@/src/lib/models/proxy-hosts";
|
||||
import { getCertificate } from "@/src/lib/models/certificates";
|
||||
import { getCloudflareSettings } from "@/src/lib/settings";
|
||||
|
||||
@@ -270,28 +270,6 @@ function parseLoadBalancerConfig(formData: FormData): LoadBalancerInput | undefi
|
||||
return Object.keys(result).length > 0 ? result : undefined;
|
||||
}
|
||||
|
||||
function parseResponseMode(value: FormDataEntryValue | null): ResponseMode {
|
||||
if (!value || typeof value !== "string") {
|
||||
return "proxy";
|
||||
}
|
||||
return value === "static" ? "static" : "proxy";
|
||||
}
|
||||
|
||||
function parseStatusCode(value: FormDataEntryValue | null): number | null {
|
||||
if (!value || typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (trimmed === "") {
|
||||
return null;
|
||||
}
|
||||
const num = parseInt(trimmed, 10);
|
||||
if (!Number.isFinite(num) || num < 100 || num > 599) {
|
||||
return 200;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
function parseDnsResolverConfig(formData: FormData): DnsResolverInput | undefined {
|
||||
if (!formData.has("dns_present")) {
|
||||
return undefined;
|
||||
@@ -370,8 +348,6 @@ export async function createProxyHostAction(
|
||||
console.warn(`[createProxyHostAction] ${warning}`);
|
||||
}
|
||||
|
||||
const responseMode = parseResponseMode(formData.get("response_mode"));
|
||||
|
||||
await createProxyHost(
|
||||
{
|
||||
name: String(formData.get("name") ?? "Untitled"),
|
||||
@@ -386,10 +362,7 @@ export async function createProxyHostAction(
|
||||
custom_reverse_proxy_json: parseOptionalText(formData.get("custom_reverse_proxy_json")),
|
||||
authentik: parseAuthentikConfig(formData),
|
||||
load_balancer: parseLoadBalancerConfig(formData),
|
||||
dns_resolver: parseDnsResolverConfig(formData),
|
||||
response_mode: responseMode,
|
||||
static_status_code: parseStatusCode(formData.get("static_status_code")),
|
||||
static_response_body: parseOptionalText(formData.get("static_response_body"))
|
||||
dns_resolver: parseDnsResolverConfig(formData)
|
||||
},
|
||||
userId
|
||||
);
|
||||
@@ -437,8 +410,6 @@ export async function updateProxyHostAction(
|
||||
}
|
||||
}
|
||||
|
||||
const responseMode = formData.has("response_mode") ? parseResponseMode(formData.get("response_mode")) : undefined;
|
||||
|
||||
await updateProxyHost(
|
||||
id,
|
||||
{
|
||||
@@ -460,10 +431,7 @@ export async function updateProxyHostAction(
|
||||
: undefined,
|
||||
authentik: parseAuthentikConfig(formData),
|
||||
load_balancer: parseLoadBalancerConfig(formData),
|
||||
dns_resolver: parseDnsResolverConfig(formData),
|
||||
response_mode: responseMode,
|
||||
static_status_code: formData.has("static_status_code") ? parseStatusCode(formData.get("static_status_code")) : undefined,
|
||||
static_response_body: formData.has("static_response_body") ? parseOptionalText(formData.get("static_response_body")) : undefined
|
||||
dns_resolver: parseDnsResolverConfig(formData)
|
||||
},
|
||||
userId
|
||||
);
|
||||
|
||||
4
drizzle/0005_remove_static_response.sql
Normal file
4
drizzle/0005_remove_static_response.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- Remove static response feature columns from proxy_hosts
|
||||
ALTER TABLE `proxy_hosts` DROP COLUMN `response_mode`;
|
||||
ALTER TABLE `proxy_hosts` DROP COLUMN `static_status_code`;
|
||||
ALTER TABLE `proxy_hosts` DROP COLUMN `static_response_body`;
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { Alert, Box, FormControl, FormControlLabel, FormLabel, MenuItem, Radio, RadioGroup, Stack, TextField, Typography } from "@mui/material";
|
||||
import { Alert, Box, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
||||
import { useFormState } from "react-dom";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect } from "react";
|
||||
import {
|
||||
createProxyHostAction,
|
||||
deleteProxyHostAction,
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { INITIAL_ACTION_STATE } from "@/src/lib/actions";
|
||||
import { AccessList } from "@/src/lib/models/access-lists";
|
||||
import { Certificate } from "@/src/lib/models/certificates";
|
||||
import { ProxyHost, ResponseMode } from "@/src/lib/models/proxy-hosts";
|
||||
import { ProxyHost } from "@/src/lib/models/proxy-hosts";
|
||||
import { AuthentikSettings } from "@/src/lib/settings";
|
||||
import { AppDialog } from "@/src/components/ui/AppDialog";
|
||||
import { AuthentikFields } from "./AuthentikFields";
|
||||
@@ -35,7 +35,6 @@ export function CreateHostDialog({
|
||||
initialData?: ProxyHost | null;
|
||||
}) {
|
||||
const [state, formAction] = useFormState(createProxyHostAction, INITIAL_ACTION_STATE);
|
||||
const [responseMode, setResponseMode] = useState<ResponseMode>(initialData?.response_mode ?? "proxy");
|
||||
|
||||
useEffect(() => {
|
||||
if (state.status === "success") {
|
||||
@@ -43,13 +42,6 @@ export function CreateHostDialog({
|
||||
}
|
||||
}, [state.status, onClose]);
|
||||
|
||||
// Reset response mode when dialog opens/closes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setResponseMode(initialData?.response_mode ?? "proxy");
|
||||
}
|
||||
}, [open, initialData]);
|
||||
|
||||
return (
|
||||
<AppDialog
|
||||
open={open}
|
||||
@@ -92,43 +84,7 @@ export function CreateHostDialog({
|
||||
required
|
||||
fullWidth
|
||||
/>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">Response Mode</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
name="response_mode"
|
||||
value={responseMode}
|
||||
onChange={(e) => setResponseMode(e.target.value as ResponseMode)}
|
||||
>
|
||||
<FormControlLabel value="proxy" control={<Radio />} label="Proxy to Upstream" />
|
||||
<FormControlLabel value="static" control={<Radio />} label="Static Response" />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
{responseMode === "proxy" && (
|
||||
<UpstreamInput defaultUpstreams={initialData?.upstreams} />
|
||||
)}
|
||||
{responseMode === "static" && (
|
||||
<>
|
||||
<TextField
|
||||
name="static_status_code"
|
||||
label="Status Code"
|
||||
type="number"
|
||||
defaultValue={initialData?.static_status_code ?? 200}
|
||||
helperText="HTTP status code to return (e.g., 200 for OK, 503 for Service Unavailable)"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
name="static_response_body"
|
||||
label="Response Body"
|
||||
placeholder=""
|
||||
defaultValue={initialData?.static_response_body ?? ""}
|
||||
helperText="Optional body text to return in the response"
|
||||
multiline
|
||||
minRows={3}
|
||||
fullWidth
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<UpstreamInput defaultUpstreams={initialData?.upstreams} />
|
||||
<TextField select name="certificate_id" label="Certificate" defaultValue={initialData?.certificate_id ?? ""} fullWidth>
|
||||
<MenuItem value="">Managed by Caddy (Auto)</MenuItem>
|
||||
{certificates.map((cert) => (
|
||||
@@ -187,7 +143,6 @@ export function EditHostDialog({
|
||||
accessLists: AccessList[];
|
||||
}) {
|
||||
const [state, formAction] = useFormState(updateProxyHostAction.bind(null, host.id), INITIAL_ACTION_STATE);
|
||||
const [responseMode, setResponseMode] = useState<ResponseMode>(host.response_mode);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.status === "success") {
|
||||
@@ -195,11 +150,6 @@ export function EditHostDialog({
|
||||
}
|
||||
}, [state.status, onClose]);
|
||||
|
||||
// Reset response mode when host changes
|
||||
useEffect(() => {
|
||||
setResponseMode(host.response_mode);
|
||||
}, [host]);
|
||||
|
||||
return (
|
||||
<AppDialog
|
||||
open={open}
|
||||
@@ -232,43 +182,7 @@ export function EditHostDialog({
|
||||
minRows={2}
|
||||
fullWidth
|
||||
/>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">Response Mode</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
name="response_mode"
|
||||
value={responseMode}
|
||||
onChange={(e) => setResponseMode(e.target.value as ResponseMode)}
|
||||
>
|
||||
<FormControlLabel value="proxy" control={<Radio />} label="Proxy to Upstream" />
|
||||
<FormControlLabel value="static" control={<Radio />} label="Static Response" />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
{responseMode === "proxy" && (
|
||||
<UpstreamInput defaultUpstreams={host.upstreams} />
|
||||
)}
|
||||
{responseMode === "static" && (
|
||||
<>
|
||||
<TextField
|
||||
name="static_status_code"
|
||||
label="Status Code"
|
||||
type="number"
|
||||
defaultValue={host.static_status_code ?? 200}
|
||||
helperText="HTTP status code to return (e.g., 200 for OK, 503 for Service Unavailable)"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
name="static_response_body"
|
||||
label="Response Body"
|
||||
placeholder=""
|
||||
defaultValue={host.static_response_body ?? ""}
|
||||
helperText="Optional body text to return in the response"
|
||||
multiline
|
||||
minRows={3}
|
||||
fullWidth
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<UpstreamInput defaultUpstreams={host.upstreams} />
|
||||
<TextField select name="certificate_id" label="Certificate" defaultValue={host.certificate_id ?? ""} fullWidth>
|
||||
<MenuItem value="">Managed by Caddy (Auto)</MenuItem>
|
||||
{certificates.map((cert) => (
|
||||
|
||||
@@ -47,9 +47,6 @@ type ProxyHostRow = {
|
||||
skip_https_hostname_validation: number;
|
||||
meta: string | null;
|
||||
enabled: number;
|
||||
response_mode: string;
|
||||
static_status_code: number | null;
|
||||
static_response_body: string | null;
|
||||
};
|
||||
|
||||
type DnsResolverMeta = {
|
||||
@@ -326,58 +323,7 @@ function buildProxyRoutes(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle static response mode
|
||||
if (row.response_mode === "static") {
|
||||
const staticHandlers: Record<string, unknown>[] = [];
|
||||
|
||||
// Build static response handler
|
||||
const staticResponseHandler: Record<string, unknown> = {
|
||||
handler: "static_response",
|
||||
status_code: row.static_status_code ?? 200,
|
||||
body: row.static_response_body ?? ""
|
||||
};
|
||||
|
||||
// Add HSTS header if enabled
|
||||
if (row.hsts_enabled) {
|
||||
const hstsValue = row.hsts_subdomains ? "max-age=63072000; includeSubDomains" : "max-age=63072000";
|
||||
staticResponseHandler.headers = {
|
||||
"Strict-Transport-Security": [hstsValue]
|
||||
};
|
||||
}
|
||||
|
||||
staticHandlers.push(staticResponseHandler);
|
||||
|
||||
// SSL redirect for static responses
|
||||
if (row.ssl_forced) {
|
||||
routes.push({
|
||||
match: [
|
||||
{
|
||||
host: domains,
|
||||
expression: '{http.request.scheme} == "http"'
|
||||
}
|
||||
],
|
||||
handle: [
|
||||
{
|
||||
handler: "static_response",
|
||||
status_code: 308,
|
||||
headers: {
|
||||
Location: ["https://{http.request.host}{http.request.uri}"]
|
||||
}
|
||||
}
|
||||
],
|
||||
terminal: true
|
||||
});
|
||||
}
|
||||
|
||||
routes.push({
|
||||
match: [{ host: domains }],
|
||||
handle: staticHandlers,
|
||||
terminal: true
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Proxy mode: require upstreams
|
||||
// Require upstreams
|
||||
const upstreams = parseJson<string[]>(row.upstreams, []);
|
||||
if (upstreams.length === 0) {
|
||||
continue;
|
||||
@@ -977,10 +923,7 @@ async function buildCaddyDocument() {
|
||||
preserveHostHeader: proxyHosts.preserveHostHeader,
|
||||
skipHttpsHostnameValidation: proxyHosts.skipHttpsHostnameValidation,
|
||||
meta: proxyHosts.meta,
|
||||
enabled: proxyHosts.enabled,
|
||||
responseMode: proxyHosts.responseMode,
|
||||
staticStatusCode: proxyHosts.staticStatusCode,
|
||||
staticResponseBody: proxyHosts.staticResponseBody
|
||||
enabled: proxyHosts.enabled
|
||||
})
|
||||
.from(proxyHosts),
|
||||
db
|
||||
@@ -1029,10 +972,7 @@ async function buildCaddyDocument() {
|
||||
preserve_host_header: h.preserveHostHeader ? 1 : 0,
|
||||
skip_https_hostname_validation: h.skipHttpsHostnameValidation ? 1 : 0,
|
||||
meta: h.meta,
|
||||
enabled: h.enabled ? 1 : 0,
|
||||
response_mode: h.responseMode,
|
||||
static_status_code: h.staticStatusCode,
|
||||
static_response_body: h.staticResponseBody
|
||||
enabled: h.enabled ? 1 : 0
|
||||
}));
|
||||
|
||||
const redirectHostRows: RedirectHostRow[] = redirectHostRecords.map((h) => ({
|
||||
|
||||
@@ -147,12 +147,7 @@ export const proxyHosts = sqliteTable("proxy_hosts", {
|
||||
updatedAt: text("updated_at").notNull(),
|
||||
skipHttpsHostnameValidation: integer("skip_https_hostname_validation", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
// Response mode: 'proxy' (default) or 'static'
|
||||
responseMode: text("response_mode").notNull().default("proxy"),
|
||||
// Static response fields (used when responseMode is 'static')
|
||||
staticStatusCode: integer("static_status_code").default(200),
|
||||
staticResponseBody: text("static_response_body")
|
||||
.default(false)
|
||||
});
|
||||
|
||||
export const redirectHosts = sqliteTable("redirect_hosts", {
|
||||
|
||||
@@ -176,8 +176,6 @@ type ProxyHostMeta = {
|
||||
dns_resolver?: DnsResolverMeta;
|
||||
};
|
||||
|
||||
export type ResponseMode = "proxy" | "static";
|
||||
|
||||
export type ProxyHost = {
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -199,15 +197,12 @@ export type ProxyHost = {
|
||||
authentik: ProxyHostAuthentikConfig | null;
|
||||
load_balancer: LoadBalancerConfig | null;
|
||||
dns_resolver: DnsResolverConfig | null;
|
||||
response_mode: ResponseMode;
|
||||
static_status_code: number | null;
|
||||
static_response_body: string | null;
|
||||
};
|
||||
|
||||
export type ProxyHostInput = {
|
||||
name: string;
|
||||
domains: string[];
|
||||
upstreams?: string[];
|
||||
upstreams: string[];
|
||||
certificate_id?: number | null;
|
||||
access_list_id?: number | null;
|
||||
ssl_forced?: boolean;
|
||||
@@ -222,9 +217,6 @@ export type ProxyHostInput = {
|
||||
authentik?: ProxyHostAuthentikInput | null;
|
||||
load_balancer?: LoadBalancerInput | null;
|
||||
dns_resolver?: DnsResolverInput | null;
|
||||
response_mode?: ResponseMode;
|
||||
static_status_code?: number | null;
|
||||
static_response_body?: string | null;
|
||||
};
|
||||
|
||||
type ProxyHostRow = typeof proxyHosts.$inferSelect;
|
||||
@@ -1141,7 +1133,6 @@ function dehydrateDnsResolver(config: DnsResolverConfig | null): DnsResolverMeta
|
||||
|
||||
function parseProxyHost(row: ProxyHostRow): ProxyHost {
|
||||
const meta = parseMeta(row.meta ?? null);
|
||||
const responseMode = row.responseMode === "static" ? "static" : "proxy";
|
||||
return {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
@@ -1162,10 +1153,7 @@ function parseProxyHost(row: ProxyHostRow): ProxyHost {
|
||||
custom_pre_handlers_json: meta.custom_pre_handlers_json ?? null,
|
||||
authentik: hydrateAuthentik(meta.authentik),
|
||||
load_balancer: hydrateLoadBalancer(meta.load_balancer),
|
||||
dns_resolver: hydrateDnsResolver(meta.dns_resolver),
|
||||
response_mode: responseMode,
|
||||
static_status_code: row.staticStatusCode ?? null,
|
||||
static_response_body: row.staticResponseBody ?? null
|
||||
dns_resolver: hydrateDnsResolver(meta.dns_resolver)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1179,22 +1167,18 @@ export async function createProxyHost(input: ProxyHostInput, actorUserId: number
|
||||
throw new Error("At least one domain must be specified");
|
||||
}
|
||||
|
||||
const responseMode = input.response_mode === "static" ? "static" : "proxy";
|
||||
|
||||
// Only require upstreams in proxy mode
|
||||
if (responseMode === "proxy" && (!input.upstreams || input.upstreams.length === 0)) {
|
||||
throw new Error("At least one upstream must be specified for proxy mode");
|
||||
if (!input.upstreams || input.upstreams.length === 0) {
|
||||
throw new Error("At least one upstream must be specified");
|
||||
}
|
||||
|
||||
const now = nowIso();
|
||||
const meta = buildMeta({}, input);
|
||||
const upstreams = input.upstreams ?? [];
|
||||
const [record] = await db
|
||||
.insert(proxyHosts)
|
||||
.values({
|
||||
name: input.name.trim(),
|
||||
domains: JSON.stringify(Array.from(new Set(input.domains.map((d) => d.trim().toLowerCase())))),
|
||||
upstreams: JSON.stringify(Array.from(new Set(upstreams.map((u) => u.trim())))),
|
||||
upstreams: JSON.stringify(Array.from(new Set(input.upstreams.map((u) => u.trim())))),
|
||||
certificateId: input.certificate_id ?? null,
|
||||
accessListId: input.access_list_id ?? null,
|
||||
ownerUserId: actorUserId,
|
||||
@@ -1207,10 +1191,7 @@ export async function createProxyHost(input: ProxyHostInput, actorUserId: number
|
||||
skipHttpsHostnameValidation: input.skip_https_hostname_validation ?? false,
|
||||
enabled: input.enabled ?? true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
responseMode,
|
||||
staticStatusCode: input.static_status_code ?? 200,
|
||||
staticResponseBody: input.static_response_body ?? null
|
||||
updatedAt: now
|
||||
})
|
||||
.returning();
|
||||
|
||||
@@ -1244,10 +1225,6 @@ export async function updateProxyHost(id: number, input: Partial<ProxyHostInput>
|
||||
throw new Error("Proxy host not found");
|
||||
}
|
||||
|
||||
const responseMode = input.response_mode !== undefined
|
||||
? (input.response_mode === "static" ? "static" : "proxy")
|
||||
: existing.response_mode;
|
||||
|
||||
const domains = input.domains ? JSON.stringify(Array.from(new Set(input.domains))) : JSON.stringify(existing.domains);
|
||||
const upstreams = input.upstreams ? JSON.stringify(Array.from(new Set(input.upstreams))) : JSON.stringify(existing.upstreams);
|
||||
const existingMeta: ProxyHostMeta = {
|
||||
@@ -1276,10 +1253,7 @@ export async function updateProxyHost(id: number, input: Partial<ProxyHostInput>
|
||||
meta,
|
||||
skipHttpsHostnameValidation: input.skip_https_hostname_validation ?? existing.skip_https_hostname_validation,
|
||||
enabled: input.enabled ?? existing.enabled,
|
||||
updatedAt: now,
|
||||
responseMode,
|
||||
staticStatusCode: input.static_status_code !== undefined ? input.static_status_code : existing.static_status_code,
|
||||
staticResponseBody: input.static_response_body !== undefined ? input.static_response_body : existing.static_response_body
|
||||
updatedAt: now
|
||||
})
|
||||
.where(eq(proxyHosts.id, id));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user