2014-03-25 16:51:42 +08:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2021-06-09 07:33:54 +08:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2014-03-25 16:51:42 +08:00
2021-06-09 07:33:54 +08:00
package install
2014-03-25 16:51:42 +08:00
2014-03-28 07:26:22 -04:00
import (
2020-10-19 22:03:08 +01:00
"net/http"
2024-02-24 00:02:14 +01:00
"net/mail"
2014-03-29 17:50:51 -04:00
"os"
2014-04-08 15:27:35 -04:00
"os/exec"
2015-02-05 12:12:37 +02:00
"path/filepath"
2025-06-18 03:48:09 +02:00
"slices"
2022-10-17 07:29:26 +08:00
"strconv"
2014-03-29 17:50:51 -04:00
"strings"
2021-01-26 23:36:53 +08:00
"time"
2014-03-29 17:50:51 -04:00
2021-09-19 19:49:59 +08:00
"code.gitea.io/gitea/models/db"
2021-12-01 15:50:01 +08:00
db_install "code.gitea.io/gitea/models/db/install"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2023-02-19 07:35:20 +00:00
"code.gitea.io/gitea/modules/auth/password/hash"
2018-02-18 18:14:37 +00:00
"code.gitea.io/gitea/modules/generate"
2019-12-15 09:51:28 +00:00
"code.gitea.io/gitea/modules/graceful"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/log"
2024-02-23 03:18:33 +01:00
"code.gitea.io/gitea/modules/optional"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/setting"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/templates"
2023-10-14 02:56:41 +02:00
"code.gitea.io/gitea/modules/timeutil"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2021-01-30 16:55:53 +08:00
"code.gitea.io/gitea/modules/web/middleware"
2023-06-21 13:50:26 +08:00
"code.gitea.io/gitea/routers/common"
2023-10-14 02:56:41 +02:00
auth_service "code.gitea.io/gitea/services/auth"
2024-02-27 15:12:22 +08:00
"code.gitea.io/gitea/services/context"
2021-04-06 20:44:05 +01:00
"code.gitea.io/gitea/services/forms"
2025-03-07 13:08:53 -08:00
"code.gitea.io/gitea/services/versioned_migration"
2014-03-28 07:26:22 -04:00
)
2014-06-22 13:14:03 -04:00
const (
2016-11-18 11:03:03 +08:00
// tplInstall template for installation page
2024-12-22 23:33:19 +08:00
tplInstall templates . TplName = "install"
tplPostInstall templates . TplName = "post-install"
2014-06-22 13:14:03 -04:00
)
2022-04-01 16:00:26 +08: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 13:44:08 +08:00
}
2022-04-01 16:00:26 +08:00
return dbTypeNames
2021-12-07 13:44:08 +08:00
}
2025-11-26 23:25:34 +08:00
func installContexter ( ) func ( next http . Handler ) http . Handler {
2026-01-24 13:11:49 +08:00
return context . ContexterInstallPage ( map [ string ] any {
"DbTypeNames" : getSupportedDbTypeNames ( ) ,
"EnvConfigKeys" : setting . CollectEnvConfigKeys ( ) ,
"CustomConfFile" : setting . CustomConf ,
"PasswordHashAlgorithms" : hash . RecommendedHashAlgorithms ,
} )
2015-02-01 12:41:03 -05:00
}
2014-03-29 17:50:51 -04:00
2016-11-18 11:03:03 +08:00
// Install render installation page
2016-03-11 11:56:52 -05:00
func Install ( ctx * context . Context ) {
2023-03-04 10:12:02 +08:00
if setting . InstallLock {
InstallDone ( ctx )
return
}
2021-04-06 20:44:05 +01:00
form := forms . InstallForm { }
2015-02-01 12:41:03 -05:00
2015-07-09 13:17:48 +08:00
// Database settings
2019-08-24 17:24:45 +08: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 12:45:14 -03:00
form . DbSchema = setting . Database . Schema
2023-07-12 06:09:23 +08:00
form . SSLMode = setting . Database . SSLMode
2015-02-01 12:41:03 -05:00
2023-03-07 18:51:06 +08:00
curDBType := setting . Database . Type . String ( )
2025-06-18 20:37:49 +02:00
if ! slices . Contains ( setting . SupportedDatabaseTypes , curDBType ) {
2021-12-07 13:44:08 +08:00
curDBType = "mysql"
}
ctx . Data [ "CurDbType" ] = curDBType
2020-11-16 15:33:41 +08:00
2015-07-09 13:17:48 +08:00
// Application general settings
form . AppName = setting . AppName
2015-02-01 12:41:03 -05:00
form . RepoRootPath = setting . RepoRootPath
2023-06-14 11:42:38 +08:00
form . LFSRootPath = setting . LFS . Storage . Path
2026-04-13 09:53:09 +08:00
form . RunUser = setting . RunUser
2015-02-01 12:41:03 -05:00
form . Domain = setting . Domain
2016-02-27 20:48:39 -05:00
form . SSHPort = setting . SSH . Port
2016-08-11 14:55:10 -07:00
form . HTTPPort = setting . HTTPPort
2016-11-27 14:03:59 +08:00
form . AppURL = setting . AppURL
2023-02-20 00:12:01 +08:00
form . LogRootPath = setting . Log . RootPath
2015-02-01 12:41:03 -05:00
2015-07-09 13:17:48 +08:00
// E-mail service settings
if setting . MailService != nil {
2022-08-02 01:24:18 -04:00
form . SMTPAddr = setting . MailService . SMTPAddr
form . SMTPPort = setting . MailService . SMTPPort
2015-07-09 16:10:31 +08:00
form . SMTPFrom = setting . MailService . From
2017-02-23 20:37:13 -05:00
form . SMTPUser = setting . MailService . User
2022-05-02 15:45:23 +07:00
form . SMTPPasswd = setting . MailService . Passwd
2014-04-26 22:34:48 -06:00
}
2015-07-09 13:17:48 +08:00
form . RegisterConfirm = setting . Service . RegisterEmailConfirm
form . MailNotify = setting . Service . EnableNotifyMail
2017-11-29 13:47:42 +01:00
form . EnableOpenIDSignIn = setting . Service . EnableOpenIDSignIn
form . EnableOpenIDSignUp = setting . Service . EnableOpenIDSignUp
2015-07-09 13:17:48 +08:00
form . DisableRegistration = setting . Service . DisableRegistration
2018-05-13 09:51:16 +02:00
form . AllowOnlyExternalRegistration = setting . Service . AllowOnlyExternalRegistration
2015-09-13 12:14:32 -04:00
form . EnableCaptcha = setting . Service . EnableCaptcha
2025-03-30 13:26:19 +08:00
form . RequireSignInView = setting . Service . RequireSignInViewStrict
2017-01-08 04:12:03 +01:00
form . DefaultKeepEmailPrivate = setting . Service . DefaultKeepEmailPrivate
2017-05-08 22:51:53 +03:00
form . DefaultAllowCreateOrganization = setting . Service . DefaultAllowCreateOrganization
2017-09-12 08:48:13 +02:00
form . DefaultEnableTimetracking = setting . Service . DefaultEnableTimetracking
2017-01-08 04:12:03 +01:00
form . NoReplyAddress = setting . Service . NoReplyAddress
2023-03-04 10:12:02 +08:00
form . PasswordAlgorithm = hash . ConfigHashAlgorithm ( setting . PasswordHashAlgo )
2014-04-26 22:34:48 -06:00
2021-01-30 16:55:53 +08:00
middleware . AssignForm ( form , ctx . Data )
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplInstall )
2014-04-10 14:37:43 -04:00
}
2021-12-01 15:50:01 +08: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
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.err_empty_db_path" ) , tplInstall , form )
2021-12-01 15:50:01 +08:00
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
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.sqlite3_not_available" , "https://docs.gitea.com/installation/install-from-binary" ) , tplInstall , form )
2021-12-01 15:50:01 +08:00
} else {
ctx . Data [ "Err_DbSetting" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_db_setting" , err ) , tplInstall , form )
2021-12-01 15:50:01 +08:00
}
return false
}
2025-08-28 11:52:43 +08:00
err = db_install . CheckDatabaseConnection ( ctx )
2021-12-01 15:50:01 +08:00
if err != nil {
ctx . Data [ "Err_DbSetting" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_db_setting" , err ) , tplInstall , form )
2021-12-01 15:50:01 +08:00
return false
}
2025-08-28 11:52:43 +08:00
hasPostInstallationUser , err := db_install . HasPostInstallationUsers ( ctx )
2021-12-01 15:50:01 +08:00
if err != nil {
ctx . Data [ "Err_DbSetting" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_db_table" , "user" , err ) , tplInstall , form )
2021-12-01 15:50:01 +08:00
return false
}
2025-08-28 11:52:43 +08:00
dbMigrationVersion , err := db_install . GetMigrationVersion ( ctx )
2021-12-01 15:50:01 +08:00
if err != nil {
ctx . Data [ "Err_DbSetting" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_db_table" , "version" , err ) , tplInstall , form )
2021-12-01 15:50:01 +08:00
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
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.reinstall_error" ) , tplInstall , form )
2021-12-01 15:50:01 +08:00
return false
}
2023-07-10 06:43:37 +08:00
log . Info ( "User confirmed re-installation of Gitea into a pre-existing database" )
2021-12-01 15:50:01 +08: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 07:33:54 +08:00
// SubmitInstall response for submit install items
func SubmitInstall ( ctx * context . Context ) {
2023-03-04 10:12:02 +08:00
if setting . InstallLock {
InstallDone ( ctx )
return
}
2016-12-20 14:32:02 +02:00
var err error
2021-12-01 15:50:01 +08:00
form := * web . GetForm ( ctx ) . ( * forms . InstallForm )
// fix form values
if form . AppURL != "" && form . AppURL [ len ( form . AppURL ) - 1 ] != '/' {
form . AppURL += "/"
}
2021-12-07 13:44:08 +08:00
ctx . Data [ "CurDbType" ] = form . DbType
2014-04-26 22:34:48 -06:00
2014-03-29 17:50:51 -04:00
if ctx . HasError ( ) {
2023-05-21 09:50:53 +08: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 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplInstall )
2014-03-29 17:50:51 -04:00
return
}
2016-12-20 14:32:02 +02:00
if _ , err = exec . LookPath ( "git" ) ; err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.test_git_failed" , err ) , tplInstall , & form )
2014-04-08 15:27:35 -04:00
return
}
2021-12-01 15:50:01 +08:00
// ---- Basic checks are passed, now test configuration.
2019-08-24 17:24:45 +08:00
2021-12-01 15:50:01 +08:00
// Test database setting.
2023-03-07 18:51:06 +08:00
setting . Database . Type = setting . DatabaseType ( form . DbType )
2019-08-24 17:24:45 +08:00
setting . Database . Host = form . DbHost
setting . Database . User = form . DbUser
setting . Database . Passwd = form . DbPasswd
setting . Database . Name = form . DbName
2020-01-20 12:45:14 -03:00
setting . Database . Schema = form . DbSchema
2019-08-24 17:24:45 +08:00
setting . Database . SSLMode = form . SSLMode
setting . Database . Path = form . DbPath
2021-11-07 11:11:27 +08:00
setting . Database . LogSQL = ! setting . IsProd
2021-02-16 22:37:20 +00:00
2021-12-01 15:50:01 +08:00
if ! checkDatabase ( ctx , & form ) {
2015-09-12 15:31:36 -04:00
return
2015-07-08 19:47:56 +08:00
}
2021-12-01 15:50:01 +08:00
// Prepare AppDataPath, it is very important for Gitea
if err = setting . PrepareAppDataPath ( ) ; err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_app_data_path" , err ) , tplInstall , & form )
2014-03-29 17:50:51 -04:00
return
}
// Test repository root path.
2020-10-11 22:27:20 +02:00
form . RepoRootPath = strings . ReplaceAll ( form . RepoRootPath , "\\" , "/" )
2016-12-20 14:32:02 +02:00
if err = os . MkdirAll ( form . RepoRootPath , os . ModePerm ) ; err != nil {
2014-09-14 19:22:52 -04:00
ctx . Data [ "Err_RepoRootPath" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_repo_path" , err ) , tplInstall , & form )
2014-03-29 17:50:51 -04:00
return
}
2016-12-26 02:16:37 +01:00
// Test LFS root path if not empty, empty meaning disable LFS
if form . LFSRootPath != "" {
2020-10-11 22:27:20 +02:00
form . LFSRootPath = strings . ReplaceAll ( form . LFSRootPath , "\\" , "/" )
2016-12-26 02:16:37 +01:00
if err := os . MkdirAll ( form . LFSRootPath , os . ModePerm ) ; err != nil {
ctx . Data [ "Err_LFSRootPath" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_lfs_path" , err ) , tplInstall , & form )
2016-12-26 02:16:37 +01:00
return
}
}
2016-02-12 09:19:45 -05:00
// Test log root path.
2020-10-11 22:27:20 +02:00
form . LogRootPath = strings . ReplaceAll ( form . LogRootPath , "\\" , "/" )
2016-12-20 14:32:02 +02:00
if err = os . MkdirAll ( form . LogRootPath , os . ModePerm ) ; err != nil {
2016-02-12 09:19:45 -05:00
ctx . Data [ "Err_LogRootPath" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_log_root_path" , err ) , tplInstall , & form )
2016-02-12 09:19:45 -05:00
return
}
2015-09-12 15:31:36 -04: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
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.no_admin_and_disable_registration" ) , tplInstall , form )
2015-09-12 15:31:36 -04:00
return
}
2019-05-28 07:18:40 +01:00
// Check admin user creation
if len ( form . AdminName ) > 0 {
// Ensure AdminName is valid
2021-11-24 17:49:20 +08:00
if err := user_model . IsUsableUsername ( form . AdminName ) ; err != nil {
2019-05-28 07:18:40 +01:00
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminName" ] = true
2021-11-24 17:49:20 +08:00
if db . IsErrNameReserved ( err ) {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.err_admin_name_is_reserved" ) , tplInstall , form )
2019-05-28 07:18:40 +01:00
return
2021-11-24 17:49:20 +08:00
} else if db . IsErrNamePatternNotAllowed ( err ) {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.err_admin_name_pattern_not_allowed" ) , tplInstall , form )
2019-05-28 07:18:40 +01:00
return
}
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.err_admin_name_is_invalid" ) , tplInstall , form )
2019-05-28 07:18:40 +01:00
return
}
// Check Admin email
if len ( form . AdminEmail ) == 0 {
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminEmail" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.err_empty_admin_email" ) , tplInstall , form )
2019-05-28 07:18:40 +01:00
return
}
// Check admin password.
if len ( form . AdminPasswd ) == 0 {
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminPasswd" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.err_empty_admin_password" ) , tplInstall , form )
2019-05-28 07:18:40 +01:00
return
}
if form . AdminPasswd != form . AdminConfirmPasswd {
ctx . Data [ "Err_Admin" ] = true
ctx . Data [ "Err_AdminPasswd" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "form.password_not_match" ) , tplInstall , form )
2019-05-28 07:18:40 +01:00
return
}
2014-03-30 11:58:21 -04:00
}
2021-12-01 15:50:01 +08:00
// Init the engine with migration
2025-03-07 13:08:53 -08:00
if err = db . InitEngineWithMigration ( ctx , versioned_migration . Migrate ) ; err != nil {
2021-12-01 15:50:01 +08:00
db . UnsetDefaultEngine ( )
ctx . Data [ "Err_DbSetting" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_db_setting" , err ) , tplInstall , & form )
2021-12-01 15:50:01 +08:00
return
2015-02-01 12:41:03 -05:00
}
2014-03-29 17:50:51 -04:00
// Save settings.
2023-06-21 13:50:26 +08:00
cfg , err := setting . NewConfigProviderFromFile ( setting . CustomConf )
2020-11-28 02:42:08 +00:00
if err != nil {
2023-06-02 17:27:30 +08:00
log . Error ( "Failed to load custom conf '%s': %v" , setting . CustomConf , err )
2015-02-13 16:48:23 -05:00
}
2023-06-02 17:27:30 +08:00
2023-06-21 13:50:26 +08: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 18:51:06 +08:00
cfg . Section ( "database" ) . Key ( "DB_TYPE" ) . SetValue ( setting . Database . Type . String ( ) )
2019-08-24 17:24:45 +08: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 12:45:14 -03:00
cfg . Section ( "database" ) . Key ( "SCHEMA" ) . SetValue ( setting . Database . Schema )
2019-08-24 17:24:45 +08:00
cfg . Section ( "database" ) . Key ( "SSL_MODE" ) . SetValue ( setting . Database . SSLMode )
cfg . Section ( "database" ) . Key ( "PATH" ) . SetValue ( setting . Database . Path )
2020-10-10 16:19:50 +01:00
cfg . Section ( "database" ) . Key ( "LOG_SQL" ) . SetValue ( "false" ) // LOG_SQL is rarely helpful
2015-02-01 14:39:58 -05:00
cfg . Section ( "repository" ) . Key ( "ROOT" ) . SetValue ( form . RepoRootPath )
2016-12-27 15:34:34 +08:00
cfg . Section ( "server" ) . Key ( "SSH_DOMAIN" ) . SetValue ( form . Domain )
2017-04-21 10:43:29 +08:00
cfg . Section ( "server" ) . Key ( "DOMAIN" ) . SetValue ( form . Domain )
2015-02-01 14:39:58 -05:00
cfg . Section ( "server" ) . Key ( "HTTP_PORT" ) . SetValue ( form . HTTPPort )
2016-11-27 14:03:59 +08:00
cfg . Section ( "server" ) . Key ( "ROOT_URL" ) . SetValue ( form . AppURL )
2023-06-18 21:57:43 +08:00
cfg . Section ( "server" ) . Key ( "APP_DATA_PATH" ) . SetValue ( setting . AppDataPath )
2014-03-29 17:50:51 -04:00
2015-08-19 20:36:19 +08:00
if form . SSHPort == 0 {
cfg . Section ( "server" ) . Key ( "DISABLE_SSH" ) . SetValue ( "true" )
} else {
cfg . Section ( "server" ) . Key ( "DISABLE_SSH" ) . SetValue ( "false" )
2025-04-01 12:14:01 +02:00
cfg . Section ( "server" ) . Key ( "SSH_PORT" ) . SetValue ( strconv . Itoa ( form . SSHPort ) )
2015-08-19 20:36:19 +08:00
}
2016-12-26 02:16:37 +01:00
if form . LFSRootPath != "" {
cfg . Section ( "server" ) . Key ( "LFS_START_SERVER" ) . SetValue ( "true" )
2022-01-23 19:02:29 +00:00
cfg . Section ( "lfs" ) . Key ( "PATH" ) . SetValue ( form . LFSRootPath )
2026-04-04 04:03:59 +08:00
if ! cfg . Section ( "server" ) . HasKey ( "LFS_JWT_SECRET_URI" ) {
_ , lfsJwtSecret := generate . NewJwtSecretWithBase64 ( )
cfg . Section ( "server" ) . Key ( "LFS_JWT_SECRET" ) . SetValue ( lfsJwtSecret )
2018-02-18 18:14:37 +00:00
}
2016-12-26 02:16:37 +01:00
} else {
cfg . Section ( "server" ) . Key ( "LFS_START_SERVER" ) . SetValue ( "false" )
}
2022-08-02 01:24:18 -04:00
if len ( strings . TrimSpace ( form . SMTPAddr ) ) > 0 {
2024-02-24 00:02:14 +01:00
if _ , err := mail . ParseAddress ( form . SMTPFrom ) ; err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.smtp_from_invalid" ) , tplInstall , & form )
2024-02-24 00:02:14 +01:00
return
}
2015-02-01 14:39:58 -05:00
cfg . Section ( "mailer" ) . Key ( "ENABLED" ) . SetValue ( "true" )
2022-08-02 01:24:18 -04:00
cfg . Section ( "mailer" ) . Key ( "SMTP_ADDR" ) . SetValue ( form . SMTPAddr )
cfg . Section ( "mailer" ) . Key ( "SMTP_PORT" ) . SetValue ( form . SMTPPort )
2015-07-09 16:10:31 +08:00
cfg . Section ( "mailer" ) . Key ( "FROM" ) . SetValue ( form . SMTPFrom )
2017-02-23 20:37:13 -05:00
cfg . Section ( "mailer" ) . Key ( "USER" ) . SetValue ( form . SMTPUser )
2015-02-01 14:39:58 -05:00
cfg . Section ( "mailer" ) . Key ( "PASSWD" ) . SetValue ( form . SMTPPasswd )
2015-07-09 13:17:48 +08:00
} else {
cfg . Section ( "mailer" ) . Key ( "ENABLED" ) . SetValue ( "false" )
2014-03-29 17:50:51 -04:00
}
2025-04-01 12:14:01 +02:00
cfg . Section ( "service" ) . Key ( "REGISTER_EMAIL_CONFIRM" ) . SetValue ( strconv . FormatBool ( form . RegisterConfirm ) )
cfg . Section ( "service" ) . Key ( "ENABLE_NOTIFY_MAIL" ) . SetValue ( strconv . FormatBool ( form . MailNotify ) )
2020-12-25 09:59:32 +00:00
2025-04-01 12:14:01 +02:00
cfg . Section ( "openid" ) . Key ( "ENABLE_OPENID_SIGNIN" ) . SetValue ( strconv . FormatBool ( form . EnableOpenIDSignIn ) )
cfg . Section ( "openid" ) . Key ( "ENABLE_OPENID_SIGNUP" ) . SetValue ( strconv . FormatBool ( form . EnableOpenIDSignUp ) )
cfg . Section ( "service" ) . Key ( "DISABLE_REGISTRATION" ) . SetValue ( strconv . FormatBool ( form . DisableRegistration ) )
cfg . Section ( "service" ) . Key ( "ALLOW_ONLY_EXTERNAL_REGISTRATION" ) . SetValue ( strconv . FormatBool ( form . AllowOnlyExternalRegistration ) )
cfg . Section ( "service" ) . Key ( "ENABLE_CAPTCHA" ) . SetValue ( strconv . FormatBool ( form . EnableCaptcha ) )
cfg . Section ( "service" ) . Key ( "REQUIRE_SIGNIN_VIEW" ) . SetValue ( strconv . FormatBool ( form . RequireSignInView ) )
cfg . Section ( "service" ) . Key ( "DEFAULT_KEEP_EMAIL_PRIVATE" ) . SetValue ( strconv . FormatBool ( form . DefaultKeepEmailPrivate ) )
cfg . Section ( "service" ) . Key ( "DEFAULT_ALLOW_CREATE_ORGANIZATION" ) . SetValue ( strconv . FormatBool ( form . DefaultAllowCreateOrganization ) )
cfg . Section ( "service" ) . Key ( "DEFAULT_ENABLE_TIMETRACKING" ) . SetValue ( strconv . FormatBool ( form . DefaultEnableTimetracking ) )
cfg . Section ( "service" ) . Key ( "NO_REPLY_ADDRESS" ) . SetValue ( form . NoReplyAddress )
cfg . Section ( "cron.update_checker" ) . Key ( "ENABLED" ) . SetValue ( strconv . FormatBool ( form . EnableUpdateChecker ) )
2014-03-29 17:50:51 -04:00
2015-02-01 14:39:58 -05:00
cfg . Section ( "session" ) . Key ( "PROVIDER" ) . SetValue ( "file" )
2014-12-20 22:51:16 -05:00
2023-06-12 18:52:49 +08:00
cfg . Section ( "log" ) . Key ( "MODE" ) . MustString ( "console" )
2023-02-20 00:12:01 +08:00
cfg . Section ( "log" ) . Key ( "LEVEL" ) . SetValue ( setting . Log . Level . String ( ) )
2016-02-12 09:19:45 -05:00
cfg . Section ( "log" ) . Key ( "ROOT_PATH" ) . SetValue ( form . LogRootPath )
2014-08-27 16:39:36 +08:00
2022-06-03 11:45:54 +08:00
cfg . Section ( "repository.pull-request" ) . Key ( "DEFAULT_MERGE_STYLE" ) . SetValue ( "merge" )
2022-01-20 02:41:59 +00:00
cfg . Section ( "repository.signing" ) . Key ( "DEFAULT_TRUST_MODEL" ) . SetValue ( "committer" )
2015-02-01 14:39:58 -05:00
cfg . Section ( "security" ) . Key ( "INSTALL_LOCK" ) . SetValue ( "true" )
2021-12-01 15:50:01 +08:00
2022-11-04 04:55:09 +08: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 {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.internal_token_failed" , err ) , tplInstall , & form )
2022-11-04 04:55:09 +08:00
return
}
cfg . Section ( "security" ) . Key ( "INTERNAL_TOKEN" ) . SetValue ( internalToken )
2016-12-20 14:32:02 +02:00
}
2021-12-01 15:50:01 +08:00
2024-05-14 22:21:38 +08:00
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
// see the "loadOAuth2From" in "setting/oauth2.go"
if ! cfg . Section ( "oauth2" ) . HasKey ( "JWT_SECRET" ) && ! cfg . Section ( "oauth2" ) . HasKey ( "JWT_SECRET_URI" ) {
2026-04-04 04:03:59 +08:00
_ , jwtSecretBase64 := generate . NewJwtSecretWithBase64 ( )
2024-05-14 22:21:38 +08:00
cfg . Section ( "oauth2" ) . Key ( "JWT_SECRET" ) . SetValue ( jwtSecretBase64 )
}
2021-12-01 15:50:01 +08: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 {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.secret_key_failed" , err ) , tplInstall , & form )
2021-12-01 15:50:01 +08:00
return
}
cfg . Section ( "security" ) . Key ( "SECRET_KEY" ) . SetValue ( secretKey )
}
2021-02-16 22:37:20 +00:00
if len ( form . PasswordAlgorithm ) > 0 {
2023-03-04 10:12:02 +08:00
var algorithm * hash . PasswordHashAlgorithm
setting . PasswordHashAlgo , algorithm = hash . SetDefaultPasswordHashAlgorithm ( form . PasswordAlgorithm )
if algorithm == nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_password_algorithm" ) , tplInstall , & form )
2023-03-04 10:12:02 +08:00
return
}
2021-02-16 22:37:20 +00:00
cfg . Section ( "security" ) . Key ( "PASSWORD_HASH_ALGO" ) . SetValue ( form . PasswordAlgorithm )
}
2014-03-29 17:50:51 -04:00
2021-12-01 15:50:01 +08:00
log . Info ( "Save settings to custom config file %s" , setting . CustomConf )
2016-12-20 14:32:02 +02:00
err = os . MkdirAll ( filepath . Dir ( setting . CustomConf ) , os . ModePerm )
2016-11-10 11:02:01 +01:00
if err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2016-11-10 11:02:01 +01:00
return
}
2023-07-10 06:43:37 +08:00
setting . EnvironmentToConfig ( cfg , os . Environ ( ) )
2016-12-20 14:32:02 +02:00
if err = cfg . SaveTo ( setting . CustomConf ) ; err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2014-03-29 17:50:51 -04:00
return
}
2022-10-18 23:16:58 +08:00
// unset default engine before reload database setting
db . UnsetDefaultEngine ( )
2021-12-01 15:50:01 +08:00
// ---- All checks are passed
// Reload settings (and re-initialize database connection)
2023-06-21 13:50:26 +08: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-29 17:50:51 -04:00
2015-12-08 00:59:14 -05:00
// Create admin account
2015-07-08 19:47:56 +08:00
if len ( form . AdminName ) > 0 {
2021-11-24 17:49:20 +08:00
u := & user_model . User {
2022-04-29 21:38:11 +02:00
Name : form . AdminName ,
Email : form . AdminEmail ,
Passwd : form . AdminPasswd ,
IsAdmin : true ,
2015-12-08 00:59:14 -05:00
}
2022-04-29 21:38:11 +02:00
overwriteDefault := & user_model . CreateUserOverwriteOptions {
2024-02-23 03:18:33 +01:00
IsRestricted : optional . Some ( false ) ,
IsActive : optional . Some ( true ) ,
2022-04-29 21:38:11 +02:00
}
2024-09-09 17:05:16 -04:00
if err = user_model . CreateUser ( ctx , u , & user_model . Meta { } , overwriteDefault ) ; err != nil {
2021-11-24 17:49:20 +08:00
if ! user_model . IsErrUserAlreadyExist ( err ) {
2015-07-08 19:47:56 +08:00
setting . InstallLock = false
ctx . Data [ "Err_AdminName" ] = true
ctx . Data [ "Err_AdminEmail" ] = true
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.invalid_admin_setting" , err ) , tplInstall , & form )
2015-07-08 19:47:56 +08:00
return
}
log . Info ( "Admin account already exist" )
2022-05-20 22:08:52 +08:00
u , _ = user_model . GetUserByName ( ctx , u . Name )
2014-03-30 11:09:59 -04:00
}
2015-12-08 00:59:14 -05:00
2023-10-14 02:56:41 +02:00
nt , token , err := auth_service . CreateAuthTokenForUserID ( ctx , u . ID )
if err != nil {
ctx . ServerError ( "CreateAuthTokenForUserID" , err )
return
}
2021-03-07 08:12:43 +00:00
2023-10-14 02:56:41 +02:00
ctx . SetSiteCookie ( setting . CookieRememberName , nt . ID + ":" + token , setting . LogInRememberDays * timeutil . Day )
2020-10-19 22:03:08 +01:00
2015-12-08 00:59:14 -05:00
// Auto-login for admin
2016-12-20 14:32:02 +02:00
if err = ctx . Session . Set ( "uid" , u . ID ) ; err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2016-11-10 11:02:01 +01:00
return
}
2016-12-20 14:32:02 +02:00
if err = ctx . Session . Set ( "uname" , u . Name ) ; err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2016-11-10 11:02:01 +01:00
return
}
2020-05-17 13:43:29 +01:00
if err = ctx . Session . Release ( ) ; err != nil {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "install.save_config_failed" , err ) , tplInstall , & form )
2020-05-17 13:43:29 +01:00
return
}
2014-03-30 11:09:59 -04:00
}
2023-07-10 06:43:37 +08:00
setting . ClearEnvConfigKeys ( )
2014-03-29 17:50:51 -04:00
log . Info ( "First-time run install finished!" )
2023-03-04 10:12:02 +08:00
InstallDone ( ctx )
2020-10-19 22:03:08 +01:00
go func ( ) {
2023-03-04 10:12:02 +08: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-19 22:03:08 +01:00
if err := srv . Shutdown ( graceful . GetManager ( ) . HammerContext ( ) ) ; err != nil {
log . Error ( "Unable to shutdown the install server! Error: %v" , err )
}
2023-03-04 10:12:02 +08:00
// After the HTTP server for "install" shuts down, the `runWeb()` will continue to run the "normal" server
2020-10-19 22:03:08 +01:00
} ( )
2014-03-25 16:51:42 +08:00
}
2023-03-04 10:12:02 +08: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"
2025-06-27 07:59:55 +02:00
func InstallDone ( ctx * context . Context ) { //nolint:revive // export stutter
2025-06-22 02:48:06 +08:00
hasUsers , _ := user_model . HasUsers ( ctx )
ctx . Data [ "IsAccountCreated" ] = hasUsers . HasAnyUser
2023-03-04 10:12:02 +08:00
ctx . HTML ( http . StatusOK , tplPostInstall )
}