浏览代码

Implemented Sentry

Yoginth 7 年之前
父节点
当前提交
1db6e4be5b
共有 10 个文件被更改,包括 76 次插入0 次删除
  1. 2 0
      cmd/admin.go
  2. 10 0
      cmd/cert.go
  3. 5 0
      cmd/web.go
  4. 6 0
      models/login_source.go
  5. 9 0
      models/repo.go
  6. 5 0
      pkg/cron/cron.go
  7. 4 0
      pkg/httplib/httplib.go
  8. 31 0
      pkg/setting/setting.go
  9. 2 0
      pkg/ssh/ssh.go
  10. 2 0
      routes/install.go

+ 2 - 0
cmd/admin.go

@@ -7,6 +7,7 @@ import (
 	"reflect"
 	"runtime"
 
+	raven "github.com/getsentry/raven-go"
 	"github.com/urfave/cli"
 )
 
@@ -138,6 +139,7 @@ func runCreateUser(c *cli.Context) error {
 		ShowAds:    true,
 		IsAdmin:    c.Bool("admin"),
 	}); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		return fmt.Errorf("CreateUser: %v", err)
 	}
 

+ 10 - 0
cmd/cert.go

@@ -17,6 +17,7 @@ import (
 	"strings"
 	"time"
 
+	raven "github.com/getsentry/raven-go"
 	"github.com/urfave/cli"
 )
 
@@ -54,6 +55,7 @@ func pemBlockForKey(priv interface{}) *pem.Block {
 	case *ecdsa.PrivateKey:
 		b, err := x509.MarshalECPrivateKey(k)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatalf("Unable to marshal ECDSA private key: %v\n", err)
 		}
 		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
@@ -64,6 +66,7 @@ func pemBlockForKey(priv interface{}) *pem.Block {
 
 func runCert(ctx *cli.Context) error {
 	if len(ctx.String("host")) == 0 {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal("Missing required --host parameter")
 	}
 
@@ -81,9 +84,11 @@ func runCert(ctx *cli.Context) error {
 	case "P521":
 		priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
 	default:
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatalf("Unrecognized elliptic curve: %q", ctx.String("ecdsa-curve"))
 	}
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatalf("Failed to generate private key: %s", err)
 	}
 
@@ -93,6 +98,7 @@ func runCert(ctx *cli.Context) error {
 	} else {
 		notBefore, err = time.Parse("Jan 2 15:04:05 2006", ctx.String("start-date"))
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatalf("Failed to parse creation date: %s", err)
 		}
 	}
@@ -102,6 +108,7 @@ func runCert(ctx *cli.Context) error {
 	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
 	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatalf("Failed to generate serial number: %s", err)
 	}
 
@@ -135,11 +142,13 @@ func runCert(ctx *cli.Context) error {
 
 	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatalf("Failed to create certificate: %s", err)
 	}
 
 	certOut, err := os.Create("cert.pem")
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatalf("Failed to open cert.pem for writing: %s", err)
 	}
 	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
@@ -148,6 +157,7 @@ func runCert(ctx *cli.Context) error {
 
 	keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatalf("Failed to open key.pem for writing: %v\n", err)
 	}
 	pem.Encode(keyOut, pemBlockForKey(priv))

+ 5 - 0
cmd/web.go

@@ -26,6 +26,7 @@ import (
 	"strings"
 
 	"github.com/Unknwon/com"
+	raven "github.com/getsentry/raven-go"
 	"github.com/go-macaron/binding"
 	"github.com/go-macaron/cache"
 	"github.com/go-macaron/captcha"
@@ -98,6 +99,7 @@ func newMacaron() *macaron.Macaron {
 
 	localeNames, err := bindata.AssetDir("conf/locale")
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Fail to list locale files: %v", err)
 	}
 	localFiles := make(map[string][]byte)
@@ -743,14 +745,17 @@ func runWeb(c *cli.Context) error {
 		// FIXME: add proper implementation of signal capture on all protocols
 		// execute this on SIGTERM or SIGINT: listener.Close()
 		if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(4, "Failed to set permission of unix socket: %v", err)
 		}
 		err = http.Serve(listener, m)
 	default:
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
 	}
 
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Failed to start server: %v", err)
 	}
 

+ 6 - 0
models/login_source.go

