repos.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2020 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package db
  5. import (
  6. "context"
  7. "fmt"
  8. "strings"
  9. "time"
  10. api "github.com/gogs/go-gogs-client"
  11. "gorm.io/gorm"
  12. "gogs.io/gogs/internal/errutil"
  13. "gogs.io/gogs/internal/repoutil"
  14. )
  15. // ReposStore is the persistent interface for repositories.
  16. //
  17. // NOTE: All methods are sorted in alphabetical order.
  18. type ReposStore interface {
  19. // Create creates a new repository record in the database. It returns
  20. // ErrNameNotAllowed when the repository name is not allowed, or
  21. // ErrRepoAlreadyExist when a repository with same name already exists for the
  22. // owner.
  23. Create(ctx context.Context, ownerID int64, opts CreateRepoOptions) (*Repository, error)
  24. // GetByName returns the repository with given owner and name. It returns
  25. // ErrRepoNotExist when not found.
  26. GetByName(ctx context.Context, ownerID int64, name string) (*Repository, error)
  27. // Touch updates the updated time to the current time and removes the bare state
  28. // of the given repository.
  29. Touch(ctx context.Context, id int64) error
  30. }
  31. var Repos ReposStore
  32. // BeforeCreate implements the GORM create hook.
  33. func (r *Repository) BeforeCreate(tx *gorm.DB) error {
  34. if r.CreatedUnix == 0 {
  35. r.CreatedUnix = tx.NowFunc().Unix()
  36. }
  37. return nil
  38. }
  39. // BeforeUpdate implements the GORM update hook.
  40. func (r *Repository) BeforeUpdate(tx *gorm.DB) error {
  41. r.UpdatedUnix = tx.NowFunc().Unix()
  42. return nil
  43. }
  44. // AfterFind implements the GORM query hook.
  45. func (r *Repository) AfterFind(_ *gorm.DB) error {
  46. r.Created = time.Unix(r.CreatedUnix, 0).Local()
  47. r.Updated = time.Unix(r.UpdatedUnix, 0).Local()
  48. return nil
  49. }
  50. type RepositoryAPIFormatOptions struct {
  51. Permission *api.Permission
  52. Parent *api.Repository
  53. }
  54. // APIFormat returns the API format of a repository.
  55. func (r *Repository) APIFormat(owner *User, opts ...RepositoryAPIFormatOptions) *api.Repository {
  56. var opt RepositoryAPIFormatOptions
  57. if len(opts) > 0 {
  58. opt = opts[0]
  59. }
  60. cloneLink := repoutil.NewCloneLink(owner.Name, r.Name, false)
  61. return &api.Repository{
  62. ID: r.ID,
  63. Owner: owner.APIFormat(),
  64. Name: r.Name,
  65. FullName: owner.Name + "/" + r.Name,
  66. Description: r.Description,
  67. Private: r.IsPrivate,
  68. Fork: r.IsFork,
  69. Parent: opt.Parent,
  70. Empty: r.IsBare,
  71. Mirror: r.IsMirror,
  72. Size: r.Size,
  73. HTMLURL: repoutil.HTMLURL(owner.Name, r.Name),
  74. SSHURL: cloneLink.SSH,
  75. CloneURL: cloneLink.HTTPS,
  76. Website: r.Website,
  77. Stars: r.NumStars,
  78. Forks: r.NumForks,
  79. Watchers: r.NumWatches,
  80. OpenIssues: r.NumOpenIssues,
  81. DefaultBranch: r.DefaultBranch,
  82. Created: r.Created,
  83. Updated: r.Updated,
  84. Permissions: opt.Permission,
  85. }
  86. }
  87. var _ ReposStore = (*repos)(nil)
  88. type repos struct {
  89. *gorm.DB
  90. }
  91. // NewReposStore returns a persistent interface for repositories with given
  92. // database connection.
  93. func NewReposStore(db *gorm.DB) ReposStore {
  94. return &repos{DB: db}
  95. }
  96. type ErrRepoAlreadyExist struct {
  97. args errutil.Args
  98. }
  99. func IsErrRepoAlreadyExist(err error) bool {
  100. _, ok := err.(ErrRepoAlreadyExist)
  101. return ok
  102. }
  103. func (err ErrRepoAlreadyExist) Error() string {
  104. return fmt.Sprintf("repository already exists: %v", err.args)
  105. }
  106. type CreateRepoOptions struct {
  107. Name string
  108. Description string
  109. DefaultBranch string
  110. Private bool
  111. Mirror bool
  112. EnableWiki bool
  113. EnableIssues bool
  114. EnablePulls bool
  115. Fork bool
  116. ForkID int64
  117. }
  118. func (db *repos) Create(ctx context.Context, ownerID int64, opts CreateRepoOptions) (*Repository, error) {
  119. err := isRepoNameAllowed(opts.Name)
  120. if err != nil {
  121. return nil, err
  122. }
  123. _, err = db.GetByName(ctx, ownerID, opts.Name)
  124. if err == nil {
  125. return nil, ErrRepoAlreadyExist{
  126. args: errutil.Args{
  127. "ownerID": ownerID,
  128. "name": opts.Name,
  129. },
  130. }
  131. } else if !IsErrRepoNotExist(err) {
  132. return nil, err
  133. }
  134. repo := &Repository{
  135. OwnerID: ownerID,
  136. LowerName: strings.ToLower(opts.Name),
  137. Name: opts.Name,
  138. Description: opts.Description,
  139. DefaultBranch: opts.DefaultBranch,
  140. IsPrivate: opts.Private,
  141. IsMirror: opts.Mirror,
  142. EnableWiki: opts.EnableWiki,
  143. EnableIssues: opts.EnableIssues,
  144. EnablePulls: opts.EnablePulls,
  145. IsFork: opts.Fork,
  146. ForkID: opts.ForkID,
  147. }
  148. return repo, db.WithContext(ctx).Create(repo).Error
  149. }
  150. var _ errutil.NotFound = (*ErrRepoNotExist)(nil)
  151. type ErrRepoNotExist struct {
  152. args errutil.Args
  153. }
  154. func IsErrRepoNotExist(err error) bool {
  155. _, ok := err.(ErrRepoNotExist)
  156. return ok
  157. }
  158. func (err ErrRepoNotExist) Error() string {
  159. return fmt.Sprintf("repository does not exist: %v", err.args)
  160. }
  161. func (ErrRepoNotExist) NotFound() bool {
  162. return true
  163. }
  164. func (db *repos) GetByName(ctx context.Context, ownerID int64, name string) (*Repository, error) {
  165. repo := new(Repository)
  166. err := db.WithContext(ctx).
  167. Where("owner_id = ? AND lower_name = ?", ownerID, strings.ToLower(name)).
  168. First(repo).
  169. Error
  170. if err != nil {
  171. if err == gorm.ErrRecordNotFound {
  172. return nil, ErrRepoNotExist{
  173. args: errutil.Args{
  174. "ownerID": ownerID,
  175. "name": name,
  176. },
  177. }
  178. }
  179. return nil, err
  180. }
  181. return repo, nil
  182. }
  183. func (db *repos) Touch(ctx context.Context, id int64) error {
  184. return db.WithContext(ctx).
  185. Model(new(Repository)).
  186. Where("id = ?", id).
  187. Updates(map[string]interface{}{
  188. "is_bare": false,
  189. "updated_unix": db.NowFunc().Unix(),
  190. }).
  191. Error
  192. }