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 }