Add embedded gists & JSON gist data/metadata (#179)

This commit is contained in:
Thomas Miceli
2023-12-21 20:00:04 +01:00
parent 845e28dd59
commit 0753c5cb54
32 changed files with 872 additions and 60 deletions

View File

@ -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"
@ -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 {
@ -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 {
@ -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")
@ -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")
@ -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

View File

@ -86,12 +86,7 @@ var (
return defaultAvatar()
},
"asset": func(file string) string {
if dev {
return "http://localhost:16157/" + file
}
return config.C.ExternalUrl + "/" + manifestEntries[file].File
},
"asset": asset,
"dev": func() bool {
return dev
},
@ -482,3 +477,10 @@ func defaultAvatar() string {
}
return config.C.ExternalUrl + "/" + manifestEntries["default.png"].File
}
func asset(file string) string {
if dev {
return "http://localhost:16157/" + file
}
return config.C.ExternalUrl + "/" + manifestEntries[file].File
}

View File

@ -36,6 +36,10 @@ func getData(ctx echo.Context, key string) any {
return data[key]
}
func dataMap(ctx echo.Context) echo.Map {
return ctx.Request().Context().Value(dataKey).(echo.Map)
}
func html(ctx echo.Context, template string) error {
return htmlWithCode(ctx, 200, template)
}