forked from tangled.org/core
this repo has no description

routes: resolve did to handle in middleware

So handles are now purely cosmetic and are resolved to DIDs in the middleware.
auth.ResolveIdent seems to add a lot of latency, so we cache the resolved ident
for 24 hours. It's still not as perfomant but we can come back to this later.

Changed files
+110 -43
routes
+2 -2
routes/git.go
···
)
func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) {
-
name := uniqueName(r)
+
name := displayRepoName(r)
name = filepath.Clean(name)
repo := filepath.Join(d.c.Repo.ScanPath, name)
···
}
func (d *Handle) UploadPack(w http.ResponseWriter, r *http.Request) {
-
name := uniqueName(r)
+
name := displayRepoName(r)
name = filepath.Clean(name)
repo := filepath.Join(d.c.Repo.ScanPath, name)
+3 -1
routes/handler.go
···
_ "github.com/bluesky-social/indigo/xrpc"
"github.com/go-chi/chi/v5"
"github.com/gorilla/sessions"
+
"github.com/icyphox/bild/auth"
"github.com/icyphox/bild/config"
"github.com/icyphox/bild/db"
-
"github.com/icyphox/bild/routes/auth"
+
"github.com/icyphox/bild/routes/middleware"
"github.com/icyphox/bild/routes/tmpl"
)
···
})
r.Route("/@{user}", func(r chi.Router) {
+
r.Use(middleware.AddDID)
r.Get("/", h.Index)
// Repo routes
+61
routes/middleware/did.go
···
+
package middleware
+
+
import (
+
"context"
+
"log"
+
"net/http"
+
"sync"
+
"time"
+
+
"github.com/bluesky-social/indigo/atproto/identity"
+
"github.com/icyphox/bild/auth"
+
)
+
+
type cachedIdent struct {
+
ident *identity.Identity
+
expiry time.Time
+
}
+
+
var (
+
identCache = make(map[string]cachedIdent)
+
cacheMutex sync.RWMutex
+
)
+
+
// Only use this middleware for routes that require a handle
+
// /@{user}/...
+
func AddDID(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
user := r.PathValue("user")
+
+
// Check cache first
+
cacheMutex.RLock()
+
if cached, ok := identCache[user]; ok && time.Now().Before(cached.expiry) {
+
cacheMutex.RUnlock()
+
ctx := context.WithValue(r.Context(), "did", cached.ident.DID.String())
+
r = r.WithContext(ctx)
+
next.ServeHTTP(w, r)
+
return
+
}
+
cacheMutex.RUnlock()
+
+
// Cache miss - resolve and cache
+
ident, err := auth.ResolveIdent(r.Context(), user)
+
if err != nil {
+
log.Println("error resolving identity", err)
+
http.Error(w, "error resolving identity", http.StatusNotFound)
+
return
+
}
+
+
cacheMutex.Lock()
+
identCache[user] = cachedIdent{
+
ident: ident,
+
expiry: time.Now().Add(24 * time.Hour),
+
}
+
cacheMutex.Unlock()
+
+
ctx := context.WithValue(r.Context(), "did", ident.DID.String())
+
r = r.WithContext(ctx)
+
+
next.ServeHTTP(w, r)
+
})
+
}
+25 -36
routes/routes.go
···
"github.com/google/uuid"
"github.com/gorilla/sessions"
shbild "github.com/icyphox/bild/api/bild"
+
"github.com/icyphox/bild/auth"
"github.com/icyphox/bild/config"
"github.com/icyphox/bild/db"
"github.com/icyphox/bild/git"
-
"github.com/icyphox/bild/routes/auth"
"github.com/russross/blackfriday/v2"
"golang.org/x/crypto/ssh"
)
···
}
func (h *Handle) Index(w http.ResponseWriter, r *http.Request) {
-
user := chi.URLParam(r, "user")
-
path := filepath.Join(h.c.Repo.ScanPath, user)
+
name := displayRepoName(r)
+
path := filepath.Join(h.c.Repo.ScanPath, name)
dirs, err := os.ReadDir(path)
if err != nil {
h.Write500(w)
···
}
infos = append(infos, info{
-
DisplayName: getDisplayName(name),
+
DisplayName: trimDotGit(name),
Name: name,
Desc: getDescription(path),
Idle: humanize.Time(c.Author.When),
···
}
func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
-
name := uniqueName(r)
+
name := displayRepoName(r)
if h.isIgnored(name) {
h.Write404(w)
return
}
-
name = filepath.Clean(name)
-
path := filepath.Join(h.c.Repo.ScanPath, name)
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, "")
if err != nil {
···
data := make(map[string]any)
data["name"] = name
-
data["displayname"] = getDisplayName(name)
+
data["displayname"] = trimDotGit(name)
data["ref"] = mainBranch
data["readme"] = readmeContent
data["commits"] = commits
···
}
func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) {
-
name := uniqueName(r)
+
name := displayRepoName(r)
if h.isIgnored(name) {
h.Write404(w)
return
···
treePath := chi.URLParam(r, "*")
ref := chi.URLParam(r, "ref")
-
name = filepath.Clean(name)
-
path := filepath.Join(h.c.Repo.ScanPath, name)
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
h.Write404(w)
···
data := make(map[string]any)
data["name"] = name
-
data["displayname"] = getDisplayName(name)
+
data["displayname"] = trimDotGit(name)
data["ref"] = ref
data["parent"] = treePath
data["desc"] = getDescription(path)
···
raw = rawParam
}
-
name := uniqueName(r)
+
name := displayRepoName(r)
if h.isIgnored(name) {
h.Write404(w)
···
treePath := chi.URLParam(r, "*")
ref := chi.URLParam(r, "ref")
-
name = filepath.Clean(name)
-
path := filepath.Join(h.c.Repo.ScanPath, name)
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
h.Write404(w)
···
}
data := make(map[string]any)
data["name"] = name
-
data["displayname"] = getDisplayName(name)
+
data["displayname"] = trimDotGit(name)
data["ref"] = ref
data["desc"] = getDescription(path)
data["path"] = treePath
···
}
func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
-
name := uniqueName(r)
+
name := displayRepoName(r)
if h.isIgnored(name) {
h.Write404(w)
return
···
setContentDisposition(w, filename)
setGZipMIME(w)
-
path := filepath.Join(h.c.Repo.ScanPath, name)
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
h.Write404(w)
···
}
func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
-
name := uniqueName(r)
+
name := displayRepoName(r)
if h.isIgnored(name) {
h.Write404(w)
return
}
ref := chi.URLParam(r, "ref")
-
path := filepath.Join(h.c.Repo.ScanPath, name)
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
h.Write404(w)
···
data["commits"] = commits
data["meta"] = h.c.Meta
data["name"] = name
-
data["displayname"] = getDisplayName(name)
+
data["displayname"] = trimDotGit(name)
data["ref"] = ref
data["desc"] = getDescription(path)
data["log"] = true
···
}
func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
-
name := uniqueName(r)
+
name := displayRepoName(r)
if h.isIgnored(name) {
h.Write404(w)
return
}
ref := chi.URLParam(r, "ref")
-
path := filepath.Join(h.c.Repo.ScanPath, name)
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
h.Write404(w)
···
data["diff"] = diff.Diff
data["meta"] = h.c.Meta
data["name"] = name
-
data["displayname"] = getDisplayName(name)
+
data["displayname"] = trimDotGit(name)
data["ref"] = ref
data["desc"] = getDescription(path)
···
return
}
-
path := filepath.Join(h.c.Repo.ScanPath, name)
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, "")
if err != nil {
h.Write404(w)
···
data["meta"] = h.c.Meta
data["name"] = name
-
data["displayname"] = getDisplayName(name)
+
data["displayname"] = trimDotGit(name)
data["branches"] = branches
data["tags"] = tags
data["desc"] = getDescription(path)
···
name := r.FormValue("name")
description := r.FormValue("description")
-
repoPath := filepath.Join(h.c.Repo.ScanPath, handle, name)
+
repoPath := filepath.Join(h.c.Repo.ScanPath, did, name)
err := git.InitBare(repoPath)
-
if err != nil {
-
h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.")
-
return
-
}
-
-
// For use by repoguard
-
didPath := filepath.Join(repoPath, "did")
-
err = os.WriteFile(didPath, []byte(did), 0644)
if err != nil {
h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.")
return
···
return
}
-
w.Header().Set("HX-Redirect", fmt.Sprintf("/@example.com/%s", name))
+
w.Header().Set("HX-Redirect", fmt.Sprintf("/@%s/%s", handle, name))
w.WriteHeader(http.StatusOK)
}
}
+19 -4
routes/util.go
···
"strings"
"github.com/go-chi/chi/v5"
+
"github.com/icyphox/bild/auth"
"github.com/icyphox/bild/git"
"github.com/microcosm-cc/bluemonday"
)
···
return err == nil
}
-
func uniqueName(r *http.Request) string {
-
user := chi.URLParam(r, "user")
+
func displayRepoName(r *http.Request) string {
+
user := r.Context().Value("did").(string)
name := chi.URLParam(r, "name")
-
return fmt.Sprintf("%s/%s", user, name)
+
+
handle, err := auth.ResolveIdent(r.Context(), user)
+
if err != nil {
+
log.Printf("failed to resolve ident: %s: %s", user, err)
+
return fmt.Sprintf("%s/%s", user, name)
+
}
+
+
return fmt.Sprintf("@%s/%s", handle.Handle.String(), name)
+
}
+
+
func didPath(r *http.Request) string {
+
did := r.Context().Value("did").(string)
+
path := filepath.Join(did, chi.URLParam(r, "name"))
+
filepath.Clean(path)
+
return path
}
-
func getDisplayName(name string) string {
+
func trimDotGit(name string) string {
return strings.TrimSuffix(name, ".git")
}