2014-04-16 00:27:29 +08:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2018-11-18 19:45:40 +01:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2014-04-16 00:27:29 +08:00
package repo
import (
2022-05-09 16:54:51 +01:00
"path"
"time"
2022-06-12 23:51:54 +08:00
git_model "code.gitea.io/gitea/models/git"
2019-03-27 17:33:00 +08:00
"code.gitea.io/gitea/modules/git"
2021-04-12 16:49:26 +02:00
"code.gitea.io/gitea/modules/httpcache"
2019-02-12 15:09:43 +00:00
"code.gitea.io/gitea/modules/lfs"
2019-06-12 21:41:28 +02:00
"code.gitea.io/gitea/modules/log"
2021-08-22 02:22:06 +08:00
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
2021-06-09 07:33:54 +08:00
"code.gitea.io/gitea/routers/common"
2024-02-27 15:12:22 +08:00
"code.gitea.io/gitea/services/context"
2014-04-16 00:27:29 +08:00
)
2019-02-12 15:09:43 +00:00
// ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
2023-07-07 07:31:56 +02:00
func ServeBlobOrLFS ( ctx * context . Context , blob * git . Blob , lastModified * time . Time ) error {
2022-05-09 16:54:51 +01:00
if httpcache . HandleGenericETagTimeCache ( ctx . Req , ctx . Resp , ` " ` + blob . ID . String ( ) + ` " ` , lastModified ) {
2021-04-12 16:49:26 +02:00
return nil
}
2019-02-12 15:09:43 +00:00
dataRc , err := blob . DataAsync ( )
if err != nil {
return err
}
2021-05-10 02:27:03 +01:00
closed := false
2019-06-12 21:41:28 +02:00
defer func ( ) {
2021-05-10 02:27:03 +01:00
if closed {
return
}
2019-06-12 21:41:28 +02:00
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
} ( )
2019-02-12 15:09:43 +00:00
2021-04-09 00:25:57 +02:00
pointer , _ := lfs . ReadPointer ( dataRc )
if pointer . IsValid ( ) {
2023-01-09 11:50:54 +08:00
meta , _ := git_model . GetLFSMetaObjectByOid ( ctx , ctx . Repo . Repository . ID , pointer . Oid )
2019-02-12 15:09:43 +00:00
if meta == nil {
2021-05-10 02:27:03 +01:00
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
closed = true
2023-05-21 09:50:53 +08:00
return common . ServeBlob ( ctx . Base , ctx . Repo . TreePath , blob , lastModified )
2019-02-12 15:09:43 +00:00
}
2021-04-12 16:49:26 +02:00
if httpcache . HandleGenericETagCache ( ctx . Req , ctx . Resp , ` " ` + pointer . Oid + ` " ` ) {
return nil
}
2021-08-22 02:22:06 +08:00
2024-05-30 15:33:50 +08:00
if setting . LFS . Storage . ServeDirect ( ) {
// If we have a signed url (S3, object storage, blob storage), redirect to this directly.
2021-08-22 02:22:06 +08:00
u , err := storage . LFS . URL ( pointer . RelativePath ( ) , blob . Name ( ) )
if u != nil && err == nil {
ctx . Redirect ( u . String ( ) )
return nil
}
}
2021-04-09 00:25:57 +02:00
lfsDataRc , err := lfs . ReadMetaObject ( meta . Pointer )
2019-02-12 15:09:43 +00:00
if err != nil {
return err
}
2019-10-16 06:55:31 +01:00
defer func ( ) {
if err = lfsDataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
} ( )
2023-05-21 09:50:53 +08:00
common . ServeContentByReadSeeker ( ctx . Base , ctx . Repo . TreePath , lastModified , lfsDataRc )
2023-05-09 15:34:36 +08:00
return nil
2019-02-12 15:09:43 +00:00
}
2021-05-10 02:27:03 +01:00
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
closed = true
2019-02-12 15:09:43 +00:00
2023-05-21 09:50:53 +08:00
return common . ServeBlob ( ctx . Base , ctx . Repo . TreePath , blob , lastModified )
2019-02-12 15:09:43 +00:00
}
2023-07-07 07:31:56 +02:00
func getBlobForEntry ( ctx * context . Context ) ( blob * git . Blob , lastModified * time . Time ) {
2022-05-09 16:54:51 +01:00
entry , err := ctx . Repo . Commit . GetTreeEntryByPath ( ctx . Repo . TreePath )
2014-11-16 21:32:26 -05:00
if err != nil {
2015-12-09 20:46:05 -05:00
if git . IsErrNotExist ( err ) {
2022-05-09 16:54:51 +01:00
ctx . NotFound ( "GetTreeEntryByPath" , err )
2014-11-16 21:32:26 -05:00
} else {
2022-05-09 16:54:51 +01:00
ctx . ServerError ( "GetTreeEntryByPath" , err )
2014-11-16 21:32:26 -05:00
}
2023-07-07 07:31:56 +02:00
return nil , nil
2014-11-16 21:32:26 -05:00
}
2022-05-09 16:54:51 +01:00
if entry . IsDir ( ) || entry . IsSubModule ( ) {
ctx . NotFound ( "getBlobForEntry" , nil )
2023-07-07 07:31:56 +02:00
return nil , nil
2022-05-09 16:54:51 +01:00
}
2022-07-25 16:39:42 +01:00
info , _ , err := git . Entries ( [ ] * git . TreeEntry { entry } ) . GetCommitsInfo ( ctx , ctx . Repo . Commit , path . Dir ( "/" + ctx . Repo . TreePath ) [ 1 : ] )
2022-05-09 16:54:51 +01:00
if err != nil {
ctx . ServerError ( "GetCommitsInfo" , err )
2023-07-07 07:31:56 +02:00
return nil , nil
2022-05-09 16:54:51 +01:00
}
if len ( info ) == 1 {
// Not Modified
2023-07-07 07:31:56 +02:00
lastModified = & info [ 0 ] . Commit . Committer . When
2022-05-09 16:54:51 +01:00
}
blob = entry . Blob ( )
2022-06-20 12:02:49 +02:00
return blob , lastModified
2022-05-09 16:54:51 +01:00
}
// SingleDownload download a file by repos path
func SingleDownload ( ctx * context . Context ) {
blob , lastModified := getBlobForEntry ( ctx )
if blob == nil {
return
}
2023-05-21 09:50:53 +08:00
if err := common . ServeBlob ( ctx . Base , ctx . Repo . TreePath , blob , lastModified ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ServeBlob" , err )
2014-11-16 21:32:26 -05:00
}
2014-04-16 00:27:29 +08:00
}
2018-11-18 19:45:40 +01:00
2019-02-12 15:09:43 +00:00
// SingleDownloadOrLFS download a file by repos path redirecting to LFS if necessary
func SingleDownloadOrLFS ( ctx * context . Context ) {
2022-05-09 16:54:51 +01:00
blob , lastModified := getBlobForEntry ( ctx )
if blob == nil {
2019-02-12 15:09:43 +00:00
return
}
2022-05-09 16:54:51 +01:00
if err := ServeBlobOrLFS ( ctx , blob , lastModified ) ; err != nil {
2019-02-12 15:09:43 +00:00
ctx . ServerError ( "ServeBlobOrLFS" , err )
}
}
2018-11-18 19:45:40 +01:00
// DownloadByID download a file by sha1 ID
func DownloadByID ( ctx * context . Context ) {
2024-06-19 06:32:45 +08:00
blob , err := ctx . Repo . GitRepo . GetBlob ( ctx . PathParam ( "sha" ) )
2018-11-18 19:45:40 +01:00
if err != nil {
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "GetBlob" , nil )
} else {
ctx . ServerError ( "GetBlob" , err )
}
return
}
2023-07-07 07:31:56 +02:00
if err = common . ServeBlob ( ctx . Base , ctx . Repo . TreePath , blob , nil ) ; err != nil {
2018-11-18 19:45:40 +01:00
ctx . ServerError ( "ServeBlob" , err )
}
}
2019-02-12 15:09:43 +00:00
// DownloadByIDOrLFS download a file by sha1 ID taking account of LFS
func DownloadByIDOrLFS ( ctx * context . Context ) {
2024-06-19 06:32:45 +08:00
blob , err := ctx . Repo . GitRepo . GetBlob ( ctx . PathParam ( "sha" ) )
2019-02-12 15:09:43 +00:00
if err != nil {
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "GetBlob" , nil )
} else {
ctx . ServerError ( "GetBlob" , err )
}
return
}
2023-07-07 07:31:56 +02:00
if err = ServeBlobOrLFS ( ctx , blob , nil ) ; err != nil {
2019-02-12 15:09:43 +00:00
ctx . ServerError ( "ServeBlob" , err )
}
}