mirror of
https://github.com/Theodor-Springmann-Stiftung/lenz-web.git
synced 2025-10-28 16:55:32 +00:00
Server
This commit is contained in:
255
git/git.go
255
git/git.go
@@ -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
15
go.mod
@@ -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
33
go.sum
@@ -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=
|
||||
|
||||
8
lenz.go
8
lenz.go
@@ -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
7
server/functions.go
Normal 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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
85
watcher.go
Normal 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
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user