2014-03-25 12:51:42 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2021-06-09 02:33:54 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2014-03-25 12:51:42 +04:00
2021-06-09 02:33:54 +03:00
package install
2014-03-25 12:51:42 +04:00
2014-03-28 15:26:22 +04:00
import (
2020-12-25 12:59:32 +03:00
"fmt"
2020-10-20 00:03:08 +03:00
"net/http"
2014-03-30 01:50:51 +04:00
"os"
2014-04-08 23:27:35 +04:00
"os/exec"
2015-02-05 13:12:37 +03:00
"path/filepath"
2022-10-17 02:29:26 +03:00
"strconv"
2014-03-30 01:50:51 +04:00
"strings"
2021-01-26 18:36:53 +03:00
"time"
2014-03-30 01:50:51 +04:00
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-12-01 10:50:01 +03:00
db_install "code.gitea.io/gitea/models/db/install"
2021-10-29 11:23:10 +03:00
"code.gitea.io/gitea/models/migrations"
2022-10-17 02:29:26 +03:00
system_model "code.gitea.io/gitea/models/system"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2023-02-19 10:35:20 +03:00
"code.gitea.io/gitea/modules/auth/password/hash"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
2018-02-18 21:14:37 +03:00
"code.gitea.io/gitea/modules/generate"
2019-12-15 12:51:28 +03:00
"code.gitea.io/gitea/modules/graceful"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/templates"
2021-06-01 22:12:50 +03:00
"code.gitea.io/gitea/modules/translation"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/user"
2020-11-28 05:42:08 +03:00
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2021-01-30 11:55:53 +03:00
"code.gitea.io/gitea/modules/web/middleware"
Refactor path & config system (#25330)
# The problem
There were many "path tricks":
* By default, Gitea uses its program directory as its work path
* Gitea tries to use the "work path" to guess its "custom path" and
"custom conf (app.ini)"
* Users might want to use other directories as work path
* The non-default work path should be passed to Gitea by GITEA_WORK_DIR
or "--work-path"
* But some Gitea processes are started without these values
* The "serv" process started by OpenSSH server
* The CLI sub-commands started by site admin
* The paths are guessed by SetCustomPathAndConf again and again
* The default values of "work path / custom path / custom conf" can be
changed when compiling
# The solution
* Use `InitWorkPathAndCommonConfig` to handle these path tricks, and use
test code to cover its behaviors.
* When Gitea's web server runs, write the WORK_PATH to "app.ini", this
value must be the most correct one, because if this value is not right,
users would find that the web UI doesn't work and then they should be
able to fix it.
* Then all other sub-commands can use the WORK_PATH in app.ini to
initialize their paths.
* By the way, when Gitea starts for git protocol, it shouldn't output
any log, otherwise the git protocol gets broken and client blocks
forever.
The "work path" priority is: WORK_PATH in app.ini > cmd arg --work-path
> env var GITEA_WORK_DIR > builtin default
The "app.ini" searching order is: cmd arg --config > cmd arg "work path
/ custom path" > env var "work path / custom path" > builtin default
## ⚠️ BREAKING
If your instance's "work path / custom path / custom conf" doesn't meet
the requirements (eg: work path must be absolute), Gitea will report a
fatal error and exit. You need to set these values according to the
error log.
----
Close #24818
Close #24222
Close #21606
Close #21498
Close #25107
Close #24981
Maybe close #24503
Replace #23301
Replace #22754
And maybe more
2023-06-21 08:50:26 +03:00
"code.gitea.io/gitea/routers/common"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2019-08-23 19:40:30 +03:00
2021-01-26 18:36:53 +03:00
"gitea.com/go-chi/session"
2014-03-28 15:26:22 +04:00
)
2014-06-22 21:14:03 +04:00
const (
2016-11-18 06:03:03 +03:00
// tplInstall template for installation page
2020-10-20 00:03:08 +03:00
tplInstall base . TplName = "install"
tplPostInstall base . TplName = "post-install"
2014-06-22 21:14:03 +04:00
)
2022-04-01 11:00:26 +03:00
// getSupportedDbTypeNames returns a slice for supported database types and names. The slice is used to keep the order
func getSupportedDbTypeNames ( ) ( dbTypeNames [ ] map [ string ] string ) {
for _ , t := range setting . SupportedDatabaseTypes {
dbTypeNames = append ( dbTypeNames , map [ string ] string { "type" : t , "name" : setting . DatabaseTypeNames [ t ] } )
2021-12-07 08:44:08 +03:00
}
2022-04-01 11:00:26 +03:00
return dbTypeNames
2021-12-07 08:44:08 +03:00
}
2023-05-04 09:36:34 +03:00
// Contexter prepare for rendering installation page
func Contexter ( ) func ( next http . Handler ) http . Handler {
2023-04-30 15:22:23 +03:00
rnd := templates . HTMLRenderer ( )
2022-04-01 11:00:26 +03:00
dbTypeNames := getSupportedDbTypeNames ( )
2023-07-10 01:43:37 +03:00
envConfigKeys := setting . CollectEnvConfigKeys ( )
2022-08-28 12:43:25 +03:00
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( resp http . ResponseWriter , req * http . Request ) {
2023-05-21 04:50:53 +03:00
base , baseCleanUp := context . NewBaseContext ( resp , req )
defer baseCleanUp ( )
2022-05-05 17:13:23 +03:00
2023-08-25 14:07:42 +03:00
ctx := context . NewWebContext ( base , rnd , session . GetSession ( req ) )
2023-05-23 04:29:15 +03:00
ctx . AppendContextValue ( context . WebContextKey , ctx )
2023-05-04 09:36:34 +03:00
ctx . Data . MergeFrom ( middleware . CommonTemplateContextData ( ) )
ctx . Data . MergeFrom ( middleware . ContextData {
2023-08-08 04:22:47 +03:00
"Context" : ctx , // TODO: use "ctx" in template and remove this
2023-07-10 01:43:37 +03:00
"locale" : ctx . Locale ,
"Title" : ctx . Locale . Tr ( "install.install" ) ,
"PageIsInstall" : true ,
"DbTypeNames" : dbTypeNames ,
"EnvConfigKeys" : envConfigKeys ,
"CustomConfFile" : setting . CustomConf ,
"AllLangs" : translation . AllLangs ( ) ,
2023-05-04 09:36:34 +03:00
"PasswordHashAlgorithms" : hash . RecommendedHashAlgorithms ,
} )
2022-08-28 12:43:25 +03:00
next . ServeHTTP ( resp , ctx . Req )
} )
}
2015-02-01 20:41:03 +03:00
}
2014-03-30 01:50:51 +04:00
2016-11-18 06:03:03 +03:00
// Install render installation page
2016-03-11 19:56:52 +03:00
func Install ( ctx * context . Context ) {
2023-03-04 05:12:02 +03:00
if setting . InstallLock {
InstallDone ( ctx )
return
}
2021-04-06 22:44:05 +03:00
form := forms . InstallForm { }
2015-02-01 20:41:03 +03:00
2015-07-09 08:17:48 +03:00
// Database settings
2019-08-24 12:24:45 +03:00
form . DbHost = setting . Database . Host
form . DbUser = setting . Database . User
form . DbPasswd = setting . Database . Passwd
form . DbName = setting . Database . Name
form . DbPath = setting . Database . Path
2020-01-20 18:45:14 +03:00
form . DbSchema = setting . Database . Schema
2023-07-12 01:09:23 +03:00
form . SSLMode = setting . Database . SSLMode
2015-02-01 20:41:03 +03:00
2023-03-07 13:51:06 +03:00
curDBType := setting . Database . Type . String ( )
2021-12-07 08:44:08 +03:00
var isCurDBTypeSupported bool
for _ , dbType := range setting . SupportedDatabaseTypes {
if dbType == curDBType {
isCurDBTypeSupported = true
break
2015-09-12 22:31:36 +03:00
}
2015-07-09 08:17:48 +03:00
}
2021-12-07 08:44:08 +03:00
if ! isCurDBTypeSupported {
curDBType = "mysql"
}
ctx . Data [ "CurDbType" ] = curDBType
2020-11-16 10:33:41 +03:00
2015-07-09 08:17:48 +03:00
// Application general settings
form . AppName = setting . AppName
2015-02-01 20:41:03 +03:00
form . RepoRootPath = setting . RepoRootPath
2023-06-14 06:42:38 +03:00
form . LFSRootPath = setting . LFS . Storage . Path
2015-02-01 20:41:03 +03:00
2017-06-18 03:30:04 +03:00
// Note(unknown): it's hard for Windows users change a running user,
2015-02-01 20:41:03 +03:00
// so just use current one if config says default.
if setting . IsWindows && setting . RunUser == "git" {
2015-07-31 09:50:11 +03:00
form . RunUser = user . CurrentUsername ( )
2015-02-01 20:41:03 +03:00
} else {
form . RunUser = setting . RunUser
2014-04-10 22:37:43 +04:00
}
2014-03-30 01:50:51 +04:00
2015-02-01 20:41:03 +03:00
form . Domain = setting . Domain
2016-02-28 04:48:39 +03:00
form . SSHPort = setting . SSH . Port
2016-08-12 00:55:10 +03:00
form . HTTPPort = setting . HTTPPort
2016-11-27 09:03:59 +03:00
form . AppURL = setting . AppURL
2023-02-19 19:12:01 +03:00
form . LogRootPath = setting . Log . RootPath
2015-02-01 20:41:03 +03:00
2015-07-09 08:17:48 +03:00
// E-mail service settings
if setting . MailService != nil {
Rework mailer settings (#18982)
* `PROTOCOL`: can be smtp, smtps, smtp+startls, smtp+unix, sendmail, dummy
* `SMTP_ADDR`: domain for SMTP, or path to unix socket
* `SMTP_PORT`: port for SMTP; defaults to 25 for `smtp`, 465 for `smtps`, and 587 for `smtp+startls`
* `ENABLE_HELO`, `HELO_HOSTNAME`: reverse `DISABLE_HELO` to `ENABLE_HELO`; default to false + system hostname
* `FORCE_TRUST_SERVER_CERT`: replace the unclear `SKIP_VERIFY`
* `CLIENT_CERT_FILE`, `CLIENT_KEY_FILE`, `USE_CLIENT_CERT`: clarify client certificates here
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-08-02 08:24:18 +03:00
form . SMTPAddr = setting . MailService . SMTPAddr
form . SMTPPort = setting . MailService . SMTPPort
2015-07-09 11:10:31 +03:00
form . SMTPFrom = setting . MailService . From
2017-02-24 04:37:13 +03:00
form . SMTPUser = setting . MailService . User
2022-05-02 11:45:23 +03:00
form . SMTPPasswd = setting . MailService . Passwd
2014-04-27 08:34:48 +04:00
}
2015-07-09 08:17:48 +03:00
form . RegisterConfirm = setting . Service . RegisterEmailConfirm
form . MailNotify = setting . Service . EnableNotifyMail
// Server and other services settings
form . OfflineMode = setting . OfflineMode
2023-01-03 23:33:41 +03:00
form . DisableGravatar = setting . DisableGravatar // when installing, there is no database connection so that given a default value
form . EnableFederatedAvatar = setting . EnableFederatedAvatar // when installing, there is no database connection so that given a default value
2022-10-17 02:29:26 +03:00
2017-11-29 15:47:42 +03:00
form . EnableOpenIDSignIn = setting . Service . EnableOpenIDSignIn
form . EnableOpenIDSignUp = setting . Service . EnableOpenIDSignUp
2015-07-09 08:17:48 +03:00
form . DisableRegistration = setting . Service . DisableRegistration
2018-05-13 10:51:16 +03:00
form . AllowOnlyExternalRegistration = setting . Service . AllowOnlyExternalRegistration
2015-09-13 19:14:32 +03:00
form . EnableCaptcha = setting . Service . EnableCaptcha
2015-07-09 08:17:48 +03:00
form . RequireSignInView = setting . Service . RequireSignInView
2017-01-08 06:12:03 +03:00
form . DefaultKeepEmailPrivate = setting . Service . DefaultKeepEmailPrivate
2017-05-08 22:51:53 +03:00
form . DefaultAllowCreateOrganization = setting . Service . DefaultAllowCreateOrganization
2017-09-12 09:48:13 +03:00
form . DefaultEnableTimetracking = setting . Service . DefaultEnableTimetracking
2017-01-08 06:12:03 +03:00
form . NoReplyAddress = setting . Service . NoReplyAddress
2023-03-04 05:12:02 +03:00
form . PasswordAlgorithm = hash . ConfigHashAlgorithm ( setting . PasswordHashAlgo )
2014-04-27 08:34:48 +04:00
2021-01-30 11:55:53 +03:00
middleware . AssignForm ( form , ctx . Data )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplInstall )
2014-04-10 22:37:43 +04:00
}
2021-12-01 10:50:01 +03:00
func checkDatabase ( ctx * context . Context , form * forms . InstallForm ) bool {
var err error
if ( setting . Database . Type == "sqlite3" ) &&
len ( setting . Database . Path ) == 0 {
ctx . Data [ "Err_DbPath" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.err_empty_db_path" ) , tplInstall , form )
return false
}
// Check if the user is trying to re-install in an installed database
db . UnsetDefaultEngine ( )
defer db . UnsetDefaultEngine ( )
if err = db . InitEngine ( ctx ) ; err != nil {
if strings . Contains ( err . Error ( ) , ` Unknown database type: sqlite3 ` ) {
ctx . Data [ "Err_DbType" ] = true
2023-08-27 14:59:12 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.sqlite3_not_available" , "https://docs.gitea.com/installation/install-from-binary" ) , tplInstall , form )
2021-12-01 10:50:01 +03:00
} else {
ctx . Data [ "Err_DbSetting" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_db_setting" , err ) , tplInstall , form )
}
return false
}
err = db_install . CheckDatabaseConnection ( )
if err != nil {
ctx . Data [ "Err_DbSetting" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_db_setting" , err ) , tplInstall , form )
return false
}
hasPostInstallationUser , err := db_install . HasPostInstallationUsers ( )
if err != nil {
ctx . Data [ "Err_DbSetting" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_db_table" , "user" , err ) , tplInstall , form )
return false
}
dbMigrationVersion , err := db_install . GetMigrationVersion ( )
if err != nil {
ctx . Data [ "Err_DbSetting" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_db_table" , "version" , err ) , tplInstall , form )
return false
}
if hasPostInstallationUser && dbMigrationVersion > 0 {
log . Error ( "The database is likely to have been used by Gitea before, database migration version=%d" , dbMigrationVersion )
confirmed := form . ReinstallConfirmFirst && form . ReinstallConfirmSecond && form . ReinstallConfirmThird
if ! confirmed {
ctx . Data [ "Err_DbInstalledBefore" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.reinstall_error" ) , tplInstall , form )
return false
}
2023-07-10 01:43:37 +03:00
log . Info ( "User confirmed re-installation of Gitea into a pre-existing database" )
2021-12-01 10:50:01 +03:00
}
if hasPostInstallationUser || dbMigrationVersion > 0 {
log . Info ( "Gitea will be installed in a database with: hasPostInstallationUser=%v, dbMigrationVersion=%v" , hasPostInstallationUser , dbMigrationVersion )
}
return true
}
2021-06-09 02:33:54 +03:00
// SubmitInstall response for submit install items
func SubmitInstall ( ctx * context . Context ) {
2023-03-04 05:12:02 +03:00
if setting . InstallLock {
InstallDone ( ctx )
return
}
2016-12-20 15:32:02 +03:00
var err error
2021-12-01 10:50:01 +03:00
form := * web . GetForm ( ctx ) . ( * forms . InstallForm )
// fix form values
if form . AppURL != "" && form . AppURL [ len ( form . AppURL ) - 1 ] != '/' {
form . AppURL += "/"
}
2021-12-07 08:44:08 +03:00
ctx . Data [ "CurDbType" ] = form . DbType
2014-04-27 08:34:48 +04:00
2014-03-30 01:50:51 +04:00
if ctx . HasError ( ) {
2023-05-21 04:50:53 +03:00
ctx . Data [ "Err_SMTP" ] = ctx . Data [ "Err_SMTPUser" ] != nil
ctx . Data [ "Err_Admin" ] = ctx . Data [ "Err_AdminName" ] != nil || ctx . Data [ "Err_AdminPasswd" ] != nil || ctx . Data [ "Err_AdminEmail" ] != nil
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplInstall )
2014-03-30 01:50:51 +04:00
return
}
2016-12-20 15:32:02 +03:00
if _ , err = exec . LookPath ( "git" ) ; err != nil {
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.test_git_failed" , err ) , tplInstall , & form )
2014-04-08 23:27:35 +04:00
return
}
2021-12-01 10:50:01 +03:00
// ---- Basic checks are passed, now test configuration.
2019-08-24 12:24:45 +03:00
2021-12-01 10:50:01 +03:00
// Test database setting.
2023-03-07 13:51:06 +03:00
setting . Database . Type = setting . DatabaseType ( form . DbType )
2019-08-24 12:24:45 +03:00
setting . Database . Host = form . DbHost
setting . Database . User = form . DbUser
setting . Database . Passwd = form . DbPasswd
setting . Database . Name = form . DbName
2020-01-20 18:45:14 +03:00
setting . Database . Schema = form . DbSchema
2019-08-24 12:24:45 +03:00
setting . Database . SSLMode = form . SSLMode
setting . Database . Path = form . DbPath
2021-11-07 06:11:27 +03:00
setting . Database . LogSQL = ! setting . IsProd
2021-02-17 01:37:20 +03:00
2021-12-01 10:50:01 +03:00
if ! checkDatabase ( ctx , & form ) {
2015-09-12 22:31:36 +03:00
return
2015-07-08 14:47:56 +03:00
}
2021-12-01 10:50:01 +03:00
// Prepare AppDataPath, it is very important for Gitea
if err = setting . PrepareAppDataPath ( ) ; err != nil {
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_app_data_path" , err ) , tplInstall , & form )
2014-03-30 01:50:51 +04:00
return
}
// Test repository root path.
2020-10-11 23:27:20 +03:00
form . RepoRootPath = strings . ReplaceAll ( form . RepoRootPath , "\\" , "/" )
2016-12-20 15:32:02 +03:00
if err = os . MkdirAll ( form . RepoRootPath , os . ModePerm ) ; err != nil {
2014-09-15 03:22:52 +04:00
ctx . Data [ "Err_RepoRootPath" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_repo_path" , err ) , tplInstall , & form )
2014-03-30 01:50:51 +04:00
return
}
2016-12-26 04:16:37 +03:00
// Test LFS root path if not empty, empty meaning disable LFS
if form . LFSRootPath != "" {
2020-10-11 23:27:20 +03:00
form . LFSRootPath = strings . ReplaceAll ( form . LFSRootPath , "\\" , "/" )
2016-12-26 04:16:37 +03:00
if err := os . MkdirAll ( form . LFSRootPath , os . ModePerm ) ; err != nil {
ctx . Data [ "Err_LFSRootPath" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_lfs_path" , err ) , tplInstall , & form )
return
}
}
2016-02-12 17:19:45 +03:00
// Test log root path.
2020-10-11 23:27:20 +03:00
form . LogRootPath = strings . ReplaceAll ( form . LogRootPath , "\\" , "/" )
2016-12-20 15:32:02 +03:00
if err = os . MkdirAll ( form . LogRootPath , os . ModePerm ) ; err != nil {
2016-02-12 17:19:45 +03:00
ctx . Data [ "Err_LogRootPath" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_log_root_path" , err ) , tplInstall , & form )
2016-02-12 17:19:45 +03:00
return
}
2016-08-10 03:41:18 +03:00
currentUser , match := setting . IsRunUserMatchCurrentUser ( form . RunUser )
if ! match {
2014-09-15 03:22:52 +04:00
ctx . Data [ "Err_RunUser" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.run_user_not_match" , form . RunUser , currentUser ) , tplInstall , & form )
2014-09-08 03:02:58 +04:00
return
}
2015-09-12 22:31:36 +03:00
// Check logic loophole between disable self-registration and no admin account.
if form . DisableRegistration && len ( form . AdminName ) == 0 {
ctx . Data [ "Err_Services" ] = true
ctx . Data [ "Err_Admin" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.no_admin_and_disable_registration" ) , tplInstall , form )
2015-09-12 22:31:36 +03:00
return
}
2019-05-28 09:18:40 +03:00
// Check admin user creation
if len ( form . AdminName ) > 0 {
// Ensure AdminName is valid
2021-11-24 12:49:20 +03:00
if err := user_model . IsUsableUsername ( form . AdminName ) ; err != nil {
2019-05-28 09:18:40 +03:00
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminName" ] = true
2021-11-24 12:49:20 +03:00
if db . IsErrNameReserved ( err ) {
2019-05-28 09:18:40 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.err_admin_name_is_reserved" ) , tplInstall , form )
return
2021-11-24 12:49:20 +03:00
} else if db . IsErrNamePatternNotAllowed ( err ) {
2019-05-28 09:18:40 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.err_admin_name_pattern_not_allowed" ) , tplInstall , form )
return
}
ctx . RenderWithErr ( ctx . Tr ( "install.err_admin_name_is_invalid" ) , tplInstall , form )
return
}
// Check Admin email
if len ( form . AdminEmail ) == 0 {
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminEmail" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.err_empty_admin_email" ) , tplInstall , form )
return
}
// Check admin password.
if len ( form . AdminPasswd ) == 0 {
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminPasswd" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.err_empty_admin_password" ) , tplInstall , form )
return
}
if form . AdminPasswd != form . AdminConfirmPasswd {
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminPasswd" ] = true
ctx . RenderWithErr ( ctx . Tr ( "form.password_not_match" ) , tplInstall , form )
return
}
2014-03-30 19:58:21 +04:00
}
2021-12-01 10:50:01 +03:00
// Init the engine with migration
if err = db . InitEngineWithMigration ( ctx , migrations . Migrate ) ; err != nil {
db . UnsetDefaultEngine ( )
ctx . Data [ "Err_DbSetting" ] = true
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_db_setting" , err ) , tplInstall , & form )
return
2015-02-01 20:41:03 +03:00
}
2014-03-30 01:50:51 +04:00
// Save settings.
Refactor path & config system (#25330)
# The problem
There were many "path tricks":
* By default, Gitea uses its program directory as its work path
* Gitea tries to use the "work path" to guess its "custom path" and
"custom conf (app.ini)"
* Users might want to use other directories as work path
* The non-default work path should be passed to Gitea by GITEA_WORK_DIR
or "--work-path"
* But some Gitea processes are started without these values
* The "serv" process started by OpenSSH server
* The CLI sub-commands started by site admin
* The paths are guessed by SetCustomPathAndConf again and again
* The default values of "work path / custom path / custom conf" can be
changed when compiling
# The solution
* Use `InitWorkPathAndCommonConfig` to handle these path tricks, and use
test code to cover its behaviors.
* When Gitea's web server runs, write the WORK_PATH to "app.ini", this
value must be the most correct one, because if this value is not right,
users would find that the web UI doesn't work and then they should be
able to fix it.
* Then all other sub-commands can use the WORK_PATH in app.ini to
initialize their paths.
* By the way, when Gitea starts for git protocol, it shouldn't output
any log, otherwise the git protocol gets broken and client blocks
forever.
The "work path" priority is: WORK_PATH in app.ini > cmd arg --work-path
> env var GITEA_WORK_DIR > builtin default
The "app.ini" searching order is: cmd arg --config > cmd arg "work path
/ custom path" > env var "work path / custom path" > builtin default
## ⚠️ BREAKING
If your instance's "work path / custom path / custom conf" doesn't meet
the requirements (eg: work path must be absolute), Gitea will report a
fatal error and exit. You need to set these values according to the
error log.
----
Close #24818
Close #24222
Close #21606
Close #21498
Close #25107
Close #24981
Maybe close #24503
Replace #23301
Replace #22754
And maybe more
2023-06-21 08:50:26 +03:00
cfg , err := setting . NewConfigProviderFromFile ( setting . CustomConf )
2020-11-28 05:42:08 +03:00
if err != nil {
2023-06-02 12:27:30 +03:00
log . Error ( "Failed to load custom conf '%s': %v" , setting . CustomConf , err )
2015-02-14 00:48:23 +03:00
}
2023-06-02 12:27:30 +03:00
Refactor path & config system (#25330)
# The problem
There were many "path tricks":
* By default, Gitea uses its program directory as its work path
* Gitea tries to use the "work path" to guess its "custom path" and
"custom conf (app.ini)"
* Users might want to use other directories as work path
* The non-default work path should be passed to Gitea by GITEA_WORK_DIR
or "--work-path"
* But some Gitea processes are started without these values
* The "serv" process started by OpenSSH server
* The CLI sub-commands started by site admin
* The paths are guessed by SetCustomPathAndConf again and again
* The default values of "work path / custom path / custom conf" can be
changed when compiling
# The solution
* Use `InitWorkPathAndCommonConfig` to handle these path tricks, and use
test code to cover its behaviors.
* When Gitea's web server runs, write the WORK_PATH to "app.ini", this
value must be the most correct one, because if this value is not right,
users would find that the web UI doesn't work and then they should be
able to fix it.
* Then all other sub-commands can use the WORK_PATH in app.ini to
initialize their paths.
* By the way, when Gitea starts for git protocol, it shouldn't output
any log, otherwise the git protocol gets broken and client blocks
forever.
The "work path" priority is: WORK_PATH in app.ini > cmd arg --work-path
> env var GITEA_WORK_DIR > builtin default
The "app.ini" searching order is: cmd arg --config > cmd arg "work path
/ custom path" > env var "work path / custom path" > builtin default
## ⚠️ BREAKING
If your instance's "work path / custom path / custom conf" doesn't meet
the requirements (eg: work path must be absolute), Gitea will report a
fatal error and exit. You need to set these values according to the
error log.
----
Close #24818
Close #24222
Close #21606
Close #21498
Close #25107
Close #24981
Maybe close #24503
Replace #23301
Replace #22754
And maybe more
2023-06-21 08:50:26 +03:00
cfg . Section ( "" ) . Key ( "APP_NAME" ) . SetValue ( form . AppName )
cfg . Section ( "" ) . Key ( "RUN_USER" ) . SetValue ( form . RunUser )
cfg . Section ( "" ) . Key ( "WORK_PATH" ) . SetValue ( setting . AppWorkPath )
cfg . Section ( "" ) . Key ( "RUN_MODE" ) . SetValue ( "prod" )
2023-03-07 13:51:06 +03:00
cfg . Section ( "database" ) . Key ( "DB_TYPE" ) . SetValue ( setting . Database . Type . String ( ) )
2019-08-24 12:24:45 +03:00
cfg . Section ( "database" ) . Key ( "HOST" ) . SetValue ( setting . Database . Host )
cfg . Section ( "database" ) . Key ( "NAME" ) . SetValue ( setting . Database . Name )
cfg . Section ( "database" ) . Key ( "USER" ) . SetValue ( setting . Database . User )
cfg . Section ( "database" ) . Key ( "PASSWD" ) . SetValue ( setting . Database . Passwd )
2020-01-20 18:45:14 +03:00
cfg . Section ( "database" ) . Key ( "SCHEMA" ) . SetValue ( setting . Database . Schema )
2019-08-24 12:24:45 +03:00
cfg . Section ( "database" ) . Key ( "SSL_MODE" ) . SetValue ( setting . Database . SSLMode )
cfg . Section ( "database" ) . Key ( "PATH" ) . SetValue ( setting . Database . Path )
2020-10-10 18:19:50 +03:00
cfg . Section ( "database" ) . Key ( "LOG_SQL" ) . SetValue ( "false" ) // LOG_SQL is rarely helpful
2015-02-01 22:39:58 +03:00
cfg . Section ( "repository" ) . Key ( "ROOT" ) . SetValue ( form . RepoRootPath )
2016-12-27 10:34:34 +03:00
cfg . Section ( "server" ) . Key ( "SSH_DOMAIN" ) . SetValue ( form . Domain )
2017-04-21 05:43:29 +03:00
cfg . Section ( "server" ) . Key ( "DOMAIN" ) . SetValue ( form . Domain )
2015-02-01 22:39:58 +03:00
cfg . Section ( "server" ) . Key ( "HTTP_PORT" ) . SetValue ( form . HTTPPort )
2016-11-27 09:03:59 +03:00
cfg . Section ( "server" ) . Key ( "ROOT_URL" ) . SetValue ( form . AppURL )
2023-06-18 16:57:43 +03:00
cfg . Section ( "server" ) . Key ( "APP_DATA_PATH" ) . SetValue ( setting . AppDataPath )
2014-03-30 01:50:51 +04:00
2015-08-19 15:36:19 +03:00
if form . SSHPort == 0 {
cfg . Section ( "server" ) . Key ( "DISABLE_SSH" ) . SetValue ( "true" )
} else {
cfg . Section ( "server" ) . Key ( "DISABLE_SSH" ) . SetValue ( "false" )
2020-12-25 12:59:32 +03:00
cfg . Section ( "server" ) . Key ( "SSH_PORT" ) . SetValue ( fmt . Sprint ( form . SSHPort ) )
2015-08-19 15:36:19 +03:00
}
2016-12-26 04:16:37 +03:00
if form . LFSRootPath != "" {
cfg . Section ( "server" ) . Key ( "LFS_START_SERVER" ) . SetValue ( "true" )
2022-01-23 22:02:29 +03:00
cfg . Section ( "lfs" ) . Key ( "PATH" ) . SetValue ( form . LFSRootPath )
2021-12-01 10:50:01 +03:00
var lfsJwtSecret string
2023-08-14 13:30:16 +03:00
if _ , lfsJwtSecret , err = generate . NewJwtSecretBase64 ( ) ; err != nil {
2018-02-18 21:14:37 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.lfs_jwt_secret_failed" , err ) , tplInstall , & form )
return
}
2021-12-01 10:50:01 +03:00
cfg . Section ( "server" ) . Key ( "LFS_JWT_SECRET" ) . SetValue ( lfsJwtSecret )
2016-12-26 04:16:37 +03:00
} else {
cfg . Section ( "server" ) . Key ( "LFS_START_SERVER" ) . SetValue ( "false" )
}
Rework mailer settings (#18982)
* `PROTOCOL`: can be smtp, smtps, smtp+startls, smtp+unix, sendmail, dummy
* `SMTP_ADDR`: domain for SMTP, or path to unix socket
* `SMTP_PORT`: port for SMTP; defaults to 25 for `smtp`, 465 for `smtps`, and 587 for `smtp+startls`
* `ENABLE_HELO`, `HELO_HOSTNAME`: reverse `DISABLE_HELO` to `ENABLE_HELO`; default to false + system hostname
* `FORCE_TRUST_SERVER_CERT`: replace the unclear `SKIP_VERIFY`
* `CLIENT_CERT_FILE`, `CLIENT_KEY_FILE`, `USE_CLIENT_CERT`: clarify client certificates here
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-08-02 08:24:18 +03:00
if len ( strings . TrimSpace ( form . SMTPAddr ) ) > 0 {
2015-02-01 22:39:58 +03:00
cfg . Section ( "mailer" ) . Key ( "ENABLED" ) . SetValue ( "true" )
Rework mailer settings (#18982)
* `PROTOCOL`: can be smtp, smtps, smtp+startls, smtp+unix, sendmail, dummy
* `SMTP_ADDR`: domain for SMTP, or path to unix socket
* `SMTP_PORT`: port for SMTP; defaults to 25 for `smtp`, 465 for `smtps`, and 587 for `smtp+startls`
* `ENABLE_HELO`, `HELO_HOSTNAME`: reverse `DISABLE_HELO` to `ENABLE_HELO`; default to false + system hostname
* `FORCE_TRUST_SERVER_CERT`: replace the unclear `SKIP_VERIFY`
* `CLIENT_CERT_FILE`, `CLIENT_KEY_FILE`, `USE_CLIENT_CERT`: clarify client certificates here
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-08-02 08:24:18 +03:00
cfg . Section ( "mailer" ) . Key ( "SMTP_ADDR" ) . SetValue ( form . SMTPAddr )
cfg . Section ( "mailer" ) . Key ( "SMTP_PORT" ) . SetValue ( form . SMTPPort )
2015-07-09 11:10:31 +03:00
cfg . Section ( "mailer" ) . Key ( "FROM" ) . SetValue ( form . SMTPFrom )
2017-02-24 04:37:13 +03:00
cfg . Section ( "mailer" ) . Key ( "USER" ) . SetValue ( form . SMTPUser )
2015-02-01 22:39:58 +03:00
cfg . Section ( "mailer" ) . Key ( "PASSWD" ) . SetValue ( form . SMTPPasswd )
2015-07-09 08:17:48 +03:00
} else {
cfg . Section ( "mailer" ) . Key ( "ENABLED" ) . SetValue ( "false" )
2014-03-30 01:50:51 +04:00
}
2020-12-25 12:59:32 +03:00
cfg . Section ( "service" ) . Key ( "REGISTER_EMAIL_CONFIRM" ) . SetValue ( fmt . Sprint ( form . RegisterConfirm ) )
cfg . Section ( "service" ) . Key ( "ENABLE_NOTIFY_MAIL" ) . SetValue ( fmt . Sprint ( form . MailNotify ) )
cfg . Section ( "server" ) . Key ( "OFFLINE_MODE" ) . SetValue ( fmt . Sprint ( form . OfflineMode ) )
2023-10-05 04:08:19 +03:00
if err := system_model . SetSettings ( ctx , map [ string ] string {
setting . Config ( ) . Picture . DisableGravatar . DynKey ( ) : strconv . FormatBool ( form . DisableGravatar ) ,
setting . Config ( ) . Picture . EnableFederatedAvatar . DynKey ( ) : strconv . FormatBool ( form . EnableFederatedAvatar ) ,
} ) ; err != nil {
2023-01-03 23:33:41 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2022-10-17 02:29:26 +03:00
return
}
2023-10-05 04:08:19 +03:00
2020-12-25 12:59:32 +03:00
cfg . Section ( "openid" ) . Key ( "ENABLE_OPENID_SIGNIN" ) . SetValue ( fmt . Sprint ( form . EnableOpenIDSignIn ) )
cfg . Section ( "openid" ) . Key ( "ENABLE_OPENID_SIGNUP" ) . SetValue ( fmt . Sprint ( form . EnableOpenIDSignUp ) )
cfg . Section ( "service" ) . Key ( "DISABLE_REGISTRATION" ) . SetValue ( fmt . Sprint ( form . DisableRegistration ) )
cfg . Section ( "service" ) . Key ( "ALLOW_ONLY_EXTERNAL_REGISTRATION" ) . SetValue ( fmt . Sprint ( form . AllowOnlyExternalRegistration ) )
cfg . Section ( "service" ) . Key ( "ENABLE_CAPTCHA" ) . SetValue ( fmt . Sprint ( form . EnableCaptcha ) )
cfg . Section ( "service" ) . Key ( "REQUIRE_SIGNIN_VIEW" ) . SetValue ( fmt . Sprint ( form . RequireSignInView ) )
cfg . Section ( "service" ) . Key ( "DEFAULT_KEEP_EMAIL_PRIVATE" ) . SetValue ( fmt . Sprint ( form . DefaultKeepEmailPrivate ) )
cfg . Section ( "service" ) . Key ( "DEFAULT_ALLOW_CREATE_ORGANIZATION" ) . SetValue ( fmt . Sprint ( form . DefaultAllowCreateOrganization ) )
cfg . Section ( "service" ) . Key ( "DEFAULT_ENABLE_TIMETRACKING" ) . SetValue ( fmt . Sprint ( form . DefaultEnableTimetracking ) )
cfg . Section ( "service" ) . Key ( "NO_REPLY_ADDRESS" ) . SetValue ( fmt . Sprint ( form . NoReplyAddress ) )
2022-11-01 22:23:56 +03:00
cfg . Section ( "cron.update_checker" ) . Key ( "ENABLED" ) . SetValue ( fmt . Sprint ( form . EnableUpdateChecker ) )
2014-03-30 01:50:51 +04:00
2015-02-01 22:39:58 +03:00
cfg . Section ( "session" ) . Key ( "PROVIDER" ) . SetValue ( "file" )
2014-12-21 06:51:16 +03:00
2023-06-12 13:52:49 +03:00
cfg . Section ( "log" ) . Key ( "MODE" ) . MustString ( "console" )
2023-02-19 19:12:01 +03:00
cfg . Section ( "log" ) . Key ( "LEVEL" ) . SetValue ( setting . Log . Level . String ( ) )
2016-02-12 17:19:45 +03:00
cfg . Section ( "log" ) . Key ( "ROOT_PATH" ) . SetValue ( form . LogRootPath )
2014-08-27 12:39:36 +04:00
2022-06-03 06:45:54 +03:00
cfg . Section ( "repository.pull-request" ) . Key ( "DEFAULT_MERGE_STYLE" ) . SetValue ( "merge" )
2022-01-20 05:41:59 +03:00
cfg . Section ( "repository.signing" ) . Key ( "DEFAULT_TRUST_MODEL" ) . SetValue ( "committer" )
2015-02-01 22:39:58 +03:00
cfg . Section ( "security" ) . Key ( "INSTALL_LOCK" ) . SetValue ( "true" )
2021-12-01 10:50:01 +03:00
2022-11-03 23:55:09 +03:00
// the internal token could be read from INTERNAL_TOKEN or INTERNAL_TOKEN_URI (the file is guaranteed to be non-empty)
// if there is no InternalToken, generate one and save to security.INTERNAL_TOKEN
if setting . InternalToken == "" {
var internalToken string
if internalToken , err = generate . NewInternalToken ( ) ; err != nil {
ctx . RenderWithErr ( ctx . Tr ( "install.internal_token_failed" , err ) , tplInstall , & form )
return
}
cfg . Section ( "security" ) . Key ( "INTERNAL_TOKEN" ) . SetValue ( internalToken )
2016-12-20 15:32:02 +03:00
}
2021-12-01 10:50:01 +03:00
// if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted
if setting . SecretKey == "" {
var secretKey string
if secretKey , err = generate . NewSecretKey ( ) ; err != nil {
ctx . RenderWithErr ( ctx . Tr ( "install.secret_key_failed" , err ) , tplInstall , & form )
return
}
cfg . Section ( "security" ) . Key ( "SECRET_KEY" ) . SetValue ( secretKey )
}
2021-02-17 01:37:20 +03:00
if len ( form . PasswordAlgorithm ) > 0 {
2023-03-04 05:12:02 +03:00
var algorithm * hash . PasswordHashAlgorithm
setting . PasswordHashAlgo , algorithm = hash . SetDefaultPasswordHashAlgorithm ( form . PasswordAlgorithm )
if algorithm == nil {
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_password_algorithm" ) , tplInstall , & form )
return
}
2021-02-17 01:37:20 +03:00
cfg . Section ( "security" ) . Key ( "PASSWORD_HASH_ALGO" ) . SetValue ( form . PasswordAlgorithm )
}
2014-03-30 01:50:51 +04:00
2021-12-01 10:50:01 +03:00
log . Info ( "Save settings to custom config file %s" , setting . CustomConf )
2016-12-20 15:32:02 +03:00
err = os . MkdirAll ( filepath . Dir ( setting . CustomConf ) , os . ModePerm )
2016-11-10 13:02:01 +03:00
if err != nil {
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2016-11-10 13:02:01 +03:00
return
}
2023-07-10 01:43:37 +03:00
setting . EnvironmentToConfig ( cfg , os . Environ ( ) )
2016-12-20 15:32:02 +03:00
if err = cfg . SaveTo ( setting . CustomConf ) ; err != nil {
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2014-03-30 01:50:51 +04:00
return
}
2022-10-18 18:16:58 +03:00
// unset default engine before reload database setting
db . UnsetDefaultEngine ( )
2021-12-01 10:50:01 +03:00
// ---- All checks are passed
// Reload settings (and re-initialize database connection)
Refactor path & config system (#25330)
# The problem
There were many "path tricks":
* By default, Gitea uses its program directory as its work path
* Gitea tries to use the "work path" to guess its "custom path" and
"custom conf (app.ini)"
* Users might want to use other directories as work path
* The non-default work path should be passed to Gitea by GITEA_WORK_DIR
or "--work-path"
* But some Gitea processes are started without these values
* The "serv" process started by OpenSSH server
* The CLI sub-commands started by site admin
* The paths are guessed by SetCustomPathAndConf again and again
* The default values of "work path / custom path / custom conf" can be
changed when compiling
# The solution
* Use `InitWorkPathAndCommonConfig` to handle these path tricks, and use
test code to cover its behaviors.
* When Gitea's web server runs, write the WORK_PATH to "app.ini", this
value must be the most correct one, because if this value is not right,
users would find that the web UI doesn't work and then they should be
able to fix it.
* Then all other sub-commands can use the WORK_PATH in app.ini to
initialize their paths.
* By the way, when Gitea starts for git protocol, it shouldn't output
any log, otherwise the git protocol gets broken and client blocks
forever.
The "work path" priority is: WORK_PATH in app.ini > cmd arg --work-path
> env var GITEA_WORK_DIR > builtin default
The "app.ini" searching order is: cmd arg --config > cmd arg "work path
/ custom path" > env var "work path / custom path" > builtin default
## ⚠️ BREAKING
If your instance's "work path / custom path / custom conf" doesn't meet
the requirements (eg: work path must be absolute), Gitea will report a
fatal error and exit. You need to set these values according to the
error log.
----
Close #24818
Close #24222
Close #21606
Close #21498
Close #25107
Close #24981
Maybe close #24503
Replace #23301
Replace #22754
And maybe more
2023-06-21 08:50:26 +03:00
setting . InitCfgProvider ( setting . CustomConf )
setting . LoadCommonSettings ( )
setting . MustInstalled ( )
setting . LoadDBSetting ( )
if err := common . InitDBEngine ( ctx ) ; err != nil {
log . Fatal ( "ORM engine initialization failed: %v" , err )
}
2014-03-30 01:50:51 +04:00
2015-12-08 08:59:14 +03:00
// Create admin account
2015-07-08 14:47:56 +03:00
if len ( form . AdminName ) > 0 {
2021-11-24 12:49:20 +03:00
u := & user_model . User {
2022-04-29 22:38:11 +03:00
Name : form . AdminName ,
Email : form . AdminEmail ,
Passwd : form . AdminPasswd ,
IsAdmin : true ,
2015-12-08 08:59:14 +03:00
}
2022-04-29 22:38:11 +03:00
overwriteDefault := & user_model . CreateUserOverwriteOptions {
IsRestricted : util . OptionalBoolFalse ,
IsActive : util . OptionalBoolTrue ,
}
2023-09-14 20:09:32 +03:00
if err = user_model . CreateUser ( ctx , u , overwriteDefault ) ; err != nil {
2021-11-24 12:49:20 +03:00
if ! user_model . IsErrUserAlreadyExist ( err ) {
2015-07-08 14:47:56 +03:00
setting . InstallLock = false
ctx . Data [ "Err_AdminName" ] = true
ctx . Data [ "Err_AdminEmail" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.invalid_admin_setting" , err ) , tplInstall , & form )
2015-07-08 14:47:56 +03:00
return
}
log . Info ( "Admin account already exist" )
2022-05-20 17:08:52 +03:00
u , _ = user_model . GetUserByName ( ctx , u . Name )
2014-03-30 19:09:59 +04:00
}
2015-12-08 08:59:14 +03:00
2023-11-22 19:26:21 +03:00
if err := ctx . SetLTACookie ( u ) ; err != nil {
ctx . RenderWithErr ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2023-10-14 03:56:41 +03:00
return
}
2021-03-07 11:12:43 +03:00
2015-12-08 08:59:14 +03:00
// Auto-login for admin
2016-12-20 15:32:02 +03:00
if err = ctx . Session . Set ( "uid" , u . ID ) ; err != nil {
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2016-11-10 13:02:01 +03:00
return
}
2020-05-17 15:43:29 +03:00
if err = ctx . Session . Release ( ) ; err != nil {
ctx . RenderWithErr ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
return
}
2014-03-30 19:09:59 +04:00
}
2023-07-10 01:43:37 +03:00
setting . ClearEnvConfigKeys ( )
2014-03-30 01:50:51 +04:00
log . Info ( "First-time run install finished!" )
2023-03-04 05:12:02 +03:00
InstallDone ( ctx )
2020-10-20 00:03:08 +03:00
go func ( ) {
2023-03-04 05:12:02 +03:00
// Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js)
// 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 ....
time . Sleep ( 3 * time . Second )
// Now get the http.Server from this request and shut it down
// NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown
srv := ctx . Value ( http . ServerContextKey ) . ( * http . Server )
2020-10-20 00:03:08 +03:00
if err := srv . Shutdown ( graceful . GetManager ( ) . HammerContext ( ) ) ; err != nil {
log . Error ( "Unable to shutdown the install server! Error: %v" , err )
}
2023-03-04 05:12:02 +03:00
// After the HTTP server for "install" shuts down, the `runWeb()` will continue to run the "normal" server
2020-10-20 00:03:08 +03:00
} ( )
2014-03-25 12:51:42 +04:00
}
2023-03-04 05:12:02 +03:00
// InstallDone shows the "post-install" page, makes it easier to develop the page.
// The name is not called as "PostInstall" to avoid misinterpretation as a handler for "POST /install"
func InstallDone ( ctx * context . Context ) { //nolint
ctx . HTML ( http . StatusOK , tplPostInstall )
}