mirror of
https://github.com/thomiceli/opengist.git
synced 2024-12-23 04:52:40 +00:00
Added gist methods related to git + Truncate command output
This commit is contained in:
parent
167abd4ae5
commit
22668be923
8 changed files with 202 additions and 112 deletions
|
@ -76,13 +76,13 @@ func GetNumberOfCommitsOfRepository(user string, gist string) (string, error) {
|
||||||
return strings.TrimSuffix(string(stdout), "\n"), err
|
return strings.TrimSuffix(string(stdout), "\n"), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFilesOfRepository(user string, gist string, commit string) ([]string, error) {
|
func GetFilesOfRepository(user string, gist string, revision string) ([]string, error) {
|
||||||
repositoryPath := RepositoryPath(user, gist)
|
repositoryPath := RepositoryPath(user, gist)
|
||||||
|
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"git",
|
"git",
|
||||||
"ls-tree",
|
"ls-tree",
|
||||||
commit,
|
revision,
|
||||||
"--name-only",
|
"--name-only",
|
||||||
)
|
)
|
||||||
cmd.Dir = repositoryPath
|
cmd.Dir = repositoryPath
|
||||||
|
@ -96,19 +96,29 @@ func GetFilesOfRepository(user string, gist string, commit string) ([]string, er
|
||||||
return slice[:len(slice)-1], nil
|
return slice[:len(slice)-1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFileContent(user string, gist string, commit string, filename string) (string, error) {
|
func GetFileContent(user string, gist string, revision string, filename string, truncate bool) (string, bool, error) {
|
||||||
repositoryPath := RepositoryPath(user, gist)
|
repositoryPath := RepositoryPath(user, gist)
|
||||||
|
|
||||||
|
var maxBytes int64 = -1
|
||||||
|
if truncate {
|
||||||
|
maxBytes = 2 << 18
|
||||||
|
}
|
||||||
|
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"git",
|
"git",
|
||||||
"--no-pager",
|
"--no-pager",
|
||||||
"show",
|
"show",
|
||||||
commit+":"+filename,
|
revision+":"+filename,
|
||||||
)
|
)
|
||||||
cmd.Dir = repositoryPath
|
cmd.Dir = repositoryPath
|
||||||
|
|
||||||
stdout, err := cmd.Output()
|
stdout, _ := cmd.StdoutPipe()
|
||||||
return string(stdout), err
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return truncateCommandOutput(stdout, maxBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLog(user string, gist string, skip string) (string, error) {
|
func GetLog(user string, gist string, skip string) (string, error) {
|
||||||
|
@ -228,7 +238,7 @@ func Push(gistTmpId string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteRepository(user string, gist string) error {
|
func DeleteRepository(user string, gist string) error {
|
||||||
return os.RemoveAll(filepath.Join(config.GetHomeDir(), "repos", strings.ToLower(user), gist))
|
return os.RemoveAll(RepositoryPath(user, gist))
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateServerInfo(user string, gist string) error {
|
func UpdateServerInfo(user string, gist string) error {
|
||||||
|
@ -239,7 +249,7 @@ func UpdateServerInfo(user string, gist string) error {
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func RPCRefs(user string, gist string, service string) ([]byte, error) {
|
func RPC(user string, gist string, service string) ([]byte, error) {
|
||||||
repositoryPath := RepositoryPath(user, gist)
|
repositoryPath := RepositoryPath(user, gist)
|
||||||
|
|
||||||
cmd := exec.Command("git", service, "--stateless-rpc", "--advertise-refs", ".")
|
cmd := exec.Command("git", service, "--stateless-rpc", "--advertise-refs", ".")
|
||||||
|
|
39
internal/git/output_parser.go
Normal file
39
internal/git/output_parser.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func truncateCommandOutput(out io.Reader, maxBytes int64) (string, bool, error) {
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if maxBytes < 0 {
|
||||||
|
// read entire output
|
||||||
|
buf, err = io.ReadAll(out)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
return string(buf), false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// read up to maxBytes bytes
|
||||||
|
buf = make([]byte, maxBytes)
|
||||||
|
n, err := io.ReadFull(out, buf)
|
||||||
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
bytesRead := int64(n)
|
||||||
|
|
||||||
|
// find index of last newline character
|
||||||
|
lastNewline := bytes.LastIndexByte(buf, '\n')
|
||||||
|
if lastNewline >= 0 {
|
||||||
|
// truncate buffer to exclude last line
|
||||||
|
buf = buf[:lastNewline]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf), bytesRead == maxBytes, nil
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"opengist/internal/git"
|
||||||
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,14 +26,13 @@ type Gist struct {
|
||||||
Likes []User `gorm:"many2many:likes;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
Likes []User `gorm:"many2many:likes;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
Forked *Gist `gorm:"foreignKey:ForkedID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`
|
Forked *Gist `gorm:"foreignKey:ForkedID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`
|
||||||
ForkedID uint
|
ForkedID uint
|
||||||
|
|
||||||
Files []File `gorm:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
Filename string `validate:"excludes=\x2f,excludes=\x5c,max=50"`
|
Filename string `validate:"excludes=\x2f,excludes=\x5c,max=50"`
|
||||||
OldFilename string `validate:"excludes=\x2f,excludes=\x5c,max=50"`
|
OldFilename string `validate:"excludes=\x2f,excludes=\x5c,max=50"`
|
||||||
Content string `validate:"required"`
|
Content string `validate:"required"`
|
||||||
|
Truncated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
|
@ -186,6 +187,96 @@ func (gist *Gist) CanWrite(user *User) bool {
|
||||||
return !(user == nil) && (gist.UserID == user.ID)
|
return !(user == nil) && (gist.UserID == user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) InitRepository() error {
|
||||||
|
return git.InitRepository(gist.User.Username, gist.Uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) DeleteRepository() error {
|
||||||
|
return git.DeleteRepository(gist.User.Username, gist.Uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) Files(revision string) ([]*File, error) {
|
||||||
|
var files []*File
|
||||||
|
filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, revision)
|
||||||
|
if err != nil {
|
||||||
|
// if the revision or the file do not exist
|
||||||
|
|
||||||
|
if exiterr, ok := err.(*exec.ExitError); ok && exiterr.ExitCode() == 128 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fileStr := range filesStr {
|
||||||
|
file, err := gist.File(revision, fileStr, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
return files, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) File(revision string, filename string, truncate bool) (*File, error) {
|
||||||
|
content, truncated, err := git.GetFileContent(gist.User.Username, gist.Uuid, revision, filename, truncate)
|
||||||
|
|
||||||
|
// if the revision or the file do not exist
|
||||||
|
if exiterr, ok := err.(*exec.ExitError); ok && exiterr.ExitCode() == 128 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &File{
|
||||||
|
Filename: filename,
|
||||||
|
Content: content,
|
||||||
|
Truncated: truncated,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) Log(skip string) error {
|
||||||
|
_, err := git.GetLog(gist.User.Username, gist.Uuid, skip)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) NbCommits() (string, error) {
|
||||||
|
return git.GetNumberOfCommitsOfRepository(gist.User.Username, gist.Uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) AddAndCommitFiles(files *[]File) error {
|
||||||
|
if err := git.CloneTmp(gist.User.Username, gist.Uuid, gist.Uuid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range *files {
|
||||||
|
if err := git.SetFileContent(gist.Uuid, file.Filename, file.Content); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.AddAll(gist.Uuid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.Commit(gist.Uuid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return git.Push(gist.Uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) ForkClone(username string, uuid string) error {
|
||||||
|
return git.ForkClone(gist.User.Username, gist.Uuid, username, uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) UpdateServerInfo() error {
|
||||||
|
return git.UpdateServerInfo(gist.User.Username, gist.Uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gist *Gist) RPC(service string) ([]byte, error) {
|
||||||
|
return git.RPC(gist.User.Username, gist.Uuid, service)
|
||||||
|
}
|
||||||
|
|
||||||
// -- DTO -- //
|
// -- DTO -- //
|
||||||
|
|
||||||
type GistDTO struct {
|
type GistDTO struct {
|
||||||
|
@ -200,7 +291,6 @@ func (dto *GistDTO) ToGist() *Gist {
|
||||||
Title: dto.Title,
|
Title: dto.Title,
|
||||||
Description: dto.Description,
|
Description: dto.Description,
|
||||||
Private: dto.Private,
|
Private: dto.Private,
|
||||||
Files: dto.Files,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +298,5 @@ func (dto *GistDTO) ToExistingGist(gist *Gist) *Gist {
|
||||||
gist.Title = dto.Title
|
gist.Title = dto.Title
|
||||||
gist.Description = dto.Description
|
gist.Description = dto.Description
|
||||||
gist.Private = dto.Private
|
gist.Private = dto.Private
|
||||||
gist.Files = dto.Files
|
|
||||||
return gist
|
return gist
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ func adminGistDelete(ctx echo.Context) error {
|
||||||
return errorRes(500, "Cannot retrieve gist", err)
|
return errorRes(500, "Cannot retrieve gist", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.DeleteRepository(gist.User.Username, gist.Uuid); err != nil {
|
if err = gist.DeleteRepository(); err != nil {
|
||||||
return errorRes(500, "Cannot delete the repository", err)
|
return errorRes(500, "Cannot delete the repository", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
@ -53,7 +52,7 @@ func gistInit(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
|
||||||
setData(ctx, "currentUrl", template.URL(ctx.Request().URL.Path))
|
setData(ctx, "currentUrl", template.URL(ctx.Request().URL.Path))
|
||||||
|
|
||||||
nbCommits, err := git.GetNumberOfCommitsOfRepository(userName, gistName)
|
nbCommits, err := gist.NbCommits()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error fetching number of commits", err)
|
return errorRes(500, "Error fetching number of commits", err)
|
||||||
}
|
}
|
||||||
|
@ -133,27 +132,19 @@ func allGists(ctx echo.Context) error {
|
||||||
|
|
||||||
func gistIndex(ctx echo.Context) error {
|
func gistIndex(ctx echo.Context) error {
|
||||||
gist := getData(ctx, "gist").(*models.Gist)
|
gist := getData(ctx, "gist").(*models.Gist)
|
||||||
userName := gist.User.Username
|
|
||||||
gistName := gist.Uuid
|
|
||||||
revision := ctx.Param("revision")
|
revision := ctx.Param("revision")
|
||||||
|
|
||||||
if revision == "" {
|
if revision == "" {
|
||||||
revision = "HEAD"
|
revision = "HEAD"
|
||||||
}
|
}
|
||||||
|
|
||||||
nbCommits := getData(ctx, "nbCommits")
|
files, err := gist.Files(revision)
|
||||||
files := make(map[string]string)
|
if err != nil {
|
||||||
if nbCommits != "0" {
|
return errorRes(500, "Error fetching files", err)
|
||||||
filesStr, err := git.GetFilesOfRepository(userName, gistName, revision)
|
}
|
||||||
if err != nil {
|
|
||||||
return errorRes(500, "Error fetching files from repository", err)
|
if len(files) == 0 {
|
||||||
}
|
return notFound("Revision not found")
|
||||||
for _, file := range filesStr {
|
|
||||||
files[file], err = git.GetFileContent(userName, gistName, revision, file)
|
|
||||||
if err != nil {
|
|
||||||
return errorRes(500, "Error fetching file content from file "+file, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setData(ctx, "page", "code")
|
setData(ctx, "page", "code")
|
||||||
|
@ -161,7 +152,6 @@ func gistIndex(ctx echo.Context) error {
|
||||||
setData(ctx, "files", files)
|
setData(ctx, "files", files)
|
||||||
setData(ctx, "revision", revision)
|
setData(ctx, "revision", revision)
|
||||||
setData(ctx, "htmlTitle", gist.Title)
|
setData(ctx, "htmlTitle", gist.Title)
|
||||||
|
|
||||||
return html(ctx, "gist.html")
|
return html(ctx, "gist.html")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +246,6 @@ func processCreate(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ctx.Bind(dto); err != nil {
|
if err := ctx.Bind(dto); err != nil {
|
||||||
fmt.Println(err)
|
|
||||||
return errorRes(400, "Cannot bind data", err)
|
return errorRes(400, "Cannot bind data", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,18 +275,10 @@ func processCreate(ctx echo.Context) error {
|
||||||
if isCreate {
|
if isCreate {
|
||||||
return html(ctx, "create.html")
|
return html(ctx, "create.html")
|
||||||
} else {
|
} else {
|
||||||
files := make(map[string]string)
|
files, err := gist.Files("HEAD")
|
||||||
filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, "HEAD")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error fetching files from repository", err)
|
return errorRes(500, "Error fetching files", err)
|
||||||
}
|
}
|
||||||
for _, file := range filesStr {
|
|
||||||
files[file], err = git.GetFileContent(gist.User.Username, gist.Uuid, "HEAD", file)
|
|
||||||
if err != nil {
|
|
||||||
return errorRes(500, "Error fetching file content from file "+file, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(ctx, "files", files)
|
setData(ctx, "files", files)
|
||||||
return html(ctx, "edit.html")
|
return html(ctx, "edit.html")
|
||||||
}
|
}
|
||||||
|
@ -310,7 +291,7 @@ func processCreate(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
user := getUserLogged(ctx)
|
user := getUserLogged(ctx)
|
||||||
gist.NbFiles = len(gist.Files)
|
gist.NbFiles = len(dto.Files)
|
||||||
|
|
||||||
if isCreate {
|
if isCreate {
|
||||||
uuidGist, err := uuid.NewRandom()
|
uuidGist, err := uuid.NewRandom()
|
||||||
|
@ -320,6 +301,7 @@ func processCreate(ctx echo.Context) error {
|
||||||
gist.Uuid = strings.Replace(uuidGist.String(), "-", "", -1)
|
gist.Uuid = strings.Replace(uuidGist.String(), "-", "", -1)
|
||||||
|
|
||||||
gist.UserID = user.ID
|
gist.UserID = user.ID
|
||||||
|
gist.User = *user
|
||||||
}
|
}
|
||||||
|
|
||||||
if gist.Title == "" {
|
if gist.Title == "" {
|
||||||
|
@ -330,41 +312,23 @@ func processCreate(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(gist.Files) > 0 {
|
if len(dto.Files) > 0 {
|
||||||
split := strings.Split(gist.Files[0].Content, "\n")
|
split := strings.Split(dto.Files[0].Content, "\n")
|
||||||
if len(split) > 10 {
|
if len(split) > 10 {
|
||||||
gist.Preview = strings.Join(split[:10], "\n")
|
gist.Preview = strings.Join(split[:10], "\n")
|
||||||
} else {
|
} else {
|
||||||
gist.Preview = gist.Files[0].Content
|
gist.Preview = dto.Files[0].Content
|
||||||
}
|
}
|
||||||
|
|
||||||
gist.PreviewFilename = gist.Files[0].Filename
|
gist.PreviewFilename = dto.Files[0].Filename
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.InitRepository(user.Username, gist.Uuid); err != nil {
|
if err = gist.InitRepository(); err != nil {
|
||||||
return errorRes(500, "Error creating the repository", err)
|
return errorRes(500, "Error creating the repository", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.CloneTmp(user.Username, gist.Uuid, gist.Uuid); err != nil {
|
if err = gist.AddAndCommitFiles(&dto.Files); err != nil {
|
||||||
return errorRes(500, "Error cloning the repository", err)
|
return errorRes(500, "Error adding and commiting files", err)
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range gist.Files {
|
|
||||||
if err = git.SetFileContent(gist.Uuid, file.Filename, file.Content); err != nil {
|
|
||||||
return errorRes(500, "Error setting file content for file "+file.Filename, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = git.AddAll(gist.Uuid); err != nil {
|
|
||||||
return errorRes(500, "Error adding files to the repository", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = git.Commit(gist.Uuid); err != nil {
|
|
||||||
return errorRes(500, "Error committing files to the local repository", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = git.Push(gist.Uuid); err != nil {
|
|
||||||
return errorRes(500, "Error pushing the local repository", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if isCreate {
|
if isCreate {
|
||||||
|
@ -395,7 +359,7 @@ func toggleVisibility(ctx echo.Context) error {
|
||||||
func deleteGist(ctx echo.Context) error {
|
func deleteGist(ctx echo.Context) error {
|
||||||
var gist = getData(ctx, "gist").(*models.Gist)
|
var gist = getData(ctx, "gist").(*models.Gist)
|
||||||
|
|
||||||
err := git.DeleteRepository(gist.User.Username, gist.Uuid)
|
err := gist.DeleteRepository()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error deleting the repository", err)
|
return errorRes(500, "Error deleting the repository", err)
|
||||||
}
|
}
|
||||||
|
@ -472,7 +436,7 @@ func fork(ctx echo.Context) error {
|
||||||
return errorRes(500, "Error forking the gist in database", err)
|
return errorRes(500, "Error forking the gist in database", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.ForkClone(gist.User.Username, gist.Uuid, currentUser.Username, newGist.Uuid); err != nil {
|
if err = gist.ForkClone(currentUser.Username, newGist.Uuid); err != nil {
|
||||||
return errorRes(500, "Error cloning the repository while forking", err)
|
return errorRes(500, "Error cloning the repository while forking", err)
|
||||||
}
|
}
|
||||||
if err = gist.IncrementForkCount(); err != nil {
|
if err = gist.IncrementForkCount(); err != nil {
|
||||||
|
@ -486,38 +450,26 @@ func fork(ctx echo.Context) error {
|
||||||
|
|
||||||
func rawFile(ctx echo.Context) error {
|
func rawFile(ctx echo.Context) error {
|
||||||
gist := getData(ctx, "gist").(*models.Gist)
|
gist := getData(ctx, "gist").(*models.Gist)
|
||||||
fileContent, err := git.GetFileContent(
|
file, err := gist.File(ctx.Param("revision"), ctx.Param("file"), false)
|
||||||
gist.User.Username,
|
|
||||||
gist.Uuid,
|
|
||||||
ctx.Param("revision"),
|
|
||||||
ctx.Param("file"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error getting file content", err)
|
return errorRes(500, "Error getting file content", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filebytes := []byte(fileContent)
|
if file == nil {
|
||||||
|
|
||||||
if len(filebytes) == 0 {
|
|
||||||
return notFound("File not found")
|
return notFound("File not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
return plainText(ctx, 200, string(filebytes))
|
return plainText(ctx, 200, file.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func edit(ctx echo.Context) error {
|
func edit(ctx echo.Context) error {
|
||||||
var gist = getData(ctx, "gist").(*models.Gist)
|
var gist = getData(ctx, "gist").(*models.Gist)
|
||||||
|
|
||||||
files := make(map[string]string)
|
files, err := gist.Files("HEAD")
|
||||||
filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, "HEAD")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error fetching files from repository", err)
|
return errorRes(500, "Error fetching files from repository", err)
|
||||||
}
|
}
|
||||||
for _, file := range filesStr {
|
|
||||||
files[file], err = git.GetFileContent(gist.User.Username, gist.Uuid, "HEAD", file)
|
|
||||||
if err != nil {
|
|
||||||
return errorRes(500, "Error fetching file content from file "+file, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(ctx, "files", files)
|
setData(ctx, "files", files)
|
||||||
setData(ctx, "htmlTitle", "Edit "+gist.Title)
|
setData(ctx, "htmlTitle", "Edit "+gist.Title)
|
||||||
|
@ -529,29 +481,24 @@ func downloadZip(ctx echo.Context) error {
|
||||||
var gist = getData(ctx, "gist").(*models.Gist)
|
var gist = getData(ctx, "gist").(*models.Gist)
|
||||||
var revision = ctx.Param("revision")
|
var revision = ctx.Param("revision")
|
||||||
|
|
||||||
files := make(map[string]string)
|
files, err := gist.Files(revision)
|
||||||
filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, revision)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error fetching files from repository", err)
|
return errorRes(500, "Error fetching files from repository", err)
|
||||||
}
|
}
|
||||||
|
if len(files) == 0 {
|
||||||
for _, file := range filesStr {
|
return notFound("No files found in this revision")
|
||||||
files[file], err = git.GetFileContent(gist.User.Username, gist.Uuid, revision, file)
|
|
||||||
if err != nil {
|
|
||||||
return errorRes(500, "Error fetching file content from file "+file, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zipFile := new(bytes.Buffer)
|
zipFile := new(bytes.Buffer)
|
||||||
|
|
||||||
zipWriter := zip.NewWriter(zipFile)
|
zipWriter := zip.NewWriter(zipFile)
|
||||||
|
|
||||||
for fileName, fileContent := range files {
|
for _, file := range files {
|
||||||
f, err := zipWriter.Create(fileName)
|
f, err := zipWriter.Create(file.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error adding a file the to the zip archive", err)
|
return errorRes(500, "Error adding a file the to the zip archive", err)
|
||||||
}
|
}
|
||||||
_, err = f.Write([]byte(fileContent))
|
_, err = f.Write([]byte(file.Content))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Error adding file content the to the zip archive", err)
|
return errorRes(500, "Error adding file content the to the zip archive", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,13 +152,13 @@ func infoRefs(ctx echo.Context) error {
|
||||||
service = strings.TrimPrefix(serviceType, "git-")
|
service = strings.TrimPrefix(serviceType, "git-")
|
||||||
|
|
||||||
if service != "upload-pack" && service != "receive-pack" {
|
if service != "upload-pack" && service != "receive-pack" {
|
||||||
if err := git.UpdateServerInfo(gist.User.Username, gist.Uuid); err != nil {
|
if err := gist.UpdateServerInfo(); err != nil {
|
||||||
return errorRes(500, "Cannot update server info", err)
|
return errorRes(500, "Cannot update server info", err)
|
||||||
}
|
}
|
||||||
return sendFile(ctx, "text/plain; charset=utf-8")
|
return sendFile(ctx, "text/plain; charset=utf-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
refs, err := git.RPCRefs(gist.User.Username, gist.Uuid, service)
|
refs, err := gist.RPC(service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorRes(500, "Cannot run git "+service, err)
|
return errorRes(500, "Cannot run git "+service, err)
|
||||||
}
|
}
|
||||||
|
|
6
templates/pages/edit.html
vendored
6
templates/pages/edit.html
vendored
|
@ -53,11 +53,11 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div id="editors" class="space-y-4">
|
<div id="editors" class="space-y-4">
|
||||||
{{ range $filename, $content := .files }}
|
{{ range $file := .files }}
|
||||||
<div class="rounded-md border border-1 border-gray-700 editor">
|
<div class="rounded-md border border-1 border-gray-700 editor">
|
||||||
<div class="border-b-1 border-gray-700 bg-gray-800 my-auto">
|
<div class="border-b-1 border-gray-700 bg-gray-800 my-auto">
|
||||||
<p class="mx-2 my-2 inline-flex">
|
<p class="mx-2 my-2 inline-flex">
|
||||||
<input type="text" value="{{ $filename }}" name="name" placeholder="Filename with extension" style="line-height: 0.05em; z-index: 99999" class="form-filename bg-gray-900 shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-700 rounded-l-md">
|
<input type="text" value="{{ $file.Filename }}" name="name" placeholder="Filename with extension" style="line-height: 0.05em; z-index: 99999" class="form-filename bg-gray-900 shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-700 rounded-l-md">
|
||||||
<button style="line-height: 0.05em" class="delete-file -ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-700 text-sm font-medium rounded-r-md text-slate-300 bg-gray-800 hover:bg-gray-900 focus:outline-none" type="button">
|
<button style="line-height: 0.05em" class="delete-file -ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-700 text-sm font-medium rounded-r-md text-slate-300 bg-gray-800 hover:bg-gray-900 focus:outline-none" type="button">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" value="{{ $content }}" name="content" class="form-filecontent">
|
<input type="hidden" value="{{ $file.Content }}" name="content" class="form-filecontent">
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
23
templates/pages/gist.html
vendored
23
templates/pages/gist.html
vendored
|
@ -2,30 +2,35 @@
|
||||||
{{ template "gist_header" .}}
|
{{ template "gist_header" .}}
|
||||||
{{ if .files }}
|
{{ if .files }}
|
||||||
<div class="grid gap-y-4">
|
<div class="grid gap-y-4">
|
||||||
{{ range $filename, $content := .files }}
|
{{ range $file := .files }}
|
||||||
<div class="rounded-md border border-1 border-gray-700 overflow-auto">
|
<div class="rounded-md border border-1 border-gray-700 overflow-auto">
|
||||||
<div class="border-b-1 border-gray-700 bg-gray-800 my-auto block">
|
<div class="border-b-1 border-gray-700 bg-gray-800 my-auto block">
|
||||||
<div class="ml-4 py-1.5 flex">
|
<div class="ml-4 py-1.5 flex">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 flex text-slate-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 flex text-slate-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
||||||
</svg>
|
</svg>
|
||||||
<span class="flex-auto ml-2 text-sm text-slate-300 filename" id="file-{{ slug $filename }}"><a href="#file-{{ slug $filename }}" class="text-slate-300 hover:text-white">{{ $filename }}</a></span>
|
<span class="flex-auto ml-2 text-sm text-slate-300 filename" id="file-{{ slug $file.Filename }}"><a href="#file-{{ slug $file.Filename }}" class="text-slate-300 hover:text-white">{{ $file.Filename }}</a></span>
|
||||||
|
|
||||||
<button class="float-right mx-2 px-2.5 py-0.5 leading-4 rounded-md text-xs font-medium bg-gray-600 border border-gray-500 hover:bg-gray-700 hover:text-slate-300 select-none copy-gist-btn"> Copy </button>
|
<button class="float-right mx-2 px-2.5 py-0.5 leading-4 rounded-md text-xs font-medium bg-gray-600 border border-gray-500 hover:bg-gray-700 hover:text-slate-300 select-none copy-gist-btn"> Copy </button>
|
||||||
<a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/raw/{{ $.commit }}/{{$filename}}" class="text-slate-300 float-right mr-2 px-2.5 py-0.5 leading-4 rounded-md text-xs font-medium bg-gray-600 border border-gray-500 hover:bg-gray-700 hover:text-slate-300 select-none"> Raw </a>
|
<a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/raw/{{ $.commit }}/{{$file.Filename}}" class="text-slate-300 float-right mr-2 px-2.5 py-0.5 leading-4 rounded-md text-xs font-medium bg-gray-600 border border-gray-500 hover:bg-gray-700 hover:text-slate-300 select-none"> Raw </a>
|
||||||
<div class="hidden gist-content">{{ $content }}</div>
|
<div class="hidden gist-content">{{ $file.Content }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{ if $file.Truncated }}
|
||||||
|
<div class="text-sm px-4 py-1.5 border-t-1 border-gray-700">
|
||||||
|
This file has been truncated. <a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/raw/{{ $.commit }}/{{$file.Filename}}">View the full file.</a>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<div class="code overflow-auto">
|
<div class="code overflow-auto">
|
||||||
{{ if isMarkdown $filename }}
|
{{ if isMarkdown $file.Filename }}
|
||||||
<div class="markdown markdown-body p-8">{{ $content }}</div>
|
<div class="markdown markdown-body p-8">{{ $file.Content }}</div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
{{ $fileslug := slug $filename }}
|
{{ $fileslug := slug $file.Filename }}
|
||||||
<table class="table-code w-full whitespace-pre" data-filename-slug="{{ $fileslug }}" data-filename="{{ $filename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;">
|
<table class="table-code w-full whitespace-pre" data-filename-slug="{{ $fileslug }}" data-filename="{{ $file.Filename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;">
|
||||||
<tbody>
|
<tbody>
|
||||||
{{ $ii := "1" }}
|
{{ $ii := "1" }}
|
||||||
{{ $i := toInt $ii }}
|
{{ $i := toInt $ii }}
|
||||||
{{ range $line := lines $content }}<tr><td id="file-{{ $fileslug }}-{{$i}}" class="select-none line-num px-4">{{$i}}</td><td class="line-code">{{ $line }}</td></tr>{{ $i = inc $i }}{{ end }}
|
{{ range $line := lines $file.Content }}<tr><td id="file-{{ $fileslug }}-{{$i}}" class="select-none line-num px-4">{{$i}}</td><td class="line-code">{{ $line }}</td></tr>{{ $i = inc $i }}{{ end }}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
Loading…
Reference in a new issue