+2
-2
routes/git.go
+2
-2
routes/git.go
···
11
)
12
13
func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) {
14
-
name := uniqueName(r)
15
name = filepath.Clean(name)
16
17
repo := filepath.Join(d.c.Repo.ScanPath, name)
···
32
}
33
34
func (d *Handle) UploadPack(w http.ResponseWriter, r *http.Request) {
35
-
name := uniqueName(r)
36
name = filepath.Clean(name)
37
38
repo := filepath.Join(d.c.Repo.ScanPath, name)
···
11
)
12
13
func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) {
14
+
name := displayRepoName(r)
15
name = filepath.Clean(name)
16
17
repo := filepath.Join(d.c.Repo.ScanPath, name)
···
32
}
33
34
func (d *Handle) UploadPack(w http.ResponseWriter, r *http.Request) {
35
+
name := displayRepoName(r)
36
name = filepath.Clean(name)
37
38
repo := filepath.Join(d.c.Repo.ScanPath, name)
+3
-1
routes/handler.go
+3
-1
routes/handler.go
···
9
_ "github.com/bluesky-social/indigo/xrpc"
10
"github.com/go-chi/chi/v5"
11
"github.com/gorilla/sessions"
12
"github.com/icyphox/bild/config"
13
"github.com/icyphox/bild/db"
14
-
"github.com/icyphox/bild/routes/auth"
15
"github.com/icyphox/bild/routes/tmpl"
16
)
17
···
77
})
78
79
r.Route("/@{user}", func(r chi.Router) {
80
r.Get("/", h.Index)
81
82
// Repo routes
···
9
_ "github.com/bluesky-social/indigo/xrpc"
10
"github.com/go-chi/chi/v5"
11
"github.com/gorilla/sessions"
12
+
"github.com/icyphox/bild/auth"
13
"github.com/icyphox/bild/config"
14
"github.com/icyphox/bild/db"
15
+
"github.com/icyphox/bild/routes/middleware"
16
"github.com/icyphox/bild/routes/tmpl"
17
)
18
···
78
})
79
80
r.Route("/@{user}", func(r chi.Router) {
81
+
r.Use(middleware.AddDID)
82
r.Get("/", h.Index)
83
84
// Repo routes
+61
routes/middleware/did.go
+61
routes/middleware/did.go
···
···
1
+
package middleware
2
+
3
+
import (
4
+
"context"
5
+
"log"
6
+
"net/http"
7
+
"sync"
8
+
"time"
9
+
10
+
"github.com/bluesky-social/indigo/atproto/identity"
11
+
"github.com/icyphox/bild/auth"
12
+
)
13
+
14
+
type cachedIdent struct {
15
+
ident *identity.Identity
16
+
expiry time.Time
17
+
}
18
+
19
+
var (
20
+
identCache = make(map[string]cachedIdent)
21
+
cacheMutex sync.RWMutex
22
+
)
23
+
24
+
// Only use this middleware for routes that require a handle
25
+
// /@{user}/...
26
+
func AddDID(next http.Handler) http.Handler {
27
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28
+
user := r.PathValue("user")
29
+
30
+
// Check cache first
31
+
cacheMutex.RLock()
32
+
if cached, ok := identCache[user]; ok && time.Now().Before(cached.expiry) {
33
+
cacheMutex.RUnlock()
34
+
ctx := context.WithValue(r.Context(), "did", cached.ident.DID.String())
35
+
r = r.WithContext(ctx)
36
+
next.ServeHTTP(w, r)
37
+
return
38
+
}
39
+
cacheMutex.RUnlock()
40
+
41
+
// Cache miss - resolve and cache
42
+
ident, err := auth.ResolveIdent(r.Context(), user)
43
+
if err != nil {
44
+
log.Println("error resolving identity", err)
45
+
http.Error(w, "error resolving identity", http.StatusNotFound)
46
+
return
47
+
}
48
+
49
+
cacheMutex.Lock()
50
+
identCache[user] = cachedIdent{
51
+
ident: ident,
52
+
expiry: time.Now().Add(24 * time.Hour),
53
+
}
54
+
cacheMutex.Unlock()
55
+
56
+
ctx := context.WithValue(r.Context(), "did", ident.DID.String())
57
+
r = r.WithContext(ctx)
58
+
59
+
next.ServeHTTP(w, r)
60
+
})
61
+
}
+25
-36
routes/routes.go
+25
-36
routes/routes.go
···
22
"github.com/google/uuid"
23
"github.com/gorilla/sessions"
24
shbild "github.com/icyphox/bild/api/bild"
25
"github.com/icyphox/bild/config"
26
"github.com/icyphox/bild/db"
27
"github.com/icyphox/bild/git"
28
-
"github.com/icyphox/bild/routes/auth"
29
"github.com/russross/blackfriday/v2"
30
"golang.org/x/crypto/ssh"
31
)
···
39
}
40
41
func (h *Handle) Index(w http.ResponseWriter, r *http.Request) {
42
-
user := chi.URLParam(r, "user")
43
-
path := filepath.Join(h.c.Repo.ScanPath, user)
44
dirs, err := os.ReadDir(path)
45
if err != nil {
46
h.Write500(w)
···
75
}
76
77
infos = append(infos, info{
78
-
DisplayName: getDisplayName(name),
79
Name: name,
80
Desc: getDescription(path),
81
Idle: humanize.Time(c.Author.When),
···
98
}
99
100
func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
101
-
name := uniqueName(r)
102
if h.isIgnored(name) {
103
h.Write404(w)
104
return
105
}
106
107
-
name = filepath.Clean(name)
108
-
path := filepath.Join(h.c.Repo.ScanPath, name)
109
110
gr, err := git.Open(path, "")
111
if err != nil {
···
164
165
data := make(map[string]any)
166
data["name"] = name
167
-
data["displayname"] = getDisplayName(name)
168
data["ref"] = mainBranch
169
data["readme"] = readmeContent
170
data["commits"] = commits
···
182
}
183
184
func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) {
185
-
name := uniqueName(r)
186
if h.isIgnored(name) {
187
h.Write404(w)
188
return
···
190
treePath := chi.URLParam(r, "*")
191
ref := chi.URLParam(r, "ref")
192
193
-
name = filepath.Clean(name)
194
-
path := filepath.Join(h.c.Repo.ScanPath, name)
195
gr, err := git.Open(path, ref)
196
if err != nil {
197
h.Write404(w)
···
207
208
data := make(map[string]any)
209
data["name"] = name
210
-
data["displayname"] = getDisplayName(name)
211
data["ref"] = ref
212
data["parent"] = treePath
213
data["desc"] = getDescription(path)
···
223
raw = rawParam
224
}
225
226
-
name := uniqueName(r)
227
228
if h.isIgnored(name) {
229
h.Write404(w)
···
232
treePath := chi.URLParam(r, "*")
233
ref := chi.URLParam(r, "ref")
234
235
-
name = filepath.Clean(name)
236
-
path := filepath.Join(h.c.Repo.ScanPath, name)
237
gr, err := git.Open(path, ref)
238
if err != nil {
239
h.Write404(w)
···
247
}
248
data := make(map[string]any)
249
data["name"] = name
250
-
data["displayname"] = getDisplayName(name)
251
data["ref"] = ref
252
data["desc"] = getDescription(path)
253
data["path"] = treePath
···
266
}
267
268
func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
269
-
name := uniqueName(r)
270
if h.isIgnored(name) {
271
h.Write404(w)
272
return
···
288
setContentDisposition(w, filename)
289
setGZipMIME(w)
290
291
-
path := filepath.Join(h.c.Repo.ScanPath, name)
292
gr, err := git.Open(path, ref)
293
if err != nil {
294
h.Write404(w)
···
317
}
318
319
func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
320
-
name := uniqueName(r)
321
if h.isIgnored(name) {
322
h.Write404(w)
323
return
324
}
325
ref := chi.URLParam(r, "ref")
326
327
-
path := filepath.Join(h.c.Repo.ScanPath, name)
328
gr, err := git.Open(path, ref)
329
if err != nil {
330
h.Write404(w)
···
342
data["commits"] = commits
343
data["meta"] = h.c.Meta
344
data["name"] = name
345
-
data["displayname"] = getDisplayName(name)
346
data["ref"] = ref
347
data["desc"] = getDescription(path)
348
data["log"] = true
···
354
}
355
356
func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
357
-
name := uniqueName(r)
358
if h.isIgnored(name) {
359
h.Write404(w)
360
return
361
}
362
ref := chi.URLParam(r, "ref")
363
364
-
path := filepath.Join(h.c.Repo.ScanPath, name)
365
gr, err := git.Open(path, ref)
366
if err != nil {
367
h.Write404(w)
···
382
data["diff"] = diff.Diff
383
data["meta"] = h.c.Meta
384
data["name"] = name
385
-
data["displayname"] = getDisplayName(name)
386
data["ref"] = ref
387
data["desc"] = getDescription(path)
388
···
399
return
400
}
401
402
-
path := filepath.Join(h.c.Repo.ScanPath, name)
403
gr, err := git.Open(path, "")
404
if err != nil {
405
h.Write404(w)
···
423
424
data["meta"] = h.c.Meta
425
data["name"] = name
426
-
data["displayname"] = getDisplayName(name)
427
data["branches"] = branches
428
data["tags"] = tags
429
data["desc"] = getDescription(path)
···
550
name := r.FormValue("name")
551
description := r.FormValue("description")
552
553
-
repoPath := filepath.Join(h.c.Repo.ScanPath, handle, name)
554
err := git.InitBare(repoPath)
555
-
if err != nil {
556
-
h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.")
557
-
return
558
-
}
559
-
560
-
// For use by repoguard
561
-
didPath := filepath.Join(repoPath, "did")
562
-
err = os.WriteFile(didPath, []byte(did), 0644)
563
if err != nil {
564
h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.")
565
return
···
571
return
572
}
573
574
-
w.Header().Set("HX-Redirect", fmt.Sprintf("/@example.com/%s", name))
575
w.WriteHeader(http.StatusOK)
576
}
577
}
···
22
"github.com/google/uuid"
23
"github.com/gorilla/sessions"
24
shbild "github.com/icyphox/bild/api/bild"
25
+
"github.com/icyphox/bild/auth"
26
"github.com/icyphox/bild/config"
27
"github.com/icyphox/bild/db"
28
"github.com/icyphox/bild/git"
29
"github.com/russross/blackfriday/v2"
30
"golang.org/x/crypto/ssh"
31
)
···
39
}
40
41
func (h *Handle) Index(w http.ResponseWriter, r *http.Request) {
42
+
name := displayRepoName(r)
43
+
path := filepath.Join(h.c.Repo.ScanPath, name)
44
dirs, err := os.ReadDir(path)
45
if err != nil {
46
h.Write500(w)
···
75
}
76
77
infos = append(infos, info{
78
+
DisplayName: trimDotGit(name),
79
Name: name,
80
Desc: getDescription(path),
81
Idle: humanize.Time(c.Author.When),
···
98
}
99
100
func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
101
+
name := displayRepoName(r)
102
if h.isIgnored(name) {
103
h.Write404(w)
104
return
105
}
106
107
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
108
109
gr, err := git.Open(path, "")
110
if err != nil {
···
163
164
data := make(map[string]any)
165
data["name"] = name
166
+
data["displayname"] = trimDotGit(name)
167
data["ref"] = mainBranch
168
data["readme"] = readmeContent
169
data["commits"] = commits
···
181
}
182
183
func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) {
184
+
name := displayRepoName(r)
185
if h.isIgnored(name) {
186
h.Write404(w)
187
return
···
189
treePath := chi.URLParam(r, "*")
190
ref := chi.URLParam(r, "ref")
191
192
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
193
gr, err := git.Open(path, ref)
194
if err != nil {
195
h.Write404(w)
···
205
206
data := make(map[string]any)
207
data["name"] = name
208
+
data["displayname"] = trimDotGit(name)
209
data["ref"] = ref
210
data["parent"] = treePath
211
data["desc"] = getDescription(path)
···
221
raw = rawParam
222
}
223
224
+
name := displayRepoName(r)
225
226
if h.isIgnored(name) {
227
h.Write404(w)
···
230
treePath := chi.URLParam(r, "*")
231
ref := chi.URLParam(r, "ref")
232
233
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
234
gr, err := git.Open(path, ref)
235
if err != nil {
236
h.Write404(w)
···
244
}
245
data := make(map[string]any)
246
data["name"] = name
247
+
data["displayname"] = trimDotGit(name)
248
data["ref"] = ref
249
data["desc"] = getDescription(path)
250
data["path"] = treePath
···
263
}
264
265
func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
266
+
name := displayRepoName(r)
267
if h.isIgnored(name) {
268
h.Write404(w)
269
return
···
285
setContentDisposition(w, filename)
286
setGZipMIME(w)
287
288
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
289
gr, err := git.Open(path, ref)
290
if err != nil {
291
h.Write404(w)
···
314
}
315
316
func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
317
+
name := displayRepoName(r)
318
if h.isIgnored(name) {
319
h.Write404(w)
320
return
321
}
322
ref := chi.URLParam(r, "ref")
323
324
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
325
gr, err := git.Open(path, ref)
326
if err != nil {
327
h.Write404(w)
···
339
data["commits"] = commits
340
data["meta"] = h.c.Meta
341
data["name"] = name
342
+
data["displayname"] = trimDotGit(name)
343
data["ref"] = ref
344
data["desc"] = getDescription(path)
345
data["log"] = true
···
351
}
352
353
func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
354
+
name := displayRepoName(r)
355
if h.isIgnored(name) {
356
h.Write404(w)
357
return
358
}
359
ref := chi.URLParam(r, "ref")
360
361
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
362
gr, err := git.Open(path, ref)
363
if err != nil {
364
h.Write404(w)
···
379
data["diff"] = diff.Diff
380
data["meta"] = h.c.Meta
381
data["name"] = name
382
+
data["displayname"] = trimDotGit(name)
383
data["ref"] = ref
384
data["desc"] = getDescription(path)
385
···
396
return
397
}
398
399
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
400
gr, err := git.Open(path, "")
401
if err != nil {
402
h.Write404(w)
···
420
421
data["meta"] = h.c.Meta
422
data["name"] = name
423
+
data["displayname"] = trimDotGit(name)
424
data["branches"] = branches
425
data["tags"] = tags
426
data["desc"] = getDescription(path)
···
547
name := r.FormValue("name")
548
description := r.FormValue("description")
549
550
+
repoPath := filepath.Join(h.c.Repo.ScanPath, did, name)
551
err := git.InitBare(repoPath)
552
if err != nil {
553
h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.")
554
return
···
560
return
561
}
562
563
+
w.Header().Set("HX-Redirect", fmt.Sprintf("/@%s/%s", handle, name))
564
w.WriteHeader(http.StatusOK)
565
}
566
}
+19
-4
routes/util.go
+19
-4
routes/util.go
···
10
"strings"
11
12
"github.com/go-chi/chi/v5"
13
"github.com/icyphox/bild/git"
14
"github.com/microcosm-cc/bluemonday"
15
)
···
23
return err == nil
24
}
25
26
-
func uniqueName(r *http.Request) string {
27
-
user := chi.URLParam(r, "user")
28
name := chi.URLParam(r, "name")
29
-
return fmt.Sprintf("%s/%s", user, name)
30
}
31
32
-
func getDisplayName(name string) string {
33
return strings.TrimSuffix(name, ".git")
34
}
35
···
10
"strings"
11
12
"github.com/go-chi/chi/v5"
13
+
"github.com/icyphox/bild/auth"
14
"github.com/icyphox/bild/git"
15
"github.com/microcosm-cc/bluemonday"
16
)
···
24
return err == nil
25
}
26
27
+
func displayRepoName(r *http.Request) string {
28
+
user := r.Context().Value("did").(string)
29
name := chi.URLParam(r, "name")
30
+
31
+
handle, err := auth.ResolveIdent(r.Context(), user)
32
+
if err != nil {
33
+
log.Printf("failed to resolve ident: %s: %s", user, err)
34
+
return fmt.Sprintf("%s/%s", user, name)
35
+
}
36
+
37
+
return fmt.Sprintf("@%s/%s", handle.Handle.String(), name)
38
+
}
39
+
40
+
func didPath(r *http.Request) string {
41
+
did := r.Context().Value("did").(string)
42
+
path := filepath.Join(did, chi.URLParam(r, "name"))
43
+
filepath.Clean(path)
44
+
return path
45
}
46
47
+
func trimDotGit(name string) string {
48
return strings.TrimSuffix(name, ".git")
49
}
50