Compare commits
2 Commits
ae4044e886
...
667b27f036
Author | SHA1 | Date | |
---|---|---|---|
667b27f036 | |||
97581703c1 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -126,3 +126,6 @@ fabric.properties
|
|||||||
# Android studio 3.1+ serialized cache file
|
# Android studio 3.1+ serialized cache file
|
||||||
.idea/caches/build_file_checksums.ser
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
luggagelocator-all
|
||||||
|
luggagelocator.tar.gz
|
||||||
|
LuggageLocator
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
33
README.md
33
README.md
@ -1,13 +1,26 @@
|
|||||||
# LuggageTracker
|
# LuggageTracker
|
||||||
|
|
||||||
## Envs
|
## Envs
|
||||||
- PORT | 8080
|
- PORT.................| 8080
|
||||||
- DB_USER | user
|
- DB_USER..............| user
|
||||||
- DB_PASS | pass
|
- DB_PASS..............| pass
|
||||||
- DB_HOST | 10.0.0.1
|
- DB_HOST..............| 10.0.0.1
|
||||||
- DB_PORT | 3306
|
- DB_PORT..............| 3306
|
||||||
- DB_SCHEMA | luggage
|
- DB_SCHEMA............| luggage
|
||||||
- DB_CA_CERT_PATH | /etc/ssl/ca-cert.pem
|
- DB_CA_CERT_PATH......| /etc/ssl/ca-cert.pem
|
||||||
- DB_CERT_PATH | /etc/ssl/client-cert.pem
|
- DB_CERT_PATH.........| /etc/ssl/client-cert.pem
|
||||||
- DB_KEY_PATH | /etc/ssl/private/client-key.pem
|
- DB_KEY_PATH..........| /etc/ssl/private/client-key.pem
|
||||||
- DB_SKIP_VERIFY | true
|
- DB_SKIP_VERIFY.......| true
|
||||||
|
- CORS_ALLOWED_ORIGINS.| http://localhost:8080,https://example.com
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
### GET
|
||||||
|
- /
|
||||||
|
- /ping
|
||||||
|
- /api/u/:user
|
||||||
|
- requires `token` query param
|
||||||
|
- /api/verify/:user
|
||||||
|
- requires `Authorization` header to be set to `Basic (code)`
|
||||||
|
- /u/:user
|
||||||
|
- /u/:user/info
|
||||||
|
- requires `token` query param
|
||||||
|
21
build.bat
Normal file
21
build.bat
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
@echo off
|
||||||
|
title Building LuggageLocator
|
||||||
|
|
||||||
|
set GOARCH=amd64
|
||||||
|
set GOOS=linux
|
||||||
|
set GIN_MODE=release
|
||||||
|
go build -ldflags="-s -w" -o LuggageLocator .
|
||||||
|
|
||||||
|
mkdir luggagelocator-all
|
||||||
|
rmdir /s /q .\luggagelocator-all
|
||||||
|
mkdir luggagelocator-all
|
||||||
|
|
||||||
|
xcopy /S .\html .\luggagelocator-all\html\
|
||||||
|
xcopy /S .\static .\luggagelocator-all\static\
|
||||||
|
xcopy /S .\templates .\luggagelocator-all\templates\
|
||||||
|
xcopy .\formatDB.sql .\luggagelocator-all\
|
||||||
|
xcopy .\LuggageLocator .\luggagelocator-all\
|
||||||
|
xcopy .\README.md .\luggagelocator-all\
|
||||||
|
|
||||||
|
del /s /q .\luggagelocator.tar.gz
|
||||||
|
tar -czvf luggagelocator.tar.gz luggagelocator-all
|
4
db.go
4
db.go
@ -94,7 +94,7 @@ func (ldb *LDB) queryUser(userName string) (u *User, newError error) {
|
|||||||
db := ldb.db
|
db := ldb.db
|
||||||
|
|
||||||
queryStr := `
|
queryStr := `
|
||||||
SELECT users.user_name, users.secret_codes, users.current_token, users.contact_number, users.email_address,
|
SELECT users.user_name, users.contact_name, users.secret_codes, users.current_token, users.contact_number, users.email_address,
|
||||||
addresses.street1, addresses.street2, addresses.city, addresses.state, addresses.postal_code,
|
addresses.street1, addresses.street2, addresses.city, addresses.state, addresses.postal_code,
|
||||||
addresses.country, addresses.created_at, addresses.updated_at
|
addresses.country, addresses.created_at, addresses.updated_at
|
||||||
FROM users
|
FROM users
|
||||||
@ -114,7 +114,7 @@ func (ldb *LDB) queryUser(userName string) (u *User, newError error) {
|
|||||||
return nil, NoEntriesFoundError
|
return nil, NoEntriesFoundError
|
||||||
}
|
}
|
||||||
user := new(User)
|
user := new(User)
|
||||||
err = res.Scan(&user.UserName, &user.SecretCodes, &user.CurrentToken, &user.ContactNumber, &user.EmailAddress,
|
err = res.Scan(&user.UserName, &user.ContactName, &user.SecretCodes, &user.CurrentToken, &user.ContactNumber, &user.EmailAddress,
|
||||||
&user.MailingAddress.Street1, &user.MailingAddress.Street2, &user.MailingAddress.City,
|
&user.MailingAddress.Street1, &user.MailingAddress.Street2, &user.MailingAddress.City,
|
||||||
&user.MailingAddress.State, &user.MailingAddress.PostalCode, &user.MailingAddress.Country,
|
&user.MailingAddress.State, &user.MailingAddress.PostalCode, &user.MailingAddress.Country,
|
||||||
&user.MailingAddress.CreatedAt, &user.MailingAddress.UpdatedAt,
|
&user.MailingAddress.CreatedAt, &user.MailingAddress.UpdatedAt,
|
||||||
|
@ -2,18 +2,19 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Luggage Tracker</title>
|
<meta name="viewport" content="width=device-width, internal-scale=1, viewport-fit=cover">
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
<title>My Luggage Info</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css?v=43">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<img class="header-icon" src="/static/icon.png" alt="icon"/>
|
<img class="header-icon" src="/static/icon.png" alt="icon"/>
|
||||||
<h1 class="title">Luggage Tracker</h1>
|
<h1 class="title">My Luggage Info</h1>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
</header>
|
</header>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="content">
|
<div class="content flex-vertical">
|
||||||
<h3>Registration</h3>
|
<h3>Registration</h3>
|
||||||
<p>Contact Steven Tracey
|
<p>Contact Steven Tracey
|
||||||
<a class="contact-link" href="mailto:steven@nevets.tech?subject=QR%20Generator%20-%20(Your%20Name)&body=Name%3A%0APhone%20Number%3A%0AEmail%20Address%3A%0AMailing%20Address%3A%0AStreet%3A%0ACity%3A%0AZip%3A%0AState%3A%0ACountry%3A">via email</a>
|
<a class="contact-link" href="mailto:steven@nevets.tech?subject=QR%20Generator%20-%20(Your%20Name)&body=Name%3A%0APhone%20Number%3A%0AEmail%20Address%3A%0AMailing%20Address%3A%0AStreet%3A%0ACity%3A%0AZip%3A%0AState%3A%0ACountry%3A">via email</a>
|
||||||
@ -26,4 +27,3 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
29
html/unauthorized.html
Normal file
29
html/unauthorized.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, internal-scale=1, viewport-fit=cover">
|
||||||
|
<title>My Luggage Info</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css?v=43">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<img class="header-icon" src="/static/icon.png" alt="icon"/>
|
||||||
|
<h1 class="title">My Luggage Info</h1>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<div class="content flex-vertical">
|
||||||
|
<div class="section-one">
|
||||||
|
<h1>Unauthorized</h1>
|
||||||
|
<p>Maybe you had the wrong code?</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p class="footer-text">Made hastily by <a class="footer-text" href="https://www.linkedin.com/in/steven-tracey18/">Steven Tracey</a></p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
34
html/verify.html
Normal file
34
html/verify.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, internal-scale=1, viewport-fit=cover">
|
||||||
|
<title>My Luggage Info</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css?v=43">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<img class="header-icon" src="/static/icon.png" alt="icon"/>
|
||||||
|
<h1 class="title">My Luggage Info</h1>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<div class="content flex-vertical">
|
||||||
|
<div class="section-one flex-horizontal">
|
||||||
|
<label for="code">Secret Code (SC):</label>
|
||||||
|
<input id="code" type="text">
|
||||||
|
<button id="submitBtn">Submit</button>
|
||||||
|
</div>
|
||||||
|
<div class="section-two flex-vertical">
|
||||||
|
<p id="status" class="hidden">Text</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p class="footer-text">Made hastily by <a class="footer-text" href="https://www.linkedin.com/in/steven-tracey18/">Steven Tracey</a></p>
|
||||||
|
</footer>
|
||||||
|
<script src="/static/verify.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
137
main.go
137
main.go
@ -11,30 +11,32 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Address struct {
|
type Address struct {
|
||||||
Id int64 `json:"address_id"`
|
Id int64 `json:"-"`
|
||||||
Street1 string `json:"street1"`
|
Street1 string `json:"street1" form:"street1"`
|
||||||
Street2 *string `json:"street2,omitempty"`
|
Street2 *string `json:"street2,omitempty" form:"street2,omitempty"`
|
||||||
City string `json:"city"`
|
City string `json:"city" form:"city"`
|
||||||
State *string `json:"state,omitempty"`
|
State *string `json:"state,omitempty" form:"state,omitempty"`
|
||||||
PostalCode string `json:"postal_code"`
|
PostalCode string `json:"postal_code" form:"postal_code"`
|
||||||
Country string `json:"country"`
|
Country string `json:"country" form:"country"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"-"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Status int32 `json:"status"`
|
Status int32 `json:"status"`
|
||||||
Id int64 `json:"user_id"`
|
Id int64 `json:"-"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name" form:"user_name"`
|
||||||
SecretCodes string `json:"secret_codes"`
|
ContactName string `json:"contact_name" form:"contact_name"`
|
||||||
CurrentToken *string
|
SecretCodes string `json:"-"`
|
||||||
ContactNumber string `json:"contact_number"`
|
CurrentToken *string `json:"-"`
|
||||||
EmailAddress string `json:"email_address"`
|
ContactNumber string `json:"contact_number" form:"phone_number"`
|
||||||
|
EmailAddress string `json:"email_address" form:"email_address"`
|
||||||
MailingAddress Address `json:"mailing_address"`
|
MailingAddress Address `json:"mailing_address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,9 +89,11 @@ func main() {
|
|||||||
go startCleanupLoop(ctx, db.db, time.Hour, 24*time.Hour)
|
go startCleanupLoop(ctx, db.db, time.Hour, 24*time.Hour)
|
||||||
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
|
allowedOrigins := strings.Split(os.Getenv("CORS_ALLOWED_ORIGINS"), ",")
|
||||||
r.Use(cors.New(cors.Config{
|
r.Use(cors.New(cors.Config{
|
||||||
AllowOrigins: []string{"http://localhost:8080", "http://localhost:8000"},
|
AllowOrigins: allowedOrigins,
|
||||||
AllowMethods: []string{"GET", "POST"},
|
AllowMethods: []string{"GET", "POST"},
|
||||||
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
||||||
ExposeHeaders: []string{"Content-Length", "Authorization"},
|
ExposeHeaders: []string{"Content-Length", "Authorization"},
|
||||||
@ -98,25 +102,47 @@ func main() {
|
|||||||
|
|
||||||
r.Static("/static", "./static")
|
r.Static("/static", "./static")
|
||||||
r.StaticFile("/favicon.ico", "./static/favicon.ico")
|
r.StaticFile("/favicon.ico", "./static/favicon.ico")
|
||||||
|
r.LoadHTMLGlob("./templates/*")
|
||||||
|
|
||||||
r.GET("/", func(c *gin.Context) {
|
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 := 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"))
|
user, err := db.queryUser(c.Param("user"))
|
||||||
if err != nil {
|
if user.CurrentToken == nil {
|
||||||
if errors.Is(err, NoEntriesFoundError) {
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
|
"status": 401,
|
||||||
"error": "User not found",
|
"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 {
|
} else {
|
||||||
fmt.Printf("Error: %s\n", err)
|
c.JSON(http.StatusOK, user)
|
||||||
c.AbortWithStatus(http.StatusBadRequest)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.JSON(http.StatusOK, user)
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"status": 401,
|
||||||
|
"error": "Unauthorized",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
api.GET("/verify/:user", func(c *gin.Context) {
|
api.GET("/verify/:user", func(c *gin.Context) {
|
||||||
@ -185,38 +211,57 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
api.GET("/ping", func(c *gin.Context) {
|
api.POST("/register/:user", func(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
var newUser User
|
||||||
"message": "pong",
|
err := c.Bind(&newUser)
|
||||||
})
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v", err)
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, newUser)
|
||||||
})
|
})
|
||||||
|
|
||||||
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"))
|
user, err := db.queryUser(c.Param("user"))
|
||||||
if user.CurrentToken == nil {
|
if user.CurrentToken == nil {
|
||||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
c.Status(http.StatusUnauthorized)
|
||||||
"status": 403,
|
c.File("./html/unauthorized.html")
|
||||||
"error": "Unauthorized",
|
|
||||||
})
|
|
||||||
} else if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 {
|
} else if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, NoEntriesFoundError) {
|
if errors.Is(err, NoEntriesFoundError) {
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
|
c.Status(http.StatusUnauthorized)
|
||||||
"error": "User not found",
|
c.File("./html/unauthorized.html")
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Error: %s\n", err)
|
fmt.Printf("Error: %s\n", err)
|
||||||
c.AbortWithStatus(http.StatusBadRequest)
|
c.HTML(http.StatusInternalServerError, "error.html.tmpl", gin.H{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
} else {
|
||||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
c.Status(http.StatusUnauthorized)
|
||||||
"status": 403,
|
c.File("./html/unauthorized.html")
|
||||||
"user": "",
|
|
||||||
"error": "Unauthorized",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
17
renderHtml.bat
Normal file
17
renderHtml.bat
Normal file
@ -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
|
213
static/style.css
213
static/style.css
@ -1,5 +1,6 @@
|
|||||||
:root {
|
:root {
|
||||||
--dark: #1f363dff;
|
--dark: #1f363dff;
|
||||||
|
--dark-click: #152429;
|
||||||
--mid-dark: #40798cff;
|
--mid-dark: #40798cff;
|
||||||
--mid: #70a9a1ff;
|
--mid: #70a9a1ff;
|
||||||
--mid-light: #9ec1a3ff;
|
--mid-light: #9ec1a3ff;
|
||||||
@ -7,43 +8,152 @@
|
|||||||
font-family: Verdana,sans-serif;
|
font-family: Verdana,sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
@media only screen and (min-width: 300px) {
|
||||||
margin: 0;
|
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 {
|
@media only screen and (min-width: 700px) {
|
||||||
display: flex;
|
body {
|
||||||
align-items: center; /* vertical centering */
|
margin: 0;
|
||||||
justify-content: space-between; /* icon left, spacer right */
|
height: 100vh;
|
||||||
background-color: var(--mid-dark);
|
display: flex;
|
||||||
padding: 0 1rem;
|
flex-direction: column;
|
||||||
height: 80px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.spacer {
|
.header {
|
||||||
flex: 0 0 64px; /* or your icon’s width */
|
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 {
|
.spacer {
|
||||||
display: block; /* remove inline whitespace */
|
flex: 0 0 64px;
|
||||||
flex: 0 0 64px; /* or your icon’s width */
|
}
|
||||||
width: 15vw; /* fill the 64px container */
|
|
||||||
height: auto; /* keep aspect ratio */
|
.header-icon {
|
||||||
max-height: 80px; /* never exceed 80px bar height */
|
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 {
|
.title {
|
||||||
flex: 1; /* take up all the space between */
|
flex: 1;
|
||||||
text-align: center; /* center text within that space */
|
text-align: center;
|
||||||
color: var(--light);
|
color: var(--light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
|
||||||
background-color: var(--dark);
|
|
||||||
display: flex;
|
|
||||||
height: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-text {
|
.footer-text {
|
||||||
display: inherit;
|
display: inherit;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
@ -78,7 +188,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.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(
|
background-image: linear-gradient(
|
||||||
to bottom right,
|
to bottom right,
|
||||||
var(--mid-light),
|
var(--mid-light),
|
||||||
@ -86,13 +200,8 @@ body {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content h1 {
|
||||||
margin: 0 auto;
|
text-align: center;
|
||||||
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 h3 {
|
.content h3 {
|
||||||
@ -102,3 +211,37 @@ body {
|
|||||||
.content p {
|
.content p {
|
||||||
text-align: center;
|
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;
|
||||||
|
}
|
32
static/verify.js
Normal file
32
static/verify.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
30
templates/error.html.tmpl
Normal file
30
templates/error.html.tmpl
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, internal-scale=1, viewport-fit=cover">
|
||||||
|
<title>My Luggage Info</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css?v=43">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<img class="header-icon" src="/static/icon.png" alt="icon"/>
|
||||||
|
<h1 class="title">My Luggage Info</h1>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<div class="content flex-vertical">
|
||||||
|
<div class="section-one">
|
||||||
|
<h1>Error</h1>
|
||||||
|
<p>Please report the following to Steven</p>
|
||||||
|
<p>{{ .error }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p class="footer-text">Made hastily by <a class="footer-text" href="https://www.linkedin.com/in/steven-tracey18/">Steven Tracey</a></p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
34
templates/info.html.tmpl
Normal file
34
templates/info.html.tmpl
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, internal-scale=1, viewport-fit=cover">
|
||||||
|
<title>My Luggage Info</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css?v=43">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<img class="header-icon" src="/static/icon.png" alt="icon"/>
|
||||||
|
<h1 class="title">My Luggage Info</h1>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<div class="content flex-vertical">
|
||||||
|
<div class="section-one flex-vertical">
|
||||||
|
<h1>Owner Information</h1>
|
||||||
|
</div>
|
||||||
|
<div class="section-two flex-vertical align-left">
|
||||||
|
<p>Contact Name: {{ .contact_name }}</p>
|
||||||
|
<p>Phone Number: {{ .phone_number }}</p>
|
||||||
|
<p>Email Address: {{ .email_address }}</p>
|
||||||
|
<p>Address: {{ .address }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p class="footer-text">Made hastily by <a class="footer-text" href="https://www.linkedin.com/in/steven-tracey18/">Steven Tracey</a></p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user