Add logo feature

This commit is contained in:
Pim van Pelt
2025-12-03 12:32:36 +01:00
parent 60a149b669
commit 99ad6fbff1
3 changed files with 119 additions and 42 deletions

View File

@@ -279,17 +279,19 @@ func generateS3HTML(entries []indexgen.FileEntry, opts *indexgen.Options, client
// Prepare template data (similar to ProcessDir in indexgen) // Prepare template data (similar to ProcessDir in indexgen)
data := struct { data := struct {
DirName string DirName string
Entries []indexgen.FileEntry Entries []indexgen.FileEntry
DirAppend bool DirAppend bool
OutputFile string OutputFile string
IsRoot bool IsRoot bool
WatermarkURL string
}{ }{
DirName: opts.TopDir, // Use bucket name as directory name DirName: opts.TopDir, // Use bucket name as directory name
Entries: entries, Entries: entries,
DirAppend: opts.DirAppend, DirAppend: opts.DirAppend,
OutputFile: opts.OutputFile, OutputFile: opts.OutputFile,
IsRoot: isRoot, IsRoot: isRoot,
WatermarkURL: opts.WatermarkURL,
} }
// Generate HTML content in memory // Generate HTML content in memory
@@ -345,6 +347,7 @@ func main() {
var s3URL string var s3URL string
var dryRun bool var dryRun bool
var showIndexFiles bool var showIndexFiles bool
var watermarkURL string
// Set defaults // Set defaults
opts.DirAppend = true opts.DirAppend = true
@@ -359,6 +362,7 @@ func main() {
flag.StringVar(&excludeRegexStr, "x", "", "exclude files matching regular expression") flag.StringVar(&excludeRegexStr, "x", "", "exclude files matching regular expression")
flag.BoolVar(&opts.Verbose, "v", false, "verbosely list every processed file") flag.BoolVar(&opts.Verbose, "v", false, "verbosely list every processed file")
flag.BoolVar(&showIndexFiles, "i", false, "show index.html files in directory listings") 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() { flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Generate directory index files (recursive is ON, hidden files included by default).\n") 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.DryRun = dryRun
opts.ShowIndexFiles = showIndexFiles opts.ShowIndexFiles = showIndexFiles
opts.WatermarkURL = watermarkURL
if s3URL != "" { if s3URL != "" {
// Parse S3 URL // Parse S3 URL

View File

@@ -168,6 +168,7 @@ type Options struct {
Verbose bool Verbose bool
DryRun bool DryRun bool
ShowIndexFiles bool ShowIndexFiles bool
WatermarkURL string
} }
type FileEntry struct { type FileEntry struct {
@@ -211,17 +212,19 @@ func ProcessDir(topDir string, opts *Options) error {
}) })
templateData := struct { templateData := struct {
DirName string DirName string
Entries []FileEntry Entries []FileEntry
DirAppend bool DirAppend bool
OutputFile string OutputFile string
IsRoot bool IsRoot bool
WatermarkURL string
}{ }{
DirName: dirName, DirName: dirName,
Entries: entries, Entries: entries,
DirAppend: opts.DirAppend, DirAppend: opts.DirAppend,
OutputFile: opts.OutputFile, OutputFile: opts.OutputFile,
IsRoot: false, // Local filesystem always shows parent directory IsRoot: false, // Local filesystem always shows parent directory
WatermarkURL: opts.WatermarkURL,
} }
if opts.DryRun { if opts.DryRun {
@@ -467,6 +470,15 @@ const htmlTemplateString = `<!DOCTYPE html>
overflow-x: hidden; overflow-x: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
color: #999; color: #999;
display: flex;
align-items: center;
}
.watermark {
height: 24px;
width: auto;
margin-right: 8px;
vertical-align: middle;
} }
h1 a { h1 a {
@@ -943,7 +955,7 @@ const htmlTemplateString = `<!DOCTYPE html>
</defs> </defs>
</svg> </svg>
<header> <header>
<h1>{{.DirName}}</h1> <h1>{{if .WatermarkURL}}<img src="{{.WatermarkURL}}" class="watermark" alt="Logo">{{end}}{{.DirName}}</h1>
</header> </header>
<main> <main>
<div class="listing"> <div class="listing">

View File

@@ -97,17 +97,19 @@ func TestHTMLTemplate(t *testing.T) {
// Test template execution with sample data // Test template execution with sample data
data := struct { data := struct {
DirName string DirName string
Entries []FileEntry Entries []FileEntry
DirAppend bool DirAppend bool
OutputFile string OutputFile string
IsRoot bool IsRoot bool
WatermarkURL string
}{ }{
DirName: "test-dir", DirName: "test-dir",
Entries: []FileEntry{}, Entries: []FileEntry{},
DirAppend: false, DirAppend: false,
OutputFile: "index.html", OutputFile: "index.html",
IsRoot: false, IsRoot: false,
WatermarkURL: "",
} }
var buf bytes.Buffer var buf bytes.Buffer
@@ -159,17 +161,19 @@ func TestHTMLTemplateWithEntries(t *testing.T) {
} }
data := struct { data := struct {
DirName string DirName string
Entries []FileEntry Entries []FileEntry
DirAppend bool DirAppend bool
OutputFile string OutputFile string
IsRoot bool IsRoot bool
WatermarkURL string
}{ }{
DirName: "test-dir", DirName: "test-dir",
Entries: entries, Entries: entries,
DirAppend: false, DirAppend: false,
OutputFile: "index.html", OutputFile: "index.html",
IsRoot: false, IsRoot: false,
WatermarkURL: "",
} }
var buf bytes.Buffer 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) { func TestReadDirEntries(t *testing.T) {
// Create a temporary directory with test files // Create a temporary directory with test files
tempDir := t.TempDir() tempDir := t.TempDir()