From 8da5d6eef3f1f0c2e57f3618d025982d5e3b9a32 Mon Sep 17 00:00:00 2001 From: space Date: Sat, 25 Jul 2026 00:28:14 +0200 Subject: [PATCH] fix: correct route registration - use full paths in rjweb-server path.http() calls rjweb-server path.http() second argument is the FULL path, not relative to prefix. Changed all routes from server.path('/prefix', path => path.http(method, '/suffix', ...)) to server.path('/', path => path.http(method, '/prefix/suffix', ...)). Also fixed: - Duplicate createContext import in useAuth.ts - notFound handler now excludes /webhook paths from SPA fallback Co-Authored-By: Claude Sonnet 4.6 --- backend/src/index.ts | 74 +++++++++++++++++------------------ frontend/src/hooks/useAuth.ts | 3 +- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 9ff5bd58..afc1161c 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -44,44 +44,44 @@ export const server = new Server( ); // Auth -server.path("/api/auth", (path) => path - .http("POST", "/login", (http) => http.onRequest(loginHandler)) - .http("POST", "/logout", (http) => http.onRequest(logoutHandler)) - .http("GET", "/me", (http) => http.onRequest(meHandler)) - .http("GET", "/setup-status", (http) => http.onRequest(setupStatusHandler)) - .http("POST", "/first-user", (http) => http.onRequest(firstUserHandler)) +server.path("/", (path) => path + .http("POST", "/api/auth/login", (http) => http.onRequest(loginHandler)) + .http("POST", "/api/auth/logout", (http) => http.onRequest(logoutHandler)) + .http("GET", "/api/auth/me", (http) => http.onRequest(meHandler)) + .http("GET", "/api/auth/setup-status", (http) => http.onRequest(setupStatusHandler)) + .http("POST", "/api/auth/first-user", (http) => http.onRequest(firstUserHandler)) ); // Webhook -server.path("/webhook", (path) => path - .http("POST", "/:userId", (http) => http.onRequest(webhookHandler)) +server.path("/", (path) => path + .http("POST", "/webhook/:userId", (http) => http.onRequest(webhookHandler)) ); // User settings -server.path("/api/user", (path) => path - .http("GET", "/settings", (http) => http.onRequest(getUserSettings)) - .http("PATCH", "/username", (http) => http.onRequest(updateUsername)) - .http("PATCH", "/password", (http) => http.onRequest(updatePassword)) - .http("PUT", "/gitea", (http) => http.onRequest(updateGitea)) - .http("PUT", "/aws", (http) => http.onRequest(updateAws)) - .http("GET", "/webhook-secret", (http) => http.onRequest(getWebhookSecret)) - .http("POST", "/webhook-secret/regenerate", (http) => http.onRequest(regenerateWebhookSecret)) +server.path("/", (path) => path + .http("GET", "/api/user/settings", (http) => http.onRequest(getUserSettings)) + .http("PATCH", "/api/user/username", (http) => http.onRequest(updateUsername)) + .http("PATCH", "/api/user/password", (http) => http.onRequest(updatePassword)) + .http("PUT", "/api/user/gitea", (http) => http.onRequest(updateGitea)) + .http("PUT", "/api/user/aws", (http) => http.onRequest(updateAws)) + .http("GET", "/api/user/webhook-secret", (http) => http.onRequest(getWebhookSecret)) + .http("POST", "/api/user/webhook-secret/regenerate", (http) => http.onRequest(regenerateWebhookSecret)) ); // Repos -server.path("/api/repos", (path) => path - .http("GET", "/", (http) => http.onRequest(listRepos)) - .http("POST", "/config", (http) => http.onRequest(saveRepoConfig)) - .http("POST", "/toggle", (http) => http.onRequest(toggleRepoEnabled)) - .http("GET", "/:owner/:repo/config", (http) => http.onRequest(getRepoConfig)) +server.path("/", (path) => path + .http("GET", "/api/repos", (http) => http.onRequest(listRepos)) + .http("POST", "/api/repos/config", (http) => http.onRequest(saveRepoConfig)) + .http("POST", "/api/repos/toggle", (http) => http.onRequest(toggleRepoEnabled)) + .http("GET", "/api/repos/:owner/:repo/config", (http) => http.onRequest(getRepoConfig)) ); // Previews -server.path("/api/previews", (path) => path - .http("GET", "/", (http) => http.onRequest(listPreviews)) - .http("GET", "/:id", (http) => http.onRequest(getPreview)) - .http("POST", "/:id/stop", (http) => http.onRequest(stopPreviewRoute)) - .ws("/:id/logs", (ws) => ws +server.path("/", (path) => path + .http("GET", "/api/previews", (http) => http.onRequest(listPreviews)) + .http("GET", "/api/previews/:id", (http) => http.onRequest(getPreview)) + .http("POST", "/api/previews/:id/stop", (http) => http.onRequest(stopPreviewRoute)) + .ws("/api/previews/:id/logs", (ws) => ws .onOpen(previewLogsWs) .onMessage(async () => {}) .onClose(async () => {}) @@ -89,25 +89,25 @@ server.path("/api/previews", (path) => path ); // Admin -server.path("/api/admin", (path) => path - .http("GET", "/users", (http) => http.onRequest(listUsers)) - .http("POST", "/users", (http) => http.onRequest(createUser)) - .http("PATCH", "/users/:id", (http) => http.onRequest(updateUser)) - .http("DELETE", "/users/:id", (http) => http.onRequest(deleteUser)) - .http("GET", "/settings", (http) => http.onRequest(getSettings)) - .http("PUT", "/settings", (http) => http.onRequest(updateSettings)) - .http("GET", "/previews", (http) => http.onRequest(adminListPreviews)) - .http("POST", "/previews/:id/stop", (http) => http.onRequest(adminStopPreview)) +server.path("/", (path) => path + .http("GET", "/api/admin/users", (http) => http.onRequest(listUsers)) + .http("POST", "/api/admin/users", (http) => http.onRequest(createUser)) + .http("PATCH", "/api/admin/users/:id", (http) => http.onRequest(updateUser)) + .http("DELETE", "/api/admin/users/:id", (http) => http.onRequest(deleteUser)) + .http("GET", "/api/admin/settings", (http) => http.onRequest(getSettings)) + .http("PUT", "/api/admin/settings", (http) => http.onRequest(updateSettings)) + .http("GET", "/api/admin/previews", (http) => http.onRequest(adminListPreviews)) + .http("POST", "/api/admin/previews/:id/stop", (http) => http.onRequest(adminStopPreview)) ); -// Static UI +// Static UI — must come AFTER API routes if (hasUiBuild) { server.path("/", (path) => path.static(uiBuildPath)); } server.notFound(async (ctr) => { const STATIC_EXT = /\.(js|mjs|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|map|json|txt|xml|webp|avif)(\?.*)?$/i; - if (!ctr.url.path.startsWith("/api") && !STATIC_EXT.test(ctr.url.path) && existsSync(uiIndexPath)) { + if (!ctr.url.path.startsWith("/api") && !ctr.url.path.startsWith("/webhook") && !STATIC_EXT.test(ctr.url.path) && existsSync(uiIndexPath)) { return ctr.status(200).printFile(uiIndexPath, { addTypes: true }); } return makeResponse({ ctr, content: { code: ERROR_MESSAGES.NOT_FOUND.code, message: ERROR_MESSAGES.NOT_FOUND.message } }); diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts index 95224765..2db1583b 100644 --- a/frontend/src/hooks/useAuth.ts +++ b/frontend/src/hooks/useAuth.ts @@ -21,8 +21,7 @@ interface AuthContextType { refresh: () => Promise; } -import { createContext as _createContext } from "react"; -export const AuthContext = _createContext({ +export const AuthContext = createContext({ user: null, loading: true, refresh: async () => {},