1
0
mirror of https://codeberg.org/pluja/kycnot.me synced 2025-03-30 12:57:58 -04:00
2024-02-14 22:13:10 +01:00

177 lines
4.5 KiB
Go

package server
import (
"bytes"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"io"
"net/http"
"strings"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"github.com/kataras/iris/v12"
"github.com/rs/zerolog/log"
"golang.org/x/image/font"
"golang.org/x/image/font/gofont/gomonobold"
"pluja.dev/kycnot.me/database"
)
func (s *Server) handleVerifyPow(c iris.Context) {
// Get id, nonce and proof from the request
id := c.Params().Get("id")
nonce := c.Params().Get("nonce")
// Verify the proof of work
valid := s.PowChallenger.PowVerifyProof(id, nonce)
// Return the result
c.JSON(iris.Map{
"valid": valid,
"status": iris.StatusOK,
})
}
// Proxies the request to the logo URL of a service by its ID
func (s *Server) handleApiPicture(c iris.Context) {
id := c.Params().Get("id")
service, err := database.Pb.GetServiceById(id)
if err != nil {
log.Error().Err(err).Msg("GetPicture: Could not get service")
respondWithPlaceholder(c, "?")
return
}
if service.LogoURL == "" {
respondWithPlaceholder(c, service.Name)
return
}
if imageData, err := s.Cache.Get(fmt.Sprintf("img-%s", service.ID)); err == nil {
ctt, _ := s.Cache.Get(fmt.Sprintf("ctt-%s", service.ID))
c.ContentType(string(ctt))
c.StatusCode(iris.StatusOK)
c.Write(imageData)
return
}
client := &http.Client{}
req, err := http.NewRequest("GET", service.LogoURL, nil)
if err != nil {
log.Error().Err(err).Msg("GetPicture: Failed to create HTTP request")
return
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.3")
req.Header.Set("Referer", service.Urls[0])
resp, err := client.Do(req)
if err != nil || resp.StatusCode != http.StatusOK {
log.Error().Err(err).Msgf("GetPicture: Could not get image for %s", service.Name)
respondWithPlaceholder(c, service.Name)
return
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Error().Err(err).Msgf("GetPicture: Could not read response body for %s", service.Name)
respondWithPlaceholder(c, service.Name)
return
}
if bodyBytes == nil {
log.Error().Msg("GetPicture: Could not read response body")
respondWithPlaceholder(c, service.Name)
return
}
s.Cache.Set(fmt.Sprintf("img-%s", service.ID), bodyBytes)
s.Cache.Set(fmt.Sprintf("ctt-%s", service.ID), []byte(resp.Header.Get("Content-Type")))
c.ContentType(resp.Header.Get("Content-Type"))
c.StatusCode(iris.StatusOK)
c.Write(bodyBytes)
}
func respondWithPlaceholder(c iris.Context, serviceName string) {
log.Debug().Msgf("Generating placeholder for %s", serviceName)
const width, height = 250, 250
const margin = 15
firstLetter := strings.ToUpper(string(serviceName[0]))
// Create an image
img := image.NewRGBA(image.Rect(0, 0, width, height))
// Set background to black
draw.Draw(img, img.Bounds(), image.NewUniform(color.Black), image.Point{}, draw.Src)
// Load the monospace font
f, err := truetype.Parse(gomonobold.TTF)
if err != nil {
log.Error().Err(err).Msg("could not parse font")
return
}
// Initialize freetype context
ctx := freetype.NewContext()
ctx.SetDPI(72)
ctx.SetFont(f)
// Parse hex color and set text color
var r, g, b uint8
hexColor := "#84cc16"
_, err = fmt.Sscanf(hexColor, "#%02x%02x%02x", &r, &g, &b)
if err != nil {
log.Error().Err(err).Msg("Invalid hex color")
return
}
textColor := color.RGBA{R: r, G: g, B: b, A: 255}
ctx.SetSrc(image.NewUniform(textColor))
ctx.SetClip(img.Bounds())
ctx.SetDst(img)
// Dynamically calculate the maximum font size
var fontSize float64
var textWidth, textHeight int
for fontSize = 205; fontSize > 10; fontSize -= 0.5 {
ctx.SetFontSize(fontSize)
opts := truetype.Options{Size: fontSize}
face := truetype.NewFace(f, &opts)
textWidth = font.MeasureString(face, firstLetter).Round()
ascent, descent := face.Metrics().Ascent, face.Metrics().Descent
textHeight = (ascent + descent).Ceil()
if textWidth <= width-margin*2 && textHeight <= height-margin*2 {
break
}
}
// Calculate position for the text
x := (width - textWidth) / 2
y := (height-textHeight)/2 + textHeight - int(ctx.PointToFixed(fontSize*0.2)>>6)
pt := freetype.Pt(x, y)
// Draw the text
if _, err := ctx.DrawString(firstLetter, pt); err != nil {
log.Error().Err(err).Msg("Failed to draw string")
return
}
// Encode to PNG
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
log.Error().Err(err).Msg("Failed to encode image")
return
}
c.ContentType("image/png")
c.StatusCode(iris.StatusOK)
c.Write(buf.Bytes())
}