| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 | 
							- // Copyright 2020 The Gogs Authors. All rights reserved.
 
- // Use of this source code is governed by a MIT-style
 
- // license that can be found in the LICENSE file.
 
- package lfs
 
- import (
 
- 	"fmt"
 
- 	"net/http"
 
- 	jsoniter "github.com/json-iterator/go"
 
- 	log "unknwon.dev/clog/v2"
 
- 	"gogs.io/gogs/internal/conf"
 
- 	"gogs.io/gogs/internal/context"
 
- 	"gogs.io/gogs/internal/db"
 
- 	"gogs.io/gogs/internal/lfsutil"
 
- 	"gogs.io/gogs/internal/strutil"
 
- )
 
- // POST /{owner}/{repo}.git/info/lfs/object/batch
 
- func serveBatch(c *context.Context, owner *db.User, repo *db.Repository) {
 
- 	var request batchRequest
 
- 	defer c.Req.Request.Body.Close()
 
- 	err := jsoniter.NewDecoder(c.Req.Request.Body).Decode(&request)
 
- 	if err != nil {
 
- 		responseJSON(c.Resp, http.StatusBadRequest, responseError{
 
- 			Message: strutil.ToUpperFirst(err.Error()),
 
- 		})
 
- 		return
 
- 	}
 
- 	// NOTE: We only support basic transfer as of now.
 
- 	transfer := transferBasic
 
- 	// Example: https://try.gogs.io/gogs/gogs.git/info/lfs/object/basic
 
- 	baseHref := fmt.Sprintf("%s%s/%s.git/info/lfs/objects/basic", conf.Server.ExternalURL, owner.Name, repo.Name)
 
- 	objects := make([]batchObject, 0, len(request.Objects))
 
- 	switch request.Operation {
 
- 	case basicOperationUpload:
 
- 		for _, obj := range request.Objects {
 
- 			var actions batchActions
 
- 			if lfsutil.ValidOID(obj.Oid) {
 
- 				actions = batchActions{
 
- 					Upload: &batchAction{
 
- 						Href: fmt.Sprintf("%s/%s", baseHref, obj.Oid),
 
- 					},
 
- 					Verify: &batchAction{
 
- 						Href: fmt.Sprintf("%s/verify", baseHref),
 
- 					},
 
- 				}
 
- 			} else {
 
- 				actions = batchActions{
 
- 					Error: &batchError{
 
- 						Code:    http.StatusUnprocessableEntity,
 
- 						Message: "Object has invalid oid",
 
- 					},
 
- 				}
 
- 			}
 
- 			objects = append(objects, batchObject{
 
- 				Oid:     obj.Oid,
 
- 				Size:    obj.Size,
 
- 				Actions: actions,
 
- 			})
 
- 		}
 
- 	case basicOperationDownload:
 
- 		oids := make([]lfsutil.OID, 0, len(request.Objects))
 
- 		for _, obj := range request.Objects {
 
- 			oids = append(oids, obj.Oid)
 
- 		}
 
- 		stored, err := db.LFS.GetObjectsByOIDs(repo.ID, oids...)
 
- 		if err != nil {
 
- 			internalServerError(c.Resp)
 
- 			log.Error("Failed to get objects [repo_id: %d, oids: %v]: %v", repo.ID, oids, err)
 
- 			return
 
- 		}
 
- 		storedSet := make(map[lfsutil.OID]*db.LFSObject, len(stored))
 
- 		for _, obj := range stored {
 
- 			storedSet[obj.OID] = obj
 
- 		}
 
- 		for _, obj := range request.Objects {
 
- 			var actions batchActions
 
- 			if stored := storedSet[obj.Oid]; stored != nil {
 
- 				if stored.Size != obj.Size {
 
- 					actions.Error = &batchError{
 
- 						Code:    http.StatusUnprocessableEntity,
 
- 						Message: "Object size mismatch",
 
- 					}
 
- 				} else {
 
- 					actions.Download = &batchAction{
 
- 						Href: fmt.Sprintf("%s/%s", baseHref, obj.Oid),
 
- 					}
 
- 				}
 
- 			} else {
 
- 				actions.Error = &batchError{
 
- 					Code:    http.StatusNotFound,
 
- 					Message: "Object does not exist",
 
- 				}
 
- 			}
 
- 			objects = append(objects, batchObject{
 
- 				Oid:     obj.Oid,
 
- 				Size:    obj.Size,
 
- 				Actions: actions,
 
- 			})
 
- 		}
 
- 	default:
 
- 		responseJSON(c.Resp, http.StatusBadRequest, responseError{
 
- 			Message: "Operation not recognized",
 
- 		})
 
- 		return
 
- 	}
 
- 	responseJSON(c.Resp, http.StatusOK, batchResponse{
 
- 		Transfer: transfer,
 
- 		Objects:  objects,
 
- 	})
 
- }
 
- // batchRequest defines the request payload for the batch endpoint.
 
- type batchRequest struct {
 
- 	Operation string `json:"operation"`
 
- 	Objects   []struct {
 
- 		Oid  lfsutil.OID `json:"oid"`
 
- 		Size int64       `json:"size"`
 
- 	} `json:"objects"`
 
- }
 
- type batchError struct {
 
- 	Code    int    `json:"code"`
 
- 	Message string `json:"message"`
 
- }
 
- type batchAction struct {
 
- 	Href string `json:"href"`
 
- }
 
- type batchActions struct {
 
- 	Download *batchAction `json:"download,omitempty"`
 
- 	Upload   *batchAction `json:"upload,omitempty"`
 
- 	Verify   *batchAction `json:"verify,omitempty"`
 
- 	Error    *batchError  `json:"error,omitempty"`
 
- }
 
- type batchObject struct {
 
- 	Oid     lfsutil.OID  `json:"oid"`
 
- 	Size    int64        `json:"size"`
 
- 	Actions batchActions `json:"actions"`
 
- }
 
- // batchResponse defines the response payload for the batch endpoint.
 
- type batchResponse struct {
 
- 	Transfer string        `json:"transfer"`
 
- 	Objects  []batchObject `json:"objects"`
 
- }
 
- type responseError struct {
 
- 	Message string `json:"message"`
 
- }
 
- const contentType = "application/vnd.git-lfs+json"
 
- func responseJSON(w http.ResponseWriter, status int, v interface{}) {
 
- 	w.Header().Set("Content-Type", contentType)
 
- 	w.WriteHeader(status)
 
- 	err := jsoniter.NewEncoder(w).Encode(v)
 
- 	if err != nil {
 
- 		log.Error("Failed to encode JSON: %v", err)
 
- 		return
 
- 	}
 
- }
 
- func internalServerError(w http.ResponseWriter) {
 
- 	responseJSON(w, http.StatusInternalServerError, responseError{
 
- 		Message: "Internal server error",
 
- 	})
 
- }
 
 
  |