Lots of changes to the website
This commit is contained in:
111
internal/changelog/changelog.go
Normal file
111
internal/changelog/changelog.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// Package changelog manages the infrastructure changelog log.
|
||||
package changelog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Categories available for changelog entries.
|
||||
var Categories = []string{"hardware", "network", "software", "migration"}
|
||||
|
||||
// Entry is a single changelog entry.
|
||||
type Entry struct {
|
||||
ID string `json:"id"`
|
||||
Date string `json:"date"` // YYYY-MM-DD
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"` // hardware, network, software, migration
|
||||
}
|
||||
|
||||
// Log holds all changelog entries.
|
||||
type Log struct {
|
||||
Entries []Entry `json:"entries"`
|
||||
}
|
||||
|
||||
// Load reads the changelog from path. Returns an empty log if the file does not exist.
|
||||
func Load(path string) (*Log, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return &Log{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var l Log
|
||||
if err := json.Unmarshal(raw, &l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(l.Entries, func(i, j int) bool {
|
||||
return l.Entries[i].Date > l.Entries[j].Date
|
||||
})
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
// save writes the log to path without sorting (internal use).
|
||||
func save(path string, l *Log) error {
|
||||
raw, err := json.MarshalIndent(l, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, raw, 0644)
|
||||
}
|
||||
|
||||
// Add appends a new entry and saves.
|
||||
func Add(path string, e Entry) error {
|
||||
l, err := Load(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.ID = fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
l.Entries = append(l.Entries, e)
|
||||
return save(path, l)
|
||||
}
|
||||
|
||||
// Update replaces an entry by ID and saves.
|
||||
func Update(path string, e Entry) error {
|
||||
l, err := Load(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, entry := range l.Entries {
|
||||
if entry.ID == e.ID {
|
||||
l.Entries[i] = e
|
||||
return save(path, l)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("entry %s not found", e.ID)
|
||||
}
|
||||
|
||||
// Delete removes an entry by ID and saves.
|
||||
func Delete(path string, id string) error {
|
||||
l, err := Load(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kept := l.Entries[:0]
|
||||
for _, e := range l.Entries {
|
||||
if e.ID != id {
|
||||
kept = append(kept, e)
|
||||
}
|
||||
}
|
||||
l.Entries = kept
|
||||
return save(path, l)
|
||||
}
|
||||
|
||||
// Get returns a single entry by ID.
|
||||
func Get(path string, id string) (*Entry, error) {
|
||||
l, err := Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, e := range l.Entries {
|
||||
if e.ID == id {
|
||||
return &e, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("entry %s not found", id)
|
||||
}
|
||||
Reference in New Issue
Block a user