task/1.12
Raw Download raw file
  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