package main import ( "crypto/tls" "database/sql" "flag" "html/template" "log/slog" "net/http" "regexp" "os" "time" "snippetbox.chaosfem.tw/internal/models" "github.com/alexedwards/scs/mysqlstore" "github.com/alexedwards/scs/v2" "github.com/go-playground/form/v4" _ "github.com/go-sql-driver/mysql" ) // for application wide dependencies type application struct { logger *slog.Logger snippets *models.SnippetModel users *models.UserModel templateCache map[string]*template.Template formDecoder *form.Decoder sessionManager *scs.SessionManager } // main it's the snippetbox webapp func main() { // configuration addr := flag.String("addr", ":4000", "HTTP network address") dsn := flag.String("dsn", "web:dbpass@/snippetbox?parseTime=true", "DB data source name") logfmt := flag.String("logfmt", "text", "Log output format") loglevel := flag.String("loglevel", "INFO", "Log level: DEBUG, INFO, WARN, or ERROR") flag.Parse() logger := loggerBuilder(logfmt, loglevel) db, err := openDB(*dsn, logger) if err != nil { logger.Error(err.Error()) os.Exit(1) } defer db.Close() templateCache, err := newTemplateCache() if err != nil { logger.Error(err.Error()) os.Exit(1) } formDecoder := form.NewDecoder() sessionManager := scs.New() sessionManager.Store = mysqlstore.New(db) sessionManager.Lifetime = 12 * time.Hour // setup the application app := &application{ logger: logger, snippets: &models.SnippetModel{DB: db}, users: &models.UserModel{DB: db}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, } tlsConfig := &tls.Config{ CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}, } srv := &http.Server{ Addr: *addr, Handler: app.routes(), ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), TLSConfig: tlsConfig, IdleTimeout: time.Minute, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } logger.Info("starting server", slog.String("addr", srv.Addr)) err = srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem") logger.Error(err.Error()) os.Exit(1) } func openDB(dsn string, logger *slog.Logger) (*sql.DB, error) { db, err := sql.Open("mysql", dsn) if err != nil { return nil, err } err = db.Ping() if err != nil { db.Close() return nil, err } passRegexp := regexp.MustCompile(":\\S+@") logger.Info("Opened DB connection pool", slog.String("adapter", "mysql"), slog.String("dsn", passRegexp.ReplaceAllString(dsn, ":@"))) return db, nil }