[PR #2436] [CLOSED] feat: Better support for deploying the frontend at a subpath #9982

Closed
opened 2026-04-23 09:19:32 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/go-vikunja/vikunja/pull/2436
Author: @mdrkrg
Created: 3/22/2026
Status: Closed

Base: mainHead: feat/path-based-serving


📝 Commits (10+)

  • 7f549b8 fix(frontend): Make sw.ts respect to frontend base URL
  • b544fea fix(frontend): Fix hard-coded API base in checkAndSetApiUrl.ts
  • f52d9d1 fix(frontend/tests): Fix hard-coded API base in tests
  • 3466082 fix(static): Use VIKUNJA_API_URL placeholder in index.html
  • 2f71492 fix(frontend/vite): Configure vite dev proxy to handle frontend path
  • ea14fda fix(frontend/oidc): Prefix frontend base to redirect URL
  • 6d662d9 chore: Rename BUILD_STANDALONE to VIKUNJA_BUILD_STANDALONE
  • 153929d feat(frontend): Add VIKUNJA_API_URL to explicitly set window.API_URL
  • df58e9d fix(auth): Make refresh token path respect to public URL
  • 88fad75 fix(api/docs): Make redoc load docs.json from public URL

📊 Changes

18 files changed (+142 additions, -44 deletions)

View changed files

📝 .github/workflows/test.yml (+2 -0)
📝 frontend/.env.local.example (+2 -0)
📝 frontend/index.html (+6 -2)
📝 frontend/src/components/tasks/partials/Comments.vue (+3 -1)
📝 frontend/src/helpers/checkAndSetApiUrl.ts (+27 -18)
📝 frontend/src/helpers/redirectToProvider.ts (+3 -1)
📝 frontend/src/sw.ts (+3 -1)
📝 frontend/tests/support/authenticateUser.ts (+2 -3)
📝 frontend/tests/support/constants.ts (+5 -0)
📝 frontend/tests/support/fixtures.ts (+2 -1)
📝 frontend/tests/support/seed.ts (+2 -1)
📝 frontend/tests/support/updateUserSettings.ts (+2 -1)
📝 frontend/vite.config.ts (+38 -8)
📝 magefile.go (+4 -0)
📝 pkg/modules/auth/auth.go (+19 -3)
📝 pkg/routes/api/v1/docs.go (+17 -2)
📝 pkg/routes/api/v1/redoc/redoc.html (+1 -1)
📝 pkg/routes/static.go (+4 -1)

📄 Description

Make the frontend work correctly when served at a subpath (e.g. /vikunja/ instead of /). Previously, API URLs, OIDC redirects, CalDAV URLs, and the service worker all had paths hardcoded relative to the domain root, which broke any deployment behind a reverse proxy with a path prefix.

This PR is based on the fix in #2440 .

What changed

  • index.html / static.go: Replaced the hardcoded window.API_URL value with a __VIKUNJA_API_URL__ marker. Vite replaces it at build time (for standalone nginx deploys) if VIKUNJA_BUILD_STANDALONE=true is set in .env.local. Otherwise the Go backend replaces it at serve time using service.publicurl.

    • I'm unsure about this change. The original static.go targets 'http://localhost:3456/api/v1' to replace, but in actual index.html it is '/api/v1', so this is already broken?

    • Current implementation would work for this case: Serving frontend and backend on the same server under a subpath (e.g., example.com/vikunja), in which it does not matter whether Vite does the API_BASE replacement or the go backend.

    • However, it is not feasible to serve frontend at subpath but backend at subdomain (e.g., example.com/vikunja served by nginx, and backend at api.example.com). Might need some refactoring. Fixed in 2e134cec1. See this comment.

  • checkAndSetApiUrl.ts: Extracted API_DEFAULT_PATH constant and joinPath/hasApiPath helpers.

  • config.ts (apiBase computed): See !2435. Uses pathname and strips /api/v1 to derive the deployment base. This fixes CalDAV URL construction in Caldav.vue.

  • redirectToProvider.ts: OIDC redirect URL now includes the base path via getFullBaseUrl(), so the callback lands on a route the Vue router handles.

  • sw.ts: Service worker API route regex is anchored to the configured base path instead of matching api/v1/ anywhere in the URL.

  • auth/auth.go: Compute refresh token cookie base path from service.publicurl rather than hard coding the base as /api/v1.

  • api/v1/docs.go: Build RedocUITemplate as template, and replace with apiBase + api/v1/docs.json.

  • Test files: Centralized TEST_API_URL constant so the API base URL for tests is defined in one place.

  • vite.config.ts: Added a transformIndexHtml plugin for the marker replacement. Made the dev proxy path and rewrite dynamic based on VIKUNJA_FRONTEND_BASE so local development works with a subpath.

How to deploy at a subpath

  1. Set VIKUNJA_FRONTEND_BASE to the desired path (e.g. /vikunja/) before building the frontend. This sets Vite's base and bakes the correct API URL into the built HTML.

  2. Configure the reverse proxy to forward requests from the subpath to Vikunja. The Go backend still expects /api/v1 at its root — the proxy should strip the prefix before forwarding.

    Example Caddyfile:

    example.com:80 {
     handle_path /vikunja* {
      reverse_proxy 127.0.0.1:3456
     }
    }
    
  3. Set service.publicurl in config.yml to the full public URL including the subpath (e.g. https://example.com/vikunja). When the Go backend serves the frontend, it will replace the window.API_BASE placeholder.

  4. For OIDC: no extra configuration needed — the redirect URL is now built dynamically from the frontend's base path.

  5. For migration services (Trello, Todoist, Microsoft To Do): the redirect URLs in config.yml are absolute URLs set by the admin, so include the subpath there if applicable.

Docs changes needed

The following should be mentioned in the deployment documentation:

  • VIKUNJA_FRONTEND_BASE: (Already there).

  • VIKUNJA_API_URL: The full URL to the Vikunja API. Only required when building the standalone frontend (see below VIKUNJA_BUILD_STANDALONE). If unset when building a standalone frontend, it would be automatically derived as VIKUNJA_FRONTEND_BASE + /api/v1. Only set this when the backend is at a different origin or path than the frontend.

  • VIKUNJA_BUILD_STANDALONE: If building a frontend that is intended to be served at a subpath by a reverse proxy, set this to true so that Vite replaces __VIKUNJA_API_URL__ with VIKUNJA_API_URL at build time.
    Otherwise go backend will handle the replacement using service.publicurl.

  • Reverse proxy subpath configuration: Document that when deploying at a subpath, the reverse proxy should strip the prefix before forwarding to the backend.
    Include example configs for common web servers.

  • Include the subpath in service.publicurl: If the Go backend serves the frontend and the deployment uses a subpath, service.publicurl must reflect that (e.g. https://example.com/vikunja).

  • Migration redirect URLs: Admins must include the subpath in migration.*.redirecturl values in config.yml if deploying at a subpath. These are not auto-detected.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/go-vikunja/vikunja/pull/2436 **Author:** [@mdrkrg](https://github.com/mdrkrg) **Created:** 3/22/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `feat/path-based-serving` --- ### 📝 Commits (10+) - [`7f549b8`](https://github.com/go-vikunja/vikunja/commit/7f549b821b0834a149e7068f4c0a8e2a7ec5ce77) fix(frontend): Make sw.ts respect to frontend base URL - [`b544fea`](https://github.com/go-vikunja/vikunja/commit/b544fea51563a05d3089303e53caa08fac7794c5) fix(frontend): Fix hard-coded API base in checkAndSetApiUrl.ts - [`f52d9d1`](https://github.com/go-vikunja/vikunja/commit/f52d9d124e7d06aafe441fd8fb55aa77571eb037) fix(frontend/tests): Fix hard-coded API base in tests - [`3466082`](https://github.com/go-vikunja/vikunja/commit/34660822867e9d567599d1d7111f8c83b9ae6f19) fix(static): Use __VIKUNJA_API_URL__ placeholder in index.html - [`2f71492`](https://github.com/go-vikunja/vikunja/commit/2f714928ae9fee9b1908d14ee8666c1a8cfda4fd) fix(frontend/vite): Configure vite dev proxy to handle frontend path - [`ea14fda`](https://github.com/go-vikunja/vikunja/commit/ea14fda8479477a5e5940ea5a1a50aaa785301f4) fix(frontend/oidc): Prefix frontend base to redirect URL - [`6d662d9`](https://github.com/go-vikunja/vikunja/commit/6d662d9bb0bbed60e232efd6a4df99f1a8a162b6) chore: Rename BUILD_STANDALONE to VIKUNJA_BUILD_STANDALONE - [`153929d`](https://github.com/go-vikunja/vikunja/commit/153929d2ded530577d5062ea41a9cf17a42d0b6c) feat(frontend): Add VIKUNJA_API_URL to explicitly set window.API_URL - [`df58e9d`](https://github.com/go-vikunja/vikunja/commit/df58e9de5ccc8dd1f12ed8744de94c3e1e5dafc1) fix(auth): Make refresh token path respect to public URL - [`88fad75`](https://github.com/go-vikunja/vikunja/commit/88fad75964c9cc3a7255dc47bd5f426cf69abd8f) fix(api/docs): Make redoc load docs.json from public URL ### 📊 Changes **18 files changed** (+142 additions, -44 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflows/test.yml` (+2 -0) 📝 `frontend/.env.local.example` (+2 -0) 📝 `frontend/index.html` (+6 -2) 📝 `frontend/src/components/tasks/partials/Comments.vue` (+3 -1) 📝 `frontend/src/helpers/checkAndSetApiUrl.ts` (+27 -18) 📝 `frontend/src/helpers/redirectToProvider.ts` (+3 -1) 📝 `frontend/src/sw.ts` (+3 -1) 📝 `frontend/tests/support/authenticateUser.ts` (+2 -3) 📝 `frontend/tests/support/constants.ts` (+5 -0) 📝 `frontend/tests/support/fixtures.ts` (+2 -1) 📝 `frontend/tests/support/seed.ts` (+2 -1) 📝 `frontend/tests/support/updateUserSettings.ts` (+2 -1) 📝 `frontend/vite.config.ts` (+38 -8) 📝 `magefile.go` (+4 -0) 📝 `pkg/modules/auth/auth.go` (+19 -3) 📝 `pkg/routes/api/v1/docs.go` (+17 -2) 📝 `pkg/routes/api/v1/redoc/redoc.html` (+1 -1) 📝 `pkg/routes/static.go` (+4 -1) </details> ### 📄 Description Make the frontend work correctly when served at a subpath (e.g. `/vikunja/` instead of `/`). Previously, API URLs, OIDC redirects, CalDAV URLs, and the service worker all had paths hardcoded relative to the domain root, which broke any deployment behind a reverse proxy with a path prefix. This PR is based on the fix in #2440 . ## What changed - **`index.html` / `static.go`**: Replaced the hardcoded `window.API_URL` value with a `__VIKUNJA_API_URL__` marker. Vite replaces it at build time (for standalone nginx deploys) if `VIKUNJA_BUILD_STANDALONE=true` is set in `.env.local`. Otherwise the Go backend replaces it at serve time using `service.publicurl`. - I'm unsure about this change. The original `static.go` targets `'http://localhost:3456/api/v1'` to replace, but in actual `index.html` it is `'/api/v1'`, so this is already broken? - Current implementation would work for this case: Serving frontend and backend on the same server under a subpath (e.g., `example.com/vikunja`), in which it does not matter whether Vite does the `API_BASE` replacement or the go backend. - ~~However, it is not feasible to serve frontend at subpath but backend at subdomain (e.g., `example.com/vikunja` served by nginx, and backend at `api.example.com`). Might need some refactoring.~~ Fixed in 2e134cec1. See [this comment](#issuecomment-4105788703). - **`checkAndSetApiUrl.ts`**: Extracted `API_DEFAULT_PATH` constant and `joinPath`/`hasApiPath` helpers. - **`config.ts` (`apiBase` computed)**: See !2435. Uses `pathname` and strips `/api/v1` to derive the deployment base. This fixes CalDAV URL construction in `Caldav.vue`. - **`redirectToProvider.ts`**: OIDC redirect URL now includes the base path via `getFullBaseUrl()`, so the callback lands on a route the Vue router handles. - **`sw.ts`**: Service worker API route regex is anchored to the configured base path instead of matching `api/v1/` anywhere in the URL. - **`auth/auth.go`**: Compute refresh token cookie base path from `service.publicurl` rather than hard coding the base as `/api/v1`. - **`api/v1/docs.go`**: Build `RedocUITemplate` as template, and replace with `apiBase + api/v1/docs.json`. - **Test files**: Centralized `TEST_API_URL` constant so the API base URL for tests is defined in one place. - **`vite.config.ts`**: Added a `transformIndexHtml` plugin for the marker replacement. Made the dev proxy path and rewrite dynamic based on `VIKUNJA_FRONTEND_BASE` so local development works with a subpath. ## How to deploy at a subpath 1. Set `VIKUNJA_FRONTEND_BASE` to the desired path (e.g. `/vikunja/`) before building the frontend. This sets Vite's `base` and bakes the correct API URL into the built HTML. 2. Configure the reverse proxy to forward requests from the subpath to Vikunja. The Go backend still expects `/api/v1` at its root — the proxy should strip the prefix before forwarding. Example Caddyfile: ```Caddyfile example.com:80 { handle_path /vikunja* { reverse_proxy 127.0.0.1:3456 } } ``` 3. Set `service.publicurl` in `config.yml` to the full public URL including the subpath (e.g. `https://example.com/vikunja`). When the Go backend serves the frontend, it will replace the `window.API_BASE` placeholder. 4. For OIDC: no extra configuration needed — the redirect URL is now built dynamically from the frontend's base path. 5. For migration services (Trello, Todoist, Microsoft To Do): the redirect URLs in `config.yml` are absolute URLs set by the admin, so include the subpath there if applicable. ## Docs changes needed The following should be mentioned in the deployment documentation: - **`VIKUNJA_FRONTEND_BASE`**: (Already there). - **`VIKUNJA_API_URL`**: The full URL to the Vikunja API. Only required when building the standalone frontend (see below `VIKUNJA_BUILD_STANDALONE`). If unset when building a standalone frontend, it would be automatically derived as `VIKUNJA_FRONTEND_BASE + /api/v1`. Only set this when the backend is at a different origin or path than the frontend. - **`VIKUNJA_BUILD_STANDALONE`**: If building a frontend that is intended to be served at a subpath by a reverse proxy, set this to `true` so that Vite replaces `__VIKUNJA_API_URL__` with `VIKUNJA_API_URL` at build time. Otherwise go backend will handle the replacement using `service.publicurl`. - **Reverse proxy subpath configuration**: Document that when deploying at a subpath, the reverse proxy should strip the prefix before forwarding to the backend. Include example configs for common web servers. - **Include the subpath in `service.publicurl`**: If the Go backend serves the frontend and the deployment uses a subpath, `service.publicurl` must reflect that (e.g. `https://example.com/vikunja`). - **Migration redirect URLs**: Admins must include the subpath in `migration.*.redirecturl` values in `config.yml` if deploying at a subpath. These are not auto-detected. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-23 09:19:32 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/vikunja#9982