Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com

fix: add owner-DID-scoped doc routes so collaborators can view shared documents

+48 -13
+1 -1
.gitignore
··· 16 16 .Trashes 17 17 ehthumbs.db 18 18 Thumbs.db 19 - server 19 + /server
+2
cmd/server/main.go
··· 101 101 mux.HandleFunc("POST /docs/new", h.NewDocumentSubmit) 102 102 mux.HandleFunc("GET /docs/{rkey}", h.DocumentView) 103 103 mux.HandleFunc("GET /docs/{rkey}/edit", h.DocumentEdit) 104 + // Collaborator-scoped routes (owner DID in path) 105 + mux.HandleFunc("GET /docs/{did}/{rkey}", h.CollaboratorDocumentView) 104 106 105 107 // API 106 108 mux.HandleFunc("POST /api/render", h.APIRender)
+45 -12
internal/handler/handler.go
··· 269 269 http.Redirect(w, r, fmt.Sprintf("/docs/%s/edit", rkey), http.StatusSeeOther) 270 270 } 271 271 272 - // DocumentView renders a document as HTML. 273 - func (h *Handler) DocumentView(w http.ResponseWriter, r *http.Request) { 274 - user := h.currentUser(r) 275 - if user == nil { 276 - http.Redirect(w, r, "/auth/atproto", http.StatusSeeOther) 277 - return 278 - } 279 - 280 - rkey := r.PathValue("rkey") 281 - client, err := h.xrpcClient(user.ID) 272 + // documentView is the shared implementation for viewing a document given an ownerDID and rkey. 273 + func (h *Handler) documentView(w http.ResponseWriter, r *http.Request, ownerUserID, ownerDID, rkey string) { 274 + client, err := h.xrpcClient(ownerUserID) 282 275 if err != nil { 283 276 http.Error(w, "Could not connect to PDS", 500) 284 277 return 285 278 } 286 279 287 - value, _, err := client.GetRecord(client.DID(), collectionDocument, rkey) 280 + value, _, err := client.GetRecord(ownerDID, collectionDocument, rkey) 288 281 if err != nil { 289 282 http.NotFound(w, r) 290 283 return ··· 302 295 rendered, _ = render.Markdown([]byte(doc.Content.Text.RawMarkdown)) 303 296 } 304 297 298 + user := h.currentUser(r) 305 299 type DocumentViewData struct { 306 300 Doc *model.Document 307 301 Rendered template.HTML ··· 311 305 User: user, 312 306 Content: DocumentViewData{Doc: doc, Rendered: template.HTML(rendered)}, 313 307 }) 308 + } 309 + 310 + // DocumentView renders a document as HTML (owner viewing their own doc). 311 + func (h *Handler) DocumentView(w http.ResponseWriter, r *http.Request) { 312 + user := h.currentUser(r) 313 + if user == nil { 314 + http.Redirect(w, r, "/auth/atproto", http.StatusSeeOther) 315 + return 316 + } 317 + 318 + rkey := r.PathValue("rkey") 319 + client, err := h.xrpcClient(user.ID) 320 + if err != nil { 321 + http.Error(w, "Could not connect to PDS", 500) 322 + return 323 + } 324 + 325 + h.documentView(w, r, user.ID, client.DID(), rkey) 326 + } 327 + 328 + // CollaboratorDocumentView renders a document owned by another user (collaborator access). 329 + func (h *Handler) CollaboratorDocumentView(w http.ResponseWriter, r *http.Request) { 330 + user := h.currentUser(r) 331 + if user == nil { 332 + http.Redirect(w, r, "/auth/atproto", http.StatusSeeOther) 333 + return 334 + } 335 + 336 + ownerDID := r.PathValue("did") 337 + rkey := r.PathValue("rkey") 338 + 339 + ownerUser, err := h.DB.GetUserByDID(ownerDID) 340 + if err != nil { 341 + http.NotFound(w, r) 342 + return 343 + } 344 + 345 + h.documentView(w, r, ownerUser.ID, ownerDID, rkey) 314 346 } 315 347 316 348 // DocumentEdit renders the editor for a document. ··· 577 609 578 610 h.DB.DeleteInvite(invite.Token) 579 611 580 - http.Redirect(w, r, "/docs/"+rKey, http.StatusSeeOther) 612 + // Redirect to owner-scoped document URL so the view handler knows whose PDS to query. 613 + http.Redirect(w, r, "/docs/"+invite.CreatedBy+"/"+rKey, http.StatusSeeOther) 581 614 } 582 615 583 616 // --- API: Comments ---