Skip to content

Commit

Permalink
Set gist URL and title via push options (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomiceli committed Apr 2, 2024
1 parent db6d6a5 commit 86ad88f
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 78 deletions.
13 changes: 13 additions & 0 deletions docs/usage/git-push-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ Opengist has support for a few [Git push options](https://git-scm.com/docs/git-p

These options are passed to `git push` command and can be used to change the metadata of a gist.

## Set URL

```shell
git push -o url=mygist # Will set the URL to https://opengist.example.com/user/mygist
```

## Change title

```shell
git push -o title=Gist123
git push -o title="My Gist 123"
```

## Change visibility

```shell
Expand Down
38 changes: 34 additions & 4 deletions internal/hooks/post_receive.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
"github.com/thomiceli/opengist/internal/utils"
"io"
"os"
"os/exec"
Expand All @@ -13,7 +14,12 @@ import (
)

func PostReceive(in io.Reader, out, er io.Writer) error {
var outputSb strings.Builder
newGist := false
opts := pushOptions()
gistUrl := os.Getenv("OPENGIST_REPOSITORY_URL_INTERNAL")
validator := utils.NewValidator()

scanner := bufio.NewScanner(in)
for scanner.Scan() {
line := scanner.Text()
Expand All @@ -29,9 +35,7 @@ func PostReceive(in io.Reader, out, er io.Writer) error {
}

if oldrev == BaseHash {
_, _ = fmt.Fprintf(out, "\nYour new repository has been created here: %s\n\n", os.Getenv("OPENGIST_REPOSITORY_URL_INTERNAL"))
_, _ = fmt.Fprintln(out, "If you want to keep working with your gist, you could set the remote URL via:")
_, _ = fmt.Fprintf(out, "git remote set-url origin %s\n\n", os.Getenv("OPENGIST_REPOSITORY_URL_INTERNAL"))
newGist = true
}
}

Expand All @@ -43,7 +47,22 @@ func PostReceive(in io.Reader, out, er io.Writer) error {

if slices.Contains([]string{"public", "unlisted", "private"}, opts["visibility"]) {
gist.Private, _ = db.ParseVisibility(opts["visibility"])
_, _ = fmt.Fprintf(out, "\nGist visibility set to %s\n\n", opts["visibility"])
outputSb.WriteString(fmt.Sprintf("Gist visibility set to %s\n\n", opts["visibility"]))
}

if opts["url"] != "" && validator.Var(opts["url"], "max=32,alphanumdashorempty") == nil {
gist.URL = opts["url"]
lastIndex := strings.LastIndex(gistUrl, "/")
gistUrl = gistUrl[:lastIndex+1] + gist.URL
if !newGist {
outputSb.WriteString(fmt.Sprintf("Gist URL set to %s. Set the Git remote URL via:\n", gistUrl))
outputSb.WriteString(fmt.Sprintf("git remote set-url origin %s\n\n", gistUrl))
}
}

if opts["title"] != "" && validator.Var(opts["title"], "max=250") == nil {
gist.Title = opts["title"]
outputSb.WriteString(fmt.Sprintf("Gist title set to \"%s\"\n\n", opts["title"]))
}

if hasNoCommits, err := git.HasNoCommits(gist.User.Username, gist.Uuid); err != nil {
Expand All @@ -65,6 +84,17 @@ func PostReceive(in io.Reader, out, er io.Writer) error {

gist.AddInIndex()

if newGist {
outputSb.WriteString(fmt.Sprintf("Your new gist has been created here: %s\n", gistUrl))
outputSb.WriteString("If you want to keep working with your gist, you could set the Git remote URL via:\n")
outputSb.WriteString(fmt.Sprintf("git remote set-url origin %s\n\n", gistUrl))
}

outputStr := outputSb.String()
if outputStr != "" {
_, _ = fmt.Fprint(out, "\n"+outputStr)
}

return nil
}

Expand Down
74 changes: 74 additions & 0 deletions internal/utils/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package utils

import (
"github.com/go-playground/validator/v10"
"regexp"
"strings"
)

type OpengistValidator struct {
v *validator.Validate
}

func NewValidator() *OpengistValidator {
v := validator.New()
_ = v.RegisterValidation("notreserved", validateReservedKeywords)
_ = v.RegisterValidation("alphanumdash", validateAlphaNumDash)
_ = v.RegisterValidation("alphanumdashorempty", validateAlphaNumDashOrEmpty)
return &OpengistValidator{v}
}

func (cv *OpengistValidator) Validate(i interface{}) error {
return cv.v.Struct(i)
}

func (cv *OpengistValidator) Var(field interface{}, tag string) error {
return cv.v.Var(field, tag)
}

func ValidationMessages(err *error) string {
errs := (*err).(validator.ValidationErrors)
messages := make([]string, len(errs))
for i, e := range errs {
switch e.Tag() {
case "max":
messages[i] = e.Field() + " is too long"
case "required":
messages[i] = e.Field() + " should not be empty"
case "excludes":
messages[i] = e.Field() + " should not include a sub directory"
case "alphanum":
messages[i] = e.Field() + " should only contain alphanumeric characters"
case "alphanumdash":
case "alphanumdashorempty":
messages[i] = e.Field() + " should only contain alphanumeric characters and dashes"
case "min":
messages[i] = "Not enough " + e.Field()
case "notreserved":
messages[i] = "Invalid " + e.Field()
}
}

return strings.Join(messages, " ; ")
}

func validateReservedKeywords(fl validator.FieldLevel) bool {
name := fl.Field().String()

restrictedNames := map[string]struct{}{}
for _, restrictedName := range []string{"assets", "register", "login", "logout", "settings", "admin-panel", "all", "search", "init", "healthcheck"} {
restrictedNames[restrictedName] = struct{}{}
}

// if the name is not in the restricted names, it is valid
_, ok := restrictedNames[name]
return !ok
}

func validateAlphaNumDash(fl validator.FieldLevel) bool {
return regexp.MustCompile(`^[a-zA-Z0-9-]+$`).MatchString(fl.Field().String())
}

func validateAlphaNumDashOrEmpty(fl validator.FieldLevel) bool {
return regexp.MustCompile(`^$|^[a-zA-Z0-9-]+$`).MatchString(fl.Field().String())
}
3 changes: 2 additions & 1 deletion internal/web/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/thomiceli/opengist/internal/utils"
"io"
"net/http"
"net/url"
Expand Down Expand Up @@ -63,7 +64,7 @@ func processRegister(ctx echo.Context) error {
}

if err := ctx.Validate(dto); err != nil {
addFlash(ctx, validationMessages(&err), "error")
addFlash(ctx, utils.ValidationMessages(&err), "error")
return html(ctx, "auth_form.html")
}

Expand Down
3 changes: 2 additions & 1 deletion internal/web/gist.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/thomiceli/opengist/internal/git"
"github.com/thomiceli/opengist/internal/index"
"github.com/thomiceli/opengist/internal/render"
"github.com/thomiceli/opengist/internal/utils"
"html/template"
"net/url"
"path/filepath"
Expand Down Expand Up @@ -539,7 +540,7 @@ func processCreate(ctx echo.Context) error {

err = ctx.Validate(dto)
if err != nil {
addFlash(ctx, validationMessages(&err), "error")
addFlash(ctx, utils.ValidationMessages(&err), "error")
if isCreate {
return html(ctx, "create.html")
} else {
Expand Down
3 changes: 2 additions & 1 deletion internal/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"github.com/thomiceli/opengist/internal/index"
"github.com/thomiceli/opengist/internal/utils"
htmlpkg "html"
"html/template"
"io"
Expand Down Expand Up @@ -205,7 +206,7 @@ func NewServer(isDev bool) *Server {

e.Use(sessionInit)

e.Validator = NewValidator()
e.Validator = utils.NewValidator()

if !dev {
parseManifestEntries()
Expand Down
7 changes: 4 additions & 3 deletions internal/web/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"github.com/thomiceli/opengist/internal/config"
"github.com/thomiceli/opengist/internal/git"
"github.com/thomiceli/opengist/internal/utils"
"os"
"path/filepath"
"strconv"
Expand Down Expand Up @@ -73,7 +74,7 @@ func sshKeysProcess(ctx echo.Context) error {
}

if err := ctx.Validate(dto); err != nil {
addFlash(ctx, validationMessages(&err), "error")
addFlash(ctx, utils.ValidationMessages(&err), "error")
return redirect(ctx, "/settings")
}
key := dto.ToSSHKey()
Expand Down Expand Up @@ -126,7 +127,7 @@ func passwordProcess(ctx echo.Context) error {
dto.Username = user.Username

if err := ctx.Validate(dto); err != nil {
addFlash(ctx, validationMessages(&err), "error")
addFlash(ctx, utils.ValidationMessages(&err), "error")
return html(ctx, "settings.html")
}

Expand Down Expand Up @@ -154,7 +155,7 @@ func usernameProcess(ctx echo.Context) error {
dto.Password = user.Password

if err := ctx.Validate(dto); err != nil {
addFlash(ctx, validationMessages(&err), "error")
addFlash(ctx, utils.ValidationMessages(&err), "error")
return redirect(ctx, "/settings")
}

Expand Down
68 changes: 0 additions & 68 deletions internal/web/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/go-playground/validator/v10"
"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
"github.com/thomiceli/opengist/internal/config"
Expand All @@ -16,7 +15,6 @@ import (
"golang.org/x/crypto/argon2"
"html/template"
"net/http"
"regexp"
"strconv"
"strings"
)
Expand Down Expand Up @@ -129,72 +127,6 @@ func loadSettings(ctx echo.Context) error {
return nil
}

type OpengistValidator struct {
v *validator.Validate
}

func NewValidator() *OpengistValidator {
v := validator.New()
_ = v.RegisterValidation("notreserved", validateReservedKeywords)
_ = v.RegisterValidation("alphanumdash", validateAlphaNumDash)
_ = v.RegisterValidation("alphanumdashorempty", validateAlphaNumDashOrEmpty)
return &OpengistValidator{v}
}

func (cv *OpengistValidator) Validate(i interface{}) error {
if err := cv.v.Struct(i); err != nil {
return err
}
return nil
}

func validationMessages(err *error) string {
errs := (*err).(validator.ValidationErrors)
messages := make([]string, len(errs))
for i, e := range errs {
switch e.Tag() {
case "max":
messages[i] = e.Field() + " is too long"
case "required":
messages[i] = e.Field() + " should not be empty"
case "excludes":
messages[i] = e.Field() + " should not include a sub directory"
case "alphanum":
messages[i] = e.Field() + " should only contain alphanumeric characters"
case "alphanumdash":
case "alphanumdashorempty":
messages[i] = e.Field() + " should only contain alphanumeric characters and dashes"
case "min":
messages[i] = "Not enough " + e.Field()
case "notreserved":
messages[i] = "Invalid " + e.Field()
}
}

return strings.Join(messages, " ; ")
}

func validateReservedKeywords(fl validator.FieldLevel) bool {
name := fl.Field().String()

restrictedNames := map[string]struct{}{}
for _, restrictedName := range []string{"assets", "register", "login", "logout", "settings", "admin-panel", "all", "search", "init", "healthcheck"} {
restrictedNames[restrictedName] = struct{}{}
}

// if the name is not in the restricted names, it is valid
_, ok := restrictedNames[name]
return !ok
}

func validateAlphaNumDash(fl validator.FieldLevel) bool {
return regexp.MustCompile(`^[a-zA-Z0-9-]+$`).MatchString(fl.Field().String())
}

func validateAlphaNumDashOrEmpty(fl validator.FieldLevel) bool {
return regexp.MustCompile(`^$|^[a-zA-Z0-9-]+$`).MatchString(fl.Field().String())
}

func getPage(ctx echo.Context) int {
page := ctx.QueryParam("page")
if page == "" {
Expand Down

0 comments on commit 86ad88f

Please sign in to comment.