12
Dockerfile
|
|
@ -1,12 +1,14 @@
|
|||
FROM golang:1.17
|
||||
FROM golang:1.18
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY src build-src
|
||||
RUN go mod init connermccall.me
|
||||
RUN go get -u github.com/gin-gonic/gin
|
||||
RUN go build build-src/main.go
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download && go mod verify
|
||||
|
||||
COPY src build-src
|
||||
|
||||
RUN go build build-src/*.go
|
||||
COPY static static
|
||||
RUN rm -rf src
|
||||
|
||||
EXPOSE 8080/tcp
|
||||
|
|
|
|||
2
go.mod
|
|
@ -3,6 +3,7 @@ module connermccall.me
|
|||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/dvcrn/maskedemail-cli v0.0.0-20230603113757-7a0ce82189f6 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.7.7 // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
|
|
@ -12,6 +13,7 @@ require (
|
|||
github.com/json-iterator/go v1.1.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
|
|
|
|||
4
go.sum
|
|
@ -1,5 +1,7 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvcrn/maskedemail-cli v0.0.0-20230603113757-7a0ce82189f6 h1:ORgd6cYAg5GdPNNE95vFXyy73rx8xy1XCprpVDGN1jE=
|
||||
github.com/dvcrn/maskedemail-cli v0.0.0-20230603113757-7a0ce82189f6/go.mod h1:TU8E0RslsOUiXYsICzcpPcThV56wOhuGt+5+NNXDx6k=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
|
|
@ -20,6 +22,8 @@ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
|||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
|
|
|
|||
32
src/main.go
|
|
@ -2,10 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
|
@ -60,5 +62,33 @@ func main() {
|
|||
"message": "OK",
|
||||
})
|
||||
})
|
||||
router.Static("/maskedemail", "./static/maskedemail")
|
||||
router.GET("mask", func(c *gin.Context) {
|
||||
description := c.Query("description")
|
||||
if description == "" {
|
||||
// Return an error response if description is not provided
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Description is required"})
|
||||
return
|
||||
}
|
||||
htmlx := c.Query("htmlx")
|
||||
domain := c.Query("domain") // This will be an empty string if not provided
|
||||
|
||||
email, err := CreateMaskedEmail(description, domain)
|
||||
if err != nil {
|
||||
// Log the error and return an error response
|
||||
log.Println("Error:", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create masked email"})
|
||||
return
|
||||
}
|
||||
|
||||
log.Print(email)
|
||||
if htmlx != "true" {
|
||||
c.String(http.StatusOK, email)
|
||||
} else {
|
||||
res := fmt.Sprintf("<h1>%s</h1>", email)
|
||||
c.String(http.StatusOK, res)
|
||||
}
|
||||
})
|
||||
|
||||
router.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
|
||||
}
|
||||
|
|
|
|||
29
src/maskedmail.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/dvcrn/maskedemail-cli/pkg"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
func CreateMaskedEmail(alias string, domain string) (string, error) {
|
||||
var fastmailAPIKey, fastmailAccountId string
|
||||
fastmailAPIKey = os.Getenv("FASTMAIL_API_KEY")
|
||||
fastmailAccountId = os.Getenv("FASTMAIL_ACCOUNT_ID")
|
||||
// Prepare the request payload
|
||||
client := pkg.NewClient(fastmailAPIKey, "personal-api", "3654417c")
|
||||
session, err := client.Session()
|
||||
if err != nil {
|
||||
log.Fatalf("initializing session: %v", err)
|
||||
}
|
||||
createRes, err := client.CreateMaskedEmail(session, fastmailAccountId, "", alias, "", true)
|
||||
if err != nil {
|
||||
log.Fatalf("error creating masked email: %v", err)
|
||||
}
|
||||
|
||||
return createRes.Email, nil
|
||||
}
|
||||
BIN
static/maskedemail/icon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
static/maskedemail/images/icons/icon-128x128.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
static/maskedemail/images/icons/icon-144x144.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
static/maskedemail/images/icons/icon-152x152.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
static/maskedemail/images/icons/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
0
static/maskedemail/images/icons/icon-384x384.png
Normal file
BIN
static/maskedemail/images/icons/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
static/maskedemail/images/icons/icon-72x72.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
static/maskedemail/images/icons/icon-96x96.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
139
static/maskedemail/index.html
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<script src="lib/htmx.min.js"></script>
|
||||
<title>Create Masked Email</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width,initial-scale=1" />
|
||||
<link rel="manifest"
|
||||
href="manifest.json" />
|
||||
<link id="favicon"
|
||||
rel="shortcut icon"
|
||||
sizes="32x32"
|
||||
href="icon-32x32.png">
|
||||
<style>
|
||||
:root {
|
||||
--color: #1a40bc;
|
||||
--input-width: 70vw;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: 70vh;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
h1,
|
||||
input::-webkit-input-placeholder,
|
||||
button {
|
||||
font-family: 'roboto', sans-serif;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
h1 {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
form {
|
||||
box-sizing: border-box;
|
||||
|
||||
box-shadow: 2px 2px 5px 1px rgba(0, 0, 0, 0.2);
|
||||
padding-bottom: 2rem;
|
||||
padding-top:2rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 40px 25px;
|
||||
width: var(--input-width);
|
||||
display: block;
|
||||
border: none;
|
||||
padding: 10px 0;
|
||||
border-bottom: solid 1px var(--color);
|
||||
transition: all 0.3s cubic-bezier(.64, .09, .08, 1);
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 96%, var(--color) 4%);
|
||||
background-position: -var(--input-width) 0;
|
||||
background-size: var(--input-width) 100%;
|
||||
background-repeat: no-repeat;
|
||||
color: darken(var(--color), 20%);
|
||||
|
||||
&:focus,
|
||||
&:valid {
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
background-position: 0 0;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: var(--color);
|
||||
font-size: 11px;
|
||||
transform: translateY(-20px);
|
||||
visibility: visible !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
font-size: 1.4rem;
|
||||
background: var(--color);
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
padding: 6px;
|
||||
width: var(--input-width);
|
||||
color: white;
|
||||
margin-left: 25px;
|
||||
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.2);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 6px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
.reset {
|
||||
font-size: 1.1rem;
|
||||
color: -var(--color);
|
||||
display: block;
|
||||
margin-top:.5rem;
|
||||
}
|
||||
.htmx-indicator{
|
||||
margin: 1rem auto 0;
|
||||
text-align: center;
|
||||
display:block;
|
||||
}
|
||||
.htmx-request .htmx-indicator{
|
||||
display:block;
|
||||
}
|
||||
.htmx-request.htmx-indicator{
|
||||
display:block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<form id="mask-form"
|
||||
hx-get="/mask"
|
||||
hx-swap="innerHTML">
|
||||
<input type="text"
|
||||
name="description"
|
||||
required
|
||||
minlength="2">
|
||||
<input type="text"
|
||||
name="domain">
|
||||
<input type="hidden"
|
||||
name="htmlx"
|
||||
value="true">
|
||||
<button type="submit">
|
||||
Generate Email
|
||||
</button>
|
||||
<div class="htmx-indicator">Loading...</div>
|
||||
</form>
|
||||
<div>
|
||||
<a class="reset" href="/maskedemail">Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1
static/maskedemail/lib/htmx.min.js
vendored
Normal file
52
static/maskedemail/manifest.json
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"name": "Masked Email Generator",
|
||||
"short_name": "masked",
|
||||
"theme_color": "#1a40bc",
|
||||
"background_color": "#fff",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"scope": "/",
|
||||
"start_url": "/maskedemail",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./images/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||