+56
-39
pkg/auth/hold_remote.go
+56
-39
pkg/auth/hold_remote.go
···
324
324
}
325
325
326
326
// isCrewMemberNoCache queries XRPC without caching (internal helper)
327
+
// Handles pagination to check all crew records, not just the first page
327
328
func (a *RemoteHoldAuthorizer) isCrewMemberNoCache(ctx context.Context, holdDID, userDID string) (bool, error) {
328
329
// Resolve DID to URL
329
330
holdURL := atproto.ResolveHoldURL(holdDID)
330
331
331
-
// Build XRPC request URL
332
-
// GET /xrpc/com.atproto.repo.listRecords?repo={did}&collection=io.atcr.hold.crew
333
-
xrpcURL := fmt.Sprintf("%s%s?repo=%s&collection=%s",
334
-
holdURL, atproto.RepoListRecords, url.QueryEscape(holdDID), url.QueryEscape(atproto.CrewCollection))
332
+
// Paginate through all crew records
333
+
cursor := ""
334
+
for {
335
+
// Build XRPC request URL with pagination
336
+
// GET /xrpc/com.atproto.repo.listRecords?repo={did}&collection=io.atcr.hold.crew&limit=100
337
+
xrpcURL := fmt.Sprintf("%s%s?repo=%s&collection=%s&limit=100",
338
+
holdURL, atproto.RepoListRecords, url.QueryEscape(holdDID), url.QueryEscape(atproto.CrewCollection))
339
+
if cursor != "" {
340
+
xrpcURL += "&cursor=" + url.QueryEscape(cursor)
341
+
}
335
342
336
-
req, err := http.NewRequestWithContext(ctx, "GET", xrpcURL, nil)
337
-
if err != nil {
338
-
return false, err
339
-
}
343
+
req, err := http.NewRequestWithContext(ctx, "GET", xrpcURL, nil)
344
+
if err != nil {
345
+
return false, err
346
+
}
340
347
341
-
resp, err := a.httpClient.Do(req)
342
-
if err != nil {
343
-
return false, fmt.Errorf("XRPC request failed: %w", err)
344
-
}
345
-
defer resp.Body.Close()
348
+
resp, err := a.httpClient.Do(req)
349
+
if err != nil {
350
+
return false, fmt.Errorf("XRPC request failed: %w", err)
351
+
}
346
352
347
-
if resp.StatusCode != http.StatusOK {
348
-
body, _ := io.ReadAll(resp.Body)
349
-
return false, fmt.Errorf("XRPC request failed: status %d: %s", resp.StatusCode, string(body))
350
-
}
353
+
if resp.StatusCode != http.StatusOK {
354
+
body, _ := io.ReadAll(resp.Body)
355
+
resp.Body.Close()
356
+
return false, fmt.Errorf("XRPC request failed: status %d: %s", resp.StatusCode, string(body))
357
+
}
358
+
359
+
// Parse response
360
+
var xrpcResp struct {
361
+
Cursor string `json:"cursor"`
362
+
Records []struct {
363
+
URI string `json:"uri"`
364
+
CID string `json:"cid"`
365
+
Value struct {
366
+
Type string `json:"$type"`
367
+
Member string `json:"member"`
368
+
Role string `json:"role"`
369
+
Permissions []string `json:"permissions"`
370
+
AddedAt string `json:"addedAt"`
371
+
} `json:"value"`
372
+
} `json:"records"`
373
+
}
351
374
352
-
// Parse response
353
-
var xrpcResp struct {
354
-
Records []struct {
355
-
URI string `json:"uri"`
356
-
CID string `json:"cid"`
357
-
Value struct {
358
-
Type string `json:"$type"`
359
-
Member string `json:"member"`
360
-
Role string `json:"role"`
361
-
Permissions []string `json:"permissions"`
362
-
AddedAt string `json:"addedAt"`
363
-
} `json:"value"`
364
-
} `json:"records"`
365
-
}
375
+
if err := json.NewDecoder(resp.Body).Decode(&xrpcResp); err != nil {
376
+
resp.Body.Close()
377
+
return false, fmt.Errorf("failed to decode XRPC response: %w", err)
378
+
}
379
+
resp.Body.Close()
366
380
367
-
if err := json.NewDecoder(resp.Body).Decode(&xrpcResp); err != nil {
368
-
return false, fmt.Errorf("failed to decode XRPC response: %w", err)
369
-
}
381
+
// Check if userDID is in this page of crew records
382
+
for _, record := range xrpcResp.Records {
383
+
if record.Value.Member == userDID {
384
+
// TODO: Check expiration if set
385
+
return true, nil
386
+
}
387
+
}
370
388
371
-
// Check if userDID is in the crew list
372
-
for _, record := range xrpcResp.Records {
373
-
if record.Value.Member == userDID {
374
-
// TODO: Check expiration if set
375
-
return true, nil
389
+
// Check if there are more pages
390
+
if xrpcResp.Cursor == "" || len(xrpcResp.Records) == 0 {
391
+
break
376
392
}
393
+
cursor = xrpcResp.Cursor
377
394
}
378
395
379
396
return false, nil