fix: IMAP-Serveradresse dynamisch aus Backend laden

Hardcodierte 192.168.1.131 in settings/page.tsx ersetzt durch
dynamischen API-Call GET /api/system/info → {fqdn, imap_port}.
Fallback auf window.location.hostname wenn API nicht antwortet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-04-06 11:06:58 +02:00
parent 3b05e949dd
commit 2a91f6e249
5 changed files with 70 additions and 4 deletions
+1
View File
@@ -191,6 +191,7 @@ func (s *Server) routes() {
s.mux.HandleFunc("GET /api/health", s.handleHealth) s.mux.HandleFunc("GET /api/health", s.handleHealth)
s.mux.HandleFunc("GET /metrics", s.handleMetrics) s.mux.HandleFunc("GET /metrics", s.handleMetrics)
s.mux.HandleFunc("GET /api/version", s.handleVersion) s.mux.HandleFunc("GET /api/version", s.handleVersion)
s.mux.HandleFunc("GET /api/system/info", s.auth(s.handleSystemInfo))
s.mux.HandleFunc("POST /api/auth/login", s.handleLogin) s.mux.HandleFunc("POST /api/auth/login", s.handleLogin)
s.mux.HandleFunc("GET /api/auth/me", s.auth(s.handleMe)) s.mux.HandleFunc("GET /api/auth/me", s.auth(s.handleMe))
s.mux.HandleFunc("POST /api/auth/logout", s.auth(s.handleLogout)) s.mux.HandleFunc("POST /api/auth/logout", s.auth(s.handleLogout))
+18
View File
@@ -0,0 +1,18 @@
package api
import "net/http"
// handleSystemInfo liefert öffentliche System-Verbindungsdaten
// (FQDN, IMAP-Port). Wird im Frontend für die IMAP-Zugang-Karte
// auf der Settings-Seite verwendet.
//
// GET /api/system/info — auth required (Endpoint zeigt nur generische,
// nicht-sensitive Verbindungsdaten an, ist aber an Login gebunden, um
// öffentliches Profiling der Installation zu vermeiden).
func (s *Server) handleSystemInfo(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]interface{}{
"fqdn": s.fqdn,
"imap_port": 9993,
"imap_port_alt": 993,
})
}
+39 -4
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useState } from "react";
import { useAuth } from "@/hooks/useAuth"; import { useAuth } from "@/hooks/useAuth";
import { Navbar } from "@/components/navbar"; import { Navbar } from "@/components/navbar";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
@@ -23,6 +23,8 @@ import {
getTOTPSetup, getTOTPSetup,
confirmTOTPSetup, confirmTOTPSetup,
disableTOTP, disableTOTP,
getSystemInfo,
type SystemInfo,
} from "@/lib/api"; } from "@/lib/api";
export default function SettingsPage() { export default function SettingsPage() {
@@ -57,6 +59,28 @@ export default function SettingsPage() {
const [disableError, setDisableError] = useState(""); const [disableError, setDisableError] = useState("");
const [disableLoading, setDisableLoading] = useState(false); const [disableLoading, setDisableLoading] = useState(false);
// ── System info (FQDN + IMAP-Ports) ───────────────────────────────────
const [systemInfo, setSystemInfo] = useState<SystemInfo | null>(null);
const [systemInfoLoading, setSystemInfoLoading] = useState(true);
useEffect(() => {
let cancelled = false;
setSystemInfoLoading(true);
getSystemInfo()
.then((info) => {
if (!cancelled) setSystemInfo(info);
})
.catch(() => {
if (!cancelled) setSystemInfo(null);
})
.finally(() => {
if (!cancelled) setSystemInfoLoading(false);
});
return () => {
cancelled = true;
};
}, []);
// Initialize email from user data once available // Initialize email from user data once available
if (user && !emailInitialized) { if (user && !emailInitialized) {
setEmail(user.email || ""); setEmail(user.email || "");
@@ -488,19 +512,30 @@ export default function SettingsPage() {
<Server className="h-3.5 w-3.5" aria-hidden="true" /> <Server className="h-3.5 w-3.5" aria-hidden="true" />
Server Server
</span> </span>
<span className="font-mono">192.168.1.131</span> <span className="font-mono">
{systemInfoLoading
? "Laden..."
: systemInfo?.fqdn ||
(typeof window !== "undefined"
? window.location.hostname
: "")}
</span>
<span className="text-muted-foreground flex items-center gap-1.5"> <span className="text-muted-foreground flex items-center gap-1.5">
<Lock className="h-3.5 w-3.5" aria-hidden="true" /> <Lock className="h-3.5 w-3.5" aria-hidden="true" />
IMAP-Port IMAP-Port
</span> </span>
<span className="font-mono">9993 (SSL/TLS)</span> <span className="font-mono">
{systemInfo?.imap_port ?? 9993} (SSL/TLS)
</span>
<span className="text-muted-foreground flex items-center gap-1.5"> <span className="text-muted-foreground flex items-center gap-1.5">
<Lock className="h-3.5 w-3.5" aria-hidden="true" /> <Lock className="h-3.5 w-3.5" aria-hidden="true" />
Alternativ-Port Alternativ-Port
</span> </span>
<span className="font-mono">993 (SSL/TLS)</span> <span className="font-mono">
{systemInfo?.imap_port_alt ?? 993} (SSL/TLS)
</span>
<span className="text-muted-foreground flex items-center gap-1.5"> <span className="text-muted-foreground flex items-center gap-1.5">
<Lock className="h-3.5 w-3.5" aria-hidden="true" /> <Lock className="h-3.5 w-3.5" aria-hidden="true" />
+2
View File
@@ -121,6 +121,7 @@ export {
export type { export type {
HealthResponse, HealthResponse,
SystemInfo,
SMTPStatus, SMTPStatus,
StorageStats, StorageStats,
ServiceStatus, ServiceStatus,
@@ -142,6 +143,7 @@ export type {
} from "./system"; } from "./system";
export { export {
getHealth, getHealth,
getSystemInfo,
getSMTPStatus, getSMTPStatus,
getStorageStats, getStorageStats,
getSystemStats, getSystemStats,
+10
View File
@@ -6,6 +6,12 @@ export interface HealthResponse {
status: string; status: string;
} }
export interface SystemInfo {
fqdn: string;
imap_port: number;
imap_port_alt: number;
}
export interface SMTPStatus { export interface SMTPStatus {
// global daemon fields (superadmin) // global daemon fields (superadmin)
running?: boolean; running?: boolean;
@@ -157,6 +163,10 @@ export async function getHealth(): Promise<HealthResponse> {
return request<HealthResponse>("/api/health"); return request<HealthResponse>("/api/health");
} }
export async function getSystemInfo(): Promise<SystemInfo> {
return request<SystemInfo>("/api/system/info");
}
export async function getSMTPStatus(): Promise<SMTPStatus> { export async function getSMTPStatus(): Promise<SMTPStatus> {
return request<SMTPStatus>("/api/admin/smtp/status"); return request<SMTPStatus>("/api/admin/smtp/status");
} }