LuggageTracker/web.go
2025-07-17 12:17:47 -04:00

309 lines
7.6 KiB
Go

package main
import (
"errors"
"fmt"
ratelimit "github.com/JGLTechnologies/gin-rate-limit"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"html/template"
"net/http"
"os"
"strconv"
"strings"
"time"
)
var sessionStore sessions.Store
var htmlRL gin.HandlerFunc
var jsonRL gin.HandlerFunc
func createSessionStore() {
secureBool, err := strconv.ParseBool(os.Getenv("SECURE_SESSION"))
if err != nil {
fmt.Printf("Unable to get Secure Session Env, defaulting to \"true\". Error: %v\n", err.Error())
secureBool = true
}
sessionStore = cookie.NewStore([]byte(os.Getenv("AUTHENTICATION_KEY")), []byte(os.Getenv("ENCRYPTION_KEY")))
sessionStore.Options(sessions.Options{
Path: "/",
MaxAge: 60 * 60 * 24, // 1 day
HttpOnly: true,
Secure: secureBool,
SameSite: http.SameSiteLaxMode,
})
}
func keyFunc(c *gin.Context) string {
return c.ClientIP()
}
func createRateLimiters() {
store := ratelimit.InMemoryStore(&ratelimit.InMemoryOptions{
Rate: time.Second,
Limit: 5,
})
htmlRL = ratelimit.RateLimiter(store, &ratelimit.Options{
ErrorHandler: func(c *gin.Context, info ratelimit.Info) {
c.HTML(http.StatusTooManyRequests, "base.html.tmpl", gin.H{
"header": "Error",
"body": fmt.Sprintf("Too many requests. Try again in %v", time.Until(info.ResetTime).String()),
})
},
KeyFunc: keyFunc,
})
jsonRL = ratelimit.RateLimiter(store, &ratelimit.Options{
ErrorHandler: func(c *gin.Context, info ratelimit.Info) {
c.JSON(http.StatusTooManyRequests, gin.H{})
},
KeyFunc: keyFunc,
})
}
func webRoot(c *gin.Context) {
c.File("./html/home.html")
}
func webRegister(c *gin.Context) {
c.File("./html/register.html")
}
func webRegisterSuccess(c *gin.Context) {
c.HTML(http.StatusOK, "base.html.tmpl", gin.H{
"header": "Registration Success",
"body": "We will reach out soon to get your tag to you!",
})
}
func webQr(c *gin.Context) {
c.File("./html/genqr.html")
}
func webPing(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
func webUserApi(c *gin.Context) {
user, err := db.queryUser(strings.ToLower(c.Param("user")))
if err != nil {
if errors.Is(err, NoEntriesFoundError) {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"status": 401,
"error": "Unauthorized",
})
return
}
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"status": 500,
"error": fmt.Sprintf("Internal Server Error: %s", err.Error()),
})
return
}
if user.CurrentToken == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"status": 401,
"error": "Unauthorized",
})
return
}
if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 {
user.Status = 200
c.JSON(http.StatusOK, user)
return
}
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"status": 401,
"error": "Unauthorized",
})
}
func webVerifyUserApi(c *gin.Context) {
user, err := db.queryUser(strings.ToLower(c.Param("user")))
if err != nil {
if errors.Is(err, NoEntriesFoundError) {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"status": 404,
"user": "",
"error": "User not found",
})
return
}
fmt.Printf("Error: %s\n", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"status": 500,
"user": "",
"error": err.Error(),
})
return
}
codes := strings.Split(user.SecretCodes, ",")
codeHeader := c.GetHeader("Authorization")
reqCodeRaw := strings.Split(codeHeader, " ")
reqCode := strings.ReplaceAll(reqCodeRaw[len(reqCodeRaw)-1], " ", "")
for _, code := range codes {
if strings.Compare(code, reqCode) == 0 {
token, err := GenerateToken(16)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": 500,
"user": "",
"error": err.Error(),
})
return
}
err = db.updateToken(user.UserName, token) // TODO make a more robust system for authorizing info page
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": 500,
"user": "",
"error": err.Error(),
})
return
}
user.Status = 200
c.JSON(http.StatusOK, gin.H{
"user": user.UserName,
"error": "",
"token": token,
})
return
}
}
c.JSON(http.StatusNotFound, gin.H{
"status": 404,
"user": "",
"error": "User not found",
})
}
func webCheckNameApi(c *gin.Context) {
users, err := db.getUsers()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": 500,
"error": err.Error(),
})
return
}
anyMatch := false
for _, user := range users {
if strings.Compare(user, strings.ToLower(c.Param("user"))) == 0 {
anyMatch = true
break
}
}
if anyMatch {
c.JSON(http.StatusConflict, gin.H{
"status": 409,
"error": "userid already exists",
})
return
}
c.JSON(http.StatusOK, gin.H{
"status": 200,
"error": "",
})
}
func webRegisterApi(c *gin.Context) { //TODO refactor with JS form submission or under logged in user dashboard
var newUser User
err := c.ShouldBind(&newUser)
if err != nil {
fmt.Printf("Error: %v\n", err)
c.HTML(http.StatusInternalServerError, "base.html.tmpl", gin.H{
"header": "Registration Error",
"body": err.Error(),
})
return
}
err = db.checkRegistrationCode(newUser.RegistrationCode)
if errors.Is(err, NoEntriesFoundError) {
c.HTML(http.StatusNotFound, "base.html.tmpl", gin.H{
"header": "Registration Error",
"body": err.Error(),
})
return
}
c.Redirect(http.StatusSeeOther, "/register/success")
}
func webUser(c *gin.Context) {
c.File("./html/verify.html")
}
func webUserInfo(c *gin.Context) {
user, err := db.queryUser(strings.ToLower(c.Param("user")))
if err != nil {
if errors.Is(err, NoEntriesFoundError) {
body := template.HTML("The user searched is not found, please try again.")
c.HTML(http.StatusNotFound, "base.html.tmpl", gin.H{
"header": "User Not Found",
"body": body,
})
return
}
}
if user.CurrentToken == nil {
c.HTML(http.StatusUnauthorized, "base.html.tmpl", gin.H{
"header": "Unauthorized",
"body": "You don't have the right token, please try again.",
})
return
}
if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 {
if err != nil {
if errors.Is(err, NoEntriesFoundError) {
c.HTML(http.StatusUnauthorized, "base.html.tmpl", gin.H{
"header": "Unauthorized",
"body": "You don't have the right token, please try again.",
})
return
}
fmt.Printf("Error: %s\n", err)
c.HTML(http.StatusInternalServerError, "base.html.tmpl", gin.H{
"header": "Error",
"body": err.Error(),
})
return
}
addressStr := fmt.Sprintf("%s", user.MailingAddress.Street1)
if user.MailingAddress.Street2 == nil {
addressStr = fmt.Sprintf("%s, %s", addressStr, user.MailingAddress.City)
} else {
addressStr = fmt.Sprintf("%s, %s, %s", addressStr, *user.MailingAddress.Street2, user.MailingAddress.City)
}
if user.MailingAddress.State == nil {
addressStr = fmt.Sprintf("%s, %s, %s", addressStr, user.MailingAddress.PostalCode, user.MailingAddress.Country)
} else {
addressStr = fmt.Sprintf("%s, %s, %s, %s", addressStr, *user.MailingAddress.State, user.MailingAddress.PostalCode, user.MailingAddress.Country)
}
c.HTML(http.StatusOK, "info.html.tmpl", gin.H{
"contact_name": user.ContactName,
"phone_number": user.ContactNumber,
"email_address": user.EmailAddress,
"address": addressStr,
})
return
}
c.HTML(http.StatusUnauthorized, "base.html.tmpl", gin.H{
"header": "Unauthorized",
"body": "You don't have the right token :/",
})
}
func webLoginAuth(c *gin.Context) {
}
func webLoginAuthPost(c *gin.Context) {
}
func webLogoutAuth(c *gin.Context) {
}