fix: guard /select-org and /auth, give dev backend a lenient healthcheck

This commit is contained in:
Matthew Meszaros
2026-05-25 03:57:02 +00:00
parent 8e41c36c07
commit b94134bce7
3 changed files with 38 additions and 18 deletions
+15 -16
View File
@@ -32,11 +32,22 @@ services:
- .:/app
- warmbly_gomodcache:/go/pkg/mod
- warmbly_gocache:/root/.cache/go-build
# air's restart loop causes brief health flaps; depends_on can't
# tolerate that, so disable. Consumer / worker fall back to
# `service_started` below for the same reason.
# Relax the prod healthcheck instead of disabling it: dev needs to
# keep one because web, seed and the base consumer all depend on
# backend with `condition: service_healthy`, and compose refuses to
# bring up a service whose service_healthy target has no
# healthcheck.
#
# The bigger start_period gives air time to do a cold first compile
# (Go build cache empty in a fresh worktree → 60-90s isn't unusual).
# depends_on only gates initial startup, so the brief unhealthy
# blips on each subsequent rebuild don't bounce dependents.
healthcheck:
disable: true
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 10
start_period: 180s
consumer:
image: warmbly-go-dev
@@ -86,18 +97,6 @@ services:
healthcheck:
disable: true
# ─── web ──────────────────────────────────────────────────────────────
# web still runs the same image as `make up`; we only re-declare it to
# weaken the `depends_on: backend` from service_healthy → service_started.
# In dev mode backend's healthcheck is disabled (air flaps would defeat
# the gate), and compose refuses to bring up any service whose
# `service_healthy` target has no healthcheck. web is a Vite dev server,
# it doesn't reach out to backend at boot, so service_started is enough.
web:
depends_on:
backend:
condition: service_started
# ─── Elixir (realtime) ────────────────────────────────────────────────
realtime:
image: warmbly-elixir-dev
+10 -1
View File
@@ -1,6 +1,7 @@
import React from "react";
import { Outlet, useNavigate } from "react-router-dom";
import { Navigate, Outlet, useNavigate } from "react-router-dom";
import { APP_URL, WEBSITE_URL } from "@/lib/information";
import getToken from "@/lib/helper/getToken";
import { Logo } from "@/components/svg";
import { Mail, BarChart3, Zap, Shield, Github, ArrowRight } from "lucide-react";
@@ -110,6 +111,14 @@ export default function AuthLayout() {
return () => window.removeEventListener("message", receiveMessage);
}, [navigate]);
// Already signed in? Skip the auth UI entirely and send the user
// into the app. /app's own guard will redirect onward to
// /select-org or /onboarding if needed. Sits after hooks so the
// Rules of Hooks aren't violated when the token state changes.
if (getToken()) {
return <Navigate to="/app/emails" replace />;
}
return (
<div
className="fixed inset-0"
+13 -1
View File
@@ -16,9 +16,10 @@
// landing, no orgs" and "ongoing management" cases.
import React from "react";
import { useNavigate } from "react-router-dom";
import { Navigate, useNavigate } from "react-router-dom";
import { LogOutIcon, Loader2Icon, MailIcon, PlusIcon, UsersIcon } from "lucide-react";
import toast from "react-hot-toast";
import getToken from "@/lib/helper/getToken";
import useOrganizations from "@/lib/api/hooks/app/organizations/useOrganizations";
import useMyInvitations from "@/lib/api/hooks/app/organizations/useMyInvitations";
import useAcceptInvitation from "@/lib/api/hooks/app/organizations/useAcceptInvitation";
@@ -53,6 +54,17 @@ function initials(name: string): string {
}
export default function SelectOrgPage() {
// Guard: this page is the post-login workspace picker. Hitting it
// without a token (direct URL, expired session) bounces to login,
// matching the protection on /app/* and /onboarding.
if (!getToken()) {
return <Navigate to="/auth/login" replace />;
}
return <SelectOrgPageInner />;
}
function SelectOrgPageInner() {
const navigate = useNavigate();
const [createOpen, setCreateOpen] = React.useState(false);