mirror of
https://github.com/thomiceli/opengist.git
synced 2025-06-19 08:27:13 +02:00
Added forks
This commit is contained in:
@ -10,28 +10,21 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetRepositoryPath(user string, gist string) (string, error) {
|
||||
return filepath.Join(config.GetHomeDir(), "repos", strings.ToLower(user), gist), nil
|
||||
func RepositoryPath(user string, gist string) string {
|
||||
return filepath.Join(config.GetHomeDir(), "repos", strings.ToLower(user), gist)
|
||||
}
|
||||
|
||||
func getTmpRepositoryPath(gistId string) (string, error) {
|
||||
dirname, err := getTmpRepositoriesPath()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(dirname, gistId), nil
|
||||
func TmpRepositoryPath(gistId string) string {
|
||||
dirname := TmpRepositoriesPath()
|
||||
return filepath.Join(dirname, gistId)
|
||||
}
|
||||
|
||||
func getTmpRepositoriesPath() (string, error) {
|
||||
return filepath.Join(config.GetHomeDir(), "tmp", "repos"), nil
|
||||
func TmpRepositoriesPath() string {
|
||||
return filepath.Join(config.GetHomeDir(), "tmp", "repos")
|
||||
}
|
||||
|
||||
func InitRepository(user string, gist string) error {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
cmd := exec.Command(
|
||||
"git",
|
||||
@ -40,7 +33,7 @@ func InitRepository(user string, gist string) error {
|
||||
repositoryPath,
|
||||
)
|
||||
|
||||
_, err = cmd.Output()
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -69,10 +62,7 @@ func InitRepository(user string, gist string) error {
|
||||
}
|
||||
|
||||
func GetNumberOfCommitsOfRepository(user string, gist string) (string, error) {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
cmd := exec.Command(
|
||||
"git",
|
||||
@ -87,10 +77,7 @@ func GetNumberOfCommitsOfRepository(user string, gist string) (string, error) {
|
||||
}
|
||||
|
||||
func GetFilesOfRepository(user string, gist string, commit string) ([]string, error) {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
cmd := exec.Command(
|
||||
"git",
|
||||
@ -110,10 +97,7 @@ func GetFilesOfRepository(user string, gist string, commit string) ([]string, er
|
||||
}
|
||||
|
||||
func GetFileContent(user string, gist string, commit string, filename string) (string, error) {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
cmd := exec.Command(
|
||||
"git",
|
||||
@ -128,10 +112,7 @@ func GetFileContent(user string, gist string, commit string, filename string) (s
|
||||
}
|
||||
|
||||
func GetLog(user string, gist string, skip string) (string, error) {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
cmd := exec.Command(
|
||||
"git",
|
||||
@ -156,19 +137,13 @@ func GetLog(user string, gist string, skip string) (string, error) {
|
||||
}
|
||||
|
||||
func CloneTmp(user string, gist string, gistTmpId string) error {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
tmpPath, err := getTmpRepositoriesPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpPath := TmpRepositoriesPath()
|
||||
|
||||
tmpRepositoryPath := path.Join(tmpPath, gistTmpId)
|
||||
|
||||
err = os.RemoveAll(tmpRepositoryPath)
|
||||
err := os.RemoveAll(tmpRepositoryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -191,25 +166,33 @@ func CloneTmp(user string, gist string, gistTmpId string) error {
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func SetFileContent(gistTmpId string, filename string, content string) error {
|
||||
repositoryPath, err := getTmpRepositoryPath(gistTmpId)
|
||||
if err != nil {
|
||||
func ForkClone(userSrc string, gistSrc string, userDst string, gistDst string) error {
|
||||
repositoryPathSrc := RepositoryPath(userSrc, gistSrc)
|
||||
repositoryPathDst := RepositoryPath(userDst, gistDst)
|
||||
|
||||
cmd := exec.Command("git", "clone", "--bare", repositoryPathSrc, repositoryPathDst)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd = exec.Command("git", "config", "user.name", userDst)
|
||||
cmd.Dir = repositoryPathDst
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func SetFileContent(gistTmpId string, filename string, content string) error {
|
||||
repositoryPath := TmpRepositoryPath(gistTmpId)
|
||||
|
||||
return os.WriteFile(filepath.Join(repositoryPath, filename), []byte(content), 0644)
|
||||
}
|
||||
|
||||
func AddAll(gistTmpId string) error {
|
||||
tmpPath, err := getTmpRepositoryPath(gistTmpId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpPath := TmpRepositoryPath(gistTmpId)
|
||||
|
||||
// in case of a change where only a file name has its case changed
|
||||
cmd := exec.Command("git", "rm", "-r", "--cached", "--ignore-unmatch", ".")
|
||||
cmd.Dir = tmpPath
|
||||
err = cmd.Run()
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -222,27 +205,21 @@ func AddAll(gistTmpId string) error {
|
||||
|
||||
func Commit(gistTmpId string) error {
|
||||
cmd := exec.Command("git", "commit", "--allow-empty", "-m", `"Opengist commit"`)
|
||||
tmpPath, err := getTmpRepositoryPath(gistTmpId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpPath := TmpRepositoryPath(gistTmpId)
|
||||
cmd.Dir = tmpPath
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func Push(gistTmpId string) error {
|
||||
tmpRepositoryPath, err := getTmpRepositoryPath(gistTmpId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpRepositoryPath := TmpRepositoryPath(gistTmpId)
|
||||
cmd := exec.Command(
|
||||
"git",
|
||||
"push",
|
||||
)
|
||||
cmd.Dir = tmpRepositoryPath
|
||||
|
||||
err = cmd.Run()
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -255,10 +232,7 @@ func DeleteRepository(user string, gist string) error {
|
||||
}
|
||||
|
||||
func UpdateServerInfo(user string, gist string) error {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
cmd := exec.Command("git", "update-server-info")
|
||||
cmd.Dir = repositoryPath
|
||||
@ -266,10 +240,7 @@ func UpdateServerInfo(user string, gist string) error {
|
||||
}
|
||||
|
||||
func RPCRefs(user string, gist string, service string) ([]byte, error) {
|
||||
repositoryPath, err := GetRepositoryPath(user, gist)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repositoryPath := RepositoryPath(user, gist)
|
||||
|
||||
cmd := exec.Command("git", service, "--stateless-rpc", "--advertise-refs", ".")
|
||||
cmd.Dir = repositoryPath
|
||||
|
@ -16,10 +16,13 @@ type Gist struct {
|
||||
User User `validate:"-"`
|
||||
NbFiles int
|
||||
NbLikes int
|
||||
NbForks int
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
|
||||
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"`
|
||||
ForkedID uint
|
||||
|
||||
Files []File `gorm:"-" validate:"min=1,dive"`
|
||||
}
|
||||
@ -40,7 +43,7 @@ type Commit struct {
|
||||
|
||||
func GetGist(user string, gistUuid string) (*Gist, error) {
|
||||
gist := new(Gist)
|
||||
err := db.Preload("User").
|
||||
err := db.Preload("User").Preload("Forked.User").
|
||||
Where("gists.uuid = ? AND users.username like ?", gistUuid, user).
|
||||
Joins("join users on gists.user_id = users.id").
|
||||
First(&gist).Error
|
||||
@ -50,7 +53,7 @@ func GetGist(user string, gistUuid string) (*Gist, error) {
|
||||
|
||||
func GetGistByID(gistId string) (*Gist, error) {
|
||||
gist := new(Gist)
|
||||
err := db.Preload("User").
|
||||
err := db.Preload("User").Preload("Forked.User").
|
||||
Where("gists.id = ?", gistId).
|
||||
First(&gist).Error
|
||||
|
||||
@ -59,7 +62,7 @@ func GetGistByID(gistId string) (*Gist, error) {
|
||||
|
||||
func GetAllGistsForCurrentUser(currentUserId uint, offset int, sort string, order string) ([]*Gist, error) {
|
||||
var gists []*Gist
|
||||
err := db.Preload("User").
|
||||
err := db.Preload("User").Preload("Forked.User").
|
||||
Where("gists.private = 0 or gists.user_id = ?", currentUserId).
|
||||
Limit(11).
|
||||
Offset(offset * 10).
|
||||
@ -82,7 +85,7 @@ func GetAllGists(offset int) ([]*Gist, error) {
|
||||
|
||||
func GetAllGistsFromUser(fromUser string, currentUserId uint, offset int, sort string, order string) ([]*Gist, error) {
|
||||
var gists []*Gist
|
||||
err := db.Preload("User").
|
||||
err := db.Preload("User").Preload("Forked.User").
|
||||
Where("users.username = ? and ((gists.private = 0) or (gists.private = 1 and gists.user_id = ?))", fromUser, currentUserId).
|
||||
Joins("join users on gists.user_id = users.id").
|
||||
Limit(11).
|
||||
@ -94,11 +97,16 @@ func GetAllGistsFromUser(fromUser string, currentUserId uint, offset int, sort s
|
||||
}
|
||||
|
||||
func CreateGist(gist *Gist) error {
|
||||
// avoids foreign key constraint error because the default value in the struct is 0
|
||||
return db.Omit("forked_id").Create(&gist).Error
|
||||
}
|
||||
|
||||
func CreateForkedGist(gist *Gist) error {
|
||||
return db.Create(&gist).Error
|
||||
}
|
||||
|
||||
func UpdateGist(gist *Gist) error {
|
||||
return db.Save(&gist).Error
|
||||
return db.Omit("forked_id").Save(&gist).Error
|
||||
}
|
||||
|
||||
func DeleteGist(gist *Gist) error {
|
||||
@ -112,16 +120,40 @@ func GistLastActiveNow(gistID uint) error {
|
||||
}
|
||||
|
||||
func AppendUserLike(gist *Gist, user *User) error {
|
||||
db.Model(&gist).Omit("updated_at").Update("nb_likes", gist.NbLikes+1)
|
||||
err := db.Model(&gist).Omit("updated_at").Update("nb_likes", gist.NbLikes+1).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.Model(&gist).Omit("updated_at").Association("Likes").Append(user)
|
||||
}
|
||||
|
||||
func RemoveUserLike(gist *Gist, user *User) error {
|
||||
db.Model(&gist).Omit("updated_at").Update("nb_likes", gist.NbLikes-1)
|
||||
err := db.Model(&gist).Omit("updated_at").Update("nb_likes", gist.NbLikes-1).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.Model(&gist).Omit("updated_at").Association("Likes").Delete(user)
|
||||
}
|
||||
|
||||
func GetUsersLikesForGists(gist *Gist, offset int) ([]*User, error) {
|
||||
func IncrementGistForkCount(gist *Gist) error {
|
||||
return db.Model(&gist).Omit("updated_at").Update("nb_forks", gist.NbForks+1).Error
|
||||
}
|
||||
|
||||
func DecrementGistForkCount(gist *Gist) error {
|
||||
return db.Model(&gist).Omit("updated_at").Update("nb_forks", gist.NbForks-1).Error
|
||||
}
|
||||
|
||||
func GetForkedGist(gist *Gist, user *User) (*Gist, error) {
|
||||
fork := new(Gist)
|
||||
err := db.Preload("User").
|
||||
Where("forked_id = ? and user_id = ?", gist.ID, user.ID).
|
||||
First(&fork).Error
|
||||
return fork, err
|
||||
}
|
||||
|
||||
func GetUsersLikesForGist(gist *Gist, offset int) ([]*User, error) {
|
||||
var users []*User
|
||||
err := db.Model(&gist).
|
||||
Where("gist_id = ?", gist.ID).
|
||||
@ -131,6 +163,19 @@ func GetUsersLikesForGists(gist *Gist, offset int) ([]*User, error) {
|
||||
return users, err
|
||||
}
|
||||
|
||||
func GetUsersForksForGist(gist *Gist, currentUserId uint, offset int) ([]*Gist, error) {
|
||||
var gists []*Gist
|
||||
err := db.Model(&gist).Preload("User").
|
||||
Where("forked_id = ?", gist.ID).
|
||||
Where("(gists.private = 0) or (gists.private = 1 and gists.user_id = ?)", currentUserId).
|
||||
Limit(11).
|
||||
Offset(offset * 10).
|
||||
Order("updated_at desc").
|
||||
Find(&gists).Error
|
||||
|
||||
return gists, err
|
||||
}
|
||||
|
||||
func UserCanWrite(user *User, gist *Gist) bool {
|
||||
return !(user == nil) && (gist.UserID == user.ID)
|
||||
}
|
||||
|
@ -53,11 +53,7 @@ func runGitCommand(ch ssh.Channel, gitCmd string, keyID uint) error {
|
||||
|
||||
_ = models.SSHKeyLastUsedNow(keyID)
|
||||
|
||||
repositoryPath, err := git.GetRepositoryPath(gist.User.Username, gist.Uuid)
|
||||
if err != nil {
|
||||
errorSsh("Failed to get repository path", err)
|
||||
return errors.New("internal server error")
|
||||
}
|
||||
repositoryPath := git.RepositoryPath(gist.User.Username, gist.Uuid)
|
||||
|
||||
cmd := exec.Command("git", verb, repositoryPath)
|
||||
cmd.Dir = repositoryPath
|
||||
|
@ -3,8 +3,10 @@ package web
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"gorm.io/gorm"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"opengist/internal/config"
|
||||
@ -413,6 +415,49 @@ func like(ctx echo.Context) error {
|
||||
return redirect(ctx, redirectTo)
|
||||
}
|
||||
|
||||
func fork(ctx echo.Context) error {
|
||||
var gist = getData(ctx, "gist").(*models.Gist)
|
||||
currentUser := getUserLogged(ctx)
|
||||
|
||||
alreadyForked, err := models.GetForkedGist(gist, currentUser)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errorRes(500, "Error checking if gist is already forked", err)
|
||||
}
|
||||
|
||||
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 := &models.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,
|
||||
}
|
||||
|
||||
if err = models.CreateForkedGist(newGist); err != nil {
|
||||
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 {
|
||||
return errorRes(500, "Error cloning the repository while forking", err)
|
||||
}
|
||||
if err = models.IncrementGistForkCount(gist); err != nil {
|
||||
return errorRes(500, "Error incrementing the fork count", err)
|
||||
}
|
||||
|
||||
return redirect(ctx, "/"+currentUser.Username+"/"+newGist.Uuid)
|
||||
}
|
||||
|
||||
func rawFile(ctx echo.Context) error {
|
||||
gist := getData(ctx, "gist").(*models.Gist)
|
||||
fileContent, err := git.GetFileContent(
|
||||
@ -505,7 +550,7 @@ func likes(ctx echo.Context) error {
|
||||
|
||||
pageInt := getPage(ctx)
|
||||
|
||||
likers, err := models.GetUsersLikesForGists(gist, pageInt-1)
|
||||
likers, err := models.GetUsersLikesForGist(gist, pageInt-1)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error getting users who liked this gist", err)
|
||||
}
|
||||
@ -518,3 +563,27 @@ func likes(ctx echo.Context) error {
|
||||
setData(ctx, "revision", "HEAD")
|
||||
return html(ctx, "likes.html")
|
||||
}
|
||||
|
||||
func forks(ctx echo.Context) error {
|
||||
var gist = getData(ctx, "gist").(*models.Gist)
|
||||
pageInt := getPage(ctx)
|
||||
|
||||
currentUser := getUserLogged(ctx)
|
||||
var fromUserID uint = 0
|
||||
if currentUser != nil {
|
||||
fromUserID = currentUser.ID
|
||||
}
|
||||
|
||||
forks, err := models.GetUsersForksForGist(gist, 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"); 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")
|
||||
}
|
||||
|
@ -50,12 +50,9 @@ func gitHttp(ctx echo.Context) error {
|
||||
strings.HasSuffix(ctx.Request().URL.Path, "git-upload-pack") ||
|
||||
ctx.Request().Method == "GET"
|
||||
|
||||
repositoryPath, err := git.GetRepositoryPath(gist.User.Username, gist.Uuid)
|
||||
if err != nil {
|
||||
return errorRes(500, "Cannot get repository path", err)
|
||||
}
|
||||
repositoryPath := git.RepositoryPath(gist.User.Username, gist.Uuid)
|
||||
|
||||
if _, err = os.Stat(repositoryPath); os.IsNotExist(err) {
|
||||
if _, err := os.Stat(repositoryPath); os.IsNotExist(err) {
|
||||
if err != nil {
|
||||
return errorRes(500, "Repository does not exist", err)
|
||||
}
|
||||
|
@ -159,6 +159,8 @@ func Start() {
|
||||
g3.POST("/edit", processCreate, logged, writePermission)
|
||||
g3.POST("/like", like, logged)
|
||||
g3.GET("/likes", likes)
|
||||
g3.POST("/fork", fork, logged)
|
||||
g3.GET("/forks", forks)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user