package main import ( "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 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}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, } logger.Info("starting server", slog.String("addr", *addr)) err = http.ListenAndServe(*addr, app.routes()) 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 }