Skip to content

Commit 50e16d6

Browse files
Endpoint for multi-files commits (#10)
* Add `createFiles` endpoint. * Add skeleton for uploading multiple files endpoint. - It uses the same basis as the method used to upload files from gitea itself yet it returns JSON instead of html. * fixup! Add skeleton for uploading multiple files endpoint. - Use the new endpoint not the old one. * Revert "Add `createFiles` endpoint." This reverts commit fe9b65d * fixup! Add skeleton for uploading multiple files endpoint. * Add protection handler for the `{repo}/upload/` endpoint. * Fix comment about renderCommitRights Co-authored-by: Kaspar Emanuel <[email protected]>
1 parent 685b85b commit 50e16d6

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

routers/web/repo/editor.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,157 @@ func UploadFilePost(ctx *context.Context) {
729729
}
730730
}
731731

732+
// UploadFilePostJson JSON response for uploading file
733+
func UploadFilePostJson(ctx *context.Context) {
734+
// `renderCommitRights` is a poorly named; it's not responsible directly for rendering anything,
735+
// it returns a boolean
736+
form := web.GetForm(ctx).(*auth.UploadRepoFileForm)
737+
canCommit := renderCommitRights(ctx)
738+
oldBranchName := ctx.Repo.BranchName
739+
branchName := oldBranchName
740+
741+
if form.CommitChoice == frmCommitChoiceNewBranch {
742+
branchName = form.NewBranchName
743+
}
744+
745+
form.TreePath = cleanUploadFileName(form.TreePath)
746+
747+
treeNames, _ := getParentTreeFields(form.TreePath)
748+
if len(treeNames) == 0 {
749+
// We must at least have one element for user to input.
750+
treeNames = []string{""}
751+
}
752+
response := make(map[string]string)
753+
754+
if ctx.HasError() {
755+
response["message"] = "Failed to commit the files"
756+
ctx.JSON(http.StatusUnprocessableEntity, response)
757+
log.Error("failed to commit files")
758+
return
759+
}
760+
761+
if oldBranchName != branchName {
762+
if _, err := repo_module.GetBranch(ctx.Repo.Repository, branchName); err == nil {
763+
response["message"] = "Branch already exists"
764+
ctx.JSON(http.StatusConflict, response)
765+
return
766+
}
767+
} else if !canCommit {
768+
response["message"] = "Can't commit to protected branch"
769+
ctx.JSON(http.StatusUnauthorized, response)
770+
return
771+
}
772+
773+
var newTreePath string
774+
for _, part := range treeNames {
775+
newTreePath = path.Join(newTreePath, part)
776+
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(newTreePath)
777+
if err != nil {
778+
if git.IsErrNotExist(err) {
779+
// Means there is no item with that name, so we're good
780+
break
781+
}
782+
response["message"] = "Something went wrong!"
783+
ctx.JSON(http.StatusInternalServerError, response)
784+
return
785+
}
786+
787+
// User can only upload files to a directory.
788+
if !entry.IsDir() {
789+
ctx.Data["Err_TreePath"] = true
790+
ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", part), tplUploadFile, &form)
791+
response["message"] = "A file with the same name of the new directory already exists."
792+
ctx.JSON(http.StatusConflict, response)
793+
return
794+
}
795+
}
796+
797+
message := strings.TrimSpace(form.CommitSummary)
798+
if len(message) == 0 {
799+
message = ctx.Tr("repo.editor.upload_files_to_dir", form.TreePath)
800+
}
801+
802+
form.CommitMessage = strings.TrimSpace(form.CommitMessage)
803+
if len(form.CommitMessage) > 0 {
804+
message += "\n\n" + form.CommitMessage
805+
}
806+
807+
if err := repofiles.UploadRepoFiles(ctx.Repo.Repository, ctx.User, &repofiles.UploadRepoFileOptions{
808+
LastCommitID: ctx.Repo.CommitID,
809+
OldBranch: oldBranchName,
810+
NewBranch: branchName,
811+
TreePath: form.TreePath,
812+
Message: message,
813+
Files: form.Files,
814+
}); err != nil {
815+
if models.IsErrLFSFileLocked(err) {
816+
response["message"] = ctx.Tr("repo.editor.upload_file_is_locked")
817+
ctx.JSON(http.StatusUnauthorized, response)
818+
} else if models.IsErrFilenameInvalid(err) {
819+
response["message"] = ctx.Tr("repo.editor.filename_is_invalid")
820+
ctx.JSON(http.StatusUnprocessableEntity, response)
821+
} else if models.IsErrFilePathInvalid(err) {
822+
fileErr := err.(models.ErrFilePathInvalid)
823+
switch fileErr.Type {
824+
case git.EntryModeSymlink:
825+
response["message"] = ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path)
826+
ctx.JSON(http.StatusConflict, response)
827+
case git.EntryModeTree:
828+
response["message"] = ctx.Tr("repo.editor.filename_is_a_directory", fileErr.Path)
829+
ctx.JSON(http.StatusConflict, response)
830+
case git.EntryModeBlob:
831+
response["message"] = ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path)
832+
ctx.JSON(http.StatusConflict, response)
833+
default:
834+
response["message"] = "Something went wrong"
835+
ctx.JSON(http.StatusInternalServerError, response)
836+
}
837+
} else if models.IsErrRepoFileAlreadyExists(err) {
838+
response["message"] = ctx.Tr("repo.editor.file_already_exists", form.TreePath)
839+
ctx.JSON(http.StatusConflict, response)
840+
} else if git.IsErrBranchNotExist(err) {
841+
branchErr := err.(git.ErrBranchNotExist)
842+
response["message"] = ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name)
843+
ctx.JSON(http.StatusNotFound, response)
844+
} else if models.IsErrBranchAlreadyExists(err) {
845+
// For when a user specifies a new branch that already exists
846+
branchErr := err.(models.ErrBranchAlreadyExists)
847+
response["message"] = ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName)
848+
ctx.JSON(http.StatusConflict, response)
849+
} else if git.IsErrPushOutOfDate(err) {
850+
response["message"] = ctx.Tr(
851+
"repo.editor.file_changed_while_editing",
852+
ctx.Repo.RepoLink+"/compare/"+ctx.Repo.CommitID+"..."+form.NewBranchName,
853+
)
854+
ctx.JSON(http.StatusConflict, response)
855+
} else if git.IsErrPushRejected(err) {
856+
errPushRej := err.(*git.ErrPushRejected)
857+
if len(errPushRej.Message) == 0 {
858+
response["message"] = ctx.Tr("repo.editor.push_rejected_no_message")
859+
ctx.JSON(http.StatusConflict, response)
860+
} else {
861+
response["response"] = ctx.Tr("repo.editor.push_rejected", utils.SanitizeFlashErrorString(errPushRej.Message))
862+
ctx.JSON(http.StatusConflict, response)
863+
}
864+
} else {
865+
// os.ErrNotExist - upload file missing in the intervening time?!
866+
log.Error(
867+
"Error during upload to repo: %-v to filepath: %s on %s from %s: %v",
868+
ctx.Repo.Repository,
869+
form.TreePath,
870+
oldBranchName,
871+
form.NewBranchName,
872+
err,
873+
)
874+
response["message"] = ctx.Tr("repo.editor.unable_to_upload_files", form.TreePath, err)
875+
ctx.JSON(http.StatusInternalServerError, response)
876+
}
877+
return
878+
}
879+
response["message"] = "Committed files successfully."
880+
ctx.JSON(http.StatusCreated, response)
881+
}
882+
732883
func cleanUploadFileName(name string) string {
733884
// Rebase the filename
734885
name = strings.Trim(path.Clean("/"+name), " /")

routers/web/web.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,8 @@ func RegisterRoutes(m *web.Route) {
790790
m.Combo("/_upload/*", repo.MustBeAbleToUpload).
791791
Get(repo.UploadFile).
792792
Post(bindIgnErr(forms.UploadRepoFileForm{}), repo.UploadFilePost)
793+
// Same as `/_upload/*` but returns JSON
794+
m.Post("/upload/*", repo.MustBeAbleToUpload, bindIgnErr(forms.UploadRepoFileForm{}), repo.UploadFilePostJson)
793795
}, context.RepoRefByType(context.RepoRefBranch), repo.MustBeEditable)
794796
m.Group("", func() {
795797
m.Post("/upload-file", repo.UploadFileToServer)

0 commit comments

Comments
 (0)