-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
119 lines (97 loc) · 2.98 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
package main
import (
"embed"
"encoding/base64"
"encoding/json"
"fmt"
"net/url"
"os"
"strings"
"text/template"
"time"
"github.com/canhlinh/svg2png"
"github.com/patrickmn/go-cache"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
)
//go:embed index.html svg.tmpl
var content embed.FS
var chrome *svg2png.Chrome
func main() {
chrome = svg2png.NewChrome().SetHeight(600).SetWith(1200).SetTimeout(10 * time.Second)
ch := cache.New(24*time.Hour, 48*time.Hour)
app := fiber.New()
app.Use(compress.New())
app.Use(cors.New())
app.Use(logger.New())
app.Get("/", func(c *fiber.Ctx) error {
c.Type("html", "UTF8")
body, _ := content.ReadFile("index.html")
return c.Send(body)
})
app.Get("/opengraph", func(c *fiber.Ctx) error {
vars := map[string]string{
"siteTitle": ensureDecoded(c.Query("siteTitle", "")),
"title": ensureDecoded(c.Query("title", "")),
"tags": ensureDecoded(c.Query("tags", "")),
"image": ensureDecoded(c.Query("image", "")),
"twitter": stripAt(ensureDecoded(c.Query("twitter", ""))),
"bluesky": stripAt(ensureDecoded(c.Query("bluesky", ""))),
"fediverse": stripAt(ensureDecoded(c.Query("fediverse", ""))),
"github": stripAt(ensureDecoded(c.Query("github", ""))),
"website": ensureDecoded(c.Query("website", "")),
"bgColor": ensureDecoded(c.Query("bgColor", c.Query("bgColour", "#fff"))),
"fgColor": ensureDecoded(c.Query("fgColor", c.Query("fgColour", "#2B414D"))),
}
key := generateKey(vars)
png, found := ch.Get(key)
if !found || len(png.([]byte)) == 0 {
var err error
png, err = generateImage(vars)
if err != nil {
fmt.Println(err)
return c.SendStatus(500)
}
ch.Set(key, png, cache.DefaultExpiration)
}
c.Type("png")
return c.Send(png.([]byte))
})
app.Listen(":3000")
}
func generateKey(vars map[string]string) string {
varsByte, _ := json.Marshal(vars)
return base64.StdEncoding.EncodeToString(varsByte)
}
func generateImage(vars map[string]string) ([]byte, error) {
file, err := os.CreateTemp(os.TempDir(), "img-*.html")
if err != nil {
return nil, err
}
defer os.Remove(file.Name())
t := template.Must(template.New("svg.tmpl").Funcs(template.FuncMap{
"split": func(input string) []string {
return strings.Split(input, ",")
},
}).ParseFS(content, "svg.tmpl"))
t.Execute(file, vars)
imageFile, err := os.CreateTemp(os.TempDir(), "img-*.png")
if err := chrome.Screenshoot(fmt.Sprintf("file://%s", file.Name()), imageFile.Name()); err != nil {
return nil, err
}
defer os.Remove(imageFile.Name())
return os.ReadFile(imageFile.Name())
}
// Some sites (LinkedIn) encode the already encoded URL so we need to double-decode to be sure
func ensureDecoded(str string) string {
decoded, err := url.QueryUnescape(str)
if err != nil {
return str
}
return decoded
}
func stripAt(str string) string {
return strings.TrimPrefix(str, "@")
}