cmd: CMD option for port number of `gogs web` to prevent first time run conflict

- routers: use new binding convention to simplify code
- templates: able to set HTTP port number in install page
This commit is contained in:
Unknwon 2015-02-01 12:41:03 -05:00
parent 3d9cda2d98
commit b293b6eaa6
10 changed files with 133 additions and 122 deletions

View File

@ -3,5 +3,11 @@ language: go
go: go:
- 1.2 - 1.2
- 1.3 - 1.3
- 1.4
- tip
sudo: false sudo: false
notifications:
email:
- u@gogs.io

View File

@ -53,7 +53,9 @@ var CmdWeb = cli.Command{
Description: `Gogs web server is the only thing you need to run, Description: `Gogs web server is the only thing you need to run,
and it takes care of all the other things for you`, and it takes care of all the other things for you`,
Action: runWeb, Action: runWeb,
Flags: []cli.Flag{}, Flags: []cli.Flag{
cli.StringFlag{"port, p", "3000", "Temporary port number to prevent conflict", ""},
},
} }
type VerChecker struct { type VerChecker struct {
@ -162,7 +164,7 @@ func newMacaron() *macaron.Macaron {
return m return m
} }
func runWeb(*cli.Context) { func runWeb(ctx *cli.Context) {
routers.GlobalInit() routers.GlobalInit()
checkVersion() checkVersion()
@ -179,9 +181,9 @@ func runWeb(*cli.Context) {
// Routers. // Routers.
m.Get("/", ignSignIn, routers.Home) m.Get("/", ignSignIn, routers.Home)
m.Get("/explore", ignSignIn, routers.Explore) m.Get("/explore", ignSignIn, routers.Explore)
// FIXME: when i'm binding form here??? m.Combo("/install", routers.InstallInit).
m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) Get(routers.Install).
m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.Group("", func() { m.Group("", func() {
m.Get("/pulls", user.Pulls) m.Get("/pulls", user.Pulls)
m.Get("/issues", user.Issues) m.Get("/issues", user.Issues)
@ -460,6 +462,12 @@ func runWeb(*cli.Context) {
// Not found handler. // Not found handler.
m.NotFound(routers.NotFound) 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 var err error
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort) listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl) log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)

View 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 that shows on every page title
APP_NAME = Gogs: Go Git Service APP_NAME = Gogs: Go Git Service
; Change it if you run locally ; Change it if you run locally

View File

@ -59,6 +59,8 @@ run_user = Run User
run_user_helper = The user must have access to Repository Root Path and run Gogs. run_user_helper = The user must have access to Repository Root Path and run Gogs.
domain = Domain domain = Domain
domain_helper = This affects SSH clone URLs. 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 = Application URL
app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in e-mail. app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in e-mail.
email_title = E-mail Service Settings (Optional) email_title = E-mail Service Settings (Optional)

View File

@ -32,7 +32,7 @@ var (
HasEngine bool HasEngine bool
DbCfg struct { DbCfg struct {
Type, Host, Name, User, Pwd, Path, SslMode string Type, Host, Name, User, Passwd, Path, SSLMode string
} }
EnableSQLite3 bool EnableSQLite3 bool
@ -58,10 +58,10 @@ func LoadModelsConfig() {
DbCfg.Host = sec.Key("HOST").String() DbCfg.Host = sec.Key("HOST").String()
DbCfg.Name = sec.Key("NAME").String() DbCfg.Name = sec.Key("NAME").String()
DbCfg.User = sec.Key("USER").String() DbCfg.User = sec.Key("USER").String()
if len(DbCfg.Pwd) == 0 { if len(DbCfg.Passwd) == 0 {
DbCfg.Pwd = sec.Key("PASSWD").String() 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") DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db")
} }
@ -70,7 +70,7 @@ func getEngine() (*xorm.Engine, error) {
switch DbCfg.Type { switch DbCfg.Type {
case "mysql": case "mysql":
cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", 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": case "postgres":
var host, port = "127.0.0.1", "5432" var host, port = "127.0.0.1", "5432"
fields := strings.Split(DbCfg.Host, ":") fields := strings.Split(DbCfg.Host, ":")
@ -81,7 +81,7 @@ func getEngine() (*xorm.Engine, error) {
port = fields[1] port = fields[1]
} }
cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s", 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": case "sqlite3":
if !EnableSQLite3 { if !EnableSQLite3 {
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type) 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) { func NewTestEngine(x *xorm.Engine) (err error) {
x, err = getEngine() x, err = getEngine()
if err != nil { 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...) return x.Sync(tables...)
@ -106,7 +106,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
func SetEngine() (err error) { func SetEngine() (err error) {
x, err = getEngine() x, err = getEngine()
if err != nil { 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, // WARNING: for serv command, MUST remove the output to os.stdout,

View File

@ -9,6 +9,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"github.com/Unknwon/com"
"github.com/Unknwon/macaron" "github.com/Unknwon/macaron"
"github.com/macaron-contrib/binding" "github.com/macaron-contrib/binding"
"github.com/macaron-contrib/session" "github.com/macaron-contrib/session"
@ -135,6 +136,10 @@ type Form interface {
binding.Validator binding.Validator
} }
func init() {
binding.SetNameMapper(com.ToSnakeCase)
}
// AssignForm assign form values back to the template data. // AssignForm assign form values back to the template data.
func AssignForm(form interface{}, data map[string]interface{}) { func AssignForm(form interface{}, data map[string]interface{}) {
typ := reflect.TypeOf(form) typ := reflect.TypeOf(form)
@ -152,6 +157,8 @@ func AssignForm(form interface{}, data map[string]interface{}) {
// Allow ignored fields in the struct // Allow ignored fields in the struct
if fieldName == "-" { if fieldName == "-" {
continue continue
} else if len(fieldName) == 0 {
fieldName = com.ToSnakeCase(field.Name)
} }
data[fieldName] = val.Field(i).Interface() data[fieldName] = val.Field(i).Interface()

View File

@ -12,26 +12,27 @@ import (
) )
type InstallForm struct { type InstallForm struct {
Database string `form:"database" binding:"Required"` DbType string `binding:"Required"`
DbHost string `form:"host"` DbHost string
DbUser string `form:"user"` DbUser string
DbPasswd string `form:"passwd"` DbPasswd string
DatabaseName string `form:"database_name"` DbName string
SslMode string `form:"ssl_mode"` SSLMode string
DatabasePath string `form:"database_path"` DbPath string
RepoRootPath string `form:"repo_path" binding:"Required"` RepoRootPath string `binding:"Required"`
RunUser string `form:"run_user" binding:"Required"` RunUser string `binding:"Required"`
Domain string `form:"domain" binding:"Required"` Domain string `binding:"Required"`
AppUrl string `form:"app_url" binding:"Required"` HTTPPort string `binding:"Required"`
SmtpHost string `form:"smtp_host"` AppUrl string `binding:"Required"`
SmtpEmail string `form:"mailer_user"` SMTPHost string
SmtpPasswd string `form:"mailer_pwd"` SMTPEmail string
RegisterConfirm string `form:"register_confirm"` SMTPPasswd string
MailNotify string `form:"mail_notify"` RegisterConfirm string
AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"` MailNotify string
AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(255)"` AdminName string `binding:"Required;AlphaDashDot;MaxSize(30)"`
ConfirmPasswd string `form:"confirm_passwd" binding:"Required;MinSize(6);MaxSize(255)"` AdminPasswd string `binding:"Required;MinSize(6);MaxSize(255)"`
AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"` 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 { func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

View File

@ -178,7 +178,7 @@ func NewConfigContext() {
log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err) log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err)
} }
} else { } 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 Cfg.NameMapper = ini.AllCapsUnderscore

View File

@ -73,12 +73,7 @@ func GlobalInit() {
checkRunMode() checkRunMode()
} }
func renderDbOption(ctx *middleware.Context) { func InstallInit(ctx *middleware.Context) {
ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"}
}
// @router /install [get]
func Install(ctx *middleware.Context, form auth.InstallForm) {
if setting.InstallLock { if setting.InstallLock {
ctx.Handle(404, "Install", errors.New("Installation is prohibited")) ctx.Handle(404, "Install", errors.New("Installation is prohibited"))
return return
@ -87,46 +82,35 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
ctx.Data["Title"] = ctx.Tr("install.install") ctx.Data["Title"] = ctx.Tr("install.install")
ctx.Data["PageIsInstall"] = true ctx.Data["PageIsInstall"] = true
// FIXME: when i'm ckeching length here? should they all be 0 no matter when? ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"}
// 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
}
if len(form.RepoRootPath) == 0 { func Install(ctx *middleware.Context) {
form.RepoRootPath = setting.RepoRootPath form := auth.InstallForm{}
}
if len(form.RunUser) == 0 { form.DbHost = models.DbCfg.Host
// Note: it's not normall to use SSH in windows so current user can be first option(not git). form.DbUser = models.DbCfg.User
if setting.IsWindows && setting.RunUser == "git" { form.DbPasswd = models.DbCfg.Passwd
form.RunUser = os.Getenv("USER") form.DbName = models.DbCfg.Name
if len(form.RunUser) == 0 { form.DbPath = models.DbCfg.Path
form.RunUser = os.Getenv("USERNAME")
} form.RepoRootPath = setting.RepoRootPath
} else {
form.RunUser = setting.RunUser // 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 {
if len(form.Domain) == 0 { form.RunUser = setting.RunUser
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 := "" curDbOp := ""
if models.EnableSQLite3 { if models.EnableSQLite3 {
curDbOp = "SQLite3" // Default when enabled. 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) { func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
if setting.InstallLock { ctx.Data["CurDbOption"] = form.DbType
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
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, INSTALL) ctx.HTML(200, INSTALL)
@ -162,18 +137,17 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
// Pass basic check, now test configuration. // Pass basic check, now test configuration.
// Test database setting. // Test database setting.
dbTypes := map[string]string{"MySQL": "mysql", "PostgreSQL": "postgres", "SQLite3": "sqlite3"} 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.Host = form.DbHost
models.DbCfg.User = form.DbUser models.DbCfg.User = form.DbUser
models.DbCfg.Pwd = form.DbPasswd models.DbCfg.Passwd = form.DbPasswd
models.DbCfg.Name = form.DatabaseName models.DbCfg.Name = form.DbName
models.DbCfg.SslMode = form.SslMode models.DbCfg.SSLMode = form.SSLMode
models.DbCfg.Path = form.DatabasePath models.DbCfg.Path = form.DbPath
// Set test engine. // Set test engine.
var x *xorm.Engine var x *xorm.Engine
if err := models.NewTestEngine(x); err != nil { 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`) { 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) ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "http://gogs.io/docs/installation/install_from_binary.html"), INSTALL, &form)
} else { } else {
@ -194,7 +168,6 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
if len(curUser) == 0 { if len(curUser) == 0 {
curUser = os.Getenv("USERNAME") curUser = os.Getenv("USERNAME")
} }
// Does not check run user when the install lock is off.
if form.RunUser != curUser { if form.RunUser != curUser {
ctx.Data["Err_RunUser"] = true ctx.Data["Err_RunUser"] = true
ctx.RenderWithErr(ctx.Tr("install.run_user_not_match", form.RunUser, curUser), INSTALL, &form) 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. // Check admin password.
if form.AdminPasswd != form.ConfirmPasswd { if form.AdminPasswd != form.AdminConfirmPasswd {
ctx.Data["Err_AdminPasswd"] = true ctx.Data["Err_AdminPasswd"] = true
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), INSTALL, form) ctx.RenderWithErr(ctx.Tr("form.password_not_match"), INSTALL, form)
return return
} }
if form.AppUrl[len(form.AppUrl)-1] != '/' {
form.AppUrl += "/"
}
// Save settings. // Save settings.
setting.Cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type) 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("HOST").SetValue(models.DbCfg.Host)
setting.Cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name) 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("USER").SetValue(models.DbCfg.User)
setting.Cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Pwd) 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("SSL_MODE").SetValue(models.DbCfg.SSLMode)
setting.Cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path) setting.Cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path)
setting.Cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath) setting.Cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath)
setting.Cfg.Section("").Key("RUN_USER").SetValue(form.RunUser) setting.Cfg.Section("").Key("RUN_USER").SetValue(form.RunUser)
setting.Cfg.Section("server").Key("DOMAIN").SetValue(form.Domain) 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) 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("ENABLED").SetValue("true")
setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SmtpHost) setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost)
setting.Cfg.Section("mailer").Key("USER").SetValue(form.SmtpEmail) setting.Cfg.Section("mailer").Key("USER").SetValue(form.SMTPEmail)
setting.Cfg.Section("mailer").Key("PASSWD").SetValue(form.SmtpPasswd) 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("REGISTER_EMAIL_CONFIRM").SetValue(com.ToStr(form.RegisterConfirm == "on"))
setting.Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(com.ToStr(form.MailNotify == "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!") log.Info("First-time run install finished!")
ctx.Flash.Success(ctx.Tr("install.install_success")) ctx.Flash.Success(ctx.Tr("install.install_success"))
ctx.Redirect(setting.AppSubUrl + "/user/login") ctx.Redirect(form.AppUrl + "user/login")
} }

View File

@ -13,7 +13,7 @@
<div class="text-center panel-desc">{{.i18n.Tr "install.requite_db_desc"}}</div> <div class="text-center panel-desc">{{.i18n.Tr "install.requite_db_desc"}}</div>
<div class="field"> <div class="field">
<label class="req">{{.i18n.Tr "install.db_type"}}</label> <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}} {{range .DbOptions}}
<option value="{{.}}"{{if eq $.CurDbOption .}}selected{{end}}>{{.}}</option> <option value="{{.}}"{{if eq $.CurDbOption .}}selected{{end}}>{{.}}</option>
{{end}} {{end}}
@ -22,20 +22,20 @@
<div class="server-sql {{if eq .CurDbOption "SQLite3"}}hide{{end}}"> <div class="server-sql {{if eq .CurDbOption "SQLite3"}}hide{{end}}">
<div class="field"> <div class="field">
<label class="req" for="host">{{.i18n.Tr "install.host"}}</label> <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="host" name="host" value="{{.host}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_DbHost}}ipt-error{{end}}" id="db_host" name="db_host" value="{{.db_host}}" />
</div> </div>
<div class="field"> <div class="field">
<label class="req" for="user">{{.i18n.Tr "install.user"}}</label> <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="user" name="user" value="{{.user}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_DbUser}}ipt-error{{end}}" id="db_user" name="db_user" value="{{.db_user}}" />
</div> </div>
<div class="field"> <div class="field">
<label class="req" for="passwd">{{.i18n.Tr "install.password"}}</label> <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="passwd" name="passwd" type="password" value="{{.passwd}}" /> <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>
<div class="field"> <div class="field">
<label class="req" for="database_name">{{.i18n.Tr "install.db_name"}}</label> <label class="req" for="db_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}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_DbName}}ipt-error{{end}}" id="db_name" name="db_name" value="{{.db_name}}" />
<label></label> <label></label>
<span class="help">{{.i18n.Tr "install.db_helper"}}</span> <span class="help">{{.i18n.Tr "install.db_helper"}}</span>
</div> </div>
@ -51,8 +51,8 @@
</div> </div>
<div class="field sqlite-setting {{if not (eq .CurDbOption "SQLite3")}}hide{{end}}"> <div class="field sqlite-setting {{if not (eq .CurDbOption "SQLite3")}}hide{{end}}">
<label class="req" for="database_path">{{.i18n.Tr "install.path"}}</label> <label class="req" for="db_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}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_DbPath}}ipt-error{{end}}" id="db_path" name="db_path" value="{{.db_path}}" />
<label></label> <label></label>
<span class="help">{{.i18n.Tr "install.sqlite_helper"}}</span> <span class="help">{{.i18n.Tr "install.sqlite_helper"}}</span>
</div> </div>
@ -61,8 +61,8 @@
<div class="text-center panel-desc">{{.i18n.Tr "install.general_title"}}</div> <div class="text-center panel-desc">{{.i18n.Tr "install.general_title"}}</div>
<div class="field"> <div class="field">
<label class="req" for="repo_path">{{.i18n.Tr "install.repo_path"}}</label> <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_path" name="repo_path" value="{{.repo_path}}" required /> <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> <label></label>
<span class="help">{{.i18n.Tr "install.repo_path_helper"}}</span> <span class="help">{{.i18n.Tr "install.repo_path_helper"}}</span>
</div> </div>
@ -78,6 +78,12 @@
<label></label> <label></label>
<span class="help">{{.i18n.Tr "install.domain_helper"}}</span> <span class="help">{{.i18n.Tr "install.domain_helper"}}</span>
</div> </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"> <div class="field">
<label class="req" for="app_url">{{.i18n.Tr "install.app_url"}}</label> <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_AppUrl}}ipt-error{{end}}" id="app_url" name="app_url" value="{{.app_url}}" required />
@ -93,12 +99,12 @@
<input class="ipt ipt-large ipt-radius {{if .Err_SmtpHost}}ipt-error{{end}}" id="smtp_host" name="smtp_host" value="{{.smtp_host}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_SmtpHost}}ipt-error{{end}}" id="smtp_host" name="smtp_host" value="{{.smtp_host}}" />
</div> </div>
<div class="field"> <div class="field">
<label for="mailer_user">{{.i18n.Tr "install.mailer_user"}}</label> <label for="smtp_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}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_SMTPEmail}}ipt-error{{end}}" id="smtp_user" name="smtp_user" value="{{.smtp_user}}" />
</div> </div>
<div class="field"> <div class="field">
<label for="mailer_pwd">{{.i18n.Tr "install.mailer_password"}}</label> <label for="smtp_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}}" /> <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> </div>
<hr> <hr>
@ -122,12 +128,12 @@
<input class="ipt ipt-large ipt-radius {{if .Err_AdminName}}ipt-error{{end}}" id="admin_name" name="admin_name" value="{{.admin_name}}" required /> <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>
<div class="field"> <div class="field">
<label class="req" for="admin_pwd">{{.i18n.Tr "install.admin_password"}}</label> <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_pwd" name="admin_pwd" type="password" value="{{.admin_pwd}}" required /> <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>
<div class="field"> <div class="field">
<label class="req" for="confirm_passwd">{{.i18n.Tr "install.confirm_password"}}</label> <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="confirm_passwd" name="confirm_passwd" type="password" required /> <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>
<div class="field"> <div class="field">
<label class="req" for="admin_email">{{.i18n.Tr "install.admin_email"}}</label> <label class="req" for="admin_email">{{.i18n.Tr "install.admin_email"}}</label>