Goit

Simple and lightweight Git web server
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

Goit/src/repo/refs.go (142 lines, 3.6 KiB) -rw-r--r-- blame download

0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
// Copyright (C) 2023, Jakob Wakeling
// All rights reserved.

package repo

import (
	"errors"
	"log"
	"net/http"
	"path/filepath"
	"slices"
	"strings"
	"time"

	"github.com/Jamozed/Goit/src/goit"
	"github.com/go-chi/chi/v5"
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
)

func HandleRefs(w http.ResponseWriter, r *http.Request) {
	auth, user, err := goit.Auth(w, r, true)
	if err != nil {
		log.Println("[admin]", err.Error())
		goit.HttpError(w, http.StatusInternalServerError)
	}

	repo, err := goit.GetRepoByName(chi.URLParam(r, "repo"))
	if err != nil {
		goit.HttpError(w, http.StatusInternalServerError)
		return
	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
		goit.HttpError(w, http.StatusNotFound)
		return
	}

	type row struct {
		Name, Hash, Message, Author, LastCommit string
		Commits                                 uint64
	}
	data := struct {
		HeaderFields
		Title          string
		Branches, Tags []row
	}{
		Title:        repo.Name + " - References",
		HeaderFields: GetHeaderFields(auth, user, repo, r.Host),
	}

	gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true))
	if err != nil {
		log.Println("[/repo/refs]", err.Error())
		goit.HttpError(w, http.StatusInternalServerError)
		return
	}

	ref, err := gr.Head()
	if err != nil {
		if !errors.Is(err, plumbing.ErrReferenceNotFound) {
			log.Println("[/repo/log]", err.Error())
			goit.HttpError(w, http.StatusInternalServerError)
			return
		}
	} else {
		if readme, _ := findPattern(gr, ref, readmePattern); readme != "" {
			data.Readme = filepath.Join("/", repo.Name, "file", readme)
		}
		if licence, _ := findPattern(gr, ref, licencePattern); licence != "" {
			data.Licence = filepath.Join("/", repo.Name, "file", licence)
		}
	}

	if iter, err := gr.Branches(); err != nil {
		log.Println("[/repo/refs]", err.Error())
		goit.HttpError(w, http.StatusInternalServerError)
		return
	} else if err := iter.ForEach(func(r *plumbing.Reference) error {
		commit, err := gr.CommitObject(r.Hash())
		if err != nil {
			return err
		}

		commits, err := goit.CommitCount(repo.Name, r.Name().Short(), r.Hash())
		if err != nil {
			return err
		}

		data.Branches = append(data.Branches, row{
			Name: r.Name().Short(), Hash: r.Hash().String(), Message: strings.SplitN(commit.Message, "\n", 2)[0],
			Author: commit.Author.Name, LastCommit: commit.Author.When.UTC().Format(time.DateTime), Commits: commits,
		})

		return nil
	}); err != nil {
		log.Println("[/repo/refs]", err.Error())
		goit.HttpError(w, http.StatusInternalServerError)
		return
	}

	if iter, err := gr.Tags(); err != nil {
		log.Println("[/repo/refs]", err.Error())
		goit.HttpError(w, http.StatusInternalServerError)
		return
	} else if err := iter.ForEach(func(r *plumbing.Reference) error {
		var c *object.Commit

		if tag, err := gr.TagObject(r.Hash()); err != nil {
			if !errors.Is(err, plumbing.ErrObjectNotFound) {
				return err
			}
		} else {
			if c, err = gr.CommitObject(tag.Target); err != nil {
				return err
			}
		}

		if c == nil {
			if c, err = gr.CommitObject(r.Hash()); err != nil {
				return err
			}
		}

		data.Tags = append(data.Tags, row{
			Name: r.Name().Short(), Message: strings.SplitN(c.Message, "\n", 2)[0], Author: c.Author.Name,
			LastCommit: c.Author.When.UTC().Format(time.DateTime), Hash: r.Hash().String(),
		})

		return nil
	}); err != nil {
		log.Println("[/repo/refs]", err.Error())
		goit.HttpError(w, http.StatusInternalServerError)
		return
	}

	slices.Reverse(data.Tags)

	if err := goit.Tmpl.ExecuteTemplate(w, "repo/refs", data); err != nil {
		log.Println("[/repo/refs]", err.Error())
	}
}