···397397 return
398398 }
399399400400- var metadataMap map[string]interface{}
400400+ var metadataMap map[string]any
401401 if err := json.Unmarshal(metadataBytes, &metadataMap); err != nil {
402402 http.Error(w, "Failed to unmarshal metadata", http.StatusInternalServerError)
403403 return
+70-29
cmd/credential-helper/main.go
···124124 fmt.Printf("docker-credential-atcr %s (commit: %s, built: %s)\n", version, commit, date)
125125 case "update":
126126 checkOnly := len(os.Args) > 2 && os.Args[2] == "--check"
127127- handleUpdate(checkOnly)
127127+ if err := handleUpdate(checkOnly); err != nil {
128128+ fmt.Fprintf(os.Stderr, "Could not update: %v", err)
129129+ os.Exit(1)
130130+ }
128131 default:
129132 fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command)
130133 os.Exit(1)
···180183181184 // Wait for user to complete OAuth flow, then retry
182185 fmt.Fprintf(os.Stderr, "Waiting for authentication")
183183- for i := 0; i < 60; i++ { // Wait up to 2 minutes
186186+ for range 60 { // Wait up to 2 minutes
184187 time.Sleep(2 * time.Second)
185188 fmt.Fprintf(os.Stderr, ".")
186189···246249 }
247250248251 // Check for updates (non-blocking due to 24h cache)
249249- checkAndNotifyUpdate(appViewURL)
252252+ if err := checkAndNotifyUpdate(appViewURL); err != nil {
253253+ fmt.Fprintf(os.Stderr, "Error checking for updates: %v", err)
254254+ }
250255251256 // Return credentials for Docker
252257 creds := Credentials{
···385390 }
386391387392 var tokenResult DeviceTokenResponse
388388- json.NewDecoder(tokenResp.Body).Decode(&tokenResult)
393393+ if err := json.NewDecoder(tokenResp.Body).Decode(&tokenResult); err != nil {
394394+ return nil, err
395395+ }
389396 tokenResp.Body.Close()
390397391398 if tokenResult.Error == "authorization_pending" {
···680687}
681688682689// handleUpdate handles the update command
683683-func handleUpdate(checkOnly bool) {
690690+func handleUpdate(checkOnly bool) error {
684691 // Default API URL
685692 apiURL := "https://atcr.io/api/credential-helper/version"
686693···704711 }
705712706713 // Compare versions
707707- if !isNewerVersion(versionInfo.Latest, version) {
714714+ shouldUpdate, err := isNewerVersion(versionInfo.Latest, version)
715715+ if err != nil {
716716+ return err
717717+ }
718718+ if shouldUpdate {
708719 fmt.Printf("You're already running the latest version (%s)\n", version)
709709- return
720720+ return nil
710721 }
711722712723 fmt.Printf("New version available: %s (current: %s)\n", versionInfo.Latest, version)
713724714725 if checkOnly {
715715- return
726726+ return nil
716727 }
717728718729 // Perform the update
···722733 }
723734724735 fmt.Println("Update completed successfully!")
736736+ return nil
725737}
726738727739// fetchVersionInfo fetches version info from the AppView API
···750762751763// isNewerVersion compares two version strings (simple semver comparison)
752764// Returns true if newVersion is newer than currentVersion
753753-func isNewerVersion(newVersion, currentVersion string) bool {
765765+func isNewerVersion(newVersion, currentVersion string) (bool, error) {
754766 // Handle "dev" version
755767 if currentVersion == "dev" {
756756- return true
768768+ return true, nil
757769 }
758770759771 // Normalize versions (strip 'v' prefix)
···768780 for i := 0; i < len(newParts) && i < len(curParts); i++ {
769781 newNum := 0
770782 curNum := 0
771771- fmt.Sscanf(newParts[i], "%d", &newNum)
772772- fmt.Sscanf(curParts[i], "%d", &curNum)
783783+ if _, err := fmt.Sscanf(newParts[i], "%d", &newNum); err != nil {
784784+ return false, err
785785+ }
786786+ if _, err := fmt.Sscanf(curParts[i], "%d", &curNum); err != nil {
787787+ return false, err
788788+ }
773789774790 if newNum > curNum {
775775- return true
791791+ return true, nil
776792 }
777793 if newNum < curNum {
778778- return false
794794+ return false, nil
779795 }
780796 }
781797782798 // If new version has more parts (e.g., 1.0.1 vs 1.0), it's newer
783783- return len(newParts) > len(curParts)
799799+ return len(newParts) > len(curParts), nil
784800}
785801786802// getPlatformKey returns the platform key for the current OS/arch
···881897 // Install new binary
882898 if err := copyFile(binaryPath, currentPath); err != nil {
883899 // Try to restore backup
884884- os.Rename(backupPath, currentPath)
900900+ if err := os.Rename(backupPath, currentPath); err != nil {
901901+ return err
902902+ }
885903 return fmt.Errorf("failed to install new binary: %w", err)
886904 }
887905888906 // Set executable permissions
889907 if err := os.Chmod(currentPath, 0755); err != nil {
890908 // Try to restore backup
891891- os.Remove(currentPath)
892892- os.Rename(backupPath, currentPath)
909909+ if err := os.Remove(currentPath); err != nil {
910910+ return err
911911+ }
912912+ if err := os.Rename(backupPath, currentPath); err != nil {
913913+ return err
914914+ }
893915 return fmt.Errorf("failed to set permissions: %w", err)
894916 }
895917···970992}
971993972994// checkAndNotifyUpdate checks for updates in the background and notifies the user
973973-func checkAndNotifyUpdate(appViewURL string) {
995995+func checkAndNotifyUpdate(appViewURL string) error {
974996 // Check if we've already checked recently
975997 cache := loadUpdateCheckCache()
976998 if cache != nil && time.Since(cache.CheckedAt) < updateCheckCacheTTL && cache.Current == version {
977999 // Cache is fresh and for current version
978978- if isNewerVersion(cache.Latest, version) {
10001000+ shouldUpdate, err := isNewerVersion(cache.Latest, version)
10011001+ if err != nil {
10021002+ return err
10031003+ }
10041004+ if shouldUpdate {
9791005 fmt.Fprintf(os.Stderr, "\nNote: A new version of docker-credential-atcr is available (%s).\n", cache.Latest)
9801006 fmt.Fprintf(os.Stderr, "Run 'docker-credential-atcr update' to upgrade.\n\n")
9811007 }
982982- return
10081008+ return nil
9831009 }
98410109851011 // Fetch version info
···9871013 versionInfo, err := fetchVersionInfo(apiURL)
9881014 if err != nil {
9891015 // Silently fail - don't interrupt credential retrieval
990990- return
10161016+ return nil
9911017 }
99210189931019 // Save to cache
994994- saveUpdateCheckCache(&UpdateCheckCache{
10201020+ err = saveUpdateCheckCache(&UpdateCheckCache{
9951021 CheckedAt: time.Now(),
9961022 Latest: versionInfo.Latest,
9971023 Current: version,
9981024 })
10251025+ if err != nil {
10261026+ return err
10271027+ }
999102810001029 // Notify if newer version available
10011001- if isNewerVersion(versionInfo.Latest, version) {
10301030+ shouldUpdate, err := isNewerVersion(versionInfo.Latest, version)
10311031+ if err != nil {
10321032+ return err
10331033+ }
10341034+ if shouldUpdate {
10021035 fmt.Fprintf(os.Stderr, "\nNote: A new version of docker-credential-atcr is available (%s).\n", versionInfo.Latest)
10031036 fmt.Fprintf(os.Stderr, "Run 'docker-credential-atcr update' to upgrade.\n\n")
10041037 }
10381038+10391039+ return nil
10051040}
1006104110071042// getUpdateCheckCachePath returns the path to the update check cache file
···10341069}
1035107010361071// saveUpdateCheckCache saves the update check cache to disk
10371037-func saveUpdateCheckCache(cache *UpdateCheckCache) {
10721072+func saveUpdateCheckCache(cache *UpdateCheckCache) error {
10381073 path := getUpdateCheckCachePath()
10391074 if path == "" {
10401040- return
10751075+ return nil
10411076 }
1042107710431078 data, err := json.MarshalIndent(cache, "", " ")
10441079 if err != nil {
10451045- return
10801080+ return nil
10461081 }
1047108210481083 // Ensure directory exists
10491084 dir := filepath.Dir(path)
10501050- os.MkdirAll(dir, 0700)
10851085+ if err := os.MkdirAll(dir, 0700); err != nil {
10861086+ return err
10871087+ }
1051108810521052- os.WriteFile(path, data, 0600)
10891089+ if err := os.WriteFile(path, data, 0600); err != nil {
10901090+ return err
10911091+ }
10921092+10931093+ return nil
10531094}
+2-2
pkg/appview/config.go
···388388 return checksums
389389 }
390390391391- pairs := strings.Split(checksumsStr, ",")
392392- for _, pair := range pairs {
391391+ pairs := strings.SplitSeq(checksumsStr, ",")
392392+ for pair := range pairs {
393393 parts := strings.SplitN(strings.TrimSpace(pair), ":", 2)
394394 if len(parts) == 2 {
395395 platform := strings.TrimSpace(parts[0])
+5-1
pkg/appview/db/device_store.go
···288288 // Check if this device's hash matches the secret
289289 if err := bcrypt.CompareHashAndPassword([]byte(device.SecretHash), []byte(secret)); err == nil {
290290 // Update last used asynchronously
291291- go s.UpdateLastUsed(device.SecretHash)
291291+ go func() {
292292+ if err := s.UpdateLastUsed(device.SecretHash); err != nil {
293293+ slog.Error("Failed to update last used", "error", err)
294294+ }
295295+ }()
292296293297 return &device, nil
294298 }
+2-5
pkg/appview/db/device_store_test.go
···5656func TestGenerateUserCode(t *testing.T) {
5757 // Generate multiple codes to test
5858 codes := make(map[string]bool)
5959- for i := 0; i < 100; i++ {
5959+ for range 100 {
6060 code := generateUserCode()
61616262 // Test format: XXXX-XXXX
···372372 return
373373 }
374374 if !tt.wantErr {
375375- if device == nil {
376376- t.Error("Expected device, got nil")
377377- }
378375 if device.DID != "did:plc:alice123" {
379376 t.Errorf("DID = %v, want did:plc:alice123", device.DID)
380377 }
···399396 }
400397401398 // Create 3 devices
402402- for i := 0; i < 3; i++ {
399399+ for i := range 3 {
403400 pending, err := store.CreatePendingAuth("Device "+string(rune('A'+i)), "192.168.1.1", "Agent")
404401 if err != nil {
405402 t.Fatalf("CreatePendingAuth() error = %v", err)
+5-5
pkg/appview/db/oauth_store.go
···339339340340// GetSessionStats returns statistics about stored OAuth sessions
341341// Useful for monitoring and debugging session health
342342-func (s *OAuthStore) GetSessionStats(ctx context.Context) (map[string]interface{}, error) {
343343- stats := make(map[string]interface{})
342342+func (s *OAuthStore) GetSessionStats(ctx context.Context) (map[string]any, error) {
343343+ stats := make(map[string]any)
344344345345 // Total sessions
346346 var totalSessions int
···392392393393// ListSessionsForMonitoring returns a list of all sessions with basic info for monitoring
394394// Returns: DID, session age (minutes), last update time
395395-func (s *OAuthStore) ListSessionsForMonitoring(ctx context.Context) ([]map[string]interface{}, error) {
395395+func (s *OAuthStore) ListSessionsForMonitoring(ctx context.Context) ([]map[string]any, error) {
396396 rows, err := s.db.QueryContext(ctx, `
397397 SELECT
398398 account_did,
···408408 }
409409 defer rows.Close()
410410411411- var sessions []map[string]interface{}
411411+ var sessions []map[string]any
412412 for rows.Next() {
413413 var did, sessionID, createdAt, updatedAt string
414414 var idleMinutes int
···418418 continue
419419 }
420420421421- sessions = append(sessions, map[string]interface{}{
421421+ sessions = append(sessions, map[string]any{
422422 "did": did,
423423 "session_id": sessionID,
424424 "created_at": createdAt,
+21-7
pkg/appview/db/queries_test.go
···1266126612671267 // Verify data exists
12681268 var count int
12691269- db.QueryRow(`SELECT COUNT(*) FROM manifests WHERE did = ?`, testUser.DID).Scan(&count)
12691269+ if err := db.QueryRow(`SELECT COUNT(*) FROM manifests WHERE did = ?`, testUser.DID).Scan(&count); err != nil {
12701270+ t.Fatalf("Expected no errors scanning manifest count, got %s", err)
12711271+ }
12701272 if count != 1 {
12711273 t.Fatalf("Expected 1 manifest, got %d", count)
12721274 }
12731273- db.QueryRow(`SELECT COUNT(*) FROM tags WHERE did = ?`, testUser.DID).Scan(&count)
12751275+ if err := db.QueryRow(`SELECT COUNT(*) FROM tags WHERE did = ?`, testUser.DID).Scan(&count); err != nil {
12761276+ t.Fatalf("Expected no errors scanning tag count, got %s", err)
12771277+ }
12741278 if count != 1 {
12751279 t.Fatalf("Expected 1 tag, got %d", count)
12761280 }
12771277- db.QueryRow(`SELECT COUNT(*) FROM layers WHERE manifest_id = ?`, manifestID).Scan(&count)
12811281+ if err := db.QueryRow(`SELECT COUNT(*) FROM layers WHERE manifest_id = ?`, manifestID).Scan(&count); err != nil {
12821282+ t.Fatalf("Expected no errors scanning layer count, got %s", err)
12831283+ }
12781284 if count != 1 {
12791285 t.Fatalf("Expected 1 layer, got %d", count)
12801286 }
···12851291 }
1286129212871293 // Verify all data was cascade deleted
12881288- db.QueryRow(`SELECT COUNT(*) FROM users WHERE did = ?`, testUser.DID).Scan(&count)
12941294+ if err := db.QueryRow(`SELECT COUNT(*) FROM users WHERE did = ?`, testUser.DID).Scan(&count); err != nil {
12951295+ t.Errorf("Expected no errors scanning user count, got %s", err)
12961296+ }
12891297 if count != 0 {
12901298 t.Errorf("Expected 0 users, got %d", count)
12911299 }
12921292- db.QueryRow(`SELECT COUNT(*) FROM manifests WHERE did = ?`, testUser.DID).Scan(&count)
13001300+ if err := db.QueryRow(`SELECT COUNT(*) FROM manifests WHERE did = ?`, testUser.DID).Scan(&count); err != nil {
13011301+ t.Errorf("Expected no errors scanning manifest count, got %s", err)
13021302+ }
12931303 if count != 0 {
12941304 t.Errorf("Expected 0 manifests after cascade delete, got %d", count)
12951305 }
12961296- db.QueryRow(`SELECT COUNT(*) FROM tags WHERE did = ?`, testUser.DID).Scan(&count)
13061306+ if err := db.QueryRow(`SELECT COUNT(*) FROM tags WHERE did = ?`, testUser.DID).Scan(&count); err != nil {
13071307+ t.Errorf("Expected no errors scanning tag count, got %s", err)
13081308+ }
12971309 if count != 0 {
12981310 t.Errorf("Expected 0 tags after cascade delete, got %d", count)
12991311 }
13001300- db.QueryRow(`SELECT COUNT(*) FROM layers WHERE manifest_id = ?`, manifestID).Scan(&count)
13121312+ if err := db.QueryRow(`SELECT COUNT(*) FROM layers WHERE manifest_id = ?`, manifestID).Scan(&count); err != nil {
13131313+ t.Errorf("Expected no errors scanning layer count, got %s", err)
13141314+ }
13011315 if count != 0 {
13021316 t.Errorf("Expected 0 layers after cascade delete, got %d", count)
13031317 }
+6-2
pkg/appview/db/readonly.go
···102102103103 // Cleanup OAuth sessions (older than 30 days)
104104 oauthStore := NewOAuthStore(database)
105105- oauthStore.CleanupOldSessions(ctx, 30*24*time.Hour)
106106- oauthStore.CleanupExpiredAuthRequests(ctx)
105105+ if err := oauthStore.CleanupOldSessions(ctx, 30*24*time.Hour); err != nil {
106106+ slog.Warn("Failed to clean up old sessions", "error", err)
107107+ }
108108+ if err := oauthStore.CleanupExpiredAuthRequests(ctx); err != nil {
109109+ slog.Warn("Failed to clean up expired auth requests", "error", err)
110110+ }
107111108112 // Cleanup device pending auths
109113 deviceStore := NewDeviceStore(database)
+2-2
pkg/appview/db/schema.go
···225225 var statements []string
226226227227 // Split on semicolons
228228- parts := strings.Split(query, ";")
228228+ parts := strings.SplitSeq(query, ";")
229229230230- for _, part := range parts {
230230+ for part := range parts {
231231 // Trim whitespace
232232 stmt := strings.TrimSpace(part)
233233
+2-2
pkg/appview/db/session_store_test.go
···252252253253 // Create multiple sessions for alice
254254 sessionIDs := make([]string, 3)
255255- for i := 0; i < 3; i++ {
255255+ for i := range 3 {
256256 id, err := store.Create(did, "alice.bsky.social", "https://pds.example.com", 1*time.Hour)
257257 if err != nil {
258258 t.Fatalf("Create() error = %v", err)
···516516517517 // Generate multiple session IDs
518518 ids := make(map[string]bool)
519519- for i := 0; i < 100; i++ {
519519+ for range 100 {
520520 id, err := store.Create("did:plc:alice123", "alice.bsky.social", "https://pds.example.com", 1*time.Hour)
521521 if err != nil {
522522 t.Fatalf("Create() error = %v", err)
+4-1
pkg/appview/db/tag_delete_test.go
···155155 t.Logf("Remaining tags in database:")
156156 for rows.Next() {
157157 var repo, tag string
158158- rows.Scan(&repo, &tag)
158158+ if err := rows.Scan(&repo, &tag); err != nil {
159159+ t.Logf("Expected no errors scanning row, got %s", err)
160160+ continue
161161+ }
159162 t.Logf(" - repository=%s, tag=%s", repo, tag)
160163 }
161164 rows.Close()
+32-8
pkg/appview/handlers/api.go
···6868 // Return success
6969 w.Header().Set("Content-Type", "application/json")
7070 w.WriteHeader(http.StatusCreated)
7171- json.NewEncoder(w).Encode(map[string]bool{"starred": true})
7171+ if err := json.NewEncoder(w).Encode(map[string]bool{"starred": true}); err != nil {
7272+ slog.Error("failed to encode json to http response body", "error", err)
7373+ w.WriteHeader(http.StatusInternalServerError)
7474+ }
7275}
73767477// UnstarRepositoryHandler handles unstarring a repository
···123126124127 // Return success
125128 w.Header().Set("Content-Type", "application/json")
126126- json.NewEncoder(w).Encode(map[string]bool{"starred": false})
129129+ if err := json.NewEncoder(w).Encode(map[string]bool{"starred": false}); err != nil {
130130+ slog.Error("failed to encode json to http response body", "error", err)
131131+ w.WriteHeader(http.StatusInternalServerError)
132132+ }
127133}
128134129135// CheckStarHandler checks if current user has starred a repository
···139145 if user == nil {
140146 // Not authenticated - return not starred
141147 w.Header().Set("Content-Type", "application/json")
142142- json.NewEncoder(w).Encode(map[string]bool{"starred": false})
148148+ if err := json.NewEncoder(w).Encode(map[string]bool{"starred": false}); err != nil {
149149+ slog.Error("failed to encode json to http response body", "error", err)
150150+ w.WriteHeader(http.StatusInternalServerError)
151151+ }
143152 return
144153 }
145154···167176 if err != nil && handleOAuthError(r.Context(), h.Refresher, user.DID, err) {
168177 // For a read operation, just return not starred instead of error
169178 w.Header().Set("Content-Type", "application/json")
170170- json.NewEncoder(w).Encode(map[string]bool{"starred": false})
179179+ if err := json.NewEncoder(w).Encode(map[string]bool{"starred": false}); err != nil {
180180+ slog.Error("failed to encode json to http response body", "error", err)
181181+ w.WriteHeader(http.StatusInternalServerError)
182182+ }
171183 return
172184 }
173185···175187176188 // Return result
177189 w.Header().Set("Content-Type", "application/json")
178178- json.NewEncoder(w).Encode(map[string]bool{"starred": starred})
190190+ if err := json.NewEncoder(w).Encode(map[string]bool{"starred": starred}); err != nil {
191191+ slog.Error("failed to encode json to http response body", "error", err)
192192+ w.WriteHeader(http.StatusInternalServerError)
193193+ }
179194}
180195181196// GetStatsHandler returns repository statistics
···205220206221 // Return stats as JSON
207222 w.Header().Set("Content-Type", "application/json")
208208- json.NewEncoder(w).Encode(stats)
223223+ if err := json.NewEncoder(w).Encode(stats); err != nil {
224224+ slog.Error("failed to encode json to http response body", "error", err)
225225+ w.WriteHeader(http.StatusInternalServerError)
226226+ }
209227}
210228211229// ManifestDetailHandler returns detailed manifest information including platforms
···241259242260 // Return manifest as JSON
243261 w.Header().Set("Content-Type", "application/json")
244244- json.NewEncoder(w).Encode(manifest)
262262+ if err := json.NewEncoder(w).Encode(manifest); err != nil {
263263+ slog.Error("failed to encode json to http response body", "error", err)
264264+ w.WriteHeader(http.StatusInternalServerError)
265265+ }
245266}
246267247268// CredentialHelperVersionResponse is the response for the credential helper version API
···299320300321 w.Header().Set("Content-Type", "application/json")
301322 w.Header().Set("Cache-Control", "public, max-age=300") // Cache for 5 minutes
302302- json.NewEncoder(w).Encode(response)
323323+ if err := json.NewEncoder(w).Encode(response); err != nil {
324324+ slog.Error("failed to encode json to http response body", "error", err)
325325+ w.WriteHeader(http.StatusInternalServerError)
326326+ }
303327}
···675675 }
676676677677 // Test 5: Process multiple deactivation events (idempotent)
678678- for i := 0; i < 3; i++ {
678678+ for i := range 3 {
679679 err = processor.ProcessAccount(context.Background(), testDID, false, "deactivated")
680680 if err != nil {
681681 t.Logf("Expected cache invalidation error on iteration %d: %v", i, err)
+7-4
pkg/appview/jetstream/worker.go
···128128129129 // Reset read deadline - we know connection is alive
130130 // Allow 90 seconds for next pong (3x ping interval)
131131- conn.SetReadDeadline(time.Now().Add(90 * time.Second))
132132- return nil
131131+ return conn.SetReadDeadline(time.Now().Add(90 * time.Second))
133132 })
134133135134 // Set initial read deadline
136136- conn.SetReadDeadline(time.Now().Add(90 * time.Second))
135135+ if err := conn.SetReadDeadline(time.Now().Add(90 * time.Second)); err != nil {
136136+ return err
137137+ }
137138138139 // Create zstd decoder for decompressing messages
139140 decoder, err := zstd.NewReader(nil)
···179180 }
180181181182 // Send ping with write deadline
182182- conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
183183+ if err := conn.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
184184+ slog.Error("Failed to set write deadline", "error", err)
185185+ }
183186 if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
184187 slog.Warn("Jetstream failed to send ping", "error", err)
185188 conn.Close()
+2-2
pkg/appview/middleware/auth_test.go
···318318 // Pre-create all users and sessions before concurrent access
319319 // This ensures database is fully initialized before goroutines start
320320 sessionIDs := make([]string, 10)
321321- for i := 0; i < 10; i++ {
321321+ for i := range 10 {
322322 did := fmt.Sprintf("did:plc:user%d", i)
323323 handle := fmt.Sprintf("user%d.bsky.social", i)
324324···358358 var wg sync.WaitGroup
359359 var mu sync.Mutex // Protect results map
360360361361- for i := 0; i < 10; i++ {
361361+ for i := range 10 {
362362 wg.Add(1)
363363 go func(index int, sessionID string) {
364364 defer wg.Done()
+7-2
pkg/appview/middleware/registry.go
···3232// pullerDIDKey is the context key for storing the authenticated user's DID from JWT
3333const pullerDIDKey contextKey = "puller.did"
34343535+// httpRequestMethodKey is the context key for the HTTP request method
3636+const httpRequestMethodKey contextKey = "http.request.method"
3737+3538// validationCacheEntry stores a validated service token with expiration
3639type validationCacheEntry struct {
3740 serviceToken string
···204207205208func init() {
206209 // Register the name resolution middleware
207207- registrymw.Register("atproto-resolver", initATProtoResolver)
210210+ if err := registrymw.Register("atproto-resolver", initATProtoResolver); err != nil {
211211+ slog.Error("Failed to register middleware", "error", err)
212212+ }
208213}
209214210215// NamespaceResolver wraps a namespace and resolves names
···555560556561 // Store HTTP method in context for routing decisions
557562 // This is used by routing_repository.go to distinguish pull (GET/HEAD) from push (PUT/POST)
558558- ctx = context.WithValue(ctx, "http.request.method", r.Method)
563563+ ctx = context.WithValue(ctx, httpRequestMethodKey, r.Method)
559564560565 // Extract Authorization header
561566 authHeader := r.Header.Get("Authorization")
···1212 "atcr.io/pkg/atproto"
1313)
14141515+type contextKey string
1616+1717+const httpRequestMethodKey contextKey = "http.request.method"
1818+1519// mockDatabase is a simple mock for testing
1620type mockDatabase struct {
1721 holdDID string
···126130 }
127131 repo := NewRoutingRepository(nil, ctx)
128132129129- pullCtx := context.WithValue(context.Background(), "http.request.method", method)
133133+ pullCtx := context.WithValue(context.Background(), httpRequestMethodKey, method)
130134 blobStore := repo.Blobs(pullCtx)
131135132136 assert.NotNil(t, blobStore)
···164168 repo := NewRoutingRepository(nil, ctx)
165169166170 // Create context with push method
167167- pushCtx := context.WithValue(context.Background(), "http.request.method", tc.method)
171171+ pushCtx := context.WithValue(context.Background(), httpRequestMethodKey, tc.method)
168172 blobStore := repo.Blobs(pushCtx)
169173170174 assert.NotNil(t, blobStore)
···317321 blobStores := make([]distribution.BlobStore, numGoroutines)
318322319323 // Concurrent access to Manifests()
320320- for i := 0; i < numGoroutines; i++ {
324324+ for i := range numGoroutines {
321325 wg.Add(1)
322326 go func(index int) {
323327 defer wg.Done()
···330334 wg.Wait()
331335332336 // Verify all stores are non-nil (due to race conditions, they may not all be the same instance)
333333- for i := 0; i < numGoroutines; i++ {
337337+ for i := range numGoroutines {
334338 assert.NotNil(t, manifestStores[i], "manifest store should not be nil")
335339 }
336340···340344 assert.NotNil(t, cachedStore)
341345342346 // Concurrent access to Blobs()
343343- for i := 0; i < numGoroutines; i++ {
347347+ for i := range numGoroutines {
344348 wg.Add(1)
345349 go func(index int) {
346350 defer wg.Done()
···351355 wg.Wait()
352356353357 // Verify all stores are non-nil (due to race conditions, they may not all be the same instance)
354354- for i := 0; i < numGoroutines; i++ {
358358+ for i := range numGoroutines {
355359 assert.NotNil(t, blobStores[i], "blob store should not be nil")
356360 }
357361···376380 repo := NewRoutingRepository(nil, ctx)
377381378382 // For pull (GET), database should take priority
379379- pullCtx := context.WithValue(context.Background(), "http.request.method", "GET")
383383+ pullCtx := context.WithValue(context.Background(), httpRequestMethodKey, "GET")
380384 blobStore := repo.Blobs(pullCtx)
381385382386 assert.NotNil(t, blobStore)
···3232 wg.Add(numGoroutines)
33333434 // Channel to collect all directory instances
3535- instances := make(chan interface{}, numGoroutines)
3535+ instances := make(chan any, numGoroutines)
36363737 // Launch many goroutines concurrently accessing GetDirectory
3838- for i := 0; i < numGoroutines; i++ {
3838+ for range numGoroutines {
3939 go func() {
4040 defer wg.Done()
4141 dir := GetDirectory()
···4848 close(instances)
49495050 // Collect all instances
5151- var dirs []interface{}
5151+ var dirs []any
5252 for dir := range instances {
5353 dirs = append(dirs, dir)
5454 }
···7272func TestGetDirectorySequential(t *testing.T) {
7373 t.Run("multiple calls in sequence", func(t *testing.T) {
7474 // Get directory multiple times in sequence
7575- dirs := make([]interface{}, 10)
7676- for i := 0; i < 10; i++ {
7575+ dirs := make([]any, 10)
7676+ for i := range 10 {
7777 dirs[i] = GetDirectory()
7878 }
7979···122122 var wg sync.WaitGroup
123123 wg.Add(numGoroutines)
124124125125- instances := make([]interface{}, numGoroutines)
125125+ instances := make([]any, numGoroutines)
126126 var mu sync.Mutex
127127128128 // Simulate many goroutines trying to get the directory simultaneously
129129- for i := 0; i < numGoroutines; i++ {
129129+ for i := range numGoroutines {
130130 go func(idx int) {
131131 defer wg.Done()
132132 dir := GetDirectory()
-1
pkg/atproto/generate.go
···11//go:build ignore
22-// +build ignore
3243package main
54
+1-1
pkg/auth/cache.go
···11-// Package token provides service token caching and management for AppView.
11+// Package auth provides service token caching and management for AppView.
22// Service tokens are JWTs issued by a user's PDS to authorize AppView to
33// act on their behalf when communicating with hold services. Tokens are
44// cached with automatic expiry parsing and 10-second safety margins.