]> source.dussan.org Git - gitea.git/commitdiff
cmd: CMD option for port number of `gogs web` to prevent first time run conflict
authorUnknwon <joe2010xtmf@163.com>
Sun, 1 Feb 2015 17:41:03 +0000 (12:41 -0500)
committerUnknwon <joe2010xtmf@163.com>
Sun, 1 Feb 2015 17:41:03 +0000 (12:41 -0500)
- routers: use new binding convention to simplify code
- templates: able to set HTTP port number in install page

.travis.yml
cmd/web.go
conf/app.ini
conf/locale/locale_en-US.ini
models/models.go
modules/auth/auth.go
modules/auth/user_form.go
modules/setting/setting.go
routers/install.go
templates/install.tmpl

index b060c69392edfe647bc70a68857a51fd8c8f3fd5..ae8533695779094829981aaeb45e58990a638aab 100644 (file)
@@ -3,5 +3,11 @@ language: go
 go:
   - 1.2
   - 1.3
+  - 1.4
+  - tip
 
 sudo: false
+
+notifications:
+  email:
+    - u@gogs.io
\ No newline at end of file
index 241abf2c9c00a55a15063425829aab558f6d196e..e6fb2925cf4fbe4ae6b051037a4cc82d236544b8 100644 (file)
@@ -53,7 +53,9 @@ var CmdWeb = cli.Command{
        Description: `Gogs web server is the only thing you need to run,
 and it takes care of all the other things for you`,
        Action: runWeb,
-       Flags:  []cli.Flag{},
+       Flags: []cli.Flag{
+               cli.StringFlag{"port, p", "3000", "Temporary port number to prevent conflict", ""},
+       },
 }
 
 type VerChecker struct {
@@ -162,7 +164,7 @@ func newMacaron() *macaron.Macaron {
        return m
 }
 
-func runWeb(*cli.Context) {
+func runWeb(ctx *cli.Context) {
        routers.GlobalInit()
        checkVersion()
 
@@ -179,9 +181,9 @@ func runWeb(*cli.Context) {
        // Routers.
        m.Get("/", ignSignIn, routers.Home)
        m.Get("/explore", ignSignIn, routers.Explore)
-       // FIXME: when i'm binding form here???
-       m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install)
-       m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost)
+       m.Combo("/install", routers.InstallInit).
+               Get(routers.Install).
+               Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
        m.Group("", func() {
                m.Get("/pulls", user.Pulls)
                m.Get("/issues", user.Issues)
@@ -460,6 +462,12 @@ func runWeb(*cli.Context) {
        // Not found handler.
        m.NotFound(routers.NotFound)
 
+       // Flag for port number in case first time run conflict.
+       if ctx.IsSet("port") {
+               setting.AppUrl = strings.Replace(setting.AppUrl, setting.HttpPort, ctx.String("port"), 1)
+               setting.HttpPort = ctx.String("port")
+       }
+
        var err error
        listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
        log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
index 1af480a82518495c96a7c81184ed97d7d6fa472b..072421505175b7aff587bb01dff2bcf09287b422 100644 (file)
@@ -1,3 +1,6 @@
+# NEVER EVER MODIFY THIS FILE
+# PLEASE MAKE CHANGES ON CORRESPONDING CUSTOM CONFIG FILE
+
 ; App name that shows on every page title
 APP_NAME = Gogs: Go Git Service
 ; Change it if you run locally
index 7db7ca0d473a3b306837bc5066ee49d744498f05..534e8d3879bc7febdd2814279a60e5a183926700 100644 (file)
@@ -59,6 +59,8 @@ run_user = Run User
 run_user_helper = The user must have access to Repository Root Path and run Gogs.
 domain = Domain
 domain_helper = This affects SSH clone URLs.
+http_port = HTTP Port
+http_port_helper = Port number which application will listen on.
 app_url = Application URL
 app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in e-mail.
 email_title = E-mail Service Settings (Optional)
index 55e7bf582285f95901b694bab9461b1a60e71c2f..df030e51bbd559a13e097b1c5c8cf3a640f9665c 100644 (file)
@@ -32,7 +32,7 @@ var (
        HasEngine bool
 
        DbCfg struct {
-               Type, Host, Name, User, Pwd, Path, SslMode string
+               Type, Host, Name, User, Passwd, Path, SSLMode string
        }
 
        EnableSQLite3 bool
@@ -58,10 +58,10 @@ func LoadModelsConfig() {
        DbCfg.Host = sec.Key("HOST").String()
        DbCfg.Name = sec.Key("NAME").String()
        DbCfg.User = sec.Key("USER").String()
-       if len(DbCfg.Pwd) == 0 {
-               DbCfg.Pwd = sec.Key("PASSWD").String()
+       if len(DbCfg.Passwd) == 0 {
+               DbCfg.Passwd = sec.Key("PASSWD").String()
        }
-       DbCfg.SslMode = sec.Key("SSL_MODE").String()
+       DbCfg.SSLMode = sec.Key("SSL_MODE").String()
        DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db")
 }
 
@@ -70,7 +70,7 @@ func getEngine() (*xorm.Engine, error) {
        switch DbCfg.Type {
        case "mysql":
                cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
-                       DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)
+                       DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name)
        case "postgres":
                var host, port = "127.0.0.1", "5432"
                fields := strings.Split(DbCfg.Host, ":")
@@ -81,7 +81,7 @@ func getEngine() (*xorm.Engine, error) {
                        port = fields[1]
                }
                cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s",
-                       DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode)
+                       DbCfg.User, DbCfg.Passwd, host, port, DbCfg.Name, DbCfg.SSLMode)
        case "sqlite3":
                if !EnableSQLite3 {
                        return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
@@ -97,7 +97,7 @@ func getEngine() (*xorm.Engine, error) {
 func NewTestEngine(x *xorm.Engine) (err error) {
        x, err = getEngine()
        if err != nil {
-               return fmt.Errorf("models.init(fail to connect to database): %v", err)
+               return fmt.Errorf("connect to database: %v", err)
        }
 
        return x.Sync(tables...)
@@ -106,7 +106,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
 func SetEngine() (err error) {
        x, err = getEngine()
        if err != nil {
-               return fmt.Errorf("models.init(fail to connect to database): %v", err)
+               return fmt.Errorf("connect to database: %v", err)
        }
 
        // WARNING: for serv command, MUST remove the output to os.stdout,
index 1dd96d8d40a0f61255518f83d1ca439239126388..ad7ce5b9ada4b69f6edafe9ae9eab03131f7d15d 100644 (file)
@@ -9,6 +9,7 @@ import (
        "reflect"
        "strings"
 
+       "github.com/Unknwon/com"
        "github.com/Unknwon/macaron"
        "github.com/macaron-contrib/binding"
        "github.com/macaron-contrib/session"
@@ -135,6 +136,10 @@ type Form interface {
        binding.Validator
 }
 
+func init() {
+       binding.SetNameMapper(com.ToSnakeCase)
+}
+
 // AssignForm assign form values back to the template data.
 func AssignForm(form interface{}, data map[string]interface{}) {
        typ := reflect.TypeOf(form)
@@ -152,6 +157,8 @@ func AssignForm(form interface{}, data map[string]interface{}) {
                // Allow ignored fields in the struct
                if fieldName == "-" {
                        continue
+               } else if len(fieldName) == 0 {
+                       fieldName = com.ToSnakeCase(field.Name)
                }
 
                data[fieldName] = val.Field(i).Interface()
index becd5cbca86863921352f17b2049a5853a218aec..3c0ff65174963e382819a90abe99be7faca3c0d0 100644 (file)
@@ -12,26 +12,27 @@ import (
 )
 
 type InstallForm struct {
-       Database        string `form:"database" binding:"Required"`
-       DbHost          string `form:"host"`
-       DbUser          string `form:"user"`
-       DbPasswd        string `form:"passwd"`
-       DatabaseName    string `form:"database_name"`
-       SslMode         string `form:"ssl_mode"`
-       DatabasePath    string `form:"database_path"`
-       RepoRootPath    string `form:"repo_path" binding:"Required"`
-       RunUser         string `form:"run_user" binding:"Required"`
-       Domain          string `form:"domain" binding:"Required"`
-       AppUrl          string `form:"app_url" binding:"Required"`
-       SmtpHost        string `form:"smtp_host"`
-       SmtpEmail       string `form:"mailer_user"`
-       SmtpPasswd      string `form:"mailer_pwd"`
-       RegisterConfirm string `form:"register_confirm"`
-       MailNotify      string `form:"mail_notify"`
-       AdminName       string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
-       AdminPasswd     string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(255)"`
-       ConfirmPasswd   string `form:"confirm_passwd" binding:"Required;MinSize(6);MaxSize(255)"`
-       AdminEmail      string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
+       DbType             string `binding:"Required"`
+       DbHost             string
+       DbUser             string
+       DbPasswd           string
+       DbName             string
+       SSLMode            string
+       DbPath             string
+       RepoRootPath       string `binding:"Required"`
+       RunUser            string `binding:"Required"`
+       Domain             string `binding:"Required"`
+       HTTPPort           string `binding:"Required"`
+       AppUrl             string `binding:"Required"`
+       SMTPHost           string
+       SMTPEmail          string
+       SMTPPasswd         string
+       RegisterConfirm    string
+       MailNotify         string
+       AdminName          string `binding:"Required;AlphaDashDot;MaxSize(30)"`
+       AdminPasswd        string `binding:"Required;MinSize(6);MaxSize(255)"`
+       AdminConfirmPasswd string `binding:"Required;MinSize(6);MaxSize(255)"`
+       AdminEmail         string `binding:"Required;Email;MaxSize(50)"`
 }
 
 func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
index bc9da3c63a6ab7436a941f018533c6c86bb33025..e7c44cdd4fce39c85b208508bd0203acfd6bdcbe 100644 (file)
@@ -178,7 +178,7 @@ func NewConfigContext() {
                        log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err)
                }
        } else {
-               log.Warn("No custom 'conf/app.ini' found, please go to '/install'")
+               log.Warn("No custom 'conf/app.ini' found, ignore this if you're running first time")
        }
        Cfg.NameMapper = ini.AllCapsUnderscore
 
index a2491c4fc7b11fdb5e18c47dc065cc816c6126c9..58e6cf664b499f13a1bf3a36771c80ceed4eeb2f 100644 (file)
@@ -73,12 +73,7 @@ func GlobalInit() {
        checkRunMode()
 }
 
-func renderDbOption(ctx *middleware.Context) {
-       ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"}
-}
-
-// @router /install [get]
-func Install(ctx *middleware.Context, form auth.InstallForm) {
+func InstallInit(ctx *middleware.Context) {
        if setting.InstallLock {
                ctx.Handle(404, "Install", errors.New("Installation is prohibited"))
                return
@@ -87,46 +82,35 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
        ctx.Data["Title"] = ctx.Tr("install.install")
        ctx.Data["PageIsInstall"] = true
 
-       // FIXME: when i'm ckeching length here? should they all be 0 no matter when?
-       // Get and assign values to install form.
-       if len(form.DbHost) == 0 {
-               form.DbHost = models.DbCfg.Host
-       }
-       if len(form.DbUser) == 0 {
-               form.DbUser = models.DbCfg.User
-       }
-       if len(form.DbPasswd) == 0 {
-               form.DbPasswd = models.DbCfg.Pwd
-       }
-       if len(form.DatabaseName) == 0 {
-               form.DatabaseName = models.DbCfg.Name
-       }
-       if len(form.DatabasePath) == 0 {
-               form.DatabasePath = models.DbCfg.Path
-       }
+       ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"}
+}
 
-       if len(form.RepoRootPath) == 0 {
-               form.RepoRootPath = setting.RepoRootPath
-       }
-       if len(form.RunUser) == 0 {
-               // Note: it's not normall to use SSH in windows so current user can be first option(not git).
-               if setting.IsWindows && setting.RunUser == "git" {
-                       form.RunUser = os.Getenv("USER")
-                       if len(form.RunUser) == 0 {
-                               form.RunUser = os.Getenv("USERNAME")
-                       }
-               } else {
-                       form.RunUser = setting.RunUser
+func Install(ctx *middleware.Context) {
+       form := auth.InstallForm{}
+
+       form.DbHost = models.DbCfg.Host
+       form.DbUser = models.DbCfg.User
+       form.DbPasswd = models.DbCfg.Passwd
+       form.DbName = models.DbCfg.Name
+       form.DbPath = models.DbCfg.Path
+
+       form.RepoRootPath = setting.RepoRootPath
+
+       // Note(unknwon): it's hard for Windows users change a running user,
+       //      so just use current one if config says default.
+       if setting.IsWindows && setting.RunUser == "git" {
+               form.RunUser = os.Getenv("USER")
+               if len(form.RunUser) == 0 {
+                       form.RunUser = os.Getenv("USERNAME")
                }
+       } else {
+               form.RunUser = setting.RunUser
        }
-       if len(form.Domain) == 0 {
-               form.Domain = setting.Domain
-       }
-       if len(form.AppUrl) == 0 {
-               form.AppUrl = setting.AppUrl
-       }
 
-       renderDbOption(ctx)
+       form.Domain = setting.Domain
+       form.HTTPPort = setting.HttpPort
+       form.AppUrl = setting.AppUrl
+
        curDbOp := ""
        if models.EnableSQLite3 {
                curDbOp = "SQLite3" // Default when enabled.
@@ -138,16 +122,7 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
 }
 
 func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
-       if setting.InstallLock {
-               ctx.Handle(404, "InstallPost", errors.New("Installation is prohibited"))
-               return
-       }
-
-       ctx.Data["Title"] = ctx.Tr("install.install")
-       ctx.Data["PageIsInstall"] = true
-
-       renderDbOption(ctx)
-       ctx.Data["CurDbOption"] = form.Database
+       ctx.Data["CurDbOption"] = form.DbType
 
        if ctx.HasError() {
                ctx.HTML(200, INSTALL)
@@ -162,18 +137,17 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
        // Pass basic check, now test configuration.
        // Test database setting.
        dbTypes := map[string]string{"MySQL": "mysql", "PostgreSQL": "postgres", "SQLite3": "sqlite3"}
-       models.DbCfg.Type = dbTypes[form.Database]
+       models.DbCfg.Type = dbTypes[form.DbType]
        models.DbCfg.Host = form.DbHost
        models.DbCfg.User = form.DbUser
-       models.DbCfg.Pwd = form.DbPasswd
-       models.DbCfg.Name = form.DatabaseName
-       models.DbCfg.SslMode = form.SslMode
-       models.DbCfg.Path = form.DatabasePath
+       models.DbCfg.Passwd = form.DbPasswd
+       models.DbCfg.Name = form.DbName
+       models.DbCfg.SSLMode = form.SSLMode
+       models.DbCfg.Path = form.DbPath
 
        // Set test engine.
        var x *xorm.Engine
        if err := models.NewTestEngine(x); err != nil {
-               // FIXME: should use core.QueryDriver (github.com/go-xorm/core)
                if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
                        ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "http://gogs.io/docs/installation/install_from_binary.html"), INSTALL, &form)
                } else {
@@ -194,7 +168,6 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
        if len(curUser) == 0 {
                curUser = os.Getenv("USERNAME")
        }
-       // Does not check run user when the install lock is off.
        if form.RunUser != curUser {
                ctx.Data["Err_RunUser"] = true
                ctx.RenderWithErr(ctx.Tr("install.run_user_not_match", form.RunUser, curUser), INSTALL, &form)
@@ -202,31 +175,36 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
        }
 
        // Check admin password.
-       if form.AdminPasswd != form.ConfirmPasswd {
+       if form.AdminPasswd != form.AdminConfirmPasswd {
                ctx.Data["Err_AdminPasswd"] = true
                ctx.RenderWithErr(ctx.Tr("form.password_not_match"), INSTALL, form)
                return
        }
 
+       if form.AppUrl[len(form.AppUrl)-1] != '/' {
+               form.AppUrl += "/"
+       }
+
        // Save settings.
        setting.Cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type)
        setting.Cfg.Section("database").Key("HOST").SetValue(models.DbCfg.Host)
        setting.Cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name)
        setting.Cfg.Section("database").Key("USER").SetValue(models.DbCfg.User)
-       setting.Cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Pwd)
-       setting.Cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SslMode)
+       setting.Cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Passwd)
+       setting.Cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SSLMode)
        setting.Cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path)
 
        setting.Cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath)
        setting.Cfg.Section("").Key("RUN_USER").SetValue(form.RunUser)
        setting.Cfg.Section("server").Key("DOMAIN").SetValue(form.Domain)
+       setting.Cfg.Section("server").Key("HTTP_PORT").SetValue(form.HTTPPort)
        setting.Cfg.Section("server").Key("ROOT_URL").SetValue(form.AppUrl)
 
-       if len(strings.TrimSpace(form.SmtpHost)) > 0 {
+       if len(strings.TrimSpace(form.SMTPHost)) > 0 {
                setting.Cfg.Section("mailer").Key("ENABLED").SetValue("true")
-               setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SmtpHost)
-               setting.Cfg.Section("mailer").Key("USER").SetValue(form.SmtpEmail)
-               setting.Cfg.Section("mailer").Key("PASSWD").SetValue(form.SmtpPasswd)
+               setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost)
+               setting.Cfg.Section("mailer").Key("USER").SetValue(form.SMTPEmail)
+               setting.Cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd)
 
                setting.Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(com.ToStr(form.RegisterConfirm == "on"))
                setting.Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(com.ToStr(form.MailNotify == "on"))
@@ -264,5 +242,5 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
 
        log.Info("First-time run install finished!")
        ctx.Flash.Success(ctx.Tr("install.install_success"))
-       ctx.Redirect(setting.AppSubUrl + "/user/login")
+       ctx.Redirect(form.AppUrl + "user/login")
 }
index f1c28031d9fd461e57728932c2e1429f9c38043f..3a7eb7877f0f7c3348857a162ca7d7366c8d7a43 100644 (file)
@@ -13,7 +13,7 @@
                         <div class="text-center panel-desc">{{.i18n.Tr "install.requite_db_desc"}}</div>
                         <div class="field">
                             <label class="req">{{.i18n.Tr "install.db_type"}}</label>
-                            <select name="database" id="install-database" class="form-control">
+                            <select name="db_type" id="install-database" class="form-control">
                                 {{range .DbOptions}}
                                 <option value="{{.}}"{{if eq $.CurDbOption .}}selected{{end}}>{{.}}</option>
                                 {{end}}
 
                         <div class="server-sql {{if eq .CurDbOption "SQLite3"}}hide{{end}}">
                             <div class="field">
-                                <label class="req" for="host">{{.i18n.Tr "install.host"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DbHost}}ipt-error{{end}}" id="host" name="host" value="{{.host}}" />
+                                <label class="req" for="db_host">{{.i18n.Tr "install.host"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbHost}}ipt-error{{end}}" id="db_host" name="db_host" value="{{.db_host}}" />
                             </div>
                             <div class="field">
-                                <label class="req" for="user">{{.i18n.Tr "install.user"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DbUser}}ipt-error{{end}}" id="user" name="user" value="{{.user}}" />
+                                <label class="req" for="db_user">{{.i18n.Tr "install.user"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbUser}}ipt-error{{end}}" id="db_user" name="db_user" value="{{.db_user}}" />
                             </div>
                             <div class="field">
-                                <label class="req" for="passwd">{{.i18n.Tr "install.password"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DbPasswd}}ipt-error{{end}}" id="passwd" name="passwd" type="password" value="{{.passwd}}" />
+                                <label class="req" for="db_passwd">{{.i18n.Tr "install.password"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbPasswd}}ipt-error{{end}}" id="db_passwd" name="db_passwd" type="password" value="{{.db_passwd}}" />
                             </div>
                             <div class="field">
-                                <label class="req" for="database_name">{{.i18n.Tr "install.db_name"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DatabaseName}}ipt-error{{end}}" id="database_name" name="database_name" value="{{.database_name}}" />
+                                <label class="req" for="db_name">{{.i18n.Tr "install.db_name"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbName}}ipt-error{{end}}" id="db_name" name="db_name" value="{{.db_name}}" />
                                 <label></label>
                                 <span class="help">{{.i18n.Tr "install.db_helper"}}</span>
                             </div>
@@ -51,8 +51,8 @@
                         </div>
 
                         <div class="field sqlite-setting {{if not (eq .CurDbOption "SQLite3")}}hide{{end}}">
-                            <label class="req" for="database_path">{{.i18n.Tr "install.path"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_DatabasePath}}ipt-error{{end}}" id="database_path" name="database_path" value="{{.database_path}}" />
+                            <label class="req" for="db_path">{{.i18n.Tr "install.path"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_DbPath}}ipt-error{{end}}" id="db_path" name="db_path" value="{{.db_path}}" />
                             <label></label>
                             <span class="help">{{.i18n.Tr "install.sqlite_helper"}}</span>
                         </div>
@@ -61,8 +61,8 @@
 
                         <div class="text-center panel-desc">{{.i18n.Tr "install.general_title"}}</div>
                         <div class="field">
-                            <label class="req" for="repo_path">{{.i18n.Tr "install.repo_path"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_RepoRootPath}}ipt-error{{end}}" id="repo_path" name="repo_path" value="{{.repo_path}}" required />
+                            <label class="req" for="repo_root_path">{{.i18n.Tr "install.repo_path"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_RepoRootPath}}ipt-error{{end}}" id="repo_root_path" name="repo_root_path" value="{{.repo_root_path}}" required />
                             <label></label>
                             <span class="help">{{.i18n.Tr "install.repo_path_helper"}}</span>
                         </div>
                             <label></label>
                             <span class="help">{{.i18n.Tr "install.domain_helper"}}</span>
                         </div>
+                        <div class="field">
+                            <label class="req" for="http_port">{{.i18n.Tr "install.http_port"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_HttpPort}}ipt-error{{end}}" id="http_port" name="http_port" value="{{.http_port}}" required />
+                            <label></label>
+                            <span class="help">{{.i18n.Tr "install.http_port_helper"}}</span>
+                        </div>
                         <div class="field">
                             <label class="req" for="app_url">{{.i18n.Tr "install.app_url"}}</label>
                             <input class="ipt ipt-large ipt-radius {{if .Err_AppUrl}}ipt-error{{end}}" id="app_url" name="app_url" value="{{.app_url}}" required />
                             <input class="ipt ipt-large ipt-radius {{if .Err_SmtpHost}}ipt-error{{end}}" id="smtp_host" name="smtp_host" value="{{.smtp_host}}" />
                         </div>
                         <div class="field">
-                            <label for="mailer_user">{{.i18n.Tr "install.mailer_user"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_SmtpEmail}}ipt-error{{end}}" id="mailer_user" name="mailer_user" value="{{.mailer_user}}" />
+                            <label for="smtp_user">{{.i18n.Tr "install.mailer_user"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_SMTPEmail}}ipt-error{{end}}" id="smtp_user" name="smtp_user" value="{{.smtp_user}}" />
                         </div>
                         <div class="field">
-                            <label for="mailer_pwd">{{.i18n.Tr "install.mailer_password"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_SmtpPasswd}}ipt-error{{end}}" id="mailer_pwd" name="mailer_pwd" type="password" value="{{.mailer_pwd}}" />
+                            <label for="smtp_pwd">{{.i18n.Tr "install.mailer_password"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_SMTPPasswd}}ipt-error{{end}}" id="smtp_pwd" name="smtp_pwd" type="password" value="{{.smtp_pwd}}" />
                         </div>
 
                         <hr>
                             <input class="ipt ipt-large ipt-radius {{if .Err_AdminName}}ipt-error{{end}}" id="admin_name" name="admin_name" value="{{.admin_name}}" required />
                         </div>
                         <div class="field">
-                            <label class="req" for="admin_pwd">{{.i18n.Tr "install.admin_password"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="admin_pwd" name="admin_pwd" type="password" value="{{.admin_pwd}}" required />
+                            <label class="req" for="admin_passwd">{{.i18n.Tr "install.admin_password"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="admin_passwd" name="admin_passwd" type="password" value="{{.admin_passwd}}" required />
                         </div>
                         <div class="field">
-                            <label class="req" for="confirm_passwd">{{.i18n.Tr "install.confirm_password"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="confirm_passwd" name="confirm_passwd" type="password" required />
+                            <label class="req" for="admin_confirm_passwd">{{.i18n.Tr "install.confirm_password"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="admin_confirm_passwd" name="admin_confirm_passwd" type="password" required />
                         </div>
                         <div class="field">
                             <label class="req" for="admin_email">{{.i18n.Tr "install.admin_email"}}</label>