2020-04-05 09:20:50 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-12-26 04:16:37 +03:00
package models
import (
2021-12-10 04:27:50 +03:00
"context"
2016-12-26 04:16:37 +03:00
"errors"
2017-12-11 07:37:04 +03:00
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2021-04-09 01:25:57 +03:00
"code.gitea.io/gitea/modules/lfs"
2019-08-15 17:46:21 +03:00
"code.gitea.io/gitea/modules/timeutil"
2019-10-28 21:31:55 +03:00
"xorm.io/builder"
2016-12-26 04:16:37 +03:00
)
// LFSMetaObject stores metadata for LFS tracked files.
type LFSMetaObject struct {
2021-04-09 01:25:57 +03:00
ID int64 ` xorm:"pk autoincr" `
lfs . Pointer ` xorm:"extends" `
2019-08-15 17:46:21 +03:00
RepositoryID int64 ` xorm:"UNIQUE(s) INDEX NOT NULL" `
Existing bool ` xorm:"-" `
CreatedUnix timeutil . TimeStamp ` xorm:"created" `
2016-12-26 04:16:37 +03:00
}
2021-09-19 14:49:59 +03:00
func init ( ) {
db . RegisterModel ( new ( LFSMetaObject ) )
}
2016-12-26 04:16:37 +03:00
// LFSTokenResponse defines the JSON structure in which the JWT token is stored.
// This structure is fetched via SSH and passed by the Git LFS client to the server
// endpoint for authorization.
type LFSTokenResponse struct {
Header map [ string ] string ` json:"header" `
Href string ` json:"href" `
}
2021-03-14 21:52:12 +03:00
// ErrLFSObjectNotExist is returned from lfs models functions in order
// to differentiate between database and missing object errors.
var ErrLFSObjectNotExist = errors . New ( "LFS Meta object does not exist" )
2016-12-26 04:16:37 +03:00
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
// if it is not already present.
func NewLFSMetaObject ( m * LFSMetaObject ) ( * LFSMetaObject , error ) {
var err error
2021-11-21 18:41:00 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2019-01-23 11:56:51 +03:00
return nil , err
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
2019-01-23 11:56:51 +03:00
2021-11-21 18:41:00 +03:00
has , err := db . GetByBean ( ctx , m )
2016-12-26 04:16:37 +03:00
if err != nil {
return nil , err
}
if has {
m . Existing = true
2021-11-21 18:41:00 +03:00
return m , committer . Commit ( )
2016-12-26 04:16:37 +03:00
}
2021-11-21 18:41:00 +03:00
if err = db . Insert ( ctx , m ) ; err != nil {
2016-12-26 04:16:37 +03:00
return nil , err
}
2021-11-21 18:41:00 +03:00
return m , committer . Commit ( )
2016-12-26 04:16:37 +03:00
}
// GetLFSMetaObjectByOid selects a LFSMetaObject entry from database by its OID.
// It may return ErrLFSObjectNotExist or a database error. If the error is nil,
// the returned pointer is a valid LFSMetaObject.
2021-12-10 04:27:50 +03:00
func GetLFSMetaObjectByOid ( repoID int64 , oid string ) ( * LFSMetaObject , error ) {
2016-12-26 04:16:37 +03:00
if len ( oid ) == 0 {
return nil , ErrLFSObjectNotExist
}
2021-12-10 04:27:50 +03:00
m := & LFSMetaObject { Pointer : lfs . Pointer { Oid : oid } , RepositoryID : repoID }
2021-09-23 18:45:36 +03:00
has , err := db . GetEngine ( db . DefaultContext ) . Get ( m )
2016-12-26 04:16:37 +03:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrLFSObjectNotExist
}
return m , nil
}
// RemoveLFSMetaObjectByOid removes a LFSMetaObject entry from database by its OID.
// It may return ErrLFSObjectNotExist or a database error.
2021-12-10 04:27:50 +03:00
func RemoveLFSMetaObjectByOid ( repoID int64 , oid string ) ( int64 , error ) {
2016-12-26 04:16:37 +03:00
if len ( oid ) == 0 {
2019-10-28 21:31:55 +03:00
return 0 , ErrLFSObjectNotExist
2016-12-26 04:16:37 +03:00
}
2021-11-21 18:41:00 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
return 0 , err
2016-12-26 04:16:37 +03:00
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
2016-12-26 04:16:37 +03:00
2021-12-10 04:27:50 +03:00
m := & LFSMetaObject { Pointer : lfs . Pointer { Oid : oid } , RepositoryID : repoID }
2021-11-21 18:41:00 +03:00
if _ , err := db . DeleteByBean ( ctx , m ) ; err != nil {
2019-10-28 21:31:55 +03:00
return - 1 , err
}
2021-11-21 18:41:00 +03:00
count , err := db . CountByBean ( ctx , & LFSMetaObject { Pointer : lfs . Pointer { Oid : oid } } )
2019-10-28 21:31:55 +03:00
if err != nil {
return count , err
}
2021-11-21 18:41:00 +03:00
return count , committer . Commit ( )
2019-10-28 21:31:55 +03:00
}
// GetLFSMetaObjects returns all LFSMetaObjects associated with a repository
2021-12-10 04:27:50 +03:00
func GetLFSMetaObjects ( repoID int64 , page , pageSize int ) ( [ ] * LFSMetaObject , error ) {
2021-11-21 18:41:00 +03:00
sess := db . GetEngine ( db . DefaultContext )
2019-10-28 21:31:55 +03:00
if page >= 0 && pageSize > 0 {
start := 0
if page > 0 {
start = ( page - 1 ) * pageSize
}
sess . Limit ( pageSize , start )
}
lfsObjects := make ( [ ] * LFSMetaObject , 0 , pageSize )
2021-12-10 04:27:50 +03:00
return lfsObjects , sess . Find ( & lfsObjects , & LFSMetaObject { RepositoryID : repoID } )
2019-10-28 21:31:55 +03:00
}
// CountLFSMetaObjects returns a count of all LFSMetaObjects associated with a repository
2021-12-10 04:27:50 +03:00
func CountLFSMetaObjects ( repoID int64 ) ( int64 , error ) {
return db . GetEngine ( db . DefaultContext ) . Count ( & LFSMetaObject { RepositoryID : repoID } )
2019-10-28 21:31:55 +03:00
}
// LFSObjectAccessible checks if a provided Oid is accessible to the user
2021-11-24 12:49:20 +03:00
func LFSObjectAccessible ( user * user_model . User , oid string ) ( bool , error ) {
2019-10-28 21:31:55 +03:00
if user . IsAdmin {
2021-09-23 18:45:36 +03:00
count , err := db . GetEngine ( db . DefaultContext ) . Count ( & LFSMetaObject { Pointer : lfs . Pointer { Oid : oid } } )
2021-04-09 10:40:34 +03:00
return count > 0 , err
2019-10-28 21:31:55 +03:00
}
2020-01-13 20:33:46 +03:00
cond := accessibleRepositoryCondition ( user )
2021-09-23 18:45:36 +03:00
count , err := db . GetEngine ( db . DefaultContext ) . Where ( cond ) . Join ( "INNER" , "repository" , "`lfs_meta_object`.repository_id = `repository`.id" ) . Count ( & LFSMetaObject { Pointer : lfs . Pointer { Oid : oid } } )
2021-04-09 10:40:34 +03:00
return count > 0 , err
2019-10-28 21:31:55 +03:00
}
// LFSAutoAssociate auto associates accessible LFSMetaObjects
2021-11-24 12:49:20 +03:00
func LFSAutoAssociate ( metas [ ] * LFSMetaObject , user * user_model . User , repoID int64 ) error {
2021-11-21 18:41:00 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2019-10-28 21:31:55 +03:00
return err
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
sess := db . GetEngine ( ctx )
2019-10-28 21:31:55 +03:00
oids := make ( [ ] interface { } , len ( metas ) )
oidMap := make ( map [ string ] * LFSMetaObject , len ( metas ) )
for i , meta := range metas {
oids [ i ] = meta . Oid
oidMap [ meta . Oid ] = meta
}
cond := builder . NewCond ( )
if ! user . IsAdmin {
cond = builder . In ( "`lfs_meta_object`.repository_id" ,
2020-01-13 20:33:46 +03:00
builder . Select ( "`repository`.id" ) . From ( "repository" ) . Where ( accessibleRepositoryCondition ( user ) ) )
2019-10-28 21:31:55 +03:00
}
newMetas := make ( [ ] * LFSMetaObject , 0 , len ( metas ) )
if err := sess . Cols ( "oid" ) . Where ( cond ) . In ( "oid" , oids ... ) . GroupBy ( "oid" ) . Find ( & newMetas ) ; err != nil {
return err
}
for i := range newMetas {
newMetas [ i ] . Size = oidMap [ newMetas [ i ] . Oid ] . Size
newMetas [ i ] . RepositoryID = repoID
}
2021-11-21 18:41:00 +03:00
if err := db . Insert ( ctx , newMetas ) ; err != nil {
2016-12-26 04:16:37 +03:00
return err
}
2021-11-21 18:41:00 +03:00
return committer . Commit ( )
2016-12-26 04:16:37 +03:00
}
2020-09-08 18:45:10 +03:00
// IterateLFS iterates lfs object
func IterateLFS ( f func ( mo * LFSMetaObject ) error ) error {
var start int
const batchSize = 100
2021-11-21 18:41:00 +03:00
var e = db . GetEngine ( db . DefaultContext )
2020-09-08 18:45:10 +03:00
for {
2021-03-14 21:52:12 +03:00
mos := make ( [ ] * LFSMetaObject , 0 , batchSize )
2021-11-21 18:41:00 +03:00
if err := e . Limit ( batchSize , start ) . Find ( & mos ) ; err != nil {
2020-09-08 18:45:10 +03:00
return err
}
if len ( mos ) == 0 {
return nil
}
start += len ( mos )
for _ , mo := range mos {
if err := f ( mo ) ; err != nil {
return err
}
}
}
}
2021-12-10 04:27:50 +03:00
// CopyLFS copies LFS data from one repo to another
func CopyLFS ( ctx context . Context , newRepo , oldRepo * repo_model . Repository ) error {
var lfsObjects [ ] * LFSMetaObject
if err := db . GetEngine ( ctx ) . Where ( "repository_id=?" , oldRepo . ID ) . Find ( & lfsObjects ) ; err != nil {
return err
}
for _ , v := range lfsObjects {
v . ID = 0
v . RepositoryID = newRepo . ID
if _ , err := db . GetEngine ( ctx ) . Insert ( v ) ; err != nil {
return err
}
}
return nil
}