This commit is contained in:
Simon Martens
2025-03-13 22:11:55 +01:00
parent f85dbab551
commit 534fabcb54
8 changed files with 332 additions and 162 deletions

View File

@@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"sync"
"time" "time"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
@@ -13,232 +12,202 @@ import (
var InvalidBranchError = errors.New("The currently checked out branch does not match the requested branch. Please checkout the correct branch first.") var InvalidBranchError = errors.New("The currently checked out branch does not match the requested branch. Please checkout the correct branch first.")
var InvalidStateError = errors.New("The GitProvider is not in a valid state. Fix the issues or continue without Git data.") var InvalidStateError = errors.New("The GitProvider is not in a valid state. Fix the issues or continue without Git data.")
var NoURLProvidedError = errors.New("No URL provided for GitProvider.") var NoURLProvidedError = errors.New("Missing URL.")
var NoPathProvidedError = errors.New("No path or branch provided for GitProvider.") var NoPathProvidedError = errors.New("Missing path.")
var NoBranchProvidedError = errors.New("Missing branch name.")
// NOTE: GitProvider does not open any worktree files, it only type Commit struct {
// - reads in information from the repo, given a path
// - clones a repo, given an URL & a path
// - pulls a repo, given a path
// In case of success it updates it's state: the commit hash and date. Then it closes the repo.
type GitProvider struct {
mu sync.Mutex
URL string
Path string Path string
URL string
Branch string Branch string
Commit string Hash string
Date time.Time Date time.Time
} }
func NewGitProvider(url string, path string, branch string) (*GitProvider, error) { func IsValidRepository(path, url, branch string) *Commit {
// TODO: check if directory is empty commit, _ := Read(path, branch, url)
// TODO: force clone return commit
if _, err := os.Stat(path); err == nil {
return GitProviderFromPath(path, branch)
}
return GitProviderFromURL(url, path, branch)
} }
func GitProviderFromPath(path string, branch string) (*GitProvider, error) { func OpenOrClone(path, url, branch string) (*Commit, error) {
if branch == "" || path == "" { commit := IsValidRepository(path, url, branch)
return nil, NoPathProvidedError if commit == nil {
} if _, err := os.Stat(path); err == nil {
err := os.RemoveAll(path)
gp := GitProvider{Path: path, Branch: branch} if err != nil {
if err := gp.Read(); err != nil {
return nil, err return nil, err
} }
}
return &gp, nil c, err := Clone(path, url, branch)
if err != nil {
return nil, err
}
return c, nil
}
return commit, nil
} }
func GitProviderFromURL(url string, path string, branch string) (*GitProvider, error) { func Pull(path, url, branch string) (*Commit, error) {
if url == "" { if url == "" {
return nil, NoURLProvidedError return nil, NoURLProvidedError
} }
if branch == "" || path == "" { if branch == "" {
return nil, NoPathProvidedError return nil, NoPathProvidedError
} }
gp := GitProvider{URL: url, Path: path, Branch: branch} if path == "" {
if err := gp.Clone(); err != nil { return nil, NoPathProvidedError
return nil, err
} }
return &gp, nil br := plumbing.NewBranchReferenceName(branch)
} repo, err := git.PlainOpen(path)
// Returs true if the repo was updated remotely, false otherwise
func (g *GitProvider) Pull() (error, bool) {
g.mu.Lock()
defer g.mu.Unlock()
branch := plumbing.NewBranchReferenceName(g.Branch)
repo, err := git.PlainOpen(g.Path)
if err != nil { if err != nil {
return err, false return nil, err
} }
wt, err := repo.Worktree() wt, err := repo.Worktree()
if err != nil { if err != nil {
return err, false return nil, err
} }
if err := wt.Pull(&git.PullOptions{ if err := wt.Pull(&git.PullOptions{
RemoteName: "origin", RemoteName: "origin",
ReferenceName: branch, ReferenceName: br,
Progress: os.Stdout, Progress: os.Stdout,
}); err != nil { }); err != nil && err != git.NoErrAlreadyUpToDate {
if err == git.NoErrAlreadyUpToDate { return nil, err
return nil, false
}
return err, false
} }
defer wt.Clean(&git.CleanOptions{Dir: true}) defer wt.Clean(&git.CleanOptions{Dir: true})
oldCommit := g.Commit return latestCommit(repo, path, branch, url)
if err := g.setValues(repo); err != nil {
return err, false
}
if oldCommit == g.Commit {
return nil, false
}
return nil, true
} }
func (g *GitProvider) Clone() error { func Read(path, branch, url string) (*Commit, error) {
if g.URL == "" { if branch == "" {
return NoURLProvidedError return nil, NoBranchProvidedError
} }
g.mu.Lock() if path == "" {
defer g.mu.Unlock() return nil, NoPathProvidedError
}
branch := plumbing.NewBranchReferenceName(g.Branch) if url == "" {
return nil, NoURLProvidedError
repo, err := git.PlainClone(g.Path, false, &git.CloneOptions{ }
URL: g.URL,
Progress: os.Stdout,
})
repo, err := git.PlainOpen(path)
if err != nil { if err != nil {
return err return nil, err
} }
if err := ValidateBranch(repo, branch); err != nil {
br := plumbing.NewBranchReferenceName(branch)
wt, err := repo.Worktree() wt, err := repo.Worktree()
if err != nil { if err != nil {
return err return nil, err
} }
defer wt.Clean(&git.CleanOptions{Dir: true}) defer wt.Clean(&git.CleanOptions{Dir: true})
if err := wt.Checkout(&git.CheckoutOptions{ if err := wt.Checkout(&git.CheckoutOptions{
Branch: branch, Branch: br,
Force: true, Force: true,
}); err != nil { }); err != nil {
return err return nil, err
} }
return g.setValues(repo) if err := ValidateBranch(repo, branch); err != nil {
return nil, err
}
}
return latestCommit(repo, path, branch, url)
} }
// Implement String Interface func Clone(path, url, branch string) (*Commit, error) {
func (g *GitProvider) String() string { if url == "" {
return fmt.Sprintf("GitProvider\nURL: %s\nPath: %s\nBranch: %s\nCommit: %s\nDate: %s\n", g.URL, g.Path, g.Branch, g.Commit, g.Date) return nil, NoURLProvidedError
}
if branch == "" {
return nil, NoBranchProvidedError
}
if path == "" {
return nil, NoPathProvidedError
}
br := plumbing.NewBranchReferenceName(branch)
repo, err := git.PlainClone(path, false, &git.CloneOptions{
URL: url,
Progress: os.Stdout,
})
if err != nil {
return nil, err
}
wt, err := repo.Worktree()
if err != nil {
return nil, err
}
defer wt.Clean(&git.CleanOptions{Dir: true})
if err := wt.Checkout(&git.CheckoutOptions{
Branch: br,
Force: true,
}); err != nil {
return nil, err
}
return latestCommit(repo, path, branch, url)
} }
func (g *GitProvider) setValues(repo *git.Repository) error { func (g Commit) String() string {
return fmt.Sprintf("Path: %s\nURL: %s\nBranch: %s\nHash: %s\nDate: %s", g.Path, g.URL, g.Branch, g.Hash, g.Date)
}
func (g Commit) Pull() (*Commit, error) {
return Pull(g.Path, g.URL, g.Branch)
}
func latestCommit(repo *git.Repository, path, branch, url string) (*Commit, error) {
log, err := repo.Log(&git.LogOptions{}) log, err := repo.Log(&git.LogOptions{})
if err != nil { if err != nil {
return err return nil, err
} }
defer log.Close() defer log.Close()
commit, err := log.Next() commit, err := log.Next()
if err != nil { if err != nil {
return err return nil, err
} }
g.Commit = commit.Hash.String() c := commit.Hash.String()
g.Date = commit.Author.When d := commit.Author.When
return nil return &Commit{Path: path, URL: url, Branch: branch, Hash: c, Date: d}, nil
} }
func (g *GitProvider) Read() error { func ValidateBranch(repo *git.Repository, branch string) error {
g.mu.Lock()
defer g.mu.Unlock()
repo, err := git.PlainOpen(g.Path)
if err != nil {
return err
}
if err := g.ValidateBranch(repo); err != nil {
branch := plumbing.NewBranchReferenceName(g.Branch)
wt, err := repo.Worktree()
if err != nil {
return err
}
defer wt.Clean(&git.CleanOptions{Dir: true})
if err := wt.Checkout(&git.CheckoutOptions{
Branch: branch,
Force: true,
}); err != nil {
return err
}
if err := g.ValidateBranch(repo); err != nil {
return err
}
}
return g.setValues(repo)
}
func (g *GitProvider) Validate() error {
repo, err := git.PlainOpen(g.Path)
if err != nil {
return err
}
if err := g.ValidateBranch(repo); err != nil {
return err
}
if err := g.ValidateCommit(); err != nil {
return err
}
return nil
}
func (g *GitProvider) ValidateBranch(repo *git.Repository) error {
head, err := repo.Head() head, err := repo.Head()
if err != nil { if err != nil {
return err return err
} }
cbranch := head.Name().Short() cbranch := head.Name().Short()
if cbranch != g.Branch { if cbranch != branch {
return InvalidBranchError return InvalidBranchError
} }
return nil return nil
} }
func (g *GitProvider) Wait() { func (g Commit) ValidateCommit() error {
g.mu.Lock() if g.Hash == "" || g.Date.IsZero() {
defer g.mu.Unlock()
}
func (g *GitProvider) ValidateCommit() error {
if g.Commit == "" || g.Date.IsZero() {
return InvalidStateError return InvalidStateError
} }
return nil return nil

15
go.mod
View File

@@ -3,7 +3,10 @@ module github.com/Theodor-Springmann-Stiftung/lenz-web
go 1.24.0 go 1.24.0
require ( require (
github.com/fsnotify/fsnotify v1.8.0
github.com/go-git/go-git/v5 v5.14.0 github.com/go-git/go-git/v5 v5.14.0
github.com/gofiber/fiber/v2 v2.52.6
github.com/gofiber/storage/memory/v2 v2.0.1
github.com/kelseyhightower/envconfig v1.4.0 github.com/kelseyhightower/envconfig v1.4.0
github.com/yalue/merged_fs v1.3.0 github.com/yalue/merged_fs v1.3.0
golang.org/x/net v0.36.0 golang.org/x/net v0.36.0
@@ -14,17 +17,29 @@ require (
dario.cat/mergo v1.0.0 // indirect dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/cloudflare/circl v1.6.0 // indirect github.com/cloudflare/circl v1.6.0 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect github.com/skeema/knownhosts v1.3.1 // indirect
github.com/tinylib/msgp v1.2.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.35.0 // indirect golang.org/x/crypto v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect golang.org/x/sys v0.30.0 // indirect

33
go.sum
View File

@@ -5,6 +5,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -20,6 +22,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
@@ -30,16 +34,24 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/storage/memory/v2 v2.0.1 h1:tAETnom9uvEB9B3I2LkgewiuqYDAH0ItrIsmT8MUEwk=
github.com/gofiber/storage/memory/v2 v2.0.1/go.mod h1:RRo3RfX6nTD/UhERyE/u5LcSfqtMo9dA4ltmieSe+QM=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -47,14 +59,25 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
@@ -67,6 +90,14 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY=
@@ -85,6 +116,8 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@@ -6,8 +6,8 @@ import (
"path/filepath" "path/filepath"
"github.com/Theodor-Springmann-Stiftung/lenz-web/config" "github.com/Theodor-Springmann-Stiftung/lenz-web/config"
gitprovider "github.com/Theodor-Springmann-Stiftung/lenz-web/git" "github.com/Theodor-Springmann-Stiftung/lenz-web/git"
xmlparsing "github.com/Theodor-Springmann-Stiftung/lenz-web/xml" "github.com/Theodor-Springmann-Stiftung/lenz-web/xml"
"github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels" "github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels"
) )
@@ -23,14 +23,14 @@ func main() {
dir := filepath.Join(cfg.BaseDIR, cfg.GITPath) dir := filepath.Join(cfg.BaseDIR, cfg.GITPath)
gp, err := gitprovider.NewGitProvider(cfg.GitURL, dir, cfg.GitBranch) gp, err := gitprovider.OpenOrClone(dir, cfg.GitURL, cfg.GitBranch)
if err != nil { if err != nil {
panic(err) panic(err)
} }
lib := xmlmodels.NewLibrary() lib := xmlmodels.NewLibrary()
lib.Parse(xmlparsing.Commit, dir, gp.Commit) lib.Parse(xmlparsing.Commit, dir, gp.Hash)
fmt.Println("Library: ", lib) fmt.Println("Library: ", lib)
} }

7
server/functions.go Normal file
View File

@@ -0,0 +1,7 @@
package server
import "github.com/gofiber/fiber/v2"
func CacheFunc(c *fiber.Ctx) bool {
return c.Query("noCache") == "true" || c.Response().StatusCode() != fiber.StatusOK
}

View File

@@ -1,11 +1,21 @@
package server package server
import "time" import (
"time"
"github.com/Theodor-Springmann-Stiftung/lenz-web/templating"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cache"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/storage/memory/v2"
)
const ( const (
// INFO: This timeout is stupid. Uploads can take a long time, other routes might not. It's messy. // INFO: This timeout is stupid.
REQUEST_TIMEOUT = 16 * time.Second // Uploads can take a long time, other routes might not. It's messy.
SERVER_TIMEOUT = 16 * time.Second REQUEST_TIMEOUT = 120 * time.Second
SERVER_TIMEOUT = 120 * time.Second
// INFO: Maybe this is too long/short? // INFO: Maybe this is too long/short?
CACHE_TIME = 24 * time.Hour CACHE_TIME = 24 * time.Hour
@@ -17,3 +27,56 @@ const (
ROUTES_FILEPATH = "./views/routes" ROUTES_FILEPATH = "./views/routes"
LAYOUT_FILEPATH = "./views/layouts" LAYOUT_FILEPATH = "./views/layouts"
) )
type Server struct {
Engine *templating.Engine
Server *fiber.App
Cache *memory.Storage
}
func New(engine *templating.Engine, debug bool) Server {
c := memory.New(memory.Config{
GCInterval: CACHE_GC_INTERVAL,
})
server := fiber.New(fiber.Config{
AppName: "Lenz",
CaseSensitive: false,
ErrorHandler: fiber.DefaultErrorHandler,
WriteTimeout: REQUEST_TIMEOUT,
ReadTimeout: REQUEST_TIMEOUT,
PassLocalsToViews: true,
Views: engine,
EnablePrintRoutes: debug,
ViewsLayout: templating.DEFAULT_LAYOUT_NAME,
UnescapePath: true,
})
if debug {
server.Use(logger.New())
}
server.Use(recover.New())
if debug {
server.Use(cache.New(cache.Config{
Next: CacheFunc,
Expiration: CACHE_TIME,
CacheControl: false,
Storage: c,
}))
} else {
server.Use(cache.New(cache.Config{
Next: CacheFunc,
Expiration: CACHE_TIME,
CacheControl: true,
Storage: c,
}))
}
return Server{
Engine: engine,
Server: server,
Cache: c,
}
}

View File

@@ -9,6 +9,7 @@ import (
"sync" "sync"
"github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/functions" "github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/functions"
"github.com/gofiber/fiber/v2"
"golang.org/x/net/websocket" "golang.org/x/net/websocket"
) )
@@ -151,7 +152,7 @@ func (e *Engine) Globals(data map[string]interface{}) {
} }
} }
func (e *Engine) Load() { func (e *Engine) Load() error {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(2) wg.Add(2)
@@ -166,6 +167,8 @@ func (e *Engine) Load() {
}() }()
wg.Wait() wg.Wait()
return nil
} }
func (e *Engine) Reload() { func (e *Engine) Reload() {
@@ -197,17 +200,12 @@ func (e *Engine) AddFuncs(funcs map[string]interface{}) {
} }
} }
func (e *Engine) Render(out io.Writer, path string, ld map[string]interface{}, layout ...string) error { func (e *Engine) Render(out io.Writer, path string, data interface{}, layout ...string) error {
ld := data.(fiber.Map)
gd := e.GlobalData gd := e.GlobalData
if ld == nil { if e.GlobalData != nil {
ld = make(map[string]interface{}) for k, v := range ld {
} gd[k] = v
// INFO: don't pollute the global data space
for k, v := range gd {
_, ok := ld[k]
if !ok {
ld[k] = v
} }
} }

85
watcher.go Normal file
View File

@@ -0,0 +1,85 @@
package main
import (
"io/fs"
"log"
"os"
"path/filepath"
"time"
"github.com/fsnotify/fsnotify"
)
const (
WATCHER_DEBOUNCE = 300 * time.Millisecond
)
// INFO: this is hot reload for poor people
type Watcher struct {
*fsnotify.Watcher
}
func RefreshWatcher(fn func()) (*Watcher, error) {
watcher := Watcher{}
w, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
watcher.Watcher = w
done := make(chan bool)
go func() {
var reloadTimer *time.Timer
for {
select {
case event := <-watcher.Events:
if event.Op&(fsnotify.Create|fsnotify.Write|fsnotify.Remove|fsnotify.Rename) != 0 {
if reloadTimer != nil {
reloadTimer.Stop()
}
reloadTimer = time.AfterFunc(WATCHER_DEBOUNCE, func() {
log.Println("Changes detected, reloading templates...")
fn()
})
}
if event.Op&fsnotify.Create == fsnotify.Create {
fi, statErr := os.Stat(event.Name)
if statErr == nil && fi.IsDir() {
_ = watcher.Add(event.Name)
log.Printf("Now watching new directory: %s", event.Name)
}
}
case err := <-watcher.Errors:
if err != nil {
log.Printf("fsnotify error: %v\n", err)
}
case <-done:
watcher.Close()
return
}
}
}()
return &watcher, nil
}
func (w *Watcher) AddRecursive(root string) error {
return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
werr := w.Add(path)
if werr != nil {
return werr
}
log.Printf("Now watching directory: %s", path)
}
return nil
})
}