@@ -16,6 +16,7 @@ import (
 	"time"
 
 	"github.com/Unknwon/com"
+	raven "github.com/getsentry/raven-go"
 	"github.com/go-macaron/binding"
 	"github.com/go-xorm/core"
 	"github.com/go-xorm/xorm"
@@ -437,6 +438,7 @@ func LoadAuthSources() {
 
 	paths, err := com.GetFileListBySuffix(authdPath, ".conf")
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to list authentication sources: %v", err)
 	}
 
@@ -445,6 +447,7 @@ func LoadAuthSources() {
 	for _, fpath := range paths {
 		authSource, err := ini.Load(fpath)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Failed to load authentication source: %v", err)
 		}
 		authSource.NameMapper = ini.TitleUnderscore
@@ -464,6 +467,7 @@ func LoadAuthSources() {
 
 		fi, err := os.Stat(fpath)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Failed to load authentication source: %v", err)
 		}
 		loginSource.Updated = fi.ModTime()
@@ -484,10 +488,12 @@ func LoadAuthSources() {
 			loginSource.Type = LOGIN_PAM
 			loginSource.Cfg = &PAMConfig{}
 		default:
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Failed to load authentication source: unknown type '%s'", authType)
 		}
 
 		if err = authSource.Section("config").MapTo(loginSource.Cfg); err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Failed to parse authentication source 'config': %v", err)
 		}
 

+ 9 - 0
models/repo.go

@@ -23,6 +23,7 @@ import (
 	"time"
 
 	"github.com/Unknwon/com"
+	raven "github.com/getsentry/raven-go"
 	"github.com/go-xorm/xorm"
 	"github.com/mcuadros/go-version"
 	"github.com/nfnt/resize"
@@ -52,12 +53,14 @@ func LoadRepoConfig() {
 	for i, t := range types {
 		files, err := bindata.AssetDir("conf/" + t)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(4, "Fail to get %s files: %v", t, err)
 		}
 		customPath := path.Join(setting.CustomPath, "conf", t)
 		if com.IsDir(customPath) {
 			customFiles, err := com.StatDir(customPath)
 			if err != nil {
+				raven.CaptureErrorAndWait(err, nil)
 				log.Fatal(4, "Fail to get custom %s files: %v", t, err)
 			}
 
@@ -99,6 +102,7 @@ func NewRepoContext() {
 
 	// Check Git installation.
 	if _, err := exec.LookPath("git"); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Fail to test 'git' command: %v (forgotten install?)", err)
 	}
 
@@ -106,11 +110,13 @@ func NewRepoContext() {
 	var err error
 	setting.Git.Version, err = git.BinVersion()
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Fail to get Git version: %v", err)
 	}
 
 	log.Info("Git Version: %s", setting.Git.Version)
 	if version.Compare("1.7.1", setting.Git.Version, ">") {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Gitote requires Git version greater or equal to 1.7.1")
 	}
 	git.HookDir = "custom_hooks"
@@ -123,10 +129,12 @@ func NewRepoContext() {
 			// ExitError indicates this config is not set
 			if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" {
 				if _, stderr, gerr := process.Exec("NewRepoContext(set "+configKey+")", "git", "config", "--global", configKey, defaultValue); gerr != nil {
+					raven.CaptureErrorAndWait(err, nil)
 					log.Fatal(4, "Fail to set git %s(%s): %s", configKey, gerr, stderr)
 				}
 				log.Info("Git config %s set to %s", configKey, defaultValue)
 			} else {
+				raven.CaptureErrorAndWait(err, nil)
 				log.Fatal(4, "Fail to get git %s(%s): %s", configKey, err, stderr)
 			}
 		}
@@ -135,6 +143,7 @@ func NewRepoContext() {
 	// Set git some configurations.
 	if _, stderr, err := process.Exec("NewRepoContext(git config --global core.quotepath false)",
 		"git", "config", "--global", "core.quotepath", "false"); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr)
 	}
 

+ 5 - 0
pkg/cron/cron.go

@@ -5,6 +5,7 @@ import (
 	"gitote/gitote/pkg/setting"
 	"time"
 
+	raven "github.com/getsentry/raven-go"
 	"gitlab.com/gitote/cron"
 	log "gopkg.in/clog.v1"
 )
@@ -19,6 +20,7 @@ func NewContext() {
 	if setting.Cron.UpdateMirror.Enabled {
 		entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Cron.(update mirrors): %v", err)
 		}
 		if setting.Cron.UpdateMirror.RunAtStart {
@@ -30,6 +32,7 @@ func NewContext() {
 	if setting.Cron.RepoHealthCheck.Enabled {
 		entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, models.GitFsck)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Cron.(repository health check): %v", err)
 		}
 		if setting.Cron.RepoHealthCheck.RunAtStart {
@@ -41,6 +44,7 @@ func NewContext() {
 	if setting.Cron.CheckRepoStats.Enabled {
 		entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, models.CheckRepoStats)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Cron.(check repository statistics): %v", err)
 		}
 		if setting.Cron.CheckRepoStats.RunAtStart {
@@ -52,6 +56,7 @@ func NewContext() {
 	if setting.Cron.RepoArchiveCleanup.Enabled {
 		entry, err = c.AddFunc("Repository archive cleanup", setting.Cron.RepoArchiveCleanup.Schedule, models.DeleteOldRepositoryArchives)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Cron.(repository archive cleanup): %v", err)
 		}
 		if setting.Cron.RepoArchiveCleanup.RunAtStart {

+ 4 - 0
pkg/httplib/httplib.go

@@ -18,6 +18,7 @@ import (
 	"sync"
 	"time"
 
+	raven "github.com/getsentry/raven-go"
 	"github.com/json-iterator/go"
 )
 
@@ -258,16 +259,19 @@ func (r *Request) getResponse() (*http.Response, error) {
 				for formname, filename := range r.files {
 					fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
 					if err != nil {
+						raven.CaptureErrorAndWait(err, nil)
 						log.Fatal(err)
 					}
 					fh, err := os.Open(filename)
 					if err != nil {
+						raven.CaptureErrorAndWait(err, nil)
 						log.Fatal(err)
 					}
 					//iocopy
 					_, err = io.Copy(fileWriter, fh)
 					fh.Close()
 					if err != nil {
+						raven.CaptureErrorAndWait(err, nil)
 						log.Fatal(err)
 					}
 				}

+ 31 - 0
pkg/setting/setting.go

@@ -16,6 +16,7 @@ import (
 	"time"
 
 	"github.com/Unknwon/com"
+	raven "github.com/getsentry/raven-go"
 	_ "github.com/go-macaron/cache/memcache"
 	_ "github.com/go-macaron/cache/redis"
 	"github.com/go-macaron/session"
@@ -339,6 +340,7 @@ func init() {
 
 	var err error
 	if AppPath, err = execPath(); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to get app path: %v\n", err)
 	}
 
@@ -386,6 +388,7 @@ func getOpenSSHVersion() string {
 	// Note: somehow version is printed to stderr
 	_, stderr, err := process.Exec("getOpenSSHVersion", "ssh", "-V")
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to get OpenSSH version: %v - %s", err, stderr)
 	}
 
@@ -400,6 +403,7 @@ func getOpenSSHVersion() string {
 func NewContext() {
 	workDir, err := WorkDir()
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to get work directory: %v", err)
 	}
 
@@ -407,6 +411,7 @@ func NewContext() {
 		IgnoreInlineComment: true,
 	}, bindata.MustAsset("conf/app.ini"))
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to parse 'conf/app.ini': %v", err)
 	}
 
@@ -421,6 +426,7 @@ func NewContext() {
 
 	if com.IsFile(CustomConf) {
 		if err = Cfg.Append(CustomConf); err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Fail to load custom conf '%s': %v", CustomConf, err)
 		}
 	} else {
@@ -430,6 +436,7 @@ func NewContext() {
 
 	homeDir, err := com.HomeDir()
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to get home directory: %v", err)
 	}
 	homeDir = strings.Replace(homeDir, "\\", "/", -1)
@@ -446,6 +453,7 @@ func NewContext() {
 	// Check if has app suburl.
 	url, err := url.Parse(AppURL)
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Invalid ROOT_URL '%s': %s", AppURL, err)
 	}
 	// Suburl should start with '/' and end without '/', such as '/{subpath}'.
@@ -466,6 +474,7 @@ func NewContext() {
 		UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
 		UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
 		if err != nil || UnixSocketPermissionParsed > 0777 {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Fail to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
 		}
 		UnixSocketPermission = uint32(UnixSocketPermissionParsed)
@@ -492,6 +501,7 @@ func NewContext() {
 	SSH.ServerCiphers = sec.Key("SSH_SERVER_CIPHERS").Strings(",")
 	SSH.KeyTestPath = os.TempDir()
 	if err = Cfg.Section("server").MapTo(&SSH); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to map SSH settings: %v", err)
 	}
 	if SSH.Disabled {
@@ -501,8 +511,10 @@ func NewContext() {
 
 	if !SSH.Disabled && !SSH.StartBuiltinServer {
 		if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Fail to create '%s': %v", SSH.RootPath, err)
 		} else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Fail to create '%s': %v", SSH.KeyTestPath, err)
 		}
 	}
@@ -575,6 +587,7 @@ func NewContext() {
 	if InstallLock {
 		currentUser, match := IsRunUserMatchCurrentUser(RunUser)
 		if !match {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Expect user '%s' but current user is: %s", RunUser, currentUser)
 		}
 	}
@@ -592,10 +605,13 @@ func NewContext() {
 	}
 	ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
 	if err = Cfg.Section("repository").MapTo(&Repository); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to map Repository settings: %v", err)
 	} else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to map Repository.Editor settings: %v", err)
 	} else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Fail to map Repository.Upload settings: %v", err)
 	}
 
@@ -649,28 +665,40 @@ func NewContext() {
 	}
 
 	if err = Cfg.Section("http").MapTo(&HTTP); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map HTTP settings: %v", err)
 	} else if err = Cfg.Section("webhook").MapTo(&Webhook); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Webhook settings: %v", err)
 	} else if err = Cfg.Section("release.attachment").MapTo(&Release.Attachment); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Release.Attachment settings: %v", err)
 	} else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Markdown settings: %v", err)
 	} else if err = Cfg.Section("smartypants").MapTo(&Smartypants); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Smartypants settings: %v", err)
 	} else if err = Cfg.Section("admin").MapTo(&Admin); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Admin settings: %v", err)
 	} else if err = Cfg.Section("cron").MapTo(&Cron); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Cron settings: %v", err)
 	} else if err = Cfg.Section("git").MapTo(&Git); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Git settings: %v", err)
 	} else if err = Cfg.Section("mirror").MapTo(&Mirror); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Mirror settings: %v", err)
 	} else if err = Cfg.Section("api").MapTo(&API); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map API settings: %v", err)
 	} else if err = Cfg.Section("ui").MapTo(&UI); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map UI settings: %v", err)
 	} else if err = Cfg.Section("prometheus").MapTo(&Prometheus); err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(2, "Failed to map Prometheus settings: %v", err)
 	}
 
