2021-11-25 03:59:51 +00:00
|
|
|
package website
|
|
|
|
|
|
|
|
import (
|
2024-05-07 17:12:52 +00:00
|
|
|
"encoding/json"
|
2021-11-25 03:59:51 +00:00
|
|
|
"errors"
|
2024-05-07 17:12:52 +00:00
|
|
|
"io"
|
2021-11-25 03:59:51 +00:00
|
|
|
"net/http"
|
2024-05-07 17:12:52 +00:00
|
|
|
"strings"
|
2021-11-25 03:59:51 +00:00
|
|
|
|
|
|
|
"git.handmade.network/hmn/hmn/src/db"
|
|
|
|
"git.handmade.network/hmn/hmn/src/models"
|
|
|
|
"git.handmade.network/hmn/hmn/src/oops"
|
2024-05-07 17:12:52 +00:00
|
|
|
"git.handmade.network/hmn/hmn/src/utils"
|
2021-11-25 03:59:51 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func APICheckUsername(c *RequestContext) ResponseData {
|
|
|
|
c.Req.ParseForm()
|
|
|
|
usernameArgs, hasUsername := c.Req.Form["username"]
|
|
|
|
found := false
|
|
|
|
canonicalUsername := ""
|
|
|
|
if hasUsername {
|
|
|
|
requestedUsername := usernameArgs[0]
|
|
|
|
found = true
|
|
|
|
c.Perf.StartBlock("SQL", "Fetch user")
|
2022-06-24 21:38:11 +00:00
|
|
|
user, err := db.QueryOne[models.User](c, c.Conn,
|
2021-11-25 03:59:51 +00:00
|
|
|
`
|
2022-05-07 18:58:00 +00:00
|
|
|
SELECT $columns
|
2021-11-25 03:59:51 +00:00
|
|
|
FROM
|
2022-05-07 13:11:05 +00:00
|
|
|
hmn_user
|
2021-11-25 03:59:51 +00:00
|
|
|
WHERE
|
2022-05-07 13:11:05 +00:00
|
|
|
LOWER(hmn_user.username) = LOWER($1)
|
2021-11-25 03:59:51 +00:00
|
|
|
AND status = ANY ($2)
|
|
|
|
`,
|
|
|
|
requestedUsername,
|
|
|
|
[]models.UserStatus{models.UserStatusConfirmed, models.UserStatusApproved},
|
|
|
|
)
|
|
|
|
c.Perf.EndBlock()
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, db.NotFound) {
|
|
|
|
found = false
|
|
|
|
} else {
|
|
|
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user: %s", requestedUsername))
|
|
|
|
}
|
|
|
|
} else {
|
2022-04-16 17:49:29 +00:00
|
|
|
canonicalUsername = user.Username
|
2021-11-25 03:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var res ResponseData
|
2022-06-24 21:38:11 +00:00
|
|
|
addCORSHeaders(c, &res)
|
2021-11-25 03:59:51 +00:00
|
|
|
if found {
|
2024-05-07 17:12:52 +00:00
|
|
|
res.WriteJson(map[string]any{
|
|
|
|
"found": true,
|
|
|
|
"canonical": canonicalUsername,
|
|
|
|
}, nil)
|
2021-11-25 03:59:51 +00:00
|
|
|
} else {
|
2024-05-07 17:12:52 +00:00
|
|
|
res.WriteJson(map[string]any{
|
|
|
|
"found": false,
|
|
|
|
}, nil)
|
2021-11-25 03:59:51 +00:00
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
2024-05-07 17:12:52 +00:00
|
|
|
|
|
|
|
func APINewsletterSignup(c *RequestContext) ResponseData {
|
|
|
|
bodyBytes := utils.Must1(io.ReadAll(c.Req.Body))
|
|
|
|
type Input struct {
|
|
|
|
Email string `json:"email"`
|
|
|
|
}
|
|
|
|
var input Input
|
|
|
|
err := json.Unmarshal(bodyBytes, &input)
|
|
|
|
if err != nil {
|
|
|
|
return c.ErrorResponse(http.StatusBadRequest, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var res ResponseData
|
|
|
|
|
|
|
|
sanitized := input.Email
|
|
|
|
sanitized = strings.TrimSpace(sanitized)
|
|
|
|
sanitized = strings.ToLower(sanitized)
|
|
|
|
if len(sanitized) > 200 {
|
|
|
|
res.StatusCode = http.StatusBadRequest
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
if !strings.Contains(sanitized, "@") {
|
|
|
|
res.StatusCode = http.StatusBadRequest
|
|
|
|
res.WriteJson(map[string]any{
|
|
|
|
"error": "bad email",
|
|
|
|
}, nil)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = c.Conn.Exec(c,
|
|
|
|
`
|
|
|
|
INSERT INTO newsletter_emails (email) VALUES ($1)
|
|
|
|
ON CONFLICT DO NOTHING
|
|
|
|
`,
|
|
|
|
sanitized,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to save email into database"))
|
|
|
|
}
|
|
|
|
|
|
|
|
res.WriteHeader(http.StatusNoContent)
|
|
|
|
return res
|
|
|
|
}
|