task/1.11
1package main
2
3import (
4 "html/template"
5 "log"
6 "net/http"
7 "regexp"
8)
9
10// PageData holds common data passed to templates
11type PageData struct {
12 Title string
13 ServiceName string
14 Content interface{}
15}
16
17// config holds all the configuration settings for the application
18type config struct {
19 port int
20 env string
21 staticDir string
22 htmlDir string
23}
24
25// application holds the application-wide dependencies and configuration
26// This demonstrates a custom handler type that can hold state and dependencies
27type application struct {
28 config config
29 logger *log.Logger
30 serviceName string
31}
32
33// TemplateHandler represents handlers that render templates
34// This is a custom handler type that implements http.Handler interface
35type TemplateHandler struct {
36 app *application
37 templateName string
38 pageName string
39 title string
40}
41
42// ServeHTTP makes TemplateHandler satisfy the http.Handler interface
43func (th *TemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
44 w.Header().Add("Server", th.app.serviceName)
45
46 // Parse templates with inheritance using configurable paths
47 ts, err := template.ParseFiles(
48 th.app.config.htmlDir+"/layouts/base.tmpl",
49 th.app.config.htmlDir+"/pages/"+th.templateName,
50 )
51 if err != nil {
52 th.app.logger.Println(err.Error())
53 http.Error(w, "Internal Server Error", 500)
54 return
55 }
56
57 // Prepare template data
58 data := PageData{
59 Title: th.title,
60 ServiceName: th.app.serviceName,
61 Content: nil,
62 }
63
64 // Execute template
65 err = ts.ExecuteTemplate(w, "base", data)
66 if err != nil {
67 th.app.logger.Println(err.Error())
68 http.Error(w, "Internal Server Error", 500)
69 }
70}
71
72// home displays the buylater.email landing page
73// This demonstrates using http.HandlerFunc to convert a regular function
74// into something that satisfies the http.Handler interface
75func (app *application) home(w http.ResponseWriter, r *http.Request) {
76 w.Header().Add("Server", app.serviceName)
77
78 // Parse templates with inheritance using configurable paths
79 ts, err := template.ParseFiles(
80 app.config.htmlDir+"/layouts/base.tmpl",
81 app.config.htmlDir+"/pages/home.tmpl",
82 )
83 if err != nil {
84 app.logger.Println(err.Error())
85 http.Error(w, "Internal Server Error", 500)
86 return
87 }
88
89 // Prepare template data
90 data := PageData{
91 Title: "Home",
92 ServiceName: app.serviceName,
93 Content: nil,
94 }
95
96 // Execute template
97 err = ts.ExecuteTemplate(w, "base", data)
98 if err != nil {
99 app.logger.Println(err.Error())
100 http.Error(w, "Internal Server Error", 500)
101 }
102}
103
104// submitForm displays the email submission form for scheduling purchase reminders
105// This will be handled by the TemplateHandler type to demonstrate custom handler types
106
107// processSubmit handles the form submission and schedules the email reminder
108// This demonstrates a method handler with business logic
109func (app *application) processSubmit(w http.ResponseWriter, r *http.Request) {
110 w.Header().Add("Server", app.serviceName)
111
112 // Parse templates with inheritance using configurable paths
113 ts, err := template.ParseFiles(
114 app.config.htmlDir+"/layouts/base.tmpl",
115 app.config.htmlDir+"/pages/complete.tmpl",
116 )
117 if err != nil {
118 app.logger.Println(err.Error())
119 http.Error(w, "Internal Server Error", 500)
120 return
121 }
122
123 // Prepare template data
124 data := PageData{
125 Title: "Submission Complete",
126 ServiceName: app.serviceName,
127 Content: nil,
128 }
129
130 // Execute template
131 err = ts.ExecuteTemplate(w, "base", data)
132 if err != nil {
133 app.logger.Println(err.Error())
134 http.Error(w, "Internal Server Error", 500)
135 }
136}
137
138// confirmWithToken handles magic link confirmation with token validation
139// This demonstrates a stateless handler function that can be converted with http.HandlerFunc
140func confirmWithToken(w http.ResponseWriter, r *http.Request) {
141 token := r.PathValue("token")
142
143 if !isValidToken(token) {
144 w.Header().Add("Server", "buylater.email")
145 http.NotFound(w, r)
146 return
147 }
148
149 w.Header().Add("Server", "buylater.email")
150 w.WriteHeader(http.StatusOK)
151 w.Write([]byte("Email Confirmed! Your purchase reminder has been activated via magic link."))
152}
153
154// isValidToken validates that the token is alphanumeric and at least 32 characters
155func isValidToken(token string) bool {
156 if len(token) < 32 {
157 return false
158 }
159
160 // Check if token contains only alphanumeric characters
161 matched, _ := regexp.MatchString("^[a-zA-Z0-9]+$", token)
162 return matched
163}
164
165// about displays information about the buylater.email service
166// This will be handled by the TemplateHandler type to demonstrate reusable custom handlers