Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add embedded gists & JSON gist data/metadata #179

Merged
merged 6 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ install:
build_frontend:
@echo "Building frontend assets..."
npx vite build
EMBED=1 npx postcss 'public/assets/embed-*.css' -c postcss.config.js --replace # until we can .nest { @tailwind } in Sass

build_backend:
@echo "Building Opengist binary..."
Expand Down
18 changes: 16 additions & 2 deletions internal/db/gist.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func (gist *Gist) File(revision string, filename string, truncate bool) (*git.Fi
return nil, nil
}

var size int64
var size uint64

size, err = git.GetFileSize(gist.User.Username, gist.Uuid, revision, filename)
if err != nil {
Expand All @@ -349,7 +349,8 @@ func (gist *Gist) File(revision string, filename string, truncate bool) (*git.Fi

return &git.File{
Filename: filename,
Size: humanize.IBytes(uint64(size)),
Size: size,
HumanSize: humanize.IBytes(size),
Content: content,
Truncated: truncated,
}, err
Expand Down Expand Up @@ -426,6 +427,19 @@ func (gist *Gist) UpdatePreviewAndCount() error {
return gist.Update()
}

func (gist *Gist) VisibilityStr() string {
switch gist.Private {
case PublicVisibility:
return "public"
case UnlistedVisibility:
return "unlisted"
case PrivateVisibility:
return "private"
default:
return ""
}
}

// -- DTO -- //

type GistDTO struct {
Expand Down
4 changes: 2 additions & 2 deletions internal/git/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func GetFileContent(user string, gist string, revision string, filename string,
return content, truncated, nil
}

func GetFileSize(user string, gist string, revision string, filename string) (int64, error) {
func GetFileSize(user string, gist string, revision string, filename string) (uint64, error) {
repositoryPath := RepositoryPath(user, gist)

cmd := exec.Command(
Expand All @@ -165,7 +165,7 @@ func GetFileSize(user string, gist string, revision string, filename string) (in
return 0, err
}

return strconv.ParseInt(strings.TrimSuffix(string(stdout), "\n"), 10, 64)
return strconv.ParseUint(strings.TrimSuffix(string(stdout), "\n"), 10, 64)
}

func GetLog(user string, gist string, skip int) ([]*Commit, error) {
Expand Down
15 changes: 8 additions & 7 deletions internal/git/output_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import (
)

type File struct {
Filename string
Size string
OldFilename string
Content string
Truncated bool
IsCreated bool
IsDeleted bool
Filename string `json:"filename"`
Size uint64 `json:"size"`
HumanSize string `json:"human_size"`
OldFilename string `json:"-"`
Content string `json:"content"`
Truncated bool `json:"truncated"`
IsCreated bool `json:"-"`
IsDeleted bool `json:"-"`
}

type CsvFile struct {
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/cs-CZ.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: Klonovat pomocí %s
gist.header.clone-http-help: Klonovat s pomocí Git pomocí základní autentizace HTTP.
gist.header.clone-ssh: Klonovat pomocí SSH
gist.header.clone-ssh-help: Klonovat s pomocí Git pomocí klíče SSH.
gist.header.share: Sdílet
gist.header.share-help: Zkopírovat sdílitelný odkaz na tento gist.
gist.header.embed:
gist.header.embed-help:
gist.header.download-zip: Stáhnout ZIP

gist.raw: Raw
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: Clone via %s
gist.header.clone-http-help: Clone with Git using HTTP basic authentication.
gist.header.clone-ssh: Clone via SSH
gist.header.clone-ssh-help: Clone with Git using an SSH key.
gist.header.share: Share
gist.header.share-help: Copy shareable link for this gist.
gist.header.embed: Embed
gist.header.embed-help: Embed this gist to your website.
gist.header.download-zip: Download ZIP

gist.raw: Raw
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/es-ES.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: Clonar via %s
gist.header.clone-http-help: Clonar con Git usando autenticación básica HTTP.
gist.header.clone-ssh: Clonar via SSH
gist.header.clone-ssh-help: Clonar con Git usando una clave SSH.
gist.header.share: Compartir
gist.header.share-help: Copiar enlace para compartir este gist.
gist.header.embed:
gist.header.embed-help:
gist.header.download-zip: Descargar ZIP

gist.raw: Sin formato
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/fr-FR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: Cloner via %s
gist.header.clone-http-help: Cloner avec Git en utilisant l'authentification HTTP basic.
gist.header.clone-ssh: Cloner via SSH
gist.header.clone-ssh-help: Cloner avec Git en utilisant une clé SSH.
gist.header.share: Partager
gist.header.share-help: Copier le lien partageable de ce gist.
gist.header.embed:
gist.header.embed-help:
gist.header.download-zip: Télécharger en ZIP

gist.raw: Brut
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/hu-HU.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: "Clone-ozás ezzel: %s"
gist.header.clone-http-help: Clone-ozás Git HTTP basic hitelesítéssel.
gist.header.clone-ssh: Clone-ozás SSH-n keresztül
gist.header.clone-ssh-help: Clone-ozás SSH kulccsal
gist.header.share: Megosztás
gist.header.share-help: Másold ki ennek a gistnek a megosztható linkjét
gist.header.embed:
gist.header.embed-help:
gist.header.download-zip: ZIP archívum letöltése

gist.raw: Eredeti
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/ru-RU.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: Клонировать с помощью %s
gist.header.clone-http-help: Клонировать с помощью Git используя аутентификацию HTTP.
gist.header.clone-ssh: Клонировать c помощью SSH
gist.header.clone-ssh-help: Клонировать c помощью Git используя ключ SSH.
gist.header.share: Поделиться
gist.header.share-help: Скопировать ссылку на фрагмент.
gist.header.embed:
gist.header.embed-help:
gist.header.download-zip: Скачать ZIP-архив

gist.raw: Исходник
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/zh-CN.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: 通过 %s 克隆
gist.header.clone-http-help: 使用 Git 通过 HTTP 基础认证克隆。
gist.header.clone-ssh: 通过 SSH 克隆
gist.header.clone-ssh-help: 使用 Git 通过 SSH 密钥克隆。
gist.header.share: 分享
gist.header.share-help: 为此 Gist 复制可供分享的链接。
gist.header.embed:
gist.header.embed-help:
gist.header.download-zip: 下载 ZIP

gist.raw: 原始文件
Expand Down
4 changes: 2 additions & 2 deletions internal/i18n/locales/zh-TW.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ gist.header.clone-http: 透過 %s 複製
gist.header.clone-http-help: 使用 HTTP 基本認證透過 Git 複製。
gist.header.clone-ssh: 透過 SSH 複製
gist.header.clone-ssh-help: 使用 SSH 金鑰透過 Git 複製。
gist.header.share: 分享
gist.header.share-help: 複製這個 Gist 的連結。
gist.header.embed:
gist.header.embed-help:
gist.header.download-zip: 下載 ZIP

gist.raw: 原始檔案
Expand Down
20 changes: 17 additions & 3 deletions internal/render/highlight.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import (
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
)

type RenderedFile struct {
*git.File
Type string
Lines []string
HTML string
Type string `json:"type"`
Lines []string `json:"-"`
HTML string `json:"-"`
}

type RenderedGist struct {
Expand Down Expand Up @@ -66,6 +67,19 @@ func HighlightFile(file *git.File) (RenderedFile, error) {
return rendered, err
}

func HighlightFiles(files []*git.File) ([]RenderedFile, error) {
renderedFiles := make([]RenderedFile, 0, len(files))
for _, file := range files {
rendered, err := HighlightFile(file)
if err != nil {
log.Warn().Err(err).Msg("Error rendering gist preview for " + file.Filename)
}
renderedFiles = append(renderedFiles, rendered)
}

return renderedFiles, nil
}

func HighlightGistPreview(gist *db.Gist) (RenderedGist, error) {
rendered := RenderedGist{
Gist: gist,
Expand Down
112 changes: 104 additions & 8 deletions internal/web/gist.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ package web

import (
"archive/zip"
"bufio"
"bytes"
"errors"
"fmt"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/render"
"html/template"
"net/url"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"

"github.com/google/uuid"
"github.com/labstack/echo/v4"
Expand All @@ -26,7 +30,17 @@ func gistInit(next echo.HandlerFunc) echo.HandlerFunc {
userName := ctx.Param("user")
gistName := ctx.Param("gistname")

gistName = strings.TrimSuffix(gistName, ".git")
switch filepath.Ext(gistName) {
case ".js":
setData(ctx, "gistpage", "js")
gistName = strings.TrimSuffix(gistName, ".js")
case ".json":
setData(ctx, "gistpage", "json")
gistName = strings.TrimSuffix(gistName, ".json")
case ".git":
setData(ctx, "gistpage", "git")
gistName = strings.TrimSuffix(gistName, ".git")
}

gist, err := db.GetGist(userName, gistName)
if err != nil {
Expand Down Expand Up @@ -71,12 +85,15 @@ func gistInit(next echo.HandlerFunc) echo.HandlerFunc {
baseHttpUrl = httpProtocol + "://" + ctx.Request().Host
}

setData(ctx, "baseHttpUrl", baseHttpUrl)

if config.C.HttpGit {
setData(ctx, "httpCloneUrl", baseHttpUrl+"/"+userName+"/"+gistName+".git")
}

setData(ctx, "httpCopyUrl", baseHttpUrl+"/"+userName+"/"+gistName)
setData(ctx, "currentUrl", template.URL(ctx.Request().URL.Path))
setData(ctx, "embedScript", fmt.Sprintf(`<script src="%s"></script>`, baseHttpUrl+"/"+userName+"/"+gistName+".js"))

nbCommits, err := gist.NbCommits()
if err != nil {
Expand Down Expand Up @@ -256,6 +273,12 @@ func allGists(ctx echo.Context) error {
}

func gistIndex(ctx echo.Context) error {
if getData(ctx, "gistpage") == "js" {
return gistJs(ctx)
} else if getData(ctx, "gistpage") == "json" {
return gistJson(ctx)
}

gist := getData(ctx, "gist").(*db.Gist)
revision := ctx.Param("revision")

Expand All @@ -272,13 +295,9 @@ func gistIndex(ctx echo.Context) error {
return notFound("Revision not found")
}

renderedFiles := make([]render.RenderedFile, 0, len(files))
for _, file := range files {
rendered, err := render.HighlightFile(file)
if err != nil {
log.Warn().Err(err).Msg("Error rendering gist preview for " + gist.Uuid + " - " + gist.PreviewFilename)
}
renderedFiles = append(renderedFiles, rendered)
renderedFiles, err := render.HighlightFiles(files)
if err != nil {
return errorRes(500, "Error rendering files", err)
}

setData(ctx, "page", "code")
Expand All @@ -289,6 +308,83 @@ func gistIndex(ctx echo.Context) error {
return html(ctx, "gist.html")
}

func gistJson(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist)
files, err := gist.Files("HEAD")
if err != nil {
return errorRes(500, "Error fetching files", err)
}

renderedFiles, err := render.HighlightFiles(files)
if err != nil {
return errorRes(500, "Error rendering files", err)
}

setData(ctx, "files", renderedFiles)

htmlbuf := bytes.Buffer{}
w := bufio.NewWriter(&htmlbuf)
if err = ctx.Echo().Renderer.Render(w, "gist_embed.html", dataMap(ctx), ctx); err != nil {
return err
}
_ = w.Flush()

jsUrl, err := url.JoinPath(getData(ctx, "baseHttpUrl").(string), gist.User.Username, gist.Uuid+".js")
if err != nil {
return errorRes(500, "Error joining url", err)
}

return ctx.JSON(200, map[string]interface{}{
"owner": gist.User.Username,
"id": gist.Uuid,
"title": gist.Title,
"description": gist.Description,
"created_at": time.Unix(gist.CreatedAt, 0).Format(time.RFC3339),
"visibility": gist.VisibilityStr(),
"files": renderedFiles,
"embed": map[string]string{
"html": htmlbuf.String(),
"css": getData(ctx, "baseHttpUrl").(string) + asset("embed.css"),
"js": jsUrl,
"js_dark": jsUrl + "?dark",
},
})
}

func gistJs(ctx echo.Context) error {
if _, exists := ctx.QueryParams()["dark"]; exists {
setData(ctx, "dark", "dark")
}

gist := getData(ctx, "gist").(*db.Gist)
files, err := gist.Files("HEAD")
if err != nil {
return errorRes(500, "Error fetching files", err)
}

renderedFiles, err := render.HighlightFiles(files)
if err != nil {
return errorRes(500, "Error rendering files", err)
}

setData(ctx, "files", renderedFiles)

htmlbuf := bytes.Buffer{}
w := bufio.NewWriter(&htmlbuf)
if err = ctx.Echo().Renderer.Render(w, "gist_embed.html", dataMap(ctx), ctx); err != nil {
return err
}
_ = w.Flush()

js := `document.write('<link rel="stylesheet" href="%s">')
document.write('%s')
`
js = fmt.Sprintf(js, getData(ctx, "baseHttpUrl").(string)+asset("embed.css"),
strings.Replace(htmlbuf.String(), "\n", `\n`, -1))
ctx.Response().Header().Set("Content-Type", "application/javascript")
return plainText(ctx, 200, js)
}

func revisions(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist)
userName := gist.User.Username
Expand Down
Loading