mirror of https://github.com/gogs/gogs.git
222 lines
5.5 KiB
Go
222 lines
5.5 KiB
Go
// 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 db
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
api "github.com/gogs/go-gogs-client"
|
|
"gorm.io/gorm"
|
|
|
|
"gogs.io/gogs/internal/errutil"
|
|
"gogs.io/gogs/internal/repoutil"
|
|
)
|
|
|
|
// ReposStore is the persistent interface for repositories.
|
|
//
|
|
// NOTE: All methods are sorted in alphabetical order.
|
|
type ReposStore interface {
|
|
// Create creates a new repository record in the database. It returns
|
|
// ErrNameNotAllowed when the repository name is not allowed, or
|
|
// ErrRepoAlreadyExist when a repository with same name already exists for the
|
|
// owner.
|
|
Create(ctx context.Context, ownerID int64, opts CreateRepoOptions) (*Repository, error)
|
|
// GetByName returns the repository with given owner and name. It returns
|
|
// ErrRepoNotExist when not found.
|
|
GetByName(ctx context.Context, ownerID int64, name string) (*Repository, error)
|
|
// Touch updates the updated time to the current time and removes the bare state
|
|
// of the given repository.
|
|
Touch(ctx context.Context, id int64) error
|
|
}
|
|
|
|
var Repos ReposStore
|
|
|
|
// BeforeCreate implements the GORM create hook.
|
|
func (r *Repository) BeforeCreate(tx *gorm.DB) error {
|
|
if r.CreatedUnix == 0 {
|
|
r.CreatedUnix = tx.NowFunc().Unix()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BeforeUpdate implements the GORM update hook.
|
|
func (r *Repository) BeforeUpdate(tx *gorm.DB) error {
|
|
r.UpdatedUnix = tx.NowFunc().Unix()
|
|
return nil
|
|
}
|
|
|
|
// AfterFind implements the GORM query hook.
|
|
func (r *Repository) AfterFind(_ *gorm.DB) error {
|
|
r.Created = time.Unix(r.CreatedUnix, 0).Local()
|
|
r.Updated = time.Unix(r.UpdatedUnix, 0).Local()
|
|
return nil
|
|
}
|
|
|
|
type RepositoryAPIFormatOptions struct {
|
|
Permission *api.Permission
|
|
Parent *api.Repository
|
|
}
|
|
|
|
// APIFormat returns the API format of a repository.
|
|
func (r *Repository) APIFormat(owner *User, opts ...RepositoryAPIFormatOptions) *api.Repository {
|
|
var opt RepositoryAPIFormatOptions
|
|
if len(opts) > 0 {
|
|
opt = opts[0]
|
|
}
|
|
|
|
cloneLink := repoutil.NewCloneLink(owner.Name, r.Name, false)
|
|
return &api.Repository{
|
|
ID: r.ID,
|
|
Owner: owner.APIFormat(),
|
|
Name: r.Name,
|
|
FullName: owner.Name + "/" + r.Name,
|
|
Description: r.Description,
|
|
Private: r.IsPrivate,
|
|
Fork: r.IsFork,
|
|
Parent: opt.Parent,
|
|
Empty: r.IsBare,
|
|
Mirror: r.IsMirror,
|
|
Size: r.Size,
|
|
HTMLURL: repoutil.HTMLURL(owner.Name, r.Name),
|
|
SSHURL: cloneLink.SSH,
|
|
CloneURL: cloneLink.HTTPS,
|
|
Website: r.Website,
|
|
Stars: r.NumStars,
|
|
Forks: r.NumForks,
|
|
Watchers: r.NumWatches,
|
|
OpenIssues: r.NumOpenIssues,
|
|
DefaultBranch: r.DefaultBranch,
|
|
Created: r.Created,
|
|
Updated: r.Updated,
|
|
Permissions: opt.Permission,
|
|
}
|
|
}
|
|
|
|
var _ ReposStore = (*repos)(nil)
|
|
|
|
type repos struct {
|
|
*gorm.DB
|
|
}
|
|
|
|
// NewReposStore returns a persistent interface for repositories with given
|
|
// database connection.
|
|
func NewReposStore(db *gorm.DB) ReposStore {
|
|
return &repos{DB: db}
|
|
}
|
|
|
|
type ErrRepoAlreadyExist struct {
|
|
args errutil.Args
|
|
}
|
|
|
|
func IsErrRepoAlreadyExist(err error) bool {
|
|
_, ok := err.(ErrRepoAlreadyExist)
|
|
return ok
|
|
}
|
|
|
|
func (err ErrRepoAlreadyExist) Error() string {
|
|
return fmt.Sprintf("repository already exists: %v", err.args)
|
|
}
|
|
|
|
type CreateRepoOptions struct {
|
|
Name string
|
|
Description string
|
|
DefaultBranch string
|
|
Private bool
|
|
Mirror bool
|
|
EnableWiki bool
|
|
EnableIssues bool
|
|
EnablePulls bool
|
|
Fork bool
|
|
ForkID int64
|
|
}
|
|
|
|
func (db *repos) Create(ctx context.Context, ownerID int64, opts CreateRepoOptions) (*Repository, error) {
|
|
err := isRepoNameAllowed(opts.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = db.GetByName(ctx, ownerID, opts.Name)
|
|
if err == nil {
|
|
return nil, ErrRepoAlreadyExist{
|
|
args: errutil.Args{
|
|
"ownerID": ownerID,
|
|
"name": opts.Name,
|
|
},
|
|
}
|
|
} else if !IsErrRepoNotExist(err) {
|
|
return nil, err
|
|
}
|
|
|
|
repo := &Repository{
|
|
OwnerID: ownerID,
|
|
LowerName: strings.ToLower(opts.Name),
|
|
Name: opts.Name,
|
|
Description: opts.Description,
|
|
DefaultBranch: opts.DefaultBranch,
|
|
IsPrivate: opts.Private,
|
|
IsMirror: opts.Mirror,
|
|
EnableWiki: opts.EnableWiki,
|
|
EnableIssues: opts.EnableIssues,
|
|
EnablePulls: opts.EnablePulls,
|
|
IsFork: opts.Fork,
|
|
ForkID: opts.ForkID,
|
|
}
|
|
return repo, db.WithContext(ctx).Create(repo).Error
|
|
}
|
|
|
|
var _ errutil.NotFound = (*ErrRepoNotExist)(nil)
|
|
|
|
type ErrRepoNotExist struct {
|
|
args errutil.Args
|
|
}
|
|
|
|
func IsErrRepoNotExist(err error) bool {
|
|
_, ok := err.(ErrRepoNotExist)
|
|
return ok
|
|
}
|
|
|
|
func (err ErrRepoNotExist) Error() string {
|
|
return fmt.Sprintf("repository does not exist: %v", err.args)
|
|
}
|
|
|
|
func (ErrRepoNotExist) NotFound() bool {
|
|
return true
|
|
}
|
|
|
|
func (db *repos) GetByName(ctx context.Context, ownerID int64, name string) (*Repository, error) {
|
|
repo := new(Repository)
|
|
err := db.WithContext(ctx).
|
|
Where("owner_id = ? AND lower_name = ?", ownerID, strings.ToLower(name)).
|
|
First(repo).
|
|
Error
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, ErrRepoNotExist{
|
|
args: errutil.Args{
|
|
"ownerID": ownerID,
|
|
"name": name,
|
|
},
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
return repo, nil
|
|
}
|
|
|
|
func (db *repos) Touch(ctx context.Context, id int64) error {
|
|
return db.WithContext(ctx).
|
|
Model(new(Repository)).
|
|
Where("id = ?", id).
|
|
Updates(map[string]interface{}{
|
|
"is_bare": false,
|
|
"updated_unix": db.NowFunc().Unix(),
|
|
}).
|
|
Error
|
|
}
|