A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go

Setup linter #4

merged opened by cuducos.me targeting main from cuducos.me/at-container-registry: golangci-lint-setup

As discussed in PR #3, this PR starts working on the linting issue #4 little by little:

  • setting up the linter in the CI
  • (temporarily) configuring the linter to ignore packages not ready for linting yet
  • fixing issues reported on pkg/hold (choice here was 100% arbitrary, just to get started)
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:3272gdrjsuikiff7qsgokgas/sh.tangled.repo.pull/3mbosefvnfz22
+68 -65
Interdiff #0 #1
.golangci.yml

This file has not been changed.

.tangled/workflows/lint.yaml

This file has not been changed.

+1 -1
cmd/appview/serve.go
··· 383 383 logoURI := cfg.Server.BaseURL + "/web-app-manifest-192x192.png" 384 384 policyURI := cfg.Server.BaseURL + "/privacy" 385 385 tosURI := cfg.Server.BaseURL + "/terms" 386 + 386 - 387 387 metadata := config.ClientMetadata() 388 388 metadata.ClientName = &cfg.Server.ClientName 389 389 metadata.ClientURI = &cfg.Server.BaseURL
pkg/hold/pds/xrpc.go

This file has not been changed.

+39 -56
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 328 327 func (a *RemoteHoldAuthorizer) isCrewMemberNoCache(ctx context.Context, holdDID, userDID string) (bool, error) { 329 328 // Resolve DID to URL 330 329 holdURL := atproto.ResolveHoldURL(holdDID) 331 330 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 - } 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)) 342 335 343 - req, err := http.NewRequestWithContext(ctx, "GET", xrpcURL, nil) 344 - if err != nil { 345 - return false, err 346 - } 336 + req, err := http.NewRequestWithContext(ctx, "GET", xrpcURL, nil) 337 + if err != nil { 338 + return false, err 339 + } 347 340 348 - resp, err := a.httpClient.Do(req) 349 - if err != nil { 350 - return false, fmt.Errorf("XRPC request failed: %w", err) 351 - } 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() 352 346 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 - } 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 + } 358 351 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 - } 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 + } 374 366 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() 367 + if err := json.NewDecoder(resp.Body).Decode(&xrpcResp); err != nil { 368 + return false, fmt.Errorf("failed to decode XRPC response: %w", err) 369 + } 380 370 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 - } 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 387 376 } 388 - 389 - // Check if there are more pages 390 - if xrpcResp.Cursor == "" || len(xrpcResp.Records) == 0 { 391 - break 392 - } 393 - cursor = xrpcResp.Cursor 394 377 } 395 378 396 379 return false, nil
+9 -4
pkg/hold/admin/admin.go
··· 150 150 151 151 // Session management 152 152 153 - func (ui *AdminUI) createSession(did, handle string) string { 153 + func (ui *AdminUI) createSession(did, handle string) (string, error) { 154 154 b := make([]byte, 32) 155 - rand.Read(b) 155 + if _, err := rand.Read(b); err != nil { 156 + return "", fmt.Errorf("failed to create session token: %w", err) 157 + } 156 158 token := base64.URLEncoding.EncodeToString(b) 157 159 158 160 ui.sessionsMu.Lock() 159 161 ui.sessions[token] = &AdminSession{DID: did, Handle: handle} 160 162 ui.sessionsMu.Unlock() 161 163 162 - return token 164 + return token, nil 163 165 } 164 166 165 167 func (ui *AdminUI) getSession(token string) *AdminSession { ··· 340 342 341 343 w.Header().Set("Content-Type", "application/json") 342 344 w.Header().Set("Cache-Control", "public, max-age=3600") 343 - json.NewEncoder(w).Encode(metadata) 345 + if err := json.NewEncoder(w).Encode(metadata); err != nil { 346 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 347 + w.WriteHeader(http.StatusInternalServerError) 348 + } 344 349 } 345 350 346 351 // Close cleans up resources (no-op now, but keeps interface consistent)
+8 -2
pkg/hold/admin/handlers.go
··· 123 123 124 124 // Otherwise return JSON 125 125 w.Header().Set("Content-Type", "application/json") 126 - json.NewEncoder(w).Encode(stats) 126 + if err := json.NewEncoder(w).Encode(stats); err != nil { 127 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 128 + w.WriteHeader(http.StatusInternalServerError) 129 + } 127 130 } 128 131 129 132 // UserUsage represents storage usage for a user ··· 192 195 193 196 // Otherwise return JSON 194 197 w.Header().Set("Content-Type", "application/json") 195 - json.NewEncoder(w).Encode(users) 198 + if err := json.NewEncoder(w).Encode(users); err != nil { 199 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 200 + w.WriteHeader(http.StatusInternalServerError) 201 + } 196 202 }
+6 -1
pkg/hold/admin/handlers_auth.go
··· 117 117 } 118 118 119 119 // Create session and set cookie 120 - token := ui.createSession(did, handle) 120 + token, err := ui.createSession(did, handle) 121 + if err != nil { 122 + slog.Error("failed to create session token", "error", err, "path", r.URL.Path) 123 + w.WriteHeader(http.StatusInternalServerError) 124 + return 125 + } 121 126 ui.setSessionCookie(w, r, token) 122 127 123 128 slog.Info("Admin login successful", "did", did, "handle", handle)
+5 -1
pkg/hold/admin/handlers_crew.go
··· 319 319 320 320 // Re-apply tier to new record 321 321 if tier != "" { 322 - ui.pds.UpdateCrewMemberTier(ctx, current.Member, tier) 322 + if err := ui.pds.UpdateCrewMemberTier(ctx, current.Member, tier); err != nil { 323 + slog.Error("failed to update crew member tier", "error", err, "path", r.URL.Path) 324 + w.WriteHeader(http.StatusInternalServerError) 325 + return 326 + } 323 327 } 324 328 } 325 329

History

3 rounds 0 comments
sign up or login to add to the discussion
2 commits
expand
Implements linter for pkg/hold
Implements linter for pkg/hold missing warnings
expand 0 comments
pull request successfully merged
6 commits
expand
Implements linter for pkg/hold
fix bug creating layer records on non-tagged pushes
fix oauth scope mismatch
implement a basic crew management admin panel
fix oauth login on admin panel for production
Implements linter for pkg/hold missing warnings
expand 0 comments
1 commit
expand
Implements linter for pkg/hold
expand 0 comments