471cfdd161
- 1 - Add: Gitea now creates timestamped database backup bundles under `[backup].PATH`, exposes the backup schedule in the installer, and surfaces the `database_backup` cron task in admin monitoring. - 2 - Add: installed instances now use `.gitea-installed` and `.gitea-recovery.ini` to enter email-gated recovery instead of falling back to public install mode when configuration or database access is broken. - 3 - Mod: the installer recovery flow now covers backup-bundle restore, bundled or manual `app.ini` reuse, uploaded SQL/GZ database restores, and repository-filesystem recovery with source-specific validation, confirmations, and preserved launcher state. - 4 - Fix: recovery now restores bundled `app.ini` snapshots when needed, discovers backup bundles from both the active backup path and persisted `.gitea-recovery.ini` path, and preserves SMTP and other rebuilt settings correctly when `app.ini` is missing or incomplete. - 5 - Fix: recovery validation and restore handling now accept either a selected backup bundle or an uploaded SQL/GZ dump, keep sensitive secrets and existing `LFS_JWT_SECRET` when appropriate, clear SQLite restore targets before import, and complete the post-install handoff without redirect loops. - 6 - Mod: fresh installs now default recovery email authorization to enabled with first-admin fallback, and the install/recovery UI, styling, and EN/RO wording were refined to match the final launcher behavior. Co-Authored-By: petru @ codex (GPT-5) <codex@openai.com> (cherry picked from commit 9879caf2292691b0cb521d12e6fee924b066bae2)
551 lines
19 KiB
Go
551 lines
19 KiB
Go
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"html"
|
|
"net"
|
|
"net/http"
|
|
"net/http/pprof"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
db_model "code.gitea.io/gitea/models/db"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/container"
|
|
"code.gitea.io/gitea/modules/graceful"
|
|
"code.gitea.io/gitea/modules/gtprof"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/process"
|
|
"code.gitea.io/gitea/modules/public"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/templates"
|
|
"code.gitea.io/gitea/modules/translation"
|
|
"code.gitea.io/gitea/modules/util"
|
|
"code.gitea.io/gitea/modules/web"
|
|
"code.gitea.io/gitea/routers"
|
|
"code.gitea.io/gitea/routers/common"
|
|
"code.gitea.io/gitea/routers/install"
|
|
web_context "code.gitea.io/gitea/services/context"
|
|
|
|
"github.com/felixge/fgprof"
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
// PIDFile could be set from build tag
|
|
var PIDFile = "/run/gitea.pid"
|
|
|
|
func newWebCommand() *cli.Command {
|
|
return &cli.Command{
|
|
Name: "web",
|
|
Usage: "Start Gitea web server",
|
|
Description: `Gitea web server is the only thing you need to run,
|
|
and it takes care of all the other things for you`,
|
|
Before: PrepareConsoleLoggerLevel(log.INFO),
|
|
Action: runWeb,
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "port",
|
|
Aliases: []string{"p"},
|
|
Value: "3000",
|
|
Usage: "Temporary port number to prevent conflict",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "install-port",
|
|
Value: "3000",
|
|
Usage: "Temporary port number to run the install page on to prevent conflict",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "pid",
|
|
Aliases: []string{"P"},
|
|
Value: PIDFile,
|
|
Usage: "Custom pid file path",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "quiet",
|
|
Aliases: []string{"q"},
|
|
Usage: "Only display Fatal logging errors until logging is set-up",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "verbose",
|
|
Usage: "Set initial logging to TRACE level until logging is properly set-up",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func runHTTPRedirector() {
|
|
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true)
|
|
defer finished()
|
|
|
|
source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
|
|
dest := strings.TrimSuffix(setting.AppURL, "/")
|
|
log.Info("Redirecting: %s to %s", source, dest)
|
|
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
target := dest + r.URL.Path
|
|
if len(r.URL.RawQuery) > 0 {
|
|
target += "?" + r.URL.RawQuery
|
|
}
|
|
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
|
})
|
|
|
|
err := runHTTP("tcp", source, "HTTP Redirector", handler, setting.RedirectorUseProxyProtocol)
|
|
if err != nil {
|
|
log.Fatal("Failed to start port redirection: %v", err)
|
|
}
|
|
}
|
|
|
|
func createPIDFile(pidPath string) {
|
|
currentPid := os.Getpid()
|
|
if err := os.MkdirAll(filepath.Dir(pidPath), os.ModePerm); err != nil {
|
|
log.Fatal("Failed to create PID folder: %v", err)
|
|
}
|
|
|
|
file, err := os.Create(pidPath)
|
|
if err != nil {
|
|
log.Fatal("Failed to create PID file: %v", err)
|
|
}
|
|
defer file.Close()
|
|
if _, err := file.WriteString(strconv.FormatInt(int64(currentPid), 10)); err != nil {
|
|
log.Fatal("Failed to write PID information: %v", err)
|
|
}
|
|
}
|
|
|
|
func showWebStartupMessage(msg string) {
|
|
log.Info("Gitea version: %s%s", setting.AppVer, setting.AppBuiltWith)
|
|
log.Info("* RunMode: %s", setting.RunMode)
|
|
log.Info("* AppPath: %s", setting.AppPath)
|
|
log.Info("* WorkPath: %s", setting.AppWorkPath)
|
|
log.Info("* CustomPath: %s", setting.CustomPath)
|
|
log.Info("* ConfigFile: %s", setting.CustomConf)
|
|
log.Info("%s", msg) // show startup message
|
|
|
|
if setting.CORSConfig.Enabled {
|
|
log.Info("CORS Service Enabled")
|
|
}
|
|
if setting.DefaultUILocation != time.Local {
|
|
log.Info("Default UI Location is %v", setting.DefaultUILocation.String())
|
|
}
|
|
if setting.MailService != nil {
|
|
log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail)
|
|
}
|
|
}
|
|
|
|
func serveInstall(cmd *cli.Command) error {
|
|
showWebStartupMessage("Prepare to run install page")
|
|
|
|
routers.InitWebInstallPage(graceful.GetManager().HammerContext())
|
|
|
|
// Flag for port number in case first time run conflict
|
|
if cmd.IsSet("port") {
|
|
if err := setPort(cmd.String("port")); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if cmd.IsSet("install-port") {
|
|
if err := setPort(cmd.String("install-port")); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
c := install.Routes()
|
|
err := listen(c, false)
|
|
if err != nil {
|
|
log.Critical("Unable to open listener for installer. Is Gitea already running?")
|
|
graceful.GetManager().DoGracefulShutdown()
|
|
}
|
|
select {
|
|
case <-graceful.GetManager().IsShutdown():
|
|
<-graceful.GetManager().Done()
|
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
|
return err
|
|
default:
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// start edit/add - by petru @ codex
|
|
func missingInstalledSQLiteDatabase() (bool, error) {
|
|
if !setting.Database.Type.IsSQLite3() {
|
|
return false, nil
|
|
}
|
|
if !(setting.InstallLock || setting.HasInstallSentinel()) {
|
|
return false, nil
|
|
}
|
|
_, err := os.Stat(setting.Database.Path)
|
|
if err == nil {
|
|
return false, nil
|
|
}
|
|
if os.IsNotExist(err) {
|
|
return true, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
func serviceUnavailableRoutes() *web.Router {
|
|
base := web.NewRouter()
|
|
base.BeforeRouting(common.ProtocolMiddlewares()...)
|
|
|
|
r := web.NewRouter()
|
|
r.AfterRouting(common.MustInitSessioner(), web_context.Contexter())
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
ctx := web_context.GetWebContext(req.Context())
|
|
// The full 503 template depends on dynamic config backed by the database,
|
|
// so the bootstrap fallback must render a standalone response here.
|
|
message := html.EscapeString(ctx.Locale.TrString("error503")) // edit/add - by petru @ codex
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
_, _ = fmt.Fprintf(w, `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>503 Service Unavailable</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#111827;color:#f3f4f6}main{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}.card{max-width:720px;width:100%%;border:1px solid #374151;border-radius:12px;background:#1f2937;padding:32px;text-align:center;box-shadow:0 20px 40px rgba(0,0,0,.35)}h1{margin:0 0 16px;font-size:32px;line-height:1.2}p{margin:0;color:#d1d5db;font-size:18px;line-height:1.6}</style></head><body><main><div class="card"><h1>503 Service Unavailable</h1><p>%s</p></div></main></body></html>`, message)
|
|
})
|
|
r.Any("/", handler) // edit/add - by petru @ codex
|
|
r.Any("/*", handler) // edit/add - by petru @ codex
|
|
|
|
base.Mount("", r)
|
|
return base
|
|
}
|
|
|
|
func serveInstalledUnavailable(cmd *cli.Command) error {
|
|
showWebStartupMessage("Prepare to run service unavailable page")
|
|
translation.InitLocales(graceful.GetManager().HammerContext())
|
|
|
|
if cmd.IsSet("port") {
|
|
if err := setPort(cmd.String("port")); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err := listen(serviceUnavailableRoutes(), false)
|
|
<-graceful.GetManager().Done()
|
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
|
return err
|
|
}
|
|
|
|
func buildAutoRecoveryConfigFromInstalledState(ctx context.Context) (*setting.RecoveryConfig, error) {
|
|
recoveryEmail := "" // edit/add - by petru @ codex
|
|
if setting.Admin.SuperAdminEnabled {
|
|
superAdminSettings := make([]*user_model.Setting, 0, 4)
|
|
if err := db_model.GetEngine(ctx).Where("setting_key=? AND setting_value=?", user_model.SettingsKeySuperAdminEnabled, "true").Asc("id").Find(&superAdminSettings); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, superAdminSetting := range superAdminSettings {
|
|
u, err := user_model.GetUserByID(ctx, superAdminSetting.UserID)
|
|
if err != nil {
|
|
if user_model.IsErrUserNotExist(err) {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
if u.IsAdmin && u.IsActive && !u.ProhibitLogin && strings.TrimSpace(u.Email) != "" {
|
|
recoveryEmail = strings.TrimSpace(u.Email)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if recoveryEmail == "" {
|
|
adminUser, err := user_model.GetAdminUser(ctx)
|
|
if err != nil {
|
|
if user_model.IsErrUserNotExist(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
if adminUser == nil || strings.TrimSpace(adminUser.Email) == "" {
|
|
return nil, nil
|
|
}
|
|
recoveryEmail = strings.TrimSpace(adminUser.Email)
|
|
}
|
|
|
|
recoveryCfg := &setting.RecoveryConfig{
|
|
Enabled: true,
|
|
AllowedEmails: recoveryEmail,
|
|
BackupPath: setting.Backup.Path,
|
|
RepoRootPath: setting.RepoRootPath,
|
|
}
|
|
if setting.MailService != nil {
|
|
recoveryCfg.SMTPAddr = setting.MailService.SMTPAddr
|
|
recoveryCfg.SMTPPort = setting.MailService.SMTPPort
|
|
recoveryCfg.SMTPFrom = setting.MailService.From
|
|
recoveryCfg.SMTPUser = setting.MailService.User
|
|
recoveryCfg.SMTPPasswd = setting.MailService.Passwd
|
|
}
|
|
return recoveryCfg, nil
|
|
}
|
|
|
|
func ensureRecoveryConfigFromInstalledState(ctx context.Context) error {
|
|
recoveryCfg, err := setting.LoadRecoveryConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if recoveryCfg != nil {
|
|
return nil
|
|
}
|
|
|
|
recoveryCfg, err = buildAutoRecoveryConfigFromInstalledState(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if recoveryCfg == nil {
|
|
return nil
|
|
}
|
|
return setting.SaveRecoveryConfig(recoveryCfg)
|
|
}
|
|
|
|
// end edit/add - by petru @ codex
|
|
|
|
func serveInstalled(c *cli.Command) error {
|
|
setting.MustInstalled()
|
|
setting.LoadDBSetting() // edit/add - by petru @ codex
|
|
if recoveryCfg, shouldServeRecovery, err := shouldServeInstalledRecovery(graceful.GetManager().HammerContext()); err != nil {
|
|
return err
|
|
} else if shouldServeRecovery {
|
|
if err := serveRecovery(c, recoveryCfg, recoveryReasonDatabaseUnavailable); err != nil {
|
|
return err
|
|
}
|
|
return serveInstalled(c)
|
|
}
|
|
|
|
if missingDB, err := missingInstalledSQLiteDatabase(); err != nil {
|
|
return err
|
|
} else if missingDB {
|
|
return serveInstalledUnavailable(c)
|
|
}
|
|
|
|
showWebStartupMessage("Prepare to run web server")
|
|
|
|
if setting.AppWorkPathMismatch {
|
|
log.Error("WORK_PATH from config %q doesn't match other paths from environment variables or command arguments. "+
|
|
"Only WORK_PATH in config should be set and used. Please make sure the path in config file is correct, "+
|
|
"remove the other outdated work paths from environment variables and command arguments", setting.CustomConf)
|
|
}
|
|
|
|
rootCfg := setting.CfgProvider
|
|
if rootCfg.Section("").Key("WORK_PATH").String() == "" {
|
|
saveCfg, err := rootCfg.PrepareSaving()
|
|
if err != nil {
|
|
log.Error("Unable to prepare saving WORK_PATH=%s to config %q: %v\nYou should set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err)
|
|
} else {
|
|
rootCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath)
|
|
saveCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath)
|
|
if err = saveCfg.Save(); err != nil {
|
|
log.Error("Unable to update WORK_PATH=%s to config %q: %v\nYou should set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// in old versions, user's custom web files are placed in "custom/public", and they were served as "http://domain.com/assets/xxx"
|
|
// now, Gitea only serves pre-defined files in the "custom/public" folder basing on the web root, the user should move their custom files to "custom/public/assets"
|
|
publicFiles, _ := public.AssetFS().ListFiles(".")
|
|
publicFilesSet := container.SetOf(publicFiles...)
|
|
publicFilesSet.Remove(".well-known")
|
|
publicFilesSet.Remove("assets")
|
|
publicFilesSet.Remove("robots.txt")
|
|
for _, fn := range publicFilesSet.Values() {
|
|
log.Error("Found legacy public asset %q in CustomPath. Please move it to %s/public/assets/%s", fn, setting.CustomPath, fn)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(setting.CustomPath, "robots.txt")); err == nil {
|
|
log.Error(`Found legacy public asset "robots.txt" in CustomPath. Please move it to %s/public/robots.txt`, setting.CustomPath)
|
|
}
|
|
|
|
routers.InitWebInstalled(graceful.GetManager().HammerContext())
|
|
|
|
// We check that AppDataPath exists here (it should have been created during installation)
|
|
// We can't check it in `InitWebInstalled`, because some integration tests
|
|
// use cmd -> InitWebInstalled, but the AppDataPath doesn't exist during those tests.
|
|
if _, err := os.Stat(setting.AppDataPath); err != nil {
|
|
log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath)
|
|
}
|
|
if err := setting.EnsureInstallSentinel(); err != nil {
|
|
log.Error("Unable to create install sentinel %q: %v", setting.InstallSentinelPath(), err) // edit/add - by petru @ codex
|
|
}
|
|
if err := ensureRecoveryConfigFromInstalledState(graceful.GetManager().HammerContext()); err != nil {
|
|
log.Error("Unable to create recovery config %q: %v", setting.RecoveryConfigPath(), err) // edit/add - by petru @ codex
|
|
}
|
|
|
|
// the AppDataTempDir is fully managed by us with a safe sub-path
|
|
// so it's safe to automatically remove the outdated files
|
|
setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour)
|
|
|
|
// Override the provided port number within the configuration
|
|
if c.IsSet("port") {
|
|
if err := setPort(c.String("port")); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
gtprof.EnableBuiltinTracer(util.Iif(setting.IsProd, 2000*time.Millisecond, 100*time.Millisecond))
|
|
|
|
// Set up Chi routes
|
|
webRoutes := routers.NormalRoutes()
|
|
err := listen(webRoutes, true)
|
|
<-graceful.GetManager().Done()
|
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
|
return err
|
|
}
|
|
|
|
func servePprof() {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
|
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
|
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
|
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
|
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
|
mux.Handle("/debug/fgprof", fgprof.Handler())
|
|
// FIXME: it should use a proper context
|
|
_, _, finished := process.GetManager().AddTypedContext(context.TODO(), "Web: PProf Server", process.SystemProcessType, true)
|
|
// The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment, it's not worth introducing a configurable option for it.
|
|
log.Info("Starting pprof server on localhost:6060")
|
|
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", mux))
|
|
finished()
|
|
}
|
|
|
|
func runWeb(ctx context.Context, cmd *cli.Command) error {
|
|
if subCmdName, valid := isValidDefaultSubCommand(cmd); !valid {
|
|
return fmt.Errorf("unknown command: %s", subCmdName)
|
|
}
|
|
|
|
managerCtx, cancel := context.WithCancel(ctx)
|
|
graceful.InitManager(managerCtx)
|
|
defer cancel()
|
|
|
|
if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 {
|
|
log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid())
|
|
} else {
|
|
log.Info("Starting Gitea on PID: %d", os.Getpid())
|
|
}
|
|
|
|
// Set pid file setting
|
|
if cmd.IsSet("pid") {
|
|
createPIDFile(cmd.String("pid"))
|
|
}
|
|
|
|
// init the HTML renderer and load templates, if error happens, it will report the error immediately and exit with error log
|
|
// in dev mode, it won't exit, but watch the template files for changes
|
|
_ = templates.PageRenderer()
|
|
|
|
if !setting.InstallLock {
|
|
if setting.HasInstallSentinel() {
|
|
recoveryCfg, err := loadEnabledRecoveryConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if recoveryCfg != nil {
|
|
if err := serveRecovery(cmd, recoveryCfg, recoveryReasonMissingConfig); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return fmt.Errorf("install mode is disabled because the install sentinel exists at %q; fix the configured database or use an explicit recovery flow instead", setting.InstallSentinelPath()) // edit/add - by petru @ codex
|
|
}
|
|
} else if err := serveInstall(cmd); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
NoInstallListener()
|
|
}
|
|
|
|
if setting.EnablePprof {
|
|
go servePprof()
|
|
}
|
|
|
|
return serveInstalled(cmd)
|
|
}
|
|
|
|
func setPort(port string) error {
|
|
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
|
|
setting.HTTPPort = port
|
|
|
|
switch setting.Protocol {
|
|
case setting.HTTPUnix:
|
|
case setting.FCGI:
|
|
case setting.FCGIUnix:
|
|
default:
|
|
defaultLocalURL := string(setting.Protocol) + "://"
|
|
if setting.HTTPAddr == "0.0.0.0" {
|
|
defaultLocalURL += "localhost"
|
|
} else {
|
|
defaultLocalURL += setting.HTTPAddr
|
|
}
|
|
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
|
|
|
// Save LOCAL_ROOT_URL if port changed
|
|
rootCfg := setting.CfgProvider
|
|
saveCfg, err := rootCfg.PrepareSaving()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save config file: %v", err)
|
|
}
|
|
rootCfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
|
saveCfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
|
if err = saveCfg.Save(); err != nil {
|
|
return fmt.Errorf("failed to save config file: %v", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func listen(m http.Handler, handleRedirector bool) error {
|
|
listenAddr := setting.HTTPAddr
|
|
if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix {
|
|
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
|
}
|
|
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: Gitea Server", process.SystemProcessType, true)
|
|
defer finished()
|
|
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
|
// This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy.
|
|
// A user may fix the configuration mistake when he sees this log.
|
|
// And this is also very helpful to maintainers to provide help to users to resolve their configuration problems.
|
|
log.Info("AppURL(ROOT_URL): %s", setting.AppURL)
|
|
|
|
if setting.LFS.StartServer {
|
|
log.Info("LFS server enabled")
|
|
}
|
|
|
|
var err error
|
|
switch setting.Protocol {
|
|
case setting.HTTP:
|
|
if handleRedirector {
|
|
NoHTTPRedirector()
|
|
}
|
|
err = runHTTP("tcp", listenAddr, "Web", m, setting.UseProxyProtocol)
|
|
case setting.HTTPS:
|
|
if setting.EnableAcme {
|
|
err = runACME(listenAddr, m)
|
|
break
|
|
}
|
|
if handleRedirector {
|
|
if setting.RedirectOtherPort {
|
|
go runHTTPRedirector()
|
|
} else {
|
|
NoHTTPRedirector()
|
|
}
|
|
}
|
|
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
|
|
case setting.FCGI:
|
|
if handleRedirector {
|
|
NoHTTPRedirector()
|
|
}
|
|
err = runFCGI("tcp", listenAddr, "FCGI Web", m, setting.UseProxyProtocol)
|
|
case setting.HTTPUnix:
|
|
if handleRedirector {
|
|
NoHTTPRedirector()
|
|
}
|
|
err = runHTTP("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
|
|
case setting.FCGIUnix:
|
|
if handleRedirector {
|
|
NoHTTPRedirector()
|
|
}
|
|
err = runFCGI("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
|
|
default:
|
|
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
|
}
|
|
if err != nil {
|
|
log.Critical("Failed to start server: %v", err)
|
|
}
|
|
log.Info("HTTP Listener: %s Closed", listenAddr)
|
|
return err
|
|
}
|