aboutsummaryrefslogtreecommitdiff
path: root/pkg/handler
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/handler')
-rw-r--r--pkg/handler/about/handler.go5
-rw-r--r--pkg/handler/auth/login.go97
-rw-r--r--pkg/handler/config/handler.go63
-rw-r--r--pkg/handler/git/handler.go193
-rw-r--r--pkg/handler/router.go23
-rw-r--r--pkg/handler/static/handler.go4
6 files changed, 284 insertions, 101 deletions
diff --git a/pkg/handler/about/handler.go b/pkg/handler/about/handler.go
index ac3d314..b3a1593 100644
--- a/pkg/handler/about/handler.go
+++ b/pkg/handler/about/handler.go
@@ -9,6 +9,7 @@ import (
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
+ "git.gabrielgio.me/cerrado/pkg/ext"
"git.gabrielgio.me/cerrado/templates"
)
@@ -26,7 +27,7 @@ func NewAboutHandler(configRepo configurationRepository) *AboutHandler {
return &AboutHandler{configRepo.GetRootReadme()}
}
-func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) error {
+func (g *AboutHandler) About(w http.ResponseWriter, r *ext.Request) error {
f, err := os.Open(g.readmePath)
if err != nil {
return err
@@ -50,6 +51,6 @@ func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) error {
gitList := &templates.AboutPage{
Body: bs,
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
diff --git a/pkg/handler/auth/login.go b/pkg/handler/auth/login.go
new file mode 100644
index 0000000..9cc13cc
--- /dev/null
+++ b/pkg/handler/auth/login.go
@@ -0,0 +1,97 @@
+package auth
+
+import (
+ "encoding/base64"
+ "net/http"
+ "time"
+
+ "git.gabrielgio.me/cerrado/pkg/ext"
+ "git.gabrielgio.me/cerrado/templates"
+)
+
+type (
+ LoginHandler struct {
+ auth authService
+ }
+
+ authService interface {
+ CheckAuth(username, password string) bool
+ IssueToken() ([]byte, error)
+ }
+)
+
+func NewLoginHandler(auth authService) *LoginHandler {
+ return &LoginHandler{
+ auth: auth,
+ }
+}
+
+func (g *LoginHandler) Logout(w http.ResponseWriter, r *ext.Request) error {
+ cookie := &http.Cookie{
+ Name: "auth",
+ Value: "",
+ Path: "/",
+ Expires: time.Unix(0, 0),
+ }
+
+ referer := r.Header.Get("Referer")
+ if referer == "" {
+ referer = "/"
+ }
+
+ http.SetCookie(w, cookie)
+ ext.Redirect(w, referer)
+ return nil
+}
+
+func (g *LoginHandler) Login(w http.ResponseWriter, r *ext.Request) error {
+ referer := r.URL.Query().Get("referer")
+
+ // if query value is empty tries to get from header
+ if referer == "" {
+ referer = r.Header.Get("Referer")
+ }
+
+ if r.Method == "GET" {
+ ext.SetHTML(w)
+
+ login := &templates.LoginPage{
+ Referer: referer,
+ }
+ templates.WritePageTemplate(w, login, r.Context())
+ } else if r.Method == "POST" {
+
+ username := r.FormValue("username")
+ password := r.FormValue("password")
+
+ if !g.auth.CheckAuth(username, password) {
+ login := &templates.LoginPage{
+ Referer: referer,
+ ErrorMessage: "Invalid login",
+ }
+ templates.WritePageTemplate(w, login, r.Context())
+ } else {
+
+ bytes, err := g.auth.IssueToken()
+ if err != nil {
+ return err
+ }
+
+ cookie := &http.Cookie{
+ Name: "auth",
+ Value: base64.StdEncoding.EncodeToString(bytes),
+ Path: "/",
+ MaxAge: 3600,
+ HttpOnly: true,
+ Secure: true,
+ SameSite: http.SameSiteStrictMode,
+ }
+
+ http.SetCookie(w, cookie)
+ ext.Redirect(w, referer)
+ }
+
+ }
+
+ return nil
+}
diff --git a/pkg/handler/config/handler.go b/pkg/handler/config/handler.go
deleted file mode 100644
index c43b54d..0000000
--- a/pkg/handler/config/handler.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package config
-
-import (
- "bytes"
- "encoding/json"
- "net/http"
-
- "github.com/alecthomas/chroma/v2/formatters/html"
- "github.com/alecthomas/chroma/v2/lexers"
- "github.com/alecthomas/chroma/v2/styles"
-
- "git.gabrielgio.me/cerrado/pkg/config"
- "git.gabrielgio.me/cerrado/pkg/ext"
- "git.gabrielgio.me/cerrado/templates"
-)
-
-type (
- configurationRepository interface {
- GetRootReadme() string
- List() []*config.GitRepositoryConfiguration
- }
-)
-
-func ConfigFile(configRepo configurationRepository) ext.ErrorRequestHandler {
- return func(w http.ResponseWriter, _ *http.Request) error {
-
- config := struct {
- RootReadme string
- Repositories []*config.GitRepositoryConfiguration
- }{
- RootReadme: configRepo.GetRootReadme(),
- Repositories: configRepo.List(),
- }
-
- b, err := json.MarshalIndent(config, "", " ")
- if err != nil {
- return err
- }
-
- lexer := lexers.Get("json")
- style := styles.Get("monokailight")
- formatter := html.New(
- html.WithLineNumbers(true),
- )
- iterator, err := lexer.Tokenise(nil, string(b))
- if err != nil {
- return err
- }
-
- var code bytes.Buffer
- err = formatter.Format(&code, style, iterator)
- if err != nil {
- return err
- }
-
- hello := &templates.ConfigPage{
- Body: code.Bytes(),
- }
-
- templates.WritePageTemplate(w, hello)
- return nil
- }
-}
diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go
index 40fae24..cb202a2 100644
--- a/pkg/handler/git/handler.go
+++ b/pkg/handler/git/handler.go
@@ -2,6 +2,7 @@ package git
import (
"bytes"
+ "compress/gzip"
"errors"
"fmt"
"io"
@@ -9,10 +10,13 @@ import (
"net/http"
"os"
"path/filepath"
+ "sort"
"strings"
+ "git.gabrielgio.me/cerrado/pkg/config"
"git.gabrielgio.me/cerrado/pkg/ext"
"git.gabrielgio.me/cerrado/pkg/service"
+ "git.gabrielgio.me/cerrado/pkg/u"
"git.gabrielgio.me/cerrado/templates"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters/html"
@@ -27,28 +31,38 @@ import (
type (
GitHandler struct {
gitService *service.GitService
- readmePath string
+ config configurationRepository
}
configurationRepository interface {
GetRootReadme() string
+ GetSyntaxHighlight() string
+ GetOrderBy() config.OrderBy
}
)
func NewGitHandler(gitService *service.GitService, confRepo configurationRepository) *GitHandler {
return &GitHandler{
gitService: gitService,
- readmePath: confRepo.GetRootReadme(),
+ config: confRepo,
}
}
-func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) error {
+func (g *GitHandler) List(w http.ResponseWriter, r *ext.Request) error {
+ // this is the only handler that needs to handle authentication itself.
+ // everything else relay on name path parameter
+ logged := ext.IsLoggedIn(r.Context())
+
repos, err := g.gitService.ListRepositories()
if err != nil {
return err
}
- f, err := os.Open(g.readmePath)
+ if !logged {
+ repos = u.Filter(repos, isPublic)
+ }
+
+ f, err := os.Open(g.config.GetRootReadme())
if err != nil {
return err
}
@@ -69,14 +83,14 @@ func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) error {
bs = markdown.Render(doc, renderer)
gitList := &templates.GitListPage{
- Respositories: repos,
+ Respositories: orderBy(repos, g.config.GetOrderBy()),
About: bs,
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
-func (g *GitHandler) Archive(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) Archive(w http.ResponseWriter, r *ext.Request) error {
ext.SetGZip(w)
name := r.PathValue("name")
file := r.PathValue("file")
@@ -84,7 +98,7 @@ func (g *GitHandler) Archive(w http.ResponseWriter, r *http.Request) error {
// TODO: remove it once we can support more than gzip
if !strings.HasSuffix(file, ".tar.gz") {
- ext.NotFound(w)
+ ext.NotFound(w, r)
return nil
}
@@ -102,7 +116,51 @@ func (g *GitHandler) Archive(w http.ResponseWriter, r *http.Request) error {
return nil
}
-func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) Multiplex(w http.ResponseWriter, r *ext.Request) error {
+ path := r.PathValue("rest")
+ name := r.PathValue("name")
+
+ if r.URL.RawQuery == "service=git-receive-pack" {
+ ext.BadRequest(w, r, "no pushing allowed")
+ return nil
+ }
+
+ if path == "info/refs" && r.URL.RawQuery == "service=git-upload-pack" && r.Method == "GET" {
+ w.Header().Set("content-type", "application/x-git-upload-pack-advertisement")
+
+ err := g.gitService.WriteInfoRefs(r.Context(), name, w)
+ if err != nil {
+ slog.Error("Error WriteInfoRefs", "error", err)
+ }
+ } else if path == "git-upload-pack" && r.Method == "POST" {
+ w.Header().Set("content-type", "application/x-git-upload-pack-result")
+ w.Header().Set("Connection", "Keep-Alive")
+ w.Header().Set("Transfer-Encoding", "chunked")
+ w.WriteHeader(http.StatusOK)
+
+ reader := r.Body
+
+ if r.Header.Get("Content-Encoding") == "gzip" {
+ var err error
+ reader, err = gzip.NewReader(r.Body)
+ if err != nil {
+ return err
+ }
+ defer reader.Close()
+ }
+
+ err := g.gitService.WriteUploadPack(r.Context(), name, reader, w)
+ if err != nil {
+ slog.Error("Error WriteUploadPack", "error", err)
+ }
+ } else if r.Method == "GET" {
+ return g.Summary(w, r)
+ }
+
+ return nil
+}
+
+func (g *GitHandler) Summary(w http.ResponseWriter, r *ext.Request) error {
ext.SetHTML(w)
name := r.PathValue("name")
ref, err := g.gitService.GetHead(name)
@@ -120,11 +178,15 @@ func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) error {
return err
}
- commits, err := g.gitService.ListCommits(name, "", 10)
+ commits, _, err := g.gitService.ListCommits(name, "", "", 10)
if err != nil {
return err
}
+ if len(tags) > 3 {
+ tags = tags[:3]
+ }
+
gitList := &templates.GitItemPage{
Name: name,
Ref: ref.Name().Short(),
@@ -134,11 +196,11 @@ func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) error {
Commits: commits,
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
-func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) About(w http.ResponseWriter, r *ext.Request) error {
ext.SetHTML(w)
name := r.PathValue("name")
ref, err := g.gitService.GetHead(name)
@@ -154,7 +216,7 @@ func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) error {
GitItemBase: &templates.GitItemAboutPage{
About: []byte("About file not configured properly"),
},
- })
+ }, r.Context())
return nil
}
if err != nil {
@@ -178,11 +240,11 @@ func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) error {
About: bs,
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
-func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) Refs(w http.ResponseWriter, r *ext.Request) error {
ext.SetHTML(w)
name := r.PathValue("name")
@@ -209,11 +271,11 @@ func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) error {
Branches: branches,
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
-func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) Tree(w http.ResponseWriter, r *ext.Request) error {
ext.SetHTML(w)
name := r.PathValue("name")
ref := r.PathValue("ref")
@@ -238,11 +300,11 @@ func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) error {
Tree: tree,
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
-func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) Blob(w http.ResponseWriter, r *ext.Request) error {
ext.SetHTML(w)
name := r.PathValue("name")
ref := r.PathValue("ref")
@@ -269,7 +331,7 @@ func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) error {
Content: []byte("Binary file"),
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
@@ -280,7 +342,8 @@ func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) error {
filename := filepath.Base(rest)
lexer := GetLexers(filename)
- style := styles.Get("xcode")
+ style := styles.Get(g.config.GetSyntaxHighlight())
+
formatter := html.New(
html.WithLineNumbers(true),
html.WithLinkableLineNumbers(true, "L"),
@@ -305,16 +368,17 @@ func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) error {
Content: code.Bytes(),
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
-func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) Log(w http.ResponseWriter, r *ext.Request) error {
ext.SetHTML(w)
name := r.PathValue("name")
ref := r.PathValue("ref")
+ from := r.URL.Query().Get("from")
- commits, err := g.gitService.ListCommits(name, ref, 1000)
+ commits, next, err := g.gitService.ListCommits(name, ref, from, 100)
if err != nil {
return err
}
@@ -324,13 +388,36 @@ func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) error {
Ref: ref,
GitItemBase: &templates.GitItemLogPage{
Commits: commits,
+ Next: next,
+ },
+ }
+ templates.WritePageTemplate(w, gitList, r.Context())
+ return nil
+}
+
+func (g *GitHandler) Ref(w http.ResponseWriter, r *ext.Request) error {
+ ext.SetHTML(w)
+ name := r.PathValue("name")
+ ref := r.PathValue("ref")
+
+ commit, tag, err := g.gitService.GetTag(ref, name)
+ if err != nil {
+ return err
+ }
+
+ gitList := &templates.GitItemPage{
+ Name: name,
+ Ref: ref,
+ GitItemBase: &templates.GitItemRefPage{
+ Commit: commit,
+ Reference: tag,
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
-func (g *GitHandler) Commit(w http.ResponseWriter, r *http.Request) error {
+func (g *GitHandler) Commit(w http.ResponseWriter, r *ext.Request) error {
ext.SetHTML(w)
name := r.PathValue("name")
ref := r.PathValue("ref")
@@ -345,15 +432,34 @@ func (g *GitHandler) Commit(w http.ResponseWriter, r *http.Request) error {
return err
}
+ lexer := lexers.Get("diff")
+ style := styles.Get(g.config.GetSyntaxHighlight())
+
+ formatter := html.New(
+ html.WithLineNumbers(true),
+ html.WithLinkableLineNumbers(true, "L"),
+ )
+
+ iterator, err := lexer.Tokenise(nil, diff)
+ if err != nil {
+ return err
+ }
+
+ var code bytes.Buffer
+ err = formatter.Format(&code, style, iterator)
+ if err != nil {
+ return err
+ }
+
gitList := &templates.GitItemPage{
Name: name,
Ref: ref,
GitItemBase: &templates.GitItemCommitPage{
Commit: commit,
- Diff: diff,
+ Diff: code.Bytes(),
},
}
- templates.WritePageTemplate(w, gitList)
+ templates.WritePageTemplate(w, gitList, r.Context())
return nil
}
@@ -362,6 +468,10 @@ func GetLexers(filename string) chroma.Lexer {
return lexers.Get("sh")
}
+ if strings.HasSuffix(filename, ".qtpl") {
+ return lexers.Get("html")
+ }
+
lexer := lexers.Get(filename)
if lexer == nil {
@@ -369,3 +479,30 @@ func GetLexers(filename string) chroma.Lexer {
}
return lexer
}
+
+func isPublic(r *service.Repository) bool {
+ return r.Public
+}
+
+func orderBy(repos []*service.Repository, order config.OrderBy) []*service.Repository {
+ switch order {
+ case config.AlphabeticalAsc:
+ sort.Slice(repos, func(i, j int) bool {
+ return repos[i].Name < repos[j].Name
+ })
+ case config.AlphabeticalDesc:
+ sort.Slice(repos, func(i, j int) bool {
+ return repos[i].Name > repos[j].Name
+ })
+ case config.LastCommitAsc:
+ sort.Slice(repos, func(i, j int) bool {
+ return repos[i].LastCommit.Commit().Committer.When.Before(repos[j].LastCommit.Commit().Committer.When)
+ })
+ case config.LastCommitDesc:
+ sort.Slice(repos, func(i, j int) bool {
+ return repos[i].LastCommit.Commit().Committer.When.After(repos[j].LastCommit.Commit().Committer.When)
+ })
+ }
+
+ return repos
+}
diff --git a/pkg/handler/router.go b/pkg/handler/router.go
index f464ac2..fea8827 100644
--- a/pkg/handler/router.go
+++ b/pkg/handler/router.go
@@ -6,7 +6,7 @@ import (
serverconfig "git.gabrielgio.me/cerrado/pkg/config"
"git.gabrielgio.me/cerrado/pkg/ext"
"git.gabrielgio.me/cerrado/pkg/handler/about"
- "git.gabrielgio.me/cerrado/pkg/handler/config"
+ "git.gabrielgio.me/cerrado/pkg/handler/auth"
"git.gabrielgio.me/cerrado/pkg/handler/git"
"git.gabrielgio.me/cerrado/pkg/handler/static"
"git.gabrielgio.me/cerrado/pkg/service"
@@ -17,12 +17,13 @@ import (
// its sub package don't leak in other places.
func MountHandler(
gitService *service.GitService,
+ authService *service.AuthService,
configRepo *serverconfig.ConfigurationRepository,
) (http.Handler, error) {
var (
- gitHandler = git.NewGitHandler(gitService, configRepo)
- aboutHandler = about.NewAboutHandler(configRepo)
- configHandler = config.ConfigFile(configRepo)
+ gitHandler = git.NewGitHandler(gitService, configRepo)
+ aboutHandler = about.NewAboutHandler(configRepo)
+ loginHandler = auth.NewLoginHandler(authService)
)
staticHandler, err := static.ServeStaticHandler()
@@ -33,17 +34,27 @@ func MountHandler(
mux := ext.NewRouter()
mux.AddMiddleware(ext.Compress)
mux.AddMiddleware(ext.Log)
+ mux.AddMiddleware(ext.VerifyRespository(configRepo))
+
+ if configRepo.IsAuthEnabled() {
+ mux.AddMiddleware(ext.Authenticate(authService))
+ mux.HandleFunc("/login/{$}", loginHandler.Login)
+ mux.HandleFunc("/logout/{$}", loginHandler.Logout)
+ } else {
+ mux.AddMiddleware(ext.DisableAuthentication)
+ }
mux.HandleFunc("/static/{file}", staticHandler)
mux.HandleFunc("/{name}/about/{$}", gitHandler.About)
- mux.HandleFunc("/{name}/", gitHandler.Summary)
+ mux.HandleFunc("/{name}", gitHandler.Multiplex)
+ mux.HandleFunc("/{name}/{rest...}", gitHandler.Multiplex)
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("/{name}/commit/{ref}/", gitHandler.Commit)
+ mux.HandleFunc("/{name}/ref/{ref}/", gitHandler.Ref)
mux.HandleFunc("/{name}/archive/{file}", gitHandler.Archive)
- 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 361f690..cdb2ae6 100644
--- a/pkg/handler/static/handler.go
+++ b/pkg/handler/static/handler.go
@@ -16,7 +16,7 @@ func ServeStaticHandler() (ext.ErrorRequestHandler, error) {
return nil, err
}
- return func(w http.ResponseWriter, r *http.Request) error {
+ return func(w http.ResponseWriter, r *ext.Request) error {
var (
f = r.PathValue("file")
e = filepath.Ext(f)
@@ -24,7 +24,7 @@ func ServeStaticHandler() (ext.ErrorRequestHandler, error) {
)
ext.SetMIME(w, m)
w.Header().Add("Cache-Control", "max-age=31536000")
- http.ServeFileFS(w, r, staticFs, f)
+ http.ServeFileFS(w, r.Request, staticFs, f)
return nil
}, nil
}