Commit Graph

6 Commits

Author SHA1 Message Date
GraceSolutions 329825ce86 fix(auth): sanitize redirect, read token from storage, 301 legacy /auth/* paths
- Add sanitizeRedirect/safeRedirectTarget helper; reject anything that isn't
  a safe local path (must start with a single /, no scheme, no whitespace,
  not /login itself). Apply in RequireAuth when encoding the current pathname
  and in AuthLogin when consuming ?redirect=.
- Make configureApi's getToken read localStorage directly via readCurrentToken
  so the first request after login cannot race the AuthContext re-render that
  previously owned the token via a React closure.
- Redirect legacy /auth/* (including the pre-flatten /auth/auth1/login) with
  HTTP 301 to /login in the webui handler so stale bookmarks can't seed the
  SPA router with a malformed URL.
- Regression test TestHandler_LegacyAuthPathRedirectsToLogin covering four
  legacy path shapes.
2026-04-23 17:12:41 -04:00
GraceSolutions 8ef06b8414 test(webui): add handler tests covering the /index.html redirect regression
Extract the handler body into a small handlerForFS(fs.FS) helper so tests
can drive it against a synthetic fstest.MapFS without depending on whether
the real frontend has been staged into dist/. Handler() is a one-line
wrapper that passes FS(), so runtime behavior is unchanged.

New webui_test.go covers:
  - / serves index.html with 200 and text/html.
  - /index.html returns 200 with no Location header (the exact regression
    that infinite-looped when we still used http.FileServer).
  - /<route> resolves to <route>/index.html with Cache-Control: no-cache.
  - /_next/static/* assets get Cache-Control: public, max-age=31536000,
    immutable and the correct Content-Type from mime.TypeByExtension.
  - Unknown paths fall back to index.html (SPA routing) with 200.
  - Empty FS falls back to the embedded placeholderHTML.
  - HEAD / returns 200 with Content-Length but no body.

All 7 tests pass; go build ./... and go vet ./... are clean.
2026-04-23 16:10:04 -04:00
GraceSolutions 24b82970c2 fix(webui): stop using http.FileServer to avoid /index.html redirect loop
The SPA handler delegated to http.FileServer after resolving requests
like /, /api-keys, and /auth/auth1/login to the corresponding
<route>/index.html from the embedded Next.js static export. http.FileServer
has a built-in behavior that 301-redirects any URL ending in /index.html
to ./, which caused every page load to enter an infinite redirect loop
(confirmed via HttpClient with AllowAutoRedirect=false: every route
returned '301 Location=./').

Stream resolved files directly from the embedded fs.FS instead. The new
serveFile helper sets Content-Type from the extension via
mime.TypeByExtension, writes Content-Length, and honors HEAD. Cache-Control
behavior is unchanged: long-lived immutable for /_next/static/*, no-cache
for everything else. SPA fallback (missing file -> index.html with 200)
still runs through serveIndex, which already opens the file directly.

Verified against a fresh windows/amd64 build (no pre-existing data dir):
  /                        -> 200 text/html 19673 B
  /index.html              -> 200 text/html 19673 B
  /api-keys                -> 200 text/html 20602 B
  /auth/auth1/login        -> 200 text/html 22190 B
  /_next/static/css/*.css  -> 200 text/css  6530 B  (immutable cache)
  /api/v1/health           -> 200 application/json {status:healthy}
  /api/v1/version          -> 200 application/json (injected version/commit)
2026-04-23 15:33:19 -04:00
GraceSolutions e29b166453 build: single-file Windows CGO binary and stop tracking UI placeholder
Make the Windows build produce a truly standalone .exe with no runtime
DLL prerequisites, and stop tracking a placeholder index.html that the
build pipeline rewrites on every run.

webui package
- Move the placeholder HTML out of a tracked file on disk and into a
  placeholderHTML const in webui.go. serveIndex falls back to that
  const when the embed does not contain index.html, so dev builds that
  skip the frontend still get a sensible landing page.
- IsBuilt now checks for index.html instead of _next/ so the helper
  stays accurate when only the placeholder is embedded.

Embed directory hygiene
- dist/.gitignore now ignores everything except itself; the directory
  still exists for //go:embed but never shows dirty after a build.
- Stop tracking the old placeholder at dist/index.html.

scripts/build.ps1
- Staging step clears everything in dist/ except .gitignore (was also
  preserving the old index.html).
- Windows amd64 builds now static-link the mingw-w64 C runtime via
  -ldflags '-linkmode external -extldflags "-static"' so the final
  orchestrad.exe depends only on standard Windows system DLLs and the
  Universal CRT (part of Windows 10+). Verified: no libgcc_s_seh-1,
  libstdc++-6, libwinpthread-1, msvcr*, or vcruntime140 imports.
- Auto-discover gcc from C:\ProgramData\mingw64, Chocolatey's mingw,
  MSYS2 (ucrt64/mingw64), and TDM-GCC install locations so fresh
  checkouts build without manual PATH edits after 'choco install mingw'.
- Windows arm64 is now skipped with a clear message when a matching
  aarch64-w64-mingw32-gcc cross-compiler is not available, instead of
  failing the whole run.

Binary impact (windows/amd64, CGO on, static): 24.06 MB.
2026-04-23 15:19:38 -04:00
GraceSolutions e396094b47 refactor(frontend): flatten Spike template to OrchestrAD-only surface
Prune the Spike NextJS PRO demo content that was still shipping inside
the embedded UI and rename the route group to match the app, so every
page we compile is one we actually use. Separately the full Spike main
is preserved at Grace-Solutions/Spike-NextJS-PRO-Template on a parallel
branch for future template pulls.

Route group
- Rename src/app/(DashboardLayout) -> src/app/(app); update all
  imports, lazy chunk references, and layout boundary names.

Remove demo routes
- src/app/(app)/{apps,charts,muicharts,forms,icons,mui-trees,
  react-tables,tables,theme-pages,ui-components,widgets,sample-page,
  dashboards} and the remaining (app)/apps/{blog,ecommerce,invoice}
  detail/edit subtrees.
- src/app/{frontend-pages,landingpage} and the blog [slug] route.
- src/app/auth/{auth2,error,maintenance} and the auth1 forgot/register
  variants; keep the primary login flow only.
- src/app/api/* demo fetchers (blog, chat, contacts, dashboard,
  eCommerce, email, invoice, kanban, notes, ticket, userprofile,
  globalFetcher); real calls go through @/lib/api.

Remove demo components and contexts
- src/app/components/{apps,dashboards,forms/form-*,pages,widgets,...}.
- src/app/context/{BlogContext,ChatContext,Ecommercecontext,...};
  keep AuthContext, CustomizerContext, Config only.
- src/app/types/{apps,auth}; AuthLogin now uses a local props type.

Layout cleanup
- (app)/layout/vertical/header/Header.tsx and horizontal/header
  drop ProductProvider / Ecommerce wrappers so the build succeeds.
- (app)/layout/horizontal/navbar/Menudata.ts mirrors the OrchestrAD
  sidebar MenuItems so both layouts expose the same routes.

Build artifacts
- Re-stage frontend/out into backend/internal/webui/dist after the
  flatten; index.html now references the (app) chunk graph.

Binary impact (windows/amd64, CGO off): ~37.5 MB -> 26.27 MB.
Embedded dist: 298 files / 14.46 MB.
2026-04-23 15:05:19 -04:00
GraceSolutions 2dde5af6cb feat(ui-embed): ship the web UI inside the Go binary
Switch Next.js to static export (output: 'export', trailingSlash: true) and add a new backend/internal/webui package that embeds frontend/out/ via //go:embed. The backend now serves the UI from its own HTTP server with SPA fallback, long-lived cache headers on _next/static/*, and no dependency on a separate Node.js runtime or static host.

- frontend/next.config.mjs: output='export', trailingSlash=true, images.unoptimized.

- backend/internal/webui: Handler() with SPA fallback, resolve() mirroring Next trailing-slash behavior, IsBuilt() helper, and a placeholder dist/index.html so go build works without a prior frontend build. Top-level .gitignore tracks only the placeholder and its own .gitignore; the copied static export is ignored.

- backend/internal/server/server.go: mount webui.Handler() as the chi NotFound handler so /api/v1/* and /health continue to be served by their handlers while all other routes resolve through the embedded UI with SPA deep-link support.

- scripts/build.ps1: build the frontend (npm install if needed, then npm run build) before any Go build, stage frontend/out into backend/internal/webui/dist, and fail loudly if the UI output is missing or empty. Adds -SkipFrontend for developer iteration.

- Remove Spike demo content that prevented static export: api/* route handlers (dynamic POST/PUT/DELETE), apps/{blog,ecommerce,invoice} dynamic [slug]/[id] detail/edit pages, and frontend-pages/blog/[slug].

- Fix TypeScript errors surfaced by enabling the production build: ApiMeta.totalCount (not total) in audit and rule-runs pages; MUI v7 Grid uses size={{...}} instead of item+xs+md in config page.

Binary size grows from ~8 MB to ~37 MB, reflecting the embedded UI. The produced single binary is now sufficient to run the full product; no separate web server is required.
2026-04-23 14:48:59 -04:00