| @@ -5,6 +5,7 @@ gogs | |||
| *.db | |||
| *.log | |||
| custom/ | |||
| data/ | |||
| .vendor/ | |||
| .idea/ | |||
| *.iml | |||
| @@ -4,7 +4,6 @@ path=github.com/gogits/gogs | |||
| [deps] | |||
| github.com/codegangsta/cli= | |||
| github.com/codegangsta/martini= | |||
| github.com/martini-contrib/sessions= | |||
| github.com/Unknwon/com= | |||
| github.com/Unknwon/cae= | |||
| github.com/Unknwon/goconfig= | |||
| @@ -1,15 +1,21 @@ | |||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://gowalker.org/github.com/gogits/gogs) | |||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) [](https://bitdeli.com/free "Bitdeli Badge") | |||
| ===================== | |||
| Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language. | |||
| Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language. | |||
| Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms** that Go supports, including Linux, Max OS X, and Windows with **ZERO** dependency. | |||
|  | |||
| ##### Current version: 0.1.5 Alpha | |||
| ##### Current version: 0.1.7 Alpha | |||
| #### Other language version | |||
| - [简体中文](README_ZH.md) | |||
| ## Purpose | |||
| There are some very good products in this category such as [gitlab](http://gitlab.com), but the environment setup steps often make us crazy. So our goal of Gogs is to build a GitHub-like clone with very easy setup steps, which take advantages of the Go Programming Language. | |||
| Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms** that Go supports, including Linux, Mac OS X, and Windows with **ZERO** dependency. | |||
| More importantly, Gogs only needs one binary to setup your own project hosting on the fly! | |||
| ## Overview | |||
| @@ -21,9 +27,9 @@ There are some very good products in this category such as [gitlab](http://gitla | |||
| ## Features | |||
| - Activity timeline | |||
| - SSH protocol support. | |||
| - SSH/HTTPS protocol support. | |||
| - Register/delete account. | |||
| - Create/delete public repository. | |||
| - Create/delete/watch public repository. | |||
| - User profile page. | |||
| - Repository viewer. | |||
| - Gravatar support. | |||
| @@ -42,8 +48,9 @@ There are two ways to install Gogs: | |||
| ## Acknowledgments | |||
| - Mail service is based on [WeTalk](https://github.com/beego/wetalk). | |||
| - Logo is inspired by [martini](https://github.com/martini-contrib). | |||
| - Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk). | |||
| - System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog). | |||
| ## Contributors | |||
| @@ -0,0 +1,53 @@ | |||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) [](https://bitdeli.com/free "Bitdeli Badge") | |||
| ===================== | |||
| Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 | |||
|  | |||
| ##### 当前版本:0.1.7 Alpha | |||
| ## 开发目的 | |||
| Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依赖,并且支持 Go 语言所支持的 **所有平台**,包括 Linux、Mac OS X 以及 Windows。 | |||
| 更重要的是,您只需要一个可执行文件就能借助 Gogs 快速搭建属于您自己的代码托管服务! | |||
| ## 项目概览 | |||
| - 有关项目设计、开发说明、变更日志和路线图,请通过 [Wiki](https://github.com/gogits/gogs/wiki) 查看。 | |||
| - 您可以到 [Trello Broad](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。 | |||
| - 想要先睹为快?通过 [在线体验](http://try.gogits.org/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。 | |||
| - 使用过程中遇到问题?尝试从 [故障排查](https://github.com/gogits/gogs/wiki/Troubleshooting) 页面获取帮助。 | |||
| ## 功能特性 | |||
| - 活动时间线 | |||
| - SSH/HTTPS 协议支持 | |||
| - 注册/删除用户 | |||
| - 创建/删除/关注公开仓库 | |||
| - 用户个人信息页面 | |||
| - 仓库浏览器 | |||
| - Gravatar 支持 | |||
| - 邮件服务(注册) | |||
| - 管理员面板 | |||
| - 支持 MySQL、PostgreSQL 以及 SQLite3(仅限二进制版本) | |||
| ## 安装部署 | |||
| 在安装 Gogs 之前,您需要先安装 [基本环境](https://github.com/gogits/gogs/wiki/Prerequirements)。 | |||
| 然后,您可以通过以下两种方式来安装 Gogs: | |||
| - [二进制安装](https://github.com/gogits/gogs/wiki/Install-from-binary): **强烈推荐** 适合体验者和实际部署 | |||
| - [源码安装](https://github.com/gogits/gogs/wiki/Install-from-source) | |||
| ## 特别鸣谢 | |||
| - Logo 基于 [martini](https://github.com/martini-contrib) 修改而来。 | |||
| - 邮件服务、模块设计基于 [WeTalk](https://github.com/beego/wetalk) 修改而来。 | |||
| - 系统监视状态基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改而来。 | |||
| ## 贡献成员 | |||
| 本项目最初由 [Unknown](https://github.com/Unknwon) 和 [lunny](https://github.com/lunny) 发起,随后 [fuxiaohei](https://github.com/fuxiaohei) 与 [slene](https://github.com/slene) 加入到开发团队。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。 | |||
| @@ -13,7 +13,8 @@ | |||
| "others": [ | |||
| "modules", | |||
| "$GOPATH/src/github.com/gogits/binding", | |||
| "$GOPATH/src/github.com/gogits/git" | |||
| "$GOPATH/src/github.com/gogits/git", | |||
| "$GOPATH/src/github.com/gogits/gfm" | |||
| ] | |||
| }, | |||
| "cmd_args": [ | |||
| @@ -32,8 +32,14 @@ PATH = data/gogs.db | |||
| [admin] | |||
| [security] | |||
| ; Use HTTPS to clone repository, otherwise use HTTP. | |||
| ENABLE_HTTPS_CLONE = false | |||
| ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | |||
| SECRET_KEY = !#@FDEWREWR&*( | |||
| ; Auto-login remember days | |||
| LOGIN_REMEMBER_DAYS = 7 | |||
| COOKIE_USERNAME = gogs_awesome | |||
| COOKIE_REMEMBER_NAME = gogs_incredible | |||
| [service] | |||
| ACTIVE_CODE_LIVE_MINUTES = 180 | |||
| @@ -44,6 +50,8 @@ REGISTER_EMAIL_CONFIRM = false | |||
| DISENABLE_REGISTERATION = false | |||
| ; User must sign in to view anything. | |||
| REQUIRE_SIGNIN_VIEW = false | |||
| ; Cache avatar as picture | |||
| ENABLE_CACHE_AVATAR = false | |||
| [mailer] | |||
| ENABLED = false | |||
| @@ -70,8 +78,38 @@ INTERVAL = 60 | |||
| ; memcache: "127.0.0.1:11211" | |||
| HOST = | |||
| [session] | |||
| ; Either "memory", "file", "redis" or "mysql", default is "memory" | |||
| PROVIDER = file | |||
| ; Provider config options | |||
| ; memory: not have any config yet | |||
| ; file: session file path, e.g. data/sessions | |||
| ; redis: config like redis server addr, poolSize, password, e.g. 127.0.0.1:6379,100,astaxie | |||
| ; mysql: go-sql-driver/mysql dsn config string, e.g. root:password@/session_table | |||
| PROVIDER_CONFIG = data/sessions | |||
| ; Session cookie name | |||
| COOKIE_NAME = i_like_gogits | |||
| ; If you use session in https only, default is false | |||
| COOKIE_SECURE = false | |||
| ; Enable set cookie, default is true | |||
| ENABLE_SET_COOKIE = true | |||
| ; Session GC time interval, default is 86400 | |||
| GC_INTERVAL_TIME = 86400 | |||
| ; Session life time, default is 86400 | |||
| SESSION_LIFE_TIME = 86400 | |||
| ; session id hash func, Either "sha1", "sha256" or "md5" default is sha1 | |||
| SESSION_ID_HASHFUNC = sha1 | |||
| ; Session hash key, default is use random string | |||
| SESSION_ID_HASHKEY = | |||
| [picture] | |||
| ; The place to picture data, either "server" or "qiniu", default is "server" | |||
| SERVICE = server | |||
| ; For "server" only, root path of picture data, default is "data/pictures" | |||
| PATH = data/pictures | |||
| [log] | |||
| ; Either "console", "file", "conn" or "smtp", default is "console" | |||
| ; Either "console", "file", "conn", "smtp" or "database", default is "console" | |||
| MODE = console | |||
| ; Buffer length of channel, keep it as it is if you don't know what it is. | |||
| BUFFER_LEN = 10000 | |||
| @@ -120,4 +158,10 @@ HOST = | |||
| USER = | |||
| PASSWD = | |||
| ; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"] | |||
| RECEIVERS = | |||
| RECEIVERS = | |||
| ; For "database" mode only | |||
| [log.database] | |||
| LEVEL = | |||
| Driver = | |||
| CONN = | |||
| @@ -2,7 +2,7 @@ | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| // gogs(Go Git Service) is a Go clone of Github. | |||
| // Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language. | |||
| package main | |||
| import ( | |||
| @@ -20,7 +20,7 @@ import ( | |||
| // Test that go1.2 tag above is included in builds. main.go refers to this definition. | |||
| const go12tag = true | |||
| const APP_VER = "0.1.5.0321" | |||
| const APP_VER = "0.1.7.0323.1" | |||
| func init() { | |||
| base.AppVer = APP_VER | |||
| @@ -7,6 +7,9 @@ package models | |||
| import ( | |||
| "encoding/json" | |||
| "time" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| // Operation types of user action. | |||
| @@ -28,7 +31,8 @@ type Action struct { | |||
| ActUserName string // Action user name. | |||
| RepoId int64 | |||
| RepoName string | |||
| Content string | |||
| RefName string | |||
| Content string `xorm:"TEXT"` | |||
| Created time.Time `xorm:"created"` | |||
| } | |||
| @@ -44,13 +48,17 @@ func (a Action) GetRepoName() string { | |||
| return a.RepoName | |||
| } | |||
| func (a Action) GetBranch() string { | |||
| return a.RefName | |||
| } | |||
| func (a Action) GetContent() string { | |||
| return a.Content | |||
| } | |||
| // CommitRepoAction records action for commit repository. | |||
| func CommitRepoAction(userId int64, userName string, | |||
| repoId int64, repoName string, commits [][]string) error { | |||
| repoId int64, repoName string, refName string, commits *base.PushCommits) error { | |||
| bs, err := json.Marshal(commits) | |||
| if err != nil { | |||
| return err | |||
| @@ -76,9 +84,22 @@ func CommitRepoAction(userId int64, userName string, | |||
| Content: string(bs), | |||
| RepoId: repoId, | |||
| RepoName: repoName, | |||
| RefName: refName, | |||
| }) | |||
| return err | |||
| } | |||
| // Update repository last update time. | |||
| repo, err := GetRepositoryByName(userId, repoName) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| repo.IsBare = false | |||
| if err = UpdateRepository(repo); err != nil { | |||
| return err | |||
| } | |||
| log.Trace("action.CommitRepoAction: %d/%s", userId, repo.LowerName) | |||
| return nil | |||
| } | |||
| @@ -92,6 +113,8 @@ func NewRepoAction(user *User, repo *Repository) error { | |||
| RepoId: repo.Id, | |||
| RepoName: repo.Name, | |||
| }) | |||
| log.Trace("action.NewRepoAction: %s/%s", user.LowerName, repo.LowerName) | |||
| return err | |||
| } | |||
| @@ -4,16 +4,155 @@ | |||
| package models | |||
| import ( | |||
| "errors" | |||
| "strings" | |||
| "time" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| var ( | |||
| ErrIssueNotExist = errors.New("Issue does not exist") | |||
| ) | |||
| // Issue represents an issue or pull request of repository. | |||
| type Issue struct { | |||
| Id int64 | |||
| RepoId int64 `xorm:"index"` | |||
| PosterId int64 | |||
| Id int64 | |||
| Index int64 // Index in one repository. | |||
| Name string | |||
| RepoId int64 `xorm:"index"` | |||
| PosterId int64 | |||
| MilestoneId int64 | |||
| AssigneeId int64 | |||
| IsPull bool // Indicates whether is a pull request or not. | |||
| IsClosed bool | |||
| Labels string `xorm:"TEXT"` | |||
| Mentions string `xorm:"TEXT"` | |||
| Content string `xorm:"TEXT"` | |||
| NumComments int | |||
| Created time.Time `xorm:"created"` | |||
| Updated time.Time `xorm:"updated"` | |||
| } | |||
| type PullRequest struct { | |||
| Id int64 | |||
| // CreateIssue creates new issue for repository. | |||
| func CreateIssue(userId, repoId, milestoneId, assigneeId int64, name, labels, content string, isPull bool) (*Issue, error) { | |||
| count, err := GetIssueCount(repoId) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| // TODO: find out mentions | |||
| mentions := "" | |||
| issue := &Issue{ | |||
| Index: count + 1, | |||
| Name: name, | |||
| RepoId: repoId, | |||
| PosterId: userId, | |||
| MilestoneId: milestoneId, | |||
| AssigneeId: assigneeId, | |||
| IsPull: isPull, | |||
| Labels: labels, | |||
| Mentions: mentions, | |||
| Content: content, | |||
| } | |||
| _, err = orm.Insert(issue) | |||
| return issue, err | |||
| } | |||
| // GetIssueCount returns count of issues in the repository. | |||
| func GetIssueCount(repoId int64) (int64, error) { | |||
| return orm.Count(&Issue{RepoId: repoId}) | |||
| } | |||
| // GetIssueById returns issue object by given id. | |||
| func GetIssueById(id int64) (*Issue, error) { | |||
| issue := new(Issue) | |||
| has, err := orm.Id(id).Get(issue) | |||
| if err != nil { | |||
| return nil, err | |||
| } else if !has { | |||
| return nil, ErrIssueNotExist | |||
| } | |||
| return issue, nil | |||
| } | |||
| // GetIssues returns a list of issues by given conditions. | |||
| func GetIssues(userId, repoId, posterId, milestoneId int64, page int, isClosed, isMention bool, labels, sortType string) ([]Issue, error) { | |||
| sess := orm.Limit(20, (page-1)*20) | |||
| if repoId > 0 { | |||
| sess.Where("repo_id=?", repoId).And("is_closed=?", isClosed) | |||
| } else { | |||
| sess.Where("is_closed=?", isClosed) | |||
| } | |||
| if userId > 0 { | |||
| sess.And("assignee_id=?", userId) | |||
| } else if posterId > 0 { | |||
| sess.And("poster_id=?", posterId) | |||
| } else if isMention { | |||
| sess.And("mentions like '%$" + base.ToStr(userId) + "|%'") | |||
| } | |||
| if milestoneId > 0 { | |||
| sess.And("milestone_id=?", milestoneId) | |||
| } | |||
| if len(labels) > 0 { | |||
| for _, label := range strings.Split(labels, ",") { | |||
| sess.And("mentions like '%$" + label + "|%'") | |||
| } | |||
| } | |||
| switch sortType { | |||
| case "oldest": | |||
| sess.Asc("created") | |||
| case "recentupdate": | |||
| sess.Desc("updated") | |||
| case "leastupdate": | |||
| sess.Asc("updated") | |||
| case "mostcomment": | |||
| sess.Desc("num_comments") | |||
| case "leastcomment": | |||
| sess.Asc("num_comments") | |||
| default: | |||
| sess.Desc("created") | |||
| } | |||
| var issues []Issue | |||
| err := sess.Find(&issues) | |||
| return issues, err | |||
| } | |||
| // Label represents a list of labels of repository for issues. | |||
| type Label struct { | |||
| Id int64 | |||
| RepoId int64 `xorm:"index"` | |||
| Names string | |||
| Colors string | |||
| } | |||
| // Milestone represents a milestone of repository. | |||
| type Milestone struct { | |||
| Id int64 | |||
| Name string | |||
| RepoId int64 `xorm:"index"` | |||
| IsClosed bool | |||
| Content string | |||
| NumIssues int | |||
| DueDate time.Time | |||
| Created time.Time `xorm:"created"` | |||
| } | |||
| // Comment represents a comment in commit and issue page. | |||
| type Comment struct { | |||
| Id int64 | |||
| Id int64 | |||
| PosterId int64 | |||
| IssueId int64 | |||
| CommitId int64 | |||
| Line int | |||
| Content string | |||
| Created time.Time `xorm:"created"` | |||
| } | |||
| @@ -72,7 +72,7 @@ func setEngine() { | |||
| func NewEngine() { | |||
| setEngine() | |||
| if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), | |||
| new(Action), new(Access)); err != nil { | |||
| new(Action), new(Access), new(Issue)); err != nil { | |||
| fmt.Printf("sync database struct error: %v\n", err) | |||
| os.Exit(2) | |||
| } | |||
| @@ -91,5 +91,5 @@ func GetStatistic() (stats Statistic) { | |||
| stats.Counter.Watch, _ = orm.Count(new(Watch)) | |||
| stats.Counter.Action, _ = orm.Count(new(Action)) | |||
| stats.Counter.Access, _ = orm.Count(new(Access)) | |||
| return stats | |||
| return | |||
| } | |||
| @@ -19,6 +19,8 @@ import ( | |||
| "time" | |||
| "github.com/Unknwon/com" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| const ( | |||
| @@ -78,7 +80,7 @@ type PublicKey struct { | |||
| OwnerId int64 `xorm:"index"` | |||
| Name string `xorm:"unique not null"` | |||
| Fingerprint string | |||
| Content string `xorm:"text not null"` | |||
| Content string `xorm:"TEXT not null"` | |||
| Created time.Time `xorm:"created"` | |||
| Updated time.Time `xorm:"updated"` | |||
| } | |||
| @@ -99,8 +101,8 @@ func AddPublicKey(key *PublicKey) (err error) { | |||
| } | |||
| // Calculate fingerprint. | |||
| tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), | |||
| "id_rsa.pub") | |||
| tmpPath := strings.Replace(filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), | |||
| "id_rsa.pub"), "\\", "/", -1) | |||
| os.MkdirAll(path.Dir(tmpPath), os.ModePerm) | |||
| if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil { | |||
| return err | |||
| @@ -127,25 +129,11 @@ func AddPublicKey(key *PublicKey) (err error) { | |||
| return nil | |||
| } | |||
| // DeletePublicKey deletes SSH key information both in database and authorized_keys file. | |||
| func DeletePublicKey(key *PublicKey) (err error) { | |||
| // Delete SSH key in database. | |||
| has, err := orm.Id(key.Id).Get(key) | |||
| if err != nil { | |||
| return err | |||
| } else if !has { | |||
| return errors.New("Public key does not exist") | |||
| } | |||
| if _, err = orm.Delete(key); err != nil { | |||
| return err | |||
| } | |||
| func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error { | |||
| // Delete SSH key in SSH key file. | |||
| sshOpLocker.Lock() | |||
| defer sshOpLocker.Unlock() | |||
| p := filepath.Join(sshPath, "authorized_keys") | |||
| tmpP := filepath.Join(sshPath, "authorized_keys.tmp") | |||
| fr, err := os.Open(p) | |||
| if err != nil { | |||
| return err | |||
| @@ -188,8 +176,29 @@ func DeletePublicKey(key *PublicKey) (err error) { | |||
| break | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| if err = os.Remove(p); err != nil { | |||
| // DeletePublicKey deletes SSH key information both in database and authorized_keys file. | |||
| func DeletePublicKey(key *PublicKey) (err error) { | |||
| // Delete SSH key in database. | |||
| has, err := orm.Id(key.Id).Get(key) | |||
| if err != nil { | |||
| return err | |||
| } else if !has { | |||
| return errors.New("Public key does not exist") | |||
| } | |||
| if _, err = orm.Delete(key); err != nil { | |||
| return err | |||
| } | |||
| p := filepath.Join(sshPath, "authorized_keys") | |||
| tmpP := filepath.Join(sshPath, "authorized_keys.tmp") | |||
| log.Trace("ssh.DeletePublicKey(authorized_keys): %s", p) | |||
| if err = rewriteAuthorizedKeys(key, p, tmpP); err != nil { | |||
| return err | |||
| } else if err = os.Remove(p); err != nil { | |||
| return err | |||
| } | |||
| return os.Rename(tmpP, p) | |||
| @@ -10,6 +10,7 @@ import ( | |||
| "fmt" | |||
| "io/ioutil" | |||
| "os" | |||
| "os/exec" | |||
| "path" | |||
| "path/filepath" | |||
| "regexp" | |||
| @@ -83,10 +84,11 @@ type Repository struct { | |||
| Name string `xorm:"index not null"` | |||
| Description string | |||
| Website string | |||
| Private bool | |||
| NumWatches int | |||
| NumStars int | |||
| NumForks int | |||
| IsPrivate bool | |||
| IsBare bool | |||
| Created time.Time `xorm:"created"` | |||
| Updated time.Time `xorm:"updated"` | |||
| } | |||
| @@ -139,7 +141,8 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv | |||
| Name: repoName, | |||
| LowerName: strings.ToLower(repoName), | |||
| Description: desc, | |||
| Private: private, | |||
| IsPrivate: private, | |||
| IsBare: repoLang == "" && license == "" && !initReadme, | |||
| } | |||
| repoPath := RepoPath(user.Name, repoName) | |||
| @@ -196,6 +199,13 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv | |||
| return nil, err | |||
| } | |||
| c := exec.Command("git", "update-server-info") | |||
| c.Dir = repoPath | |||
| err = c.Run() | |||
| if err != nil { | |||
| log.Error("repo.CreateRepository(exec update-server-info): %v", err) | |||
| } | |||
| return repo, NewRepoAction(user, repo) | |||
| } | |||
| @@ -369,6 +379,18 @@ func RepoPath(userName, repoName string) string { | |||
| return filepath.Join(UserPath(userName), repoName+".git") | |||
| } | |||
| func UpdateRepository(repo *Repository) error { | |||
| if len(repo.Description) > 255 { | |||
| repo.Description = repo.Description[:255] | |||
| } | |||
| if len(repo.Website) > 255 { | |||
| repo.Website = repo.Website[:255] | |||
| } | |||
| _, err := orm.Id(repo.Id).UseBool().Cols("description", "website").Update(repo) | |||
| return err | |||
| } | |||
| // DeleteRepository deletes a repository for a user or orgnaztion. | |||
| func DeleteRepository(userId, repoId int64, userName string) (err error) { | |||
| repo := &Repository{Id: repoId, OwnerId: userId} | |||
| @@ -413,9 +435,9 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) { | |||
| } | |||
| // GetRepositoryByName returns the repository by given name under user if exists. | |||
| func GetRepositoryByName(user *User, repoName string) (*Repository, error) { | |||
| func GetRepositoryByName(userId int64, repoName string) (*Repository, error) { | |||
| repo := &Repository{ | |||
| OwnerId: user.Id, | |||
| OwnerId: userId, | |||
| LowerName: strings.ToLower(repoName), | |||
| } | |||
| has, err := orm.Get(repo) | |||
| @@ -201,7 +201,14 @@ func VerifyUserActiveCode(code string) (user *User) { | |||
| // UpdateUser updates user's information. | |||
| func UpdateUser(user *User) (err error) { | |||
| _, err = orm.Id(user.Id).UseBool().Update(user) | |||
| if len(user.Location) > 255 { | |||
| user.Location = user.Location[:255] | |||
| } | |||
| if len(user.Website) > 255 { | |||
| user.Website = user.Website[:255] | |||
| } | |||
| _, err = orm.Id(user.Id).UseBool().Cols("website", "location", "is_active", "is_admin").Update(user) | |||
| return err | |||
| } | |||
| @@ -279,9 +286,7 @@ func GetUserByName(name string) (*User, error) { | |||
| if len(name) == 0 { | |||
| return nil, ErrUserNotExist | |||
| } | |||
| user := &User{ | |||
| LowerName: strings.ToLower(name), | |||
| } | |||
| user := &User{LowerName: strings.ToLower(name)} | |||
| has, err := orm.Get(user) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -61,6 +61,7 @@ func (f *RegisterForm) Validate(errors *binding.Errors, req *http.Request, conte | |||
| type LogInForm struct { | |||
| UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` | |||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| Remember string `form:"remember"` | |||
| } | |||
| func (f *LogInForm) Name(field string) string { | |||
| @@ -0,0 +1,54 @@ | |||
| // 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 auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/binding" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| type CreateIssueForm struct { | |||
| IssueName string `form:"name" binding:"Required;MaxSize(50)"` | |||
| RepoId int64 `form:"repoid" binding:"Required"` | |||
| MilestoneId int64 `form:"milestoneid" binding:"Required"` | |||
| AssigneeId int64 `form:"assigneeid"` | |||
| Labels string `form:"labels"` | |||
| Content string `form:"content"` | |||
| } | |||
| func (f *CreateIssueForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "IssueName": "Issue name", | |||
| "RepoId": "Repository ID", | |||
| "MilestoneId": "Milestone ID", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *CreateIssueForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| if req.Method == "GET" || errors.Count() == 0 { | |||
| return | |||
| } | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| data["HasError"] = true | |||
| AssignForm(f, data) | |||
| if len(errors.Overall) > 0 { | |||
| for _, err := range errors.Overall { | |||
| log.Error("CreateIssueForm.Validate: %v", err) | |||
| } | |||
| return | |||
| } | |||
| validate(errors, data, f) | |||
| } | |||
| @@ -9,7 +9,8 @@ import ( | |||
| "reflect" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/martini-contrib/sessions" | |||
| "github.com/gogits/session" | |||
| "github.com/gogits/binding" | |||
| @@ -19,7 +20,7 @@ import ( | |||
| ) | |||
| // SignedInId returns the id of signed in user. | |||
| func SignedInId(session sessions.Session) int64 { | |||
| func SignedInId(session session.SessionStore) int64 { | |||
| userId := session.Get("userId") | |||
| if userId == nil { | |||
| return 0 | |||
| @@ -34,7 +35,7 @@ func SignedInId(session sessions.Session) int64 { | |||
| } | |||
| // SignedInName returns the name of signed in user. | |||
| func SignedInName(session sessions.Session) string { | |||
| func SignedInName(session session.SessionStore) string { | |||
| userName := session.Get("userName") | |||
| if userName == nil { | |||
| return "" | |||
| @@ -46,7 +47,7 @@ func SignedInName(session sessions.Session) string { | |||
| } | |||
| // SignedInUser returns the user object of signed user. | |||
| func SignedInUser(session sessions.Session) *models.User { | |||
| func SignedInUser(session session.SessionStore) *models.User { | |||
| id := SignedInId(session) | |||
| if id <= 0 { | |||
| return nil | |||
| @@ -61,7 +62,7 @@ func SignedInUser(session sessions.Session) *models.User { | |||
| } | |||
| // IsSignedIn check if any user has signed in. | |||
| func IsSignedIn(session sessions.Session) bool { | |||
| func IsSignedIn(session session.SessionStore) bool { | |||
| return SignedInId(session) > 0 | |||
| } | |||
| @@ -16,6 +16,7 @@ import ( | |||
| "github.com/Unknwon/goconfig" | |||
| "github.com/gogits/cache" | |||
| "github.com/gogits/session" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| @@ -37,21 +38,35 @@ var ( | |||
| RunUser string | |||
| RepoRootPath string | |||
| EnableHttpsClone bool | |||
| LogInRememberDays int | |||
| CookieUserName string | |||
| CookieRememberName string | |||
| Cfg *goconfig.ConfigFile | |||
| MailService *Mailer | |||
| LogMode string | |||
| LogConfig string | |||
| Cache cache.Cache | |||
| CacheAdapter string | |||
| CacheConfig string | |||
| LogMode string | |||
| LogConfig string | |||
| SessionProvider string | |||
| SessionConfig *session.Config | |||
| SessionManager *session.Manager | |||
| PictureService string | |||
| PictureRootPath string | |||
| ) | |||
| var Service struct { | |||
| RegisterEmailConfirm bool | |||
| DisenableRegisteration bool | |||
| RequireSignInView bool | |||
| EnableCacheAvatar bool | |||
| ActiveCodeLives int | |||
| ResetPwdCodeLives int | |||
| } | |||
| @@ -82,6 +97,7 @@ func newService() { | |||
| Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) | |||
| Service.DisenableRegisteration = Cfg.MustBool("service", "DISENABLE_REGISTERATION", false) | |||
| Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW", false) | |||
| Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR", false) | |||
| } | |||
| func newLogService() { | |||
| @@ -129,6 +145,10 @@ func newLogService() { | |||
| Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"), | |||
| Cfg.MustValue(modeSec, "RECEIVERS", "[]"), | |||
| Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve")) | |||
| case "database": | |||
| LogConfig = fmt.Sprintf(`{"level":%s,"driver":%s,"conn":%s}`, level, | |||
| Cfg.MustValue(modeSec, "Driver"), | |||
| Cfg.MustValue(modeSec, "CONN")) | |||
| } | |||
| log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), LogMode, LogConfig) | |||
| @@ -159,6 +179,34 @@ func newCacheService() { | |||
| log.Info("Cache Service Enabled") | |||
| } | |||
| func newSessionService() { | |||
| SessionProvider = Cfg.MustValue("session", "PROVIDER", "memory") | |||
| SessionConfig = new(session.Config) | |||
| SessionConfig.ProviderConfig = Cfg.MustValue("session", "PROVIDER_CONFIG") | |||
| SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits") | |||
| SessionConfig.CookieSecure = Cfg.MustBool("session", "COOKIE_SECURE") | |||
| SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true) | |||
| SessionConfig.GcIntervalTime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400) | |||
| SessionConfig.SessionLifeTime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400) | |||
| SessionConfig.SessionIDHashFunc = Cfg.MustValue("session", "SESSION_ID_HASHFUNC", "sha1") | |||
| SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY") | |||
| if SessionProvider == "file" { | |||
| os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm) | |||
| } | |||
| var err error | |||
| SessionManager, err = session.NewManager(SessionProvider, *SessionConfig) | |||
| if err != nil { | |||
| fmt.Printf("Init session system failed, provider: %s, %v\n", | |||
| SessionProvider, err) | |||
| os.Exit(2) | |||
| } | |||
| log.Info("Session Service Enabled") | |||
| } | |||
| func newMailService() { | |||
| // Check mailer setting. | |||
| if Cfg.MustBool("mailer", "ENABLED") { | |||
| @@ -214,6 +262,15 @@ func NewConfigContext() { | |||
| SecretKey = Cfg.MustValue("security", "SECRET_KEY") | |||
| RunUser = Cfg.MustValue("", "RUN_USER") | |||
| EnableHttpsClone = Cfg.MustBool("security", "ENABLE_HTTPS_CLONE", false) | |||
| LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS") | |||
| CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME") | |||
| CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | |||
| PictureService = Cfg.MustValue("picture", "SERVICE") | |||
| PictureRootPath = Cfg.MustValue("picture", "PATH") | |||
| // Determine and create root git reposiroty path. | |||
| RepoRootPath = Cfg.MustValue("repository", "ROOT") | |||
| if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | |||
| @@ -226,6 +283,7 @@ func NewServices() { | |||
| newService() | |||
| newLogService() | |||
| newCacheService() | |||
| newSessionService() | |||
| newMailService() | |||
| newRegisterMailService() | |||
| } | |||
| @@ -72,7 +72,7 @@ func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, | |||
| func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte { | |||
| htmlFlags := 0 | |||
| htmlFlags |= gfm.HTML_USE_XHTML | |||
| // htmlFlags |= gfm.HTML_USE_XHTML | |||
| // htmlFlags |= gfm.HTML_USE_SMARTYPANTS | |||
| // htmlFlags |= gfm.HTML_SMARTYPANTS_FRACTIONS | |||
| // htmlFlags |= gfm.HTML_SMARTYPANTS_LATEX_DASHES | |||
| @@ -81,7 +81,7 @@ func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte { | |||
| htmlFlags |= gfm.HTML_SKIP_SCRIPT | |||
| htmlFlags |= gfm.HTML_GITHUB_BLOCKCODE | |||
| htmlFlags |= gfm.HTML_OMIT_CONTENTS | |||
| htmlFlags |= gfm.HTML_COMPLETE_PAGE | |||
| // htmlFlags |= gfm.HTML_COMPLETE_PAGE | |||
| renderer := &CustomRender{ | |||
| Renderer: gfm.HtmlRenderer(htmlFlags, "", ""), | |||
| urlPrefix: urlPrefix, | |||
| @@ -25,13 +25,17 @@ func EncodeMd5(str string) string { | |||
| return hex.EncodeToString(m.Sum(nil)) | |||
| } | |||
| // Random generate string | |||
| func GetRandomString(n int) string { | |||
| // GetRandomString generate random string by specify chars. | |||
| func GetRandomString(n int, alphabets ...byte) string { | |||
| const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |||
| var bytes = make([]byte, n) | |||
| rand.Read(bytes) | |||
| for i, b := range bytes { | |||
| bytes[i] = alphanum[b%byte(len(alphanum))] | |||
| if len(alphabets) == 0 { | |||
| bytes[i] = alphanum[b%byte(len(alphanum))] | |||
| } else { | |||
| bytes[i] = alphabets[b%byte(len(alphabets))] | |||
| } | |||
| } | |||
| return string(bytes) | |||
| } | |||
| @@ -111,6 +115,85 @@ const ( | |||
| Year = 12 * Month | |||
| ) | |||
| func computeTimeDiff(diff int64) (int64, string) { | |||
| diffStr := "" | |||
| switch { | |||
| case diff <= 0: | |||
| diff = 0 | |||
| diffStr = "now" | |||
| case diff < 2: | |||
| diff = 0 | |||
| diffStr = "1 second" | |||
| case diff < 1*Minute: | |||
| diffStr = fmt.Sprintf("%d seconds", diff) | |||
| diff = 0 | |||
| case diff < 2*Minute: | |||
| diff -= 1 * Minute | |||
| diffStr = "1 minute" | |||
| case diff < 1*Hour: | |||
| diffStr = fmt.Sprintf("%d minutes", diff/Minute) | |||
| diff -= diff / Minute * Minute | |||
| case diff < 2*Hour: | |||
| diff -= 1 * Hour | |||
| diffStr = "1 hour" | |||
| case diff < 1*Day: | |||
| diffStr = fmt.Sprintf("%d hours", diff/Hour) | |||
| diff -= diff / Hour * Hour | |||
| case diff < 2*Day: | |||
| diff -= 1 * Day | |||
| diffStr = "1 day" | |||
| case diff < 1*Week: | |||
| diffStr = fmt.Sprintf("%d days", diff/Day) | |||
| diff -= diff / Day * Day | |||
| case diff < 2*Week: | |||
| diff -= 1 * Week | |||
| diffStr = "1 week" | |||
| case diff < 1*Month: | |||
| diffStr = fmt.Sprintf("%d weeks", diff/Week) | |||
| diff -= diff / Week * Week | |||
| case diff < 2*Month: | |||
| diff -= 1 * Month | |||
| diffStr = "1 month" | |||
| case diff < 1*Year: | |||
| diffStr = fmt.Sprintf("%d months", diff/Month) | |||
| diff -= diff / Month * Month | |||
| case diff < 2*Year: | |||
| diff -= 1 * Year | |||
| diffStr = "1 year" | |||
| default: | |||
| diffStr = fmt.Sprintf("%d years", diff/Year) | |||
| diff = 0 | |||
| } | |||
| return diff, diffStr | |||
| } | |||
| // TimeSincePro calculates the time interval and generate full user-friendly string. | |||
| func TimeSincePro(then time.Time) string { | |||
| now := time.Now() | |||
| diff := now.Unix() - then.Unix() | |||
| if then.After(now) { | |||
| return "future" | |||
| } | |||
| var timeStr, diffStr string | |||
| for { | |||
| if diff == 0 { | |||
| break | |||
| } | |||
| diff, diffStr = computeTimeDiff(diff) | |||
| timeStr += ", " + diffStr | |||
| } | |||
| return strings.TrimPrefix(timeStr, ", ") | |||
| } | |||
| // TimeSince calculates the time interval and generate user-friendly string. | |||
| func TimeSince(then time.Time) string { | |||
| now := time.Now() | |||
| @@ -123,7 +206,6 @@ func TimeSince(then time.Time) string { | |||
| } | |||
| switch { | |||
| case diff <= 0: | |||
| return "now" | |||
| case diff <= 2: | |||
| @@ -156,8 +238,10 @@ func TimeSince(then time.Time) string { | |||
| case diff < 1*Year: | |||
| return fmt.Sprintf("%d months %s", diff/Month, lbl) | |||
| case diff < 18*Month: | |||
| case diff < 2*Year: | |||
| return fmt.Sprintf("1 year %s", lbl) | |||
| default: | |||
| return fmt.Sprintf("%d years %s", diff/Year, lbl) | |||
| } | |||
| return then.String() | |||
| } | |||
| @@ -387,6 +471,7 @@ type Actioner interface { | |||
| GetOpType() int | |||
| GetActUserName() string | |||
| GetRepoName() string | |||
| GetBranch() string | |||
| GetContent() string | |||
| } | |||
| @@ -409,25 +494,34 @@ const ( | |||
| TPL_COMMIT_REPO_LI = `<div><img id="gogs-user-avatar-commit" src="%s?s=16" alt="user-avatar" title="username"/> <a href="/%s/%s/commit/%s">%s</a> %s</div>` | |||
| ) | |||
| type PushCommits struct { | |||
| Len int | |||
| Commits [][]string | |||
| } | |||
| // ActionDesc accepts int that represents action operation type | |||
| // and returns the description. | |||
| func ActionDesc(act Actioner, avatarLink string) string { | |||
| actUserName := act.GetActUserName() | |||
| repoName := act.GetRepoName() | |||
| branch := act.GetBranch() | |||
| content := act.GetContent() | |||
| switch act.GetOpType() { | |||
| case 1: // Create repository. | |||
| return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, actUserName, repoName, repoName) | |||
| case 5: // Commit repository. | |||
| var commits [][]string | |||
| if err := json.Unmarshal([]byte(content), &commits); err != nil { | |||
| var push *PushCommits | |||
| if err := json.Unmarshal([]byte(content), &push); err != nil { | |||
| return err.Error() | |||
| } | |||
| buf := bytes.NewBuffer([]byte("\n")) | |||
| for _, commit := range commits { | |||
| for _, commit := range push.Commits { | |||
| buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, avatarLink, actUserName, repoName, commit[0], commit[0][:7], commit[1]) + "\n") | |||
| } | |||
| return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, actUserName, repoName, "master", "master", actUserName, repoName, actUserName, repoName, | |||
| if push.Len > 3 { | |||
| buf.WriteString(fmt.Sprintf(`<div><a href="/%s/%s/commits/%s">%d other commits >></a></div>`, actUserName, repoName, branch, push.Len)) | |||
| } | |||
| return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, actUserName, repoName, branch, branch, actUserName, repoName, actUserName, repoName, | |||
| buf.String()) | |||
| default: | |||
| return "invalid type" | |||
| @@ -5,44 +5,54 @@ | |||
| package middleware | |||
| import ( | |||
| "net/url" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| // SignInRequire requires user to sign in. | |||
| func SignInRequire(redirect bool) martini.Handler { | |||
| return func(ctx *Context) { | |||
| if !ctx.IsSigned { | |||
| if redirect { | |||
| ctx.Redirect("/user/login") | |||
| } | |||
| return | |||
| } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { | |||
| ctx.Data["Title"] = "Activate Your Account" | |||
| ctx.HTML(200, "user/active") | |||
| return | |||
| } | |||
| } | |||
| type ToggleOptions struct { | |||
| SignInRequire bool | |||
| SignOutRequire bool | |||
| AdminRequire bool | |||
| DisableCsrf bool | |||
| } | |||
| // SignOutRequire requires user to sign out. | |||
| func SignOutRequire() martini.Handler { | |||
| func Toggle(options *ToggleOptions) martini.Handler { | |||
| return func(ctx *Context) { | |||
| if ctx.IsSigned { | |||
| if options.SignOutRequire && ctx.IsSigned { | |||
| ctx.Redirect("/") | |||
| return | |||
| } | |||
| } | |||
| } | |||
| // AdminRequire requires user signed in as administor. | |||
| func AdminRequire() martini.Handler { | |||
| return func(ctx *Context) { | |||
| if !ctx.User.IsAdmin { | |||
| ctx.Error(403) | |||
| return | |||
| if !options.DisableCsrf { | |||
| if ctx.Req.Method == "POST" { | |||
| if !ctx.CsrfTokenValid() { | |||
| ctx.Error(403, "CSRF token does not match") | |||
| return | |||
| } | |||
| } | |||
| } | |||
| if options.SignInRequire { | |||
| if !ctx.IsSigned { | |||
| ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI)) | |||
| ctx.Redirect("/user/login") | |||
| return | |||
| } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { | |||
| ctx.Data["Title"] = "Activate Your Account" | |||
| ctx.HTML(200, "user/active") | |||
| return | |||
| } | |||
| } | |||
| if options.AdminRequire { | |||
| if !ctx.User.IsAdmin { | |||
| ctx.Error(403) | |||
| return | |||
| } | |||
| ctx.Data["PageIsAdmin"] = true | |||
| } | |||
| ctx.Data["PageIsAdmin"] = true | |||
| } | |||
| } | |||
| @@ -5,14 +5,20 @@ | |||
| package middleware | |||
| import ( | |||
| "crypto/hmac" | |||
| "crypto/sha1" | |||
| "encoding/base64" | |||
| "fmt" | |||
| "html/template" | |||
| "net/http" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/martini-contrib/sessions" | |||
| "github.com/gogits/cache" | |||
| "github.com/gogits/session" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| @@ -27,11 +33,13 @@ type Context struct { | |||
| p martini.Params | |||
| Req *http.Request | |||
| Res http.ResponseWriter | |||
| Session sessions.Session | |||
| Session session.SessionStore | |||
| Cache cache.Cache | |||
| User *models.User | |||
| IsSigned bool | |||
| csrfToken string | |||
| Repo struct { | |||
| IsValid bool | |||
| IsOwner bool | |||
| @@ -90,23 +98,157 @@ func (ctx *Context) Handle(status int, title string, err error) { | |||
| ctx.HTML(status, fmt.Sprintf("status/%d", status)) | |||
| } | |||
| func (ctx *Context) GetCookie(name string) string { | |||
| cookie, err := ctx.Req.Cookie(name) | |||
| if err != nil { | |||
| return "" | |||
| } | |||
| return cookie.Value | |||
| } | |||
| func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { | |||
| cookie := http.Cookie{} | |||
| cookie.Name = name | |||
| cookie.Value = value | |||
| if len(others) > 0 { | |||
| switch v := others[0].(type) { | |||
| case int: | |||
| cookie.MaxAge = v | |||
| case int64: | |||
| cookie.MaxAge = int(v) | |||
| case int32: | |||
| cookie.MaxAge = int(v) | |||
| } | |||
| } | |||
| // default "/" | |||
| if len(others) > 1 { | |||
| if v, ok := others[1].(string); ok && len(v) > 0 { | |||
| cookie.Path = v | |||
| } | |||
| } else { | |||
| cookie.Path = "/" | |||
| } | |||
| // default empty | |||
| if len(others) > 2 { | |||
| if v, ok := others[2].(string); ok && len(v) > 0 { | |||
| cookie.Domain = v | |||
| } | |||
| } | |||
| // default empty | |||
| if len(others) > 3 { | |||
| switch v := others[3].(type) { | |||
| case bool: | |||
| cookie.Secure = v | |||
| default: | |||
| if others[3] != nil { | |||
| cookie.Secure = true | |||
| } | |||
| } | |||
| } | |||
| // default false. for session cookie default true | |||
| if len(others) > 4 { | |||
| if v, ok := others[4].(bool); ok && v { | |||
| cookie.HttpOnly = true | |||
| } | |||
| } | |||
| ctx.Res.Header().Add("Set-Cookie", cookie.String()) | |||
| } | |||
| // Get secure cookie from request by a given key. | |||
| func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { | |||
| val := ctx.GetCookie(key) | |||
| if val == "" { | |||
| return "", false | |||
| } | |||
| parts := strings.SplitN(val, "|", 3) | |||
| if len(parts) != 3 { | |||
| return "", false | |||
| } | |||
| vs := parts[0] | |||
| timestamp := parts[1] | |||
| sig := parts[2] | |||
| h := hmac.New(sha1.New, []byte(Secret)) | |||
| fmt.Fprintf(h, "%s%s", vs, timestamp) | |||
| if fmt.Sprintf("%02x", h.Sum(nil)) != sig { | |||
| return "", false | |||
| } | |||
| res, _ := base64.URLEncoding.DecodeString(vs) | |||
| return string(res), true | |||
| } | |||
| // Set Secure cookie for response. | |||
| func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { | |||
| vs := base64.URLEncoding.EncodeToString([]byte(value)) | |||
| timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) | |||
| h := hmac.New(sha1.New, []byte(Secret)) | |||
| fmt.Fprintf(h, "%s%s", vs, timestamp) | |||
| sig := fmt.Sprintf("%02x", h.Sum(nil)) | |||
| cookie := strings.Join([]string{vs, timestamp, sig}, "|") | |||
| ctx.SetCookie(name, cookie, others...) | |||
| } | |||
| func (ctx *Context) CsrfToken() string { | |||
| if len(ctx.csrfToken) > 0 { | |||
| return ctx.csrfToken | |||
| } | |||
| token := ctx.GetCookie("_csrf") | |||
| if len(token) == 0 { | |||
| token = base.GetRandomString(30) | |||
| ctx.SetCookie("_csrf", token) | |||
| } | |||
| ctx.csrfToken = token | |||
| return token | |||
| } | |||
| func (ctx *Context) CsrfTokenValid() bool { | |||
| token := ctx.Query("_csrf") | |||
| if token == "" { | |||
| token = ctx.Req.Header.Get("X-Csrf-Token") | |||
| } | |||
| if token == "" { | |||
| return false | |||
| } else if ctx.csrfToken != token { | |||
| return false | |||
| } | |||
| return true | |||
| } | |||
| // InitContext initializes a classic context for a request. | |||
| func InitContext() martini.Handler { | |||
| return func(res http.ResponseWriter, r *http.Request, c martini.Context, | |||
| session sessions.Session, rd *Render) { | |||
| return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) { | |||
| ctx := &Context{ | |||
| c: c, | |||
| // p: p, | |||
| Req: r, | |||
| Res: res, | |||
| Session: session, | |||
| Cache: base.Cache, | |||
| Render: rd, | |||
| Req: r, | |||
| Res: res, | |||
| Cache: base.Cache, | |||
| Render: rd, | |||
| } | |||
| ctx.Data["PageStartTime"] = time.Now() | |||
| // start session | |||
| ctx.Session = base.SessionManager.SessionStart(res, r) | |||
| rw := res.(martini.ResponseWriter) | |||
| rw.Before(func(martini.ResponseWriter) { | |||
| ctx.Session.SessionRelease(res) | |||
| }) | |||
| // Get user from session if logined. | |||
| user := auth.SignedInUser(session) | |||
| user := auth.SignedInUser(ctx.Session) | |||
| ctx.User = user | |||
| ctx.IsSigned = user != nil | |||
| @@ -119,7 +261,9 @@ func InitContext() martini.Handler { | |||
| ctx.Data["IsAdmin"] = ctx.User.IsAdmin | |||
| } | |||
| ctx.Data["PageStartTime"] = time.Now() | |||
| // get or create csrf token | |||
| ctx.Data["CsrfToken"] = ctx.CsrfToken() | |||
| ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`) | |||
| c.Map(ctx) | |||
| @@ -242,8 +242,11 @@ func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOpt | |||
| } | |||
| } | |||
| func (r *Render) Error(status int) { | |||
| func (r *Render) Error(status int, message ...string) { | |||
| r.WriteHeader(status) | |||
| if len(message) > 0 { | |||
| r.Write([]byte(message[0])) | |||
| } | |||
| } | |||
| func (r *Render) Redirect(location string, status ...int) { | |||
| @@ -54,7 +54,7 @@ func RepoAssignment(redirect bool) martini.Handler { | |||
| ctx.Repo.Owner = user | |||
| // get repository | |||
| repo, err := models.GetRepositoryByName(user, params["reponame"]) | |||
| repo, err := models.GetRepositoryByName(user.Id, params["reponame"]) | |||
| if err != nil { | |||
| if redirect { | |||
| ctx.Redirect("/") | |||
| @@ -69,8 +69,12 @@ func RepoAssignment(redirect bool) martini.Handler { | |||
| ctx.Repo.IsWatching = models.IsWatching(ctx.User.Id, repo.Id) | |||
| } | |||
| ctx.Repo.Repository = repo | |||
| scheme := "http" | |||
| if base.EnableHttpsClone { | |||
| scheme = "https" | |||
| } | |||
| ctx.Repo.CloneLink.SSH = fmt.Sprintf("git@%s:%s/%s.git", base.Domain, user.LowerName, repo.LowerName) | |||
| ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("https://%s/%s/%s.git", base.Domain, user.LowerName, repo.LowerName) | |||
| ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s://%s/%s/%s.git", scheme, base.Domain, user.LowerName, repo.LowerName) | |||
| ctx.Data["IsRepositoryValid"] = true | |||
| ctx.Data["Repository"] = repo | |||
| @@ -346,6 +346,10 @@ html, body { | |||
| border-left: 4px solid #DD4B39; | |||
| } | |||
| #gogs-repo-setting-container .form-horizontal label { | |||
| line-height: 30px; | |||
| } | |||
| /* gogits user ssh keys */ | |||
| #gogs-ssh-keys .list-group-item { | |||
| @@ -575,12 +579,12 @@ html, body { | |||
| min-width: 200px; | |||
| } | |||
| #gogs-repo-clone .dropdown-menu{ | |||
| #gogs-repo-clone .dropdown-menu { | |||
| width: 400px; | |||
| padding: 20px; | |||
| } | |||
| #gogs-repo-clone .input-group{ | |||
| #gogs-repo-clone .input-group { | |||
| margin-bottom: 15px; | |||
| } | |||
| @@ -135,13 +135,12 @@ | |||
| box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc; | |||
| } | |||
| .markdown > pre > code, | |||
| .markdown > pre > ol.linenums > li > code { | |||
| .markdown > pre > code { | |||
| white-space: pre; | |||
| word-wrap: normal; | |||
| } | |||
| .markdown > pre > ol.linenums > li > code { | |||
| .markdown > pre > ol.linenums > li { | |||
| padding: 0 10px; | |||
| } | |||
| @@ -175,6 +174,10 @@ | |||
| margin-bottom: 0; | |||
| } | |||
| .markdown img { | |||
| max-width: 100%; | |||
| } | |||
| .markdown .btn { | |||
| color: #fff; | |||
| } | |||
| @@ -2,6 +2,56 @@ var Gogits = { | |||
| "PageIsSignup": false | |||
| }; | |||
| (function($){ | |||
| // extend jQuery ajax, set csrf token value | |||
| var ajax = $.ajax; | |||
| $.extend({ | |||
| ajax: function(url, options) { | |||
| if (typeof url === 'object') { | |||
| options = url; | |||
| url = undefined; | |||
| } | |||
| options = options || {}; | |||
| url = options.url; | |||
| var csrftoken = $('meta[name=_csrf]').attr('content'); | |||
| var headers = options.headers || {}; | |||
| var domain = document.domain.replace(/\./ig, '\\.'); | |||
| if (!/^(http:|https:).*/.test(url) || eval('/^(http:|https:)\\/\\/(.+\\.)*' + domain + '.*/').test(url)) { | |||
| headers = $.extend(headers, {'X-Csrf-Token':csrftoken}); | |||
| } | |||
| options.headers = headers; | |||
| var callback = options.success; | |||
| options.success = function(data){ | |||
| if(data.once){ | |||
| // change all _once value if ajax data.once exist | |||
| $('[name=_once]').val(data.once); | |||
| } | |||
| if(callback){ | |||
| callback.apply(this, arguments); | |||
| } | |||
| }; | |||
| return ajax(url, options); | |||
| }, | |||
| changeHash: function(hash) { | |||
| if(history.pushState) { | |||
| history.pushState(null, null, hash); | |||
| } | |||
| else { | |||
| location.hash = hash; | |||
| } | |||
| }, | |||
| deSelect: function() { | |||
| if(window.getSelection) { | |||
| window.getSelection().removeAllRanges(); | |||
| } else { | |||
| document.selection.empty(); | |||
| } | |||
| } | |||
| }); | |||
| }(jQuery)); | |||
| (function ($) { | |||
| Gogits.showTab = function (selector, index) { | |||
| @@ -65,7 +115,7 @@ var Gogits = { | |||
| }; | |||
| // fix dropdown inside click | |||
| Gogits.initDropDown = function(){ | |||
| $('.dropdown-menu').on('click','a,button,input,select',function(e){ | |||
| $('.dropdown-menu.no-propagation').on('click',function(e){ | |||
| e.stopPropagation(); | |||
| }); | |||
| }; | |||
| @@ -77,25 +127,6 @@ var Gogits = { | |||
| $pre.addClass('prettyprint linenums'); | |||
| prettyPrint(); | |||
| var $lineNums = $pre.parent().siblings('.lines-num'); | |||
| if ($lineNums.length > 0) { | |||
| var nums = $pre.find('ol.linenums > li').length; | |||
| for (var i = 1; i <= nums; i++) { | |||
| $lineNums.append('<span id="L' + i + '" rel=".L' + i + '">' + i + '</span>'); | |||
| } | |||
| var last; | |||
| $(document).on('click', '.lines-num span', function () { | |||
| var $e = $(this); | |||
| if (last) { | |||
| last.removeClass('active'); | |||
| } | |||
| last = $e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel')); | |||
| last.addClass('active'); | |||
| window.location.href = '#' + $e.attr('id'); | |||
| }); | |||
| } | |||
| // Set anchor. | |||
| var headers = {}; | |||
| $md.find('h1, h2, h3, h4, h5, h6').each(function () { | |||
| @@ -115,6 +146,70 @@ var Gogits = { | |||
| }); | |||
| } | |||
| Gogits.renderCodeView = function () { | |||
| function selectRange($list, $select, $from){ | |||
| $list.removeClass('active'); | |||
| if($from){ | |||
| var a = parseInt($select.attr('rel').substr(1)); | |||
| var b = parseInt($from.attr('rel').substr(1)); | |||
| var c; | |||
| if(a != b){ | |||
| if(a > b){ | |||
| c = a; | |||
| a = b; | |||
| b = c; | |||
| } | |||
| var classes = []; | |||
| for(i = a; i <= b; i++) { | |||
| classes.push('.L'+i); | |||
| } | |||
| $list.filter(classes.join(',')).addClass('active'); | |||
| $.changeHash('#L' + a + '-' + 'L' + b); | |||
| return | |||
| } | |||
| } | |||
| $select.addClass('active'); | |||
| $.changeHash('#' + $select.attr('rel')); | |||
| } | |||
| $(document).on('click', '.lines-num span', function (e) { | |||
| var $select = $(this); | |||
| var $list = $select.parent().siblings('.lines-code').find('ol.linenums > li'); | |||
| selectRange($list, $list.filter('[rel='+$select.attr('rel')+']'), (e.shiftKey?$list.filter('.active').eq(0):null)); | |||
| $.deSelect(); | |||
| }); | |||
| $('.code-view .lines-code > pre').each(function(){ | |||
| var $pre = $(this); | |||
| var $lineCode = $pre.parent(); | |||
| var $lineNums = $lineCode.siblings('.lines-num'); | |||
| if ($lineNums.length > 0) { | |||
| var nums = $pre.find('ol.linenums > li').length; | |||
| for (var i = 1; i <= nums; i++) { | |||
| $lineNums.append('<span id="L' + i + '" rel="L' + i + '">' + i + '</span>'); | |||
| } | |||
| } | |||
| }); | |||
| $(window).on('hashchange', function(e) { | |||
| var m = window.location.hash.match(/^#(L\d+)\-(L\d+)$/); | |||
| var $list = $('.code-view ol.linenums > li'); | |||
| if(m){ | |||
| var $first = $list.filter('.'+m[1]); | |||
| selectRange($list, $first, $list.filter('.'+m[2])); | |||
| $("html, body").scrollTop($first.offset().top-200); | |||
| console.log($first.offset().top); | |||
| return; | |||
| } | |||
| m = window.location.hash.match(/^#(L\d+)$/); | |||
| if(m){ | |||
| var $first = $list.filter('.'+m[1]); | |||
| selectRange($list, $first); | |||
| $("html, body").scrollTop($first.offset().top-200); | |||
| } | |||
| }).trigger('hashchange'); | |||
| }; | |||
| })(jQuery); | |||
| // ajax utils | |||
| @@ -144,6 +239,7 @@ function initCore() { | |||
| Gogits.initModals(); | |||
| Gogits.initDropDown(); | |||
| Gogits.renderMarkdown(); | |||
| Gogits.renderCodeView(); | |||
| } | |||
| function initRegister() { | |||
| @@ -340,7 +340,7 @@ q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(? | |||
| s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, | |||
| q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= | |||
| c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol"); | |||
| r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d+1),k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a; | |||
| r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.setAttribute("rel", "L"+(i+d+1)),k.className="L"+(i+d+1),k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a; | |||
| a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g, | |||
| t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], | |||
| "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], | |||
| @@ -5,7 +5,10 @@ | |||
| package admin | |||
| import ( | |||
| "fmt" | |||
| "runtime" | |||
| "strings" | |||
| "time" | |||
| "github.com/codegangsta/martini" | |||
| @@ -14,10 +17,93 @@ import ( | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| ) | |||
| var startTime = time.Now() | |||
| var sysStatus struct { | |||
| Uptime string | |||
| NumGoroutine int | |||
| // General statistics. | |||
| MemAllocated string // bytes allocated and still in use | |||
| MemTotal string // bytes allocated (even if freed) | |||
| MemSys string // bytes obtained from system (sum of XxxSys below) | |||
| Lookups uint64 // number of pointer lookups | |||
| MemMallocs uint64 // number of mallocs | |||
| MemFrees uint64 // number of frees | |||
| // Main allocation heap statistics. | |||
| HeapAlloc string // bytes allocated and still in use | |||
| HeapSys string // bytes obtained from system | |||
| HeapIdle string // bytes in idle spans | |||
| HeapInuse string // bytes in non-idle span | |||
| HeapReleased string // bytes released to the OS | |||
| HeapObjects uint64 // total number of allocated objects | |||
| // Low-level fixed-size structure allocator statistics. | |||
| // Inuse is bytes used now. | |||
| // Sys is bytes obtained from system. | |||
| StackInuse string // bootstrap stacks | |||
| StackSys string | |||
| MSpanInuse string // mspan structures | |||
| MSpanSys string | |||
| MCacheInuse string // mcache structures | |||
| MCacheSys string | |||
| BuckHashSys string // profiling bucket hash table | |||
| GCSys string // GC metadata | |||
| OtherSys string // other system allocations | |||
| // Garbage collector statistics. | |||
| NextGC string // next run in HeapAlloc time (bytes) | |||
| LastGC string // last run in absolute time (ns) | |||
| PauseTotalNs string | |||
| PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] | |||
| NumGC uint32 | |||
| } | |||
| func updateSystemStatus() { | |||
| sysStatus.Uptime = base.TimeSincePro(startTime) | |||
| m := new(runtime.MemStats) | |||
| runtime.ReadMemStats(m) | |||
| sysStatus.NumGoroutine = runtime.NumGoroutine() | |||
| sysStatus.MemAllocated = base.FileSize(int64(m.Alloc)) | |||
| sysStatus.MemTotal = base.FileSize(int64(m.TotalAlloc)) | |||
| sysStatus.MemSys = base.FileSize(int64(m.Sys)) | |||
| sysStatus.Lookups = m.Lookups | |||
| sysStatus.MemMallocs = m.Mallocs | |||
| sysStatus.MemFrees = m.Frees | |||
| sysStatus.HeapAlloc = base.FileSize(int64(m.HeapAlloc)) | |||
| sysStatus.HeapSys = base.FileSize(int64(m.HeapSys)) | |||
| sysStatus.HeapIdle = base.FileSize(int64(m.HeapIdle)) | |||
| sysStatus.HeapInuse = base.FileSize(int64(m.HeapInuse)) | |||
| sysStatus.HeapReleased = base.FileSize(int64(m.HeapReleased)) | |||
| sysStatus.HeapObjects = m.HeapObjects | |||
| sysStatus.StackInuse = base.FileSize(int64(m.StackInuse)) | |||
| sysStatus.StackSys = base.FileSize(int64(m.StackSys)) | |||
| sysStatus.MSpanInuse = base.FileSize(int64(m.MSpanInuse)) | |||
| sysStatus.MSpanSys = base.FileSize(int64(m.MSpanSys)) | |||
| sysStatus.MCacheInuse = base.FileSize(int64(m.MCacheInuse)) | |||
| sysStatus.MCacheSys = base.FileSize(int64(m.MCacheSys)) | |||
| sysStatus.BuckHashSys = base.FileSize(int64(m.BuckHashSys)) | |||
| sysStatus.GCSys = base.FileSize(int64(m.GCSys)) | |||
| sysStatus.OtherSys = base.FileSize(int64(m.OtherSys)) | |||
| sysStatus.NextGC = base.FileSize(int64(m.NextGC)) | |||
| sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000) | |||
| sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000) | |||
| sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000) | |||
| sysStatus.NumGC = m.NumGC | |||
| } | |||
| func Dashboard(ctx *middleware.Context) { | |||
| ctx.Data["Title"] = "Admin Dashboard" | |||
| ctx.Data["PageIsDashboard"] = true | |||
| ctx.Data["Stats"] = models.GetStatistic() | |||
| updateSystemStatus() | |||
| ctx.Data["SysStatus"] = sysStatus | |||
| ctx.HTML(200, "admin/dashboard") | |||
| } | |||
| @@ -55,6 +141,7 @@ func Config(ctx *middleware.Context) { | |||
| ctx.Data["Domain"] = base.Domain | |||
| ctx.Data["RunUser"] = base.RunUser | |||
| ctx.Data["RunMode"] = strings.Title(martini.Env) | |||
| ctx.Data["EnableHttpsClone"] = base.EnableHttpsClone | |||
| ctx.Data["RepoRootPath"] = base.RepoRootPath | |||
| ctx.Data["Service"] = base.Service | |||
| @@ -70,6 +157,12 @@ func Config(ctx *middleware.Context) { | |||
| ctx.Data["CacheAdapter"] = base.CacheAdapter | |||
| ctx.Data["CacheConfig"] = base.CacheConfig | |||
| ctx.Data["SessionProvider"] = base.SessionProvider | |||
| ctx.Data["SessionConfig"] = base.SessionConfig | |||
| ctx.Data["PictureService"] = base.PictureService | |||
| ctx.Data["PictureRootPath"] = base.PictureRootPath | |||
| ctx.Data["LogMode"] = base.LogMode | |||
| ctx.Data["LogConfig"] = base.LogConfig | |||
| @@ -107,3 +107,38 @@ func EditUser(ctx *middleware.Context, params martini.Params, form auth.AdminEdi | |||
| log.Trace("%s User profile updated by admin(%s): %s", ctx.Req.RequestURI, | |||
| ctx.User.LowerName, ctx.User.LowerName) | |||
| } | |||
| func DeleteUser(ctx *middleware.Context, params martini.Params) { | |||
| ctx.Data["Title"] = "Edit Account" | |||
| ctx.Data["PageIsUsers"] = true | |||
| uid, err := base.StrTo(params["userid"]).Int() | |||
| if err != nil { | |||
| ctx.Handle(200, "admin.user.EditUser", err) | |||
| return | |||
| } | |||
| u, err := models.GetUserById(int64(uid)) | |||
| if err != nil { | |||
| ctx.Handle(200, "admin.user.EditUser", err) | |||
| return | |||
| } | |||
| if err = models.DeleteUser(u); err != nil { | |||
| ctx.Data["HasError"] = true | |||
| switch err { | |||
| case models.ErrUserOwnRepos: | |||
| ctx.Data["ErrorMsg"] = "This account still has ownership of repository, owner has to delete or transfer them first." | |||
| ctx.Data["User"] = u | |||
| ctx.HTML(200, "admin/users/edit") | |||
| default: | |||
| ctx.Handle(200, "admin.user.DeleteUser", err) | |||
| } | |||
| return | |||
| } | |||
| log.Trace("%s User deleted by admin(%s): %s", ctx.Req.RequestURI, | |||
| ctx.User.LowerName, ctx.User.LowerName) | |||
| ctx.Redirect("/admin/users") | |||
| } | |||
| @@ -20,5 +20,12 @@ func Home(ctx *middleware.Context) { | |||
| func Help(ctx *middleware.Context) { | |||
| ctx.Data["PageIsHelp"] = true | |||
| ctx.Data["Title"] = "Help" | |||
| ctx.HTML(200, "help") | |||
| } | |||
| func NotFound(ctx *middleware.Context) { | |||
| ctx.Data["PageIsNotFound"] = true | |||
| ctx.Data["Title"] = "Page Not Found" | |||
| ctx.Handle(404, "home.NotFound", nil) | |||
| } | |||
| @@ -0,0 +1,85 @@ | |||
| // 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 repo | |||
| import ( | |||
| "fmt" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| ) | |||
| func Issues(ctx *middleware.Context, params martini.Params) { | |||
| ctx.Data["Title"] = "Issues" | |||
| ctx.Data["IsRepoToolbarIssues"] = true | |||
| milestoneId, _ := base.StrTo(params["milestone"]).Int() | |||
| page, _ := base.StrTo(params["page"]).Int() | |||
| var err error | |||
| ctx.Data["Issues"], err = models.GetIssues(0, ctx.Repo.Repository.Id, 0, | |||
| int64(milestoneId), page, params["state"] == "closed", false, params["labels"], params["sortType"]) | |||
| if err != nil { | |||
| ctx.Handle(200, "issue.Issues: %v", err) | |||
| return | |||
| } | |||
| ctx.HTML(200, "repo/issues") | |||
| } | |||
| func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { | |||
| if !ctx.Repo.IsOwner { | |||
| ctx.Handle(404, "issue.CreateIssue", nil) | |||
| return | |||
| } | |||
| ctx.Data["Title"] = "Create issue" | |||
| if ctx.Req.Method == "GET" { | |||
| ctx.HTML(200, "issue/create") | |||
| return | |||
| } | |||
| if ctx.HasError() { | |||
| ctx.HTML(200, "issue/create") | |||
| return | |||
| } | |||
| issue, err := models.CreateIssue(ctx.User.Id, form.RepoId, form.MilestoneId, form.AssigneeId, | |||
| form.IssueName, form.Labels, form.Content, false) | |||
| if err == nil { | |||
| log.Trace("%s Issue created: %d", form.RepoId, issue.Id) | |||
| ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", params["username"], params["reponame"], issue.Index)) | |||
| return | |||
| } | |||
| ctx.Handle(200, "issue.CreateIssue", err) | |||
| } | |||
| func ViewIssue(ctx *middleware.Context, params martini.Params) { | |||
| issueid, err := base.StrTo(params["issueid"]).Int() | |||
| if err != nil { | |||
| ctx.Handle(404, "issue.ViewIssue", err) | |||
| return | |||
| } | |||
| issue, err := models.GetIssueById(int64(issueid)) | |||
| if err != nil { | |||
| if err == models.ErrIssueNotExist { | |||
| ctx.Handle(404, "issue.ViewIssue", err) | |||
| } else { | |||
| ctx.Handle(200, "issue.ViewIssue", err) | |||
| } | |||
| return | |||
| } | |||
| ctx.Data["Title"] = issue.Name | |||
| ctx.Data["Issue"] = issue | |||
| ctx.HTML(200, "issue/view") | |||
| } | |||
| @@ -5,8 +5,17 @@ | |||
| package repo | |||
| import ( | |||
| "path" | |||
| "strings" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/webdav" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| ) | |||
| @@ -22,11 +31,16 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) { | |||
| return | |||
| } | |||
| if ctx.HasError() { | |||
| ctx.HTML(200, "repo/create") | |||
| return | |||
| } | |||
| _, err := models.CreateRepository(ctx.User, form.RepoName, form.Description, | |||
| form.Language, form.License, form.Visibility == "private", form.InitReadme == "on") | |||
| if err == nil { | |||
| log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName) | |||
| ctx.Redirect("/"+ctx.User.Name+"/"+form.RepoName, 302) | |||
| ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName) | |||
| return | |||
| } else if err == models.ErrRepoAlreadyExist { | |||
| ctx.RenderWithErr("Repository name has already been used", "repo/create", &form) | |||
| @@ -38,13 +52,247 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) { | |||
| ctx.Handle(200, "repo.Create", err) | |||
| } | |||
| func SettingPost(ctx *middleware.Context) { | |||
| func Branches(ctx *middleware.Context, params martini.Params) { | |||
| if !ctx.Repo.IsValid { | |||
| return | |||
| } | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| ctx.Handle(200, "repo.Branches", err) | |||
| return | |||
| } else if len(brs) == 0 { | |||
| ctx.Handle(404, "repo.Branches", nil) | |||
| return | |||
| } | |||
| ctx.Data["Username"] = params["username"] | |||
| ctx.Data["Reponame"] = params["reponame"] | |||
| ctx.Data["Branchname"] = brs[0] | |||
| ctx.Data["Branches"] = brs | |||
| ctx.Data["IsRepoToolbarBranches"] = true | |||
| ctx.HTML(200, "repo/branches") | |||
| } | |||
| func Single(ctx *middleware.Context, params martini.Params) { | |||
| if !ctx.Repo.IsValid { | |||
| return | |||
| } | |||
| if len(params["branchname"]) == 0 { | |||
| params["branchname"] = "master" | |||
| } | |||
| // Get tree path | |||
| treename := params["_1"] | |||
| if len(treename) > 0 && treename[len(treename)-1] == '/' { | |||
| ctx.Redirect("/" + ctx.Repo.Owner.LowerName + "/" + | |||
| ctx.Repo.Repository.Name + "/src/" + params["branchname"] + "/" + treename[:len(treename)-1]) | |||
| return | |||
| } | |||
| ctx.Data["IsRepoToolbarSource"] = true | |||
| // Branches. | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| //log.Error("repo.Single(GetBranches): %v", err) | |||
| ctx.Handle(404, "repo.Single(GetBranches)", err) | |||
| return | |||
| } else if ctx.Repo.Repository.IsBare { | |||
| ctx.Data["IsBareRepo"] = true | |||
| ctx.HTML(200, "repo/single") | |||
| return | |||
| } | |||
| ctx.Data["Branches"] = brs | |||
| repoFile, err := models.GetTargetFile(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"], treename) | |||
| if err != nil && err != models.ErrRepoFileNotExist { | |||
| //log.Error("repo.Single(GetTargetFile): %v", err) | |||
| ctx.Handle(404, "repo.Single(GetTargetFile)", err) | |||
| return | |||
| } | |||
| branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] | |||
| if len(treename) != 0 && repoFile == nil { | |||
| ctx.Handle(404, "repo.Single", nil) | |||
| return | |||
| } | |||
| if repoFile != nil && repoFile.IsFile() { | |||
| if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { | |||
| ctx.Data["FileIsLarge"] = true | |||
| } else if blob, err := repoFile.LookupBlob(); err != nil { | |||
| //log.Error("repo.Single(repoFile.LookupBlob): %v", err) | |||
| ctx.Handle(404, "repo.Single(repoFile.LookupBlob)", err) | |||
| } else { | |||
| ctx.Data["IsFile"] = true | |||
| ctx.Data["FileName"] = repoFile.Name | |||
| ext := path.Ext(repoFile.Name) | |||
| if len(ext) > 0 { | |||
| ext = ext[1:] | |||
| } | |||
| ctx.Data["FileExt"] = ext | |||
| readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) | |||
| ctx.Data["ReadmeExist"] = readmeExist | |||
| if readmeExist { | |||
| ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), "")) | |||
| } else { | |||
| ctx.Data["FileContent"] = string(blob.Contents()) | |||
| } | |||
| } | |||
| } else { | |||
| // Directory and file list. | |||
| files, err := models.GetReposFiles(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"], treename) | |||
| if err != nil { | |||
| //log.Error("repo.Single(GetReposFiles): %v", err) | |||
| ctx.Handle(404, "repo.Single(GetReposFiles)", err) | |||
| return | |||
| } | |||
| ctx.Data["Files"] = files | |||
| var readmeFile *models.RepoFile | |||
| for _, f := range files { | |||
| if !f.IsFile() || !base.IsReadmeFile(f.Name) { | |||
| continue | |||
| } else { | |||
| readmeFile = f | |||
| break | |||
| } | |||
| } | |||
| if readmeFile != nil { | |||
| ctx.Data["ReadmeExist"] = true | |||
| // if file large than 1M not show it | |||
| if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { | |||
| ctx.Data["FileIsLarge"] = true | |||
| } else if blob, err := readmeFile.LookupBlob(); err != nil { | |||
| ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err) | |||
| return | |||
| } else { | |||
| // current repo branch link | |||
| ctx.Data["FileName"] = readmeFile.Name | |||
| ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) | |||
| } | |||
| } | |||
| } | |||
| ctx.Data["Username"] = params["username"] | |||
| ctx.Data["Reponame"] = params["reponame"] | |||
| ctx.Data["Branchname"] = params["branchname"] | |||
| var treenames []string | |||
| Paths := make([]string, 0) | |||
| if len(treename) > 0 { | |||
| treenames = strings.Split(treename, "/") | |||
| for i, _ := range treenames { | |||
| Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) | |||
| } | |||
| ctx.Data["HasParentPath"] = true | |||
| if len(Paths)-2 >= 0 { | |||
| ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2] | |||
| } | |||
| } | |||
| // Get latest commit according username and repo name | |||
| commit, err := models.GetCommit(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"]) | |||
| if err != nil { | |||
| log.Error("repo.Single(GetCommit): %v", err) | |||
| ctx.Handle(404, "repo.Single(GetCommit)", err) | |||
| return | |||
| } | |||
| ctx.Data["LastCommit"] = commit | |||
| ctx.Data["Paths"] = Paths | |||
| ctx.Data["Treenames"] = treenames | |||
| ctx.Data["BranchLink"] = branchLink | |||
| ctx.HTML(200, "repo/single") | |||
| } | |||
| func Http(ctx *middleware.Context, params martini.Params) { | |||
| /*if !ctx.Repo.IsValid { | |||
| return | |||
| }*/ | |||
| // TODO: access check | |||
| username := params["username"] | |||
| reponame := params["reponame"] | |||
| if strings.HasSuffix(reponame, ".git") { | |||
| reponame = reponame[:len(reponame)-4] | |||
| } | |||
| prefix := path.Join("/", username, params["reponame"]) | |||
| server := &webdav.Server{ | |||
| Fs: webdav.Dir(models.RepoPath(username, reponame)), | |||
| TrimPrefix: prefix, | |||
| Listings: true, | |||
| } | |||
| server.ServeHTTP(ctx.ResponseWriter, ctx.Req) | |||
| } | |||
| func Setting(ctx *middleware.Context, params martini.Params) { | |||
| if !ctx.Repo.IsOwner { | |||
| ctx.Handle(404, "repo.Setting", nil) | |||
| return | |||
| } | |||
| ctx.Data["IsRepoToolbarSetting"] = true | |||
| if ctx.Repo.Repository.IsBare { | |||
| ctx.Data["IsBareRepo"] = true | |||
| ctx.HTML(200, "repo/setting") | |||
| return | |||
| } | |||
| var title string | |||
| if t, ok := ctx.Data["Title"].(string); ok { | |||
| title = t | |||
| } | |||
| if len(params["branchname"]) == 0 { | |||
| params["branchname"] = "master" | |||
| } | |||
| ctx.Data["Branchname"] = params["branchname"] | |||
| ctx.Data["Title"] = title + " - settings" | |||
| ctx.HTML(200, "repo/setting") | |||
| } | |||
| func SettingPost(ctx *middleware.Context, params martini.Params) { | |||
| if !ctx.Repo.IsOwner { | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| switch ctx.Query("action") { | |||
| case "update": | |||
| ctx.Repo.Repository.Description = ctx.Query("desc") | |||
| ctx.Repo.Repository.Website = ctx.Query("site") | |||
| if err := models.UpdateRepository(ctx.Repo.Repository); err != nil { | |||
| ctx.Handle(404, "repo.SettingPost(update)", err) | |||
| return | |||
| } | |||
| ctx.Data["IsSuccess"] = true | |||
| ctx.HTML(200, "repo/setting") | |||
| log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName) | |||
| case "delete": | |||
| if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { | |||
| ctx.Data["ErrorMsg"] = "Please make sure you entered repository name is correct." | |||
| @@ -56,8 +304,68 @@ func SettingPost(ctx *middleware.Context) { | |||
| ctx.Handle(200, "repo.Delete", err) | |||
| return | |||
| } | |||
| log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName) | |||
| ctx.Redirect("/") | |||
| } | |||
| } | |||
| func Commits(ctx *middleware.Context, params martini.Params) { | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| ctx.Handle(200, "repo.Commits", err) | |||
| return | |||
| } else if len(brs) == 0 { | |||
| ctx.Handle(404, "repo.Commits", nil) | |||
| return | |||
| } | |||
| log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName) | |||
| ctx.Redirect("/", 302) | |||
| ctx.Data["IsRepoToolbarCommits"] = true | |||
| commits, err := models.GetCommits(params["username"], | |||
| params["reponame"], params["branchname"]) | |||
| if err != nil { | |||
| ctx.Handle(404, "repo.Commits", nil) | |||
| return | |||
| } | |||
| ctx.Data["Username"] = params["username"] | |||
| ctx.Data["Reponame"] = params["reponame"] | |||
| ctx.Data["CommitCount"] = commits.Len() | |||
| ctx.Data["Commits"] = commits | |||
| ctx.HTML(200, "repo/commits") | |||
| } | |||
| func Pulls(ctx *middleware.Context) { | |||
| ctx.Data["IsRepoToolbarPulls"] = true | |||
| ctx.HTML(200, "repo/pulls") | |||
| } | |||
| func Action(ctx *middleware.Context, params martini.Params) { | |||
| var err error | |||
| switch params["action"] { | |||
| case "watch": | |||
| err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) | |||
| case "unwatch": | |||
| err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) | |||
| case "desc": | |||
| if !ctx.Repo.IsOwner { | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| ctx.Repo.Repository.Description = ctx.Query("desc") | |||
| ctx.Repo.Repository.Website = ctx.Query("site") | |||
| err = models.UpdateRepository(ctx.Repo.Repository) | |||
| } | |||
| if err != nil { | |||
| log.Error("repo.Action(%s): %v", params["action"], err) | |||
| ctx.JSON(200, map[string]interface{}{ | |||
| "ok": false, | |||
| "err": err.Error(), | |||
| }) | |||
| return | |||
| } | |||
| ctx.JSON(200, map[string]interface{}{ | |||
| "ok": true, | |||
| }) | |||
| } | |||
| @@ -1,307 +0,0 @@ | |||
| // 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 repo | |||
| import ( | |||
| "path" | |||
| "strings" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/webdav" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| ) | |||
| func Branches(ctx *middleware.Context, params martini.Params) { | |||
| if !ctx.Repo.IsValid { | |||
| return | |||
| } | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| ctx.Handle(200, "repo.Branches", err) | |||
| return | |||
| } else if len(brs) == 0 { | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| ctx.Data["Username"] = params["username"] | |||
| ctx.Data["Reponame"] = params["reponame"] | |||
| ctx.Data["Branchname"] = brs[0] | |||
| ctx.Data["Branches"] = brs | |||
| ctx.Data["IsRepoToolbarBranches"] = true | |||
| ctx.HTML(200, "repo/branches") | |||
| } | |||
| func Single(ctx *middleware.Context, params martini.Params) { | |||
| if !ctx.Repo.IsValid { | |||
| return | |||
| } | |||
| if len(params["branchname"]) == 0 { | |||
| params["branchname"] = "master" | |||
| } | |||
| // Get tree path | |||
| treename := params["_1"] | |||
| if len(treename) > 0 && treename[len(treename)-1] == '/' { | |||
| ctx.Redirect("/"+ctx.Repo.Owner.LowerName+"/"+ | |||
| ctx.Repo.Repository.Name+"/src/"+params["branchname"]+"/"+treename[:len(treename)-1], 302) | |||
| return | |||
| } | |||
| ctx.Data["IsRepoToolbarSource"] = true | |||
| // Branches. | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| log.Error("repo.Single(GetBranches): %v", err) | |||
| ctx.Error(404) | |||
| return | |||
| } else if len(brs) == 0 { | |||
| ctx.Data["IsBareRepo"] = true | |||
| ctx.HTML(200, "repo/single") | |||
| return | |||
| } | |||
| ctx.Data["Branches"] = brs | |||
| repoFile, err := models.GetTargetFile(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"], treename) | |||
| if err != nil && err != models.ErrRepoFileNotExist { | |||
| log.Error("repo.Single(GetTargetFile): %v", err) | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] | |||
| if len(treename) != 0 && repoFile == nil { | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| if repoFile != nil && repoFile.IsFile() { | |||
| if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { | |||
| ctx.Data["FileIsLarge"] = true | |||
| } else if blob, err := repoFile.LookupBlob(); err != nil { | |||
| log.Error("repo.Single(repoFile.LookupBlob): %v", err) | |||
| ctx.Error(404) | |||
| } else { | |||
| ctx.Data["IsFile"] = true | |||
| ctx.Data["FileName"] = repoFile.Name | |||
| ext := path.Ext(repoFile.Name) | |||
| if len(ext) > 0 { | |||
| ext = ext[1:] | |||
| } | |||
| ctx.Data["FileExt"] = ext | |||
| readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) | |||
| ctx.Data["ReadmeExist"] = readmeExist | |||
| if readmeExist { | |||
| ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), "")) | |||
| } else { | |||
| ctx.Data["FileContent"] = string(blob.Contents()) | |||
| } | |||
| } | |||
| } else { | |||
| // Directory and file list. | |||
| files, err := models.GetReposFiles(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"], treename) | |||
| if err != nil { | |||
| log.Error("repo.Single(GetReposFiles): %v", err) | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| ctx.Data["Files"] = files | |||
| var readmeFile *models.RepoFile | |||
| for _, f := range files { | |||
| if !f.IsFile() || !base.IsReadmeFile(f.Name) { | |||
| continue | |||
| } else { | |||
| readmeFile = f | |||
| break | |||
| } | |||
| } | |||
| if readmeFile != nil { | |||
| ctx.Data["ReadmeExist"] = true | |||
| // if file large than 1M not show it | |||
| if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { | |||
| ctx.Data["FileIsLarge"] = true | |||
| } else if blob, err := readmeFile.LookupBlob(); err != nil { | |||
| log.Error("repo.Single(readmeFile.LookupBlob): %v", err) | |||
| ctx.Error(404) | |||
| return | |||
| } else { | |||
| // current repo branch link | |||
| ctx.Data["FileName"] = readmeFile.Name | |||
| ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) | |||
| } | |||
| } | |||
| } | |||
| ctx.Data["Username"] = params["username"] | |||
| ctx.Data["Reponame"] = params["reponame"] | |||
| ctx.Data["Branchname"] = params["branchname"] | |||
| var treenames []string | |||
| Paths := make([]string, 0) | |||
| if len(treename) > 0 { | |||
| treenames = strings.Split(treename, "/") | |||
| for i, _ := range treenames { | |||
| Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) | |||
| } | |||
| ctx.Data["HasParentPath"] = true | |||
| if len(Paths)-2 >= 0 { | |||
| ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2] | |||
| } | |||
| } | |||
| // Get latest commit according username and repo name | |||
| commit, err := models.GetCommit(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"]) | |||
| if err != nil { | |||
| log.Error("repo.Single(GetCommit): %v", err) | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| ctx.Data["LastCommit"] = commit | |||
| ctx.Data["Paths"] = Paths | |||
| ctx.Data["Treenames"] = treenames | |||
| ctx.Data["BranchLink"] = branchLink | |||
| ctx.HTML(200, "repo/single") | |||
| } | |||
| func Http(ctx *middleware.Context, params martini.Params) { | |||
| /*if !ctx.Repo.IsValid { | |||
| return | |||
| }*/ | |||
| // TODO: access check | |||
| username := params["username"] | |||
| reponame := params["reponame"] | |||
| if strings.HasSuffix(reponame, ".git") { | |||
| reponame = reponame[:len(reponame)-4] | |||
| } | |||
| prefix := path.Join("/", username, params["reponame"]) | |||
| server := &webdav.Server{ | |||
| Fs: webdav.Dir(models.RepoPath(username, reponame)), | |||
| TrimPrefix: prefix, | |||
| Listings: true, | |||
| } | |||
| server.ServeHTTP(ctx.ResponseWriter, ctx.Req) | |||
| } | |||
| func Setting(ctx *middleware.Context, params martini.Params) { | |||
| if !ctx.Repo.IsOwner { | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| ctx.Data["IsRepoToolbarSetting"] = true | |||
| // Branches. | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| log.Error("repo.Setting(GetBranches): %v", err) | |||
| ctx.Error(404) | |||
| return | |||
| } else if len(brs) == 0 { | |||
| ctx.Data["IsBareRepo"] = true | |||
| ctx.HTML(200, "repo/setting") | |||
| return | |||
| } | |||
| var title string | |||
| if t, ok := ctx.Data["Title"].(string); ok { | |||
| title = t | |||
| } | |||
| if len(params["branchname"]) == 0 { | |||
| params["branchname"] = "master" | |||
| } | |||
| ctx.Data["Branchname"] = params["branchname"] | |||
| ctx.Data["Title"] = title + " - settings" | |||
| ctx.HTML(200, "repo/setting") | |||
| } | |||
| func Commits(ctx *middleware.Context, params martini.Params) { | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| ctx.Handle(200, "repo.Commits", err) | |||
| return | |||
| } else if len(brs) == 0 { | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| ctx.Data["IsRepoToolbarCommits"] = true | |||
| commits, err := models.GetCommits(params["username"], | |||
| params["reponame"], params["branchname"]) | |||
| if err != nil { | |||
| ctx.Error(404) | |||
| return | |||
| } | |||
| ctx.Data["Username"] = params["username"] | |||
| ctx.Data["Reponame"] = params["reponame"] | |||
| ctx.Data["CommitCount"] = commits.Len() | |||
| ctx.Data["Commits"] = commits | |||
| ctx.HTML(200, "repo/commits") | |||
| } | |||
| func Issues(ctx *middleware.Context) { | |||
| ctx.Data["IsRepoToolbarIssues"] = true | |||
| ctx.HTML(200, "repo/issues") | |||
| } | |||
| func Pulls(ctx *middleware.Context) { | |||
| ctx.Data["IsRepoToolbarPulls"] = true | |||
| ctx.HTML(200, "repo/pulls") | |||
| } | |||
| func Action(ctx *middleware.Context, params martini.Params) { | |||
| var err error | |||
| switch params["action"] { | |||
| case "watch": | |||
| err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) | |||
| case "unwatch": | |||
| err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) | |||
| } | |||
| if err != nil { | |||
| log.Error("repo.Action(%s): %v", params["action"], err) | |||
| ctx.JSON(200, map[string]interface{}{ | |||
| "ok": false, | |||
| "err": err.Error(), | |||
| }) | |||
| return | |||
| } | |||
| ctx.JSON(200, map[string]interface{}{ | |||
| "ok": true, | |||
| }) | |||
| } | |||
| @@ -6,6 +6,7 @@ package user | |||
| import ( | |||
| "fmt" | |||
| "net/url" | |||
| "strings" | |||
| "github.com/codegangsta/martini" | |||
| @@ -77,7 +78,45 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||
| ctx.Data["Title"] = "Log In" | |||
| if ctx.Req.Method == "GET" { | |||
| ctx.HTML(200, "user/signin") | |||
| // Check auto-login. | |||
| userName := ctx.GetCookie(base.CookieUserName) | |||
| if len(userName) == 0 { | |||
| ctx.HTML(200, "user/signin") | |||
| return | |||
| } | |||
| isSucceed := false | |||
| defer func() { | |||
| if !isSucceed { | |||
| log.Trace("%s auto-login cookie cleared: %s", ctx.Req.RequestURI, userName) | |||
| ctx.SetCookie(base.CookieUserName, "", -1) | |||
| ctx.SetCookie(base.CookieRememberName, "", -1) | |||
| } | |||
| }() | |||
| user, err := models.GetUserByName(userName) | |||
| if err != nil { | |||
| ctx.HTML(200, "user/signin") | |||
| return | |||
| } | |||
| secret := base.EncodeMd5(user.Rands + user.Passwd) | |||
| value, _ := ctx.GetSecureCookie(secret, base.CookieRememberName) | |||
| if value != user.Name { | |||
| ctx.HTML(200, "user/signin") | |||
| return | |||
| } | |||
| isSucceed = true | |||
| ctx.Session.Set("userId", user.Id) | |||
| ctx.Session.Set("userName", user.Name) | |||
| redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")) | |||
| if len(redirectTo) > 0 { | |||
| ctx.SetCookie("redirect_to", "", -1) | |||
| ctx.Redirect(redirectTo) | |||
| } else { | |||
| ctx.Redirect("/") | |||
| } | |||
| return | |||
| } | |||
| @@ -88,7 +127,8 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||
| user, err := models.LoginUserPlain(form.UserName, form.Password) | |||
| if err != nil { | |||
| if err.Error() == models.ErrUserNotExist.Error() { | |||
| if err == models.ErrUserNotExist { | |||
| log.Trace("%s Log in failed: %s/%s", ctx.Req.RequestURI, form.UserName, form.Password) | |||
| ctx.RenderWithErr("Username or password is not correct", "user/signin", &form) | |||
| return | |||
| } | |||
| @@ -97,14 +137,29 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||
| return | |||
| } | |||
| if form.Remember == "on" { | |||
| secret := base.EncodeMd5(user.Rands + user.Passwd) | |||
| days := 86400 * base.LogInRememberDays | |||
| ctx.SetCookie(base.CookieUserName, user.Name, days) | |||
| ctx.SetSecureCookie(secret, base.CookieRememberName, user.Name, days) | |||
| } | |||
| ctx.Session.Set("userId", user.Id) | |||
| ctx.Session.Set("userName", user.Name) | |||
| ctx.Redirect("/") | |||
| redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")) | |||
| if len(redirectTo) > 0 { | |||
| ctx.SetCookie("redirect_to", "", -1) | |||
| ctx.Redirect(redirectTo) | |||
| } else { | |||
| ctx.Redirect("/") | |||
| } | |||
| } | |||
| func SignOut(ctx *middleware.Context) { | |||
| ctx.Session.Delete("userId") | |||
| ctx.Session.Delete("userName") | |||
| ctx.SetCookie(base.CookieUserName, "", -1) | |||
| ctx.SetCookie(base.CookieRememberName, "", -1) | |||
| ctx.Redirect("/") | |||
| } | |||
| @@ -246,7 +301,7 @@ func Activate(ctx *middleware.Context) { | |||
| if len(code) == 0 { | |||
| ctx.Data["IsActivatePage"] = true | |||
| if ctx.User.IsActive { | |||
| ctx.Error(404) | |||
| ctx.Handle(404, "user.Activate", nil) | |||
| return | |||
| } | |||
| // Resend confirmation e-mail. | |||
| @@ -274,7 +329,7 @@ func Activate(ctx *middleware.Context) { | |||
| ctx.Session.Set("userId", user.Id) | |||
| ctx.Session.Set("userName", user.Name) | |||
| ctx.Redirect("/", 302) | |||
| ctx.Redirect("/") | |||
| return | |||
| } | |||
| @@ -5,14 +5,19 @@ | |||
| package main | |||
| import ( | |||
| "bytes" | |||
| "container/list" | |||
| "fmt" | |||
| "io" | |||
| "os" | |||
| "os/exec" | |||
| "strconv" | |||
| "strings" | |||
| "github.com/codegangsta/cli" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| @@ -39,12 +44,27 @@ gogs serv provide access auth for repositories`, | |||
| Flags: []cli.Flag{}, | |||
| } | |||
| func parseCmd(cmd string) (string, string) { | |||
| ss := strings.SplitN(cmd, " ", 2) | |||
| if len(ss) != 2 { | |||
| return "", "" | |||
| } | |||
| verb, args := ss[0], ss[1] | |||
| if verb == "git" { | |||
| ss = strings.SplitN(args, " ", 2) | |||
| args = ss[1] | |||
| verb = fmt.Sprintf("%s %s", verb, ss[0]) | |||
| } | |||
| return verb, args | |||
| } | |||
| func In(b string, sl map[string]int) bool { | |||
| _, e := sl[b] | |||
| return e | |||
| } | |||
| func runServ(*cli.Context) { | |||
| func runServ(k *cli.Context) { | |||
| base.NewConfigContext() | |||
| models.LoadModelsConfig() | |||
| models.NewEngine() | |||
| @@ -84,15 +104,16 @@ func runServ(*cli.Context) { | |||
| repoName = repoName[:len(repoName)-4] | |||
| } | |||
| os.Setenv("userName", user.Name) | |||
| os.Setenv("userId", strconv.Itoa(int(user.Id))) | |||
| repo, err := models.GetRepositoryByName(user, repoName) | |||
| repo, err := models.GetRepositoryByName(user.Id, repoName) | |||
| var isExist bool = true | |||
| if err != nil { | |||
| println("Unavilable repository", err) | |||
| return | |||
| if err == models.ErrRepoNotExist { | |||
| isExist = false | |||
| } else { | |||
| println("Unavilable repository", err) | |||
| return | |||
| } | |||
| } | |||
| os.Setenv("repoId", strconv.Itoa(int(repo.Id))) | |||
| os.Setenv("repoName", repoName) | |||
| isWrite := In(verb, COMMANDS_WRITE) | |||
| isRead := In(verb, COMMANDS_READONLY) | |||
| @@ -130,12 +151,6 @@ func runServ(*cli.Context) { | |||
| return | |||
| } | |||
| isExist, err := models.IsRepositoryExist(user, repoName) | |||
| if err != nil { | |||
| println("Inernel error:", err.Error()) | |||
| return | |||
| } | |||
| if !isExist { | |||
| if isRead { | |||
| println("Repository", user.Name+"/"+repoName, "is not exist") | |||
| @@ -149,28 +164,127 @@ func runServ(*cli.Context) { | |||
| } | |||
| } | |||
| rep, err := git.OpenRepository(models.RepoPath(user.Name, repoName)) | |||
| if err != nil { | |||
| println(err.Error()) | |||
| return | |||
| } | |||
| refs, err := rep.AllReferencesMap() | |||
| if err != nil { | |||
| println(err.Error()) | |||
| return | |||
| } | |||
| gitcmd := exec.Command(verb, rRepo) | |||
| gitcmd.Dir = base.RepoRootPath | |||
| gitcmd.Stdout = os.Stdout | |||
| var s string | |||
| b := bytes.NewBufferString(s) | |||
| gitcmd.Stdout = io.MultiWriter(os.Stdout, b) | |||
| //gitcmd.Stdin = io.MultiReader(os.Stdin, b) | |||
| gitcmd.Stdin = os.Stdin | |||
| gitcmd.Stderr = os.Stderr | |||
| if err = gitcmd.Run(); err != nil { | |||
| println("execute command error:", err.Error()) | |||
| } | |||
| } | |||
| func parseCmd(cmd string) (string, string) { | |||
| ss := strings.SplitN(cmd, " ", 2) | |||
| if len(ss) != 2 { | |||
| return "", "" | |||
| if !strings.HasPrefix(cmd, "git-receive-pack") { | |||
| return | |||
| } | |||
| verb, args := ss[0], ss[1] | |||
| if verb == "git" { | |||
| ss = strings.SplitN(args, " ", 2) | |||
| args = ss[1] | |||
| verb = fmt.Sprintf("%s %s", verb, ss[0]) | |||
| // update | |||
| //w, _ := os.Create("serve.log") | |||
| //defer w.Close() | |||
| //log.SetOutput(w) | |||
| var t = "ok refs/heads/" | |||
| var i int | |||
| var refname string | |||
| for { | |||
| l, err := b.ReadString('\n') | |||
| if err != nil { | |||
| break | |||
| } | |||
| i = i + 1 | |||
| l = l[:len(l)-1] | |||
| idx := strings.Index(l, t) | |||
| if idx > 0 { | |||
| refname = l[idx+len(t):] | |||
| } | |||
| } | |||
| var ref *git.Reference | |||
| var ok bool | |||
| var l *list.List | |||
| //log.Info("----", refname, "-----") | |||
| if ref, ok = refs[refname]; !ok { | |||
| refs, err = rep.AllReferencesMap() | |||
| if err != nil { | |||
| println(err.Error()) | |||
| return | |||
| } | |||
| if ref, ok = refs[refname]; !ok { | |||
| println("unknow reference name -", refname, "-") | |||
| return | |||
| } | |||
| l, err = ref.AllCommits() | |||
| if err != nil { | |||
| println(err.Error()) | |||
| return | |||
| } | |||
| } else { | |||
| //log.Info("----", ref, "-----") | |||
| var last *git.Commit | |||
| //log.Info("00000", ref.Oid.String()) | |||
| last, err = ref.LastCommit() | |||
| if err != nil { | |||
| println(err.Error()) | |||
| return | |||
| } | |||
| ref2, err := rep.LookupReference(ref.Name) | |||
| if err != nil { | |||
| println(err.Error()) | |||
| return | |||
| } | |||
| //log.Info("11111", ref2.Oid.String()) | |||
| before, err := ref2.LastCommit() | |||
| if err != nil { | |||
| println(err.Error()) | |||
| return | |||
| } | |||
| //log.Info("----", before.Id(), "-----", last.Id()) | |||
| l = ref.CommitsBetween(before, last) | |||
| } | |||
| commits := make([][]string, 0) | |||
| var maxCommits = 3 | |||
| for e := l.Front(); e != nil; e = e.Next() { | |||
| commit := e.Value.(*git.Commit) | |||
| commits = append(commits, []string{commit.Id().String(), commit.Message()}) | |||
| if len(commits) >= maxCommits { | |||
| break | |||
| } | |||
| } | |||
| if err = models.CommitRepoAction(user.Id, user.Name, | |||
| repo.Id, repoName, refname, &base.PushCommits{l.Len(), commits}); err != nil { | |||
| log.Error("runUpdate.models.CommitRepoAction: %v", err, commits) | |||
| } else { | |||
| //log.Info("refname", refname) | |||
| //log.Info("Listen: %v", cmd) | |||
| //fmt.Println("...", cmd) | |||
| //runUpdate(k) | |||
| c := exec.Command("git", "update-server-info") | |||
| c.Dir = models.RepoPath(user.Name, repoName) | |||
| err := c.Run() | |||
| if err != nil { | |||
| log.Error("update-server-info: %v", err) | |||
| } | |||
| } | |||
| return verb, args | |||
| } | |||
| @@ -17,6 +17,7 @@ | |||
| <div><b>Run User:</b> {{.RunUser}}</div> | |||
| <div><b>Run Mode:</b> {{.RunMode}}</div> | |||
| <hr/> | |||
| <div><b>Enable HTTPS Clone</b> <i class="fa fa{{if .EnableHttpsClone}}-check{{end}}-square-o"></i></div> | |||
| <div><b>Repository Root Path:</b> {{.RepoRootPath}}</div> | |||
| </div> | |||
| </div> | |||
| @@ -45,6 +46,7 @@ | |||
| <div><b>Register Email Confirmation:</b> <i class="fa fa{{if .Service.RegisterEmailConfirm}}-check{{end}}-square-o"></i></div> | |||
| <div><b>Disenable Registeration:</b> <i class="fa fa{{if .Service.DisenableRegisteration}}-check{{end}}-square-o"></i></div> | |||
| <div><b>Require Sign In View:</b> <i class="fa fa{{if .Service.RequireSignInView}}-check{{end}}-square-o"></i></div> | |||
| <div><b>Enable Cache Avatar:</b> <i class="fa fa{{if .Service.EnableCacheAvatar}}-check{{end}}-square-o"></i></div> | |||
| <hr/> | |||
| <div><b>Active Code Lives:</b> {{.Service.ActiveCodeLives}} minutes</div> | |||
| <div><b>Reset Password Code Lives:</b> {{.Service.ResetPwdCodeLives}} minutes</div> | |||
| @@ -76,6 +78,36 @@ | |||
| </div> | |||
| </div> | |||
| <div class="panel panel-default"> | |||
| <div class="panel-heading"> | |||
| Session Configuration | |||
| </div> | |||
| <div class="panel-body"> | |||
| <div><b>Session Provider:</b> {{.SessionProvider}}</div> | |||
| <div><b>Cookie Name:</b> {{.SessionConfig.CookieName}}</div> | |||
| <div><b>Enable Set Cookie:</b> <i class="fa fa{{if .SessionConfig.EnableSetCookie}}-check{{end}}-square-o"></i></div> | |||
| <div><b>GC Interval Time:</b> {{.SessionConfig.GcIntervalTime}} seconds</div> | |||
| <div><b>Session Life Time:</b> {{.SessionConfig.SessionLifeTime}} seconds</div> | |||
| <div><b>HTTPS Only:</b> <i class="fa fa{{if .SessionConfig.CookieSecure}}-check{{end}}-square-o"></i></div> | |||
| <div><b>Cookie Life Time:</b> {{.SessionConfig.CookieLifeTime}} seconds</div> | |||
| <div><b>Session ID Hash Function:</b> {{.SessionConfig.SessionIDHashFunc}}</div> | |||
| <div><b>Session ID Hash Key:</b> {{.SessionConfig.SessionIDHashKey}}</div> | |||
| <div><b>Provider Config:</b> {{.SessionConfig.ProviderConfig}}</div> | |||
| </div> | |||
| </div> | |||
| <div class="panel panel-default"> | |||
| <div class="panel-heading"> | |||
| Picture Configuration | |||
| </div> | |||
| <div class="panel-body"> | |||
| <div><b>Picture Service:</b> {{.PictureService}}</div> | |||
| <div><b>Picture Root Path:</b> {{.PictureRootPath}}</div> | |||
| </div> | |||
| </div> | |||
| <div class="panel panel-default"> | |||
| <div class="panel-heading"> | |||
| Log Configuration | |||
| @@ -15,10 +15,42 @@ | |||
| <div class="panel panel-default"> | |||
| <div class="panel-heading"> | |||
| System Status | |||
| System Monitor Status | |||
| </div> | |||
| <div class="panel-body"> | |||
| <div>Server Uptime: <b>{{.SysStatus.Uptime}}</b></div> | |||
| <div>Current Goroutines: <b>{{.SysStatus.NumGoroutine}}</b></div> | |||
| <hr/> | |||
| <div>Current Memory Usage: <b>{{.SysStatus.MemAllocated}}</b></div> | |||
| <div>Total Memory Allocated: <b>{{.SysStatus.MemTotal}}</b></div> | |||
| <div>Memory Obtained: <b>{{.SysStatus.MemSys}}</b></div> | |||
| <div>Pointer Lookup Times: <b>{{.SysStatus.Lookups}}</b></div> | |||
| <div>Memory Allocate Times: <b>{{.SysStatus.MemMallocs}}</b></div> | |||
| <div>Memory Free Times: <b>{{.SysStatus.MemFrees}}</b></div> | |||
| <hr/> | |||
| <div>Current Heap Usage: <b>{{.SysStatus.HeapAlloc}}</b></div> | |||
| <div>Heap Memory Obtained: <b>{{.SysStatus.HeapSys}}</b></div> | |||
| <div>Heap Memory Idle: <b>{{.SysStatus.HeapIdle}}</b></div> | |||
| <div>Heap Memory In Use: <b>{{.SysStatus.HeapInuse}}</b></div> | |||
| <div>Heap Memory Released: <b>{{.SysStatus.HeapReleased}}</b></div> | |||
| <div>Heap Objects: <b>{{.SysStatus.HeapObjects}}</b></div> | |||
| <hr/> | |||
| <div>Bootstrap Stack Usage: <b>{{.SysStatus.StackInuse}}</b></div> | |||
| <div>Stack Memory Obtained: <b>{{.SysStatus.StackSys}}</b></div> | |||
| <div>MSpan Structures Usage: <b>{{.SysStatus.MSpanInuse}}</b></div> | |||
| <div>MSpan Structures Obtained: <b>{{.SysStatus.HeapSys}}</b></div> | |||
| <div>MCache Structures Usage: <b>{{.SysStatus.MCacheInuse}}</b></div> | |||
| <div>MCache Structures Obtained: <b>{{.SysStatus.MCacheSys}}</b></div> | |||
| <div>Profiling Bucket Hash Table Obtained: <b>{{.SysStatus.BuckHashSys}}</b></div> | |||
| <div>GC Metadada Obtained: <b>{{.SysStatus.GCSys}}</b></div> | |||
| <div>Other System Allocation Obtained: <b>{{.SysStatus.OtherSys}}</b></div> | |||
| <hr/> | |||
| <div>Next GC Recycle: <b>{{.SysStatus.NextGC}}</b></div> | |||
| <div>Last GC Time: <b>{{.SysStatus.LastGC}} ago</b></div> | |||
| <div>Total GC Pause: <b>{{.SysStatus.PauseTotalNs}}</b></div> | |||
| <div>Last GC Pause: <b>{{.SysStatus.PauseNs}}</b></div> | |||
| <div>GC Times: <b>{{.SysStatus.NumGC}}</b></div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -27,7 +27,7 @@ | |||
| <td>{{.Id}}</td> | |||
| <th>{{.UserName}}</th> | |||
| <td><a href="/{{.UserName}}/{{.Name}}">{{.Name}}</a></td> | |||
| <td><i class="fa fa{{if .Private}}-check{{end}}-square-o"></i></td> | |||
| <td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td> | |||
| <td>{{.NumWatches}}</td> | |||
| <td>{{.NumForks}}</td> | |||
| <td>{{DateFormat .Created "M d, Y"}}</td> | |||
| @@ -12,6 +12,7 @@ | |||
| <br/> | |||
| <form action="/admin/users/{{.User.Id}}" method="post" class="form-horizontal"> | |||
| {{if .IsSuccess}}<p class="alert alert-success">Account profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | |||
| {{.CsrfTokenHtml}} | |||
| <input type="hidden" value="{{.User.Id}}" name="userId"/> | |||
| <div class="form-group"> | |||
| <label class="col-md-3 control-label">Username: </label> | |||
| @@ -71,7 +72,7 @@ | |||
| <div class="form-group"> | |||
| <div class="col-md-offset-3 col-md-6"> | |||
| <button type="submit" class="btn btn-lg btn-primary btn-block">Update account profile</button> | |||
| <!-- <a type="button" href="/admin/users/{{.User.Id}}/delete" class="btn btn-lg btn-danger btn-block">Delete this account</a> --> | |||
| <a type="button" href="/admin/users/{{.User.Id}}/delete" class="btn btn-lg btn-danger btn-block">Delete this account</a> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| @@ -11,6 +11,7 @@ | |||
| <div class="panel-body"> | |||
| <br/> | |||
| <form action="/admin/users/new" method="post" class="form-horizontal"> | |||
| {{.CsrfTokenHtml}} | |||
| <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | |||
| <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | |||
| <label class="col-md-3 control-label">Username: </label> | |||
| @@ -8,6 +8,7 @@ | |||
| <meta name="author" content="Gogs - Go Git Service" /> | |||
| <meta name="description" content="Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language" /> | |||
| <meta name="keywords" content="go, git"> | |||
| <meta name="_csrf" content="{{.CsrfToken}}" /> | |||
| <!-- Stylesheets --> | |||
| <link href="/css/bootstrap.min.css" rel="stylesheet" /> | |||
| @@ -2,6 +2,7 @@ | |||
| {{template "base/navbar" .}} | |||
| <div class="container" id="gogs-body"> | |||
| <form action="/repo/create" method="post" class="form-horizontal gogs-card" id="gogs-repo-create"> | |||
| {{.CsrfTokenHtml}} | |||
| <h3>Create New Repository</h3> | |||
| <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | |||
| <div class="form-group"> | |||
| @@ -1,11 +1,11 @@ | |||
| <div id="gogs-body-nav" class="gogs-repo-nav"> | |||
| <div class="container"> | |||
| <div class="row"> | |||
| <div class="col-md-6"> | |||
| <div class="col-md-7"> | |||
| <h3 class="name"><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3> | |||
| <p class="desc">{{.Repository.Description}}{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p> | |||
| <p class="desc">{{.Repository.Description}}{{if .Repository.Website}} <a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p> | |||
| </div> | |||
| <div class="col-md-6 actions text-right clone-group-btn"> | |||
| <div class="col-md-5 actions text-right clone-group-btn"> | |||
| {{if not .IsBareRepo}} | |||
| <!--<div class="btn-group" id="gogs-repo-clone"> | |||
| <button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button> | |||
| @@ -18,7 +18,7 @@ | |||
| <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> | |||
| <span class="caret"></span> | |||
| </button> | |||
| <div class="dropdown-menu clone-group-btn dropdown-menu-right"> | |||
| <div class="dropdown-menu clone-group-btn dropdown-menu-right no-propagation"> | |||
| <div class="input-group"> | |||
| <span class="input-group-btn"> | |||
| <button class="btn btn-default" data-link="{{.CloneLink.SSH}}" type="button">SSH</button> | |||
| @@ -32,7 +32,7 @@ | |||
| <p class="help-block text-center">Need help cloning? Visit <a href="#">Help</a>!</p> | |||
| </div> | |||
| </div> | |||
| <div class="btn-group {{if .IsRepositoryWatching}}watching{{else}}no-watching{{end}}" id="gogs-repo-watching" data-watch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/watch" data-unwatch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/unwatch"> | |||
| <div class="btn-group {{if .IsRepositoryWatching}}watching{{else}}no-watching{{end}}" id="gogs-repo-watching" data-watch="/{{.Owner.Name}}/{{.Repository.Name}}/action/watch" data-unwatch="/{{.Owner.Name}}/{{.Repository.Name}}/action/unwatch"> | |||
| {{if .IsRepositoryWatching}} | |||
| <button type="button" class="btn btn-default"><i class="fa fa-eye fa-lg fa-m"></i></button> | |||
| {{else}} | |||
| @@ -12,14 +12,42 @@ | |||
| </div> | |||
| <div id="gogs-repo-setting-container" class="col-md-9"> | |||
| {{if .ErrorMsg}}<p class="alert alert-danger">{{.ErrorMsg}}</p>{{end}} | |||
| {{if .IsSuccess}}<p class="alert alert-success">Repository option has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | |||
| <div class="panel panel-default"> | |||
| <div class="panel-heading"> | |||
| Repository Options | |||
| </div> | |||
| <div class="panel-body"> | |||
| <form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="form-horizontal"> | |||
| {{.CsrfTokenHtml}} | |||
| <input type="hidden" name="action" value="update"> | |||
| <div class="form-group"> | |||
| <label class="col-md-3 text-right">Description</label> | |||
| <div class="col-md-9"> | |||
| <textarea class="form-control" name="desc" id="repo-desc" rows="3">{{.Repository.Description}}</textarea> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label class="col-md-3 text-right">Official Site</label> | |||
| <div class="col-md-9"> | |||
| <input type="url" class="form-control" name="site" value="{{.Repository.Website}}" /> | |||
| </div> | |||
| </div> | |||
| <!-- <div class="form-group"> | |||
| <label class="col-md-3 text-right">Default Branch</label> | |||
| <div class="col-md-9"> | |||
| <select name="branch" id="repo-default-branch" class="form-control"> | |||
| <option value="">Branch</option> | |||
| </select> | |||
| </div> | |||
| </div> --> | |||
| <div class="form-group"> | |||
| <div class="col-md-9 col-md-offset-3"> | |||
| <button class="btn btn-primary" type="submit">Save Options</button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| @@ -40,6 +68,7 @@ | |||
| <div class="modal fade" id="delete-repository-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |||
| <div class="modal-dialog"> | |||
| <form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="modal-content"> | |||
| {{.CsrfTokenHtml}} | |||
| <input type="hidden" name="action" value="delete"> | |||
| <div class="modal-header"> | |||
| @@ -16,12 +16,12 @@ | |||
| {{.FileContent|str2html}} | |||
| </div> | |||
| {{else}} | |||
| <div class="panel-body file-body file-code"> | |||
| <div class="panel-body file-body file-code code-view"> | |||
| <table> | |||
| <tbody> | |||
| <tr> | |||
| <td class="lines-num"></td> | |||
| <td class="lines-code markdown"><pre class="linenums lang-{{.FileExt}}"><code>{{.FileContent}}</code></pre></td> | |||
| <td class="lines-code markdown"><pre class="prettyprint linenums lang-{{.FileExt}}">{{.FileContent}}</pre></td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| @@ -0,0 +1,9 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div id="gogs-body" class="container text-center"> | |||
| <p style="margin-top: 80px"><img src="/img/404.png" alt="404"/></p> | |||
| <hr/> | |||
| <p>Application Version: {{AppVer}}</p> | |||
| <p>If you think it is an error, please open an issue on <a href="https://github.com/gogits/gogs/issues/new">GitHub</a>.</p> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -0,0 +1,10 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div id="gogs-body" class="container text-center"> | |||
| <p style="margin-top: 80px"><img src="/img/500.png" alt="404"/></p> | |||
| <hr/> | |||
| <p>An error is occurred : {{.ErrorMsg}}</p> | |||
| <hr/> | |||
| <p>Application Version: {{AppVer}}</p> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -1,7 +1,8 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div id="gogs-body" class="container"> | |||
| <form action="/user/activate" method="get" class="form-horizontal gogs-card" id="gogs-login-card"> | |||
| <form action="/user/activate" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | |||
| {{.CsrfTokenHtml}} | |||
| <h3>Activate Your Account</h3> | |||
| {{if .IsActivatePage}} | |||
| {{if .ServiceNotEnabled}} | |||
| @@ -22,6 +22,7 @@ | |||
| <div class="modal fade" id="delete-account-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |||
| <div class="modal-dialog"> | |||
| <form action="/user/delete" method="post" class="modal-content" id="gogs-user-delete"> | |||
| {{.CsrfTokenHtml}} | |||
| <div class="modal-header"> | |||
| <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | |||
| <h4 class="modal-title" id="myModalLabel">Delete Account</h4> | |||
| @@ -5,7 +5,9 @@ | |||
| <div id="gogs-user-setting-container" class="col-md-9"> | |||
| <div id="gogs-setting-pwd"> | |||
| <h4>Password</h4> | |||
| <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password">{{if .IsSuccess}} | |||
| <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password"> | |||
| {{.CsrfTokenHtml}} | |||
| {{if .IsSuccess}} | |||
| <p class="alert alert-success">Password is changed successfully. You can now sign in via new password.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | |||
| <div class="form-group"> | |||
| <label class="col-md-3 control-label">Old Password<strong class="text-danger">*</strong></label> | |||
| @@ -22,6 +22,7 @@ | |||
| <div class="modal fade" id="ssh-add-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |||
| <div class="modal-dialog"> | |||
| <form class="modal-content form-horizontal" id="gogs-ssh-form" method="post" action="/user/setting/ssh/"> | |||
| {{.CsrfTokenHtml}} | |||
| <div class="modal-header"> | |||
| <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | |||
| <h4 class="modal-title" id="myModalLabel">Add SSH Key</h4> | |||
| @@ -6,6 +6,7 @@ | |||
| <div id="gogs-setting-pwd"> | |||
| <h4>Account Profile</h4> | |||
| <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting"> | |||
| {{.CsrfTokenHtml}} | |||
| {{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | |||
| <p>Your Email will be public and used for Account related notifications and any web based operations made via the web.</p> | |||
| <div class="form-group"> | |||
| @@ -2,6 +2,7 @@ | |||
| {{template "base/navbar" .}} | |||
| <div class="container" id="gogs-body" data-page="user-signin"> | |||
| <form action="/user/login" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | |||
| {{.CsrfTokenHtml}} | |||
| <h3>Log in</h3> | |||
| <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | |||
| <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | |||
| @@ -18,6 +19,17 @@ | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <div class="col-md-6 col-md-offset-4"> | |||
| <div class="checkbox"> | |||
| <label> | |||
| <input type="checkbox" name="remember" {{if .remember}}checked{{end}}> | |||
| <strong>Remember me</strong> | |||
| </label> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <div class="col-md-offset-4 col-md-6"> | |||
| <button type="submit" class="btn btn-lg btn-primary">Log In</button> | |||
| @@ -2,6 +2,7 @@ | |||
| {{template "base/navbar" .}} | |||
| <div class="container" id="gogs-body" data-page="user-signup"> | |||
| <form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | |||
| {{.CsrfTokenHtml}} | |||
| {{if .DisenableRegisteration}} | |||
| Sorry, registeration has been disenabled, you can only get account from administrator. | |||
| {{else}} | |||
| @@ -4,16 +4,9 @@ | |||
| package main | |||
| import ( | |||
| "os" | |||
| "strconv" | |||
| import "github.com/codegangsta/cli" | |||
| "github.com/codegangsta/cli" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| //"github.com/gogits/gogs/modules/log" | |||
| var CmdUpdate = cli.Command{ | |||
| Name: "update", | |||
| @@ -26,6 +19,9 @@ gogs serv provide access auth for repositories`, | |||
| // for command: ./gogs update | |||
| func runUpdate(*cli.Context) { | |||
| /*w, _ := os.Create("update.log") | |||
| log.SetOutput(w) | |||
| userName := os.Getenv("userName") | |||
| userId := os.Getenv("userId") | |||
| repoId := os.Getenv("repoId") | |||
| @@ -35,16 +31,19 @@ func runUpdate(*cli.Context) { | |||
| repo, err := git.OpenRepository(f) | |||
| if err != nil { | |||
| log.Error("runUpdate.Open repoId: %v", err) | |||
| return | |||
| } | |||
| ref, err := repo.LookupReference("HEAD") | |||
| if err != nil { | |||
| log.Error("runUpdate.Ref repoId: %v", err) | |||
| return | |||
| } | |||
| lastCommit, err := repo.LookupCommit(ref.Oid) | |||
| if err != nil { | |||
| log.Error("runUpdate.Commit repoId: %v", err) | |||
| return | |||
| } | |||
| @@ -63,5 +62,8 @@ func runUpdate(*cli.Context) { | |||
| if err = models.CommitRepoAction(int64(sUserId), userName, | |||
| int64(sRepoId), repoName, commits); err != nil { | |||
| log.Error("runUpdate.models.CommitRepoAction: %v", err) | |||
| } | |||
| } else { | |||
| l := exec.Command("exec", "git", "update-server-info") | |||
| l.Run() | |||
| }*/ | |||
| } | |||
| @@ -12,7 +12,6 @@ import ( | |||
| "github.com/codegangsta/cli" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/martini-contrib/sessions" | |||
| "github.com/gogits/binding" | |||
| @@ -82,72 +81,95 @@ func runWeb(*cli.Context) { | |||
| // Middlewares. | |||
| m.Use(middleware.Renderer(middleware.RenderOptions{Funcs: []template.FuncMap{base.TemplateFuncs}})) | |||
| // TODO: should use other store because cookie store is not secure. | |||
| store := sessions.NewCookieStore([]byte("secret123")) | |||
| m.Use(sessions.Sessions("my_session", store)) | |||
| m.Use(middleware.InitContext()) | |||
| reqSignIn := middleware.SignInRequire(true) | |||
| ignSignIn := middleware.SignInRequire(base.Service.RequireSignInView) | |||
| reqSignOut := middleware.SignOutRequire() | |||
| reqSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true}) | |||
| ignSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: base.Service.RequireSignInView}) | |||
| reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true}) | |||
| // Routers. | |||
| m.Get("/", ignSignIn, routers.Home) | |||
| m.Get("/issues", reqSignIn, user.Issues) | |||
| m.Get("/pulls", reqSignIn, user.Pulls) | |||
| m.Get("/stars", reqSignIn, user.Stars) | |||
| m.Any("/user/login", reqSignOut, binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | |||
| m.Any("/user/logout", reqSignIn, user.SignOut) | |||
| m.Any("/user/sign_up", reqSignOut, binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | |||
| m.Any("/user/delete", reqSignIn, user.Delete) | |||
| m.Get("/user/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | |||
| m.Get("/user/activate", user.Activate) | |||
| m.Any("/user/setting", reqSignIn, binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | |||
| m.Any("/user/setting/password", reqSignIn, binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | |||
| m.Any("/user/setting/ssh", reqSignIn, binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | |||
| m.Any("/user/setting/notification", reqSignIn, user.SettingNotification) | |||
| m.Any("/user/setting/security", reqSignIn, user.SettingSecurity) | |||
| m.Get("/help", routers.Help) | |||
| avatarCache := avatar.HttpHandler("public/img/avatar/", "public/img/avatar/default.jpg") | |||
| m.Get("/avatar/:hash", avatarCache.ServeHTTP) | |||
| m.Group("/user", func(r martini.Router) { | |||
| r.Any("/login", binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | |||
| r.Any("/sign_up", binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | |||
| }, reqSignOut) | |||
| m.Group("/user", func(r martini.Router) { | |||
| r.Any("/logout", user.SignOut) | |||
| r.Any("/delete", user.Delete) | |||
| r.Any("/setting", binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | |||
| }, reqSignIn) | |||
| m.Group("/user", func(r martini.Router) { | |||
| r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | |||
| r.Get("/activate", user.Activate) | |||
| }) | |||
| m.Group("/user/setting", func(r martini.Router) { | |||
| r.Any("/password", binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | |||
| r.Any("/ssh", binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | |||
| r.Any("/notification", user.SettingNotification) | |||
| r.Any("/security", user.SettingSecurity) | |||
| }, reqSignIn) | |||
| m.Get("/user/:username", ignSignIn, user.Profile) | |||
| m.Any("/repo/create", reqSignIn, binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | |||
| m.Get("/help", routers.Help) | |||
| avatarHandler := avatar.HttpHandler("public/img/avatar", "public/img/avatar/default.jpg") | |||
| m.Get("/avatar/:hash", avatarHandler.ServeHTTP) | |||
| adminReq := middleware.AdminRequire() | |||
| m.Get("/admin", reqSignIn, adminReq, admin.Dashboard) | |||
| m.Get("/admin/users", reqSignIn, adminReq, admin.Users) | |||
| m.Any("/admin/users/new", reqSignIn, adminReq, binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser) | |||
| m.Any("/admin/users/:userid", reqSignIn, adminReq, binding.BindIgnErr(auth.AdminEditUserForm{}), admin.EditUser) | |||
| m.Get("/admin/repos", reqSignIn, adminReq, admin.Repositories) | |||
| m.Get("/admin/config", reqSignIn, adminReq, admin.Config) | |||
| m.Post("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.SettingPost) | |||
| m.Get("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.Setting) | |||
| m.Get("/:username/:reponame/commits/:branchname", ignSignIn, middleware.RepoAssignment(true), repo.Commits) | |||
| m.Get("/:username/:reponame/issues", ignSignIn, middleware.RepoAssignment(true), repo.Issues) | |||
| m.Get("/:username/:reponame/pulls", ignSignIn, middleware.RepoAssignment(true), repo.Pulls) | |||
| m.Get("/:username/:reponame/branches", ignSignIn, middleware.RepoAssignment(true), repo.Branches) | |||
| m.Get("/:username/:reponame/action/:action", reqSignIn, middleware.RepoAssignment(true), repo.Action) | |||
| m.Get("/:username/:reponame/src/:branchname/**", | |||
| ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame/src/:branchname", | |||
| ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| m.Any("/:username/:reponame/**", ignSignIn, repo.Http) | |||
| adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) | |||
| m.Get("/admin", adminReq, admin.Dashboard) | |||
| m.Group("/admin", func(r martini.Router) { | |||
| r.Get("/users", admin.Users) | |||
| r.Get("/repos", admin.Repositories) | |||
| r.Get("/config", admin.Config) | |||
| }, adminReq) | |||
| m.Group("/admin/users", func(r martini.Router) { | |||
| r.Any("/new", binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser) | |||
| r.Any("/:userid", binding.BindIgnErr(auth.AdminEditUserForm{}), admin.EditUser) | |||
| r.Any("/:userid/delete", admin.DeleteUser) | |||
| }, adminReq) | |||
| m.Group("/:username/:reponame", func(r martini.Router) { | |||
| r.Post("/settings", repo.SettingPost) | |||
| r.Get("/settings", repo.Setting) | |||
| r.Get("/action/:action", repo.Action) | |||
| }, reqSignIn, middleware.RepoAssignment(true)) | |||
| m.Group("/:username/:reponame", func(r martini.Router) { | |||
| r.Get("/commits/:branchname", repo.Commits) | |||
| r.Get("/issues", repo.Issues) | |||
| r.Any("/issues/new", binding.BindIgnErr(auth.CreateIssueForm{}), repo.CreateIssue) | |||
| r.Get("/issues/:issueid", repo.ViewIssue) | |||
| r.Get("/pulls", repo.Pulls) | |||
| r.Get("/branches", repo.Branches) | |||
| r.Get("/src/:branchname", repo.Single) | |||
| r.Get("/src/:branchname/**", repo.Single) | |||
| r.Get("/commits/:branchname", repo.Commits) | |||
| r.Get("/commits/:branchname", repo.Commits) | |||
| }, ignSignIn, middleware.RepoAssignment(true)) | |||
| // TODO: implement single commit page | |||
| // m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| // m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| m.Group("/:username", func(r martini.Router) { | |||
| r.Get("/:reponame", middleware.RepoAssignment(true), repo.Single) | |||
| r.Any("/:reponame/**", repo.Http) | |||
| }, ignSignIn) | |||
| if martini.Env == martini.Dev { | |||
| m.Get("/template/**", dev.TemplatePreview) | |||
| } | |||
| // Not found handler. | |||
| m.NotFound(routers.NotFound) | |||
| listenAddr := fmt.Sprintf("%s:%s", | |||
| base.Cfg.MustValue("server", "HTTP_ADDR"), | |||
| base.Cfg.MustValue("server", "HTTP_PORT", "3000")) | |||