-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
152 lines (133 loc) · 3.63 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main
import (
"crypto/rand"
"math/big"
"regexp"
"strings"
"github.com/aws/aws-lambda-go/lambda"
"github.com/g-harel/targetblank/internal/crypto"
"github.com/g-harel/targetblank/internal/handler"
"github.com/g-harel/targetblank/internal/parse"
"github.com/g-harel/targetblank/services/mailer"
"github.com/g-harel/targetblank/services/secrets"
"github.com/g-harel/targetblank/services/storage"
)
var mailerSend = mailer.Send
var secretsKey = secrets.Key
var storagePageCreate = storage.PageCreate
var defaultDocument = `version 1
title = Welcome!
===
This is your new page, make sure you save the url so that you can come back to it.
You can also share the url with others if you want to show off your links!
Click the edit button in the top right to start making changes.
---
Helpful Links
Document Format [https://github.com/g-harel/targetblank/#document-format]
Keyboard Shortcuts [https://github.com/g-harel/targetblank/#keyboard-shortcuts]
`
// Generates a pseudorandom page id.
func genPageID() string {
// Alphabet of unambiguous characters (without "Il0O").
var alphabet = []rune("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ")
b := make([]rune, 6)
for i := range b {
pos, err := rand.Int(rand.Reader, big.NewInt(int64(len(alphabet))))
if err != nil {
panic(err)
}
b[i] = alphabet[pos.Int64()]
}
return string(b)
}
// Create creates a new page for the given email address.
// An email is then sent to the owner with a temporary link.
func Create(req *handler.Request, res *handler.Response) *handler.Error {
email := strings.TrimSpace(req.Body)
match, err := regexp.MatchString(`^\S+@\S+\.\S+$`, email)
if err != nil {
return handler.InternalErr("match email pattern: %v", err)
}
if !match {
return handler.ClientErr(handler.ErrInvalidEmail)
}
emailHash, err := crypto.Hash(email)
if err != nil {
return handler.InternalErr("hash email: %v", err)
}
page := &storage.Page{Email: emailHash}
pass := make([]byte, 16)
_, err = rand.Read(pass)
if err != nil {
return handler.InternalErr("generate random password: %v", err)
}
passHash, err := crypto.Hash(string(pass))
if err != nil {
return handler.InternalErr("hash password: %v", err)
}
page.Password = passHash
doc, err := parse.Document(defaultDocument)
if err != nil {
return handler.InternalErr("parse default document: %v", err)
}
page.Document = doc
page.Published = true
// Loop until an available address is found.
for {
page.Addr = genPageID()
err := storagePageCreate(page)
if err == storage.ErrFailedCondition {
// Try again.
continue
}
if err != nil {
return handler.InternalErr("create page: %v", err)
}
break
}
key, err := secretsKey()
if err != nil {
return handler.InternalErr("read secret key: %v", err)
}
token, err := handler.CreateRestrictedToken(key, page.Addr)
if err != nil {
return handler.InternalErr("create restricted token: %v", err)
}
err = mailerSend(
email,
"Your page is ready",
`<html>
<body>
<p>
<a href="https://targetblank.org">
<img src="https://user-images.githubusercontent.com/9319710/84103267-2c54b380-a9d8-11ea-857b-718185e9e396.png" />
</a>
</p>
<h3>
<a href="https://targetblank.org/{{.Addr}}/reset/{{.Token}}">
Claim your page.
</a>
</h3>
<p>
<sup>Link will expire in 10 minutes.</sup>
</p>
</body>
</html>`,
&struct {
Addr string
Token string
}{
Addr: page.Addr,
Token: token,
},
)
if err != nil {
return handler.InternalErr("send email: %v", err)
}
res.Body = page.Addr
res.ContentType("text/plain")
return nil
}
func main() {
lambda.Start(handler.New(Create))
}