From 99ad6fbff13165c98602a9b536e85cf1c434be25 Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Wed, 3 Dec 2025 12:32:36 +0100 Subject: [PATCH] Add logo feature --- cmd/s3-genindex/main.go | 27 ++++---- internal/indexgen/indexgen.go | 34 ++++++---- internal/indexgen/indexgen_test.go | 100 +++++++++++++++++++++++------ 3 files changed, 119 insertions(+), 42 deletions(-) diff --git a/cmd/s3-genindex/main.go b/cmd/s3-genindex/main.go index 436393d..d944f78 100644 --- a/cmd/s3-genindex/main.go +++ b/cmd/s3-genindex/main.go @@ -279,17 +279,19 @@ func generateS3HTML(entries []indexgen.FileEntry, opts *indexgen.Options, client // Prepare template data (similar to ProcessDir in indexgen) data := struct { - DirName string - Entries []indexgen.FileEntry - DirAppend bool - OutputFile string - IsRoot bool + DirName string + Entries []indexgen.FileEntry + DirAppend bool + OutputFile string + IsRoot bool + WatermarkURL string }{ - DirName: opts.TopDir, // Use bucket name as directory name - Entries: entries, - DirAppend: opts.DirAppend, - OutputFile: opts.OutputFile, - IsRoot: isRoot, + DirName: opts.TopDir, // Use bucket name as directory name + Entries: entries, + DirAppend: opts.DirAppend, + OutputFile: opts.OutputFile, + IsRoot: isRoot, + WatermarkURL: opts.WatermarkURL, } // Generate HTML content in memory @@ -345,6 +347,7 @@ func main() { var s3URL string var dryRun bool var showIndexFiles bool + var watermarkURL string // Set defaults opts.DirAppend = true @@ -359,6 +362,7 @@ func main() { flag.StringVar(&excludeRegexStr, "x", "", "exclude files matching regular expression") flag.BoolVar(&opts.Verbose, "v", false, "verbosely list every processed file") flag.BoolVar(&showIndexFiles, "i", false, "show index.html files in directory listings") + flag.StringVar(&watermarkURL, "wm", "", "watermark logo URL to display in top left corner") flag.Usage = func() { fmt.Fprintf(os.Stderr, "Generate directory index files (recursive is ON, hidden files included by default).\n") @@ -398,9 +402,10 @@ func main() { } } - // Set dry run and show index files flags + // Set dry run, show index files, and watermark URL opts.DryRun = dryRun opts.ShowIndexFiles = showIndexFiles + opts.WatermarkURL = watermarkURL if s3URL != "" { // Parse S3 URL diff --git a/internal/indexgen/indexgen.go b/internal/indexgen/indexgen.go index 00cdfa5..83e83c6 100644 --- a/internal/indexgen/indexgen.go +++ b/internal/indexgen/indexgen.go @@ -168,6 +168,7 @@ type Options struct { Verbose bool DryRun bool ShowIndexFiles bool + WatermarkURL string } type FileEntry struct { @@ -211,17 +212,19 @@ func ProcessDir(topDir string, opts *Options) error { }) templateData := struct { - DirName string - Entries []FileEntry - DirAppend bool - OutputFile string - IsRoot bool + DirName string + Entries []FileEntry + DirAppend bool + OutputFile string + IsRoot bool + WatermarkURL string }{ - DirName: dirName, - Entries: entries, - DirAppend: opts.DirAppend, - OutputFile: opts.OutputFile, - IsRoot: false, // Local filesystem always shows parent directory + DirName: dirName, + Entries: entries, + DirAppend: opts.DirAppend, + OutputFile: opts.OutputFile, + IsRoot: false, // Local filesystem always shows parent directory + WatermarkURL: opts.WatermarkURL, } if opts.DryRun { @@ -467,6 +470,15 @@ const htmlTemplateString = ` overflow-x: hidden; text-overflow: ellipsis; color: #999; + display: flex; + align-items: center; + } + + .watermark { + height: 24px; + width: auto; + margin-right: 8px; + vertical-align: middle; } h1 a { @@ -943,7 +955,7 @@ const htmlTemplateString = `
-

{{.DirName}}

+

{{if .WatermarkURL}}Logo{{end}}{{.DirName}}

diff --git a/internal/indexgen/indexgen_test.go b/internal/indexgen/indexgen_test.go index f7916e1..0189eb8 100644 --- a/internal/indexgen/indexgen_test.go +++ b/internal/indexgen/indexgen_test.go @@ -97,17 +97,19 @@ func TestHTMLTemplate(t *testing.T) { // Test template execution with sample data data := struct { - DirName string - Entries []FileEntry - DirAppend bool - OutputFile string - IsRoot bool + DirName string + Entries []FileEntry + DirAppend bool + OutputFile string + IsRoot bool + WatermarkURL string }{ - DirName: "test-dir", - Entries: []FileEntry{}, - DirAppend: false, - OutputFile: "index.html", - IsRoot: false, + DirName: "test-dir", + Entries: []FileEntry{}, + DirAppend: false, + OutputFile: "index.html", + IsRoot: false, + WatermarkURL: "", } var buf bytes.Buffer @@ -159,17 +161,19 @@ func TestHTMLTemplateWithEntries(t *testing.T) { } data := struct { - DirName string - Entries []FileEntry - DirAppend bool - OutputFile string - IsRoot bool + DirName string + Entries []FileEntry + DirAppend bool + OutputFile string + IsRoot bool + WatermarkURL string }{ - DirName: "test-dir", - Entries: entries, - DirAppend: false, - OutputFile: "index.html", - IsRoot: false, + DirName: "test-dir", + Entries: entries, + DirAppend: false, + OutputFile: "index.html", + IsRoot: false, + WatermarkURL: "", } var buf bytes.Buffer @@ -196,6 +200,62 @@ func TestHTMLTemplateWithEntries(t *testing.T) { } } +func TestHTMLTemplateWithWatermark(t *testing.T) { + tmpl := GetHTMLTemplate() + if tmpl == nil { + t.Fatal("GetHTMLTemplate() returned nil") + } + + // Test template execution with watermark + data := struct { + DirName string + Entries []FileEntry + DirAppend bool + OutputFile string + IsRoot bool + WatermarkURL string + }{ + DirName: "test-dir", + Entries: []FileEntry{}, + DirAppend: false, + OutputFile: "index.html", + IsRoot: false, + WatermarkURL: "https://example.com/logo.svg", + } + + var buf bytes.Buffer + err := tmpl.Execute(&buf, data) + if err != nil { + t.Fatalf("Template execution with watermark failed: %v", err) + } + + output := buf.String() + + // Check that watermark image is included + if !bytes.Contains([]byte(output), []byte(`src="https://example.com/logo.svg"`)) { + t.Error("Template output should contain watermark image URL") + } + + if !bytes.Contains([]byte(output), []byte(`class="watermark"`)) { + t.Error("Template output should contain watermark CSS class") + } + + // Test without watermark + data.WatermarkURL = "" + buf.Reset() + err = tmpl.Execute(&buf, data) + if err != nil { + t.Fatalf("Template execution without watermark failed: %v", err) + } + + outputNoWatermark := buf.String() + + // Check that watermark image is NOT included when URL is empty + if bytes.Contains([]byte(outputNoWatermark), []byte(`class="watermark"`)) { + t.Error("Template output should not contain watermark when URL is empty") + } +} + func TestReadDirEntries(t *testing.T) { // Create a temporary directory with test files tempDir := t.TempDir()