···23import (
4 "net/http"
5- "regexp"
6 "strings"
78 "github.com/go-chi/chi/v5"
09)
1011func (s *State) Router() http.Handler {
···19 // Check if the first path element is a valid handle without '@' or a flattened DID
20 pathParts := strings.SplitN(pat, "/", 2)
21 if len(pathParts) > 0 {
22- if isHandleNoAt(pathParts[0]) {
23 // Redirect to the same path but with '@' prefixed to the handle
24 redirectPath := "@" + pat
25 http.Redirect(w, r, "/"+redirectPath, http.StatusFound)
26 return
27- } else if isFlattenedDid(pathParts[0]) {
28 // Redirect to the unflattened DID version
29- unflattenedDid := unflattenDid(pathParts[0])
30 var redirectPath string
31 if len(pathParts) > 1 {
32 redirectPath = unflattenedDid + "/" + pathParts[1]
···42 })
4344 return router
45-}
46-47-func isHandleNoAt(s string) bool {
48- // ref: https://atproto.com/specs/handle
49- re := regexp.MustCompile(`^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
50- return re.MatchString(s)
51-}
52-53-func unflattenDid(s string) string {
54- if !isFlattenedDid(s) {
55- return s
56- }
57-58- parts := strings.SplitN(s[4:], "-", 2) // Skip "did-" prefix and split on first "-"
59- if len(parts) != 2 {
60- return s
61- }
62-63- return "did:" + parts[0] + ":" + parts[1]
64-}
65-66-// isFlattenedDid checks if the given string is a flattened DID.
67-// A flattened DID is a DID with the :s swapped to -s to satisfy certain
68-// application requirements, such as Go module naming conventions.
69-func isFlattenedDid(s string) bool {
70- // Check if the string starts with "did-"
71- if !strings.HasPrefix(s, "did-") {
72- return false
73- }
74-75- // Split the string to extract method and identifier
76- parts := strings.SplitN(s[4:], "-", 2) // Skip "did-" prefix and split on first "-"
77- if len(parts) != 2 {
78- return false
79- }
80-81- // Reconstruct as a standard DID format
82- // Example: "did-plc-xyz-abc" becomes "did:plc:xyz-abc"
83- reconstructed := "did:" + parts[0] + ":" + parts[1]
84- re := regexp.MustCompile(`^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$`)
85-86- return re.MatchString(reconstructed)
87}
8889func (s *State) UserRouter() http.Handler {
···23import (
4 "net/http"
05 "strings"
67 "github.com/go-chi/chi/v5"
8+ "github.com/sotangled/tangled/appview/state/userutil"
9)
1011func (s *State) Router() http.Handler {
···19 // Check if the first path element is a valid handle without '@' or a flattened DID
20 pathParts := strings.SplitN(pat, "/", 2)
21 if len(pathParts) > 0 {
22+ if userutil.IsHandleNoAt(pathParts[0]) {
23 // Redirect to the same path but with '@' prefixed to the handle
24 redirectPath := "@" + pat
25 http.Redirect(w, r, "/"+redirectPath, http.StatusFound)
26 return
27+ } else if userutil.IsFlattenedDid(pathParts[0]) {
28 // Redirect to the unflattened DID version
29+ unflattenedDid := userutil.UnflattenDid(pathParts[0])
30 var redirectPath string
31 if len(pathParts) > 1 {
32 redirectPath = unflattenedDid + "/" + pathParts[1]
···42 })
4344 return router
00000000000000000000000000000000000000000045}
4647func (s *State) UserRouter() http.Handler {
···1+package userutil
2+3+import (
4+ "regexp"
5+ "strings"
6+)
7+8+func IsHandleNoAt(s string) bool {
9+ // ref: https://atproto.com/specs/handle
10+ re := regexp.MustCompile(`^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
11+ return re.MatchString(s)
12+}
13+14+func UnflattenDid(s string) string {
15+ if !IsFlattenedDid(s) {
16+ return s
17+ }
18+19+ parts := strings.SplitN(s[4:], "-", 2) // Skip "did-" prefix and split on first "-"
20+ if len(parts) != 2 {
21+ return s
22+ }
23+24+ return "did:" + parts[0] + ":" + parts[1]
25+}
26+27+// IsFlattenedDid checks if the given string is a flattened DID.
28+func IsFlattenedDid(s string) bool {
29+ // Check if the string starts with "did-"
30+ if !strings.HasPrefix(s, "did-") {
31+ return false
32+ }
33+34+ // Split the string to extract method and identifier
35+ parts := strings.SplitN(s[4:], "-", 2) // Skip "did-" prefix and split on first "-"
36+ if len(parts) != 2 {
37+ return false
38+ }
39+40+ // Reconstruct as a standard DID format
41+ // Example: "did-plc-xyz-abc" becomes "did:plc:xyz-abc"
42+ reconstructed := "did:" + parts[0] + ":" + parts[1]
43+ re := regexp.MustCompile(`^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$`)
44+45+ return re.MatchString(reconstructed)
46+}
47+48+// FlattenDid converts a DID to a flattened format.
49+// A flattened DID is a DID with the :s swapped to -s to satisfy certain
50+// application requirements, such as Go module naming conventions.
51+func FlattenDid(s string) string {
52+ if !IsFlattenedDid(s) {
53+ return s
54+ }
55+56+ parts := strings.SplitN(s[4:], ":", 2) // Skip "did:" prefix and split on first ":"
57+ if len(parts) != 2 {
58+ return s
59+ }
60+61+ return "did-" + parts[0] + "-" + parts[1]
62+}