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