9c9576b5b9
ci / test (macos-latest) (push) Has been cancelled
ci / test (ubuntu-latest) (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / govulncheck (push) Has been cancelled
ci / tidy (push) Has been cancelled
ci / webview (push) Has been cancelled
ci / windows-gui (push) Has been cancelled
release / check (push) Has been cancelled
release / release (push) Has been cancelled
Docs / build (push) Has been cancelled
Docs / deploy-pages (push) Has been cancelled
Docs / deploy-cloudflare (push) Has been cancelled
56 lines
1.6 KiB
Go
56 lines
1.6 KiB
Go
package pack
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/tamnd/kage/zim"
|
|
)
|
|
|
|
// 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)
|
|
return
|
|
}
|
|
serveBlob(w, blob)
|
|
return
|
|
}
|
|
blob, err := r.Get(zim.NamespaceContent, p)
|
|
if errors.Is(err, zim.ErrNotFound) {
|
|
http.NotFound(w, req)
|
|
return
|
|
}
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
serveBlob(w, blob)
|
|
})
|
|
}
|
|
|
|
func serveBlob(w http.ResponseWriter, b zim.Blob) {
|
|
if b.MimeType != "" {
|
|
w.Header().Set("Content-Type", b.MimeType)
|
|
}
|
|
_, _ = w.Write(b.Data)
|
|
}
|