Skip to content

Commit

Permalink
Fix 404, improve Cache behavior, semver sorting, readme support
Browse files Browse the repository at this point in the history
  • Loading branch information
BastienClement committed Jul 14, 2024
1 parent db64f72 commit 9da8f4f
Show file tree
Hide file tree
Showing 8 changed files with 404 additions and 136 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ go 1.22.5

require (
cloud.google.com/go/storage v1.43.0
github.com/dustin/go-humanize v1.0.1
github.com/hashicorp/go-version v1.7.0
google.golang.org/api v0.188.0
)

Expand All @@ -22,6 +24,7 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
github.com/yuin/goldmark v1.7.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
Expand Down Expand Up @@ -66,6 +68,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
Expand All @@ -77,6 +81,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
Expand Down
159 changes: 159 additions & 0 deletions index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package main

import (
"bufio"
"context"
_ "embed"
"fmt"
"log/slog"
"net/http"
"slices"
"strings"
"time"

"cloud.google.com/go/storage"
"github.com/dustin/go-humanize"
"google.golang.org/api/iterator"
)

type Link struct {
Target string
Extra string
}

//go:embed page.html
var pageHtml []byte

func handleIndex(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.Header().Set("Last-Modified", time.Now().Truncate(time.Minute).Format(http.TimeFormat)) // Listing shows relative timestamps.
w.Header().Set("Cache-Control", defaultCacheControl)

if r.Method == http.MethodHead {
// Directory index always returns 200 OK.
return
}

var links []Link

links = append(links, linksFromMountPoints(r.URL.Path)...)

var storageLinks, readmeObject = linksFromStorage(r.Context(), r.URL.Path)
links = append(links, storageLinks...)

links = slices.Compact(links)
slices.SortStableFunc(links, sortLinks)

var output = bufio.NewWriter(w)

output.Write(pageHtml)
output.WriteString("<main><table>\n")
if r.URL.Path != "/" {
output.WriteString("<tr><td><a href=\"../\">../</a></td></tr>\n")
}
for i, link := range links {
// Split links with and without extra information into separate tables.
if i > 0 && links[i-1].Extra != "" && link.Extra == "" {
output.WriteString("</table><table>\n")
}
// Skip the favicon link on the root page.
if link.Target == "favicon.ico" && r.URL.Path == "/" {
continue
}
output.WriteString(fmt.Sprintf("<tr><td><a href=\"%s\">%s</a></td>%s</tr>\n", link.Target, link.Target, link.Extra))
}
output.WriteString("</table></main>")

if readmeObject != nil && *readme {
output.WriteString("\n<footer>\n")
renderReadme(r.Context(), output, readmeObject)
output.WriteString("</footer>")
}

output.Flush()
}

func linksFromMountPoints(path string) (links []Link) {
for _, mountPoint := range mountPoints {
if mountPoint.Path != path && strings.HasPrefix(mountPoint.Path, path) {
links = append(links, Link{strings.SplitAfterN(strings.TrimPrefix(mountPoint.Path, path), "/", 2)[0], ""})
}
}
return
}

func linksFromStorage(ctx context.Context, path string) (links []Link, readme *storage.ObjectAttrs) {
var mountPoint = findMountPoint(path)
if mountPoint == nil {
return
}

bucket := client.Bucket(mountPoint.Bucket)
query := &storage.Query{
Prefix: mountPoint.Prefix + strings.TrimPrefix(path, mountPoint.Path),
Delimiter: "/",
}

slog.Debug("listing objects", "bucket", mountPoint.Bucket, "query", query)

objects := bucket.Objects(ctx, query)
for {
attrs, err := objects.Next()
if err == iterator.Done {
break
} else if err != nil {
slog.Error("failed to list objects", "err", err)
break
}

if attrs.Name != "" {
if strings.ToLower(attrs.Name) == "readme.md" {
readme = attrs
if *skipReadme {
continue
}
}
if attrs.Name != query.Prefix {
links = append(links, Link{
strings.TrimPrefix(attrs.Name, query.Prefix),
fmt.Sprintf(
"<td>%s</td><td><time title=\"%s\">%s</time></td><td>%x</td>",
humanize.IBytes(uint64(attrs.Size)),
attrs.Updated.Format(time.DateTime),
humanize.Time(attrs.Updated),
attrs.MD5,
),
})
}
} else if attrs.Prefix != "" {
links = append(links, Link{strings.TrimPrefix(attrs.Prefix, query.Prefix), ""})
} else {
slog.Warn("unexpected object", "attrs", attrs)
}
}
return
}

func sortLinks(a, b Link) int {
if aHasExtra, bHasExtra := a.Extra != "", b.Extra != ""; aHasExtra != bHasExtra {
if aHasExtra {
return -1
}
return 1
}

if *versionSort {
va, i := guessVersion(a.Target)
vb, j := guessVersion(b.Target)
if va != nil && vb != nil {
if cmp := strings.Compare(a.Target[:i], b.Target[:j]); cmp != 0 {
return cmp
}
if cmp := vb.Compare(va); cmp != 0 {
return cmp
}
}
}

return strings.Compare(a.Target, b.Target)
}
Loading

0 comments on commit 9da8f4f

Please sign in to comment.