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