Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/roots/wp-packages
go 1.26.1

require (
github.com/CloudyKit/jet/v6 v6.3.2
github.com/aws/aws-sdk-go-v2 v1.41.4
github.com/aws/aws-sdk-go-v2/credentials v1.19.12
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1
Expand All @@ -21,6 +22,7 @@ require (
)

require (
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v6 v6.3.2 h1:BPaX0lnXTZ9TniICiiK/0iJqzeGJ2ibvB4DjAqLMBSM=
github.com/CloudyKit/jet/v6 v6.3.2/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=
github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 h1:3kGOqnh1pPeddVa/E37XNTaWJ8W6vrbYV9lJEkCnhuY=
Expand Down
80 changes: 50 additions & 30 deletions internal/http/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"encoding/json"
"fmt"
"net/http"

"github.com/CloudyKit/jet/v6"
"os"
"path/filepath"
"slices"
Expand Down Expand Up @@ -80,7 +82,7 @@ type versionRow struct {
IsLatest bool
}

func handleIndex(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleIndex(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
filters := publicFilters{
Search: r.URL.Query().Get("search"),
Expand Down Expand Up @@ -126,23 +128,27 @@ func handleIndex(a *app.App, tmpl *templateSet) http.HandlerFunc {
return
}

render(w, r, tmpl.index, "layout", map[string]any{
render(w, r, tmpl, "index.html", map[string]any{
"Packages": packages,
"Filters": filters,
"Page": page,
"Total": total,
"TotalPages": totalPages,
"Stats": stats,
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
"OGImage": ogImageURL(a.Config, "social/default.png"),
"JSONLD": jsonLDData,
"BlogPosts": a.Blog.Posts(),
"Pagination": buildPagination(page, totalPages, "#package-results", "#filter-form:top",
func(p int) string { return paginateURL(filters, p) },
func(p int) string { return paginatePartialURL(filters, p) },
),
"Stats": stats,
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
"OGImage": ogImageURL(a.Config, "social/default.png"),
"JSONLD": jsonLDData,
"BlogPosts": a.Blog.Posts(),
})
}
}

func handleIndexPartial(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleIndexPartial(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
filters := publicFilters{
Search: r.URL.Query().Get("search"),
Expand All @@ -168,31 +174,35 @@ func handleIndexPartial(a *app.App, tmpl *templateSet) http.HandlerFunc {
totalPages := (total + perPage - 1) / perPage

w.Header().Set("X-Robots-Tag", "noindex")
render(w, r, tmpl.indexPartial, "package-results", map[string]any{
render(w, r, tmpl, "package_results.html", map[string]any{
"Packages": packages,
"Filters": filters,
"Page": page,
"Total": total,
"TotalPages": totalPages,
"Pagination": buildPagination(page, totalPages, "#package-results", "#filter-form:top",
func(p int) string { return paginateURL(filters, p) },
func(p int) string { return paginatePartialURL(filters, p) },
),
})
}
}

func handleDocs(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleDocs(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400")
render(w, r, tmpl.docs, "layout", map[string]any{
render(w, r, tmpl, "docs.html", map[string]any{
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
"OGImage": ogImageURL(a.Config, "social/default.png"),
})
}
}

func handleCompare(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleCompare(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400")
render(w, r, tmpl.compare, "layout", map[string]any{
render(w, r, tmpl, "compare.html", map[string]any{
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
"OGImage": ogImageURL(a.Config, "social/default.png"),
Expand All @@ -202,7 +212,7 @@ func handleCompare(a *app.App, tmpl *templateSet) http.HandlerFunc {

const untaggedPerPage = 20

func handleUntagged(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleUntagged(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page < 1 {
Expand All @@ -227,7 +237,7 @@ func handleUntagged(a *app.App, tmpl *templateSet) http.HandlerFunc {
_ = a.DB.QueryRowContext(r.Context(), "SELECT active_plugins FROM package_stats WHERE id = 1").Scan(&totalPlugins)

w.Header().Set("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400")
render(w, r, tmpl.untagged, "layout", map[string]any{
render(w, r, tmpl, "untagged.html", map[string]any{
"Packages": packages,
"Filter": filter,
"Search": search,
Expand All @@ -237,14 +247,18 @@ func handleUntagged(a *app.App, tmpl *templateSet) http.HandlerFunc {
"Total": int64(total),
"TotalPlugins": totalPlugins,
"TotalPages": totalPages,
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
"OGImage": ogImageURL(a.Config, "social/default.png"),
"Pagination": buildPagination(page, totalPages, "#untagged-results", "#untagged-form:top",
func(p int) string { return untaggedPaginateURL(filter, search, author, sort, p) },
func(p int) string { return untaggedPaginatePartialURL(filter, search, author, sort, p) },
),
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
"OGImage": ogImageURL(a.Config, "social/default.png"),
})
}
}

func handleUntaggedPartial(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleUntaggedPartial(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page < 1 {
Expand All @@ -266,7 +280,7 @@ func handleUntaggedPartial(a *app.App, tmpl *templateSet) http.HandlerFunc {
totalPages := (total + untaggedPerPage - 1) / untaggedPerPage

w.Header().Set("X-Robots-Tag", "noindex")
render(w, r, tmpl.untaggedPartial, "untagged-results", map[string]any{
render(w, r, tmpl, "untagged_results.html", map[string]any{
"Packages": packages,
"Filter": filter,
"Search": search,
Expand All @@ -275,6 +289,10 @@ func handleUntaggedPartial(a *app.App, tmpl *templateSet) http.HandlerFunc {
"Page": page,
"Total": int64(total),
"TotalPages": totalPages,
"Pagination": buildPagination(page, totalPages, "#untagged-results", "#untagged-form:top",
func(p int) string { return untaggedPaginateURL(filter, search, author, sort, p) },
func(p int) string { return untaggedPaginatePartialURL(filter, search, author, sort, p) },
),
})
}
}
Expand Down Expand Up @@ -324,10 +342,10 @@ func handleUntaggedAuthors(a *app.App) http.HandlerFunc {
}
}

func handleWordpressCore(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleWordpressCore(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400")
render(w, r, tmpl.wordpressCore, "layout", map[string]any{
render(w, r, tmpl, "wordpress_core.html", map[string]any{
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
"OGImage": ogImageURL(a.Config, "social/default.png"),
Expand All @@ -336,7 +354,7 @@ func handleWordpressCore(a *app.App, tmpl *templateSet) http.HandlerFunc {
}
}

func handleDetail(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleDetail(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
pkgType := r.PathValue("type")
name := r.PathValue("name")
Expand All @@ -351,7 +369,7 @@ func handleDetail(a *app.App, tmpl *templateSet) http.HandlerFunc {
http.Redirect(w, r, "https://wp-packages.org/", http.StatusFound)
} else {
w.WriteHeader(http.StatusNotFound)
render(w, r, tmpl.notFound, "layout", map[string]any{"Gone": false, "CDNURL": a.Config.R2.CDNPublicURL})
render(w, r, tmpl, "404.html", map[string]any{"Gone": false, "CDNURL": a.Config.R2.CDNPublicURL})
}
return
}
Expand Down Expand Up @@ -429,7 +447,7 @@ func handleDetail(a *app.App, tmpl *templateSet) http.HandlerFunc {
return
}

render(w, r, tmpl.detail, "layout", map[string]any{
render(w, r, tmpl, "detail.html", map[string]any{
"Package": pkg,
"Versions": versions,
"MonthlyInstalls": monthlyInstalls,
Expand All @@ -449,9 +467,9 @@ var logFiles = map[string]string{
"check-status": filepath.Join("storage", "logs", "check-status.log"),
}

func handleAdminLogs(tmpl *templateSet) http.HandlerFunc {
func handleAdminLogs(tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
render(w, r, tmpl.adminLogs, "admin_layout", nil)
render(w, r, tmpl, "admin_logs.html", nil)
}
}

Expand Down Expand Up @@ -937,7 +955,7 @@ type statusPageCheck struct {
Changes []packages.StatusCheckChange
}

func handleStatus(a *app.App, tmpl *templateSet) http.HandlerFunc {
func handleStatus(a *app.App, tmpl *jet.Set) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
cutoff := time.Now().Add(-24 * time.Hour).UnixMilli()
Expand Down Expand Up @@ -1022,10 +1040,12 @@ func handleStatus(a *app.App, tmpl *templateSet) http.HandlerFunc {
"PackagesUpdated24h": packagesUpdated24h,
"Deactivated24h": deactivated24h,
"Reactivated24h": reactivated24h,
"AppURL": a.Config.AppURL,
"CDNURL": a.Config.R2.CDNPublicURL,
}
if len(statusBuilds) > 0 {
data["LastBuildStartedAt"] = statusBuilds[0].StartedAt
}
render(w, r, tmpl.status, "layout", data)
render(w, r, tmpl, "status.html", data)
}
}
5 changes: 3 additions & 2 deletions internal/http/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"time"

"github.com/CloudyKit/jet/v6"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/roots/wp-packages/internal/app"
)
Expand Down Expand Up @@ -146,7 +147,7 @@ func NewRouter(a *app.App) http.Handler {
// handler touched the response (checked via a context flag set by routeMarker),
// replace the default body with the custom template. 405s and handler-generated
// 404s pass through untouched.
func appHandler(mux *http.ServeMux, tmpl *templateSet, a *app.App, sitemapPackages http.HandlerFunc) http.Handler {
func appHandler(mux *http.ServeMux, tmpl *jet.Set, a *app.App, sitemapPackages http.HandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Sitemap-packages prefix can't be expressed as a ServeMux pattern
// (wildcards must be full path segments).
Expand All @@ -163,7 +164,7 @@ func appHandler(mux *http.ServeMux, tmpl *templateSet, a *app.App, sitemapPackag
// so rec.dispatched is false only when the mux itself returned 404/405/etc.
if rec.code == http.StatusNotFound && !rec.dispatched {
w.WriteHeader(http.StatusNotFound)
render(w, r, tmpl.notFound, "layout", map[string]any{"Gone": false, "CDNURL": a.Config.R2.CDNPublicURL})
render(w, r, tmpl, "404.html", map[string]any{"Gone": false, "CDNURL": a.Config.R2.CDNPublicURL})
}
})
}
Expand Down
Loading
Loading