@@ -734,6 +762,7 @@ func newLogService() {
 		mode = strings.ToLower(strings.TrimSpace(mode))
 		sec, err := Cfg.GetSection("log." + mode)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Unknown logger mode: %s", mode)
 		}
 
@@ -759,6 +788,7 @@ func newLogService() {
 		case log.FILE:
 			logPath := path.Join(LogRootPath, "gitote.log")
 			if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
+				raven.CaptureErrorAndWait(err, nil)
 				log.Fatal(2, "Fail to create log directory '%s': %v", path.Dir(logPath), err)
 			}
 
@@ -877,6 +907,7 @@ func newMailService() {
 	if len(MailService.From) > 0 {
 		parsed, err := mail.ParseAddress(MailService.From)
 		if err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Invalid mailer.FROM (%s): %v", MailService.From, err)
 		}
 		MailService.FromEmail = parsed.Address

+ 2 - 0
pkg/ssh/ssh.go

@@ -13,6 +13,7 @@ import (
 	"strings"
 
 	"github.com/Unknwon/com"
+	raven "github.com/getsentry/raven-go"
 	"golang.org/x/crypto/ssh"
 	log "gopkg.in/clog.v1"
 )
@@ -108,6 +109,7 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) {
 func listen(config *ssh.ServerConfig, host string, port int) {
 	listener, err := net.Listen("tcp", host+":"+com.ToStr(port))
 	if err != nil {
+		raven.CaptureErrorAndWait(err, nil)
 		log.Fatal(4, "Fail to start SSH server: %v", err)
 	}
 	for {

+ 2 - 0
routes/install.go

@@ -19,6 +19,7 @@ import (
 	"strings"
 
 	"github.com/Unknwon/com"
+	raven "github.com/getsentry/raven-go"
 	"github.com/go-xorm/xorm"
 	"gitlab.com/gitote/git-module"
 	log "gopkg.in/clog.v1"
@@ -57,6 +58,7 @@ func GlobalInit() {
 		highlight.NewContext()
 		markup.NewSanitizer()
 		if err := models.NewEngine(); err != nil {
+			raven.CaptureErrorAndWait(err, nil)
 			log.Fatal(2, "Fail to initialize ORM engine: %v", err)
 		}
 		models.HasEngine = true