github.com/kotovmak/go-admin@v1.1.1/modules/config/config.go (about)

     1  // Copyright 2019 GoAdmin Core Team. All rights reserved.
     2  // Use of this source code is governed by a Apache-2.0 style
     3  // license that can be found in the LICENSE file.
     4  
     5  package config
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"html/template"
    11  	"io/ioutil"
    12  	"path/filepath"
    13  	"reflect"
    14  	"strconv"
    15  	"strings"
    16  	"sync"
    17  	"sync/atomic"
    18  	"time"
    19  
    20  	"github.com/kotovmak/go-admin/modules/logger"
    21  	"github.com/kotovmak/go-admin/modules/utils"
    22  	"github.com/kotovmak/go-admin/plugins/admin/modules/form"
    23  	"gopkg.in/ini.v1"
    24  	"gopkg.in/yaml.v2"
    25  )
    26  
    27  // Database is a type of database connection config.
    28  //
    29  // Because a little difference of different database driver.
    30  // The Config has multiple options but may not be used.
    31  // Such as the sqlite driver only use the File option which
    32  // can be ignored when the driver is mysql.
    33  //
    34  // If the Dsn is configured, when driver is mysql/postgresql/
    35  // mssql, the other configurations will be ignored, except for
    36  // MaxIdleConns and MaxOpenConns.
    37  type Database struct {
    38  	Host       string `json:"host,omitempty" yaml:"host,omitempty" ini:"host,omitempty"`
    39  	Port       string `json:"port,omitempty" yaml:"port,omitempty" ini:"port,omitempty"`
    40  	User       string `json:"user,omitempty" yaml:"user,omitempty" ini:"user,omitempty"`
    41  	Pwd        string `json:"pwd,omitempty" yaml:"pwd,omitempty" ini:"pwd,omitempty"`
    42  	Name       string `json:"name,omitempty" yaml:"name,omitempty" ini:"name,omitempty"`
    43  	Driver     string `json:"driver,omitempty" yaml:"driver,omitempty" ini:"driver,omitempty"`
    44  	DriverMode string `json:"driver_mode,omitempty" yaml:"driver_mode,omitempty" ini:"driver_mode,omitempty"`
    45  	File       string `json:"file,omitempty" yaml:"file,omitempty" ini:"file,omitempty"`
    46  	Dsn        string `json:"dsn,omitempty" yaml:"dsn,omitempty" ini:"dsn,omitempty"`
    47  
    48  	MaxIdleConns    int           `json:"max_idle_con,omitempty" yaml:"max_idle_con,omitempty" ini:"max_idle_con,omitempty"`
    49  	MaxOpenConns    int           `json:"max_open_con,omitempty" yaml:"max_open_con,omitempty" ini:"max_open_con,omitempty"`
    50  	ConnMaxLifetime time.Duration `json:"conn_max_life_time,omitempty" yaml:"conn_max_life_time,omitempty" ini:"conn_max_life_time,omitempty"`
    51  	ConnMaxIdleTime time.Duration `json:"conn_max_idle_time,omitempty" yaml:"conn_max_idle_time,omitempty" ini:"conn_max_idle_time,omitempty"`
    52  
    53  	Params map[string]string `json:"params,omitempty" yaml:"params,omitempty" ini:"params,omitempty"`
    54  }
    55  
    56  func (d Database) GetDSN() string {
    57  	if d.Dsn != "" {
    58  		return d.Dsn
    59  	}
    60  
    61  	if d.Driver == DriverMysql {
    62  		return d.User + ":" + d.Pwd + "@tcp(" + d.Host + ":" + d.Port + ")/" +
    63  			d.Name + d.ParamStr()
    64  	}
    65  	if d.Driver == DriverPostgresql {
    66  		return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s"+d.ParamStr(),
    67  			d.Host, d.Port, d.User, d.Pwd, d.Name)
    68  	}
    69  	if d.Driver == DriverMssql {
    70  		return fmt.Sprintf("user id=%s;password=%s;server=%s;port=%s;database=%s;"+d.ParamStr(),
    71  			d.User, d.Pwd, d.Host, d.Port, d.Name)
    72  	}
    73  	if d.Driver == DriverSqlite {
    74  		return d.File + d.ParamStr()
    75  	}
    76  	if d.Driver == DriverOceanBase {
    77  		return d.User + ":" + d.Pwd + "@tcp(" + d.Host + ":" + d.Port + ")/" +
    78  			d.Name + d.ParamStr()
    79  	}
    80  	return ""
    81  }
    82  
    83  func (d Database) ParamStr() string {
    84  	p := ""
    85  	if d.Params == nil {
    86  		d.Params = make(map[string]string)
    87  	}
    88  	if d.Driver == DriverMysql || d.Driver == DriverSqlite || d.Driver == DriverOceanBase {
    89  		if d.Driver == DriverMysql || d.Driver == DriverOceanBase {
    90  			if _, ok := d.Params["charset"]; !ok {
    91  				d.Params["charset"] = "utf8mb4"
    92  			}
    93  		}
    94  		if len(d.Params) > 0 {
    95  			p = "?"
    96  			for k, v := range d.Params {
    97  				p += k + "=" + v + "&"
    98  			}
    99  			p = p[:len(p)-1]
   100  		}
   101  	}
   102  	if d.Driver == DriverMssql {
   103  		if _, ok := d.Params["encrypt"]; !ok {
   104  			d.Params["encrypt"] = "disable"
   105  		}
   106  		for k, v := range d.Params {
   107  			p += k + "=" + v + ";"
   108  		}
   109  		p = p[:len(p)-1]
   110  	}
   111  	if d.Driver == DriverPostgresql {
   112  		if _, ok := d.Params["sslmode"]; !ok {
   113  			d.Params["sslmode"] = "disable"
   114  		}
   115  		p = " "
   116  		for k, v := range d.Params {
   117  			p += k + "=" + v + " "
   118  		}
   119  		p = p[:len(p)-1]
   120  	}
   121  	return p
   122  }
   123  
   124  // DatabaseList is a map of Database.
   125  type DatabaseList map[string]Database
   126  
   127  // GetDefault get the default Database.
   128  func (d DatabaseList) GetDefault() Database {
   129  	return d["default"]
   130  }
   131  
   132  // Add add a Database to the DatabaseList.
   133  func (d DatabaseList) Add(key string, db Database) {
   134  	d[key] = db
   135  }
   136  
   137  // GroupByDriver group the Databases with the drivers.
   138  func (d DatabaseList) GroupByDriver() map[string]DatabaseList {
   139  	drivers := make(map[string]DatabaseList)
   140  	for key, item := range d {
   141  		if driverList, ok := drivers[item.Driver]; ok {
   142  			driverList.Add(key, item)
   143  		} else {
   144  			drivers[item.Driver] = make(DatabaseList)
   145  			drivers[item.Driver].Add(key, item)
   146  		}
   147  	}
   148  	return drivers
   149  }
   150  
   151  func (d DatabaseList) JSON() string {
   152  	return utils.JSON(d)
   153  }
   154  
   155  func (d DatabaseList) Copy() DatabaseList {
   156  	var c = make(DatabaseList)
   157  	for k, v := range d {
   158  		c[k] = v
   159  	}
   160  	return c
   161  }
   162  
   163  func (d DatabaseList) Connections() []string {
   164  	conns := make([]string, len(d))
   165  	count := 0
   166  	for key := range d {
   167  		conns[count] = key
   168  		count++
   169  	}
   170  	return conns
   171  }
   172  
   173  func GetDatabaseListFromJSON(m string) DatabaseList {
   174  	var d = make(DatabaseList)
   175  	if m == "" {
   176  		panic("wrong config")
   177  	}
   178  	_ = json.Unmarshal([]byte(m), &d)
   179  	return d
   180  }
   181  
   182  const (
   183  	// EnvTest is a const value of test environment.
   184  	EnvTest = "test"
   185  	// EnvLocal is a const value of local environment.
   186  	EnvLocal = "local"
   187  	// EnvProd is a const value of production environment.
   188  	EnvProd = "prod"
   189  
   190  	// DriverMysql is a const value of mysql driver.
   191  	DriverMysql = "mysql"
   192  	// DriverSqlite is a const value of sqlite driver.
   193  	DriverSqlite = "sqlite"
   194  	// DriverPostgresql is a const value of postgresql driver.
   195  	DriverPostgresql = "postgresql"
   196  	// DriverMssql is a const value of mssql driver.
   197  	DriverMssql = "mssql"
   198  	// DriverOceanBase is a const value of mysql driver.
   199  	DriverOceanBase = "oceanbase"
   200  )
   201  
   202  // Store is the file store config. Path is the local store path.
   203  // and prefix is the url prefix used to visit it.
   204  type Store struct {
   205  	Path   string `json:"path,omitempty" yaml:"path,omitempty" ini:"path,omitempty"`
   206  	Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty" ini:"prefix,omitempty"`
   207  }
   208  
   209  func (s Store) URL(suffix string) string {
   210  	if len(suffix) > 4 && suffix[:4] == "http" {
   211  		return suffix
   212  	}
   213  	if s.Prefix == "" {
   214  		if suffix[0] == '/' {
   215  			return suffix
   216  		}
   217  		return "/" + suffix
   218  	}
   219  	if s.Prefix[0] == '/' {
   220  		if suffix[0] == '/' {
   221  			return s.Prefix + suffix
   222  		}
   223  		return s.Prefix + "/" + suffix
   224  	}
   225  	if suffix[0] == '/' {
   226  		if len(s.Prefix) > 4 && s.Prefix[:4] == "http" {
   227  			return s.Prefix + suffix
   228  		}
   229  		return "/" + s.Prefix + suffix
   230  	}
   231  	if len(s.Prefix) > 4 && s.Prefix[:4] == "http" {
   232  		return s.Prefix + "/" + suffix
   233  	}
   234  	return "/" + s.Prefix + "/" + suffix
   235  }
   236  
   237  func (s Store) JSON() string {
   238  	if s.Path == "" && s.Prefix == "" {
   239  		return ""
   240  	}
   241  	return utils.JSON(s)
   242  }
   243  
   244  func GetStoreFromJSON(m string) Store {
   245  	var s Store
   246  	if m == "" {
   247  		return s
   248  	}
   249  	_ = json.Unmarshal([]byte(m), &s)
   250  	return s
   251  }
   252  
   253  // Config type is the global config of goAdmin. It will be
   254  // initialized in the engine.
   255  type Config struct {
   256  	// An map supports multi database connection. The first
   257  	// element of Databases is the default connection. See the
   258  	// file connection.go.
   259  	Databases DatabaseList `json:"database,omitempty" yaml:"database,omitempty" ini:"database,omitempty"`
   260  
   261  	// The application unique ID. Once generated, don't modify.
   262  	AppID string `json:"app_id,omitempty" yaml:"app_id,omitempty" ini:"app_id,omitempty"`
   263  
   264  	// The cookie domain used in the auth modules. see
   265  	// the session.go.
   266  	Domain string `json:"domain,omitempty" yaml:"domain,omitempty" ini:"domain,omitempty"`
   267  
   268  	// Used to set as the localize language which show in the
   269  	// interface.
   270  	Language string `json:"language,omitempty" yaml:"language,omitempty" ini:"language,omitempty"`
   271  
   272  	// The global url prefix.
   273  	UrlPrefix string `json:"prefix,omitempty" yaml:"prefix,omitempty" ini:"prefix,omitempty"`
   274  
   275  	// The theme name of template.
   276  	Theme string `json:"theme,omitempty" yaml:"theme,omitempty" ini:"theme,omitempty"`
   277  
   278  	// The path where files will be stored into.
   279  	Store Store `json:"store,omitempty" yaml:"store,omitempty" ini:"store,omitempty"`
   280  
   281  	// The title of web page.
   282  	Title string `json:"title,omitempty" yaml:"title,omitempty" ini:"title,omitempty"`
   283  
   284  	// Logo is the top text in the sidebar.
   285  	Logo template.HTML `json:"logo,omitempty" yaml:"logo,omitempty" ini:"logo,omitempty"`
   286  
   287  	// Mini-logo is the top text in the sidebar when folding.
   288  	MiniLogo template.HTML `json:"mini_logo,omitempty" yaml:"mini_logo,omitempty" ini:"mini_logo,omitempty"`
   289  
   290  	// The url redirect to after login.
   291  	IndexUrl string `json:"index,omitempty" yaml:"index,omitempty" ini:"index,omitempty"`
   292  
   293  	// Login page URL
   294  	LoginUrl string `json:"login_url,omitempty" yaml:"login_url,omitempty" ini:"login_url,omitempty"`
   295  
   296  	// Debug mode
   297  	Debug bool `json:"debug,omitempty" yaml:"debug,omitempty" ini:"debug,omitempty"`
   298  
   299  	// Env is the environment,which maybe local,test,prod.
   300  	Env string `json:"env,omitempty" yaml:"env,omitempty" ini:"env,omitempty"`
   301  
   302  	// Info log path.
   303  	InfoLogPath string `json:"info_log,omitempty" yaml:"info_log,omitempty" ini:"info_log,omitempty"`
   304  
   305  	// Error log path.
   306  	ErrorLogPath string `json:"error_log,omitempty" yaml:"error_log,omitempty" ini:"error_log,omitempty"`
   307  
   308  	// Access log path.
   309  	AccessLogPath string `json:"access_log,omitempty" yaml:"access_log,omitempty" ini:"access_log,omitempty"`
   310  
   311  	// Access assets log off
   312  	AccessAssetsLogOff bool `json:"access_assets_log_off,omitempty" yaml:"access_assets_log_off,omitempty" ini:"access_assets_log_off,omitempty"`
   313  
   314  	// Sql operator record log switch.
   315  	SqlLog bool `json:"sql_log,omitempty" yaml:"sql_log,omitempty" ini:"sql_log,omitempty"`
   316  
   317  	AccessLogOff bool `json:"access_log_off,omitempty" yaml:"access_log_off,omitempty" ini:"access_log_off,omitempty"`
   318  	InfoLogOff   bool `json:"info_log_off,omitempty" yaml:"info_log_off,omitempty" ini:"info_log_off,omitempty"`
   319  	ErrorLogOff  bool `json:"error_log_off,omitempty" yaml:"error_log_off,omitempty" ini:"error_log_off,omitempty"`
   320  
   321  	Logger Logger `json:"logger,omitempty" yaml:"logger,omitempty" ini:"logger,omitempty"`
   322  
   323  	// Color scheme.
   324  	ColorScheme string `json:"color_scheme,omitempty" yaml:"color_scheme,omitempty" ini:"color_scheme,omitempty"`
   325  
   326  	// Session valid time duration,units are seconds. Default 7200.
   327  	SessionLifeTime int `json:"session_life_time,omitempty" yaml:"session_life_time,omitempty" ini:"session_life_time,omitempty"`
   328  
   329  	// Assets visit link.
   330  	AssetUrl string `json:"asset_url,omitempty" yaml:"asset_url,omitempty" ini:"asset_url,omitempty"`
   331  
   332  	// File upload engine,default "local"
   333  	FileUploadEngine FileUploadEngine `json:"file_upload_engine,omitempty" yaml:"file_upload_engine,omitempty" ini:"file_upload_engine,omitempty"`
   334  
   335  	// Custom html in the tag head.
   336  	CustomHeadHtml template.HTML `json:"custom_head_html,omitempty" yaml:"custom_head_html,omitempty" ini:"custom_head_html,omitempty"`
   337  
   338  	// Custom html after body.
   339  	CustomFootHtml template.HTML `json:"custom_foot_html,omitempty" yaml:"custom_foot_html,omitempty" ini:"custom_foot_html,omitempty"`
   340  
   341  	// Footer Info html
   342  	FooterInfo template.HTML `json:"footer_info,omitempty" yaml:"footer_info,omitempty" ini:"footer_info,omitempty"`
   343  
   344  	// Login page title
   345  	LoginTitle string `json:"login_title,omitempty" yaml:"login_title,omitempty" ini:"login_title,omitempty"`
   346  
   347  	// Login page logo
   348  	LoginLogo template.HTML `json:"login_logo,omitempty" yaml:"login_logo,omitempty" ini:"login_logo,omitempty"`
   349  
   350  	// Auth user table
   351  	AuthUserTable string `json:"auth_user_table,omitempty" yaml:"auth_user_table,omitempty" ini:"auth_user_table,omitempty"`
   352  
   353  	// Extra config info
   354  	Extra ExtraInfo `json:"extra,omitempty" yaml:"extra,omitempty" ini:"extra,omitempty"`
   355  
   356  	// Page animation
   357  	Animation PageAnimation `json:"animation,omitempty" yaml:"animation,omitempty" ini:"animation,omitempty"`
   358  
   359  	// Limit login with different IPs
   360  	NoLimitLoginIP bool `json:"no_limit_login_ip,omitempty" yaml:"no_limit_login_ip,omitempty" ini:"no_limit_login_ip,omitempty"`
   361  
   362  	// When site off is true, website will be closed
   363  	SiteOff bool `json:"site_off,omitempty" yaml:"site_off,omitempty" ini:"site_off,omitempty"`
   364  
   365  	// Hide config center entrance flag
   366  	HideConfigCenterEntrance bool `json:"hide_config_center_entrance,omitempty" yaml:"hide_config_center_entrance,omitempty" ini:"hide_config_center_entrance,omitempty"`
   367  
   368  	// Prohibit config modification
   369  	ProhibitConfigModification bool `json:"prohibit_config_modification,omitempty" yaml:"prohibit_config_modification,omitempty" ini:"prohibit_config_modification,omitempty"`
   370  
   371  	// Hide app info entrance flag
   372  	HideAppInfoEntrance bool `json:"hide_app_info_entrance,omitempty" yaml:"hide_app_info_entrance,omitempty" ini:"hide_app_info_entrance,omitempty"`
   373  
   374  	// Hide tool entrance flag
   375  	HideToolEntrance bool `json:"hide_tool_entrance,omitempty" yaml:"hide_tool_entrance,omitempty" ini:"hide_tool_entrance,omitempty"`
   376  
   377  	HidePluginEntrance bool `json:"hide_plugin_entrance,omitempty" yaml:"hide_plugin_entrance,omitempty" ini:"hide_plugin_entrance,omitempty"`
   378  
   379  	Custom404HTML template.HTML `json:"custom_404_html,omitempty" yaml:"custom_404_html,omitempty" ini:"custom_404_html,omitempty"`
   380  
   381  	Custom403HTML template.HTML `json:"custom_403_html,omitempty" yaml:"custom_403_html,omitempty" ini:"custom_403_html,omitempty"`
   382  
   383  	Custom500HTML template.HTML `json:"custom_500_html,omitempty" yaml:"custom_500_html,omitempty" ini:"custom_500_html,omitempty"`
   384  
   385  	// Update Process Function
   386  	UpdateProcessFn UpdateConfigProcessFn `json:"-" yaml:"-" ini:"-"`
   387  
   388  	// Favicon string `json:"favicon,omitempty" yaml:"favicon,omitempty" ini:"favicon,omitempty"`
   389  
   390  	// Is open admin plugin json api
   391  	OpenAdminApi bool `json:"open_admin_api,omitempty" yaml:"open_admin_api,omitempty" ini:"open_admin_api,omitempty"`
   392  
   393  	HideVisitorUserCenterEntrance bool `json:"hide_visitor_user_center_entrance,omitempty" yaml:"hide_visitor_user_center_entrance,omitempty" ini:"hide_visitor_user_center_entrance,omitempty"`
   394  
   395  	ExcludeThemeComponents []string `json:"exclude_theme_components,omitempty" yaml:"exclude_theme_components,omitempty" ini:"exclude_theme_components,omitempty"`
   396  
   397  	BootstrapFilePath string `json:"bootstrap_file_path,omitempty" yaml:"bootstrap_file_path,omitempty" ini:"bootstrap_file_path,omitempty"`
   398  
   399  	GoModFilePath string `json:"go_mod_file_path,omitempty" yaml:"go_mod_file_path,omitempty" ini:"go_mod_file_path,omitempty"`
   400  
   401  	AllowDelOperationLog bool `json:"allow_del_operation_log,omitempty" yaml:"allow_del_operation_log,omitempty" ini:"allow_del_operation_log,omitempty"`
   402  
   403  	OperationLogOff bool `json:"operation_log_off,omitempty" yaml:"operation_log_off,omitempty" ini:"operation_log_off,omitempty"`
   404  
   405  	AssetRootPath string `json:"asset_root_path,omitempty" yaml:"asset_root_path,omitempty" ini:"asset_root_path,omitempty"`
   406  
   407  	URLFormat URLFormat `json:"url_format,omitempty" yaml:"url_format,omitempty" ini:"url_format,omitempty"`
   408  
   409  	prefix string       `json:"-" yaml:"-" ini:"-"`
   410  	lock   sync.RWMutex `json:"-" yaml:"-" ini:"-"`
   411  }
   412  
   413  type Logger struct {
   414  	Encoder EncoderCfg `json:"encoder,omitempty" yaml:"encoder,omitempty" ini:"encoder,omitempty"`
   415  	Rotate  RotateCfg  `json:"rotate,omitempty" yaml:"rotate,omitempty" ini:"rotate,omitempty"`
   416  	Level   int8       `json:"level,omitempty" yaml:"level,omitempty" ini:"level,omitempty"`
   417  }
   418  
   419  type EncoderCfg struct {
   420  	TimeKey       string `json:"time_key,omitempty" yaml:"time_key,omitempty" ini:"time_key,omitempty"`
   421  	LevelKey      string `json:"level_key,omitempty" yaml:"level_key,omitempty" ini:"level_key,omitempty"`
   422  	NameKey       string `json:"name_key,omitempty" yaml:"name_key,omitempty" ini:"name_key,omitempty"`
   423  	CallerKey     string `json:"caller_key,omitempty" yaml:"caller_key,omitempty" ini:"caller_key,omitempty"`
   424  	MessageKey    string `json:"message_key,omitempty" yaml:"message_key,omitempty" ini:"message_key,omitempty"`
   425  	StacktraceKey string `json:"stacktrace_key,omitempty" yaml:"stacktrace_key,omitempty" ini:"stacktrace_key,omitempty"`
   426  	Level         string `json:"level,omitempty" yaml:"level,omitempty" ini:"level,omitempty"`
   427  	Time          string `json:"time,omitempty" yaml:"time,omitempty" ini:"time,omitempty"`
   428  	Duration      string `json:"duration,omitempty" yaml:"duration,omitempty" ini:"duration,omitempty"`
   429  	Caller        string `json:"caller,omitempty" yaml:"caller,omitempty" ini:"caller,omitempty"`
   430  	Encoding      string `json:"encoding,omitempty" yaml:"encoding,omitempty" ini:"encoding,omitempty"`
   431  }
   432  
   433  type RotateCfg struct {
   434  	MaxSize    int  `json:"max_size,omitempty" yaml:"max_size,omitempty" ini:"max_size,omitempty"`
   435  	MaxBackups int  `json:"max_backups,omitempty" yaml:"max_backups,omitempty" ini:"max_backups,omitempty"`
   436  	MaxAge     int  `json:"max_age,omitempty" yaml:"max_age,omitempty" ini:"max_age,omitempty"`
   437  	Compress   bool `json:"compress,omitempty" yaml:"compress,omitempty" ini:"compress,omitempty"`
   438  }
   439  
   440  type URLFormat struct {
   441  	Info       string `json:"info,omitempty" yaml:"info,omitempty" ini:"info,omitempty"`
   442  	Detail     string `json:"detail,omitempty" yaml:"detail,omitempty" ini:"detail,omitempty"`
   443  	Create     string `json:"create,omitempty" yaml:"create,omitempty" ini:"create,omitempty"`
   444  	Delete     string `json:"delete,omitempty" yaml:"delete,omitempty" ini:"delete,omitempty"`
   445  	Export     string `json:"export,omitempty" yaml:"export,omitempty" ini:"export,omitempty"`
   446  	Edit       string `json:"edit,omitempty" yaml:"edit,omitempty" ini:"edit,omitempty"`
   447  	ShowEdit   string `json:"show_edit,omitempty" yaml:"show_edit,omitempty" ini:"show_edit,omitempty"`
   448  	ShowCreate string `json:"show_create,omitempty" yaml:"show_create,omitempty" ini:"show_create,omitempty"`
   449  	Update     string `json:"update,omitempty" yaml:"update,omitempty" ini:"update,omitempty"`
   450  }
   451  
   452  func (f URLFormat) SetDefault() URLFormat {
   453  	f.Detail = utils.SetDefault(f.Detail, "", "/info/:__prefix/detail")
   454  	f.ShowEdit = utils.SetDefault(f.ShowEdit, "", "/info/:__prefix/edit")
   455  	f.ShowCreate = utils.SetDefault(f.ShowCreate, "", "/info/:__prefix/new")
   456  	f.Edit = utils.SetDefault(f.Edit, "", "/edit/:__prefix")
   457  	f.Create = utils.SetDefault(f.Create, "", "/new/:__prefix")
   458  	f.Delete = utils.SetDefault(f.Delete, "", "/delete/:__prefix")
   459  	f.Export = utils.SetDefault(f.Export, "", "/export/:__prefix")
   460  	f.Info = utils.SetDefault(f.Info, "", "/info/:__prefix")
   461  	f.Update = utils.SetDefault(f.Update, "", "/update/:__prefix")
   462  	return f
   463  }
   464  
   465  type ExtraInfo map[string]interface{}
   466  
   467  type UpdateConfigProcessFn func(values form.Values) (form.Values, error)
   468  
   469  // see more: https://daneden.github.io/animate.css/
   470  type PageAnimation struct {
   471  	Type     string  `json:"type,omitempty" yaml:"type,omitempty" ini:"type,omitempty"`
   472  	Duration float32 `json:"duration,omitempty" yaml:"duration,omitempty" ini:"duration,omitempty"`
   473  	Delay    float32 `json:"delay,omitempty" yaml:"delay,omitempty" ini:"delay,omitempty"`
   474  }
   475  
   476  func (p PageAnimation) JSON() string {
   477  	if p.Type == "" {
   478  		return ""
   479  	}
   480  	return utils.JSON(p)
   481  }
   482  
   483  // FileUploadEngine is a file upload engine.
   484  type FileUploadEngine struct {
   485  	Name   string                 `json:"name,omitempty" yaml:"name,omitempty" ini:"name,omitempty"`
   486  	Config map[string]interface{} `json:"config,omitempty" yaml:"config,omitempty" ini:"config,omitempty"`
   487  }
   488  
   489  func (f FileUploadEngine) JSON() string {
   490  	if f.Name == "" {
   491  		return ""
   492  	}
   493  	if len(f.Config) == 0 {
   494  		f.Config = nil
   495  	}
   496  	return utils.JSON(f)
   497  }
   498  
   499  func GetFileUploadEngineFromJSON(m string) FileUploadEngine {
   500  	var f FileUploadEngine
   501  	if m == "" {
   502  		return f
   503  	}
   504  	_ = json.Unmarshal([]byte(m), &f)
   505  	return f
   506  }
   507  
   508  // GetIndexURL get the index url with prefix.
   509  func (c *Config) GetIndexURL() string {
   510  	index := c.Index()
   511  	if index == "/" {
   512  		return c.Prefix()
   513  	}
   514  
   515  	return c.Prefix() + index
   516  }
   517  
   518  // Url get url with the given suffix.
   519  func (c *Config) Url(suffix string) string {
   520  	if c.prefix == "/" {
   521  		return suffix
   522  	}
   523  	if suffix == "/" {
   524  		return c.prefix
   525  	}
   526  	return c.prefix + suffix
   527  }
   528  
   529  // IsTestEnvironment check the environment if it is test.
   530  func (c *Config) IsTestEnvironment() bool {
   531  	return c.Env == EnvTest
   532  }
   533  
   534  // IsLocalEnvironment check the environment if it is local.
   535  func (c *Config) IsLocalEnvironment() bool {
   536  	return c.Env == EnvLocal
   537  }
   538  
   539  // IsProductionEnvironment check the environment if it is production.
   540  func (c *Config) IsProductionEnvironment() bool {
   541  	return c.Env == EnvProd
   542  }
   543  
   544  // IsNotProductionEnvironment check the environment if it is not production.
   545  func (c *Config) IsNotProductionEnvironment() bool {
   546  	return c.Env != EnvProd
   547  }
   548  
   549  func (c *Config) IsAllowConfigModification() bool {
   550  	return !c.ProhibitConfigModification
   551  }
   552  
   553  // URLRemovePrefix remove prefix from the given url.
   554  func (c *Config) URLRemovePrefix(url string) string {
   555  	if url == c.prefix {
   556  		return "/"
   557  	}
   558  	if c.prefix == "/" {
   559  		return url
   560  	}
   561  	return strings.Replace(url, c.prefix, "", 1)
   562  }
   563  
   564  // Index return the index url without prefix.
   565  func (c *Config) Index() string {
   566  	if c.IndexUrl == "" {
   567  		return "/"
   568  	}
   569  	if c.IndexUrl[0] != '/' {
   570  		return "/" + c.IndexUrl
   571  	}
   572  	return c.IndexUrl
   573  }
   574  
   575  // Prefix return the prefix.
   576  func (c *Config) Prefix() string {
   577  	return c.prefix
   578  }
   579  
   580  // AssertPrefix return the prefix of assert.
   581  func (c *Config) AssertPrefix() string {
   582  	if c.prefix == "/" {
   583  		return ""
   584  	}
   585  	return c.prefix
   586  }
   587  
   588  func (c *Config) AddUpdateProcessFn(fn UpdateConfigProcessFn) *Config {
   589  	c.UpdateProcessFn = fn
   590  	return c
   591  }
   592  
   593  // PrefixFixSlash return the prefix fix the slash error.
   594  func (c *Config) PrefixFixSlash() string {
   595  	if c.UrlPrefix == "/" {
   596  		return ""
   597  	}
   598  	if c.UrlPrefix != "" && c.UrlPrefix[0] != '/' {
   599  		return "/" + c.UrlPrefix
   600  	}
   601  	return c.UrlPrefix
   602  }
   603  
   604  func (c *Config) Copy() *Config {
   605  
   606  	c.lock.RLock()
   607  	defer c.lock.RUnlock()
   608  
   609  	var (
   610  		newCfg   = new(Config)
   611  		srcType  = reflect.TypeOf(c).Elem()
   612  		srcVal   = reflect.ValueOf(c).Elem()
   613  		distType = reflect.TypeOf(newCfg).Elem()
   614  		distVal  = reflect.ValueOf(newCfg).Elem()
   615  	)
   616  
   617  	for i := 0; i < distType.NumField(); i++ {
   618  		v := distVal.Field(i)
   619  		if distType.Field(i).Type.String() == "config.DatabaseList" {
   620  			newCfg.Databases = c.Databases.Copy()
   621  		} else if v.CanInterface() {
   622  			for j := 0; j < srcType.NumField(); j++ {
   623  				if distType.Field(i).Name == srcType.Field(j).Name {
   624  					v.Set(reflect.ValueOf(srcVal.Field(i).Interface()))
   625  					break
   626  				}
   627  			}
   628  		}
   629  	}
   630  
   631  	newCfg.prefix = c.prefix
   632  
   633  	return newCfg
   634  }
   635  
   636  func (c *Config) ToMap() map[string]string {
   637  	c.lock.RLock()
   638  	defer c.lock.RUnlock()
   639  
   640  	var (
   641  		m     = make(map[string]string)
   642  		rType = reflect.TypeOf(c).Elem()
   643  		rVal  = reflect.ValueOf(c).Elem()
   644  	)
   645  
   646  	for i := 0; i < rType.NumField(); i++ {
   647  		v := rVal.Field(i)
   648  		if !v.CanInterface() {
   649  			continue
   650  		}
   651  		t := rType.Field(i)
   652  		keyName := t.Tag.Get("json")
   653  		if keyName == "-" {
   654  			continue
   655  		}
   656  		keyName = keyName[:len(keyName)-10]
   657  		switch t.Type.Kind() {
   658  		case reflect.Bool:
   659  			m[keyName] = strconv.FormatBool(v.Bool())
   660  		case reflect.String:
   661  			if keyName == "prefix" {
   662  				keyName = "url_prefix"
   663  			} else if keyName == "index" {
   664  				keyName = "index_url"
   665  			} else if keyName == "info_log" || keyName == "error_log" || keyName == "access_log" {
   666  				keyName += "_path"
   667  			}
   668  			m[keyName] = v.String()
   669  		case reflect.Int:
   670  			m[keyName] = fmt.Sprintf("%d", v.Int())
   671  		case reflect.Struct:
   672  			switch t.Type.String() {
   673  			case "config.PageAnimation":
   674  				m["animation_type"] = c.Animation.Type
   675  				m["animation_duration"] = fmt.Sprintf("%.2f", c.Animation.Duration)
   676  				m["animation_delay"] = fmt.Sprintf("%.2f", c.Animation.Delay)
   677  			case "config.Logger":
   678  				m["logger_rotate_max_size"] = strconv.Itoa(c.Logger.Rotate.MaxSize)
   679  				m["logger_rotate_max_backups"] = strconv.Itoa(c.Logger.Rotate.MaxBackups)
   680  				m["logger_rotate_max_age"] = strconv.Itoa(c.Logger.Rotate.MaxAge)
   681  				m["logger_rotate_compress"] = strconv.FormatBool(c.Logger.Rotate.Compress)
   682  
   683  				m["logger_encoder_time_key"] = c.Logger.Encoder.TimeKey
   684  				m["logger_encoder_level_key"] = c.Logger.Encoder.LevelKey
   685  				m["logger_encoder_name_key"] = c.Logger.Encoder.NameKey
   686  				m["logger_encoder_caller_key"] = c.Logger.Encoder.CallerKey
   687  				m["logger_encoder_message_key"] = c.Logger.Encoder.MessageKey
   688  				m["logger_encoder_stacktrace_key"] = c.Logger.Encoder.StacktraceKey
   689  				m["logger_encoder_level"] = c.Logger.Encoder.Level
   690  				m["logger_encoder_time"] = c.Logger.Encoder.Time
   691  				m["logger_encoder_duration"] = c.Logger.Encoder.Duration
   692  				m["logger_encoder_caller"] = c.Logger.Encoder.Caller
   693  				m["logger_encoder_encoding"] = c.Logger.Encoder.Encoding
   694  				m["logger_level"] = strconv.Itoa(int(c.Logger.Level))
   695  			case "config.DatabaseList":
   696  				m["databases"] = utils.JSON(v.Interface())
   697  			case "config.FileUploadEngine":
   698  				m["file_upload_engine"] = c.FileUploadEngine.JSON()
   699  			}
   700  		case reflect.Map:
   701  			if t.Type.String() == "config.ExtraInfo" {
   702  				if len(c.Extra) == 0 {
   703  					m["extra"] = ""
   704  				} else {
   705  					m["extra"] = utils.JSON(c.Extra)
   706  				}
   707  			}
   708  		default:
   709  			m[keyName] = utils.JSON(v.Interface())
   710  		}
   711  	}
   712  	return m
   713  }
   714  
   715  func (c *Config) Update(m map[string]string) error {
   716  	c.lock.Lock()
   717  	defer c.lock.Unlock()
   718  	rType := reflect.TypeOf(c).Elem()
   719  	rVal := reflect.ValueOf(c).Elem()
   720  	for i := 0; i < rType.NumField(); i++ {
   721  		v := rVal.Field(i)
   722  		if !v.CanInterface() {
   723  			continue
   724  		}
   725  		t := rType.Field(i)
   726  		keyName := t.Tag.Get("json")
   727  		if keyName == "-" {
   728  			continue
   729  		}
   730  		keyName = keyName[:len(keyName)-10]
   731  		switch t.Type.Kind() {
   732  		case reflect.Bool:
   733  			if mv, ok := m[keyName]; ok {
   734  				v.Set(reflect.ValueOf(utils.ParseBool(mv)))
   735  			}
   736  		case reflect.String:
   737  			if t.Type.String() == "template.HTML" {
   738  				if mv, ok := m[keyName]; ok {
   739  					v.Set(reflect.ValueOf(template.HTML(mv)))
   740  				}
   741  				continue
   742  			}
   743  			if keyName == "prefix" {
   744  				keyName = "url_prefix"
   745  			} else if keyName == "index" {
   746  				keyName = "index_url"
   747  			} else if keyName == "info_log" || keyName == "error_log" || keyName == "access_log" {
   748  				keyName += "_path"
   749  			}
   750  			if mv, ok := m[keyName]; ok {
   751  				if keyName == "info_log" || keyName == "error_log" || keyName == "access_log" {
   752  					v.Set(reflect.ValueOf(utils.SetDefault(mv, v.String(), v.String())))
   753  				} else if keyName == "app_id" {
   754  					v.Set(reflect.ValueOf(utils.SetDefault(mv, "", v.String())))
   755  				} else if keyName == "color_scheme" {
   756  					if m["theme"] == "adminlte" {
   757  						v.Set(reflect.ValueOf(mv))
   758  					}
   759  				} else {
   760  					v.Set(reflect.ValueOf(mv))
   761  				}
   762  			}
   763  		case reflect.Int:
   764  			ses, _ := strconv.Atoi(m[keyName])
   765  			if ses != 0 {
   766  				v.Set(reflect.ValueOf(ses))
   767  			}
   768  		case reflect.Struct:
   769  			switch t.Type.String() {
   770  			case "config.PageAnimation":
   771  				c.Animation.Type = m["animation_type"]
   772  				c.Animation.Duration = utils.ParseFloat32(m["animation_duration"])
   773  				c.Animation.Delay = utils.ParseFloat32(m["animation_delay"])
   774  			case "config.Logger":
   775  				c.Logger.Rotate.MaxSize, _ = strconv.Atoi(m["logger_rotate_max_size"])
   776  				c.Logger.Rotate.MaxBackups, _ = strconv.Atoi(m["logger_rotate_max_backups"])
   777  				c.Logger.Rotate.MaxAge, _ = strconv.Atoi(m["logger_rotate_max_age"])
   778  				c.Logger.Rotate.Compress = utils.ParseBool(m["logger_rotate_compress"])
   779  
   780  				c.Logger.Encoder.Encoding = m["logger_encoder_encoding"]
   781  				loggerLevel, _ := strconv.Atoi(m["logger_level"])
   782  				c.Logger.Level = int8(loggerLevel)
   783  
   784  				if c.Logger.Encoder.Encoding == "json" {
   785  					c.Logger.Encoder.TimeKey = m["logger_encoder_time_key"]
   786  					c.Logger.Encoder.LevelKey = m["logger_encoder_level_key"]
   787  					c.Logger.Encoder.NameKey = m["logger_encoder_name_key"]
   788  					c.Logger.Encoder.CallerKey = m["logger_encoder_caller_key"]
   789  					c.Logger.Encoder.MessageKey = m["logger_encoder_message_key"]
   790  					c.Logger.Encoder.StacktraceKey = m["logger_encoder_stacktrace_key"]
   791  					c.Logger.Encoder.Level = m["logger_encoder_level"]
   792  					c.Logger.Encoder.Time = m["logger_encoder_time"]
   793  					c.Logger.Encoder.Duration = m["logger_encoder_duration"]
   794  					c.Logger.Encoder.Caller = m["logger_encoder_caller"]
   795  				}
   796  
   797  				initLogger(c)
   798  			case "config.FileUploadEngine":
   799  				c.FileUploadEngine = GetFileUploadEngineFromJSON(m["file_upload_engine"])
   800  			}
   801  		case reflect.Map:
   802  			if t.Type.String() == "config.ExtraInfo" && m["extra"] != "" {
   803  				var extra = make(map[string]interface{})
   804  				_ = json.Unmarshal([]byte(m["extra"]), &extra)
   805  				c.Extra = extra
   806  			}
   807  		}
   808  	}
   809  	return nil
   810  }
   811  
   812  // eraseSens erase sensitive info.
   813  func (c *Config) EraseSens() *Config {
   814  	for key := range c.Databases {
   815  		c.Databases[key] = Database{
   816  			Driver: c.Databases[key].Driver,
   817  		}
   818  	}
   819  	return c
   820  }
   821  
   822  var (
   823  	_global        = new(Config)
   824  	count          uint32
   825  	initializeLock sync.Mutex
   826  )
   827  
   828  // ReadFromJson read the Config from a JSON file.
   829  func ReadFromJson(path string) Config {
   830  	jsonByte, err := ioutil.ReadFile(path)
   831  
   832  	if err != nil {
   833  		panic(err)
   834  	}
   835  
   836  	var cfg Config
   837  
   838  	err = json.Unmarshal(jsonByte, &cfg)
   839  
   840  	if err != nil {
   841  		panic(err)
   842  	}
   843  
   844  	return cfg
   845  }
   846  
   847  // ReadFromYaml read the Config from a YAML file.
   848  func ReadFromYaml(path string) Config {
   849  	jsonByte, err := ioutil.ReadFile(path)
   850  
   851  	if err != nil {
   852  		panic(err)
   853  	}
   854  
   855  	var cfg Config
   856  
   857  	err = yaml.Unmarshal(jsonByte, &cfg)
   858  
   859  	if err != nil {
   860  		panic(err)
   861  	}
   862  
   863  	return cfg
   864  }
   865  
   866  // ReadFromINI read the Config from a INI file.
   867  func ReadFromINI(path string) Config {
   868  	iniCfg, err := ini.Load(path)
   869  
   870  	if err != nil {
   871  		panic(err)
   872  	}
   873  
   874  	var cfg = Config{
   875  		Databases: make(DatabaseList),
   876  	}
   877  
   878  	err = iniCfg.MapTo(&cfg)
   879  
   880  	if err != nil {
   881  		panic(err)
   882  	}
   883  
   884  	for _, child := range iniCfg.ChildSections("database") {
   885  		var d Database
   886  		err = child.MapTo(&d)
   887  		if err != nil {
   888  			panic(err)
   889  		}
   890  		cfg.Databases[child.Name()[9:]] = d
   891  	}
   892  
   893  	return cfg
   894  }
   895  
   896  func SetDefault(cfg *Config) *Config {
   897  	cfg.Title = utils.SetDefault(cfg.Title, "", "GoAdmin")
   898  	cfg.LoginTitle = utils.SetDefault(cfg.LoginTitle, "", "GoAdmin")
   899  	cfg.Logo = template.HTML(utils.SetDefault(string(cfg.Logo), "", "<b>Go</b>Admin"))
   900  	cfg.MiniLogo = template.HTML(utils.SetDefault(string(cfg.MiniLogo), "", "<b>G</b>A"))
   901  	cfg.Theme = utils.SetDefault(cfg.Theme, "", "adminlte")
   902  	cfg.IndexUrl = utils.SetDefault(cfg.IndexUrl, "", "/info/manager")
   903  	cfg.LoginUrl = utils.SetDefault(cfg.LoginUrl, "", "/login")
   904  	cfg.AuthUserTable = utils.SetDefault(cfg.AuthUserTable, "", "goadmin_users")
   905  	if cfg.Theme == "adminlte" {
   906  		cfg.ColorScheme = utils.SetDefault(cfg.ColorScheme, "", "skin-black")
   907  	}
   908  	cfg.AssetRootPath = utils.SetDefault(cfg.AssetRootPath, "", "./public/")
   909  	cfg.AssetRootPath = filepath.ToSlash(cfg.AssetRootPath)
   910  	cfg.FileUploadEngine.Name = utils.SetDefault(cfg.FileUploadEngine.Name, "", "local")
   911  	cfg.Env = utils.SetDefault(cfg.Env, "", EnvProd)
   912  	if cfg.SessionLifeTime == 0 {
   913  		// default two hours
   914  		cfg.SessionLifeTime = 7200
   915  	}
   916  	cfg.AppID = utils.Uuid(12)
   917  	if cfg.UrlPrefix == "" {
   918  		cfg.prefix = "/"
   919  	} else if cfg.UrlPrefix[0] != '/' {
   920  		cfg.prefix = "/" + cfg.UrlPrefix
   921  	} else {
   922  		cfg.prefix = cfg.UrlPrefix
   923  	}
   924  	cfg.URLFormat = cfg.URLFormat.SetDefault()
   925  	return cfg
   926  }
   927  
   928  // Initialize initialize the config.
   929  func Initialize(cfg *Config) *Config {
   930  
   931  	initializeLock.Lock()
   932  	defer initializeLock.Unlock()
   933  
   934  	if atomic.LoadUint32(&count) != 0 {
   935  		panic("can not initialize config twice")
   936  	}
   937  	atomic.StoreUint32(&count, 1)
   938  
   939  	initLogger(SetDefault(cfg))
   940  
   941  	_global = cfg
   942  
   943  	return _global
   944  }
   945  
   946  func initLogger(cfg *Config) {
   947  	logger.InitWithConfig(logger.Config{
   948  		InfoLogOff:         cfg.InfoLogOff,
   949  		ErrorLogOff:        cfg.ErrorLogOff,
   950  		AccessLogOff:       cfg.AccessLogOff,
   951  		SqlLogOpen:         cfg.SqlLog,
   952  		InfoLogPath:        cfg.InfoLogPath,
   953  		ErrorLogPath:       cfg.ErrorLogPath,
   954  		AccessLogPath:      cfg.AccessLogPath,
   955  		AccessAssetsLogOff: cfg.AccessAssetsLogOff,
   956  		Rotate: logger.RotateCfg{
   957  			MaxSize:    cfg.Logger.Rotate.MaxSize,
   958  			MaxBackups: cfg.Logger.Rotate.MaxBackups,
   959  			MaxAge:     cfg.Logger.Rotate.MaxAge,
   960  			Compress:   cfg.Logger.Rotate.Compress,
   961  		},
   962  		Encode: logger.EncoderCfg{
   963  			TimeKey:       cfg.Logger.Encoder.TimeKey,
   964  			LevelKey:      cfg.Logger.Encoder.LevelKey,
   965  			NameKey:       cfg.Logger.Encoder.NameKey,
   966  			CallerKey:     cfg.Logger.Encoder.CallerKey,
   967  			MessageKey:    cfg.Logger.Encoder.MessageKey,
   968  			StacktraceKey: cfg.Logger.Encoder.StacktraceKey,
   969  			Level:         cfg.Logger.Encoder.Level,
   970  			Time:          cfg.Logger.Encoder.Time,
   971  			Duration:      cfg.Logger.Encoder.Duration,
   972  			Caller:        cfg.Logger.Encoder.Caller,
   973  			Encoding:      cfg.Logger.Encoder.Encoding,
   974  		},
   975  		Debug: cfg.Debug,
   976  		Level: cfg.Logger.Level,
   977  	})
   978  }
   979  
   980  // AssertPrefix return the prefix of assert.
   981  func AssertPrefix() string {
   982  	return _global.AssertPrefix()
   983  }
   984  
   985  // GetIndexURL get the index url with prefix.
   986  func GetIndexURL() string {
   987  	return _global.GetIndexURL()
   988  }
   989  
   990  // IsProductionEnvironment check the environment if it is production.
   991  func IsProductionEnvironment() bool {
   992  	return _global.IsProductionEnvironment()
   993  }
   994  
   995  // IsNotProductionEnvironment check the environment if it is not production.
   996  func IsNotProductionEnvironment() bool {
   997  	return _global.IsNotProductionEnvironment()
   998  }
   999  
  1000  // URLRemovePrefix remove prefix from the given url.
  1001  func URLRemovePrefix(url string) string {
  1002  	return _global.URLRemovePrefix(url)
  1003  }
  1004  
  1005  func Url(suffix string) string {
  1006  	return _global.Url(suffix)
  1007  }
  1008  
  1009  func GetURLFormats() URLFormat {
  1010  	return _global.URLFormat
  1011  }
  1012  
  1013  // Prefix return the prefix.
  1014  func Prefix() string {
  1015  	return _global.prefix
  1016  }
  1017  
  1018  // PrefixFixSlash return the prefix fix the slash error.
  1019  func PrefixFixSlash() string {
  1020  	return _global.PrefixFixSlash()
  1021  }
  1022  
  1023  // Get gets the config.
  1024  func Get() *Config {
  1025  	_global.lock.RLock()
  1026  	defer _global.lock.RUnlock()
  1027  
  1028  	return _global.Copy().EraseSens()
  1029  }
  1030  
  1031  // Getter methods
  1032  // ============================
  1033  
  1034  func GetDatabases() DatabaseList {
  1035  	var list = make(DatabaseList, len(_global.Databases))
  1036  	for key := range _global.Databases {
  1037  		list[key] = Database{
  1038  			Driver:     _global.Databases[key].Driver,
  1039  			DriverMode: _global.Databases[key].DriverMode,
  1040  		}
  1041  	}
  1042  	return list
  1043  }
  1044  
  1045  func GetDomain() string {
  1046  	_global.lock.RLock()
  1047  	defer _global.lock.RUnlock()
  1048  	return _global.Domain
  1049  }
  1050  
  1051  func GetLanguage() string {
  1052  	_global.lock.RLock()
  1053  	defer _global.lock.RUnlock()
  1054  	return _global.Language
  1055  }
  1056  
  1057  func GetAppID() string {
  1058  	_global.lock.RLock()
  1059  	defer _global.lock.RUnlock()
  1060  	return _global.AppID
  1061  }
  1062  
  1063  func GetUrlPrefix() string {
  1064  	_global.lock.RLock()
  1065  	defer _global.lock.RUnlock()
  1066  	return _global.UrlPrefix
  1067  }
  1068  
  1069  func GetOpenAdminApi() bool {
  1070  	_global.lock.RLock()
  1071  	defer _global.lock.RUnlock()
  1072  	return _global.OpenAdminApi
  1073  }
  1074  
  1075  func GetAllowDelOperationLog() bool {
  1076  	_global.lock.RLock()
  1077  	defer _global.lock.RUnlock()
  1078  	return _global.AllowDelOperationLog
  1079  }
  1080  
  1081  func GetOperationLogOff() bool {
  1082  	_global.lock.RLock()
  1083  	defer _global.lock.RUnlock()
  1084  	return _global.OperationLogOff
  1085  }
  1086  
  1087  func GetCustom500HTML() template.HTML {
  1088  	_global.lock.RLock()
  1089  	defer _global.lock.RUnlock()
  1090  	return _global.Custom500HTML
  1091  }
  1092  
  1093  func GetCustom404HTML() template.HTML {
  1094  	_global.lock.RLock()
  1095  	defer _global.lock.RUnlock()
  1096  	return _global.Custom404HTML
  1097  }
  1098  
  1099  func GetCustom403HTML() template.HTML {
  1100  	_global.lock.RLock()
  1101  	defer _global.lock.RUnlock()
  1102  	return _global.Custom403HTML
  1103  }
  1104  
  1105  func GetTheme() string {
  1106  	_global.lock.RLock()
  1107  	defer _global.lock.RUnlock()
  1108  	return _global.Theme
  1109  }
  1110  
  1111  func GetStore() Store {
  1112  	_global.lock.RLock()
  1113  	defer _global.lock.RUnlock()
  1114  	return _global.Store
  1115  }
  1116  
  1117  func GetTitle() string {
  1118  	_global.lock.RLock()
  1119  	defer _global.lock.RUnlock()
  1120  	return _global.Title
  1121  }
  1122  
  1123  func GetAssetRootPath() string {
  1124  	_global.lock.RLock()
  1125  	defer _global.lock.RUnlock()
  1126  	return _global.AssetRootPath
  1127  }
  1128  
  1129  func GetLogo() template.HTML {
  1130  	_global.lock.RLock()
  1131  	defer _global.lock.RUnlock()
  1132  	return _global.Logo
  1133  }
  1134  
  1135  func GetSiteOff() bool {
  1136  	_global.lock.RLock()
  1137  	defer _global.lock.RUnlock()
  1138  	return _global.SiteOff
  1139  }
  1140  
  1141  func GetMiniLogo() template.HTML {
  1142  	_global.lock.RLock()
  1143  	defer _global.lock.RUnlock()
  1144  	return _global.MiniLogo
  1145  }
  1146  
  1147  func GetIndexUrl() string {
  1148  	_global.lock.RLock()
  1149  	defer _global.lock.RUnlock()
  1150  	return _global.IndexUrl
  1151  }
  1152  
  1153  func GetLoginUrl() string {
  1154  	_global.lock.RLock()
  1155  	defer _global.lock.RUnlock()
  1156  	return _global.LoginUrl
  1157  }
  1158  
  1159  func GetDebug() bool {
  1160  	_global.lock.RLock()
  1161  	defer _global.lock.RUnlock()
  1162  	return _global.Debug
  1163  }
  1164  
  1165  func GetEnv() string {
  1166  	_global.lock.RLock()
  1167  	defer _global.lock.RUnlock()
  1168  	return _global.Env
  1169  }
  1170  
  1171  func GetInfoLogPath() string {
  1172  	_global.lock.RLock()
  1173  	defer _global.lock.RUnlock()
  1174  	return _global.InfoLogPath
  1175  }
  1176  
  1177  func GetErrorLogPath() string {
  1178  	_global.lock.RLock()
  1179  	defer _global.lock.RUnlock()
  1180  	return _global.ErrorLogPath
  1181  }
  1182  
  1183  func GetAccessLogPath() string {
  1184  	_global.lock.RLock()
  1185  	defer _global.lock.RUnlock()
  1186  	return _global.AccessLogPath
  1187  }
  1188  
  1189  func GetSqlLog() bool {
  1190  	_global.lock.RLock()
  1191  	defer _global.lock.RUnlock()
  1192  	return _global.SqlLog
  1193  }
  1194  
  1195  func GetAccessLogOff() bool {
  1196  	_global.lock.RLock()
  1197  	defer _global.lock.RUnlock()
  1198  	return _global.AccessLogOff
  1199  }
  1200  func GetInfoLogOff() bool {
  1201  	_global.lock.RLock()
  1202  	defer _global.lock.RUnlock()
  1203  	return _global.InfoLogOff
  1204  }
  1205  func GetErrorLogOff() bool {
  1206  	_global.lock.RLock()
  1207  	defer _global.lock.RUnlock()
  1208  	return _global.ErrorLogOff
  1209  }
  1210  
  1211  func GetColorScheme() string {
  1212  	_global.lock.RLock()
  1213  	defer _global.lock.RUnlock()
  1214  	return _global.ColorScheme
  1215  }
  1216  
  1217  func GetSessionLifeTime() int {
  1218  	_global.lock.RLock()
  1219  	defer _global.lock.RUnlock()
  1220  	return _global.SessionLifeTime
  1221  }
  1222  
  1223  func GetAssetUrl() string {
  1224  	_global.lock.RLock()
  1225  	defer _global.lock.RUnlock()
  1226  	return _global.AssetUrl
  1227  }
  1228  
  1229  func GetFileUploadEngine() FileUploadEngine {
  1230  	_global.lock.RLock()
  1231  	defer _global.lock.RUnlock()
  1232  	return _global.FileUploadEngine
  1233  }
  1234  
  1235  func GetCustomHeadHtml() template.HTML {
  1236  	_global.lock.RLock()
  1237  	defer _global.lock.RUnlock()
  1238  	return _global.CustomHeadHtml
  1239  }
  1240  
  1241  func GetCustomFootHtml() template.HTML {
  1242  	_global.lock.RLock()
  1243  	defer _global.lock.RUnlock()
  1244  	return _global.CustomFootHtml
  1245  }
  1246  
  1247  func GetFooterInfo() template.HTML {
  1248  	_global.lock.RLock()
  1249  	defer _global.lock.RUnlock()
  1250  	return _global.FooterInfo
  1251  }
  1252  
  1253  func GetLoginTitle() string {
  1254  	_global.lock.RLock()
  1255  	defer _global.lock.RUnlock()
  1256  	return _global.LoginTitle
  1257  }
  1258  
  1259  func GetLoginLogo() template.HTML {
  1260  	_global.lock.RLock()
  1261  	defer _global.lock.RUnlock()
  1262  	return _global.LoginLogo
  1263  }
  1264  
  1265  func GetAuthUserTable() string {
  1266  	_global.lock.RLock()
  1267  	defer _global.lock.RUnlock()
  1268  	return _global.AuthUserTable
  1269  }
  1270  
  1271  func GetExtra() map[string]interface{} {
  1272  	_global.lock.RLock()
  1273  	defer _global.lock.RUnlock()
  1274  	return _global.Extra
  1275  }
  1276  
  1277  func GetAnimation() PageAnimation {
  1278  	_global.lock.RLock()
  1279  	defer _global.lock.RUnlock()
  1280  	return _global.Animation
  1281  }
  1282  
  1283  func GetNoLimitLoginIP() bool {
  1284  	_global.lock.RLock()
  1285  	defer _global.lock.RUnlock()
  1286  	return _global.NoLimitLoginIP
  1287  }
  1288  
  1289  func GetHideVisitorUserCenterEntrance() bool {
  1290  	_global.lock.RLock()
  1291  	defer _global.lock.RUnlock()
  1292  	return _global.HideVisitorUserCenterEntrance
  1293  }
  1294  
  1295  func GetExcludeThemeComponents() []string {
  1296  	_global.lock.RLock()
  1297  	defer _global.lock.RUnlock()
  1298  	return _global.ExcludeThemeComponents
  1299  }
  1300  
  1301  type Service struct {
  1302  	C *Config
  1303  }
  1304  
  1305  func (s *Service) Name() string {
  1306  	return "config"
  1307  }
  1308  
  1309  func SrvWithConfig(c *Config) *Service {
  1310  	return &Service{c}
  1311  }
  1312  
  1313  func GetService(s interface{}) *Config {
  1314  	if srv, ok := s.(*Service); ok {
  1315  		return srv.C
  1316  	}
  1317  	panic("wrong service")
  1318  }