-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpost.go
133 lines (117 loc) · 3.29 KB
/
post.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
package main
import (
"errors"
"net/http"
"git.tcp.direct/kayos/common/entropy"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
"git.tcp.direct/tcp.direct/tcp.ac/config"
)
func mustJson(v any) []byte {
js, err := json.Marshal(v)
if err != nil {
panic(err)
}
return js
}
var (
message500 = mustJson(map[string]string{"error": "internal server error"})
message400 = mustJson(map[string]string{"error": "bad request"})
message404 = mustJson(map[string]string{"error": "file not found"})
messageAdmin404 = mustJson(map[string]string{"error": "post id does not exist"})
)
type validator interface {
checkURL(c *gin.Context) error
checkContent(c *gin.Context, data []byte) error
checkAndScrubPost(c any) ([]byte, error)
getContentType(c *gin.Context) (string, error)
finalize(data []byte) ([]byte, error)
}
func post(c any, vas validator, t EntryType, priv bool) error {
scrubbed, err := vas.checkAndScrubPost(c)
if err != nil {
switch c.(type) {
case *gin.Context:
return errThrow(c.(*gin.Context), http.StatusBadRequest, err, message400)
default:
return err
}
}
p := newPost(t, scrubbed, priv)
var exists bool
// the keyspace (stored in memory) for db.With("hsh") are hashes
// making it quick to find duplicates. the value is the uid
if db.With("hsh").Has(p.Sum()) {
p, err, exists = getOldRef(p)
if err != nil {
switch c.(type) {
case *gin.Context:
return errThrow(c.(*gin.Context), http.StatusInternalServerError, err, message500)
default:
return err
}
}
}
if exists {
p.NewPostResponse(c)
return nil
}
p = instantiateWithIDs(p)
if p == nil {
switch c.(type) {
case *gin.Context:
return errThrow(c.(*gin.Context), 500, err, message500)
default:
return errors.New("upload failed")
}
}
err = savePost(p)
if err != nil {
switch c.(type) {
case *gin.Context:
return errThrow(c.(*gin.Context), http.StatusInternalServerError, err, message500)
default:
return err
}
}
// good to go, send them to the finisher function
p.Log().Trace().Msg("saved to database successfully, sending to NewPostResponse")
p.NewPostResponse(c)
return nil
}
func savePost(p *Post) error {
// insert actual file to database
p.Log().Trace().Msg("saving file to database")
err := db.With(p.TypeCode(true)).Put([]byte(p.UID()), p.Bytes())
if err != nil {
return err
}
return db.
With("key").
Put(
[]byte(p.DelKey()),
[]byte(p.TypeCode(false)+"."+p.UID()),
)
}
func instantiateWithIDs(p *Post) *Post {
slog := log.With().Str("caller", "instantiateWithIDs").Logger()
// generate new uid and delete key
p.uid = entropy.RandStrWithUpper(config.UIDSize)
p.key = entropy.RandStrWithUpper(config.DeleteKeySize)
// lets make sure that we don't clash even though its highly unlikely
for db.With(p.TypeCode(true)).Has([]byte(p.UID())) {
slog.Warn().Msg(" uid already exists! generating new...")
p.uid = entropy.RandStrWithUpper(config.UIDSize)
}
for db.With("key").Has([]byte(p.DelKey())) {
slog.Warn().Msg(" delete key already exists! generating new...")
p.key = entropy.RandStrWithUpper(config.DeleteKeySize)
}
// save checksum to db to prevent dupes in the future
err := db.With("hsh").Put(p.Sum(), []byte(p.UID()))
if err != nil {
slog.Error().Err(err).Msg("failed to save checksum to db")
return nil
}
return p
}