Jayvens
This commit is contained in:
163
helios/build/generate-index.go
Normal file
163
helios/build/generate-index.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IndexData struct {
|
||||
Dir string
|
||||
Files []FileEntry
|
||||
RootURL string
|
||||
}
|
||||
|
||||
type FileEntry struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Date string `json:"date,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
Gradeband *string `json:"gradeband,omitempty"`
|
||||
}
|
||||
|
||||
func parseFilename(name string) (date, source, topic string, gradeband *string) {
|
||||
// Remove extension
|
||||
nameWithoutExt := strings.TrimSuffix(name, filepath.Ext(name))
|
||||
|
||||
// Split by " - "
|
||||
parts := strings.Split(nameWithoutExt, " - ")
|
||||
|
||||
if len(parts) < 2 {
|
||||
// Can't parse, return empty
|
||||
return
|
||||
}
|
||||
|
||||
date = parts[0]
|
||||
source = parts[1]
|
||||
|
||||
if len(parts) == 2 {
|
||||
// No topic or gradeband
|
||||
return
|
||||
}
|
||||
|
||||
if len(parts) == 3 {
|
||||
// Just topic, no gradeband
|
||||
topic = parts[2]
|
||||
return
|
||||
}
|
||||
|
||||
// 4+ parts: third part is gradeband, remaining parts are topic
|
||||
gb := parts[2]
|
||||
gradeband = &gb
|
||||
topic = strings.Join(parts[3:], " - ")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func generateIndex(dir string, rootURL string) error {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read directory: %w", err)
|
||||
}
|
||||
|
||||
// Normalize root URL (remove trailing slash)
|
||||
rootURL = strings.TrimSuffix(rootURL, "/")
|
||||
|
||||
var fileNames []string
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
ext := filepath.Ext(name)
|
||||
// Skip the index files, build files, directories, and hidden files
|
||||
if !entry.IsDir() &&
|
||||
name != "index.html" && name != "index.json" &&
|
||||
ext != ".go" && ext != ".tmpl" &&
|
||||
!filepath.HasPrefix(name, ".") {
|
||||
fileNames = append(fileNames, name)
|
||||
}
|
||||
}
|
||||
sort.Strings(fileNames)
|
||||
|
||||
// Build file entries with absolute URLs
|
||||
var files []FileEntry
|
||||
dirName := filepath.Base(dir)
|
||||
for _, name := range fileNames {
|
||||
url := fmt.Sprintf("%s/%s/%s", rootURL, dirName, name)
|
||||
date, source, topic, gradeband := parseFilename(name)
|
||||
files = append(files, FileEntry{
|
||||
Name: name,
|
||||
URL: url,
|
||||
Date: date,
|
||||
Source: source,
|
||||
Topic: topic,
|
||||
Gradeband: gradeband,
|
||||
})
|
||||
}
|
||||
|
||||
templatePath := "helios/build/index.html.tmpl"
|
||||
tmpl, err := template.ParseFiles(templatePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse template: %w", err)
|
||||
}
|
||||
|
||||
indexPath := filepath.Join(dir, "index.html")
|
||||
f, err := os.Create(indexPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create index.html: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data := IndexData{
|
||||
Dir: dirName,
|
||||
Files: files,
|
||||
RootURL: rootURL,
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(f, data); err != nil {
|
||||
return fmt.Errorf("failed to execute template: %w", err)
|
||||
}
|
||||
log.Println(indexPath)
|
||||
|
||||
// Generate index.json
|
||||
jsonPath := filepath.Join(dir, "index.json")
|
||||
jsonFile, err := os.Create(jsonPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create index.json: %w", err)
|
||||
}
|
||||
defer jsonFile.Close()
|
||||
|
||||
encoder := json.NewEncoder(jsonFile)
|
||||
encoder.SetIndent("", " ")
|
||||
encoder.SetEscapeHTML(false)
|
||||
if err := encoder.Encode(files); err != nil {
|
||||
return fmt.Errorf("failed to encode JSON: %w", err)
|
||||
}
|
||||
log.Println(jsonPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootURL := flag.String("root", "https://s.fc.run", "Root domain URL for absolute links")
|
||||
flag.Parse()
|
||||
|
||||
dirs := flag.Args()
|
||||
if len(dirs) == 0 {
|
||||
fmt.Println("Usage: generate-index [-root <url>] <directory> [<directory>...]")
|
||||
fmt.Println(" -root string")
|
||||
fmt.Println(" Root domain URL for absolute links (default \"https://s.fc.run\")")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := generateIndex(dir, *rootURL); err != nil {
|
||||
log.Fatalf("Error processing %s: %v", dir, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
28
helios/build/index.html.tmpl
Normal file
28
helios/build/index.html.tmpl
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Index of {{.Dir}}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 50px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
h1 { color: #333; }
|
||||
ul { list-style: none; padding: 0; }
|
||||
li { padding: 8px 0; border-bottom: 1px solid #eee; }
|
||||
a { text-decoration: none; color: #0066cc; }
|
||||
a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index of {{.Dir}}</h1>
|
||||
<ul>
|
||||
{{- range .Files}}
|
||||
<li><a href="{{.Name}}">{{.Name}}</a></li>
|
||||
{{- end}}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user