| @@ -620,6 +620,18 @@ func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) { | |||
| return getRepoCloudBrain(cb) | |||
| } | |||
| func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) { | |||
| cloudBrains := make([]*Cloudbrain, 0) | |||
| err := x.Cols("job_id", "status", "type").Where("user_id=? AND (status =? OR status=?)", userID, string(JobRunning), string(JobWaiting)).Find(&cloudBrains) | |||
| return cloudBrains, err | |||
| } | |||
| func GetCloudbrainsNeededStopByRepoID(repoID int64) ([]*Cloudbrain, error) { | |||
| cloudBrains := make([]*Cloudbrain, 0) | |||
| err := x.Cols("job_id", "status", "type").Where("repo_id=? AND (status =? OR status=?)", repoID, string(JobRunning), string(JobWaiting)).Find(&cloudBrains) | |||
| return cloudBrains, err | |||
| } | |||
| func SetCloudbrainStatusByJobID(jobID string, status CloudbrainStatus) (err error) { | |||
| cb := &Cloudbrain{JobID: jobID, Status: string(status)} | |||
| _, err = x.Cols("status").Where("cloudbrain.job_id=?", jobID).Update(cb) | |||
| @@ -59,6 +59,9 @@ var ( | |||
| x *xorm.Engine | |||
| tables []interface{} | |||
| xStatistic *xorm.Engine | |||
| tablesStatistic []interface{} | |||
| // HasEngine specifies if we have a xorm.Engine | |||
| HasEngine bool | |||
| ) | |||
| @@ -132,14 +135,17 @@ func init() { | |||
| new(RecommendOrg), | |||
| ) | |||
| tablesStatistic = append(tablesStatistic, | |||
| new(FileChunk)) | |||
| gonicNames := []string{"SSL", "UID"} | |||
| for _, name := range gonicNames { | |||
| names.LintGonicMapper[name] = true | |||
| } | |||
| } | |||
| func getEngine() (*xorm.Engine, error) { | |||
| connStr, err := setting.DBConnStr() | |||
| func getEngine(database *setting.DBInfo) (*xorm.Engine, error) { | |||
| connStr, err := setting.DBConnStr(database) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -153,14 +159,12 @@ func getEngine() (*xorm.Engine, error) { | |||
| } | |||
| engine.SetSchema(setting.Database.Schema) | |||
| HasEngine = true | |||
| return engine, nil | |||
| } | |||
| // NewTestEngine sets a new test xorm.Engine | |||
| func NewTestEngine(x *xorm.Engine) (err error) { | |||
| x, err = getEngine() | |||
| x, err = getEngine(setting.Database) | |||
| if err != nil { | |||
| return fmt.Errorf("Connect to database: %v", err) | |||
| } | |||
| @@ -171,43 +175,80 @@ func NewTestEngine(x *xorm.Engine) (err error) { | |||
| return x.StoreEngine("InnoDB").Sync2(tables...) | |||
| } | |||
| // SetEngine sets the xorm.Engine | |||
| // setEngine sets the xorm.Engine | |||
| func setEngine(engine *xorm.Engine, table []interface{}, database *setting.DBInfo) (err error) { | |||
| engine.SetMapper(names.GonicMapper{}) | |||
| // WARNING: for serv command, MUST remove the output to os.stdout, | |||
| // so use log file to instead print to stdout. | |||
| engine.SetLogger(NewXORMLogger(setting.Database.LogSQL)) | |||
| engine.ShowSQL(setting.Database.LogSQL) | |||
| engine.SetMaxOpenConns(setting.Database.MaxOpenConns) | |||
| engine.SetMaxIdleConns(setting.Database.MaxIdleConns) | |||
| engine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) | |||
| engine.Sync2(table...) | |||
| MigrateCustom(engine) | |||
| return nil | |||
| } | |||
| func SetEngine() (err error) { | |||
| x, err = getEngine() | |||
| x, err = getEngine(setting.Database) | |||
| if err != nil { | |||
| return fmt.Errorf("Failed to connect to database: %v", err) | |||
| } | |||
| if err = setEngine(x, tables, setting.Database); err != nil { | |||
| return err | |||
| } | |||
| xStatistic, err = getEngine(setting.DatabaseStatistic) | |||
| if err != nil { | |||
| return fmt.Errorf("Failed to connect to database: %v", err) | |||
| } | |||
| if err = setEngine(xStatistic, tablesStatistic, setting.DatabaseStatistic); err != nil { | |||
| return err | |||
| } | |||
| x.SetMapper(names.GonicMapper{}) | |||
| // WARNING: for serv command, MUST remove the output to os.stdout, | |||
| // so use log file to instead print to stdout. | |||
| x.SetLogger(NewXORMLogger(setting.Database.LogSQL)) | |||
| x.ShowSQL(setting.Database.LogSQL) | |||
| x.SetMaxOpenConns(setting.Database.MaxOpenConns) | |||
| x.SetMaxIdleConns(setting.Database.MaxIdleConns) | |||
| x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) | |||
| x.Sync2(tables...) | |||
| MigrateCustom(x) | |||
| return nil | |||
| } | |||
| // NewEngine initializes a new xorm.Engine | |||
| func NewEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) { | |||
| if err = SetEngine(); err != nil { | |||
| x, err = getEngine(setting.Database) | |||
| if err != nil { | |||
| return fmt.Errorf("Failed to connect to database: %v", err) | |||
| } | |||
| if err = newEngine(ctx, migrateFunc, x, tables, setting.Database); err != nil { | |||
| return fmt.Errorf("newEngine failed: %v", err) | |||
| } | |||
| xStatistic, err = getEngine(setting.DatabaseStatistic) | |||
| if err != nil { | |||
| return fmt.Errorf("Failed to connect to database: %v", err) | |||
| } | |||
| if err = newEngine(ctx, migrateFunc, xStatistic, tablesStatistic, setting.DatabaseStatistic); err != nil { | |||
| return fmt.Errorf("newEngine statistic failed: %v", err) | |||
| } | |||
| HasEngine = true | |||
| return nil | |||
| } | |||
| // newEngine initializes a new xorm.Engine | |||
| func newEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error, engine *xorm.Engine, table []interface{}, database *setting.DBInfo) (err error) { | |||
| if err = setEngine(engine, table, database); err != nil { | |||
| return err | |||
| } | |||
| x.SetDefaultContext(ctx) | |||
| engine.SetDefaultContext(ctx) | |||
| if err = x.Ping(); err != nil { | |||
| if err = engine.Ping(); err != nil { | |||
| return err | |||
| } | |||
| if err = migrateFunc(x); err != nil { | |||
| if err = migrateFunc(engine); err != nil { | |||
| return fmt.Errorf("migrate: %v", err) | |||
| } | |||
| if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil { | |||
| if err = engine.StoreEngine("InnoDB").Sync2(table...); err != nil { | |||
| return fmt.Errorf("sync database struct error: %v", err) | |||
| } | |||
| @@ -257,6 +298,11 @@ func Ping() error { | |||
| if x != nil { | |||
| return x.Ping() | |||
| } | |||
| if xStatistic != nil { | |||
| return xStatistic.Ping() | |||
| } | |||
| return errors.New("database not configured") | |||
| } | |||
| @@ -1424,6 +1424,12 @@ func GetAllRepositories() ([]*Repository, error) { | |||
| return getALLRepositories(x) | |||
| } | |||
| func GetAllRepositoriesByFilterCols(columns ...string) ([]*Repository, error) { | |||
| repos := make([]*Repository, 0, 1000) | |||
| return repos, x.Cols(columns...).Find(&repos) | |||
| } | |||
| func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err error) { | |||
| repo.LowerName = strings.ToLower(repo.Name) | |||
| @@ -0,0 +1,39 @@ | |||
| package models | |||
| import "code.gitea.io/gitea/modules/git" | |||
| func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | |||
| return git.GetRepoKPIStats(repo.RepoPath()) | |||
| } | |||
| func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) { | |||
| authors := make(map[string]*git.UserKPIStats) | |||
| repositorys, err := GetAllRepositoriesByFilterCols("owner_name", "name") | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| for _, repository := range repositorys { | |||
| authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath()) | |||
| if err1 != nil { | |||
| return nil, err | |||
| } | |||
| for key, value := range authorsOneRepo { | |||
| if _, ok := authors[key]; !ok { | |||
| authors[key] = &git.UserKPIStats{ | |||
| Name: value.Name, | |||
| Email: value.Email, | |||
| Commits: 0, | |||
| CommitLines: 0, | |||
| } | |||
| } | |||
| authors[key].Commits += value.Commits | |||
| authors[key].CommitLines += value.CommitLines | |||
| } | |||
| } | |||
| return authors, nil | |||
| } | |||
| @@ -0,0 +1,258 @@ | |||
| package git | |||
| import ( | |||
| "bufio" | |||
| "bytes" | |||
| "fmt" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| type RepoKPIStats struct { | |||
| Contributors int64 | |||
| KeyContributors int64 | |||
| ContributorsAdded int64 | |||
| CommitsAdded int64 | |||
| CommitLinesModified int64 | |||
| Authors []*UserKPITypeStats | |||
| } | |||
| type UserKPIStats struct { | |||
| Name string | |||
| Email string | |||
| Commits int64 | |||
| CommitLines int64 | |||
| } | |||
| type UserKPITypeStats struct { | |||
| UserKPIStats | |||
| isNewContributor bool //是否是4个月内的新增贡献者 | |||
| } | |||
| func GetRepoKPIStats(repoPath string) (*RepoKPIStats, error) { | |||
| stats := &RepoKPIStats{} | |||
| contributors, err := GetContributors(repoPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| timeUntil := time.Now() | |||
| fourMonthAgo := timeUntil.AddDate(0, -4, 0) | |||
| recentlyContributors, err := getContributors(repoPath, fourMonthAgo) | |||
| newContributersDict := make(map[string]struct{}) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if contributors != nil { | |||
| stats.Contributors = int64(len(contributors)) | |||
| for _, contributor := range contributors { | |||
| if contributor.CommitCnt >= 3 { | |||
| stats.KeyContributors++ | |||
| } | |||
| if recentlyContributors != nil { | |||
| for _, recentlyContributor := range recentlyContributors { | |||
| if recentlyContributor.Email == contributor.Email && recentlyContributor.CommitCnt == contributor.CommitCnt { | |||
| stats.ContributorsAdded++ | |||
| newContributersDict[recentlyContributor.Email] = struct{}{} | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| err = setRepoKPIStats(repoPath, fourMonthAgo, stats, newContributersDict) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||
| } | |||
| return stats, nil | |||
| } | |||
| //获取一天内的用户贡献指标 | |||
| func GetUserKPIStats(repoPath string) (map[string]*UserKPIStats, error) { | |||
| timeUntil := time.Now() | |||
| oneDayAgo := timeUntil.AddDate(0, 0, -1) | |||
| since := oneDayAgo.Format(time.RFC3339) | |||
| args := []string{"log", "--numstat", "--no-merges", "--branches=*", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--since='%s'", since)} | |||
| stdout, err := NewCommand(args...).RunInDirBytes(repoPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| scanner := bufio.NewScanner(bytes.NewReader(stdout)) | |||
| scanner.Split(bufio.ScanLines) | |||
| usersKPIStatses := make(map[string]*UserKPIStats) | |||
| var author string | |||
| p := 0 | |||
| var email string | |||
| for scanner.Scan() { | |||
| l := strings.TrimSpace(scanner.Text()) | |||
| if l == "---" { | |||
| p = 1 | |||
| } else if p == 0 { | |||
| continue | |||
| } else { | |||
| p++ | |||
| } | |||
| if p > 4 && len(l) == 0 { | |||
| continue | |||
| } | |||
| switch p { | |||
| case 1: // Separator | |||
| case 2: // Commit sha-1 | |||
| case 3: // Author | |||
| author = l | |||
| case 4: // E-mail | |||
| email = strings.ToLower(l) | |||
| if _, ok := usersKPIStatses[email]; !ok { | |||
| usersKPIStatses[email] = &UserKPIStats{ | |||
| Name: author, | |||
| Email: email, | |||
| Commits: 0, | |||
| CommitLines: 0, | |||
| } | |||
| } | |||
| usersKPIStatses[email].Commits++ | |||
| default: // Changed file | |||
| if parts := strings.Fields(l); len(parts) >= 3 { | |||
| if parts[0] != "-" { | |||
| if c, err := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 64); err == nil { | |||
| usersKPIStatses[email].CommitLines += c | |||
| } | |||
| } | |||
| if parts[1] != "-" { | |||
| if c, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64); err == nil { | |||
| usersKPIStatses[email].CommitLines += c | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return usersKPIStatses, nil | |||
| } | |||
| func setRepoKPIStats(repoPath string, fromTime time.Time, stats *RepoKPIStats, newContributers map[string]struct{}) error { | |||
| since := fromTime.Format(time.RFC3339) | |||
| args := []string{"log", "--numstat", "--no-merges", "--branches=*", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--since='%s'", since)} | |||
| stdout, err := NewCommand(args...).RunInDirBytes(repoPath) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| scanner := bufio.NewScanner(bytes.NewReader(stdout)) | |||
| scanner.Split(bufio.ScanLines) | |||
| authors := make(map[string]*UserKPITypeStats) | |||
| var author string | |||
| p := 0 | |||
| var email string | |||
| for scanner.Scan() { | |||
| l := strings.TrimSpace(scanner.Text()) | |||
| if l == "---" { | |||
| p = 1 | |||
| } else if p == 0 { | |||
| continue | |||
| } else { | |||
| p++ | |||
| } | |||
| if p > 4 && len(l) == 0 { | |||
| continue | |||
| } | |||
| switch p { | |||
| case 1: // Separator | |||
| case 2: // Commit sha-1 | |||
| stats.CommitsAdded++ | |||
| case 3: // Author | |||
| author = l | |||
| case 4: // E-mail | |||
| email = strings.ToLower(l) | |||
| if _, ok := authors[email]; !ok { | |||
| authors[email] = &UserKPITypeStats{ | |||
| UserKPIStats: UserKPIStats{ | |||
| Name: author, | |||
| Email: email, | |||
| Commits: 0, | |||
| CommitLines: 0, | |||
| }, | |||
| isNewContributor: false, | |||
| } | |||
| } | |||
| if _, ok := newContributers[email]; ok { | |||
| authors[email].isNewContributor = true | |||
| } | |||
| authors[email].Commits++ | |||
| default: // Changed file | |||
| if parts := strings.Fields(l); len(parts) >= 3 { | |||
| if parts[0] != "-" { | |||
| if c, err := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 64); err == nil { | |||
| stats.CommitLinesModified += c | |||
| authors[email].CommitLines += c | |||
| } | |||
| } | |||
| if parts[1] != "-" { | |||
| if c, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64); err == nil { | |||
| stats.CommitLinesModified += c | |||
| authors[email].CommitLines += c | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| a := make([]*UserKPITypeStats, 0, len(authors)) | |||
| for _, v := range authors { | |||
| a = append(a, v) | |||
| } | |||
| // Sort authors descending depending on commit count | |||
| sort.Slice(a, func(i, j int) bool { | |||
| return a[i].Commits > a[j].Commits | |||
| }) | |||
| stats.Authors = a | |||
| return nil | |||
| } | |||
| func getContributors(repoPath string, fromTime time.Time) ([]Contributor, error) { | |||
| since := fromTime.Format(time.RFC3339) | |||
| cmd := NewCommand("shortlog", "-sne", "--all", fmt.Sprintf("--since='%s'", since)) | |||
| stdout, err := cmd.RunInDir(repoPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| stdout = strings.Trim(stdout, "\n") | |||
| contributorRows := strings.Split(stdout, "\n") | |||
| if len(contributorRows) > 0 { | |||
| contributorsInfo := make([]Contributor, len(contributorRows)) | |||
| for i := 0; i < len(contributorRows); i++ { | |||
| var oneCount string = strings.Trim(contributorRows[i], " ") | |||
| if strings.Index(oneCount, "\t") < 0 { | |||
| continue | |||
| } | |||
| number := oneCount[0:strings.Index(oneCount, "\t")] | |||
| commitCnt, _ := strconv.Atoi(number) | |||
| committer := oneCount[strings.Index(oneCount, "\t")+1 : strings.LastIndex(oneCount, " ")] | |||
| committer = strings.Trim(committer, " ") | |||
| email := oneCount[strings.Index(oneCount, "<")+1 : strings.Index(oneCount, ">")] | |||
| contributorsInfo[i] = Contributor{ | |||
| commitCnt, committer, email, | |||
| } | |||
| } | |||
| return contributorsInfo, nil | |||
| } | |||
| return nil, nil | |||
| } | |||
| @@ -24,111 +24,119 @@ var ( | |||
| EnableSQLite3 bool | |||
| // Database holds the database settings | |||
| Database = struct { | |||
| Type string | |||
| Host string | |||
| Name string | |||
| User string | |||
| Passwd string | |||
| Schema string | |||
| SSLMode string | |||
| Path string | |||
| LogSQL bool | |||
| Charset string | |||
| Timeout int // seconds | |||
| UseSQLite3 bool | |||
| UseMySQL bool | |||
| UseMSSQL bool | |||
| UsePostgreSQL bool | |||
| DBConnectRetries int | |||
| DBConnectBackoff time.Duration | |||
| MaxIdleConns int | |||
| MaxOpenConns int | |||
| ConnMaxLifetime time.Duration | |||
| IterateBufferSize int | |||
| }{ | |||
| Timeout: 500, | |||
| } | |||
| Database *DBInfo | |||
| DatabaseStatistic *DBInfo | |||
| ) | |||
| type DBInfo struct { | |||
| Type string | |||
| Host string | |||
| Name string | |||
| User string | |||
| Passwd string | |||
| Schema string | |||
| SSLMode string | |||
| Path string | |||
| LogSQL bool | |||
| Charset string | |||
| Timeout int // seconds | |||
| UseSQLite3 bool | |||
| UseMySQL bool | |||
| UseMSSQL bool | |||
| UsePostgreSQL bool | |||
| DBConnectRetries int | |||
| DBConnectBackoff time.Duration | |||
| MaxIdleConns int | |||
| MaxOpenConns int | |||
| ConnMaxLifetime time.Duration | |||
| IterateBufferSize int | |||
| } | |||
| // GetDBTypeByName returns the dataase type as it defined on XORM according the given name | |||
| func GetDBTypeByName(name string) string { | |||
| return dbTypes[name] | |||
| } | |||
| // InitDBConfig loads the database settings | |||
| func InitDBConfig() { | |||
| sec := Cfg.Section("database") | |||
| Database.Type = sec.Key("DB_TYPE").String() | |||
| switch Database.Type { | |||
| // initDBConfig loads the database settings | |||
| func initDBConfig(section string, database *DBInfo) { | |||
| sec := Cfg.Section(section) | |||
| database.Type = sec.Key("DB_TYPE").String() | |||
| switch database.Type { | |||
| case "sqlite3": | |||
| Database.UseSQLite3 = true | |||
| database.UseSQLite3 = true | |||
| case "mysql": | |||
| Database.UseMySQL = true | |||
| database.UseMySQL = true | |||
| case "postgres": | |||
| Database.UsePostgreSQL = true | |||
| database.UsePostgreSQL = true | |||
| case "mssql": | |||
| Database.UseMSSQL = true | |||
| database.UseMSSQL = true | |||
| } | |||
| Database.Host = sec.Key("HOST").String() | |||
| Database.Name = sec.Key("NAME").String() | |||
| Database.User = sec.Key("USER").String() | |||
| if len(Database.Passwd) == 0 { | |||
| Database.Passwd = sec.Key("PASSWD").String() | |||
| database.Host = sec.Key("HOST").String() | |||
| database.Name = sec.Key("NAME").String() | |||
| database.User = sec.Key("USER").String() | |||
| if len(database.Passwd) == 0 { | |||
| database.Passwd = sec.Key("PASSWD").String() | |||
| } | |||
| Database.Schema = sec.Key("SCHEMA").String() | |||
| Database.SSLMode = sec.Key("SSL_MODE").MustString("disable") | |||
| Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"}) | |||
| Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db")) | |||
| Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500) | |||
| Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2) | |||
| if Database.UseMySQL { | |||
| Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second) | |||
| database.Schema = sec.Key("SCHEMA").String() | |||
| database.SSLMode = sec.Key("SSL_MODE").MustString("disable") | |||
| database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"}) | |||
| database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db")) | |||
| database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500) | |||
| database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2) | |||
| if database.UseMySQL { | |||
| database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second) | |||
| } else { | |||
| Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(0) | |||
| database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(0) | |||
| } | |||
| Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0) | |||
| database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0) | |||
| Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) | |||
| Database.LogSQL = sec.Key("LOG_SQL").MustBool(true) | |||
| Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10) | |||
| Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second) | |||
| database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) | |||
| database.LogSQL = sec.Key("LOG_SQL").MustBool(true) | |||
| database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10) | |||
| database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second) | |||
| } | |||
| func InitDBConfig() { | |||
| Database = new(DBInfo) | |||
| DatabaseStatistic = new(DBInfo) | |||
| initDBConfig("database", Database) | |||
| initDBConfig("database_statistic", DatabaseStatistic) | |||
| } | |||
| // DBConnStr returns database connection string | |||
| func DBConnStr() (string, error) { | |||
| func DBConnStr(database *DBInfo) (string, error) { | |||
| connStr := "" | |||
| var Param = "?" | |||
| if strings.Contains(Database.Name, Param) { | |||
| if strings.Contains(database.Name, Param) { | |||
| Param = "&" | |||
| } | |||
| switch Database.Type { | |||
| switch database.Type { | |||
| case "mysql": | |||
| connType := "tcp" | |||
| if Database.Host[0] == '/' { // looks like a unix socket | |||
| if database.Host[0] == '/' { // looks like a unix socket | |||
| connType = "unix" | |||
| } | |||
| tls := Database.SSLMode | |||
| tls := database.SSLMode | |||
| if tls == "disable" { // allow (Postgres-inspired) default value to work in MySQL | |||
| tls = "false" | |||
| } | |||
| connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%scharset=%s&parseTime=true&tls=%s", | |||
| Database.User, Database.Passwd, connType, Database.Host, Database.Name, Param, Database.Charset, tls) | |||
| database.User, database.Passwd, connType, database.Host, database.Name, Param, database.Charset, tls) | |||
| case "postgres": | |||
| connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Param, Database.SSLMode) | |||
| connStr = getPostgreSQLConnectionString(database.Host, database.User, database.Passwd, database.Name, Param, database.SSLMode) | |||
| case "mssql": | |||
| host, port := ParseMSSQLHostPort(Database.Host) | |||
| connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd) | |||
| host, port := ParseMSSQLHostPort(database.Host) | |||
| connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, database.Name, database.User, database.Passwd) | |||
| case "sqlite3": | |||
| if !EnableSQLite3 { | |||
| return "", errors.New("this binary version does not build support for SQLite3") | |||
| } | |||
| if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil { | |||
| if err := os.MkdirAll(path.Dir(database.Path), os.ModePerm); err != nil { | |||
| return "", fmt.Errorf("Failed to create directories: %v", err) | |||
| } | |||
| connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate", Database.Path, Database.Timeout) | |||
| connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate", database.Path, database.Timeout) | |||
| default: | |||
| return "", fmt.Errorf("Unknown database type: %s", Database.Type) | |||
| return "", fmt.Errorf("Unknown database type: %s", database.Type) | |||
| } | |||
| return connStr, nil | |||
| @@ -13,6 +13,8 @@ import ( | |||
| "strings" | |||
| "time" | |||
| "code.gitea.io/gitea/modules/modelarts" | |||
| "code.gitea.io/gitea/modules/git" | |||
| "code.gitea.io/gitea/modules/storage" | |||
| @@ -361,6 +363,58 @@ func CloudBrainStop(ctx *context.Context) { | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/cloudbrain") | |||
| } | |||
| func StopJobsByUserID(userID int64) { | |||
| cloudBrains, err := models.GetCloudbrainsNeededStopByUserID(userID) | |||
| if err != nil { | |||
| log.Warn("Failed to get cloudBrain info", err) | |||
| return | |||
| } | |||
| StopJobs(cloudBrains) | |||
| } | |||
| func StopJobsByRepoID(repoID int64) { | |||
| cloudBrains, err := models.GetCloudbrainsNeededStopByRepoID(repoID) | |||
| if err != nil { | |||
| log.Warn("Failed to get cloudBrain info", err) | |||
| return | |||
| } | |||
| StopJobs(cloudBrains) | |||
| } | |||
| /** | |||
| */ | |||
| func StopJobs(cloudBrains []*models.Cloudbrain) { | |||
| for _, taskInfo := range cloudBrains { | |||
| if taskInfo.Type == models.TypeCloudBrainOne { | |||
| err := cloudbrain.StopJob(taskInfo.JobID) | |||
| logErrorAndUpdateJobStatus(err, taskInfo) | |||
| } else { | |||
| param := models.NotebookAction{ | |||
| Action: models.ActionStop, | |||
| } | |||
| _, err := modelarts.StopJob(taskInfo.JobID, param) | |||
| logErrorAndUpdateJobStatus(err, taskInfo) | |||
| } | |||
| } | |||
| } | |||
| func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) { | |||
| if err != nil { | |||
| log.Warn("Failed to stop cloudBrain job:"+taskInfo.JobID, err) | |||
| } else { | |||
| taskInfo.Status = string(models.JobStopped) | |||
| err = models.UpdateJob(taskInfo) | |||
| if err != nil { | |||
| log.Warn("UpdateJob failed", err) | |||
| } | |||
| } | |||
| } | |||
| func CloudBrainDel(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| @@ -440,6 +440,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
| return | |||
| } | |||
| log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name) | |||
| go StopJobsByRepoID(repo.ID) | |||
| ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success")) | |||
| ctx.Redirect(ctx.Repo.Owner.DashboardLink()) | |||
| @@ -11,6 +11,8 @@ import ( | |||
| "net/http" | |||
| "strings" | |||
| "code.gitea.io/gitea/routers/repo" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/auth" | |||
| "code.gitea.io/gitea/modules/auth/oauth2" | |||
| @@ -1056,6 +1058,7 @@ func SignOut(ctx *context.Context) { | |||
| }) | |||
| } | |||
| HandleSignOut(ctx) | |||
| go repo.StopJobsByUserID(ctx.User.ID) | |||
| ctx.Redirect(setting.AppSubURL + "/") | |||
| } | |||