1
0
mirror of https://github.com/go-gitea/gitea.git synced 2024-11-04 08:17:24 -05:00

Add flash

This commit is contained in:
Unknown 2014-04-10 16:36:50 -04:00
parent 459223cf01
commit 45462662e9
17 changed files with 179 additions and 129 deletions

View File

@ -19,7 +19,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.2.3.0410 Alpha"
const APP_VER = "0.2.4.0410 Alpha"
func init() {
base.AppVer = APP_VER

View File

@ -14,6 +14,8 @@ import (
"path"
"strings"
"github.com/Unknwon/com"
"github.com/gogits/git"
"github.com/gogits/gogs/modules/base"
@ -163,13 +165,11 @@ func getReposFiles(userName, repoName, commitId string, rpath string) ([]*RepoFi
return 0
}
cmd := exec.Command("git", "log", "-1", "--pretty=format:%H", commitId, "--", path.Join(dirname, entry.Name))
cmd.Dir = repopath
out, err := cmd.Output()
stdout, _, err := com.ExecCmdDir(repopath, "git", "log", "-1", "--pretty=format:%H", commitId, "--", path.Join(dirname, entry.Name))
if err != nil {
return 0
}
filecm, err := repo.GetCommit(string(out))
filecm, err := repo.GetCommit(string(stdout))
if err != nil {
return 0
}

View File

@ -21,6 +21,7 @@ func init() {
func NewLogger(bufLen int64, mode, config string) {
Mode, Config = mode, config
logger = logs.NewLogger(bufLen)
logger.SetLogFuncCallDepth(3)
logger.SetLogger(mode, config)
}

View File

@ -91,10 +91,11 @@ func (ctx *Context) HTML(status int, name string, htmlOpt ...HTMLOptions) {
// RenderWithErr used for page has form validation but need to prompt error to users.
func (ctx *Context) RenderWithErr(msg, tpl string, form auth.Form) {
ctx.Flash.Error(msg)
if form != nil {
auth.AssignForm(form, ctx.Data)
}
ctx.Flash.ErrorMsg = msg
ctx.Data["Flash"] = ctx.Flash
ctx.HTML(200, tpl)
}
@ -274,22 +275,25 @@ func InitContext() martini.Handler {
// start session
ctx.Session = base.SessionManager.SessionStart(res, r)
ctx.Flash = &Flash{}
// Get flash.
values, err := url.ParseQuery(ctx.GetCookie("gogs_flash"))
if err != nil {
log.Error("InitContext.ParseQuery(flash): %v", err)
} else {
ctx.Flash.Values = values
} else if len(values) > 0 {
ctx.Flash = &Flash{Values: values}
ctx.Flash.ErrorMsg = ctx.Flash.Get("error")
ctx.Flash.SuccessMsg = ctx.Flash.Get("success")
ctx.Data["Flash"] = ctx.Flash
ctx.SetCookie("gogs_flash", "", -1)
}
ctx.Flash = &Flash{Values: url.Values{}}
rw := res.(martini.ResponseWriter)
rw.Before(func(martini.ResponseWriter) {
ctx.Session.SessionRelease(res)
if flash := ctx.Flash.Encode(); len(flash) > 0 {
ctx.SetCookie("gogs_flash", ctx.Flash.Encode(), -1)
ctx.SetCookie("gogs_flash", ctx.Flash.Encode(), 0)
}
})

View File

@ -23,10 +23,6 @@ import (
"github.com/gogits/gogs/modules/middleware"
)
type installRouter int
var InstallRouter installRouter = 1
// Check run mode(Default of martini is Dev).
func checkRunMode() {
switch base.Cfg.MustValue("", "RUN_MODE") {
@ -58,7 +54,7 @@ func GlobalInit() {
checkRunMode()
}
func (r installRouter) Get(ctx *middleware.Context, form auth.InstallForm) {
func Install(ctx *middleware.Context, form auth.InstallForm) {
if base.InstallLock {
ctx.Handle(404, "install.Install", errors.New("Installation is prohibited"))
return
@ -101,7 +97,7 @@ func (r installRouter) Get(ctx *middleware.Context, form auth.InstallForm) {
ctx.HTML(200, "install")
}
func (r installRouter) Post(ctx *middleware.Context, form auth.InstallForm) {
func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
if base.InstallLock {
ctx.Handle(404, "install.Install", errors.New("Installation is prohibited"))
return

View File

@ -14,8 +14,16 @@ import (
"github.com/gogits/gogs/modules/middleware"
)
func Setting(ctx *middleware.Context) {
ctx.Data["Title"] = "Setting"
ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSetting"] = true
ctx.Data["Owner"] = ctx.User
ctx.HTML(200, "user/setting")
}
// Render user setting page (email, website modify)
func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) {
func SettingPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
ctx.Data["Title"] = "Setting"
ctx.Data["PageIsUserSetting"] = true // For navbar arrow.
ctx.Data["IsUserPageSetting"] = true // For setting nav highlight.
@ -23,7 +31,7 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) {
user := ctx.User
ctx.Data["Owner"] = user
if ctx.Req.Method == "GET" || ctx.HasError() {
if ctx.HasError() {
ctx.HTML(200, "user/setting")
return
}
@ -32,13 +40,13 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) {
if user.Name != form.UserName {
isExist, err := models.IsUserExist(form.UserName)
if err != nil {
ctx.Handle(404, "user.Setting(update: check existence)", err)
ctx.Handle(500, "user.Setting(update: check existence)", err)
return
} else if isExist {
ctx.RenderWithErr("User name has been taken.", "user/setting", &form)
return
} else if err = models.ChangeUserName(user, form.UserName); err != nil {
ctx.Handle(404, "user.Setting(change user name)", err)
ctx.Handle(500, "user.Setting(change user name)", err)
return
}
log.Trace("%s User name changed: %s -> %s", ctx.Req.RequestURI, user.Name, form.UserName)
@ -52,13 +60,13 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) {
user.Avatar = base.EncodeMd5(form.Avatar)
user.AvatarEmail = form.Avatar
if err := models.UpdateUser(user); err != nil {
ctx.Handle(200, "setting.Setting", err)
ctx.Handle(500, "setting.Setting", err)
return
}
ctx.Data["IsSuccess"] = true
ctx.HTML(200, "user/setting")
log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName)
ctx.Flash.Success("Your profile has been successfully updated.")
ctx.Redirect("/user/setting")
}
func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) {

View File

@ -93,11 +93,10 @@ func SocialSignIn(ctx *middleware.Context, tokens oauth2.Tokens) {
log.Info("login soc id: %v", socid)
return
}
config := &oauth.Config{
//ClientId: base.OauthService.Github.ClientId,
//ClientSecret: base.OauthService.Github.ClientSecret, // FIXME: I don't know why compile error here
ClientId: "09383403ff2dc16daaa1",
ClientSecret: "0e4aa0c3630df396cdcea01a9d45cacf79925fea",
ClientId: base.OauthService.GitHub.ClientId,
ClientSecret: base.OauthService.GitHub.ClientSecret,
RedirectURL: strings.TrimSuffix(base.AppUrl, "/") + ctx.Req.URL.RequestURI(),
Scope: base.OauthService.GitHub.Scopes,
AuthURL: "https://github.com/login/oauth/authorize",

View File

@ -74,10 +74,9 @@ func Profile(ctx *middleware.Context, params martini.Params) {
ctx.HTML(200, "user/profile")
}
func SignIn(ctx *middleware.Context, form auth.LogInForm) {
func SignIn(ctx *middleware.Context) {
ctx.Data["Title"] = "Log In"
if ctx.Req.Method == "GET" {
if base.OauthService != nil {
ctx.Data["OauthEnabled"] = true
ctx.Data["OauthGitHubEnabled"] = base.OauthService.GitHub.Enabled
@ -115,14 +114,21 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) {
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 {
if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
ctx.SetCookie("redirect_to", "", -1)
ctx.Redirect(redirectTo)
} else {
return
}
ctx.Redirect("/")
}
return
func SignInPost(ctx *middleware.Context, form auth.LogInForm) {
ctx.Data["Title"] = "Log In"
if base.OauthService != nil {
ctx.Data["OauthEnabled"] = true
ctx.Data["OauthGitHubEnabled"] = base.OauthService.GitHub.Enabled
}
if ctx.HasError() {
@ -138,7 +144,7 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) {
return
}
ctx.Handle(200, "user.SignIn", err)
ctx.Handle(500, "user.SignIn", err)
return
}
@ -151,13 +157,13 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) {
ctx.Session.Set("userId", user.Id)
ctx.Session.Set("userName", user.Name)
redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to"))
if len(redirectTo) > 0 {
if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
ctx.SetCookie("redirect_to", "", -1)
ctx.Redirect(redirectTo)
} else {
ctx.Redirect("/")
return
}
ctx.Redirect("/")
}
func SignOut(ctx *middleware.Context) {
@ -168,7 +174,7 @@ func SignOut(ctx *middleware.Context) {
ctx.Redirect("/")
}
func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
func SignUp(ctx *middleware.Context) {
ctx.Data["Title"] = "Sign Up"
ctx.Data["PageIsSignUp"] = true
@ -178,8 +184,15 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
return
}
if ctx.Req.Method == "GET" {
ctx.HTML(200, "user/signup")
}
func SignUpPost(ctx *middleware.Context, form auth.RegisterForm) {
ctx.Data["Title"] = "Sign Up"
ctx.Data["PageIsSignUp"] = true
if base.Service.DisenableRegisteration {
ctx.Handle(403, "user.SignUpPost", nil)
return
}
@ -213,7 +226,7 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
case models.ErrUserNameIllegal:
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "user/signup", &form)
default:
ctx.Handle(200, "user.SignUp", err)
ctx.Handle(500, "user.SignUp", err)
}
return
}
@ -240,25 +253,28 @@ func Delete(ctx *middleware.Context) {
ctx.Data["Title"] = "Delete Account"
ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSettingDelete"] = true
if ctx.Req.Method == "GET" {
ctx.HTML(200, "user/delete")
return
}
tmpUser := models.User{Passwd: ctx.Query("password")}
func DeletePost(ctx *middleware.Context) {
ctx.Data["Title"] = "Delete Account"
ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSettingDelete"] = true
tmpUser := models.User{
Passwd: ctx.Query("password"),
Salt: ctx.User.Salt,
}
tmpUser.EncodePasswd()
if len(tmpUser.Passwd) == 0 || tmpUser.Passwd != ctx.User.Passwd {
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = "Password is not correct. Make sure you are owner of this account."
if tmpUser.Passwd != ctx.User.Passwd {
ctx.Flash.Error("Password is not correct. Make sure you are owner of this account.")
} else {
if err := models.DeleteUser(ctx.User); err != nil {
ctx.Data["HasError"] = true
switch err {
case models.ErrUserOwnRepos:
ctx.Data["ErrorMsg"] = "Your account still have ownership of repository, you have to delete or transfer them first."
ctx.Flash.Error("Your account still have ownership of repository, you have to delete or transfer them first.")
default:
ctx.Handle(200, "user.Delete", err)
ctx.Handle(500, "user.Delete", err)
return
}
} else {
@ -267,7 +283,7 @@ func Delete(ctx *middleware.Context) {
}
}
ctx.HTML(200, "user/delete")
ctx.Redirect("/user/delete")
}
const (
@ -439,10 +455,17 @@ func ForgotPasswd(ctx *middleware.Context) {
}
ctx.Data["IsResetRequest"] = true
if ctx.Req.Method == "GET" {
ctx.HTML(200, "user/forgot_passwd")
}
func ForgotPasswdPost(ctx *middleware.Context) {
ctx.Data["Title"] = "Forgot Password"
if base.MailService == nil {
ctx.Handle(403, "user.ForgotPasswdPost", nil)
return
}
ctx.Data["IsResetRequest"] = true
email := ctx.Query("email")
u, err := models.GetUserByEmail(email)
@ -450,7 +473,7 @@ func ForgotPasswd(ctx *middleware.Context) {
if err == models.ErrUserNotExist {
ctx.RenderWithErr("This e-mail address does not associate to any account.", "user/forgot_passwd", nil)
} else {
ctx.Handle(404, "user.ResetPasswd(check existence)", err)
ctx.Handle(500, "user.ResetPasswd(check existence)", err)
}
return
}
@ -473,6 +496,8 @@ func ForgotPasswd(ctx *middleware.Context) {
}
func ResetPasswd(ctx *middleware.Context) {
ctx.Data["Title"] = "Reset Password"
code := ctx.Query("code")
if len(code) == 0 {
ctx.Error(404)
@ -480,11 +505,19 @@ func ResetPasswd(ctx *middleware.Context) {
}
ctx.Data["Code"] = code
if ctx.Req.Method == "GET" {
ctx.Data["IsResetForm"] = true
ctx.HTML(200, "user/reset_passwd")
}
func ResetPasswdPost(ctx *middleware.Context) {
ctx.Data["Title"] = "Reset Password"
code := ctx.Query("code")
if len(code) == 0 {
ctx.Error(404)
return
}
ctx.Data["Code"] = code
if u := models.VerifyUserActiveCode(code); u != nil {
// Validate password length.
@ -500,7 +533,7 @@ func ResetPasswd(ctx *middleware.Context) {
u.Salt = models.GetUserSalt()
u.EncodePasswd()
if err := models.UpdateUser(u); err != nil {
ctx.Handle(404, "user.ResetPasswd(UpdateUser)", err)
ctx.Handle(500, "user.ResetPasswd(UpdateUser)", err)
return
}

View File

@ -1 +1,2 @@
{{if .Flash.ErrorMsg}}<div class="alert alert-danger form-error">{{.Flash.ErrorMsg}}</div>{{end}}
{{if .Flash.SuccessMsg}}<div class="alert alert-success">{{.Flash.SuccessMsg}}</div>{{end}}

View File

@ -2,8 +2,8 @@
{{template "base/navbar" .}}
<div id="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>
{{if .ErrorMsg}}<hr/>
<p>An error is occurred : {{.ErrorMsg}}</p>{{end}}
<hr/>
<p>Application Version: {{AppVer}}</p>
</div>

View File

@ -12,13 +12,16 @@
<li class="list-group-item list-group-item-success"><a href="/user/delete">Delete Account</a></li>
</ul>
</div>
<div id="user-setting-container" class="col-md-9">
<h4>Delete Account</h4>
<p class="alert alert-danger">{{if not .HasError}}The operation will delete your account permanently. Sorry to see you go, but we know you'll back soon.{{else}}{{.ErrorMsg}}{{end}}</p>
{{template "base/alert" .}}
{{if not .Flash.ErrorMsg}}<p class="alert alert-danger">The operation will delete your account permanently. Sorry to see you go, but we know you'll back soon.</p>{{end}}
<div class="form-group">
<button type="submit" class="btn btn-danger btn-lg" href="#delete-account-modal" id="delete-account" data-toggle="modal">Delete Account</button>
</div>
</div>
<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="user-delete">

View File

@ -4,7 +4,7 @@
<form action="/user/forget_password" method="post" class="form-horizontal card" id="login-card">
{{.CsrfTokenHtml}}
<h3>Reset Your Password</h3>
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
{{template "base/alert" .}}
{{if .IsResetSent}}
<p>A confirmation e-mail has been sent to <b>{{.Email}}</b>, please check your inbox within {{.Hours}} hours.</p>
<hr/>

View File

@ -4,7 +4,7 @@
<form action="/user/reset_password?code={{.Code}}" method="post" class="form-horizontal card" id="login-card">
{{.CsrfTokenHtml}}
<h3>Reset Your Pasword</h3>
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
{{template "base/alert" .}}
{{if .IsResetForm}}
<div class="form-group">
<label class="col-md-4 control-label">Password: </label>

View File

@ -7,7 +7,7 @@
<h4>Account Profile</h4>
<form class="form-horizontal" id="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}}
{{template "base/alert" .}}
<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">
<label class="col-md-2 control-label" for="user-setting-username">Username<strong class="text-danger">*</strong></label>

View File

@ -4,7 +4,7 @@
<form action="/user/login" method="post" class="form-horizontal card" id="login-card">
{{.CsrfTokenHtml}}
<h3>Log in</h3>
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
{{template "base/alert" .}}
<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}">
<label class="col-md-4 control-label">Username: </label>
<div class="col-md-6">

View File

@ -7,7 +7,7 @@
Sorry, registeration has been disenabled, you can only get account from administrator.
{{else}}
<h3>Sign Up</h3>
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
{{template "base/alert" .}}
<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}">
<label class="col-md-4 control-label">Username: </label>
<div class="col-md-6">

43
web.go
View File

@ -83,8 +83,8 @@ func runWeb(*cli.Context) {
// Routers.
m.Get("/", ignSignIn, routers.Home)
m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.InstallRouter.Get)
m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallRouter.Post)
m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install)
m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.Get("/issues", reqSignIn, user.Issues)
m.Get("/pulls", reqSignIn, user.Pulls)
m.Get("/stars", reqSignIn, user.Stars)
@ -98,33 +98,38 @@ func runWeb(*cli.Context) {
m.Get("/avatar/:hash", avt.ServeHTTP)
m.Group("/user", func(r martini.Router) {
r.Any("/login", binding.BindIgnErr(auth.LogInForm{}), user.SignIn)
r.Any("/login/github", user.SocialSignIn)
r.Any("/sign_up", binding.BindIgnErr(auth.RegisterForm{}), user.SignUp)
r.Any("/forget_password", user.ForgotPasswd)
r.Any("/reset_password", user.ResetPasswd)
r.Get("/login", user.SignIn)
r.Post("/login", bindIgnErr(auth.LogInForm{}), user.SignInPost)
r.Get("/login/github", user.SocialSignIn)
r.Get("/sign_up", user.SignUp)
r.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
r.Get("/forget_password", user.ForgotPasswd)
r.Post("/forget_password", user.ForgotPasswdPost)
r.Get("/reset_password", user.ResetPasswd)
r.Post("/reset_password", user.ResetPasswdPost)
}, 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)
r.Get("/logout", user.SignOut)
r.Get("/delete", user.Delete)
r.Post("/delete", user.DeletePost)
r.Get("/setting", user.Setting)
r.Post("/setting", bindIgnErr(auth.UpdateProfileForm{}), user.SettingPost)
}, 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("/password", bindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword)
r.Any("/ssh", 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.Any("/repo/mirror", reqSignIn, binding.BindIgnErr(auth.CreateRepoForm{}), repo.Mirror)
m.Any("/repo/create", reqSignIn, bindIgnErr(auth.CreateRepoForm{}), repo.Create)
m.Any("/repo/mirror", reqSignIn, bindIgnErr(auth.CreateRepoForm{}), repo.Mirror)
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
@ -135,8 +140,8 @@ func runWeb(*cli.Context) {
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("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUser)
r.Any("/:userid", bindIgnErr(auth.AdminEditUserForm{}), admin.EditUser)
r.Any("/:userid/delete", admin.DeleteUser)
}, adminReq)
@ -148,8 +153,8 @@ func runWeb(*cli.Context) {
r.Post("/settings", repo.SettingPost)
r.Get("/settings", repo.Setting)
r.Get("/action/:action", repo.Action)
r.Any("/issues/new", binding.BindIgnErr(auth.CreateIssueForm{}), repo.CreateIssue)
r.Post("/issues/:index", binding.BindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
r.Any("/issues/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssue)
r.Post("/issues/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
r.Post("/comment/:action", repo.Comment)
}, reqSignIn, middleware.RepoAssignment(true))