Files
Mycontent/soul-api/cmd/server/main.go
Alex-larget f9e352be96 feat: enhance user profile setup for new users
- Introduced a mandatory profile guide for new users to complete their avatar and nickname before proceeding.
- Updated the avatar-nickname page to ensure fields are empty on entry, preventing auto-fill issues.
- Implemented checks to ensure users cannot navigate away without completing their profile.
- Removed deprecated privacy modal handling, streamlining the user experience.
- Adjusted UI elements to indicate required fields for new users.
2026-04-01 16:11:30 +08:00

103 lines
2.7 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 main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"soul-api/internal/config"
"soul-api/internal/database"
"soul-api/internal/handler"
"soul-api/internal/redis"
"soul-api/internal/router"
"soul-api/internal/wechat"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatal("load config: ", err)
}
config.SetCurrent(cfg)
log.Printf("CORS 允许源(%d 个): %v", len(cfg.CORSOrigins), cfg.CORSOrigins)
if err := database.Init(cfg.DBDSN); err != nil {
log.Fatal("database: ", err)
}
if err := wechat.Init(cfg); err != nil {
log.Fatal("wechat: ", err)
}
if err := wechat.InitTransfer(cfg); err != nil {
log.Fatal("wechat transfer: ", err)
}
if cfg.RedisURL != "" && cfg.RedisURL != "disable" {
if err := redis.Init(cfg.RedisURL); err != nil {
log.Printf("redis: 连接失败,跳过(%v", err)
} else {
defer redis.Close()
}
}
r := router.Setup(cfg)
srv := &http.Server{
Addr: ":" + cfg.Port,
Handler: r,
}
// 预热 Redis 缓存,避免首请求冷启动 502
go func() {
time.Sleep(2 * time.Second) // 等 DB 完全就绪
handler.WarmAllChaptersCache()
handler.WarmBookPartsCache()
handler.WarmConfigCache()
handler.WarmLatestChaptersCache()
}()
go func() {
log.Printf("soul-api listen on :%s (mode=%s)", cfg.Port, cfg.Mode)
log.Printf(" -> 访问地址: http://localhost:%s (健康检查: http://localhost:%s/health)", cfg.Port, cfg.Port)
if cfg.UploadDir != "" {
log.Printf(" -> 上传目录: %s", cfg.UploadDir)
}
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal("listen: ", err)
}
}()
// 内置订单对账定时任务SYNC_ORDERS_INTERVAL_MINUTES > 0 时启动)
if cfg.SyncOrdersIntervalMinutes > 0 {
interval := time.Duration(cfg.SyncOrdersIntervalMinutes) * time.Minute
go func() {
// 启动后延迟 1 分钟执行第一次,避免与启动流程抢资源
time.Sleep(1 * time.Minute)
ticker := time.NewTicker(interval)
defer ticker.Stop()
handler.SyncOrdersLogf("内置定时任务已启动,间隔 %d 分钟", cfg.SyncOrdersIntervalMinutes)
for {
ctx := context.Background()
synced, total, err := handler.RunSyncOrders(ctx, 7)
if err != nil {
handler.SyncOrdersLogf("对账失败: %v", err)
} else if total > 0 {
handler.SyncOrdersLogf("本轮检查 %d 笔,补齐 %d 笔漏单", total, synced)
}
<-ticker.C
}
}()
}
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("server shutdown: ", err)
}
log.Println("bye")
}