task/1.12
1package main
2
3import (
4 "flag"
5 "log"
6 "net/http"
7 "os"
8 "strconv"
9)
10
11func main() {
12 // Declare an instance of the config struct to hold configuration settings
13 var cfg config
14
15 // Parse command-line flags with sensible defaults
16 flag.IntVar(&cfg.port, "port", 4000, "HTTP server port")
17 flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)")
18 flag.StringVar(&cfg.staticDir, "static-dir", "./ui/static", "Path to static assets")
19 flag.StringVar(&cfg.htmlDir, "html-dir", "./ui/html", "Path to HTML templates")
20 flag.Parse()
21
22 // Check for environment variable overrides
23 if envPort := os.Getenv("PORT"); envPort != "" {
24 if port, err := strconv.Atoi(envPort); err == nil {
25 cfg.port = port
26 }
27 }
28 if envEnv := os.Getenv("ENV"); envEnv != "" {
29 cfg.env = envEnv
30 }
31 if envStaticDir := os.Getenv("STATIC_DIR"); envStaticDir != "" {
32 cfg.staticDir = envStaticDir
33 }
34 if envHtmlDir := os.Getenv("HTML_DIR"); envHtmlDir != "" {
35 cfg.htmlDir = envHtmlDir
36 }
37
38 // Create loggers for different types of messages
39 infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
40 errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
41
42 // Log startup configuration
43 infoLog.Printf("Configuration loaded: port=%d, env=%s, staticDir=%s, htmlDir=%s",
44 cfg.port, cfg.env, cfg.staticDir, cfg.htmlDir)
45
46 // Create an instance of the application struct, containing the application-wide
47 // dependencies and configuration settings.
48 app := &application{
49 config: cfg,
50 logger: struct {
51 info *log.Logger
52 error *log.Logger
53 }{
54 info: infoLog,
55 error: errorLog,
56 },
57 serviceName: "buylater.email",
58 }
59
60 // Initialize a new servemux (router) - this stores the mapping between
61 // URL patterns and their corresponding handlers.
62 mux := http.NewServeMux()
63
64 // Create a file server which serves files out of the configured static directory.
65 // Note that the path given to the http.Dir function is relative to the project
66 // directory root.
67 fileServer := http.FileServer(http.Dir(app.config.staticDir))
68
69 // Use the mux.Handle() function to register the file server as the handler for
70 // all URL paths that start with "/static/". For matching paths, we strip the
71 // "/static" prefix before the request reaches the file server.
72 mux.Handle("GET /static/", http.StripPrefix("/static", fileServer))
73
74 // Register handlers for buylater.email routes - demonstrating different handler patterns:
75
76 // 1. Method handler converted to http.Handler using http.HandlerFunc
77 mux.Handle("GET /{$}", http.HandlerFunc(app.home))
78
79 // 2. Custom handler type that implements http.Handler interface directly
80 mux.Handle("GET /submit", &TemplateHandler{
81 app: app,
82 templateName: "submit.tmpl",
83 pageName: "submit",
84 title: "Submit",
85 })
86
87 // 3. Method handler with business logic
88 mux.Handle("POST /submit", http.HandlerFunc(app.processSubmit))
89
90 // 4. Simple function handler converted to http.Handler
91 mux.Handle("GET /confirm/{token}", http.HandlerFunc(confirmWithToken))
92
93 // 5. Another custom handler type instance for the about page
94 mux.Handle("GET /about", &TemplateHandler{
95 app: app,
96 templateName: "about.tmpl",
97 pageName: "about",
98 title: "About",
99 })
100
101 // Print a log message to indicate the server is starting.
102 app.logger.info.Printf("Starting server on http://localhost:%d in %s mode", app.config.port, app.config.env)
103
104 // Start the web server on the configured port. If ListenAndServe returns an error
105 // we use log.Fatal() to log the error and terminate the program.
106 err := http.ListenAndServe(":"+strconv.Itoa(app.config.port), mux)
107 log.Fatal(err)
108}