Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a96233cf73 | |||
| 2064ef918a | |||
| 9eabd93098 | |||
| 2ccf7bc5db |
+18
-1
@@ -6,6 +6,22 @@ All notable changes to kage are recorded here. The format follows
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.3.1] - 2026-06-15
|
||||
|
||||
### Fixed
|
||||
|
||||
- A served mirror whose entry point is a nested page no longer loses its CSS and
|
||||
images when opened at the root. kage saves each page's asset links as
|
||||
mirror-relative paths (`../_kage/...`) computed for that page's own location,
|
||||
but the viewer answered `/` with the main page's bytes in place, so the browser
|
||||
resolved those relative URLs against `/` and missed every one. A
|
||||
`developer.apple.com/documentation` mirror, whose main page is
|
||||
`developer.apple.com/documentation/index.html`, landed at `/` completely
|
||||
unstyled. kage now redirects `/` to the main page's canonical content path, the
|
||||
way the archive's `W/mainPage` redirect already does, so the browser resolves
|
||||
the page's relative assets correctly. Kiwix was unaffected because it follows
|
||||
that redirect itself.
|
||||
|
||||
## [0.3.0] - 2026-06-15
|
||||
|
||||
### Added
|
||||
@@ -180,7 +196,8 @@ can browse offline, with every script stripped out.
|
||||
a multi-arch container image on GHCR (Chromium bundled), checksums, SBOMs, and
|
||||
a cosign signature, all cut from one version tag by GoReleaser.
|
||||
|
||||
[Unreleased]: https://github.com/tamnd/kage/compare/v0.3.0...HEAD
|
||||
[Unreleased]: https://github.com/tamnd/kage/compare/v0.3.1...HEAD
|
||||
[0.3.1]: https://github.com/tamnd/kage/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/tamnd/kage/compare/v0.2.1...v0.3.0
|
||||
[0.2.1]: https://github.com/tamnd/kage/compare/v0.2.0...v0.2.1
|
||||
[0.2.0]: https://github.com/tamnd/kage/compare/v0.1.2...v0.2.0
|
||||
|
||||
@@ -6,6 +6,12 @@ weight: 40
|
||||
|
||||
The authoritative, commit-level history lives in [`CHANGELOG.md`](https://github.com/tamnd/kage/blob/main/CHANGELOG.md) and on the [releases page](https://github.com/tamnd/kage/releases). This page summarises each version.
|
||||
|
||||
## v0.3.1
|
||||
|
||||
A fix for broken styling when a packed mirror's home page is a nested page.
|
||||
|
||||
- **`/` redirects to the main page instead of serving it in place.** A page's saved asset links are mirror-relative (`../_kage/...`), computed for that page's own location. The viewer was answering `/` with the main page's bytes directly, so the browser resolved those links against `/` and 404ed the page's CSS and images. A `developer.apple.com/documentation` mirror opened at `/` came up completely unstyled. kage now redirects `/` to the main page's canonical path, the way the archive's `W/mainPage` redirect does, so relative assets resolve correctly. Kiwix already followed that redirect, so it was never affected.
|
||||
|
||||
## v0.3.0
|
||||
|
||||
Leaner mirrors, and a way to publish one as a dataset. A clone now keeps the assets that make a site readable offline and leaves the bulk downloads on the live web, and a packed archive converts to a columnar table that drops straight into dataset tooling.
|
||||
|
||||
@@ -326,6 +326,24 @@ func TestHandler(t *testing.T) {
|
||||
if code, body := get("/"); code != 200 || !bytes.Contains([]byte(body), []byte("Example Home")) {
|
||||
t.Errorf("GET / = %d %.30q", code, body)
|
||||
}
|
||||
|
||||
// "/" must redirect to the main page's canonical content path, not serve its
|
||||
// bytes in place, so the page's mirror-relative asset URLs resolve against the
|
||||
// right base instead of 404ing.
|
||||
noFollow := &http.Client{CheckRedirect: func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}}
|
||||
resp, err := noFollow.Get(srv.URL + "/")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusFound {
|
||||
t.Errorf("GET / status = %d, want 302", resp.StatusCode)
|
||||
}
|
||||
if loc := resp.Header.Get("Location"); loc != "/index.html" {
|
||||
t.Errorf("GET / Location = %q, want %q", loc, "/index.html")
|
||||
}
|
||||
if code, _ := get("/about/index.html"); code != 200 {
|
||||
t.Errorf("GET /about/index.html = %d", code)
|
||||
}
|
||||
|
||||
+13
-3
@@ -8,14 +8,24 @@ import (
|
||||
"github.com/tamnd/kage/zim"
|
||||
)
|
||||
|
||||
// Handler serves a ZIM archive over HTTP. "/" maps to the archive's main page;
|
||||
// "/a/b.png" maps to the C/a/b.png content entry. Because the saved HTML's links
|
||||
// are mirror-relative paths, and those are exactly the C urls, a click in a
|
||||
// Handler serves a ZIM archive over HTTP. "/" redirects to the archive's main
|
||||
// page; "/a/b.png" maps to the C/a/b.png content entry. Because the saved HTML's
|
||||
// links are mirror-relative paths, and those are exactly the C urls, a click in a
|
||||
// served page hits the right entry with no rewriting. A miss is a plain 404.
|
||||
func Handler(r *zim.Reader) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
p := strings.TrimPrefix(req.URL.Path, "/")
|
||||
if p == "" {
|
||||
// The main page's saved HTML carries mirror-relative asset URLs
|
||||
// (../_kage/...) computed for its own nested location, so serving its
|
||||
// bytes at "/" would resolve them against the wrong base and 404 the
|
||||
// page's CSS and images. Redirect to the page's canonical content path
|
||||
// instead, the way the archive's W/mainPage redirect does, so the
|
||||
// browser resolves those relative URLs correctly.
|
||||
if ns, url, ok := r.MainPageRef(); ok && ns == zim.NamespaceContent {
|
||||
http.Redirect(w, req, "/"+url, http.StatusFound)
|
||||
return
|
||||
}
|
||||
blob, err := r.MainPage()
|
||||
if err != nil {
|
||||
http.NotFound(w, req)
|
||||
|
||||
Reference in New Issue
Block a user