You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
2.8 KiB
113 lines
2.8 KiB
package main |
|
|
|
import ( |
|
"embed" |
|
"encoding/base64" |
|
"encoding/json" |
|
"fmt" |
|
"os" |
|
"strings" |
|
"text/template" |
|
"time" |
|
"net/url" |
|
|
|
"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": ensureDecoded(c.Query("twitter", "")), |
|
"github": 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 |
|
} |