mirror of
https://github.com/thomiceli/opengist.git
synced 2024-12-23 04:52:40 +00:00
845e28dd59
Added Chroma & Goldmark Added Mermaidjs More languages supported Add default values for gist links input Added copy code from markdown blocks
693 lines
18 KiB
Go
693 lines
18 KiB
Go
package web
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"errors"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/thomiceli/opengist/internal/render"
|
|
"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 {
|
|
return func(ctx echo.Context) error {
|
|
currUser := getUserLogged(ctx)
|
|
|
|
userName := ctx.Param("user")
|
|
gistName := ctx.Param("gistname")
|
|
|
|
gistName = strings.TrimSuffix(gistName, ".git")
|
|
|
|
gist, err := db.GetGist(userName, gistName)
|
|
if err != nil {
|
|
return notFound("Gist not found")
|
|
}
|
|
|
|
if gist.Private == db.PrivateVisibility {
|
|
if currUser == nil || currUser.ID != gist.UserID {
|
|
return notFound("Gist not found")
|
|
}
|
|
}
|
|
|
|
setData(ctx, "gist", gist)
|
|
|
|
if config.C.SshGit {
|
|
var sshDomain string
|
|
|
|
if config.C.SshExternalDomain != "" {
|
|
sshDomain = config.C.SshExternalDomain
|
|
} else {
|
|
sshDomain = strings.Split(ctx.Request().Host, ":")[0]
|
|
}
|
|
|
|
if config.C.SshPort == "22" {
|
|
setData(ctx, "sshCloneUrl", sshDomain+":"+userName+"/"+gistName+".git")
|
|
} else {
|
|
setData(ctx, "sshCloneUrl", "ssh://"+sshDomain+":"+config.C.SshPort+"/"+userName+"/"+gistName+".git")
|
|
}
|
|
}
|
|
|
|
httpProtocol := "http"
|
|
if ctx.Request().TLS != nil || ctx.Request().Header.Get("X-Forwarded-Proto") == "https" {
|
|
httpProtocol = "https"
|
|
}
|
|
setData(ctx, "httpProtocol", strings.ToUpper(httpProtocol))
|
|
|
|
var baseHttpUrl string
|
|
// if a custom external url is set, use it
|
|
if config.C.ExternalUrl != "" {
|
|
baseHttpUrl = config.C.ExternalUrl
|
|
} else {
|
|
baseHttpUrl = httpProtocol + "://" + ctx.Request().Host
|
|
}
|
|
|
|
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))
|
|
|
|
nbCommits, err := gist.NbCommits()
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching number of commits", err)
|
|
}
|
|
setData(ctx, "nbCommits", nbCommits)
|
|
|
|
if currUser != nil {
|
|
hasLiked, err := currUser.HasLiked(gist)
|
|
if err != nil {
|
|
return errorRes(500, "Cannot get user like status", err)
|
|
}
|
|
setData(ctx, "hasLiked", hasLiked)
|
|
}
|
|
|
|
if gist.Private > 0 {
|
|
setData(ctx, "NoIndex", true)
|
|
}
|
|
|
|
return next(ctx)
|
|
}
|
|
}
|
|
|
|
// gistSoftInit try to load a gist (same as gistInit) but does not return a 404 if the gist is not found
|
|
// useful for git clients using HTTP to obfuscate the existence of a private gist
|
|
func gistSoftInit(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(ctx echo.Context) error {
|
|
userName := ctx.Param("user")
|
|
gistName := ctx.Param("gistname")
|
|
|
|
gistName = strings.TrimSuffix(gistName, ".git")
|
|
|
|
gist, _ := db.GetGist(userName, gistName)
|
|
setData(ctx, "gist", gist)
|
|
|
|
return next(ctx)
|
|
}
|
|
}
|
|
|
|
// gistNewPushInit has the same behavior as gistSoftInit but create a new gist empty instead
|
|
func gistNewPushSoftInit(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
setData(c, "gist", new(db.Gist))
|
|
return next(c)
|
|
}
|
|
}
|
|
|
|
func allGists(ctx echo.Context) error {
|
|
var err error
|
|
var urlPage string
|
|
|
|
fromUserStr := ctx.Param("user")
|
|
userLogged := getUserLogged(ctx)
|
|
pageInt := getPage(ctx)
|
|
|
|
sort := "created"
|
|
sortText := tr(ctx, "gist.list.sort-by-created")
|
|
order := "desc"
|
|
orderText := tr(ctx, "gist.list.order-by-desc")
|
|
|
|
if ctx.QueryParam("sort") == "updated" {
|
|
sort = "updated"
|
|
sortText = tr(ctx, "gist.list.sort-by-updated")
|
|
}
|
|
|
|
if ctx.QueryParam("order") == "asc" {
|
|
order = "asc"
|
|
orderText = tr(ctx, "gist.list.order-by-asc")
|
|
}
|
|
|
|
setData(ctx, "sort", sortText)
|
|
setData(ctx, "order", orderText)
|
|
|
|
var gists []*db.Gist
|
|
var currentUserId uint
|
|
if userLogged != nil {
|
|
currentUserId = userLogged.ID
|
|
} else {
|
|
currentUserId = 0
|
|
}
|
|
|
|
if fromUserStr == "" {
|
|
urlctx := ctx.Request().URL.Path
|
|
if strings.HasSuffix(urlctx, "search") {
|
|
setData(ctx, "htmlTitle", "Search results")
|
|
setData(ctx, "mode", "search")
|
|
setData(ctx, "searchQuery", ctx.QueryParam("q"))
|
|
setData(ctx, "searchQueryUrl", template.URL("&q="+ctx.QueryParam("q")))
|
|
urlPage = "search"
|
|
gists, err = db.GetAllGistsFromSearch(currentUserId, ctx.QueryParam("q"), pageInt-1, sort, order)
|
|
} else if strings.HasSuffix(urlctx, "all") {
|
|
setData(ctx, "htmlTitle", "All gists")
|
|
setData(ctx, "mode", "all")
|
|
urlPage = "all"
|
|
gists, err = db.GetAllGistsForCurrentUser(currentUserId, pageInt-1, sort, order)
|
|
}
|
|
} else {
|
|
liked := false
|
|
forked := false
|
|
|
|
liked, err = regexp.MatchString(`/[^/]*/liked`, ctx.Request().URL.Path)
|
|
if err != nil {
|
|
return errorRes(500, "Error matching regexp", err)
|
|
}
|
|
|
|
forked, err = regexp.MatchString(`/[^/]*/forked`, ctx.Request().URL.Path)
|
|
if err != nil {
|
|
return errorRes(500, "Error matching regexp", err)
|
|
}
|
|
|
|
var fromUser *db.User
|
|
|
|
fromUser, err = db.GetUserByUsername(fromUserStr)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return notFound("User not found")
|
|
}
|
|
return errorRes(500, "Error fetching user", err)
|
|
}
|
|
setData(ctx, "fromUser", fromUser)
|
|
|
|
if countFromUser, err := db.CountAllGistsFromUser(fromUser.ID, currentUserId); err != nil {
|
|
return errorRes(500, "Error counting gists", err)
|
|
} else {
|
|
setData(ctx, "countFromUser", countFromUser)
|
|
}
|
|
|
|
if countLiked, err := db.CountAllGistsLikedByUser(fromUser.ID, currentUserId); err != nil {
|
|
return errorRes(500, "Error counting liked gists", err)
|
|
} else {
|
|
setData(ctx, "countLiked", countLiked)
|
|
}
|
|
|
|
if countForked, err := db.CountAllGistsForkedByUser(fromUser.ID, currentUserId); err != nil {
|
|
return errorRes(500, "Error counting forked gists", err)
|
|
} else {
|
|
setData(ctx, "countForked", countForked)
|
|
}
|
|
|
|
if liked {
|
|
urlPage = fromUserStr + "/liked"
|
|
setData(ctx, "htmlTitle", "All gists liked by "+fromUserStr)
|
|
setData(ctx, "mode", "liked")
|
|
gists, err = db.GetAllGistsLikedByUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
|
|
} else if forked {
|
|
urlPage = fromUserStr + "/forked"
|
|
setData(ctx, "htmlTitle", "All gists forked by "+fromUserStr)
|
|
setData(ctx, "mode", "forked")
|
|
gists, err = db.GetAllGistsForkedByUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
|
|
} else {
|
|
urlPage = fromUserStr
|
|
setData(ctx, "htmlTitle", "All gists from "+fromUserStr)
|
|
setData(ctx, "mode", "fromUser")
|
|
gists, err = db.GetAllGistsFromUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
|
|
}
|
|
}
|
|
|
|
renderedFiles := make([]*render.RenderedGist, 0, len(gists))
|
|
for _, gist := range gists {
|
|
rendered, err := render.HighlightGistPreview(gist)
|
|
if err != nil {
|
|
log.Warn().Err(err).Msg("Error rendering gist preview for " + gist.Uuid + " - " + gist.PreviewFilename)
|
|
}
|
|
renderedFiles = append(renderedFiles, &rendered)
|
|
}
|
|
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching gists", err)
|
|
}
|
|
|
|
if err = paginate(ctx, renderedFiles, pageInt, 10, "gists", fromUserStr, 2, "&sort="+sort+"&order="+order); err != nil {
|
|
return errorRes(404, "Page not found", nil)
|
|
}
|
|
|
|
setData(ctx, "urlPage", urlPage)
|
|
return html(ctx, "all.html")
|
|
}
|
|
|
|
func gistIndex(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
revision := ctx.Param("revision")
|
|
|
|
if revision == "" {
|
|
revision = "HEAD"
|
|
}
|
|
|
|
files, err := gist.Files(revision)
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching files", err)
|
|
}
|
|
|
|
if len(files) == 0 {
|
|
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)
|
|
}
|
|
|
|
setData(ctx, "page", "code")
|
|
setData(ctx, "commit", revision)
|
|
setData(ctx, "files", renderedFiles)
|
|
setData(ctx, "revision", revision)
|
|
setData(ctx, "htmlTitle", gist.Title)
|
|
return html(ctx, "gist.html")
|
|
}
|
|
|
|
func revisions(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
userName := gist.User.Username
|
|
gistName := gist.Uuid
|
|
|
|
pageInt := getPage(ctx)
|
|
|
|
commits, err := gist.Log((pageInt - 1) * 10)
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching commits log", err)
|
|
}
|
|
|
|
if err := paginate(ctx, commits, pageInt, 10, "commits", userName+"/"+gistName+"/revisions", 2); err != nil {
|
|
return errorRes(404, "Page not found", nil)
|
|
}
|
|
|
|
emailsSet := map[string]struct{}{}
|
|
for _, commit := range commits {
|
|
if commit.AuthorEmail == "" {
|
|
continue
|
|
}
|
|
emailsSet[strings.ToLower(commit.AuthorEmail)] = struct{}{}
|
|
}
|
|
|
|
emailsUsers, err := db.GetUsersFromEmails(emailsSet)
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching users emails", err)
|
|
}
|
|
|
|
setData(ctx, "page", "revisions")
|
|
setData(ctx, "revision", "HEAD")
|
|
setData(ctx, "emails", emailsUsers)
|
|
setData(ctx, "htmlTitle", "Revision of "+gist.Title)
|
|
|
|
return html(ctx, "revisions.html")
|
|
}
|
|
|
|
func create(ctx echo.Context) error {
|
|
setData(ctx, "htmlTitle", "Create a new gist")
|
|
return html(ctx, "create.html")
|
|
}
|
|
|
|
func processCreate(ctx echo.Context) error {
|
|
isCreate := false
|
|
if ctx.Request().URL.Path == "/" {
|
|
isCreate = true
|
|
}
|
|
|
|
err := ctx.Request().ParseForm()
|
|
if err != nil {
|
|
return errorRes(400, "Bad request", err)
|
|
}
|
|
|
|
dto := new(db.GistDTO)
|
|
var gist *db.Gist
|
|
|
|
if isCreate {
|
|
setData(ctx, "htmlTitle", "Create a new gist")
|
|
} else {
|
|
gist = getData(ctx, "gist").(*db.Gist)
|
|
setData(ctx, "htmlTitle", "Edit "+gist.Title)
|
|
}
|
|
|
|
if err := ctx.Bind(dto); err != nil {
|
|
return errorRes(400, "Cannot bind data", err)
|
|
}
|
|
|
|
dto.Files = make([]db.FileDTO, 0)
|
|
fileCounter := 0
|
|
for i := 0; i < len(ctx.Request().PostForm["content"]); i++ {
|
|
name := ctx.Request().PostForm["name"][i]
|
|
content := ctx.Request().PostForm["content"][i]
|
|
|
|
if name == "" {
|
|
fileCounter += 1
|
|
name = "gistfile" + strconv.Itoa(fileCounter) + ".txt"
|
|
}
|
|
|
|
escapedValue, err := url.QueryUnescape(content)
|
|
if err != nil {
|
|
return errorRes(400, "Invalid character unescaped", err)
|
|
}
|
|
|
|
dto.Files = append(dto.Files, db.FileDTO{
|
|
Filename: strings.Trim(name, " "),
|
|
Content: escapedValue,
|
|
})
|
|
}
|
|
|
|
err = ctx.Validate(dto)
|
|
if err != nil {
|
|
addFlash(ctx, validationMessages(&err), "error")
|
|
if isCreate {
|
|
return html(ctx, "create.html")
|
|
} else {
|
|
files, err := gist.Files("HEAD")
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching files", err)
|
|
}
|
|
setData(ctx, "files", files)
|
|
return html(ctx, "edit.html")
|
|
}
|
|
}
|
|
|
|
if isCreate {
|
|
gist = dto.ToGist()
|
|
} else {
|
|
gist = dto.ToExistingGist(gist)
|
|
}
|
|
|
|
user := getUserLogged(ctx)
|
|
gist.NbFiles = len(dto.Files)
|
|
|
|
if isCreate {
|
|
uuidGist, err := uuid.NewRandom()
|
|
if err != nil {
|
|
return errorRes(500, "Error creating an UUID", err)
|
|
}
|
|
gist.Uuid = strings.Replace(uuidGist.String(), "-", "", -1)
|
|
|
|
gist.UserID = user.ID
|
|
gist.User = *user
|
|
}
|
|
|
|
if gist.Title == "" {
|
|
if ctx.Request().PostForm["name"][0] == "" {
|
|
gist.Title = "gist:" + gist.Uuid
|
|
} else {
|
|
gist.Title = ctx.Request().PostForm["name"][0]
|
|
}
|
|
}
|
|
|
|
if len(dto.Files) > 0 {
|
|
split := strings.Split(dto.Files[0].Content, "\n")
|
|
if len(split) > 10 {
|
|
gist.Preview = strings.Join(split[:10], "\n")
|
|
} else {
|
|
gist.Preview = dto.Files[0].Content
|
|
}
|
|
|
|
gist.PreviewFilename = dto.Files[0].Filename
|
|
}
|
|
|
|
if err = gist.InitRepository(); err != nil {
|
|
return errorRes(500, "Error creating the repository", err)
|
|
}
|
|
|
|
if err = gist.AddAndCommitFiles(&dto.Files); err != nil {
|
|
return errorRes(500, "Error adding and committing files", err)
|
|
}
|
|
|
|
if isCreate {
|
|
if err = gist.Create(); err != nil {
|
|
return errorRes(500, "Error creating the gist", err)
|
|
}
|
|
} else {
|
|
if err = gist.Update(); err != nil {
|
|
return errorRes(500, "Error updating the gist", err)
|
|
}
|
|
}
|
|
|
|
return redirect(ctx, "/"+user.Username+"/"+gist.Uuid)
|
|
}
|
|
|
|
func toggleVisibility(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
|
|
gist.Private = (gist.Private + 1) % 3
|
|
if err := gist.Update(); err != nil {
|
|
return errorRes(500, "Error updating this gist", err)
|
|
}
|
|
|
|
addFlash(ctx, "Gist visibility has been changed", "success")
|
|
return redirect(ctx, "/"+gist.User.Username+"/"+gist.Uuid)
|
|
}
|
|
|
|
func deleteGist(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
|
|
if err := gist.Delete(); err != nil {
|
|
return errorRes(500, "Error deleting this gist", err)
|
|
}
|
|
|
|
addFlash(ctx, "Gist has been deleted", "success")
|
|
return redirect(ctx, "/")
|
|
}
|
|
|
|
func like(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
currentUser := getUserLogged(ctx)
|
|
|
|
hasLiked, err := currentUser.HasLiked(gist)
|
|
if err != nil {
|
|
return errorRes(500, "Error checking if user has liked a gist", err)
|
|
}
|
|
|
|
if hasLiked {
|
|
err = gist.RemoveUserLike(getUserLogged(ctx))
|
|
} else {
|
|
err = gist.AppendUserLike(getUserLogged(ctx))
|
|
}
|
|
|
|
if err != nil {
|
|
return errorRes(500, "Error liking/dislking this gist", err)
|
|
}
|
|
|
|
redirectTo := "/" + gist.User.Username + "/" + gist.Uuid
|
|
if r := ctx.QueryParam("redirecturl"); r != "" {
|
|
redirectTo = r
|
|
}
|
|
return redirect(ctx, redirectTo)
|
|
}
|
|
|
|
func fork(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
currentUser := getUserLogged(ctx)
|
|
|
|
alreadyForked, err := gist.GetForkParent(currentUser)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errorRes(500, "Error checking if gist is already forked", err)
|
|
}
|
|
|
|
if gist.User.ID == currentUser.ID {
|
|
addFlash(ctx, "Unable to fork own gists", "error")
|
|
return redirect(ctx, "/"+gist.User.Username+"/"+gist.Uuid)
|
|
}
|
|
|
|
if alreadyForked.ID != 0 {
|
|
return redirect(ctx, "/"+alreadyForked.User.Username+"/"+alreadyForked.Uuid)
|
|
}
|
|
|
|
uuidGist, err := uuid.NewRandom()
|
|
if err != nil {
|
|
return errorRes(500, "Error creating an UUID", err)
|
|
}
|
|
|
|
newGist := &db.Gist{
|
|
Uuid: strings.Replace(uuidGist.String(), "-", "", -1),
|
|
Title: gist.Title,
|
|
Preview: gist.Preview,
|
|
PreviewFilename: gist.PreviewFilename,
|
|
Description: gist.Description,
|
|
Private: gist.Private,
|
|
UserID: currentUser.ID,
|
|
ForkedID: gist.ID,
|
|
NbFiles: gist.NbFiles,
|
|
}
|
|
|
|
if err = newGist.CreateForked(); err != nil {
|
|
return errorRes(500, "Error forking the gist in database", err)
|
|
}
|
|
|
|
if err = gist.ForkClone(currentUser.Username, newGist.Uuid); err != nil {
|
|
return errorRes(500, "Error cloning the repository while forking", err)
|
|
}
|
|
if err = gist.IncrementForkCount(); err != nil {
|
|
return errorRes(500, "Error incrementing the fork count", err)
|
|
}
|
|
|
|
addFlash(ctx, "Gist has been forked", "success")
|
|
|
|
return redirect(ctx, "/"+currentUser.Username+"/"+newGist.Uuid)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
if file == nil {
|
|
return notFound("File not found")
|
|
}
|
|
|
|
return plainText(ctx, 200, file.Content)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
if file == nil {
|
|
return notFound("File not found")
|
|
}
|
|
|
|
ctx.Response().Header().Set("Content-Type", "text/plain")
|
|
ctx.Response().Header().Set("Content-Disposition", "attachment; filename="+file.Filename)
|
|
ctx.Response().Header().Set("Content-Length", strconv.Itoa(len(file.Content)))
|
|
_, err = ctx.Response().Write([]byte(file.Content))
|
|
|
|
if err != nil {
|
|
return errorRes(500, "Error downloading the file", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func edit(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
|
|
files, err := gist.Files("HEAD")
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching files from repository", err)
|
|
}
|
|
|
|
setData(ctx, "files", files)
|
|
setData(ctx, "htmlTitle", "Edit "+gist.Title)
|
|
|
|
return html(ctx, "edit.html")
|
|
}
|
|
|
|
func downloadZip(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
revision := ctx.Param("revision")
|
|
|
|
files, err := gist.Files(revision)
|
|
if err != nil {
|
|
return errorRes(500, "Error fetching files from repository", err)
|
|
}
|
|
if len(files) == 0 {
|
|
return notFound("No files found in this revision")
|
|
}
|
|
|
|
zipFile := new(bytes.Buffer)
|
|
|
|
zipWriter := zip.NewWriter(zipFile)
|
|
|
|
for _, file := range files {
|
|
fh := &zip.FileHeader{
|
|
Name: file.Filename,
|
|
Method: zip.Deflate,
|
|
}
|
|
f, err := zipWriter.CreateHeader(fh)
|
|
if err != nil {
|
|
return errorRes(500, "Error adding a file the to the zip archive", err)
|
|
}
|
|
_, err = f.Write([]byte(file.Content))
|
|
if err != nil {
|
|
return errorRes(500, "Error adding file content the to the zip archive", err)
|
|
}
|
|
}
|
|
err = zipWriter.Close()
|
|
if err != nil {
|
|
return errorRes(500, "Error closing the zip archive", err)
|
|
}
|
|
|
|
ctx.Response().Header().Set("Content-Type", "application/zip")
|
|
ctx.Response().Header().Set("Content-Disposition", "attachment; filename="+gist.Uuid+".zip")
|
|
ctx.Response().Header().Set("Content-Length", strconv.Itoa(len(zipFile.Bytes())))
|
|
_, err = ctx.Response().Write(zipFile.Bytes())
|
|
if err != nil {
|
|
return errorRes(500, "Error writing the zip archive", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func likes(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
|
|
pageInt := getPage(ctx)
|
|
|
|
likers, err := gist.GetUsersLikes(pageInt - 1)
|
|
if err != nil {
|
|
return errorRes(500, "Error getting users who liked this gist", err)
|
|
}
|
|
|
|
if err = paginate(ctx, likers, pageInt, 30, "likers", gist.User.Username+"/"+gist.Uuid+"/likes", 1); err != nil {
|
|
return errorRes(404, "Page not found", nil)
|
|
}
|
|
|
|
setData(ctx, "htmlTitle", "Like for "+gist.Title)
|
|
setData(ctx, "revision", "HEAD")
|
|
return html(ctx, "likes.html")
|
|
}
|
|
|
|
func forks(ctx echo.Context) error {
|
|
gist := getData(ctx, "gist").(*db.Gist)
|
|
pageInt := getPage(ctx)
|
|
|
|
currentUser := getUserLogged(ctx)
|
|
var fromUserID uint = 0
|
|
if currentUser != nil {
|
|
fromUserID = currentUser.ID
|
|
}
|
|
|
|
forks, err := gist.GetForks(fromUserID, pageInt-1)
|
|
if err != nil {
|
|
return errorRes(500, "Error getting users who liked this gist", err)
|
|
}
|
|
|
|
if err = paginate(ctx, forks, pageInt, 30, "forks", gist.User.Username+"/"+gist.Uuid+"/forks", 2); err != nil {
|
|
return errorRes(404, "Page not found", nil)
|
|
}
|
|
|
|
setData(ctx, "htmlTitle", "Forks for "+gist.Title)
|
|
setData(ctx, "revision", "HEAD")
|
|
return html(ctx, "forks.html")
|
|
}
|