Files
dudu/server/internal/config/config.go
T
wangjia 19281d9c42
ci / server (push) Failing after 10s
ci / design-system (push) Failing after 10s
feat(auth+desktop): 邮箱验证码登录 + 登录窗改版(微信/邮箱双方式,无边框)
后端(协议 → 存储 → 接口,E2E 测试全绿):
- protocol:AuthEmailCodeRequest / AuthEmailRequest DTO
- store:EmailIdentity 表(邮箱唯一索引)+ authmail:* Redis key
- auth/email.go:POST /v1/auth/email/code 发 6 位码(crypto/rand,
  60s 冷却 SetNX,10 分钟 TTL);POST /v1/auth/email 常量时间比对、
  码一次性、邮箱建号(昵称取前缀)→ JWT(与微信登录同响应形态)
- Mailer 接口 + MockMailer(验证码打日志供联调;SMTP 未配置自动降级,
  装配结果入启动日志,上线前须换真实实现)
- TestEmailLogin E2E:发码/冷却 429/错码拒绝/登录/token 可用/码防复用

桌面(Rust 命令 + UI 改版):
- api.rs:login_email_code / login_email(成功保存 token + ws 重连,
  与扫码登录同路径)
- 登录窗改版(原型 LoginPurchase 先行,dark 截图验收):logo + 分段
  切换(微信扫码 / 邮箱登录,与设置页同控件语言)+ 邮箱表单
  (60s 倒计时、错误文案、未注册自动建号提示)
- 二维码真渲染:qrcode 库画 canvas(前景/背景取主题 token),
  替换原 URL 文本占位;过期态半透明化
- 登录窗无边框化:transparent + Overlay 标题栏 + 原生贴形阴影;
  抽公共 WindowFrame 组件(设置窗同步重构复用)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:40:16 +08:00

114 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package config 环境变量配置(密钥经部署注入,本地开发用 .env.local / docker compose)。
package config
import (
"encoding/json"
"log/slog"
"os"
"strconv"
"dudu/server/pkg/protocol"
)
type Config struct {
Addr string // HTTP 监听地址
PostgresDSN string
RedisAddr string
RedisDB int
JWTSecret string
JWTTTLHours int
// 微信开放平台 / 商户号(申请中可留空,相关模块降级为 mock)
WechatWebAppID string
WechatWebSecret string
WechatMobileAppID string
WechatMobileSecret string
WxPayMchID string
WxPayCertSerial string
WxPayPrivateKeyPEM string
WxPayAPIv3Key string
WxPayNotifyURL string
// ASR
ASRProvider string // gummy | mock
DashScopeAPIKey string
// OSS(反馈图片)
OSSEndpoint string
OSSBucket string
OSSKeyID string
OSSKeySecret string
// SMTP(邮箱验证码登录;未配置降级 mock mailer
SMTPHost string
// AppLatest 启动时解析 APP_LATEST_JSON 一次(17G):平台→版本信息。
// 解析失败或未配置则为 nil/空,handler 据此返回 204,不 fail-fast。
AppLatest map[string]protocol.AppLatestResponse
}
func Load() Config {
return Config{
Addr: getenv("ADDR", ":8080"),
PostgresDSN: getenv("POSTGRES_DSN", "host=localhost user=dudu password=dudu dbname=dudu port=5432 sslmode=disable TimeZone=Asia/Shanghai"),
RedisAddr: getenv("REDIS_ADDR", "localhost:6379"),
RedisDB: getint("REDIS_DB", 0),
JWTSecret: getenv("JWT_SECRET", "dev-secret-change-me"),
JWTTTLHours: getint("JWT_TTL_HOURS", 24*7),
WechatWebAppID: os.Getenv("WECHAT_WEB_APPID"),
WechatWebSecret: os.Getenv("WECHAT_WEB_SECRET"),
WechatMobileAppID: os.Getenv("WECHAT_MOBILE_APPID"),
WechatMobileSecret: os.Getenv("WECHAT_MOBILE_SECRET"),
WxPayMchID: os.Getenv("WXPAY_MCHID"),
WxPayCertSerial: os.Getenv("WXPAY_CERT_SERIAL"),
WxPayPrivateKeyPEM: os.Getenv("WXPAY_PRIVATE_KEY_PEM"),
WxPayAPIv3Key: os.Getenv("WXPAY_APIV3_KEY"),
WxPayNotifyURL: os.Getenv("WXPAY_NOTIFY_URL"),
ASRProvider: getenv("ASR_PROVIDER", "mock"),
DashScopeAPIKey: os.Getenv("DASHSCOPE_API_KEY"),
OSSEndpoint: os.Getenv("OSS_ENDPOINT"),
OSSBucket: os.Getenv("OSS_BUCKET"),
SMTPHost: os.Getenv("SMTP_HOST"),
OSSKeyID: os.Getenv("OSS_KEY_ID"),
OSSKeySecret: os.Getenv("OSS_KEY_SECRET"),
AppLatest: loadAppLatest(),
}
}
// loadAppLatest 启动时解析一次 APP_LATEST_JSON17G)。失败仅告警并返回 nil,
// 不 fail-fast——未配置此项时服务仍需正常启动。
func loadAppLatest() map[string]protocol.AppLatestResponse {
raw := os.Getenv("APP_LATEST_JSON")
if raw == "" {
return nil
}
var all map[string]protocol.AppLatestResponse
if err := json.Unmarshal([]byte(raw), &all); err != nil {
slog.Warn("invalid APP_LATEST_JSON, app update disabled", "err", err)
return nil
}
return all
}
func getenv(k, def string) string {
if v := os.Getenv(k); v != "" {
return v
}
return def
}
func getint(k string, def int) int {
if v := os.Getenv(k); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return def
}