···44 "log"
55 "github.com/gin-gonic/gin"
6677+ "net/url"
88+ "strings"
79 "fmt"
810 "bytes"
911 "encoding/json"
···3840 r.POST("/xrpc/com.atproto.sync.requestCrawl", getDidThenHandleSyncXRPC)
394140424343+ // TODO validate sig & move some of this out of here
4144 r.POST("/oauth/token", func(c *gin.Context) {
4242- // 1. Read the body bytes from Gin's request object
4545+ // 1. Read the body bytes
4346 bodyBytes, err := io.ReadAll(c.Request.Body)
4447 if err != nil {
4548 fmt.Printf("Error reading body: %v\n", err)
4949+ c.AbortWithStatus(http.StatusBadRequest)
5050+ return
4651 }
5252+5353+ dpopHeader := c.GetHeader("DPoP")
5454+5555+ // 2. Parse the x-www-form-urlencoded body
5656+ formValues, err := url.ParseQuery(string(bodyBytes))
5757+ if err != nil {
5858+ fmt.Printf("Error parsing form data: %v\n", err)
5959+ }
6060+6161+ // Add dpop and reencode
6262+ formValues.Set("dpop", dpopHeader)
6363+ bodyBytes = []byte(formValues.Encode())
6464+6565+ codeJWT := formValues.Get("code")
6666+6767+ // Default fallback in case extraction fails
6868+ fullHost := c.Request.Host
6969+ did := "did:web:" + fullHost
7070+7171+ // 3. Extract the 'sub' claim from the code JWT
7272+ if codeJWT != "" {
7373+ parts := strings.Split(codeJWT, ".")
7474+ if len(parts) == 3 { // A valid JWT has 3 parts
7575+ // The payload is the second part (index 1)
7676+ // JWT uses RawURLEncoding (no padding)
7777+ payloadBytes, err := base64.RawURLEncoding.DecodeString(parts[1])
7878+ if err == nil {
7979+ var payload struct {
8080+ Sub string `json:"sub"`
8181+ }
8282+ if err := json.Unmarshal(payloadBytes, &payload); err == nil && payload.Sub != "" {
8383+ did = payload.Sub // Successfully extracted did:web:...
8484+ }
8585+ } else {
8686+ fmt.Printf("Error decoding JWT payload: %v\n", err)
8787+ }
8888+ }
8989+ }
9090+9191+ // 4. Debug outputs
9292+ //fmt.Printf("--- DEBUG REQUEST BODY ---\n%s\n---------------------\n", string(bodyBytes))
9393+ fmt.Printf("Routing sync to DID: %s\n", did)
9494+9595+ // 5. CRITICAL: Restore the stream back into Gin's request object
9696+ c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
9797+9898+ // 6. Hand over to your actual handler
9999+ handleSyncXRPC(c, did)
100100+ })
101101+102102+ //r.POST("/oauth/token", func(c *gin.Context) {
103103+ // // 1. Read the body bytes from Gin's request object
104104+ // bodyBytes, err := io.ReadAll(c.Request.Body)
105105+ // if err != nil {
106106+ // fmt.Printf("Error reading body: %v\n", err)
107107+ // }
471084848- // 2. Print it out
4949- fmt.Printf("--- DEBUG REQUEST BODY ---\n%s\n---------------------\n", string(bodyBytes))
109109+ // // 2. Print it out
110110+ // fmt.Printf("--- DEBUG REQUEST BODY ---\n%s\n---------------------\n", string(bodyBytes))
501115151- // 3. CRITICAL: Restore the stream back into Gin's request object
5252- c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
112112+ // // 3. CRITICAL: Restore the stream back into Gin's request object
113113+ // c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
531145454- // 4. Now hand over the standard w and r to your actual handler
5555- getDidThenHandleSyncXRPC(c)
5656- })
115115+ // // 4. Now hand over the standard w and r to your actual handler
116116+ // getDidThenHandleSyncXRPC(c)
117117+ // handleSyncXRPC(c, "did:web:" + fullHost)
118118+ //})
5711958120 r.POST("/xrpc/com.atproto.repo.uploadBlob", func(c *gin.Context) {
59121 // Set the response header so the client knows it's getting base64 text
···2020//
2121// The pending channel carries signalMsg for the header frame, then the
2222// peer's readPump sends the binary body via a separate binaryPending channel.
2323-func requestFromPeer(ctx context.Context, p *peer, method string, params map[string]string) (dcResponse, error) {
2323+func requestFromPeer(ctx context.Context, p *peer, method string, params map[string]string, body []byte) (dcResponse, error) {
2424 requestID := randomHex(16)
25252626 headerCh := make(chan signalMsg, 1)
···3838 p.mu.Unlock()
3939 }()
40404141- p.sendMsg(signalMsg{
4242- Type: "request",
4343- RequestID: requestID,
4444- Method: method,
4545- Params: params,
4646- })
4141+ // Initialize the message
4242+ msg := signalMsg{
4343+ Type: "request",
4444+ RequestID: requestID,
4545+ Method: method,
4646+ Params: params,
4747+ }
4848+4949+ // Only set Length if it's above 0
5050+ if len(body) > 0 {
5151+ msg.Length = len(body)
5252+ }
5353+5454+ // Send the prepared message
5555+ p.sendMsg(msg)
5656+5757+ if len(body) > 0 {
5858+ // Assuming you have or will create a sendBinary method on your peer struct.
5959+ // This should wrap conn.WriteMessage(websocket.BinaryMessage, body)
6060+ p.sendBinary(body)
6161+ }
47624863 timeout := time.NewTimer(60 * time.Second)
4964 defer timeout.Stop()
···98113 return nil, false
99114 }
100115 return &h, true
101101-}116116+}
+1-1
extension/atproto-repo.js
···993993 const out = new Uint8Array(binary.length);
994994 for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
995995 return out;
996996-}996996+}