2022-06-03 05:01:54 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"embed"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2022-06-05 18:29:44 +00:00
|
|
|
"html"
|
2022-06-03 05:01:54 +00:00
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/go-chi/chi"
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:embed dist/*
|
|
|
|
var fs embed.FS
|
|
|
|
|
|
|
|
type entity struct {
|
|
|
|
AvatarURL *string `json:"avatar_url"`
|
|
|
|
IconURL *string `json:"icon_url"`
|
|
|
|
Description *string `json:"description"`
|
|
|
|
Color *string `json:"color"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var baseURL = "https://api.pluralkit.me/v2"
|
|
|
|
|
|
|
|
var version = "dev"
|
2022-06-05 17:07:56 +00:00
|
|
|
var versionJS string
|
2022-06-03 05:01:54 +00:00
|
|
|
|
2022-06-05 15:33:09 +00:00
|
|
|
const defaultEmbed = `<meta property="og:title" content="PluralKit | web dashboard" /> <meta name="theme-color" content="#da9317">`
|
|
|
|
|
2022-06-03 05:01:54 +00:00
|
|
|
func main() {
|
2022-06-05 17:07:56 +00:00
|
|
|
versionJS = "<script>window.pluralkitVersion = '" + version + "'</script>"
|
|
|
|
|
2022-06-03 05:01:54 +00:00
|
|
|
r := chi.NewRouter()
|
|
|
|
|
|
|
|
r.Use(func(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
rw.Header().Set("X-PluralKit-Version", version)
|
|
|
|
next.ServeHTTP(rw, r)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
r.NotFound(notFoundHandler)
|
|
|
|
|
|
|
|
r.Get("/profile/{type}/{id}", func(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
defer func() {
|
|
|
|
if a := recover(); a != nil {
|
|
|
|
notFoundHandler(rw, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
createEmbed(rw, r)
|
|
|
|
})
|
|
|
|
|
|
|
|
http.ListenAndServe(":8080", r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func notFoundHandler(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
var data []byte
|
|
|
|
var err error
|
|
|
|
|
|
|
|
// lol
|
|
|
|
if strings.HasSuffix(r.URL.Path, ".js") {
|
|
|
|
data, err = fs.ReadFile("dist" + r.URL.Path)
|
|
|
|
rw.Header().Add("content-type", "application/javascript")
|
|
|
|
} else if strings.HasSuffix(r.URL.Path, ".css") {
|
|
|
|
data, err = fs.ReadFile("dist" + r.URL.Path)
|
|
|
|
rw.Header().Add("content-type", "text/css")
|
|
|
|
} else if strings.HasSuffix(r.URL.Path, ".map") {
|
|
|
|
data, err = fs.ReadFile("dist" + r.URL.Path)
|
|
|
|
} else {
|
|
|
|
data, err = fs.ReadFile("dist/index.html")
|
|
|
|
rw.Header().Add("content-type", "text/html")
|
2022-06-05 17:07:56 +00:00
|
|
|
data = []byte(strings.Replace(string(data), `<!-- extra data -->`, defaultEmbed+versionJS, 1))
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rw.Write(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// explanation for createEmbed:
|
|
|
|
// we don't care about errors, we just want to return a HTML page as soon as possible
|
2022-09-14 20:56:52 +00:00
|
|
|
// `panic(1)` is caught by upstream, which then returns the raw HTML page
|
2022-06-03 05:01:54 +00:00
|
|
|
|
|
|
|
func createEmbed(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
entityType := chi.URLParam(r, "type")
|
|
|
|
id := chi.URLParam(r, "id")
|
|
|
|
|
|
|
|
var path string
|
|
|
|
|
|
|
|
switch entityType {
|
|
|
|
case "s":
|
|
|
|
path = "/systems/" + id
|
|
|
|
case "m":
|
|
|
|
path = "/members/" + id
|
|
|
|
case "g":
|
|
|
|
path = "/groups/" + id
|
|
|
|
default:
|
2022-09-14 20:56:52 +00:00
|
|
|
panic(1)
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res, err := http.Get(baseURL + path)
|
|
|
|
if err != nil {
|
2022-09-14 20:56:52 +00:00
|
|
|
panic(1)
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
if res.StatusCode != 200 {
|
2022-09-14 20:56:52 +00:00
|
|
|
panic(1)
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var data entity
|
|
|
|
body, _ := io.ReadAll(res.Body)
|
|
|
|
err = json.Unmarshal(body, &data)
|
|
|
|
if err != nil {
|
2022-09-14 20:56:52 +00:00
|
|
|
panic(1)
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
text := fmt.Sprintf(`<link type="application/json+oembed" href="%s/%s/oembed.json" />%s`, baseURL, path, "\n")
|
|
|
|
|
|
|
|
if data.AvatarURL != nil {
|
2022-06-05 18:29:44 +00:00
|
|
|
text += fmt.Sprintf(`<meta content='%s' property='og:image'>%s`, html.EscapeString(*data.AvatarURL), "\n")
|
2022-06-03 05:01:54 +00:00
|
|
|
} else if data.IconURL != nil {
|
2022-06-05 18:29:44 +00:00
|
|
|
text += fmt.Sprintf(`<meta content='%s' property='og:image'>%s`, html.EscapeString(*data.IconURL), "\n")
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if data.Description != nil {
|
2022-06-05 18:29:44 +00:00
|
|
|
text += fmt.Sprintf(`<meta content="%s" property="og:description">%s`, html.EscapeString(*data.Description), "\n")
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if data.Color != nil {
|
2022-06-05 18:29:44 +00:00
|
|
|
text += fmt.Sprintf(`<meta name="theme-color" content="#%s">%s`, html.EscapeString(*data.Color), "\n")
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
html, err := fs.ReadFile("dist/index.html")
|
|
|
|
if err != nil {
|
2022-09-14 20:56:52 +00:00
|
|
|
panic(1)
|
2022-06-03 05:01:54 +00:00
|
|
|
}
|
2022-06-05 17:07:56 +00:00
|
|
|
html = []byte(strings.Replace(string(html), `<!-- extra data -->`, text+versionJS, 1))
|
2022-06-03 05:01:54 +00:00
|
|
|
|
|
|
|
rw.Header().Add("content-type", "text/html")
|
|
|
|
rw.Write(html)
|
|
|
|
}
|