tangled
alpha
login
or
join now
margin.at
/
margin
88
fork
atom
Write on the margins of the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
88
fork
atom
overview
issues
4
pulls
1
pipelines
add highlight quick API handler
scanash.com
1 month ago
fb80a55b
2c84b534
+79
2 changed files
expand all
collapse all
unified
split
backend
internal
api
apikey.go
handler.go
+78
backend/internal/api/apikey.go
···
267
267
})
268
268
}
269
269
270
270
+
type QuickHighlightRequest struct {
271
271
+
URL string `json:"url"`
272
272
+
Selector interface{} `json:"selector"`
273
273
+
Color string `json:"color,omitempty"`
274
274
+
}
275
275
+
276
276
+
func (h *APIKeyHandler) QuickHighlight(w http.ResponseWriter, r *http.Request) {
277
277
+
apiKey, err := h.authenticateAPIKey(r)
278
278
+
if err != nil {
279
279
+
http.Error(w, err.Error(), http.StatusUnauthorized)
280
280
+
return
281
281
+
}
282
282
+
283
283
+
var req QuickHighlightRequest
284
284
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
285
285
+
http.Error(w, "Invalid request body", http.StatusBadRequest)
286
286
+
return
287
287
+
}
288
288
+
289
289
+
if req.URL == "" || req.Selector == nil {
290
290
+
http.Error(w, "URL and selector are required", http.StatusBadRequest)
291
291
+
return
292
292
+
}
293
293
+
294
294
+
session, err := h.getSessionByDID(apiKey.OwnerDID)
295
295
+
if err != nil {
296
296
+
http.Error(w, "User session not found. Please log in to margin.at first.", http.StatusUnauthorized)
297
297
+
return
298
298
+
}
299
299
+
300
300
+
urlHash := db.HashURL(req.URL)
301
301
+
color := req.Color
302
302
+
if color == "" {
303
303
+
color = "yellow"
304
304
+
}
305
305
+
306
306
+
record := xrpc.NewHighlightRecord(req.URL, urlHash, req.Selector, color, nil)
307
307
+
308
308
+
var result *xrpc.CreateRecordOutput
309
309
+
err = h.refresher.ExecuteWithAutoRefresh(r, session, func(client *xrpc.Client, did string) error {
310
310
+
var createErr error
311
311
+
result, createErr = client.CreateRecord(r.Context(), did, xrpc.CollectionHighlight, record)
312
312
+
return createErr
313
313
+
})
314
314
+
if err != nil {
315
315
+
http.Error(w, "Failed to create highlight: "+err.Error(), http.StatusInternalServerError)
316
316
+
return
317
317
+
}
318
318
+
319
319
+
h.db.UpdateAPIKeyLastUsed(apiKey.ID)
320
320
+
321
321
+
selectorJSON, _ := json.Marshal(req.Selector)
322
322
+
selectorStr := string(selectorJSON)
323
323
+
colorPtr := &color
324
324
+
325
325
+
highlight := &db.Highlight{
326
326
+
URI: result.URI,
327
327
+
AuthorDID: apiKey.OwnerDID,
328
328
+
TargetSource: req.URL,
329
329
+
TargetHash: urlHash,
330
330
+
SelectorJSON: &selectorStr,
331
331
+
Color: colorPtr,
332
332
+
CreatedAt: time.Now(),
333
333
+
IndexedAt: time.Now(),
334
334
+
CID: &result.CID,
335
335
+
}
336
336
+
if err := h.db.CreateHighlight(highlight); err != nil {
337
337
+
fmt.Printf("Warning: failed to index highlight in local DB: %v\n", err)
338
338
+
}
339
339
+
340
340
+
w.Header().Set("Content-Type", "application/json")
341
341
+
json.NewEncoder(w).Encode(map[string]string{
342
342
+
"uri": result.URI,
343
343
+
"cid": result.CID,
344
344
+
"message": "Highlight created successfully",
345
345
+
})
346
346
+
}
347
347
+
270
348
func (h *APIKeyHandler) authenticateAPIKey(r *http.Request) (*db.APIKey, error) {
271
349
auth := r.Header.Get("Authorization")
272
350
if auth == "" {
+1
backend/internal/api/handler.go
···
78
78
79
79
r.Post("/quick/bookmark", h.apiKeys.QuickBookmark)
80
80
r.Post("/quick/annotation", h.apiKeys.QuickAnnotation)
81
81
+
r.Post("/quick/highlight", h.apiKeys.QuickHighlight)
81
82
})
82
83
}
83
84