You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gitea/modules/setting/setting.go

619 lines
17 KiB

11 years ago
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
11 years ago
package setting
11 years ago
import (
"fmt"
"net/url"
11 years ago
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
11 years ago
"strings"
"time"
11 years ago
"gopkg.in/ini.v1"
11 years ago
"github.com/Unknwon/com"
10 years ago
"github.com/macaron-contrib/oauth2"
"github.com/macaron-contrib/session"
11 years ago
"github.com/gogits/gogs/modules/bindata"
11 years ago
"github.com/gogits/gogs/modules/log"
// "github.com/gogits/gogs/modules/ssh"
"github.com/gogits/gogs/modules/user"
11 years ago
)
11 years ago
type Scheme string
11 years ago
11 years ago
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
FCGI Scheme = "fcgi"
11 years ago
)
11 years ago
10 years ago
type LandingPage string
const (
LANDING_PAGE_HOME LandingPage = "/"
LANDING_PAGE_EXPLORE LandingPage = "/explore"
)
11 years ago
var (
11 years ago
// App settings.
AppVer string
AppName string
AppUrl string
AppSubUrl string
11 years ago
// Server settings.
Protocol Scheme
Domain string
HttpAddr, HttpPort string
DisableSSH bool
SSHPort int
SSHDomain string
11 years ago
OfflineMode bool
DisableRouterLog bool
CertFile, KeyFile string
StaticRootPath string
EnableGzip bool
10 years ago
LandingPageUrl LandingPage
11 years ago
// Security settings.
11 years ago
InstallLock bool
SecretKey string
LogInRememberDays int
CookieUserName string
CookieRememberName string
ReverseProxyAuthUser string
11 years ago
// Database settings.
UseSQLite3 bool
UseMySQL bool
UsePostgreSQL bool
11 years ago
// Webhook settings.
Webhook struct {
QueueLength int
DeliverTimeout int
SkipTLSVerify bool
Types []string
PagingNum int
}
11 years ago
11 years ago
// Repository settings.
RepoRootPath string
ScriptType string
AnsiCharset string
// UI settings.
IssuePagingNum int
11 years ago
11 years ago
// Picture settings.
PictureService string
AvatarUploadPath string
GravatarSource string
DisableGravatar bool
11 years ago
// Log settings.
11 years ago
LogRootPath string
LogModes []string
LogConfigs []string
11 years ago
// Attachment settings.
AttachmentPath string
AttachmentAllowedTypes string
AttachmentMaxSize int64
AttachmentMaxFiles int
AttachmentEnabled bool
// Time settings.
TimeFormat string
11 years ago
// Cache settings.
CacheAdapter string
CacheInternal int
CacheConn string
11 years ago
11 years ago
EnableRedis bool
EnableMemcache bool
// Session settings.
10 years ago
SessionConfig session.Options
11 years ago
// Git settings.
Git struct {
MaxGitDiffLines int
GcArgs []string `delim:" "`
}
// Cron tasks.
Cron struct {
UpdateMirror struct {
Enabled bool
RunAtStart bool
Schedule string
} `ini:"cron.update_mirrors"`
RepoHealthCheck struct {
Enabled bool
RunAtStart bool
Schedule string
Args []string `delim:" "`
} `ini:"cron.repo_health_check"`
CheckRepoStats struct {
Enabled bool
RunAtStart bool
Schedule string
} `ini:"cron.check_repo_stats"`
}
// I18n settings.
Langs, Names []string
dateLangs map[string]string
// Other settings.
ShowFooterBranding bool
11 years ago
// Global setting objects.
10 years ago
Cfg *ini.File
CustomPath string // Custom directory path.
CustomConf string
ProdMode bool
RunUser string
IsWindows bool
HasRobotsTxt bool
11 years ago
)
func DateLang(lang string) string {
name, ok := dateLangs[lang]
if ok {
return name
}
return "en"
}
func init() {
IsWindows = runtime.GOOS == "windows"
log.NewLogger(0, "console", `{"level": 0}`)
}
func ExecPath() (string, error) {
11 years ago
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
p, err := filepath.Abs(file)
if err != nil {
return "", err
}
return p, nil
}
// WorkDir returns absolute path of work directory.
func WorkDir() (string, error) {
execPath, err := ExecPath()
if err != nil {
return execPath, err
}
// Note: we don't use path.Dir here because it does not handle case
// which path starts with two "/" in Windows: "//psf/Home/..."
execPath = strings.Replace(execPath, "\\", "/", -1)
i := strings.LastIndex(execPath, "/")
if i == -1 {
return execPath, nil
}
return execPath[:i], nil
11 years ago
}
func forcePathSeparator(path string) {
if strings.Contains(path, "\\") {
log.Fatal(4, "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")
}
}
11 years ago
// NewConfigContext initializes configuration context.
11 years ago
// NOTE: do not print any log except error.
11 years ago
func NewConfigContext() {
workDir, err := WorkDir()
if err != nil {
log.Fatal(4, "Fail to get work directory: %v", err)
11 years ago
}
Cfg, err = ini.Load(bindata.MustAsset("conf/app.ini"))
11 years ago
if err != nil {
log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err)
11 years ago
}
CustomPath = os.Getenv("GOGS_CUSTOM")
if len(CustomPath) == 0 {
CustomPath = workDir + "/custom"
11 years ago
}
if len(CustomConf) == 0 {
CustomConf = CustomPath + "/conf/app.ini"
}
if com.IsFile(CustomConf) {
if err = Cfg.Append(CustomConf); err != nil {
log.Fatal(4, "Fail to load custom conf '%s': %v", CustomConf, err)
11 years ago
}
} else {
log.Warn("Custom config (%s) not found, ignore this if you're running first time", CustomConf)
11 years ago
}
Cfg.NameMapper = ini.AllCapsUnderscore
11 years ago
10 years ago
LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(workDir, "log"))
forcePathSeparator(LogRootPath)
10 years ago
sec := Cfg.Section("server")
AppName = Cfg.Section("").Key("APP_NAME").MustString("Gogs: Go Git Service")
AppUrl = sec.Key("ROOT_URL").MustString("http://localhost:3000/")
10 years ago
if AppUrl[len(AppUrl)-1] != '/' {
AppUrl += "/"
}
11 years ago
// Check if has app suburl.
url, err := url.Parse(AppUrl)
if err != nil {
log.Fatal(4, "Invalid ROOT_URL(%s): %s", AppUrl, err)
}
AppSubUrl = strings.TrimSuffix(url.Path, "/")
11 years ago
Protocol = HTTP
10 years ago
if sec.Key("PROTOCOL").String() == "https" {
11 years ago
Protocol = HTTPS
10 years ago
CertFile = sec.Key("CERT_FILE").String()
KeyFile = sec.Key("KEY_FILE").String()
} else if sec.Key("PROTOCOL").String() == "fcgi" {
Protocol = FCGI
}
10 years ago
Domain = sec.Key("DOMAIN").MustString("localhost")
HttpAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
HttpPort = sec.Key("HTTP_PORT").MustString("3000")
DisableSSH = sec.Key("DISABLE_SSH").MustBool()
SSHDomain = sec.Key("SSH_DOMAIN").MustString(Domain)
SSHPort = sec.Key("SSH_PORT").MustInt(22)
10 years ago
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir)
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
switch sec.Key("LANDING_PAGE").MustString("home") {
10 years ago
case "explore":
LandingPageUrl = LANDING_PAGE_EXPLORE
default:
LandingPageUrl = LANDING_PAGE_HOME
}
10 years ago
sec = Cfg.Section("security")
InstallLock = sec.Key("INSTALL_LOCK").MustBool()
SecretKey = sec.Key("SECRET_KEY").String()
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
CookieUserName = sec.Key("COOKIE_USERNAME").String()
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
sec = Cfg.Section("attachment")
AttachmentPath = sec.Key("PATH").MustString("data/attachments")
if !filepath.IsAbs(AttachmentPath) {
AttachmentPath = path.Join(workDir, AttachmentPath)
}
AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
10 years ago
AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(32)
AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
10 years ago
AttachmentEnabled = sec.Key("ENABLE").MustBool(true)
TimeFormat = map[string]string{
"ANSIC": time.ANSIC,
"UnixDate": time.UnixDate,
"RubyDate": time.RubyDate,
"RFC822": time.RFC822,
"RFC822Z": time.RFC822Z,
"RFC850": time.RFC850,
"RFC1123": time.RFC1123,
"RFC1123Z": time.RFC1123Z,
"RFC3339": time.RFC3339,
"RFC3339Nano": time.RFC3339Nano,
"Kitchen": time.Kitchen,
"Stamp": time.Stamp,
"StampMilli": time.StampMilli,
"StampMicro": time.StampMicro,
"StampNano": time.StampNano,
10 years ago
}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
10 years ago
RunUser = Cfg.Section("").Key("RUN_USER").String()
curUser := user.CurrentUsername()
11 years ago
// Does not check run user when the install lock is off.
if InstallLock && RunUser != curUser {
log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)
11 years ago
}
// Determine and create root git repository path.
11 years ago
homeDir, err := com.HomeDir()
if err != nil {
log.Fatal(4, "Fail to get home directory: %v", err)
11 years ago
}
homeDir = strings.Replace(homeDir, "\\", "/", -1)
10 years ago
sec = Cfg.Section("repository")
RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gogs-repositories"))
forcePathSeparator(RepoRootPath)
if !filepath.IsAbs(RepoRootPath) {
RepoRootPath = path.Join(workDir, RepoRootPath)
} else {
RepoRootPath = path.Clean(RepoRootPath)
}
10 years ago
ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
AnsiCharset = sec.Key("ANSI_CHARSET").MustString("")
11 years ago
// UI settings.
IssuePagingNum = Cfg.Section("ui").Key("ISSUE_PAGING_NUM").MustInt(10)
10 years ago
sec = Cfg.Section("picture")
PictureService = sec.Key("SERVICE").In("server", []string{"server"})
AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString("data/avatars")
forcePathSeparator(AvatarUploadPath)
if !filepath.IsAbs(AvatarUploadPath) {
AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
}
switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
case "duoshuo":
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
case "gravatar":
GravatarSource = "//1.gravatar.com/avatar/"
default:
GravatarSource = source
}
10 years ago
DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
if OfflineMode {
DisableGravatar = true
}
if err = Cfg.Section("git").MapTo(&Git); err != nil {
log.Fatal(4, "Fail to map Git settings: %v", err)
} else if Cfg.Section("cron").MapTo(&Cron); err != nil {
log.Fatal(4, "Fail to map Cron settings: %v", err)
}
10 years ago
Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
dateLangs = Cfg.Section("i18n.datelang").KeysHash()
ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()
HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
11 years ago
}
11 years ago
var Service struct {
ActiveCodeLives int
ResetPwdCodeLives int
10 years ago
RegisterEmailConfirm bool
DisableRegistration bool
ShowRegistrationButton bool
10 years ago
RequireSignInView bool
EnableCacheAvatar bool
EnableNotifyMail bool
EnableReverseProxyAuth bool
EnableReverseProxyAutoRegister bool
DisableMinimumKeySizeCheck bool
11 years ago
}
11 years ago
func newService() {
sec := Cfg.Section("service")
Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration)
Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
Service.EnableCacheAvatar = sec.Key("ENABLE_CACHE_AVATAR").MustBool()
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
10 years ago
Service.DisableMinimumKeySizeCheck = sec.Key("DISABLE_MINIMUM_KEY_SIZE_CHECK").MustBool()
11 years ago
}
var logLevels = map[string]string{
"Trace": "0",
"Debug": "1",
"Info": "2",
"Warn": "3",
"Error": "4",
"Critical": "5",
}
func newLogService() {
log.Info("%s %s", AppName, AppVer)
11 years ago
// Get and check log mode.
10 years ago
LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
LogConfigs = make([]string, len(LogModes))
for i, mode := range LogModes {
mode = strings.TrimSpace(mode)
10 years ago
sec, err := Cfg.GetSection("log." + mode)
if err != nil {
log.Fatal(4, "Unknown log mode: %s", mode)
}
validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
// Log level.
levelName := Cfg.Section("log."+mode).Key("LEVEL").In(
Cfg.Section("log").Key("LEVEL").In("Trace", validLevels),
validLevels)
level, ok := logLevels[levelName]
if !ok {
log.Fatal(4, "Unknown log level: %s", levelName)
}
// Generate log configuration.
switch mode {
case "console":
LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
case "file":
10 years ago
logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "gogs.log"))
os.MkdirAll(path.Dir(logPath), os.ModePerm)
LogConfigs[i] = fmt.Sprintf(
`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
logPath,
10 years ago
sec.Key("LOG_ROTATE").MustBool(true),
sec.Key("MAX_LINES").MustInt(1000000),
1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
sec.Key("DAILY_ROTATE").MustBool(true),
sec.Key("MAX_DAYS").MustInt(7))
case "conn":
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
10 years ago
sec.Key("RECONNECT_ON_MSG").MustBool(),
sec.Key("RECONNECT").MustBool(),
sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),
sec.Key("ADDR").MustString(":7020"))
case "smtp":
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
10 years ago
sec.Key("USER").MustString("example@example.com"),
sec.Key("PASSWD").MustString("******"),
sec.Key("HOST").MustString("127.0.0.1:25"),
sec.Key("RECEIVERS").MustString("[]"),
sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
case "database":
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
10 years ago
sec.Key("DRIVER").String(),
sec.Key("CONN").String())
}
10 years ago
log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i])
log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
11 years ago
}
}
func newCacheService() {
10 years ago
CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
if EnableRedis {
9 years ago
log.Info("Redis Supported")
}
if EnableMemcache {
9 years ago
log.Info("Memcache Supported")
}
11 years ago
switch CacheAdapter {
case "memory":
10 years ago
CacheInternal = Cfg.Section("cache").Key("INTERVAL").MustInt(60)
11 years ago
case "redis", "memcache":
10 years ago
CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ")
11 years ago
default:
log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
11 years ago
}
log.Info("Cache Service Enabled")
}
func newSessionService() {
10 years ago
SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",
[]string{"memory", "file", "redis", "mysql"})
10 years ago
SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")
SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gogits")
10 years ago
SessionConfig.CookiePath = AppSubUrl
10 years ago
SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool()
SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
11 years ago
log.Info("Session Service Enabled")
}
11 years ago
// Mailer represents mail service.
type Mailer struct {
Name string
Host string
From string
User, Passwd string
DisableHelo bool
HeloHostname string
SkipVerify bool
UseCertificate bool
CertFile, KeyFile string
11 years ago
}
type OauthInfo struct {
10 years ago
oauth2.Options
AuthUrl, TokenUrl string
11 years ago
}
// Oauther represents oauth service.
type Oauther struct {
GitHub, Google, Tencent,
Twitter, Weibo bool
OauthInfos map[string]*OauthInfo
}
var (
MailService *Mailer
OauthService *Oauther
)
11 years ago
func newMailService() {
10 years ago
sec := Cfg.Section("mailer")
11 years ago
// Check mailer setting.
10 years ago
if !sec.Key("ENABLED").MustBool() {
11 years ago
return
}
MailService = &Mailer{
Name: sec.Key("NAME").MustString(AppName),
Host: sec.Key("HOST").String(),
User: sec.Key("USER").String(),
Passwd: sec.Key("PASSWD").String(),
DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
HeloHostname: sec.Key("HELO_HOSTNAME").String(),
SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
CertFile: sec.Key("CERT_FILE").String(),
KeyFile: sec.Key("KEY_FILE").String(),
11 years ago
}
10 years ago
MailService.From = sec.Key("FROM").MustString(MailService.User)
11 years ago
log.Info("Mail Service Enabled")
}
func newRegisterMailService() {
10 years ago
if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
11 years ago
return
} else if MailService == nil {
log.Warn("Register Mail Service: Mail Service is not enabled")
return
}
Service.RegisterEmailConfirm = true
log.Info("Register Mail Service Enabled")
}
func newNotifyMailService() {
10 years ago
if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
11 years ago
return
} else if MailService == nil {
log.Warn("Notify Mail Service: Mail Service is not enabled")
return
}
11 years ago
Service.EnableNotifyMail = true
11 years ago
log.Info("Notify Mail Service Enabled")
}
11 years ago
func newWebhookService() {
sec := Cfg.Section("webhook")
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
Webhook.Types = []string{"gogs", "slack"}
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
11 years ago
}
11 years ago
func NewServices() {
11 years ago
newService()
newLogService()
newCacheService()
newSessionService()
newMailService()
newRegisterMailService()
newNotifyMailService()
11 years ago
newWebhookService()
// ssh.Listen("2222")
11 years ago
}