| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- package database
- import (
- "context"
- "fmt"
- "os"
- "path"
- "path/filepath"
- "strings"
- "time"
- "github.com/cockroachdb/errors"
- "gorm.io/gorm"
- "gorm.io/gorm/logger"
- "gorm.io/gorm/schema"
- log "unknwon.dev/clog/v2"
- "gogs.io/gogs/internal/conf"
- "gogs.io/gogs/internal/database/migrations"
- "gogs.io/gogs/internal/dbutil"
- )
- var (
- db *gorm.DB
- legacyTables []any
- HasEngine bool
- )
- func init() {
- legacyTables = append(legacyTables,
- new(User), new(PublicKey), new(TwoFactor), new(TwoFactorRecoveryCode),
- new(Repository), new(DeployKey), new(Collaboration), new(Upload),
- new(Watch), new(Star),
- new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser),
- new(Label), new(IssueLabel), new(Milestone),
- new(Mirror), new(Release), new(Webhook), new(HookTask),
- new(ProtectBranch), new(ProtectBranchWhitelist),
- new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
- )
- }
- func getGormDB(gormLogger logger.Writer) (*gorm.DB, error) {
- if conf.Database.Type == "sqlite3" {
- if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
- return nil, errors.Newf("create directories: %v", err)
- }
- }
- level := logger.Info
- if conf.IsProdMode() {
- level = logger.Warn
- }
- logger.Default = logger.New(gormLogger, logger.Config{
- SlowThreshold: 100 * time.Millisecond,
- LogLevel: level,
- })
- gormDB, err := dbutil.OpenDB(
- conf.Database,
- &gorm.Config{
- SkipDefaultTransaction: true,
- NamingStrategy: schema.NamingStrategy{
- SingularTable: true,
- },
- NowFunc: func() time.Time {
- return time.Now().UTC().Truncate(time.Microsecond)
- },
- },
- )
- if err != nil {
- return nil, errors.Wrap(err, "open database")
- }
- sqlDB, err := gormDB.DB()
- if err != nil {
- return nil, errors.Wrap(err, "get underlying *sql.DB")
- }
- sqlDB.SetMaxOpenConns(conf.Database.MaxOpenConns)
- sqlDB.SetMaxIdleConns(conf.Database.MaxIdleConns)
- sqlDB.SetConnMaxLifetime(time.Minute)
- switch conf.Database.Type {
- case "postgres":
- conf.UsePostgreSQL = true
- case "mysql":
- conf.UseMySQL = true
- gormDB = gormDB.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{})
- case "sqlite3":
- conf.UseSQLite3 = true
- case "mssql":
- conf.UseMSSQL = true
- }
- return gormDB, nil
- }
- func NewTestEngine() error {
- var err error
- db, err = getGormDB(&dbutil.Logger{Writer: os.Stdout})
- if err != nil {
- return errors.Newf("connect to database: %v", err)
- }
- for _, table := range legacyTables {
- if db.Migrator().HasTable(table) {
- continue
- }
- if err = db.Migrator().AutoMigrate(table); err != nil {
- return errors.Wrap(err, "auto migrate")
- }
- }
- return nil
- }
- func SetEngine() (*gorm.DB, error) {
- var logPath string
- if conf.HookMode {
- logPath = filepath.Join(conf.Log.RootPath, "hooks", "gorm.log")
- } else {
- logPath = filepath.Join(conf.Log.RootPath, "gorm.log")
- }
- sec := conf.File.Section("log.gorm")
- fileWriter, err := log.NewFileWriter(logPath,
- log.FileRotationConfig{
- Rotate: sec.Key("ROTATE").MustBool(true),
- Daily: sec.Key("ROTATE_DAILY").MustBool(true),
- MaxSize: sec.Key("MAX_SIZE").MustInt64(100) * 1024 * 1024,
- MaxDays: sec.Key("MAX_DAYS").MustInt64(3),
- },
- )
- if err != nil {
- return nil, errors.Newf("create 'gorm.log': %v", err)
- }
- var gormLogger logger.Writer
- if conf.HookMode {
- gormLogger = &dbutil.Logger{Writer: fileWriter}
- } else {
- gormLogger, err = newLogWriter()
- if err != nil {
- return nil, errors.Wrap(err, "new log writer")
- }
- }
- db, err = getGormDB(gormLogger)
- if err != nil {
- return nil, err
- }
- return NewConnection(gormLogger)
- }
- func NewEngine() error {
- gormDB, err := SetEngine()
- if err != nil {
- return err
- }
- if err = migrations.Migrate(gormDB); err != nil {
- return errors.Newf("migrate: %v", err)
- }
- for _, table := range legacyTables {
- if gormDB.Migrator().HasTable(table) {
- continue
- }
- name := strings.TrimPrefix(fmt.Sprintf("%T", table), "*database.")
- if err = gormDB.Migrator().AutoMigrate(table); err != nil {
- return errors.Wrapf(err, "auto migrate %q", name)
- }
- log.Trace("Auto migrated %q", name)
- }
- HasEngine = true
- return nil
- }
- type Statistic struct {
- Counter struct {
- User, Org, PublicKey,
- Repo, Watch, Star, Action, Access,
- Issue, Comment, Oauth, Follow,
- Mirror, Release, LoginSource, Webhook,
- Milestone, Label, HookTask,
- Team, UpdateTask, Attachment int64
- }
- }
- func GetStatistic(ctx context.Context) (stats Statistic) {
- stats.Counter.User = Handle.Users().Count(ctx)
- stats.Counter.Org = CountOrganizations()
- var count int64
- db.Model(new(PublicKey)).Count(&count)
- stats.Counter.PublicKey = count
- stats.Counter.Repo = CountRepositories(true)
- db.Model(new(Watch)).Count(&count)
- stats.Counter.Watch = count
- db.Model(new(Star)).Count(&count)
- stats.Counter.Star = count
- db.Model(new(Action)).Count(&count)
- stats.Counter.Action = count
- db.Model(new(Access)).Count(&count)
- stats.Counter.Access = count
- db.Model(new(Issue)).Count(&count)
- stats.Counter.Issue = count
- db.Model(new(Comment)).Count(&count)
- stats.Counter.Comment = count
- stats.Counter.Oauth = 0
- db.Model(new(Follow)).Count(&count)
- stats.Counter.Follow = count
- db.Model(new(Mirror)).Count(&count)
- stats.Counter.Mirror = count
- db.Model(new(Release)).Count(&count)
- stats.Counter.Release = count
- stats.Counter.LoginSource = Handle.LoginSources().Count(ctx)
- db.Model(new(Webhook)).Count(&count)
- stats.Counter.Webhook = count
- db.Model(new(Milestone)).Count(&count)
- stats.Counter.Milestone = count
- db.Model(new(Label)).Count(&count)
- stats.Counter.Label = count
- db.Model(new(HookTask)).Count(&count)
- stats.Counter.HookTask = count
- db.Model(new(Team)).Count(&count)
- stats.Counter.Team = count
- db.Model(new(Attachment)).Count(&count)
- stats.Counter.Attachment = count
- return stats
- }
- func Ping() error {
- if db == nil {
- return errors.New("database not available")
- }
- sqlDB, err := db.DB()
- if err != nil {
- return err
- }
- return sqlDB.Ping()
- }
- // The version table. Should have only one row with id==1
- type Version struct {
- ID int64
- Version int64
- }
|