Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .claude/rules/server-apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ area: webapp
type: fix
---
Fix pages occasionally loading unstyled during deploys. The dashboard now recovers automatically.
Brief description of what changed and why.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the required user-facing release-note format here.

This placeholder still only asks for a “brief description,” so it does not enforce the PR’s intended 1–2 short, non-technical sentences for dashboard users. Since the body is published verbatim, make the example explicitly state those constraints.

Suggested wording
-Brief description of what changed and why.
+Write 1–2 short, non-technical sentences for dashboard users; this text is copied verbatim into user-facing release notes.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Brief description of what changed and why.
Write 1–2 short, non-technical sentences for dashboard users; this text is copied verbatim into user-facing release notes.

EOF
```

- **area**: `webapp` | `supervisor`
- **type**: `feature` | `fix` | `improvement` | `breaking`
- If the PR also touches `packages/`, just the changeset is sufficient (no `.server-changes/` needed).

The body ships **verbatim in user-facing release notes**. Keep it to 1–2 short sentences, non-technical, written for a dashboard user: describe what changed for them, never the implementation (no header names, endpoints, middleware, storage mechanisms, internal tools). See `.server-changes/README.md` for full guidance.
6 changes: 0 additions & 6 deletions .server-changes/stale-deploy-asset-recovery.md

This file was deleted.

272 changes: 0 additions & 272 deletions apps/webapp/app/components/StaleAssetRecovery.tsx

This file was deleted.

6 changes: 0 additions & 6 deletions apps/webapp/app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,6 @@ export default function handleRequest(
) {
const url = new URL(request.url);

// Stale documents reference /build asset hashes that 404 after a deploy —
// always revalidate HTML. Route-set headers win.
if (!responseHeaders.has("Cache-Control")) {
responseHeaders.set("Cache-Control", "no-cache");
}

if (url.pathname.startsWith("/login")) {
responseHeaders.set("X-Frame-Options", "SAMEORIGIN");
responseHeaders.set("Content-Security-Policy", "frame-ancestors 'self'");
Expand Down
8 changes: 0 additions & 8 deletions apps/webapp/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { ToastMessage } from "~/models/message.server";
import { commitSession, getSession } from "~/models/message.server";
import tailwindStylesheetUrl from "~/tailwind.css";
import { RouteErrorDisplay } from "./components/ErrorDisplay";
import { StaleAssetRecovery } from "./components/StaleAssetRecovery";
import { AppContainer, MainCenteredContainer } from "./components/layout/AppLayout";
import { ShortcutsProvider } from "./components/primitives/ShortcutsProvider";
import { Toast } from "./components/primitives/Toast";
Expand All @@ -19,11 +18,6 @@ import { getUser } from "./services/session.server";
import { getTimezonePreference } from "./services/preferences/uiPreferences.server";
import { appEnvTitleTag } from "./utils";

// Derived here (not inside StaleAssetRecovery) so the shared component takes
// the flag as a prop. NODE_ENV is statically replaced in browser bundles, and
// the ErrorBoundary can't rely on loader data.
const isProduction = process.env.NODE_ENV === "production";

export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: tailwindStylesheetUrl }];
};
Expand Down Expand Up @@ -105,7 +99,6 @@ export function ErrorBoundary() {
<head>
<meta charSet="utf-8" />

<StaleAssetRecovery isProduction={isProduction} />
<Meta />
<Links />
</head>
Expand All @@ -132,7 +125,6 @@ export default function App() {
<>
<html lang="en" className="h-full" data-theme="dark">
<head>
<StaleAssetRecovery isProduction={isProduction} />
<Meta />
<Links />
</head>
Expand Down
17 changes: 0 additions & 17 deletions apps/webapp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,23 +134,6 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
const port = process.env.REMIX_APP_PORT || process.env.PORT || 3000;

if (process.env.HTTP_SERVER_DISABLED !== "true") {
// Stamp every response with the build this replica is running. The
// client-side stale-asset recovery reads it from Remix loader fetches to
// turn an ordinary navigation into a full document load when the server
// has moved to a new build.
app.use((_req, res, next) => {
res.set("X-Build-Id", build.assets.version);
next();
});

// Reports the build this replica is running. The client-side stale-asset
// recovery polls it after a /build 404 and reloads only once the server
// reports a different build than the one the page was rendered with.
app.get("/build-version", (_req, res) => {
res.set("Cache-Control", "no-store");
res.json({ version: build.assets.version });
});

const socketIo: { io: IoServer } | undefined = build.entry.module.socketIo;
const wss: WebSocketServer | undefined = build.entry.module.wss;
const apiRateLimiter: RateLimitMiddleware = build.entry.module.apiRateLimiter;
Expand Down