Merge branch 'main' into dev

This commit is contained in:
miloschwartz
2026-07-29 17:09:54 -04:00
4 changed files with 16 additions and 6 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ services:
- 80:80 # Port for traefik because of the network_mode - 80:80 # Port for traefik because of the network_mode
traefik: traefik:
image: traefik:v3.6 image: traefik:v3.7
container_name: traefik container_name: traefik
restart: unless-stopped restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service network_mode: service:gerbil # Ports appear on the gerbil service
+1 -1
View File
@@ -50,7 +50,7 @@ services:
- 80:80{{end}} - 80:80{{end}}
traefik: traefik:
image: docker.io/traefik:v3.6 image: docker.io/traefik:v3.7
container_name: traefik container_name: traefik
restart: unless-stopped restart: unless-stopped
{{if .InstallGerbil}}network_mode: service:gerbil # Ports appear on the gerbil service{{end}}{{if not .InstallGerbil}} {{if .InstallGerbil}}network_mode: service:gerbil # Ports appear on the gerbil service{{end}}{{if not .InstallGerbil}}
+6 -1
View File
@@ -1397,6 +1397,12 @@ authenticated.put(
// Auth routes // Auth routes
export const authRouter = Router(); export const authRouter = Router();
unauthenticated.use("/auth", authRouter); unauthenticated.use("/auth", authRouter);
// Register setup-check BEFORE the global auth rate limiter.
// This endpoint is called on every dashboard root page load (pure boolean
// read, no secrets) and must not consume the auth rate-limit budget.
authRouter.get("/initial-setup-complete", auth.initialSetupComplete);
authRouter.use( authRouter.use(
rateLimit({ rateLimit({
windowMs: windowMs:
@@ -1701,7 +1707,6 @@ authRouter.post("/idp/:idpId/oidc/generate-url", idp.generateOidcUrl);
authRouter.post("/idp/:idpId/oidc/validate-callback", idp.validateOidcCallback); authRouter.post("/idp/:idpId/oidc/validate-callback", idp.validateOidcCallback);
authRouter.put("/set-server-admin", auth.setServerAdmin); authRouter.put("/set-server-admin", auth.setServerAdmin);
authRouter.get("/initial-setup-complete", auth.initialSetupComplete);
authRouter.post("/validate-setup-token", auth.validateSetupToken); authRouter.post("/validate-setup-token", auth.validateSetupToken);
// Security Key routes // Security Key routes
+8 -3
View File
@@ -29,14 +29,19 @@ export default async function Page(props: {
const user = await verifySession({ skipCheckVerifyEmail: true }); const user = await verifySession({ skipCheckVerifyEmail: true });
let complete = false; let complete: boolean | null = null; // null means "unknown" (request errored)
try { try {
const setupRes = await internal.get< const setupRes = await internal.get<
AxiosResponse<InitialSetupCompleteResponse> AxiosResponse<InitialSetupCompleteResponse>
>(`/auth/initial-setup-complete`, await authCookieHeader()); >(`/auth/initial-setup-complete`, await authCookieHeader());
complete = setupRes.data.data.complete; complete = setupRes.data.data.complete;
} catch (e) {} } catch (e) {
if (!complete) { // Swallow errors (e.g. 429 rate limit, 500, network failure).
// Only redirect to initial-setup when the server *confirms* setup
// is incomplete (complete === false). If the request itself failed we
// cannot tell, so fall through to the login redirect instead.
}
if (complete === false) {
redirect("/auth/initial-setup"); redirect("/auth/initial-setup");
} }