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"
"fmt"
"os"
"sync"
"time"
"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 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 NoPathProvidedError = errors.New("No path or branch provided for GitProvider.")
var NoURLProvidedError = errors.New("Missing URL.")
var NoPathProvidedError = errors.New("Missing path.")
var NoBranchProvidedError = errors.New("Missing branch name.")
// NOTE: GitProvider does not open any worktree files, it only
// - 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
type Commit struct {
Path string
URL string
Branch string
Commit string
Hash string
Date time.Time
}
func NewGitProvider(url string, path string, branch string) (*GitProvider, error) {
// TODO: check if directory is empty
// TODO: force clone
if _, err := os.Stat(path); err == nil {
return GitProviderFromPath(path, branch)
}
return GitProviderFromURL(url, path, branch)
func IsValidRepository(path, url, branch string) *Commit {
commit, _ := Read(path, branch, url)
return commit
}
func GitProviderFromPath(path string, branch string) (*GitProvider, error) {
if branch == "" || path == "" {
return nil, NoPathProvidedError
func OpenOrClone(path, url, branch string) (*Commit, error) {
commit := IsValidRepository(path, url, branch)
if commit == nil {
if _, err := os.Stat(path); err == nil {
err := os.RemoveAll(path)
if err != nil {
return nil, err
}
}
c, err := Clone(path, url, branch)
if err != nil {
return nil, err
}
return c, nil
}
gp := GitProvider{Path: path, Branch: branch}
if err := gp.Read(); err != nil {
return nil, err
}
return &gp, nil
return commit, nil
}
func GitProviderFromURL(url string, path string, branch string) (*GitProvider, error) {
func Pull(path, url, branch string) (*Commit, error) {
if url == "" {
return nil, NoURLProvidedError
}
if branch == "" || path == "" {
if branch == "" {
return nil, NoPathProvidedError
}
gp := GitProvider{URL: url, Path: path, Branch: branch}
if err := gp.Clone(); err != nil {
return nil, err
if path == "" {
return nil, NoPathProvidedError
}
return &gp, nil
}
// 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)
br := plumbing.NewBranchReferenceName(branch)
repo, err := git.PlainOpen(path)
if err != nil {
return err, false
return nil, err
}
wt, err := repo.Worktree()
if err != nil {
return err, false
return nil, err
}
if err := wt.Pull(&git.PullOptions{
RemoteName: "origin",
ReferenceName: branch,
ReferenceName: br,
Progress: os.Stdout,
}); err != nil {
if err == git.NoErrAlreadyUpToDate {
return nil, false
}
return err, false
}); err != nil && err != git.NoErrAlreadyUpToDate {
return nil, err
}
defer wt.Clean(&git.CleanOptions{Dir: true})
oldCommit := g.Commit
if err := g.setValues(repo); err != nil {
return err, false
}
if oldCommit == g.Commit {
return nil, false
}
return nil, true
return latestCommit(repo, path, branch, url)
}
func (g *GitProvider) Clone() error {
if g.URL == "" {
return NoURLProvidedError
func Read(path, branch, url string) (*Commit, error) {
if branch == "" {
return nil, NoBranchProvidedError
}
g.mu.Lock()
defer g.mu.Unlock()
if path == "" {
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,
repo, err := git.PlainOpen(path)
if err != nil {
return nil, err
}
if err := ValidateBranch(repo, branch); err != nil {
br := plumbing.NewBranchReferenceName(branch)
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
}
if err := ValidateBranch(repo, branch); err != nil {
return nil, err
}
}
return latestCommit(repo, path, branch, url)
}
func Clone(path, url, branch string) (*Commit, error) {
if url == "" {
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 err
return nil, err
}
wt, err := repo.Worktree()
if err != nil {
return err
return nil, err
}
defer wt.Clean(&git.CleanOptions{Dir: true})
if err := wt.Checkout(&git.CheckoutOptions{
Branch: branch,
Branch: br,
Force: true,
}); err != nil {
return err
return nil, err
}
return g.setValues(repo)
return latestCommit(repo, path, branch, url)
}
// Implement String Interface
func (g *GitProvider) String() string {
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)
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 *GitProvider) setValues(repo *git.Repository) error {
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{})
if err != nil {
return err
return nil, err
}
defer log.Close()
commit, err := log.Next()
if err != nil {
return err
return nil, err
}
g.Commit = commit.Hash.String()
g.Date = commit.Author.When
c := commit.Hash.String()
d := commit.Author.When
return nil
return &Commit{Path: path, URL: url, Branch: branch, Hash: c, Date: d}, nil
}
func (g *GitProvider) Read() 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 {
func ValidateBranch(repo *git.Repository, branch string) error {
head, err := repo.Head()
if err != nil {
return err
}
cbranch := head.Name().Short()
if cbranch != g.Branch {
if cbranch != branch {
return InvalidBranchError
}
return nil
}
func (g *GitProvider) Wait() {
g.mu.Lock()
defer g.mu.Unlock()
}
func (g *GitProvider) ValidateCommit() error {
if g.Commit == "" || g.Date.IsZero() {
func (g Commit) ValidateCommit() error {
if g.Hash == "" || g.Date.IsZero() {
return InvalidStateError
}
return nil

15
go.mod
View File

@@ -3,7 +3,10 @@ module github.com/Theodor-Springmann-Stiftung/lenz-web
go 1.24.0
require (
github.com/fsnotify/fsnotify v1.8.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/yalue/merged_fs v1.3.0
golang.org/x/net v0.36.0
@@ -14,17 +17,29 @@ require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // 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/cyphar/filepath-securejoin v0.4.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/go-billy/v5 v5.6.2 // 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/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/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // 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
golang.org/x/crypto v0.35.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/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
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/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
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/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
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/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
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/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
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/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/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
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/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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
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/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/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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/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/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
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/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
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-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-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/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@@ -6,8 +6,8 @@ import (
"path/filepath"
"github.com/Theodor-Springmann-Stiftung/lenz-web/config"
gitprovider "github.com/Theodor-Springmann-Stiftung/lenz-web/git"
xmlparsing "github.com/Theodor-Springmann-Stiftung/lenz-web/xml"
"github.com/Theodor-Springmann-Stiftung/lenz-web/git"
"github.com/Theodor-Springmann-Stiftung/lenz-web/xml"
"github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels"
)
@@ -23,14 +23,14 @@ func main() {
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 {
panic(err)
}
lib := xmlmodels.NewLibrary()
lib.Parse(xmlparsing.Commit, dir, gp.Commit)
lib.Parse(xmlparsing.Commit, dir, gp.Hash)
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
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 (
// INFO: This timeout is stupid. Uploads can take a long time, other routes might not. It's messy.
REQUEST_TIMEOUT = 16 * time.Second
SERVER_TIMEOUT = 16 * time.Second
// INFO: This timeout is stupid.
// Uploads can take a long time, other routes might not. It's messy.
REQUEST_TIMEOUT = 120 * time.Second
SERVER_TIMEOUT = 120 * time.Second
// INFO: Maybe this is too long/short?
CACHE_TIME = 24 * time.Hour
@@ -17,3 +27,56 @@ const (
ROUTES_FILEPATH = "./views/routes"
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"
"github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/functions"
"github.com/gofiber/fiber/v2"
"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.Add(2)
@@ -166,6 +167,8 @@ func (e *Engine) Load() {
}()
wg.Wait()
return nil
}
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
if ld == nil {
ld = make(map[string]interface{})
}
// INFO: don't pollute the global data space
for k, v := range gd {
_, ok := ld[k]
if !ok {
ld[k] = v
if e.GlobalData != nil {
for k, v := range ld {
gd[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
})
}