config.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package config
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "fmt"
  6. "github.com/spf13/viper"
  7. )
  8. type AppConfig struct {
  9. AppEnv string
  10. AppPort string
  11. LogLevel string
  12. RedisAddr string
  13. MySQLDSN string
  14. PostgresDSN string
  15. MongoDSN string
  16. MinIoEndpoint string
  17. MinioAccessKey string
  18. MinioSecretKey string
  19. MinioBucketName string
  20. AsynqmonPort string
  21. TotpViewPassword string
  22. TotpViewHost string
  23. TotpViewPort string
  24. SimCardDingTalkWebhook string
  25. SimCardDingTalkSecret string
  26. }
  27. type FwhApiConfig struct {
  28. FwhEnergy string
  29. FwhOss string
  30. FwhUser string
  31. FwhPassword string
  32. FwhTotpSecret string
  33. }
  34. type FwhMqttConfig struct {
  35. FwhMqttBroker string
  36. FwhMqttCaUrl string
  37. FwhMqttCertUrl string
  38. FwhMqttKeyUrl string
  39. }
  40. // FwhEsConfig Elasticsearch(iotcore-stat) 连接配置
  41. type FwhEsConfig struct {
  42. // EsHost 形如 "alb-prod.franklinwh.click:5603"(不含 scheme)
  43. EsHost string
  44. // EsUser / EsPassword basic auth 凭证
  45. EsUser string
  46. EsPassword string
  47. // EsIndexPrefix 索引前缀,形如 "iotcore-stat",
  48. // 实际索引名会拼接日期,如 "iotcore-stat-2026.09.07"
  49. EsIndexPrefix string
  50. // EsInsecureSkipVerify 是否跳过 TLS 证书校验(对应 curl -k)
  51. EsInsecureSkipVerify bool
  52. }
  53. type PlaneApiConfig struct {
  54. Host string
  55. ApiKey string
  56. WorkspaceSlug string
  57. ProjectId string
  58. LabelBugId string
  59. LabelFeatureId string
  60. }
  61. type PlaneWebApiConfig struct {
  62. Host string
  63. Email string
  64. Password string
  65. WorkspaceSlug string
  66. ProjectId string
  67. LabelBugId string
  68. LabelFeatureId string
  69. LabelFeedbackId string
  70. }
  71. // AgentConfig 新 AI 助手(AgentServer)配置,见 docs/ai-assistant-agentserver/technical-design.md §3.2
  72. type AgentConfig struct {
  73. // Enabled 新 AI 助手总开关(AGENT_ENABLED)
  74. Enabled bool
  75. // ServerURL AgentServer 地址,如 http://127.0.0.1:8787(AGENT_SERVER_URL)
  76. ServerURL string
  77. // ApiKey AgentServer 签发的平台 API Key,只在后端使用(AGENT_SERVER_API_KEY)
  78. ApiKey string
  79. // ProfileDefault 默认 Profile ID(AGENT_PROFILE_DEFAULT),为空时使用 API Key 的默认 Profile
  80. ProfileDefault string
  81. // ProfileByRole 角色到 Profile 的映射,格式 "role:prf_x,role2:prf_y",按顺序取第一个命中的(AGENT_PROFILE_BY_ROLE)
  82. ProfileByRole string
  83. // AllowedRoles 允许使用新助手的角色,逗号分隔;为空表示所有登录用户(AGENT_ALLOWED_ROLES)
  84. AllowedRoles string
  85. }
  86. type Config struct {
  87. App AppConfig
  88. FwhApi FwhApiConfig
  89. FwhTestApi FwhApiConfig
  90. FwhMqtt FwhMqttConfig
  91. FwhTestMqtt FwhMqttConfig
  92. FwhEs FwhEsConfig
  93. PlaneApi PlaneApiConfig
  94. PlaneWebApi PlaneWebApiConfig
  95. Agent AgentConfig
  96. }
  97. // 需先执行LoadConfig后才能读取ENV
  98. // var APP_ENV string = "dev"
  99. // var FWH_ENERGY string = "energy.franklinwh.com"
  100. // var FWH_OSS string = "oss.franklinwh.com"
  101. // var FWH_USER string = ""
  102. // var FWH_PASSWORD string = "" // The production API receives its MD5 digest.
  103. // LoadConfig 读取配置文件和环境变量。
  104. // 可选参数 configPaths 指定候选配置文件路径(按顺序尝试,命中第一个存在的即停)。
  105. // 不传时使用默认路径 ["./.env", "./config.env"](后端主服务用)。
  106. // MCP 等独立进程可传入自己的配置文件,例如 LoadConfig("./config.agent-mcp.env")。
  107. func LoadConfig(configPaths ...string) (*Config, error) {
  108. v := viper.New()
  109. // 默认值(避免缺省时崩溃)
  110. // APP
  111. v.SetDefault("APP_ENV", "dev")
  112. v.SetDefault("APP_PORT", "8080")
  113. v.SetDefault("REDIS_ADDR", "localhost:6379")
  114. v.SetDefault("MYSQL_DSN", "")
  115. v.SetDefault("POSTGRES_DSN", "host=localhost user=fwh_toolbox password=fwh_toolbox dbname=fwh_toolbox_db port=5432 sslmode=disable")
  116. v.SetDefault("MONGO_DSN", "mongodb://localhost:27017/FwhToolboxDbDev")
  117. v.SetDefault("MINIO_ENDPOINT", "localhost:9198")
  118. v.SetDefault("MINIO_ACCESS_KEY", "fwh-toolbox")
  119. v.SetDefault("MINIO_SECRET_KEY", "fwh-toolbox")
  120. v.SetDefault("MINIO_BUCKET_NAME", "fwh-toolbox")
  121. v.SetDefault("ASYNQMON_PORT", "8081")
  122. v.SetDefault("FWH_TOTP_VIEW_PORT", "8303")
  123. v.SetDefault("FWH_TOTP_VIEW_HOST", "127.0.0.1")
  124. // FWH API
  125. v.SetDefault("FWH_ENERGY", "energy.franklinwh.com")
  126. v.SetDefault("FWH_OSS", "oss.franklinwh.com")
  127. // FWH MQTT
  128. // FWH ES(iotcore-stat)
  129. v.SetDefault("FWH_ES_INDEX_PREFIX", "iotcore-stat")
  130. v.SetDefault("FWH_ES_INSECURE_SKIP_VERIFY", true)
  131. // 新 AI 助手(AgentServer)
  132. v.SetDefault("AGENT_ENABLED", false)
  133. v.SetDefault("AGENT_SERVER_URL", "http://127.0.0.1:8787")
  134. // 多文件路径支持:优先使用调用方传入的路径,否则回退默认
  135. paths := configPaths
  136. if len(paths) == 0 {
  137. paths = []string{"./.env", "./config.env"}
  138. }
  139. var readErr error
  140. for _, path := range paths {
  141. v.SetConfigFile(path)
  142. if err := v.ReadInConfig(); err == nil {
  143. fmt.Printf("✅ Using config file: %s\n", path)
  144. readErr = nil
  145. break
  146. } else {
  147. readErr = err
  148. }
  149. }
  150. // 环境变量覆盖文件配置
  151. v.AutomaticEnv()
  152. // 如果两个文件都没读到,也没关系,直接用默认值
  153. if readErr != nil {
  154. fmt.Println("⚠️ No config file found, using default values & environment variables")
  155. }
  156. // APP_ENV = v.GetString("APP_ENV")
  157. // FWH_ENERGY = v.GetString("FWH_ENERGY")
  158. // FWH_OSS = v.GetString("FWH_OSS")
  159. // FWH_USER = v.GetString("FWH_USER")
  160. // FWH_PASSWORD = v.GetString("FWH_PASSWORD")
  161. h := md5.New()
  162. h.Write([]byte(v.GetString("FWH_PASSWORD")))
  163. fwhApiPassword := hex.EncodeToString(h.Sum(nil))
  164. h = md5.New()
  165. h.Write([]byte(v.GetString("FWH_TEST_PASSWORD")))
  166. fwhTestApiPassword := hex.EncodeToString(h.Sum(nil))
  167. return &Config{
  168. App: AppConfig{
  169. AppEnv: v.GetString("APP_ENV"),
  170. AppPort: v.GetString("APP_PORT"),
  171. LogLevel: v.GetString("LOG_LEVEL"),
  172. RedisAddr: v.GetString("REDIS_ADDR"),
  173. MySQLDSN: v.GetString("MYSQL_DSN"),
  174. PostgresDSN: v.GetString("POSTGRES_DSN"),
  175. MongoDSN: v.GetString("MONGO_DSN"),
  176. MinIoEndpoint: v.GetString("MINIO_ENDPOINT"),
  177. MinioAccessKey: v.GetString("MINIO_ACCESS_KEY"),
  178. MinioSecretKey: v.GetString("MINIO_SECRET_KEY"),
  179. MinioBucketName: v.GetString("MINIO_BUCKET_NAME"),
  180. AsynqmonPort: v.GetString("ASYNQMON_PORT"),
  181. TotpViewPassword: v.GetString("FWH_TOTP_VIEW_PASSWORD"),
  182. TotpViewHost: v.GetString("FWH_TOTP_VIEW_HOST"),
  183. TotpViewPort: v.GetString("FWH_TOTP_VIEW_PORT"),
  184. SimCardDingTalkWebhook: v.GetString("SIM_CARD_DINGTALK_WEBHOOK"),
  185. SimCardDingTalkSecret: v.GetString("SIM_CARD_DINGTALK_SECRET"),
  186. },
  187. FwhApi: FwhApiConfig{
  188. FwhEnergy: v.GetString("FWH_ENERGY"),
  189. FwhOss: v.GetString("FWH_OSS"),
  190. FwhUser: v.GetString("FWH_USER"),
  191. FwhPassword: fwhApiPassword,
  192. FwhTotpSecret: v.GetString("FWH_TOTP_SECRET"),
  193. },
  194. FwhTestApi: FwhApiConfig{
  195. FwhEnergy: v.GetString("FWH_TEST_ENERGY"),
  196. FwhOss: v.GetString("FWH_TEST_OSS"),
  197. FwhUser: v.GetString("FWH_TEST_USER"),
  198. FwhPassword: fwhTestApiPassword,
  199. },
  200. FwhMqtt: FwhMqttConfig{
  201. FwhMqttBroker: v.GetString("FWH_MQTT_BROKER"),
  202. FwhMqttCaUrl: v.GetString("FWH_MQTT_CA"),
  203. FwhMqttCertUrl: v.GetString("FWH_MQTT_CERT"),
  204. FwhMqttKeyUrl: v.GetString("FWH_MQTT_KEY"),
  205. },
  206. FwhTestMqtt: FwhMqttConfig{
  207. FwhMqttBroker: v.GetString("FWH_MQTT_TEST_BROKER"),
  208. FwhMqttCaUrl: v.GetString("FWH_MQTT_TEST_CA"),
  209. FwhMqttCertUrl: v.GetString("FWH_MQTT_TEST_CERT"),
  210. FwhMqttKeyUrl: v.GetString("FWH_MQTT_TEST_KEY"),
  211. },
  212. FwhEs: FwhEsConfig{
  213. EsHost: v.GetString("FWH_ES_HOST"),
  214. EsUser: v.GetString("FWH_ES_USER"),
  215. EsPassword: v.GetString("FWH_ES_PASSWORD"),
  216. EsIndexPrefix: v.GetString("FWH_ES_INDEX_PREFIX"),
  217. EsInsecureSkipVerify: v.GetBool("FWH_ES_INSECURE_SKIP_VERIFY"),
  218. },
  219. PlaneApi: PlaneApiConfig{
  220. Host: v.GetString("PLANE_HOST"),
  221. ApiKey: v.GetString("PLANE_API_KEY"),
  222. WorkspaceSlug: v.GetString("PLANE_WORKSPACE_SLUG"),
  223. ProjectId: v.GetString("PLANE_PROJECT_ID"),
  224. LabelBugId: v.GetString("PLANE_LABEL_BUG"),
  225. LabelFeatureId: v.GetString("PLANE_LABEL_FEATURE"),
  226. },
  227. PlaneWebApi: PlaneWebApiConfig{
  228. Host: v.GetString("PLANE_WEB_HOST"),
  229. Email: v.GetString("PLANE_WEB_EMAIL"),
  230. Password: v.GetString("PLANE_WEB_PASSWORD"),
  231. WorkspaceSlug: v.GetString("PLANE_WEB_WORKSPACE_SLUG"),
  232. ProjectId: v.GetString("PLANE_WEB_PROJECT_ID"),
  233. LabelBugId: v.GetString("PLANE_WEB_LABEL_BUG"),
  234. LabelFeatureId: v.GetString("PLANE_WEB_LABEL_FEATURE"),
  235. LabelFeedbackId: v.GetString("PLANE_WEB_LABEL_FEEDBACK"),
  236. },
  237. Agent: AgentConfig{
  238. Enabled: v.GetBool("AGENT_ENABLED"),
  239. ServerURL: v.GetString("AGENT_SERVER_URL"),
  240. ApiKey: v.GetString("AGENT_SERVER_API_KEY"),
  241. ProfileDefault: v.GetString("AGENT_PROFILE_DEFAULT"),
  242. ProfileByRole: v.GetString("AGENT_PROFILE_BY_ROLE"),
  243. AllowedRoles: v.GetString("AGENT_ALLOWED_ROLES"),
  244. },
  245. }, nil
  246. }