Skip to content

Commit c19b766

Browse files
author
Mosaad Sallam
committed
feat: Automatically upload non-empty result files to R2 after writing and log their public URLs.
1 parent ef5697e commit c19b766

2 files changed

Lines changed: 64 additions & 2 deletions

File tree

internal/modules/r2storage/r2storage.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,36 @@ func IsEnabled() bool {
123123
return isEnabled && r2Config != nil && r2Config.Enabled
124124
}
125125

126+
// UploadResultFileAndLog uploads a result file to R2 only if it's non-empty.
127+
// The r2Key is the object key (e.g. "results/example.com/subs/subdomains.txt").
128+
// It prints a prominent [R2-RESULT-URL] log line so AI agents can extract the URL.
129+
// Returns the public URL or an empty string if the file was empty or R2 is not enabled.
130+
func UploadResultFileAndLog(localPath, r2Key string) string {
131+
if !IsEnabled() {
132+
return ""
133+
}
134+
135+
info, err := os.Stat(localPath)
136+
if err != nil {
137+
return "" // File doesn't exist
138+
}
139+
if info.Size() == 0 {
140+
log.Printf("[R2] ⏭️ Skipping empty file: %s", localPath)
141+
return ""
142+
}
143+
144+
publicURL, err := UploadFile(localPath, r2Key, true) // skipTimestamp=true, key already has domain/path
145+
if err != nil {
146+
log.Printf("[R2] ⚠️ Failed to upload result file %s: %v", localPath, err)
147+
return ""
148+
}
149+
150+
// Print a very visible marker line — AI agents and log parsers use this
151+
log.Printf("[R2-RESULT-URL] %s -> %s", localPath, publicURL)
152+
fmt.Printf("\n🔗 R2 Result: %s\n URL: %s\n\n", localPath, publicURL)
153+
return publicURL
154+
}
155+
126156
// UploadFile uploads a file to R2 and returns the public URL
127157
// If skipTimestamp is true, the file is uploaded without a timestamp prefix (for cache/backups)
128158
func UploadFile(filePath, objectKey string, skipTimestamp bool) (string, error) {

internal/modules/utils/file.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@ package utils
33
import (
44
"context"
55
"fmt"
6+
"log"
67
"os"
78
"path/filepath"
89
"strings"
910
"time"
11+
12+
"github.com/h0tak88r/AutoAR/internal/modules/r2storage"
1013
)
1114

1215
// WriteLines writes lines to a file (one per line)
13-
// Creates parent directories if they don't exist
16+
// Creates parent directories if they don't exist.
17+
// After writing, if R2 storage is enabled and the file is non-empty,
18+
// it automatically uploads the file and prints the public URL.
1419
func WriteLines(path string, lines []string) error {
1520
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
1621
return err
@@ -19,9 +24,36 @@ func WriteLines(path string, lines []string) error {
1924
if len(lines) > 0 {
2025
data += "\n"
2126
}
22-
return os.WriteFile(path, []byte(data), 0644)
27+
if err := os.WriteFile(path, []byte(data), 0644); err != nil {
28+
return err
29+
}
30+
31+
// Auto-upload to R2 if enabled and file is non-empty
32+
if len(lines) > 0 && r2storage.IsEnabled() {
33+
go uploadResultAsync(path)
34+
}
35+
36+
return nil
2337
}
2438

39+
// uploadResultAsync uploads a result file to R2 in the background.
40+
// The R2 key strips the leading "/" and uses the full local path as key.
41+
func uploadResultAsync(path string) {
42+
// Derive R2 key from the path:
43+
// Strip absolute prefix up to "new-results" so the key is e.g.
44+
// new-results/example.com/subs/subdomains.txt
45+
r2Key := path
46+
if idx := strings.Index(path, "new-results/"); idx >= 0 {
47+
r2Key = path[idx:]
48+
} else if strings.HasPrefix(r2Key, "/") {
49+
r2Key = r2Key[1:]
50+
}
51+
r2storage.UploadResultFileAndLog(path, r2Key)
52+
log.Printf("[R2] ✅ Auto-uploaded result file: %s", path)
53+
}
54+
55+
56+
2557
// ReadLines reads lines from a file
2658
func ReadLines(path string) ([]string, error) {
2759
data, err := os.ReadFile(path)

0 commit comments

Comments
 (0)