package main import ( "context" "io/fs" "log" "net/http" "os" "nestly/internal/auth" "nestly/internal/handlers" "nestly/internal/models" "nestly/internal/storage" "nestly/internal/templates" "nestly/web" ) const demoEmail = "demo@nestly.test" func envOr(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func main() { port := envOr("PORT", "3000") dsn := envOr("DB_DSN", "nestly.db") secret := os.Getenv("SESSION_SECRET") if secret == "" { secret = "nestly-dev-secret" log.Println("警告:未設定 SESSION_SECRET,使用開發用預設值,正式環境務必指定") } ctx := context.Background() db, err := storage.Open(dsn) if err != nil { log.Fatal(err) } accounts := models.NewAccountStore(db) if err := accounts.AutoMigrate(ctx); err != nil { log.Fatal(err) } seedDemoAccount(ctx, accounts) templateFS, err := fs.Sub(web.TemplatesFS, "templates") if err != nil { log.Fatal(err) } engine, err := templates.New(templateFS) if err != nil { log.Fatal(err) } router := handlers.NewRouter(handlers.Dependencies{ Templates: engine, Accounts: accounts, Sessions: auth.NewSessionManager(secret, os.Getenv("COOKIE_SECURE") == "true"), }) log.Printf("Nestly 啟動於 http://localhost:%s", port) if err := http.ListenAndServe(":"+port, router); err != nil { log.Fatal(err) } } // seedDemoAccount 於資料庫無此帳號時建立示範帳號,方便本地試用登入流程。 func seedDemoAccount(ctx context.Context, accounts *models.AccountStore) { if _, err := accounts.FindByEmail(ctx, demoEmail); err == nil { return } else if err != models.ErrNotFound { log.Fatal(err) } acct := &models.Account{Email: demoEmail, Name: "示範帳號", Role: models.RoleMember} if err := acct.SetPassword("nestly1234"); err != nil { log.Fatal(err) } if err := accounts.Create(ctx, acct); err != nil { log.Fatal(err) } log.Printf("已建立示範帳號 %s(密碼:nestly1234)", demoEmail) }