code.gitea.io/gitea@v1.21.7/routers/install/install.go (about) 1 // Copyright 2014 The Gogs Authors. All rights reserved. 2 // Copyright 2021 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package install 6 7 import ( 8 "fmt" 9 "net/http" 10 "net/mail" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "strconv" 15 "strings" 16 "time" 17 18 "code.gitea.io/gitea/models/db" 19 db_install "code.gitea.io/gitea/models/db/install" 20 "code.gitea.io/gitea/models/migrations" 21 system_model "code.gitea.io/gitea/models/system" 22 user_model "code.gitea.io/gitea/models/user" 23 "code.gitea.io/gitea/modules/auth/password/hash" 24 "code.gitea.io/gitea/modules/base" 25 "code.gitea.io/gitea/modules/context" 26 "code.gitea.io/gitea/modules/generate" 27 "code.gitea.io/gitea/modules/graceful" 28 "code.gitea.io/gitea/modules/log" 29 "code.gitea.io/gitea/modules/setting" 30 "code.gitea.io/gitea/modules/templates" 31 "code.gitea.io/gitea/modules/translation" 32 "code.gitea.io/gitea/modules/user" 33 "code.gitea.io/gitea/modules/util" 34 "code.gitea.io/gitea/modules/web" 35 "code.gitea.io/gitea/modules/web/middleware" 36 "code.gitea.io/gitea/routers/common" 37 "code.gitea.io/gitea/services/forms" 38 39 "gitea.com/go-chi/session" 40 ) 41 42 const ( 43 // tplInstall template for installation page 44 tplInstall base.TplName = "install" 45 tplPostInstall base.TplName = "post-install" 46 ) 47 48 // getSupportedDbTypeNames returns a slice for supported database types and names. The slice is used to keep the order 49 func getSupportedDbTypeNames() (dbTypeNames []map[string]string) { 50 for _, t := range setting.SupportedDatabaseTypes { 51 dbTypeNames = append(dbTypeNames, map[string]string{"type": t, "name": setting.DatabaseTypeNames[t]}) 52 } 53 return dbTypeNames 54 } 55 56 // Contexter prepare for rendering installation page 57 func Contexter() func(next http.Handler) http.Handler { 58 rnd := templates.HTMLRenderer() 59 dbTypeNames := getSupportedDbTypeNames() 60 envConfigKeys := setting.CollectEnvConfigKeys() 61 return func(next http.Handler) http.Handler { 62 return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 63 base, baseCleanUp := context.NewBaseContext(resp, req) 64 defer baseCleanUp() 65 66 ctx := context.NewWebContext(base, rnd, session.GetSession(req)) 67 ctx.AppendContextValue(context.WebContextKey, ctx) 68 ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) 69 ctx.Data.MergeFrom(middleware.ContextData{ 70 "Context": ctx, // TODO: use "ctx" in template and remove this 71 "locale": ctx.Locale, 72 "Title": ctx.Locale.Tr("install.install"), 73 "PageIsInstall": true, 74 "DbTypeNames": dbTypeNames, 75 "EnvConfigKeys": envConfigKeys, 76 "CustomConfFile": setting.CustomConf, 77 "AllLangs": translation.AllLangs(), 78 79 "PasswordHashAlgorithms": hash.RecommendedHashAlgorithms, 80 }) 81 next.ServeHTTP(resp, ctx.Req) 82 }) 83 } 84 } 85 86 // Install render installation page 87 func Install(ctx *context.Context) { 88 if setting.InstallLock { 89 InstallDone(ctx) 90 return 91 } 92 93 form := forms.InstallForm{} 94 95 // Database settings 96 form.DbHost = setting.Database.Host 97 form.DbUser = setting.Database.User 98 form.DbPasswd = setting.Database.Passwd 99 form.DbName = setting.Database.Name 100 form.DbPath = setting.Database.Path 101 form.DbSchema = setting.Database.Schema 102 form.SSLMode = setting.Database.SSLMode 103 104 curDBType := setting.Database.Type.String() 105 var isCurDBTypeSupported bool 106 for _, dbType := range setting.SupportedDatabaseTypes { 107 if dbType == curDBType { 108 isCurDBTypeSupported = true 109 break 110 } 111 } 112 if !isCurDBTypeSupported { 113 curDBType = "mysql" 114 } 115 ctx.Data["CurDbType"] = curDBType 116 117 // Application general settings 118 form.AppName = setting.AppName 119 form.RepoRootPath = setting.RepoRootPath 120 form.LFSRootPath = setting.LFS.Storage.Path 121 122 // Note(unknown): it's hard for Windows users change a running user, 123 // so just use current one if config says default. 124 if setting.IsWindows && setting.RunUser == "git" { 125 form.RunUser = user.CurrentUsername() 126 } else { 127 form.RunUser = setting.RunUser 128 } 129 130 form.Domain = setting.Domain 131 form.SSHPort = setting.SSH.Port 132 form.HTTPPort = setting.HTTPPort 133 form.AppURL = setting.AppURL 134 form.LogRootPath = setting.Log.RootPath 135 136 // E-mail service settings 137 if setting.MailService != nil { 138 form.SMTPAddr = setting.MailService.SMTPAddr 139 form.SMTPPort = setting.MailService.SMTPPort 140 form.SMTPFrom = setting.MailService.From 141 form.SMTPUser = setting.MailService.User 142 form.SMTPPasswd = setting.MailService.Passwd 143 } 144 form.RegisterConfirm = setting.Service.RegisterEmailConfirm 145 form.MailNotify = setting.Service.EnableNotifyMail 146 147 // Server and other services settings 148 form.OfflineMode = setting.OfflineMode 149 form.DisableGravatar = setting.DisableGravatar // when installing, there is no database connection so that given a default value 150 form.EnableFederatedAvatar = setting.EnableFederatedAvatar // when installing, there is no database connection so that given a default value 151 152 form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn 153 form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp 154 form.DisableRegistration = setting.Service.DisableRegistration 155 form.AllowOnlyExternalRegistration = setting.Service.AllowOnlyExternalRegistration 156 form.EnableCaptcha = setting.Service.EnableCaptcha 157 form.RequireSignInView = setting.Service.RequireSignInView 158 form.DefaultKeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate 159 form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization 160 form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking 161 form.NoReplyAddress = setting.Service.NoReplyAddress 162 form.PasswordAlgorithm = hash.ConfigHashAlgorithm(setting.PasswordHashAlgo) 163 164 middleware.AssignForm(form, ctx.Data) 165 ctx.HTML(http.StatusOK, tplInstall) 166 } 167 168 func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool { 169 var err error 170 171 if (setting.Database.Type == "sqlite3") && 172 len(setting.Database.Path) == 0 { 173 ctx.Data["Err_DbPath"] = true 174 ctx.RenderWithErr(ctx.Tr("install.err_empty_db_path"), tplInstall, form) 175 return false 176 } 177 178 // Check if the user is trying to re-install in an installed database 179 db.UnsetDefaultEngine() 180 defer db.UnsetDefaultEngine() 181 182 if err = db.InitEngine(ctx); err != nil { 183 if strings.Contains(err.Error(), `Unknown database type: sqlite3`) { 184 ctx.Data["Err_DbType"] = true 185 ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "https://docs.gitea.com/installation/install-from-binary"), tplInstall, form) 186 } else { 187 ctx.Data["Err_DbSetting"] = true 188 ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, form) 189 } 190 return false 191 } 192 193 err = db_install.CheckDatabaseConnection() 194 if err != nil { 195 ctx.Data["Err_DbSetting"] = true 196 ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, form) 197 return false 198 } 199 200 hasPostInstallationUser, err := db_install.HasPostInstallationUsers() 201 if err != nil { 202 ctx.Data["Err_DbSetting"] = true 203 ctx.RenderWithErr(ctx.Tr("install.invalid_db_table", "user", err), tplInstall, form) 204 return false 205 } 206 dbMigrationVersion, err := db_install.GetMigrationVersion() 207 if err != nil { 208 ctx.Data["Err_DbSetting"] = true 209 ctx.RenderWithErr(ctx.Tr("install.invalid_db_table", "version", err), tplInstall, form) 210 return false 211 } 212 213 if hasPostInstallationUser && dbMigrationVersion > 0 { 214 log.Error("The database is likely to have been used by Gitea before, database migration version=%d", dbMigrationVersion) 215 confirmed := form.ReinstallConfirmFirst && form.ReinstallConfirmSecond && form.ReinstallConfirmThird 216 if !confirmed { 217 ctx.Data["Err_DbInstalledBefore"] = true 218 ctx.RenderWithErr(ctx.Tr("install.reinstall_error"), tplInstall, form) 219 return false 220 } 221 222 log.Info("User confirmed re-installation of Gitea into a pre-existing database") 223 } 224 225 if hasPostInstallationUser || dbMigrationVersion > 0 { 226 log.Info("Gitea will be installed in a database with: hasPostInstallationUser=%v, dbMigrationVersion=%v", hasPostInstallationUser, dbMigrationVersion) 227 } 228 229 return true 230 } 231 232 // SubmitInstall response for submit install items 233 func SubmitInstall(ctx *context.Context) { 234 if setting.InstallLock { 235 InstallDone(ctx) 236 return 237 } 238 239 var err error 240 241 form := *web.GetForm(ctx).(*forms.InstallForm) 242 243 // fix form values 244 if form.AppURL != "" && form.AppURL[len(form.AppURL)-1] != '/' { 245 form.AppURL += "/" 246 } 247 248 ctx.Data["CurDbType"] = form.DbType 249 250 if ctx.HasError() { 251 ctx.Data["Err_SMTP"] = ctx.Data["Err_SMTPUser"] != nil 252 ctx.Data["Err_Admin"] = ctx.Data["Err_AdminName"] != nil || ctx.Data["Err_AdminPasswd"] != nil || ctx.Data["Err_AdminEmail"] != nil 253 ctx.HTML(http.StatusOK, tplInstall) 254 return 255 } 256 257 if _, err = exec.LookPath("git"); err != nil { 258 ctx.RenderWithErr(ctx.Tr("install.test_git_failed", err), tplInstall, &form) 259 return 260 } 261 262 // ---- Basic checks are passed, now test configuration. 263 264 // Test database setting. 265 setting.Database.Type = setting.DatabaseType(form.DbType) 266 setting.Database.Host = form.DbHost 267 setting.Database.User = form.DbUser 268 setting.Database.Passwd = form.DbPasswd 269 setting.Database.Name = form.DbName 270 setting.Database.Schema = form.DbSchema 271 setting.Database.SSLMode = form.SSLMode 272 setting.Database.Path = form.DbPath 273 setting.Database.LogSQL = !setting.IsProd 274 275 if !checkDatabase(ctx, &form) { 276 return 277 } 278 279 // Prepare AppDataPath, it is very important for Gitea 280 if err = setting.PrepareAppDataPath(); err != nil { 281 ctx.RenderWithErr(ctx.Tr("install.invalid_app_data_path", err), tplInstall, &form) 282 return 283 } 284 285 // Test repository root path. 286 form.RepoRootPath = strings.ReplaceAll(form.RepoRootPath, "\\", "/") 287 if err = os.MkdirAll(form.RepoRootPath, os.ModePerm); err != nil { 288 ctx.Data["Err_RepoRootPath"] = true 289 ctx.RenderWithErr(ctx.Tr("install.invalid_repo_path", err), tplInstall, &form) 290 return 291 } 292 293 // Test LFS root path if not empty, empty meaning disable LFS 294 if form.LFSRootPath != "" { 295 form.LFSRootPath = strings.ReplaceAll(form.LFSRootPath, "\\", "/") 296 if err := os.MkdirAll(form.LFSRootPath, os.ModePerm); err != nil { 297 ctx.Data["Err_LFSRootPath"] = true 298 ctx.RenderWithErr(ctx.Tr("install.invalid_lfs_path", err), tplInstall, &form) 299 return 300 } 301 } 302 303 // Test log root path. 304 form.LogRootPath = strings.ReplaceAll(form.LogRootPath, "\\", "/") 305 if err = os.MkdirAll(form.LogRootPath, os.ModePerm); err != nil { 306 ctx.Data["Err_LogRootPath"] = true 307 ctx.RenderWithErr(ctx.Tr("install.invalid_log_root_path", err), tplInstall, &form) 308 return 309 } 310 311 currentUser, match := setting.IsRunUserMatchCurrentUser(form.RunUser) 312 if !match { 313 ctx.Data["Err_RunUser"] = true 314 ctx.RenderWithErr(ctx.Tr("install.run_user_not_match", form.RunUser, currentUser), tplInstall, &form) 315 return 316 } 317 318 // Check logic loophole between disable self-registration and no admin account. 319 if form.DisableRegistration && len(form.AdminName) == 0 { 320 ctx.Data["Err_Services"] = true 321 ctx.Data["Err_Admin"] = true 322 ctx.RenderWithErr(ctx.Tr("install.no_admin_and_disable_registration"), tplInstall, form) 323 return 324 } 325 326 // Check admin user creation 327 if len(form.AdminName) > 0 { 328 // Ensure AdminName is valid 329 if err := user_model.IsUsableUsername(form.AdminName); err != nil { 330 ctx.Data["Err_Admin"] = true 331 ctx.Data["Err_AdminName"] = true 332 if db.IsErrNameReserved(err) { 333 ctx.RenderWithErr(ctx.Tr("install.err_admin_name_is_reserved"), tplInstall, form) 334 return 335 } else if db.IsErrNamePatternNotAllowed(err) { 336 ctx.RenderWithErr(ctx.Tr("install.err_admin_name_pattern_not_allowed"), tplInstall, form) 337 return 338 } 339 ctx.RenderWithErr(ctx.Tr("install.err_admin_name_is_invalid"), tplInstall, form) 340 return 341 } 342 // Check Admin email 343 if len(form.AdminEmail) == 0 { 344 ctx.Data["Err_Admin"] = true 345 ctx.Data["Err_AdminEmail"] = true 346 ctx.RenderWithErr(ctx.Tr("install.err_empty_admin_email"), tplInstall, form) 347 return 348 } 349 // Check admin password. 350 if len(form.AdminPasswd) == 0 { 351 ctx.Data["Err_Admin"] = true 352 ctx.Data["Err_AdminPasswd"] = true 353 ctx.RenderWithErr(ctx.Tr("install.err_empty_admin_password"), tplInstall, form) 354 return 355 } 356 if form.AdminPasswd != form.AdminConfirmPasswd { 357 ctx.Data["Err_Admin"] = true 358 ctx.Data["Err_AdminPasswd"] = true 359 ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplInstall, form) 360 return 361 } 362 } 363 364 // Init the engine with migration 365 if err = db.InitEngineWithMigration(ctx, migrations.Migrate); err != nil { 366 db.UnsetDefaultEngine() 367 ctx.Data["Err_DbSetting"] = true 368 ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form) 369 return 370 } 371 372 // Save settings. 373 cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf) 374 if err != nil { 375 log.Error("Failed to load custom conf '%s': %v", setting.CustomConf, err) 376 } 377 378 cfg.Section("").Key("APP_NAME").SetValue(form.AppName) 379 cfg.Section("").Key("RUN_USER").SetValue(form.RunUser) 380 cfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath) 381 cfg.Section("").Key("RUN_MODE").SetValue("prod") 382 383 cfg.Section("database").Key("DB_TYPE").SetValue(setting.Database.Type.String()) 384 cfg.Section("database").Key("HOST").SetValue(setting.Database.Host) 385 cfg.Section("database").Key("NAME").SetValue(setting.Database.Name) 386 cfg.Section("database").Key("USER").SetValue(setting.Database.User) 387 cfg.Section("database").Key("PASSWD").SetValue(setting.Database.Passwd) 388 cfg.Section("database").Key("SCHEMA").SetValue(setting.Database.Schema) 389 cfg.Section("database").Key("SSL_MODE").SetValue(setting.Database.SSLMode) 390 cfg.Section("database").Key("PATH").SetValue(setting.Database.Path) 391 cfg.Section("database").Key("LOG_SQL").SetValue("false") // LOG_SQL is rarely helpful 392 393 cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath) 394 cfg.Section("server").Key("SSH_DOMAIN").SetValue(form.Domain) 395 cfg.Section("server").Key("DOMAIN").SetValue(form.Domain) 396 cfg.Section("server").Key("HTTP_PORT").SetValue(form.HTTPPort) 397 cfg.Section("server").Key("ROOT_URL").SetValue(form.AppURL) 398 cfg.Section("server").Key("APP_DATA_PATH").SetValue(setting.AppDataPath) 399 400 if form.SSHPort == 0 { 401 cfg.Section("server").Key("DISABLE_SSH").SetValue("true") 402 } else { 403 cfg.Section("server").Key("DISABLE_SSH").SetValue("false") 404 cfg.Section("server").Key("SSH_PORT").SetValue(fmt.Sprint(form.SSHPort)) 405 } 406 407 if form.LFSRootPath != "" { 408 cfg.Section("server").Key("LFS_START_SERVER").SetValue("true") 409 cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath) 410 var lfsJwtSecret string 411 if _, lfsJwtSecret, err = generate.NewJwtSecretWithBase64(); err != nil { 412 ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form) 413 return 414 } 415 cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret) 416 } else { 417 cfg.Section("server").Key("LFS_START_SERVER").SetValue("false") 418 } 419 420 if len(strings.TrimSpace(form.SMTPAddr)) > 0 { 421 if _, err := mail.ParseAddress(form.SMTPFrom); err != nil { 422 ctx.RenderWithErr(ctx.Tr("install.smtp_from_invalid"), tplInstall, &form) 423 return 424 } 425 426 cfg.Section("mailer").Key("ENABLED").SetValue("true") 427 cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr) 428 cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) 429 cfg.Section("mailer").Key("FROM").SetValue(form.SMTPFrom) 430 cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser) 431 cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd) 432 } else { 433 cfg.Section("mailer").Key("ENABLED").SetValue("false") 434 } 435 cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(fmt.Sprint(form.RegisterConfirm)) 436 cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(fmt.Sprint(form.MailNotify)) 437 438 cfg.Section("server").Key("OFFLINE_MODE").SetValue(fmt.Sprint(form.OfflineMode)) 439 if err := system_model.SetSettings(ctx, map[string]string{ 440 setting.Config().Picture.DisableGravatar.DynKey(): strconv.FormatBool(form.DisableGravatar), 441 setting.Config().Picture.EnableFederatedAvatar.DynKey(): strconv.FormatBool(form.EnableFederatedAvatar), 442 }); err != nil { 443 ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) 444 return 445 } 446 447 cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(fmt.Sprint(form.EnableOpenIDSignIn)) 448 cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(fmt.Sprint(form.EnableOpenIDSignUp)) 449 cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(fmt.Sprint(form.DisableRegistration)) 450 cfg.Section("service").Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").SetValue(fmt.Sprint(form.AllowOnlyExternalRegistration)) 451 cfg.Section("service").Key("ENABLE_CAPTCHA").SetValue(fmt.Sprint(form.EnableCaptcha)) 452 cfg.Section("service").Key("REQUIRE_SIGNIN_VIEW").SetValue(fmt.Sprint(form.RequireSignInView)) 453 cfg.Section("service").Key("DEFAULT_KEEP_EMAIL_PRIVATE").SetValue(fmt.Sprint(form.DefaultKeepEmailPrivate)) 454 cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").SetValue(fmt.Sprint(form.DefaultAllowCreateOrganization)) 455 cfg.Section("service").Key("DEFAULT_ENABLE_TIMETRACKING").SetValue(fmt.Sprint(form.DefaultEnableTimetracking)) 456 cfg.Section("service").Key("NO_REPLY_ADDRESS").SetValue(fmt.Sprint(form.NoReplyAddress)) 457 cfg.Section("cron.update_checker").Key("ENABLED").SetValue(fmt.Sprint(form.EnableUpdateChecker)) 458 459 cfg.Section("session").Key("PROVIDER").SetValue("file") 460 461 cfg.Section("log").Key("MODE").MustString("console") 462 cfg.Section("log").Key("LEVEL").SetValue(setting.Log.Level.String()) 463 cfg.Section("log").Key("ROOT_PATH").SetValue(form.LogRootPath) 464 465 cfg.Section("repository.pull-request").Key("DEFAULT_MERGE_STYLE").SetValue("merge") 466 467 cfg.Section("repository.signing").Key("DEFAULT_TRUST_MODEL").SetValue("committer") 468 469 cfg.Section("security").Key("INSTALL_LOCK").SetValue("true") 470 471 // the internal token could be read from INTERNAL_TOKEN or INTERNAL_TOKEN_URI (the file is guaranteed to be non-empty) 472 // if there is no InternalToken, generate one and save to security.INTERNAL_TOKEN 473 if setting.InternalToken == "" { 474 var internalToken string 475 if internalToken, err = generate.NewInternalToken(); err != nil { 476 ctx.RenderWithErr(ctx.Tr("install.internal_token_failed", err), tplInstall, &form) 477 return 478 } 479 cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(internalToken) 480 } 481 482 // if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted 483 if setting.SecretKey == "" { 484 var secretKey string 485 if secretKey, err = generate.NewSecretKey(); err != nil { 486 ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form) 487 return 488 } 489 cfg.Section("security").Key("SECRET_KEY").SetValue(secretKey) 490 } 491 492 if len(form.PasswordAlgorithm) > 0 { 493 var algorithm *hash.PasswordHashAlgorithm 494 setting.PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(form.PasswordAlgorithm) 495 if algorithm == nil { 496 ctx.RenderWithErr(ctx.Tr("install.invalid_password_algorithm"), tplInstall, &form) 497 return 498 } 499 cfg.Section("security").Key("PASSWORD_HASH_ALGO").SetValue(form.PasswordAlgorithm) 500 } 501 502 log.Info("Save settings to custom config file %s", setting.CustomConf) 503 504 err = os.MkdirAll(filepath.Dir(setting.CustomConf), os.ModePerm) 505 if err != nil { 506 ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) 507 return 508 } 509 510 setting.EnvironmentToConfig(cfg, os.Environ()) 511 512 if err = cfg.SaveTo(setting.CustomConf); err != nil { 513 ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) 514 return 515 } 516 517 // unset default engine before reload database setting 518 db.UnsetDefaultEngine() 519 520 // ---- All checks are passed 521 522 // Reload settings (and re-initialize database connection) 523 setting.InitCfgProvider(setting.CustomConf) 524 setting.LoadCommonSettings() 525 setting.MustInstalled() 526 setting.LoadDBSetting() 527 if err := common.InitDBEngine(ctx); err != nil { 528 log.Fatal("ORM engine initialization failed: %v", err) 529 } 530 531 // Create admin account 532 if len(form.AdminName) > 0 { 533 u := &user_model.User{ 534 Name: form.AdminName, 535 Email: form.AdminEmail, 536 Passwd: form.AdminPasswd, 537 IsAdmin: true, 538 } 539 overwriteDefault := &user_model.CreateUserOverwriteOptions{ 540 IsRestricted: util.OptionalBoolFalse, 541 IsActive: util.OptionalBoolTrue, 542 } 543 544 if err = user_model.CreateUser(ctx, u, overwriteDefault); err != nil { 545 if !user_model.IsErrUserAlreadyExist(err) { 546 setting.InstallLock = false 547 ctx.Data["Err_AdminName"] = true 548 ctx.Data["Err_AdminEmail"] = true 549 ctx.RenderWithErr(ctx.Tr("install.invalid_admin_setting", err), tplInstall, &form) 550 return 551 } 552 log.Info("Admin account already exist") 553 u, _ = user_model.GetUserByName(ctx, u.Name) 554 } 555 556 days := 86400 * setting.LogInRememberDays 557 ctx.SetSiteCookie(setting.CookieUserName, u.Name, days) 558 559 ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd), 560 setting.CookieRememberName, u.Name, days) 561 562 // Auto-login for admin 563 if err = ctx.Session.Set("uid", u.ID); err != nil { 564 ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) 565 return 566 } 567 if err = ctx.Session.Set("uname", u.Name); err != nil { 568 ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) 569 return 570 } 571 572 if err = ctx.Session.Release(); err != nil { 573 ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) 574 return 575 } 576 } 577 578 setting.ClearEnvConfigKeys() 579 log.Info("First-time run install finished!") 580 InstallDone(ctx) 581 582 go func() { 583 // Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js) 584 // What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Gitea in the future .... 585 time.Sleep(3 * time.Second) 586 587 // Now get the http.Server from this request and shut it down 588 // NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown 589 srv := ctx.Value(http.ServerContextKey).(*http.Server) 590 if err := srv.Shutdown(graceful.GetManager().HammerContext()); err != nil { 591 log.Error("Unable to shutdown the install server! Error: %v", err) 592 } 593 594 // After the HTTP server for "install" shuts down, the `runWeb()` will continue to run the "normal" server 595 }() 596 } 597 598 // InstallDone shows the "post-install" page, makes it easier to develop the page. 599 // The name is not called as "PostInstall" to avoid misinterpretation as a handler for "POST /install" 600 func InstallDone(ctx *context.Context) { //nolint 601 ctx.HTML(http.StatusOK, tplPostInstall) 602 }