diff --git a/README.md b/README.md index 4219e4ed03c9..8c971fdb857e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ There are some very good products in this category such as [gitlab](http://gitla - Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, develop specification, change log and road map. - See [Trello Broad](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team. -- Try it before anything? Go down to **Installation -> Install from binary** section!. +- Try it before anything? Do it [online](http://try.gogits.org/Unknown/gogs) or go down to **Installation -> Install from binary** section! - Having troubles? Get help from [Troubleshooting](https://github.com/gogits/gogs/wiki/Troubleshooting). ## Features diff --git a/gogs.go b/gogs.go index a600c53d1229..a9f7e6b5c4d3 100644 --- a/gogs.go +++ b/gogs.go @@ -15,12 +15,12 @@ import ( "github.com/gogits/gogs/modules/base" ) -// +build go1.1 +// +build go1.2 -// Test that go1.1 tag above is included in builds. main.go refers to this definition. -const go11tag = true +// Test that go1.2 tag above is included in builds. main.go refers to this definition. +const go12tag = true -const APP_VER = "0.1.3.0320.1" +const APP_VER = "0.1.4.0321" func init() { base.AppVer = APP_VER diff --git a/models/issue.go b/models/issue.go new file mode 100644 index 000000000000..c669d201f6e7 --- /dev/null +++ b/models/issue.go @@ -0,0 +1,19 @@ +// Copyright 2014 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 models + +type Issue struct { + Id int64 + RepoId int64 `xorm:"index"` + PosterId int64 +} + +type PullRequest struct { + Id int64 +} + +type Comment struct { + Id int64 +} diff --git a/models/models.go b/models/models.go index 2e0bb759d414..214d1c767a15 100644 --- a/models/models.go +++ b/models/models.go @@ -15,30 +15,7 @@ import ( "github.com/gogits/gogs/modules/base" ) -var ( - orm *xorm.Engine - RepoRootPath string -) - -type Members struct { - Id int64 - OrgId int64 `xorm:"unique(s) index"` - UserId int64 `xorm:"unique(s)"` -} - -type Issue struct { - Id int64 - RepoId int64 `xorm:"index"` - PosterId int64 -} - -type PullRequest struct { - Id int64 -} - -type Comment struct { - Id int64 -} +var orm *xorm.Engine func setEngine() { dbType := base.Cfg.MustValue("database", "DB_TYPE") @@ -65,8 +42,8 @@ func setEngine() { os.Exit(2) } - // TODO: for serv command, MUST remove the output to os.stdout, so - // use log file to instead print to stdout + // WARNNING: for serv command, MUST remove the output to os.stdout, + // so use log file to instead print to stdout. //x.ShowDebug = true //orm.ShowErr = true @@ -77,20 +54,29 @@ func setEngine() { } orm.Logger = f orm.ShowSQL = true - - // Determine and create root git reposiroty path. - RepoRootPath = base.Cfg.MustValue("repository", "ROOT") - if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { - fmt.Printf("models.init(fail to create RepoRootPath(%s)): %v\n", RepoRootPath, err) - os.Exit(2) - } } func init() { setEngine() - if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Access), - new(Action), new(Watch)); err != nil { + if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), + new(Action), new(Access)); err != nil { fmt.Printf("sync database struct error: %v\n", err) os.Exit(2) } } + +type Statistic struct { + Counter struct { + User, PublicKey, Repo, Watch, Action, Access int64 + } +} + +func GetStatistic() (stats Statistic) { + stats.Counter.User, _ = orm.Count(new(User)) + stats.Counter.PublicKey, _ = orm.Count(new(PublicKey)) + stats.Counter.Repo, _ = orm.Count(new(Repository)) + stats.Counter.Watch, _ = orm.Count(new(Watch)) + stats.Counter.Action, _ = orm.Count(new(Action)) + stats.Counter.Access, _ = orm.Count(new(Access)) + return stats +} diff --git a/models/publickey.go b/models/publickey.go index 092436d55fb4..c69bca681d66 100644 --- a/models/publickey.go +++ b/models/publickey.go @@ -27,8 +27,12 @@ const ( ) var ( - sshOpLocker = sync.Mutex{} + ErrKeyAlreadyExist = errors.New("Public key already exist") +) +var sshOpLocker = sync.Mutex{} + +var ( sshPath string appPath string ) @@ -79,10 +83,6 @@ type PublicKey struct { Updated time.Time `xorm:"updated"` } -var ( - ErrKeyAlreadyExist = errors.New("Public key already exist") -) - // GenAuthorizedKey returns formatted public key string. func GenAuthorizedKey(keyId int64, key string) string { return fmt.Sprintf(TPL_PUBLICK_KEY+"\n", appPath, keyId, key) diff --git a/models/repo.go b/models/repo.go index bcbc58678582..f7173d76d033 100644 --- a/models/repo.go +++ b/models/repo.go @@ -27,63 +27,18 @@ import ( "github.com/gogits/gogs/modules/log" ) -// Repository represents a git repository. -type Repository struct { - Id int64 - OwnerId int64 `xorm:"unique(s)"` - ForkId int64 - LowerName string `xorm:"unique(s) index not null"` - Name string `xorm:"index not null"` - Description string - Website string - Private bool - NumWatchs int - NumStars int - NumForks int - Created time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` -} - -// Watch is connection request for receiving repository notifycation. -type Watch struct { - Id int64 - RepoId int64 `xorm:"UNIQUE(watch)"` - UserId int64 `xorm:"UNIQUE(watch)"` -} - -// Watch or unwatch repository. -func WatchRepo(userId, repoId int64, watch bool) (err error) { - if watch { - _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}) - } else { - _, err = orm.Delete(&Watch{0, repoId, userId}) - } - return err -} - -// GetWatches returns all watches of given repository. -func GetWatches(repoId int64) ([]Watch, error) { - watches := make([]Watch, 0, 10) - err := orm.Find(&watches, &Watch{RepoId: repoId}) - return watches, err -} - -// IsWatching checks if user has watched given repository. -func IsWatching(userId, repoId int64) bool { - has, _ := orm.Get(&Watch{0, repoId, userId}) - return has -} - var ( - gitInitLocker = sync.Mutex{} - LanguageIgns, Licenses []string + ErrRepoAlreadyExist = errors.New("Repository already exist") + ErrRepoNotExist = errors.New("Repository does not exist") + ErrRepoFileNotExist = errors.New("Target Repo file does not exist") + ErrRepoNameIllegal = errors.New("Repository name contains illegal characters") + ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded") ) +var gitInitLocker = sync.Mutex{} + var ( - ErrRepoAlreadyExist = errors.New("Repository already exist") - ErrRepoNotExist = errors.New("Repository does not exist") - ErrRepoFileNotExist = errors.New("Target Repo file does not exist") - ErrRepoNameIllegal = errors.New("Repository name contains illegal characters") + LanguageIgns, Licenses []string ) func init() { @@ -117,6 +72,23 @@ func init() { } } +// Repository represents a git repository. +type Repository struct { + Id int64 + OwnerId int64 `xorm:"unique(s)"` + ForkId int64 + LowerName string `xorm:"unique(s) index not null"` + Name string `xorm:"index not null"` + Description string + Website string + Private bool + NumWatches int + NumStars int + NumForks int + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + // IsRepositoryExist returns true if the repository with given name under user has already existed. func IsRepositoryExist(user *User, repoName string) (bool, error) { repo := Repository{OwnerId: user.Id} @@ -351,6 +323,60 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep return nil } +// GetRepos returns given number of repository objects with offset. +func GetRepos(num, offset int) ([]Repository, error) { + repos := make([]Repository, 0, num) + err := orm.Limit(num, offset).Asc("id").Find(&repos) + return repos, err +} + +func RepoPath(userName, repoName string) string { + return filepath.Join(UserPath(userName), repoName+".git") +} + +// DeleteRepository deletes a repository for a user or orgnaztion. +func DeleteRepository(userId, repoId int64, userName string) (err error) { + repo := &Repository{Id: repoId, OwnerId: userId} + has, err := orm.Get(repo) + if err != nil { + return err + } else if !has { + return ErrRepoNotExist + } + + session := orm.NewSession() + if err = session.Begin(); err != nil { + return err + } + if _, err = session.Delete(&Repository{Id: repoId}); err != nil { + session.Rollback() + return err + } + if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil { + session.Rollback() + return err + } + rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" + if _, err = session.Exec(rawSql, userId); err != nil { + session.Rollback() + return err + } + if _, err = session.Delete(&Watch{RepoId: repoId}); err != nil { + session.Rollback() + return err + } + if err = session.Commit(); err != nil { + session.Rollback() + return err + } + if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil { + // TODO: log and delete manully + log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err) + return err + } + return nil +} + // GetRepositoryByName returns the repository by given name under user if exists. func GetRepositoryByName(user *User, repoName string) (*Repository, error) { repo := &Repository{ @@ -388,6 +414,36 @@ func GetRepositoryCount(user *User) (int64, error) { return orm.Count(&Repository{OwnerId: user.Id}) } +// Watch is connection request for receiving repository notifycation. +type Watch struct { + Id int64 + RepoId int64 `xorm:"UNIQUE(watch)"` + UserId int64 `xorm:"UNIQUE(watch)"` +} + +// Watch or unwatch repository. +func WatchRepo(userId, repoId int64, watch bool) (err error) { + if watch { + _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}) + } else { + _, err = orm.Delete(&Watch{0, repoId, userId}) + } + return err +} + +// GetWatches returns all watches of given repository. +func GetWatches(repoId int64) ([]Watch, error) { + watches := make([]Watch, 0, 10) + err := orm.Find(&watches, &Watch{RepoId: repoId}) + return watches, err +} + +// IsWatching checks if user has watched given repository. +func IsWatching(userId, repoId int64) bool { + has, _ := orm.Get(&Watch{0, repoId, userId}) + return has +} + func StarReposiory(user *User, repoName string) error { return nil } @@ -408,60 +464,6 @@ func ForkRepository(reposName string, userId int64) { } -func RepoPath(userName, repoName string) string { - return filepath.Join(UserPath(userName), repoName+".git") -} - -// DeleteRepository deletes a repository for a user or orgnaztion. -func DeleteRepository(userId, repoId int64, userName string) (err error) { - repo := &Repository{Id: repoId, OwnerId: userId} - has, err := orm.Get(repo) - if err != nil { - return err - } else if !has { - return ErrRepoNotExist - } - - session := orm.NewSession() - if err = session.Begin(); err != nil { - return err - } - if _, err = session.Delete(&Repository{Id: repoId}); err != nil { - session.Rollback() - return err - } - if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil { - session.Rollback() - return err - } - rawSql := "UPDATE user SET num_repos = num_repos - 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_repos = num_repos - 1 WHERE id = ?" - } - if _, err = session.Exec(rawSql, userId); err != nil { - session.Rollback() - return err - } - if _, err = session.Delete(&Watch{RepoId: repoId}); err != nil { - session.Rollback() - return err - } - if err = session.Commit(); err != nil { - session.Rollback() - return err - } - if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil { - // TODO: log and delete manully - log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err) - return err - } - return nil -} - -var ( - ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded") -) - // RepoFile represents a file object in git repository. type RepoFile struct { *git.TreeEntry diff --git a/models/user.go b/models/user.go index 990e1954a5db..3c1109128587 100644 --- a/models/user.go +++ b/models/user.go @@ -33,6 +33,14 @@ const ( LT_LDAP ) +var ( + ErrUserOwnRepos = errors.New("User still have ownership of repositories") + ErrUserAlreadyExist = errors.New("User already exist") + ErrUserNotExist = errors.New("User does not exist") + ErrEmailAlreadyUsed = errors.New("E-mail already used") + ErrUserNameIllegal = errors.New("User name contains illegal characters") +) + // User represents the object of individual and member of organization. type User struct { Id int64 @@ -67,20 +75,28 @@ func (user *User) AvatarLink() string { return "http://1.gravatar.com/avatar/" + user.Avatar } -type Follow struct { - Id int64 - UserId int64 `xorm:"unique(s)"` - FollowId int64 `xorm:"unique(s)"` - Created time.Time `xorm:"created"` +// NewGitSig generates and returns the signature of given user. +func (user *User) NewGitSig() *git.Signature { + return &git.Signature{ + Name: user.Name, + Email: user.Email, + When: time.Now(), + } } -var ( - ErrUserOwnRepos = errors.New("User still have ownership of repositories") - ErrUserAlreadyExist = errors.New("User already exist") - ErrUserNotExist = errors.New("User does not exist") - ErrEmailAlreadyUsed = errors.New("E-mail already used") - ErrUserNameIllegal = errors.New("User name contains illegal characters") -) +// EncodePasswd encodes password to safe format. +func (user *User) EncodePasswd() error { + newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64) + user.Passwd = fmt.Sprintf("%x", newPasswd) + return err +} + +// Member represents user is member of organization. +type Member struct { + Id int64 + OrgId int64 `xorm:"unique(member) index"` + UserId int64 `xorm:"unique(member)"` +} // IsUserExist checks if given user name exist, // the user name should be noncased unique. @@ -93,15 +109,6 @@ func IsEmailUsed(email string) (bool, error) { return orm.Get(&User{Email: email}) } -// NewGitSig generates and returns the signature of given user. -func (user *User) NewGitSig() *git.Signature { - return &git.Signature{ - Name: user.Name, - Email: user.Email, - When: time.Now(), - } -} - // return a user salt token func GetUserSalt() string { return base.GetRandomString(10) @@ -151,6 +158,13 @@ func RegisterUser(user *User) (*User, error) { return user, err } +// GetUsers returns given number of user objects with offset. +func GetUsers(num, offset int) ([]User, error) { + users := make([]User, 0, num) + err := orm.Limit(num, offset).Asc("id").Find(&users) + return users, err +} + // get user by erify code func getVerifyUser(code string) (user *User) { if len(code) <= base.TimeLimitCodeLength { @@ -229,24 +243,14 @@ func DeleteUser(user *User) error { return err } -// EncodePasswd encodes password to safe format. -func (user *User) EncodePasswd() error { - newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64) - user.Passwd = fmt.Sprintf("%x", newPasswd) - return err -} - // UserPath returns the path absolute path of user repositories. func UserPath(userName string) string { - return filepath.Join(RepoRootPath, strings.ToLower(userName)) + return filepath.Join(base.RepoRootPath, strings.ToLower(userName)) } func GetUserByKeyId(keyId int64) (*User, error) { user := new(User) - rawSql := "SELECT a.* FROM user AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "SELECT a.* FROM \"user\" AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" - } + rawSql := "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" has, err := orm.Sql(rawSql, keyId).Get(user) if err != nil { return nil, err @@ -303,6 +307,13 @@ func LoginUserPlain(name, passwd string) (*User, error) { return &user, err } +// Follow is connection request for receiving user notifycation. +type Follow struct { + Id int64 + UserId int64 `xorm:"unique(follow)"` + FollowId int64 `xorm:"unique(follow)"` +} + // FollowUser marks someone be another's follower. func FollowUser(userId int64, followId int64) (err error) { session := orm.NewSession() @@ -314,19 +325,13 @@ func FollowUser(userId int64, followId int64) (err error) { return err } - rawSql := "UPDATE user SET num_followers = num_followers + 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followers = num_followers + 1 WHERE id = ?" - } + rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?" if _, err = session.Exec(rawSql, followId); err != nil { session.Rollback() return err } - rawSql = "UPDATE user SET num_followings = num_followings + 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followings = num_followings + 1 WHERE id = ?" - } + rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?" if _, err = session.Exec(rawSql, userId); err != nil { session.Rollback() return err @@ -345,19 +350,13 @@ func UnFollowUser(userId int64, unFollowId int64) (err error) { return err } - rawSql := "UPDATE user SET num_followers = num_followers - 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followers = num_followers - 1 WHERE id = ?" - } + rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?" if _, err = session.Exec(rawSql, unFollowId); err != nil { session.Rollback() return err } - rawSql = "UPDATE user SET num_followings = num_followings - 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followings = num_followings - 1 WHERE id = ?" - } + rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?" if _, err = session.Exec(rawSql, userId); err != nil { session.Rollback() return err diff --git a/modules/base/conf.go b/modules/base/conf.go index fdbf3ad38549..81f32bd59153 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -26,12 +26,14 @@ type Mailer struct { } var ( - AppVer string - AppName string - AppLogo string - AppUrl string - Domain string - SecretKey string + AppVer string + AppName string + AppLogo string + AppUrl string + Domain string + SecretKey string + RepoRootPath string + Cfg *goconfig.ConfigFile MailService *Mailer ) @@ -173,6 +175,13 @@ func init() { AppUrl = Cfg.MustValue("server", "ROOT_URL") Domain = Cfg.MustValue("server", "DOMAIN") SecretKey = Cfg.MustValue("security", "SECRET_KEY") + + // Determine and create root git reposiroty path. + RepoRootPath = Cfg.MustValue("repository", "ROOT") + if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { + fmt.Printf("models.init(fail to create RepoRootPath(%s)): %v\n", RepoRootPath, err) + os.Exit(2) + } } func NewServices() { diff --git a/routers/admin/admin.go b/routers/admin/admin.go index c7523b7f5960..a37f1207c971 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -5,20 +5,35 @@ package admin import ( + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/middleware" ) func Dashboard(ctx *middleware.Context) { ctx.Data["Title"] = "Admin Dashboard" + ctx.Data["Stats"] = models.GetStatistic() ctx.HTML(200, "admin/dashboard") } func Users(ctx *middleware.Context) { ctx.Data["Title"] = "User Management" + + var err error + ctx.Data["Users"], err = models.GetUsers(100, 0) + if err != nil { + ctx.Handle(200, "admin.Users", err) + return + } ctx.HTML(200, "admin/users") } func Repositories(ctx *middleware.Context) { ctx.Data["Title"] = "Repository Management" + var err error + ctx.Data["Repos"], err = models.GetRepos(100, 0) + if err != nil { + ctx.Handle(200, "admin.Repositories", err) + return + } ctx.HTML(200, "admin/repos") } diff --git a/serve.go b/serve.go index ddc670235e28..2a17a334901e 100644 --- a/serve.go +++ b/serve.go @@ -12,7 +12,9 @@ import ( "strings" "github.com/codegangsta/cli" + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" ) var ( @@ -144,7 +146,7 @@ func runServ(*cli.Context) { } gitcmd := exec.Command(verb, rRepo) - gitcmd.Dir = models.RepoRootPath + gitcmd.Dir = base.RepoRootPath gitcmd.Stdout = os.Stdout gitcmd.Stdin = os.Stdin gitcmd.Stderr = os.Stderr diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 84456c85b828..6a914b65f779 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -16,7 +16,7 @@
- Gogs database has 4 users, 3 repositories, 4 SSH keys. + Gogs database has {{.Stats.Counter.User}} users, {{.Stats.Counter.PublicKey}} SSH keys, {{.Stats.Counter.Repo}} repositories, {{.Stats.Counter.Watch}} watches, {{.Stats.Counter.Action}} actions, and {{.Stats.Counter.Access}} accesses.
diff --git a/templates/admin/repos.tmpl b/templates/admin/repos.tmpl index ec7f47e09024..4522c6679245 100644 --- a/templates/admin/repos.tmpl +++ b/templates/admin/repos.tmpl @@ -16,6 +16,30 @@
+ + + + + + + + + + + + + {{range .Repos}} + + + + + + + + + {{end}} + +
IdNamePrivateWatchesForksCreated
{{.Id}}{{.Name}}{{.NumWatches}}{{.NumForks}}{{DateFormat .Created "M d, Y"}}
diff --git a/templates/admin/users.tmpl b/templates/admin/users.tmpl index 8acf256d0547..c087f268f288 100644 --- a/templates/admin/users.tmpl +++ b/templates/admin/users.tmpl @@ -16,6 +16,32 @@
+ + + + + + + + + + + + + + {{range .Users}} + + + + + + + + + + {{end}} + +
IdNameE-mailActivedAdminReposJoin
{{.Id}}{{.Name}}{{.Email}}{{.NumRepos}}{{DateFormat .Created "M d, Y"}}