diff options
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/ext/compression.go | 2 | ||||
| -rw-r--r-- | pkg/ext/log.go | 53 | ||||
| -rw-r--r-- | pkg/ext/router.go | 72 | ||||
| -rw-r--r-- | pkg/git/git.go | 3 | ||||
| -rw-r--r-- | pkg/handler/about/handler.go | 10 | ||||
| -rw-r--r-- | pkg/handler/config/handler.go | 16 | ||||
| -rw-r--r-- | pkg/handler/git/handler.go | 61 | ||||
| -rw-r--r-- | pkg/handler/router.go | 36 | ||||
| -rw-r--r-- | pkg/handler/static/handler.go | 6 | ||||
| -rw-r--r-- | pkg/service/git.go | 29 | 
10 files changed, 208 insertions, 80 deletions
| diff --git a/pkg/ext/compression.go b/pkg/ext/compression.go index 92144b8..57ad49a 100644 --- a/pkg/ext/compression.go +++ b/pkg/ext/compression.go @@ -24,7 +24,7 @@ type CompressionResponseWriter struct {  	compressWriter io.Writer  } -func Compress(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { +func Compress(next http.HandlerFunc) http.HandlerFunc {  	return func(w http.ResponseWriter, r *http.Request) {  		if accept, ok := r.Header["Accept-Encoding"]; ok {  			if compress, algo := GetCompressionWriter(u.FirstOrZero(accept), w); algo != "" { diff --git a/pkg/ext/log.go b/pkg/ext/log.go new file mode 100644 index 0000000..a9d26a9 --- /dev/null +++ b/pkg/ext/log.go @@ -0,0 +1,53 @@ +package ext + +import ( +	"log/slog" +	"net/http" +	"time" +) + +type statusWraper struct { +	statusCode  int +	innerWriter http.ResponseWriter +} + +func (s *statusWraper) Header() http.Header { +	return s.innerWriter.Header() +} + +func (s *statusWraper) Write(b []byte) (int, error) { +	return s.innerWriter.Write(b) +} + +func (s *statusWraper) WriteHeader(statusCode int) { +	s.statusCode = statusCode +	s.innerWriter.WriteHeader(statusCode) +} + +func (s *statusWraper) StatusCode() int { +	if s.statusCode == 0 { +		return 200 +	} +	return s.statusCode +} + +func wrap(w http.ResponseWriter) *statusWraper { +	return &statusWraper{ +		innerWriter: w, +	} +} + +func Log(next http.HandlerFunc) http.HandlerFunc { +	return func(w http.ResponseWriter, r *http.Request) { +		t := time.Now() +		s := wrap(w) +		next(s, r) +		slog.Info( +			"Http request", +			"method", r.Method, +			"code", s.StatusCode(), +			"path", r.URL, +			"elapsed", time.Since(t), +		) +	} +} diff --git a/pkg/ext/router.go b/pkg/ext/router.go new file mode 100644 index 0000000..5d22814 --- /dev/null +++ b/pkg/ext/router.go @@ -0,0 +1,72 @@ +package ext + +import ( +	"errors" +	"fmt" +	"net/http" + +	"git.gabrielgio.me/cerrado/pkg/service" +	"git.gabrielgio.me/cerrado/templates" +) + +type ( +	Router struct { +		middlewares []Middleware +		router      *http.ServeMux +	} +	Middleware          func(next http.HandlerFunc) http.HandlerFunc +	ErrorRequestHandler func(w http.ResponseWriter, r *http.Request) error +) + +func NewRouter() *Router { +	return &Router{ +		router: http.NewServeMux(), +	} +} +func (r *Router) Handler() http.Handler { +	return r.router +} + +func (r *Router) AddMiddleware(middleware Middleware) { +	r.middlewares = append(r.middlewares, middleware) +} + +func wrapError(next ErrorRequestHandler) http.HandlerFunc { +	return func(w http.ResponseWriter, r *http.Request) { +		if err := next(w, r); err != nil { +			if errors.Is(err, service.RepositoryNotFoundErr) { +				NotFound(w) +			} else { +				InternalServerError(w, err) +			} +		} +	} +} + +func (r *Router) run(next ErrorRequestHandler) http.HandlerFunc { +	return func(w http.ResponseWriter, re *http.Request) { +		req := wrapError(next) +		for _, r := range r.middlewares { +			req = r(req) +		} +		req(w, re) +	} +} + +func (r *Router) HandleFunc(path string, handler ErrorRequestHandler) { +	r.router.HandleFunc(path, r.run(handler)) +} + +func NotFound(w http.ResponseWriter) { +	w.WriteHeader(http.StatusNotFound) +	templates.WritePageTemplate(w, &templates.ErrorPage{ +		Message: "Not Found", +	}) +} + +func InternalServerError(w http.ResponseWriter, err error) { +	w.WriteHeader(http.StatusInternalServerError) +	templates.WritePageTemplate(w, &templates.ErrorPage{ +		Message: fmt.Sprintf("Internal Server Error:\n%s", err.Error()), +	}) +} diff --git a/pkg/git/git.go b/pkg/git/git.go index 6a7b91f..ad5d3bc 100644 --- a/pkg/git/git.go +++ b/pkg/git/git.go @@ -99,9 +99,6 @@ func (g *GitRepository) Commits() ([]*object.Commit, error) {  		}  		commits = append(commits, c)  	} -	if err != nil { -		return nil, err -	}  	return commits, nil  } diff --git a/pkg/handler/about/handler.go b/pkg/handler/about/handler.go index 1acde60..ac3d314 100644 --- a/pkg/handler/about/handler.go +++ b/pkg/handler/about/handler.go @@ -2,7 +2,6 @@ package about  import (  	"io" -	"log/slog"  	"net/http"  	"os" @@ -27,17 +26,15 @@ func NewAboutHandler(configRepo configurationRepository) *AboutHandler {  	return &AboutHandler{configRepo.GetRootReadme()}  } -func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) { +func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) error {  	f, err := os.Open(g.readmePath)  	if err != nil { -		slog.Error("Error loading readme file", "error", err) -		return +		return err  	}  	bs, err := io.ReadAll(f)  	if err != nil { -		slog.Error("Error reading readme file bytes", "error", err) -		return +		return err  	}  	extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock @@ -54,4 +51,5 @@ func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) {  		Body: bs,  	}  	templates.WritePageTemplate(w, gitList) +	return nil  } diff --git a/pkg/handler/config/handler.go b/pkg/handler/config/handler.go index 30f4283..c43b54d 100644 --- a/pkg/handler/config/handler.go +++ b/pkg/handler/config/handler.go @@ -3,7 +3,6 @@ package config  import (  	"bytes"  	"encoding/json" -	"log/slog"  	"net/http"  	"github.com/alecthomas/chroma/v2/formatters/html" @@ -11,6 +10,7 @@ import (  	"github.com/alecthomas/chroma/v2/styles"  	"git.gabrielgio.me/cerrado/pkg/config" +	"git.gabrielgio.me/cerrado/pkg/ext"  	"git.gabrielgio.me/cerrado/templates"  ) @@ -21,8 +21,8 @@ type (  	}  ) -func ConfigFile(configRepo configurationRepository) func(http.ResponseWriter, *http.Request) { -	return func(w http.ResponseWriter, _ *http.Request) { +func ConfigFile(configRepo configurationRepository) ext.ErrorRequestHandler { +	return func(w http.ResponseWriter, _ *http.Request) error {  		config := struct {  			RootReadme   string @@ -34,8 +34,7 @@ func ConfigFile(configRepo configurationRepository) func(http.ResponseWriter, *h  		b, err := json.MarshalIndent(config, "", "  ")  		if err != nil { -			slog.Error("Error parsing json", "error", err) -			return +			return err  		}  		lexer := lexers.Get("json") @@ -45,15 +44,13 @@ func ConfigFile(configRepo configurationRepository) func(http.ResponseWriter, *h  		)  		iterator, err := lexer.Tokenise(nil, string(b))  		if err != nil { -			slog.Error("Error tokenise", "error", err) -			return +			return err  		}  		var code bytes.Buffer  		err = formatter.Format(&code, style, iterator)  		if err != nil { -			slog.Error("Error format", "error", err) -			return +			return err  		}  		hello := &templates.ConfigPage{ @@ -61,5 +58,6 @@ func ConfigFile(configRepo configurationRepository) func(http.ResponseWriter, *h  		}  		templates.WritePageTemplate(w, hello) +		return nil  	}  } diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go index 7bdf372..d952fef 100644 --- a/pkg/handler/git/handler.go +++ b/pkg/handler/git/handler.go @@ -3,7 +3,6 @@ package git  import (  	"bytes"  	"io" -	"log/slog"  	"net/http"  	"os"  	"path/filepath" @@ -50,23 +49,20 @@ func NewGitHandler(gitService gitService, confRepo configurationRepository) *Git  	}  } -func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) { +func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) error {  	repos, err := g.gitService.ListRepositories()  	if err != nil { -		slog.Error("Error listing repo", "error", err) -		return +		return err  	}  	f, err := os.Open(g.readmePath)  	if err != nil { -		slog.Error("Error loading readme file", "error", err) -		return +		return err  	}  	bs, err := io.ReadAll(f)  	if err != nil { -		slog.Error("Error reading readme file bytes", "error", err) -		return +		return err  	}  	extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock @@ -84,15 +80,15 @@ func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) {  		About:         bs,  	}  	templates.WritePageTemplate(w, gitList) +	return nil  } -func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) { +func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) error {  	ext.SetHTML(w)  	name := r.PathValue("name")  	ref, err := g.gitService.GetHead(name)  	if err != nil { -		slog.Error("Error loading head", "error", err) -		return +		return err  	}  	gitList := &templates.GitItemPage{ @@ -101,15 +97,15 @@ func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) {  		GitItemBase: &templates.GitItemSummaryPage{},  	}  	templates.WritePageTemplate(w, gitList) +	return nil  } -func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) { +func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) error {  	ext.SetHTML(w)  	name := r.PathValue("name")  	ref, err := g.gitService.GetHead(name)  	if err != nil { -		slog.Error("Error loading head", "error", err) -		return +		return err  	}  	gitList := &templates.GitItemPage{  		Name:        name, @@ -117,28 +113,26 @@ func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) {  		GitItemBase: &templates.GitItemAboutPage{},  	}  	templates.WritePageTemplate(w, gitList) +	return nil  } -func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) { +func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) error {  	ext.SetHTML(w)  	name := r.PathValue("name")  	tags, err := g.gitService.ListTags(name)  	if err != nil { -		slog.Error("Error loading tags", "error", err) -		return +		return err  	}  	branches, err := g.gitService.ListBranches(name)  	if err != nil { -		slog.Error("Error loading branches", "error", err) -		return +		return err  	}  	ref, err := g.gitService.GetHead(name)  	if err != nil { -		slog.Error("Error loading head", "error", err) -		return +		return err  	}  	gitList := &templates.GitItemPage{ @@ -150,9 +144,10 @@ func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) {  		},  	}  	templates.WritePageTemplate(w, gitList) +	return nil  } -func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) { +func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) error {  	ext.SetHTML(w)  	name := r.PathValue("name")  	ref := r.PathValue("ref") @@ -160,8 +155,7 @@ func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) {  	tree, err := g.gitService.GetTree(name, ref, rest)  	if err != nil { -		slog.Error("Error loading tree", "error", err) -		return +		return err  	}  	gitList := &templates.GitItemPage{ @@ -175,9 +169,10 @@ func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) {  		},  	}  	templates.WritePageTemplate(w, gitList) +	return nil  } -func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) { +func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) error {  	ext.SetHTML(w)  	name := r.PathValue("name")  	ref := r.PathValue("ref") @@ -185,8 +180,7 @@ func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) {  	file, err := g.gitService.GetFileContent(name, ref, rest)  	if err != nil { -		slog.Error("Error loading blob", "error", err) -		return +		return err  	}  	filename := filepath.Base(rest) @@ -197,15 +191,13 @@ func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) {  	)  	iterator, err := lexer.Tokenise(nil, file)  	if err != nil { -		slog.Error("Error tokenise", "error", err) -		return +		return err  	}  	var code bytes.Buffer  	err = formatter.Format(&code, style, iterator)  	if err != nil { -		slog.Error("Error format", "error", err) -		return +		return err  	}  	gitList := &templates.GitItemPage{ @@ -217,17 +209,17 @@ func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) {  		},  	}  	templates.WritePageTemplate(w, gitList) +	return nil  } -func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) { +func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) error {  	ext.SetHTML(w)  	name := r.PathValue("name")  	ref := r.PathValue("ref")  	commits, err := g.gitService.ListCommits(name, ref)  	if err != nil { -		slog.Error("Error loading commits", "error", err) -		return +		return err  	}  	gitList := &templates.GitItemPage{ @@ -238,6 +230,7 @@ func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) {  		},  	}  	templates.WritePageTemplate(w, gitList) +	return nil  }  func GetLexers(filename string) chroma.Lexer { diff --git a/pkg/handler/router.go b/pkg/handler/router.go index bf13ad5..3da812f 100644 --- a/pkg/handler/router.go +++ b/pkg/handler/router.go @@ -20,9 +20,9 @@ func MountHandler(  	configRepo *serverconfig.ConfigurationRepository,  ) (http.Handler, error) {  	var ( -		gitHandler   = git.NewGitHandler(gitService, configRepo) -		aboutHandler = about.NewAboutHandler(configRepo) -		configHander = config.ConfigFile(configRepo) +		gitHandler    = git.NewGitHandler(gitService, configRepo) +		aboutHandler  = about.NewAboutHandler(configRepo) +		configHandler = config.ConfigFile(configRepo)  	)  	staticHandler, err := static.ServeStaticHandler() @@ -30,21 +30,19 @@ func MountHandler(  		return nil, err  	} -	mux := http.NewServeMux() +	mux := ext.NewRouter() +	mux.AddMiddleware(ext.Compress) +	mux.AddMiddleware(ext.Log) -	mux.HandleFunc("/static/{file}", m(staticHandler)) -	mux.HandleFunc("/{name}/about/{$}", m(gitHandler.About)) -	mux.HandleFunc("/{name}", m(gitHandler.Summary)) -	mux.HandleFunc("/{name}/refs/{$}", m(gitHandler.Refs)) -	mux.HandleFunc("/{name}/tree/{ref}/{rest...}", m(gitHandler.Tree)) -	mux.HandleFunc("/{name}/blob/{ref}/{rest...}", m(gitHandler.Blob)) -	mux.HandleFunc("/{name}/log/{ref}", m(gitHandler.Log)) -	mux.HandleFunc("/config", m(configHander)) -	mux.HandleFunc("/about", m(aboutHandler.About)) -	mux.HandleFunc("/", m(gitHandler.List)) -	return mux, nil -} - -func m(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { -	return ext.Compress(next) +	mux.HandleFunc("/static/{file}", staticHandler) +	mux.HandleFunc("/{name}/about/{$}", gitHandler.About) +	mux.HandleFunc("/{name}", gitHandler.Summary) +	mux.HandleFunc("/{name}/refs/{$}", gitHandler.Refs) +	mux.HandleFunc("/{name}/tree/{ref}/{rest...}", gitHandler.Tree) +	mux.HandleFunc("/{name}/blob/{ref}/{rest...}", gitHandler.Blob) +	mux.HandleFunc("/{name}/log/{ref}", gitHandler.Log) +	mux.HandleFunc("/config", configHandler) +	mux.HandleFunc("/about", aboutHandler.About) +	mux.HandleFunc("/", gitHandler.List) +	return mux.Handler(), nil  } diff --git a/pkg/handler/static/handler.go b/pkg/handler/static/handler.go index 5155068..0973d75 100644 --- a/pkg/handler/static/handler.go +++ b/pkg/handler/static/handler.go @@ -10,19 +10,21 @@ import (  	"git.gabrielgio.me/cerrado/static"  ) -func ServeStaticHandler() (func(w http.ResponseWriter, r *http.Request), error) { +func ServeStaticHandler() (ext.ErrorRequestHandler, error) {  	staticFs, err := fs.Sub(static.Static, ".")  	if err != nil {  		return nil, err  	} -	return func(w http.ResponseWriter, r *http.Request) { +	return func(w http.ResponseWriter, r *http.Request) error {  		var (  			f = r.PathValue("file")  			e = filepath.Ext(f)  			m = mime.TypeByExtension(e)  		)  		ext.SetMIME(w, m) +		w.Header().Add("Cache-Control", "immutable")  		http.ServeFileFS(w, r, staticFs, f) +		return nil  	}, nil  } diff --git a/pkg/service/git.go b/pkg/service/git.go index 31a1cbb..94e2adc 100644 --- a/pkg/service/git.go +++ b/pkg/service/git.go @@ -1,6 +1,7 @@  package service  import ( +	"errors"  	"log/slog"  	"os"  	"path" @@ -31,6 +32,10 @@ type (  	}  ) +var ( +	RepositoryNotFoundErr = errors.New("Repository not found") +) +  // TODO: make it configurable  const timeFormat = "2006.01.02 15:04:05" @@ -84,8 +89,10 @@ func (g *GitService) ListRepositories() ([]*Repository, error) {  }  func (g *GitService) ListCommits(name, ref string) ([]*object.Commit, error) { -	// TODO: handle nil  	r := g.configRepo.GetByName(name) +	if r == nil { +		return nil, RepositoryNotFoundErr +	}  	repo, err := git.OpenRepository(r.Path)  	if err != nil { @@ -100,8 +107,10 @@ func (g *GitService) ListCommits(name, ref string) ([]*object.Commit, error) {  }  func (g *GitService) GetTree(name, ref, path string) (*object.Tree, error) { -	// TODO: handle nil  	r := g.configRepo.GetByName(name) +	if r == nil { +		return nil, RepositoryNotFoundErr +	}  	repo, err := git.OpenRepository(r.Path)  	if err != nil { @@ -116,8 +125,10 @@ func (g *GitService) GetTree(name, ref, path string) (*object.Tree, error) {  }  func (g *GitService) GetFileContent(name, ref, path string) (string, error) { -	// TODO: handle nil  	r := g.configRepo.GetByName(name) +	if r == nil { +		return "", RepositoryNotFoundErr +	}  	repo, err := git.OpenRepository(r.Path)  	if err != nil { @@ -132,8 +143,10 @@ func (g *GitService) GetFileContent(name, ref, path string) (string, error) {  }  func (g *GitService) ListTags(name string) ([]*object.Tag, error) { -	// TODO: handle nil  	r := g.configRepo.GetByName(name) +	if r == nil { +		return nil, RepositoryNotFoundErr +	}  	repo, err := git.OpenRepository(r.Path)  	if err != nil { @@ -143,8 +156,10 @@ func (g *GitService) ListTags(name string) ([]*object.Tag, error) {  }  func (g *GitService) ListBranches(name string) ([]*plumbing.Reference, error) { -	// TODO: handle nil  	r := g.configRepo.GetByName(name) +	if r == nil { +		return nil, RepositoryNotFoundErr +	}  	repo, err := git.OpenRepository(r.Path)  	if err != nil { @@ -154,8 +169,10 @@ func (g *GitService) ListBranches(name string) ([]*plumbing.Reference, error) {  }  func (g *GitService) GetHead(name string) (*plumbing.Reference, error) { -	// TODO: handle nil  	r := g.configRepo.GetByName(name) +	if r == nil { +		return nil, RepositoryNotFoundErr +	}  	repo, err := git.OpenRepository(r.Path)  	if err != nil { | 
