-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathitem.go
More file actions
233 lines (203 loc) · 6 KB
/
Copy pathitem.go
File metadata and controls
233 lines (203 loc) · 6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Copyright 2023 Bill Nixon. All rights reserved.
// Use of this source code is governed by the license found in the LICENSE file.
package main
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/bnixon67/webapp/webauth"
"github.com/bnixon67/webapp/webhandler"
"github.com/bnixon67/webapp/webutil"
)
// ItemPageData contains data passed to the HTML template.
type ItemPageData struct {
Title string
Message string
User webauth.User
Item Item
IsAuctionOpen bool
Bids []Bid
}
// ItemHandler display an item.
func (app *BidApp) ItemHandler(w http.ResponseWriter, r *http.Request) {
// Get logger with request info and function name.
logger := webhandler.RequestLoggerWithFuncName(r)
// Check if the HTTP method is valid.
if !webutil.CheckAllowedMethods(w, r, http.MethodGet, http.MethodPost) {
logger.Error("invalid method")
return
}
if r.URL.Path == "/item" {
logger.Warn("bad request")
webutil.RespondWithError(w, http.StatusBadRequest)
return
}
// get idString from URL path
idString := strings.TrimPrefix(r.URL.Path, "/item/")
if idString == "" {
logger.Warn("missing id")
webutil.RespondWithError(w, http.StatusBadRequest)
return
}
// convert idString to int
id, err := strconv.Atoi(idString)
if err != nil {
logger.Error("unable to convert id", "idString", idString, "err", err)
webutil.RespondWithError(w, http.StatusBadRequest)
return
}
currentUser, err := app.DB.UserFromRequest(w, r)
if err != nil {
logger.Error("failed to GetUser", "err", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
switch r.Method {
case http.MethodGet:
app.itemGetHandler(w, r, id, currentUser)
case http.MethodPost:
app.itemPostHandler(w, r, id, currentUser)
}
}
func (app *BidApp) itemGetHandler(w http.ResponseWriter, r *http.Request, id int, user webauth.User) {
// Get logger with request info and function name.
logger := webhandler.RequestLoggerWithFuncName(r)
// get item from database
item, err := app.BidDB.GetItem(id)
if err != nil {
logger.Error("unable to GetItem", "id", id, "err", err)
webutil.RespondWithError(w, http.StatusNotFound)
return
}
// get bids for item from database
bids, err := app.BidDB.GetBidsForItem(id)
if err != nil {
logger.Error("unable to GetBidsForItem", "id", id, "err", err)
// TODO: what to display to user if this fails
}
// display page
err = webutil.RenderTemplateOrError(app.Tmpl, w, "item.html",
ItemPageData{
Title: app.Cfg.App.Name,
Message: "",
User: user,
Item: item,
IsAuctionOpen: app.IsAuctionOpen(),
Bids: bids,
})
if err != nil {
logger.Error("unable to RenderTemplate", "err", err)
return
}
logger.Info("displayed item", "username", user.Username, "item", item,
"auction open", app.IsAuctionOpen(), "bids", len(bids))
}
func (app *BidApp) itemPostHandler(w http.ResponseWriter, r *http.Request, id int, user webauth.User) {
// Get logger with request info and function name.
logger := webhandler.RequestLoggerWithFuncName(r)
var msg string
var err error
// get bidAmount
bidAmountStr := r.PostFormValue("bidAmount")
if bidAmountStr == "" {
logger.Warn("no bidAmount")
w.WriteHeader(http.StatusBadRequest)
return
}
bidAmount, err := strconv.ParseFloat(bidAmountStr, 64)
if err != nil {
msg = "Invalid bid amount."
logger.Error("unable to parse",
"bidAmountStr", bidAmountStr,
"err", err)
}
// negative bid
if bidAmount <= 0 {
msg = "Invalid bid amount."
logger.Error("zero or negative bid",
"bidAmount", bidAmount)
}
// invalid user
if user == (webauth.User{}) {
msg = "Invalid user."
logger.Error("invalid user")
}
// submit bid if we have a valid user and bidAmount and open Auction
if user != (webauth.User{}) && bidAmount > 0 && app.IsAuctionOpen() {
bidResult, err := app.BidDB.PlaceBid(id, bidAmount, user.Username)
if err != nil {
logger.Error("unable to PlaceBid",
"id", id, "bidAmount", bidAmount, "user", user,
"err", err)
msg = bidResult.Message
} else {
logger.Info("PlaceBid",
"id", id,
"bidAmount", bidAmount,
"user", user,
"bidResult", bidResult,
)
msg = bidResult.Message
if bidResult.BidPlaced && bidResult.PriorBidder != "" && bidResult.PriorBidder != user.Username {
user, err := app.DB.UserForName(bidResult.PriorBidder)
if err != nil {
logger.Error("unable to GetUserForName",
"PriorBidder", bidResult.PriorBidder,
"err", err)
}
// get item from database
// TODO: eliminate extra GetItem call
item, err := app.BidDB.GetItem(id)
if err != nil {
logger.Error("unable to GetItem", "id", id, "err", err)
}
emailText := fmt.Sprintf(
"You have been outbid on %q. Visit %s/item/%d to rebid.",
item.Title, app.Cfg.Auth.BaseURL, id)
err = app.Cfg.SMTP.SendMessage(app.Cfg.EmailFrom, []string{user.Email}, app.Cfg.App.Name, emailText)
if err != nil {
logger.Error("unable to send email",
"to", user.Email,
"err", err)
}
}
}
} else if !app.IsAuctionOpen() {
msg = "Auction is not open"
}
// get item from database
item, err := app.BidDB.GetItem(id)
if err != nil {
logger.Error("unable to get item", "id", id, "err", err)
w.WriteHeader(http.StatusNotFound)
return
}
// get bids for item from database
bids, err := app.BidDB.GetBidsForItem(id)
if err != nil {
logger.Error("unable to get bids for item", "id", id, "err", err)
// TODO: what to display to user if this fails
}
// display page
err = webutil.RenderTemplateOrError(app.Tmpl, w, "item.html",
ItemPageData{
Title: app.Cfg.App.Name,
Message: msg,
User: user,
Item: item,
IsAuctionOpen: app.IsAuctionOpen(),
Bids: bids,
})
if err != nil {
logger.Error("unable to RenderTemplate", "err", err)
return
}
logger.Info("post bid",
"message", msg,
"username", user.Username,
"item", item,
"auction open", app.IsAuctionOpen(),
"bids", len(bids),
)
}