models.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package database
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "path"
  7. "path/filepath"
  8. "strings"
  9. "time"
  10. "github.com/cockroachdb/errors"
  11. "gorm.io/gorm"
  12. "gorm.io/gorm/logger"
  13. "gorm.io/gorm/schema"
  14. log "unknwon.dev/clog/v2"
  15. "gogs.io/gogs/internal/conf"
  16. "gogs.io/gogs/internal/database/migrations"
  17. "gogs.io/gogs/internal/dbutil"
  18. )
  19. var (
  20. db *gorm.DB
  21. legacyTables []any
  22. HasEngine bool
  23. )
  24. func init() {
  25. legacyTables = append(legacyTables,
  26. new(User), new(PublicKey), new(TwoFactor), new(TwoFactorRecoveryCode),
  27. new(Repository), new(DeployKey), new(Collaboration), new(Upload),
  28. new(Watch), new(Star),
  29. new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser),
  30. new(Label), new(IssueLabel), new(Milestone),
  31. new(Mirror), new(Release), new(Webhook), new(HookTask),
  32. new(ProtectBranch), new(ProtectBranchWhitelist),
  33. new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
  34. )
  35. }
  36. func getGormDB(gormLogger logger.Writer) (*gorm.DB, error) {
  37. if conf.Database.Type == "sqlite3" {
  38. if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
  39. return nil, errors.Newf("create directories: %v", err)
  40. }
  41. }
  42. level := logger.Info
  43. if conf.IsProdMode() {
  44. level = logger.Warn
  45. }
  46. logger.Default = logger.New(gormLogger, logger.Config{
  47. SlowThreshold: 100 * time.Millisecond,
  48. LogLevel: level,
  49. })
  50. gormDB, err := dbutil.OpenDB(
  51. conf.Database,
  52. &gorm.Config{
  53. SkipDefaultTransaction: true,
  54. NamingStrategy: schema.NamingStrategy{
  55. SingularTable: true,
  56. },
  57. NowFunc: func() time.Time {
  58. return time.Now().UTC().Truncate(time.Microsecond)
  59. },
  60. },
  61. )
  62. if err != nil {
  63. return nil, errors.Wrap(err, "open database")
  64. }
  65. sqlDB, err := gormDB.DB()
  66. if err != nil {
  67. return nil, errors.Wrap(err, "get underlying *sql.DB")
  68. }
  69. sqlDB.SetMaxOpenConns(conf.Database.MaxOpenConns)
  70. sqlDB.SetMaxIdleConns(conf.Database.MaxIdleConns)
  71. sqlDB.SetConnMaxLifetime(time.Minute)
  72. switch conf.Database.Type {
  73. case "postgres":
  74. conf.UsePostgreSQL = true
  75. case "mysql":
  76. conf.UseMySQL = true
  77. gormDB = gormDB.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{})
  78. case "sqlite3":
  79. conf.UseSQLite3 = true
  80. case "mssql":
  81. conf.UseMSSQL = true
  82. }
  83. return gormDB, nil
  84. }
  85. func NewTestEngine() error {
  86. var err error
  87. db, err = getGormDB(&dbutil.Logger{Writer: os.Stdout})
  88. if err != nil {
  89. return errors.Newf("connect to database: %v", err)
  90. }
  91. for _, table := range legacyTables {
  92. if db.Migrator().HasTable(table) {
  93. continue
  94. }
  95. if err = db.Migrator().AutoMigrate(table); err != nil {
  96. return errors.Wrap(err, "auto migrate")
  97. }
  98. }
  99. return nil
  100. }
  101. func SetEngine() (*gorm.DB, error) {
  102. var logPath string
  103. if conf.HookMode {
  104. logPath = filepath.Join(conf.Log.RootPath, "hooks", "gorm.log")
  105. } else {
  106. logPath = filepath.Join(conf.Log.RootPath, "gorm.log")
  107. }
  108. sec := conf.File.Section("log.gorm")
  109. fileWriter, err := log.NewFileWriter(logPath,
  110. log.FileRotationConfig{
  111. Rotate: sec.Key("ROTATE").MustBool(true),
  112. Daily: sec.Key("ROTATE_DAILY").MustBool(true),
  113. MaxSize: sec.Key("MAX_SIZE").MustInt64(100) * 1024 * 1024,
  114. MaxDays: sec.Key("MAX_DAYS").MustInt64(3),
  115. },
  116. )
  117. if err != nil {
  118. return nil, errors.Newf("create 'gorm.log': %v", err)
  119. }
  120. var gormLogger logger.Writer
  121. if conf.HookMode {
  122. gormLogger = &dbutil.Logger{Writer: fileWriter}
  123. } else {
  124. gormLogger, err = newLogWriter()
  125. if err != nil {
  126. return nil, errors.Wrap(err, "new log writer")
  127. }
  128. }
  129. db, err = getGormDB(gormLogger)
  130. if err != nil {
  131. return nil, err
  132. }
  133. return NewConnection(gormLogger)
  134. }
  135. func NewEngine() error {
  136. gormDB, err := SetEngine()
  137. if err != nil {
  138. return err
  139. }
  140. if err = migrations.Migrate(gormDB); err != nil {
  141. return errors.Newf("migrate: %v", err)
  142. }
  143. for _, table := range legacyTables {
  144. if gormDB.Migrator().HasTable(table) {
  145. continue
  146. }
  147. name := strings.TrimPrefix(fmt.Sprintf("%T", table), "*database.")
  148. if err = gormDB.Migrator().AutoMigrate(table); err != nil {
  149. return errors.Wrapf(err, "auto migrate %q", name)
  150. }
  151. log.Trace("Auto migrated %q", name)
  152. }
  153. HasEngine = true
  154. return nil
  155. }
  156. type Statistic struct {
  157. Counter struct {
  158. User, Org, PublicKey,
  159. Repo, Watch, Star, Action, Access,
  160. Issue, Comment, Oauth, Follow,
  161. Mirror, Release, LoginSource, Webhook,
  162. Milestone, Label, HookTask,
  163. Team, UpdateTask, Attachment int64
  164. }
  165. }
  166. func GetStatistic(ctx context.Context) (stats Statistic) {
  167. stats.Counter.User = Handle.Users().Count(ctx)
  168. stats.Counter.Org = CountOrganizations()
  169. var count int64
  170. db.Model(new(PublicKey)).Count(&count)
  171. stats.Counter.PublicKey = count
  172. stats.Counter.Repo = CountRepositories(true)
  173. db.Model(new(Watch)).Count(&count)
  174. stats.Counter.Watch = count
  175. db.Model(new(Star)).Count(&count)
  176. stats.Counter.Star = count
  177. db.Model(new(Action)).Count(&count)
  178. stats.Counter.Action = count
  179. db.Model(new(Access)).Count(&count)
  180. stats.Counter.Access = count
  181. db.Model(new(Issue)).Count(&count)
  182. stats.Counter.Issue = count
  183. db.Model(new(Comment)).Count(&count)
  184. stats.Counter.Comment = count
  185. stats.Counter.Oauth = 0
  186. db.Model(new(Follow)).Count(&count)
  187. stats.Counter.Follow = count
  188. db.Model(new(Mirror)).Count(&count)
  189. stats.Counter.Mirror = count
  190. db.Model(new(Release)).Count(&count)
  191. stats.Counter.Release = count
  192. stats.Counter.LoginSource = Handle.LoginSources().Count(ctx)
  193. db.Model(new(Webhook)).Count(&count)
  194. stats.Counter.Webhook = count
  195. db.Model(new(Milestone)).Count(&count)
  196. stats.Counter.Milestone = count
  197. db.Model(new(Label)).Count(&count)
  198. stats.Counter.Label = count
  199. db.Model(new(HookTask)).Count(&count)
  200. stats.Counter.HookTask = count
  201. db.Model(new(Team)).Count(&count)
  202. stats.Counter.Team = count
  203. db.Model(new(Attachment)).Count(&count)
  204. stats.Counter.Attachment = count
  205. return stats
  206. }
  207. func Ping() error {
  208. if db == nil {
  209. return errors.New("database not available")
  210. }
  211. sqlDB, err := db.DB()
  212. if err != nil {
  213. return err
  214. }
  215. return sqlDB.Ping()
  216. }
  217. // The version table. Should have only one row with id==1
  218. type Version struct {
  219. ID int64
  220. Version int64
  221. }