Add stable /project/{slug}/latest/ permalink #119

Merged
qwc merged 3 commits from feature/latest-version-permalink into main 2026-06-18 10:04:50 +02:00
Owner

Problem

There was no shareable URL that always points to a project's newest version. The in-page "latest" link resolved to a concrete version tag, so any link you copied or shared went stale the moment a newer version was uploaded.

What this adds

A rolling permalink that always tracks the current latest version:

/project/{slug}/latest/            → newest version's home page
/project/{slug}/latest/{path...}   → that page in the newest version
  • handleServeLatest resolves the latest version and 302-redirects to the concrete version's doc URL, preserving the sub-path. Marked Cache-Control: no-store so caches don't pin it to one version.
  • "Latest" uses the existing latestVersionTag rule — pinned version if set, else highest semver — matching the project page's "Latest" badge and the frontpage. So pinning a version also repoints the permalink.
  • Access is gated identically to handleServeDoc: the redirect never reveals that versions exist on a project the user can't view (anonymous on a non-public project → login).
  • The literal latest segment is registered as a more-specific route than {version}, so it wins for that path. latest is therefore a reserved URL segment (documented).
  • The project detail page shows a read-only "Latest version permalink" field with a Copy button.

Docs

  • first-project tutorial gains a "Linking to the latest version" section.
  • pin-versions notes the permalink follows the pin.

Tests

latest_version_test.go: redirect-to-newest, sub-path preservation, pin precedence, no-versions → 404, and anonymous-on-private → /login. Handler package green.

Design note

Implemented as a redirect (not serve-in-place) so it reuses all the existing, tested serving logic — access checks, PDF viewer, overlay injection, security-header exemptions — with no duplication. The stable /latest/ URL is the thing you share; clicking it always resolves to whatever is newest at that moment.

🤖 Generated with Claude Code

## Problem There was no shareable URL that always points to a project's **newest** version. The in-page "latest" link resolved to a concrete version tag, so any link you copied or shared went stale the moment a newer version was uploaded. ## What this adds A rolling permalink that always tracks the current latest version: ``` /project/{slug}/latest/ → newest version's home page /project/{slug}/latest/{path...} → that page in the newest version ``` - `handleServeLatest` resolves the latest version and **302-redirects** to the concrete version's doc URL, preserving the sub-path. Marked `Cache-Control: no-store` so caches don't pin it to one version. - "Latest" uses the existing `latestVersionTag` rule — **pinned version if set, else highest semver** — matching the project page's "Latest" badge and the frontpage. So pinning a version also repoints the permalink. - Access is gated **identically to `handleServeDoc`**: the redirect never reveals that versions exist on a project the user can't view (anonymous on a non-public project → login). - The literal `latest` segment is registered as a more-specific route than `{version}`, so it wins for that path. `latest` is therefore a reserved URL segment (documented). - The project detail page shows a read-only **"Latest version permalink"** field with a **Copy** button. ## Docs - `first-project` tutorial gains a "Linking to the latest version" section. - `pin-versions` notes the permalink follows the pin. ## Tests `latest_version_test.go`: redirect-to-newest, sub-path preservation, pin precedence, no-versions → 404, and anonymous-on-private → `/login`. Handler package green. ## Design note Implemented as a redirect (not serve-in-place) so it reuses all the existing, tested serving logic — access checks, PDF viewer, overlay injection, security-header exemptions — with no duplication. The stable `/latest/` URL is the thing you share; clicking it always resolves to whatever is newest at that moment. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add stable /project/{slug}/latest/ permalink
All checks were successful
CI / test (pull_request) Successful in 1m5s
CI / build (pull_request) Successful in 41s
CI / docker (pull_request) Has been skipped
c81095df2d
There was no shareable URL that always points to a project's newest
version — links had to bake in a concrete version tag, so they went stale
on the next upload. Add a rolling permalink that redirects to the current
latest:

- GET /project/{slug}/latest and /project/{slug}/latest/{path...} resolve
  the latest version (handleServeLatest) and 302-redirect to the concrete
  version's doc URL, preserving the sub-path. The redirect is marked
  Cache-Control: no-store so caches don't pin it to one version.
- "Latest" uses the existing latestVersionTag rule (pinned version if set,
  else highest semver), matching the project page's "Latest" badge and the
  frontpage. Access is gated identically to handleServeDoc, so the redirect
  never leaks versions on a project the user can't see.
- The literal "latest" segment is registered as a more-specific route than
  {version}, so it wins for that path; "latest" is therefore a reserved URL
  segment (documented).
- Project detail page shows a read-only "Latest version permalink" field
  with a Copy button.
- Docs: first-project tutorial gains a "Linking to the latest version"
  section; pin-versions notes the permalink follows the pin.
- Tests: redirect-to-newest, path preservation, pin precedence, no-versions
  404, and anonymous-on-private → login.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Serve latest in place instead of redirecting
All checks were successful
CI / test (pull_request) Successful in 1m6s
CI / build (pull_request) Successful in 41s
CI / docker (pull_request) Has been skipped
a0fdb4aae4
Switch the /project/{slug}/latest/ permalink from a 302 redirect to
serving the resolved version's content directly at the /latest/ URL. The
address bar stays /latest/, so relative links inside the docs keep
resolving under /latest/ and a visitor who lands on the permalink keeps
browsing "latest" rather than being bounced to a versioned URL.

- Extract the PDF/overlay/HTML serving body of handleServeDoc into a shared
  serveVersionDoc helper; handleServeDoc and handleServeLatest both call it,
  so explicit-version and latest serving behave identically (PDF viewer,
  overlay injection, path safety all reused, no duplication).
- handleServeLatest now resolves the latest tag (still pin-aware), loads the
  version, sets Cache-Control: no-cache (revalidate so a new upload is
  picked up next request), and serves in place.
- Bare /project/{slug}/latest (no trailing slash) 301-redirects to
  /latest/ so the served index page's relative links resolve correctly.
- Docs/tests updated to reflect serve-in-place behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix overlay version switch/compare on the /latest/ URL
All checks were successful
CI / test (pull_request) Successful in 1m4s
CI / build (pull_request) Successful in 41s
CI / docker (pull_request) Has been skipped
95e8f09ef7
Serving the latest version in place means the URL segment ("latest")
differs from the resolved concrete tag the overlay reports in data-current
(e.g. v1.5). The overlay JS computed the in-doc path suffix by stripping
"/project/{slug}/{current}" from window.location.pathname — but the path
actually contains "latest", so the stripping cut into the wrong characters
and produced malformed version-switch, compare-fetch, and link-intercept
URLs.

Derive urlVersion from the actual URL path segment and use it for all path
math (suffix extraction, internal-link prefix matching); keep `current`
(the resolved tag) for concrete-version concerns (dropdown selection,
download link, diff target). Now switching versions and comparing work
correctly whether the page was reached via /latest/ or an explicit version
URL.

Server side already passes the resolved concrete tag to the overlay; added
a test asserting /latest/ serves data-current="v2.0.0" so the JS always has
a real version to build comparison URLs from.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
qwc merged commit 52f49d9caa into main 2026-06-18 10:04:50 +02:00
qwc deleted branch feature/latest-version-permalink 2026-06-18 10:04:50 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
qwc-open/asiakirjat!119
No description provided.