diff --git a/internal/db/gist.go b/internal/db/gist.go
index d54007f..d11fe5e 100644
--- a/internal/db/gist.go
+++ b/internal/db/gist.go
@@ -1,14 +1,48 @@
package db
import (
- "github.com/labstack/echo/v4"
- "github.com/thomiceli/opengist/internal/git"
- "gorm.io/gorm"
+ "fmt"
"os/exec"
"strings"
"time"
+
+ "github.com/labstack/echo/v4"
+ "github.com/thomiceli/opengist/internal/git"
+ "gorm.io/gorm"
)
+type Visibility int
+
+const (
+ PublicVisibility Visibility = iota
+ UnlistedVisibility
+ PrivateVisibility
+)
+
+func (v Visibility) Next() Visibility {
+ switch v {
+ case PublicVisibility:
+ return UnlistedVisibility
+ case UnlistedVisibility:
+ return PrivateVisibility
+ default:
+ return PublicVisibility
+ }
+}
+
+func ParseVisibility[T string | int](v T) (Visibility, error) {
+ switch s := fmt.Sprint(v); s {
+ case "0":
+ return PublicVisibility, nil
+ case "1":
+ return UnlistedVisibility, nil
+ case "2":
+ return PrivateVisibility, nil
+ default:
+ return -1, fmt.Errorf("unknown visibility %q", s)
+ }
+}
+
type Gist struct {
ID uint `gorm:"primaryKey"`
Uuid string
@@ -16,7 +50,7 @@ type Gist struct {
Preview string
PreviewFilename string
Description string
- Private int // 0: public, 1: unlisted, 2: private
+ Private Visibility // 0: public, 1: unlisted, 2: private
UserID uint
User User
NbFiles int
@@ -386,12 +420,12 @@ func (gist *Gist) UpdatePreviewAndCount() error {
// -- DTO -- //
type GistDTO struct {
- Title string `validate:"max=250" form:"title"`
- Description string `validate:"max=1000" form:"description"`
- Private int `validate:"number,min=0,max=2" form:"private"`
- Files []FileDTO `validate:"min=1,dive"`
- Name []string `form:"name"`
- Content []string `form:"content"`
+ Title string `validate:"max=250" form:"title"`
+ Description string `validate:"max=1000" form:"description"`
+ Private Visibility `validate:"number,min=0,max=2" form:"private"`
+ Files []FileDTO `validate:"min=1,dive"`
+ Name []string `form:"name"`
+ Content []string `form:"content"`
}
type FileDTO struct {
diff --git a/internal/db/user.go b/internal/db/user.go
index d5ec34a..341b240 100644
--- a/internal/db/user.go
+++ b/internal/db/user.go
@@ -100,7 +100,6 @@ func GetUsersFromEmails(emailsSet map[string]struct{}) (map[string]*User, error)
err := db.
Where("email IN ?", emails).
Find(&users).Error
-
if err != nil {
return nil, err
}
diff --git a/internal/web/gist.go b/internal/web/gist.go
index 0f245b0..2a0367c 100644
--- a/internal/web/gist.go
+++ b/internal/web/gist.go
@@ -4,16 +4,17 @@ import (
"archive/zip"
"bytes"
"errors"
- "github.com/google/uuid"
- "github.com/labstack/echo/v4"
- "github.com/thomiceli/opengist/internal/config"
- "github.com/thomiceli/opengist/internal/db"
- "gorm.io/gorm"
"html/template"
"net/url"
"regexp"
"strconv"
"strings"
+
+ "github.com/google/uuid"
+ "github.com/labstack/echo/v4"
+ "github.com/thomiceli/opengist/internal/config"
+ "github.com/thomiceli/opengist/internal/db"
+ "gorm.io/gorm"
)
func gistInit(next echo.HandlerFunc) echo.HandlerFunc {
@@ -30,7 +31,7 @@ func gistInit(next echo.HandlerFunc) echo.HandlerFunc {
return notFound("Gist not found")
}
- if gist.Private == 2 {
+ if gist.Private == db.PrivateVisibility {
if currUser == nil || currUser.ID != gist.UserID {
return notFound("Gist not found")
}
@@ -433,7 +434,7 @@ func processCreate(ctx echo.Context) error {
}
func toggleVisibility(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
+ gist := getData(ctx, "gist").(*db.Gist)
gist.Private = (gist.Private + 1) % 3
if err := gist.Update(); err != nil {
@@ -445,7 +446,7 @@ func toggleVisibility(ctx echo.Context) error {
}
func deleteGist(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
+ gist := getData(ctx, "gist").(*db.Gist)
if err := gist.Delete(); err != nil {
return errorRes(500, "Error deleting this gist", err)
@@ -456,7 +457,7 @@ func deleteGist(ctx echo.Context) error {
}
func like(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
+ gist := getData(ctx, "gist").(*db.Gist)
currentUser := getUserLogged(ctx)
hasLiked, err := currentUser.HasLiked(gist)
@@ -482,7 +483,7 @@ func like(ctx echo.Context) error {
}
func fork(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
+ gist := getData(ctx, "gist").(*db.Gist)
currentUser := getUserLogged(ctx)
alreadyForked, err := gist.GetForkParent(currentUser)
@@ -535,7 +536,6 @@ func fork(ctx echo.Context) error {
func rawFile(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist)
file, err := gist.File(ctx.Param("revision"), ctx.Param("file"), false)
-
if err != nil {
return errorRes(500, "Error getting file content", err)
}
@@ -550,7 +550,6 @@ func rawFile(ctx echo.Context) error {
func downloadFile(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist)
file, err := gist.File(ctx.Param("revision"), ctx.Param("file"), false)
-
if err != nil {
return errorRes(500, "Error getting file content", err)
}
@@ -572,7 +571,7 @@ func downloadFile(ctx echo.Context) error {
}
func edit(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
+ gist := getData(ctx, "gist").(*db.Gist)
files, err := gist.Files("HEAD")
if err != nil {
@@ -586,8 +585,8 @@ func edit(ctx echo.Context) error {
}
func downloadZip(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
- var revision = ctx.Param("revision")
+ gist := getData(ctx, "gist").(*db.Gist)
+ revision := ctx.Param("revision")
files, err := gist.Files(revision)
if err != nil {
@@ -631,7 +630,7 @@ func downloadZip(ctx echo.Context) error {
}
func likes(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
+ gist := getData(ctx, "gist").(*db.Gist)
pageInt := getPage(ctx)
@@ -650,7 +649,7 @@ func likes(ctx echo.Context) error {
}
func forks(ctx echo.Context) error {
- var gist = getData(ctx, "gist").(*db.Gist)
+ gist := getData(ctx, "gist").(*db.Gist)
pageInt := getPage(ctx)
currentUser := getUserLogged(ctx)
diff --git a/internal/web/git_http.go b/internal/web/git_http.go
index a514d8d..4d69dca 100644
--- a/internal/web/git_http.go
+++ b/internal/web/git_http.go
@@ -6,13 +6,6 @@ import (
"encoding/base64"
"errors"
"fmt"
- "github.com/google/uuid"
- "github.com/labstack/echo/v4"
- "github.com/rs/zerolog/log"
- "github.com/thomiceli/opengist/internal/db"
- "github.com/thomiceli/opengist/internal/git"
- "github.com/thomiceli/opengist/internal/memdb"
- "gorm.io/gorm"
"net/http"
"os"
"os/exec"
@@ -21,6 +14,14 @@ import (
"strconv"
"strings"
"time"
+
+ "github.com/google/uuid"
+ "github.com/labstack/echo/v4"
+ "github.com/rs/zerolog/log"
+ "github.com/thomiceli/opengist/internal/db"
+ "github.com/thomiceli/opengist/internal/git"
+ "github.com/thomiceli/opengist/internal/memdb"
+ "gorm.io/gorm"
)
var routes = []struct {
@@ -73,7 +74,7 @@ func gitHttp(ctx echo.Context) error {
// - user wants to clone/pull a private gist
// - gist is not found (obfuscation)
// - admin setting to require login is set to true
- if isPull && gist.Private != 2 && gist.ID != 0 && !getData(ctx, "RequireLogin").(bool) {
+ if isPull && gist.Private != db.PrivateVisibility && gist.ID != 0 && !getData(ctx, "RequireLogin").(bool) {
return route.handler(ctx)
}
diff --git a/internal/web/server.go b/internal/web/server.go
index 48f867b..c5989c6 100644
--- a/internal/web/server.go
+++ b/internal/web/server.go
@@ -4,6 +4,16 @@ import (
"context"
"encoding/json"
"fmt"
+ htmlpkg "html"
+ "html/template"
+ "io"
+ "net/http"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
@@ -16,106 +26,99 @@ import (
"github.com/thomiceli/opengist/public"
"github.com/thomiceli/opengist/templates"
"golang.org/x/text/language"
- htmlpkg "html"
- "html/template"
- "io"
- "net/http"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "time"
)
-var dev bool
-var store *sessions.CookieStore
-var re = regexp.MustCompile("[^a-z0-9]+")
-var fm = template.FuncMap{
- "split": strings.Split,
- "indexByte": strings.IndexByte,
- "toInt": func(i string) int {
- val, _ := strconv.Atoi(i)
- return val
- },
- "inc": func(i int) int {
- return i + 1
- },
- "splitGit": func(i string) []string {
- return strings.FieldsFunc(i, func(r rune) bool {
- return r == ',' || r == ' '
- })
- },
- "lines": func(i string) []string {
- return strings.Split(i, "\n")
- },
- "isMarkdown": func(i string) bool {
- return strings.ToLower(filepath.Ext(i)) == ".md"
- },
- "isCsv": func(i string) bool {
- return strings.ToLower(filepath.Ext(i)) == ".csv"
- },
- "csvFile": func(file *git.File) *git.CsvFile {
- if strings.ToLower(filepath.Ext(file.Filename)) != ".csv" {
- return nil
- }
+var (
+ dev bool
+ store *sessions.CookieStore
+ re = regexp.MustCompile("[^a-z0-9]+")
+ fm = template.FuncMap{
+ "split": strings.Split,
+ "indexByte": strings.IndexByte,
+ "toInt": func(i string) int {
+ val, _ := strconv.Atoi(i)
+ return val
+ },
+ "inc": func(i int) int {
+ return i + 1
+ },
+ "splitGit": func(i string) []string {
+ return strings.FieldsFunc(i, func(r rune) bool {
+ return r == ',' || r == ' '
+ })
+ },
+ "lines": func(i string) []string {
+ return strings.Split(i, "\n")
+ },
+ "isMarkdown": func(i string) bool {
+ return strings.ToLower(filepath.Ext(i)) == ".md"
+ },
+ "isCsv": func(i string) bool {
+ return strings.ToLower(filepath.Ext(i)) == ".csv"
+ },
+ "csvFile": func(file *git.File) *git.CsvFile {
+ if strings.ToLower(filepath.Ext(file.Filename)) != ".csv" {
+ return nil
+ }
- csvFile, err := git.ParseCsv(file)
- if err != nil {
- return nil
- }
+ csvFile, err := git.ParseCsv(file)
+ if err != nil {
+ return nil
+ }
- return csvFile
- },
- "httpStatusText": http.StatusText,
- "loadedTime": func(startTime time.Time) string {
- return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
- },
- "slug": func(s string) string {
- return strings.Trim(re.ReplaceAllString(strings.ToLower(s), "-"), "-")
- },
- "avatarUrl": func(user *db.User, noGravatar bool) string {
- if user.AvatarURL != "" {
- return user.AvatarURL
- }
+ return csvFile
+ },
+ "httpStatusText": http.StatusText,
+ "loadedTime": func(startTime time.Time) string {
+ return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
+ },
+ "slug": func(s string) string {
+ return strings.Trim(re.ReplaceAllString(strings.ToLower(s), "-"), "-")
+ },
+ "avatarUrl": func(user *db.User, noGravatar bool) string {
+ if user.AvatarURL != "" {
+ return user.AvatarURL
+ }
- if user.MD5Hash != "" && !noGravatar {
- return "https://www.gravatar.com/avatar/" + user.MD5Hash + "?d=identicon&s=200"
- }
+ if user.MD5Hash != "" && !noGravatar {
+ return "https://www.gravatar.com/avatar/" + user.MD5Hash + "?d=identicon&s=200"
+ }
- return defaultAvatar()
- },
- "asset": func(file string) string {
- if dev {
- return "http://localhost:16157/" + file
- }
- return config.C.ExternalUrl + "/" + manifestEntries[file].File
- },
- "dev": func() bool {
- return dev
- },
- "defaultAvatar": defaultAvatar,
- "visibilityStr": func(visibility int, lowercase bool) string {
- s := "Public"
- switch visibility {
- case 1:
- s = "Unlisted"
- case 2:
- s = "Private"
- }
+ return defaultAvatar()
+ },
+ "asset": func(file string) string {
+ if dev {
+ return "http://localhost:16157/" + file
+ }
+ return config.C.ExternalUrl + "/" + manifestEntries[file].File
+ },
+ "dev": func() bool {
+ return dev
+ },
+ "defaultAvatar": defaultAvatar,
+ "visibilityStr": func(visibility db.Visibility, lowercase bool) string {
+ s := "Public"
+ switch visibility {
+ case 1:
+ s = "Unlisted"
+ case 2:
+ s = "Private"
+ }
- if lowercase {
- return strings.ToLower(s)
- }
- return s
- },
- "unescape": htmlpkg.UnescapeString,
- "join": func(s ...string) string {
- return strings.Join(s, "")
- },
- "toStr": func(i interface{}) string {
- return fmt.Sprint(i)
- },
-}
+ if lowercase {
+ return strings.ToLower(s)
+ }
+ return s
+ },
+ "unescape": htmlpkg.UnescapeString,
+ "join": func(s ...string) string {
+ return strings.Join(s, "")
+ },
+ "toStr": func(i interface{}) string {
+ return fmt.Sprint(i)
+ },
+ }
+)
type Template struct {
templates *template.Template
@@ -159,7 +162,7 @@ func NewServer(isDev bool) *Server {
return nil
},
}))
- //e.Use(middleware.Recover())
+ // e.Use(middleware.Recover())
e.Use(middleware.Secure())
e.Renderer = &Template{
@@ -316,7 +319,6 @@ func dataInit(next echo.HandlerFunc) echo.HandlerFunc {
func locale(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
-
// Check URL arguments
lang := ctx.Request().URL.Query().Get("lang")
changeLang := lang != ""
@@ -335,7 +337,7 @@ func locale(next echo.HandlerFunc) echo.HandlerFunc {
changeLang = false
}
- //3.Then check from 'Accept-Language' header.
+ // 3.Then check from 'Accept-Language' header.
if len(lang) == 0 {
tags, _, _ := language.ParseAcceptLanguage(ctx.Request().Header.Get("Accept-Language"))
lang = i18n.Locales.MatchTag(tags)
diff --git a/internal/web/settings.go b/internal/web/settings.go
index 3df99fd..457324d 100644
--- a/internal/web/settings.go
+++ b/internal/web/settings.go
@@ -3,12 +3,13 @@ package web
import (
"crypto/md5"
"fmt"
- "github.com/labstack/echo/v4"
- "github.com/thomiceli/opengist/internal/db"
- "golang.org/x/crypto/ssh"
"strconv"
"strings"
"time"
+
+ "github.com/labstack/echo/v4"
+ "github.com/thomiceli/opengist/internal/db"
+ "golang.org/x/crypto/ssh"
)
func userSettings(ctx echo.Context) error {
@@ -62,7 +63,7 @@ func accountDeleteProcess(ctx echo.Context) error {
func sshKeysProcess(ctx echo.Context) error {
user := getUserLogged(ctx)
- var dto = new(db.SSHKeyDTO)
+ dto := new(db.SSHKeyDTO)
if err := ctx.Bind(dto); err != nil {
return errorRes(400, "Cannot bind data", err)
}
@@ -93,7 +94,6 @@ func sshKeysProcess(ctx echo.Context) error {
func sshKeysDelete(ctx echo.Context) error {
user := getUserLogged(ctx)
keyId, err := strconv.Atoi(ctx.Param("id"))
-
if err != nil {
return redirect(ctx, "/settings")
}
diff --git a/internal/web/test/gist_test.go b/internal/web/test/gist_test.go
index b477e0e..4dfc50a 100644
--- a/internal/web/test/gist_test.go
+++ b/internal/web/test/gist_test.go
@@ -1,10 +1,11 @@
package test
import (
+ "testing"
+
"github.com/stretchr/testify/require"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
- "testing"
)
func TestGists(t *testing.T) {
@@ -110,7 +111,7 @@ func TestVisibility(t *testing.T) {
gist1 := db.GistDTO{
Title: "gist1",
Description: "my first gist",
- Private: 1,
+ Private: db.UnlistedVisibility,
Name: []string{""},
Content: []string{"yeah"},
}
@@ -119,25 +120,25 @@ func TestVisibility(t *testing.T) {
gist1db, err := db.GetGistByID("1")
require.NoError(t, err)
- require.Equal(t, 1, gist1db.Private)
+ require.Equal(t, db.UnlistedVisibility, gist1db.Private)
err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", nil, 302)
require.NoError(t, err)
gist1db, err = db.GetGistByID("1")
require.NoError(t, err)
- require.Equal(t, 2, gist1db.Private)
+ require.Equal(t, db.PrivateVisibility, gist1db.Private)
err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", nil, 302)
require.NoError(t, err)
gist1db, err = db.GetGistByID("1")
require.NoError(t, err)
- require.Equal(t, 0, gist1db.Private)
+ require.Equal(t, db.PublicVisibility, gist1db.Private)
err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", nil, 302)
require.NoError(t, err)
gist1db, err = db.GetGistByID("1")
require.NoError(t, err)
- require.Equal(t, 1, gist1db.Private)
+ require.Equal(t, db.UnlistedVisibility, gist1db.Private)
}
func TestLikeFork(t *testing.T) {
diff --git a/public/main.ts b/public/main.ts
index 3360680..b759ed8 100644
--- a/public/main.ts
+++ b/public/main.ts
@@ -154,12 +154,18 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('gist-visibility-menu-button')!.onclick = () => {
gistmenuvisibility!.classList.toggle('hidden');
}
+ const lastVisibility = localStorage.getItem('visibility');
Array.from(document.querySelectorAll('.gist-visibility-option')).forEach((el) => {
+ const visibility = (el as HTMLElement).dataset.visibility || '0';
(el as HTMLElement).onclick = () => {
submitgistbutton.textContent = (el as HTMLElement).dataset.btntext;
- submitgistbutton!.value = (el as HTMLElement).dataset.visibility || '0';
+ submitgistbutton!.value = visibility;
+ localStorage.setItem('visibility', visibility);
gistmenuvisibility!.classList.add('hidden');
}
+ if (lastVisibility === visibility) {
+ (el as HTMLElement).click();
+ }
});
}
});
diff --git a/templates/pages/edit.html b/templates/pages/edit.html
index a89067b..f6c7ebc 100644
--- a/templates/pages/edit.html
+++ b/templates/pages/edit.html
@@ -21,7 +21,7 @@