mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
165 lines
4.4 KiB
Go
165 lines
4.4 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"time"
|
|
|
|
auth_model "github.com/hanzoai/git/models/auth"
|
|
git_model "github.com/hanzoai/git/models/git"
|
|
"github.com/hanzoai/git/modules/git"
|
|
"github.com/hanzoai/git/modules/httpcache"
|
|
"github.com/hanzoai/git/modules/httplib"
|
|
"github.com/hanzoai/git/modules/lfs"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
"github.com/hanzoai/git/modules/storage"
|
|
"github.com/hanzoai/git/routers/common"
|
|
"github.com/hanzoai/git/services/context"
|
|
)
|
|
|
|
func checkDownloadTokenScope(ctx *context.Context) bool {
|
|
context.CheckRepoScopedToken(ctx, ctx.Repo.Repository, auth_model.Read)
|
|
return !ctx.Written()
|
|
}
|
|
|
|
// ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
|
|
func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Time) error {
|
|
if httpcache.HandleGenericETagPrivateCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
|
|
return nil
|
|
}
|
|
|
|
lfsPointerBuf, err := blob.GetBlobBytes(lfs.MetaFileMaxSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pointer, _ := lfs.ReadPointerFromBuffer(lfsPointerBuf)
|
|
if pointer.IsValid() {
|
|
meta, _ := git_model.GetLFSMetaObjectByOid(ctx, ctx.Repo.Repository.ID, pointer.Oid)
|
|
if meta == nil {
|
|
return common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified)
|
|
}
|
|
if httpcache.HandleGenericETagPrivateCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`, meta.UpdatedUnix.AsTimePtr()) {
|
|
return nil
|
|
}
|
|
|
|
if setting.LFS.Storage.ServeDirect() {
|
|
// If we have a signed url (S3, object storage, blob storage), redirect to this directly.
|
|
u, err := storage.LFS.ServeDirectURL(pointer.RelativePath(), blob.Name(), ctx.Req.Method, nil)
|
|
if u != nil && err == nil {
|
|
ctx.Redirect(u.String())
|
|
return nil
|
|
}
|
|
}
|
|
|
|
lfsDataFile, err := lfs.ReadMetaObject(meta.Pointer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer lfsDataFile.Close()
|
|
httplib.ServeUserContentByFile(ctx.Req, ctx.Resp, lfsDataFile, httplib.ServeHeaderOptions{Filename: ctx.Repo.TreePath})
|
|
return nil
|
|
}
|
|
|
|
return common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified)
|
|
}
|
|
|
|
func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
|
|
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx, ctx.Repo.GitRepo, ctx.Repo.TreePath)
|
|
if err != nil {
|
|
if git.IsErrNotExist(err) {
|
|
ctx.NotFound(err)
|
|
} else {
|
|
ctx.ServerError("GetTreeEntryByPath", err)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
if entry.IsDir() || entry.IsSubModule() {
|
|
ctx.NotFound(nil)
|
|
return nil, nil
|
|
}
|
|
|
|
latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
|
|
if err != nil {
|
|
ctx.ServerError("GetTreePathLatestCommit", err)
|
|
return nil, nil
|
|
}
|
|
lastModified := &latestCommit.Committer.When
|
|
|
|
return entry.Blob(ctx.Repo.GitRepo), lastModified
|
|
}
|
|
|
|
// SingleDownload download a file by repos path
|
|
func SingleDownload(ctx *context.Context) {
|
|
if !checkDownloadTokenScope(ctx) {
|
|
return
|
|
}
|
|
|
|
blob, lastModified := getBlobForEntry(ctx)
|
|
if blob == nil {
|
|
return
|
|
}
|
|
|
|
if err := common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified); err != nil {
|
|
ctx.ServerError("ServeBlob", err)
|
|
}
|
|
}
|
|
|
|
// SingleDownloadOrLFS download a file by repos path redirecting to LFS if necessary
|
|
func SingleDownloadOrLFS(ctx *context.Context) {
|
|
if !checkDownloadTokenScope(ctx) {
|
|
return
|
|
}
|
|
|
|
blob, lastModified := getBlobForEntry(ctx)
|
|
if blob == nil {
|
|
return
|
|
}
|
|
|
|
if err := ServeBlobOrLFS(ctx, blob, lastModified); err != nil {
|
|
ctx.ServerError("ServeBlobOrLFS", err)
|
|
}
|
|
}
|
|
|
|
// DownloadByID download a file by sha1 ID
|
|
func DownloadByID(ctx *context.Context) {
|
|
if !checkDownloadTokenScope(ctx) {
|
|
return
|
|
}
|
|
|
|
blob, err := ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
|
|
if err != nil {
|
|
if git.IsErrNotExist(err) {
|
|
ctx.NotFound(nil)
|
|
} else {
|
|
ctx.ServerError("GetBlob", err)
|
|
}
|
|
return
|
|
}
|
|
if err = common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, nil); err != nil {
|
|
ctx.ServerError("ServeBlob", err)
|
|
}
|
|
}
|
|
|
|
// DownloadByIDOrLFS download a file by sha1 ID taking account of LFS
|
|
func DownloadByIDOrLFS(ctx *context.Context) {
|
|
if !checkDownloadTokenScope(ctx) {
|
|
return
|
|
}
|
|
|
|
blob, err := ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
|
|
if err != nil {
|
|
if git.IsErrNotExist(err) {
|
|
ctx.NotFound(nil)
|
|
} else {
|
|
ctx.ServerError("GetBlob", err)
|
|
}
|
|
return
|
|
}
|
|
if err = ServeBlobOrLFS(ctx, blob, nil); err != nil {
|
|
ctx.ServerError("ServeBlob", err)
|
|
}
|
|
}
|