Registration
Contact Steven Tracey
via email
@@ -26,4 +27,3 @@
-
diff --git a/html/unauthorized.html b/html/unauthorized.html
new file mode 100644
index 0000000..10fc688
--- /dev/null
+++ b/html/unauthorized.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
My Luggage Info
+
+
+
+
+
+
+
+
+
Unauthorized
+
Maybe you had the wrong code?
+
+
+
+
+
+
+
diff --git a/html/verify.html b/html/verify.html
new file mode 100644
index 0000000..c01bdc4
--- /dev/null
+++ b/html/verify.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
My Luggage Info
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main.go b/main.go
index 29f9308..58053a7 100644
--- a/main.go
+++ b/main.go
@@ -11,28 +11,30 @@ import (
"github.com/gin-gonic/gin"
"log"
"net/http"
+ "os"
"strings"
"time"
)
type Address struct {
- Id int64 `json:"address_id"`
+ Id int64 `json:"-"`
Street1 string `json:"street1"`
Street2 *string `json:"street2,omitempty"`
City string `json:"city"`
State *string `json:"state,omitempty"`
PostalCode string `json:"postal_code"`
Country string `json:"country"`
- CreatedAt time.Time `json:"created_at"`
- UpdatedAt time.Time `json:"updated_at"`
+ CreatedAt time.Time `json:"-"`
+ UpdatedAt time.Time `json:"-"`
}
type User struct {
- Status int32 `json:"status"`
- Id int64 `json:"user_id"`
- UserName string `json:"user_name"`
- SecretCodes string `json:"secret_codes"`
- CurrentToken *string
+ Status int32 `json:"status"`
+ Id int64 `json:"-"`
+ UserName string `json:"user_name"`
+ ContactName string `json:"contact_name"`
+ SecretCodes string `json:"-"`
+ CurrentToken *string `json:"-"`
ContactNumber string `json:"contact_number"`
EmailAddress string `json:"email_address"`
MailingAddress Address `json:"mailing_address"`
@@ -87,9 +89,11 @@ func main() {
go startCleanupLoop(ctx, db.db, time.Hour, 24*time.Hour)
r := gin.Default()
+ gin.SetMode(gin.ReleaseMode)
+ allowedOrigins := strings.Split(os.Getenv("CORS_ALLOWED_ORIGINS"), ",")
r.Use(cors.New(cors.Config{
- AllowOrigins: []string{"http://localhost:8080", "http://localhost:8000"},
+ AllowOrigins: allowedOrigins,
AllowMethods: []string{"GET", "POST"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length", "Authorization"},
@@ -98,25 +102,47 @@ func main() {
r.Static("/static", "./static")
r.StaticFile("/favicon.ico", "./static/favicon.ico")
+ r.LoadHTMLGlob("./templates/*")
r.GET("/", func(c *gin.Context) {
- c.File("./static/index.html")
+ c.File("./html/home.html")
+ })
+ r.GET("/ping", func(c *gin.Context) {
+ c.JSON(http.StatusOK, gin.H{
+ "message": "pong",
+ })
})
api := r.Group("/api")
- api.GET("/luggage/:user", func(c *gin.Context) {
+ api.GET("/u/:user", func(c *gin.Context) {
user, err := db.queryUser(c.Param("user"))
- if err != nil {
- if errors.Is(err, NoEntriesFoundError) {
- c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
- "error": "User not found",
- })
+ if user.CurrentToken == nil {
+ c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
+ "status": 401,
+ "error": "Unauthorized",
+ })
+ } else if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 {
+ if err != nil {
+ if errors.Is(err, NoEntriesFoundError) {
+ c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
+ "status": 401,
+ "error": "Unauthorized",
+ })
+ } else {
+ fmt.Printf("Error: %s\n", err)
+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
+ "status": 500,
+ "error": err.Error(),
+ })
+ }
} else {
- fmt.Printf("Error: %s\n", err)
- c.AbortWithStatus(http.StatusBadRequest)
+ c.JSON(http.StatusOK, user)
}
} else {
- c.JSON(http.StatusOK, user)
+ c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
+ "status": 401,
+ "error": "Unauthorized",
+ })
}
})
api.GET("/verify/:user", func(c *gin.Context) {
@@ -185,38 +211,49 @@ func main() {
}
}
})
- api.GET("/ping", func(c *gin.Context) {
- c.JSON(http.StatusOK, gin.H{
- "message": "pong",
- })
- })
- r.GET("/u/:user/info", func(c *gin.Context) {
+ user := r.Group("/u")
+ user.GET("/:user", func(c *gin.Context) {
+ c.File("./html/verify.html")
+ })
+ user.GET("/:user/info", func(c *gin.Context) {
user, err := db.queryUser(c.Param("user"))
if user.CurrentToken == nil {
- c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
- "status": 403,
- "error": "Unauthorized",
- })
+ c.Status(http.StatusUnauthorized)
+ c.File("./html/unauthorized.html")
} else if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 {
if err != nil {
if errors.Is(err, NoEntriesFoundError) {
- c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
- "error": "User not found",
- })
+ c.Status(http.StatusUnauthorized)
+ c.File("./html/unauthorized.html")
} else {
fmt.Printf("Error: %s\n", err)
- c.AbortWithStatus(http.StatusBadRequest)
+ c.HTML(http.StatusInternalServerError, "error.html.tmpl", gin.H{
+ "error": err.Error(),
+ })
}
} else {
- c.JSON(http.StatusOK, user)
+ 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,
+ })
}
} else {
- c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
- "status": 403,
- "user": "",
- "error": "Unauthorized",
- })
+ c.Status(http.StatusUnauthorized)
+ c.File("./html/unauthorized.html")
}
})
diff --git a/renderHtml.bat b/renderHtml.bat
new file mode 100644
index 0000000..98df8c8
--- /dev/null
+++ b/renderHtml.bat
@@ -0,0 +1,17 @@
+@echo off
+title Render HTML from PHP
+
+del .\html\*
+del .\static\style.css
+del .\static\verify.js
+del .\templates\*
+
+curl -o ./html/home.html http://localhost:8080/home.php
+curl -o ./html/unauthorized.html http://localhost:8080/unauthorized.php
+curl -o ./html/verify.html http://localhost:8080/verify.php
+
+curl -o ./templates/info.html.tmpl http://localhost:8080/info.php
+curl -o ./templates/error.html.tmpl http://localhost:8080/error.php
+
+curl -o ./static/verify.js http://localhost:8080/static/verify.js
+curl -o ./static/style.css http://localhost:8080/static/style.css
diff --git a/static/style.css b/static/style.css
index bc0b40c..fd60afd 100644
--- a/static/style.css
+++ b/static/style.css
@@ -1,5 +1,6 @@
:root {
--dark: #1f363dff;
+ --dark-click: #152429;
--mid-dark: #40798cff;
--mid: #70a9a1ff;
--mid-light: #9ec1a3ff;
@@ -7,43 +8,152 @@
font-family: Verdana,sans-serif;
}
-body {
- margin: 0;
+@media only screen and (min-width: 300px) {
+ body {
+ margin: 0;
+ height: 92vh;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .header {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ justify-content: space-between;
+ background-color: var(--mid-dark);
+ padding: 0 1rem;
+ height: 120px;
+ }
+
+ .spacer {
+ flex: 0 0 72px;
+ }
+
+ .header-icon {
+ display: block;
+ flex: 0 0 72px;
+ width: 20vw;
+ height: auto;
+ max-height: 85px;
+ }
+
+ .footer {
+ background-color: var(--dark);
+ display: flex;
+ flex: 0 0 auto;
+ height: 120px;
+ }
+
+ .content {
+ display: flex;
+ width: 85vw;
+ max-width: 800px;
+ padding: 1.5rem;
+ background: white;
+ border-radius: 5px;
+ text-align: center;
+ }
+
+ .content input {
+ font-size: 16px;
+ }
+
+ .content button {
+ color: var(--dark);
+ background-color: var(--light);
+ border-radius: 8px;
+ border: 2px solid;
+ border-color: var(--dark);
+ padding: 8px 12px;
+ text-align: center;
+ text-decoration: none;
+ font-size: 16px;
+ cursor: pointer;
+ }
+
+ .align-left {
+ align-items: flex-start;
+ margin-left: 5vw;
+ }
}
-.header {
- display: flex;
- align-items: center; /* vertical centering */
- justify-content: space-between; /* icon left, spacer right */
- background-color: var(--mid-dark);
- padding: 0 1rem;
- height: 80px;
-}
+@media only screen and (min-width: 700px) {
+ body {
+ margin: 0;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ }
-.spacer {
- flex: 0 0 64px; /* or your icon’s width */
-}
+ .header {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ justify-content: space-between;
+ background-color: var(--mid-dark);
+ padding: 0 1rem;
+ height: 80px;
+ }
-.header-icon {
- display: block; /* remove inline whitespace */
- flex: 0 0 64px; /* or your icon’s width */
- width: 15vw; /* fill the 64px container */
- height: auto; /* keep aspect ratio */
- max-height: 80px; /* never exceed 80px bar height */
+ .spacer {
+ flex: 0 0 64px;
+ }
+
+ .header-icon {
+ display: block;
+ flex: 0 0 64px;
+ width: 15vw;
+ height: auto;
+ max-height: 80px;
+ }
+
+ .footer {
+ background-color: var(--dark);
+ display: flex;
+ flex: 0 0 auto;
+ height: 80px;
+ }
+
+ .content {
+ display: flex;
+ width: 90vw;
+ max-width: 800px;
+ padding: 1.5rem;
+ background: white;
+ border-radius: 5px;
+ text-align: center;
+ }
+
+ .content input {
+ font-size: 16px;
+ }
+
+ .content button {
+ color: var(--dark);
+ background-color: var(--light);
+ border-radius: 8px;
+ border: 2px solid;
+ border-color: var(--dark);
+ padding: 8px 12px;
+ text-align: center;
+ text-decoration: none;
+ font-size: 16px;
+ cursor: pointer;
+ }
+
+ .align-left {
+ align-items: flex-start;
+ margin-left: 5vw;
+ }
}
.title {
- flex: 1; /* take up all the space between */
- text-align: center; /* center text within that space */
+ flex: 1;
+ text-align: center;
color: var(--light);
}
-.footer {
- background-color: var(--dark);
- display: flex;
- height: 80px;
-}
-
.footer-text {
display: inherit;
align-content: center;
@@ -78,7 +188,11 @@ body {
}
.container {
- padding: 20vh 0 calc(80vh - 303px) 0;
+ flex: 1 0 auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 0;
background-image: linear-gradient(
to bottom right,
var(--mid-light),
@@ -86,13 +200,8 @@ body {
);
}
-.content {
- margin: 0 auto;
- width: 90vw; /* takes 90% of viewport (or container) width */
- max-width: 800px; /* but never wider than 800px */
- padding: 1.5rem; /* give it some breathing room inside */
- background: white;
- border-radius: 5px;
+.content h1 {
+ text-align: center;
}
.content h3 {
@@ -101,4 +210,38 @@ body {
.content p {
text-align: center;
+}
+
+.content button:hover {
+ color: var(--light);
+ background-color: var(--dark);
+}
+
+.content button:active {
+ background-color: var(--dark-click);
+}
+
+.flex-vertical {
+ flex-direction: column;
+}
+
+.flex-horizontal {
+ flex-direction: row;
+}
+
+.hidden {
+ display: none;
+}
+
+.section-one {
+ flex: 0 0 auto;
+}
+
+.section-two {
+ display: flex;
+ flex: 1 0 auto;
+}
+
+#status {
+ margin-bottom: 0;
}
\ No newline at end of file
diff --git a/static/verify.js b/static/verify.js
new file mode 100644
index 0000000..40924ad
--- /dev/null
+++ b/static/verify.js
@@ -0,0 +1,32 @@
+document.getElementById("submitBtn").addEventListener('click', function(e) {
+ let code = document.getElementById("code").value;
+ console.log("Clicked: " + code);
+
+ const path = window.location.pathname;
+ const parts = path.split("/");
+ const user = parts.pop();
+
+ fetch("/api/verify/" + user, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': "Basic " + code,
+ }
+ }).then(response => response.json())
+ .then(data => {
+ let statusText = document.getElementById("status");
+ console.log(data);
+ statusText.classList.remove("hidden");
+ if (data.status === 404) {
+ // Not found
+ statusText.innerText = "User with that code not found";
+ } else if (data.status === 200) {
+ // Display found and redirect to baseUrl/u/user/info with auth token
+ statusText.innerText = "User found, redirecting...";
+ window.location.replace("/u/" + user + "/info?token=" + data.token);
+ } else {
+ // Error
+ statusText.innerText = "Error, please send this to Steven to be fixed. Error: " + data.error;
+ }
+ })
+})
\ No newline at end of file
diff --git a/templates/error.html.tmpl b/templates/error.html.tmpl
new file mode 100644
index 0000000..2b971d9
--- /dev/null
+++ b/templates/error.html.tmpl
@@ -0,0 +1,30 @@
+
+
+
+
+
+
My Luggage Info
+
+
+
+
+
+
+
+
+
Error
+
Please report the following to Steven
+
{{ .error }}
+
+
+
+
+
+
+
diff --git a/templates/info.html.tmpl b/templates/info.html.tmpl
new file mode 100644
index 0000000..18965df
--- /dev/null
+++ b/templates/info.html.tmpl
@@ -0,0 +1,34 @@
+
+
+
+
+
+
My Luggage Info
+
+
+
+
+
+
+
+
+
Owner Information
+
+
+
Contact Name: {{ .contact_name }}
+
Phone Number: {{ .phone_number }}
+
Email Address: {{ .email_address }}
+
Address: {{ .address }}
+
+
+
+
+
+
+
\ No newline at end of file