Compare commits

..

No commits in common. "f2616c041b80facb159432d2c8c7e0f004676d55" and "d272fa90b45762df9ab89467d90d3e59a75e3672" have entirely different histories.

22 changed files with 99 additions and 777 deletions

View file

@ -50,8 +50,6 @@ type ConfigAdmin struct {
// The password has to be supplied in the `password` GET form value for all requests
// to /profiling/*
ProfilingPassword string `toml:"profiling_password"`
// Allow registration on the server
// If disabled, user must be manually created (currently via the debug server)
AllowRegistration bool `toml:"allow_registration"`
}
@ -105,22 +103,6 @@ type ConfigSelf struct {
ServerDisplayName string `toml:"server_display_name"`
}
// Contains experimental features that could be good to have
// but are either in an unstable implementation state
// or maybe not widely supported by other implementations
//
// All features controlled by this config section
// **MUST BE**
// disabled by default
type ConfigExperimental struct {
// Use ED25519 key pairs instead of RSA 2048
// ED25519 keys are shorter and safer, but might not be supported by other
// implementations
// Both are created and stored for each local user. If this flag is enabled,
// Linstrom shares the ED25519 key on request, otherwise the RSA key
UseEd25519Keys bool `toml:"use_ed25519_keys"`
}
type Config struct {
General ConfigGeneral `toml:"general"`
SSL ConfigSSL `toml:"ssl"`
@ -130,7 +112,6 @@ type Config struct {
Mail ConfigMail `toml:"mail"`
Self ConfigSelf `toml:"self"`
S3 ConfigS3 `toml:"s3"`
Experimental ConfigExperimental `toml:"experimental"`
}
var GlobalConfig Config
@ -197,9 +178,6 @@ var defaultConfig Config = Config{
Endpoint: "http://localhost:3900",
UseSSL: false,
},
Experimental: ConfigExperimental{
UseEd25519Keys: false,
},
}
func (gc *ConfigGeneral) GetFullDomain() string {

View file

@ -44,6 +44,3 @@
endpoint = "http://localhost:3900"
use_ssl = false
bucket_name = "linstrom-bucket"
[experimental]
use_ed25519_keys = false

6
go.mod
View file

@ -5,7 +5,7 @@ go 1.23.0
toolchain go1.23.7
require (
git.mstar.dev/mstar/goutils v1.12.1
git.mstar.dev/mstar/goutils v1.12.0
github.com/BurntSushi/toml v1.4.0
github.com/dgraph-io/ristretto v0.2.0
github.com/eko/gocache/lib/v4 v4.1.6
@ -24,7 +24,6 @@ require (
github.com/rs/zerolog v1.33.0
golang.org/x/crypto v0.36.0
golang.org/x/image v0.20.0
golang.org/x/sys v0.32.0
gorm.io/driver/postgres v1.5.7
gorm.io/gen v0.3.26
gorm.io/gorm v1.25.12
@ -33,7 +32,6 @@ require (
require (
filippo.io/edwards25519 v1.1.0 // indirect
git.mstar.dev/mstar/canvas v0.13.1 // indirect
git.mstar.dev/mstar/goap v1.2.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
@ -49,7 +47,6 @@ require (
github.com/go-webauthn/x v0.1.14 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-tpm v0.9.1 // indirect
@ -86,6 +83,7 @@ require (
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.33.0 // indirect

7
go.sum
View file

@ -33,8 +33,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.mstar.dev/mstar/canvas v0.13.1 h1:+oJRv3O+1vDOqMQFXfV6r+o2JiZGBARadlWXOrK6WUo=
git.mstar.dev/mstar/canvas v0.13.1/go.mod h1:CzLWCvOvHXsLbwU9l8WBL/RU5VAorgJ9+Ald5yhWoMs=
git.mstar.dev/mstar/goap v0.0.0-20250407153813-45fa095a1597 h1:KUdA5J1ArvD7X4z8ttE41xaCo+Hv/vWB8scoT+HIpOE=
git.mstar.dev/mstar/goap v0.0.0-20250407153813-45fa095a1597/go.mod h1:cVCXcMGdYk8pySulIYSqMuvGG6lYEkibM5pPy97gylQ=
git.mstar.dev/mstar/goap v1.2.0 h1:jiWgU7zria+uMEq40lyvrNqofmw4Voe+sRboxxcnbZE=
@ -59,8 +57,6 @@ git.mstar.dev/mstar/goutils v1.11.1 h1:G21MjZzQDnpC7h+ZkfITbqX+jBQtqZ4FB7rj4K6id
git.mstar.dev/mstar/goutils v1.11.1/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
git.mstar.dev/mstar/goutils v1.12.0 h1:d88hLS8KnLUCI+8aWBR6228M43hxHdJpj8WuSqm4LAM=
git.mstar.dev/mstar/goutils v1.12.0/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
git.mstar.dev/mstar/goutils v1.12.1 h1:HZKKzMNfx7JKSUi5s8SwwUFEqEX6xvkM6NMf+Pht+lo=
git.mstar.dev/mstar/goutils v1.12.1/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@ -154,8 +150,6 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -398,7 +392,6 @@ golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

View file

@ -160,6 +160,6 @@ func newServer() {
}()
}
log.Info().Msg("Starting public server")
public := webpublic.New(":8080", &defaultDuck)
public := webpublic.New(":8080")
public.Start()
}

View file

@ -1,100 +0,0 @@
package identicon
import (
"git.mstar.dev/mstar/canvas"
mathutils "git.mstar.dev/mstar/goutils/math"
)
type vec2 struct {
X, Y float64
}
type block struct {
canvas *canvas.Canvas
blockType int
primary string
secondary string
hash int
pos vec2
cellSize int
margin int
scale float64
}
func (b *block) Offset() {
b.canvas.Save()
b.canvas.Translate(0.6*b.scale, -0.6*b.scale)
}
func (b *block) ResetOffset() {
b.canvas.Restore()
}
func (b *block) MakePath(hash int, offset int) {
mod := mathutils.Abs(hash+offset) % 4
switch mod {
case 0:
// Top
b.canvas.BeginPath()
b.canvas.MoveTo(float64(b.pos.X), float64(b.pos.Y))
b.canvas.LineTo(b.pos.X+float64(int(b.cellSize)), float64(b.pos.Y))
b.canvas.LineTo(b.pos.X+float64(int(b.cellSize)), b.pos.Y+float64(int(b.cellSize)))
b.canvas.ClosePath()
case 1:
// Right
b.canvas.BeginPath()
b.canvas.MoveTo(b.pos.X+float64(int(b.cellSize)), float64(b.pos.Y))
b.canvas.LineTo(b.pos.X+float64(int(b.cellSize)), b.pos.Y+float64(int(b.cellSize)))
b.canvas.LineTo(float64(b.pos.X), b.pos.Y+float64(int(b.cellSize)))
b.canvas.ClosePath()
case 2:
// Bottom
b.canvas.BeginPath()
b.canvas.MoveTo(float64(b.pos.X), float64(b.pos.Y))
b.canvas.LineTo(float64(b.pos.X), b.pos.Y+float64(int(b.cellSize)))
b.canvas.LineTo(b.pos.X+float64(int(b.cellSize)), b.pos.Y+float64(int(b.cellSize)))
b.canvas.ClosePath()
case 3:
// Left
b.canvas.BeginPath()
b.canvas.MoveTo(float64(b.pos.X), float64(b.pos.Y))
b.canvas.LineTo(b.pos.X+float64(int(b.cellSize)), float64(b.pos.Y))
b.canvas.LineTo(float64(b.pos.X), b.pos.Y+float64(int(b.cellSize)))
b.canvas.ClosePath()
default:
// Top
b.canvas.BeginPath()
b.canvas.MoveTo(float64(b.pos.X), float64(b.pos.Y))
b.canvas.LineTo(b.pos.X+float64(int(b.cellSize)), float64(b.pos.Y))
b.canvas.LineTo(b.pos.X+float64(int(b.cellSize)), b.pos.Y+float64(int(b.cellSize)))
b.canvas.ClosePath()
}
}
func (b *block) Draw() {
b.Offset()
switch b.blockType {
case 1:
b.MakePath(b.hash, b.hash%3)
b.canvas.SetFillStyle(b.primary)
b.canvas.Fill()
b.MakePath(b.hash, b.hash%5)
b.canvas.SetFillStyle(b.secondary)
b.canvas.Fill()
case 2:
b.MakePath(b.hash, b.hash%4)
b.canvas.SetFillStyle(b.secondary)
b.canvas.Fill()
b.MakePath(b.hash, b.hash%3)
b.canvas.SetFillStyle(b.primary)
b.canvas.Fill()
default:
b.MakePath(b.hash, b.hash%7)
b.canvas.SetFillStyle(b.secondary)
b.canvas.Fill()
b.MakePath(b.hash, b.hash%8)
b.canvas.SetFillStyle(b.primary)
b.canvas.Fill()
}
b.ResetOffset()
}

View file

@ -1,218 +0,0 @@
package identicon
import (
"errors"
"fmt"
"image"
"math"
"strings"
"git.mstar.dev/mstar/canvas"
"git.mstar.dev/mstar/canvas/backend/softwarebackend"
mathutils "git.mstar.dev/mstar/goutils/math"
"git.mstar.dev/mstar/goutils/sliceutils"
)
type identicon struct {
canvas *canvas.Canvas
primary string
secondary string
margin int
scale float64
cellSize int
hash int
blocks []block
shape shape
hasStroke bool
strokeWeight int
strokeColor string
compositeOperation string
palette []string
}
const canvasSize = 500
var (
palette = []string{
"#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3",
"#03A9F4", "#00BCD4", "#009688", "#4CAF50", "#8BC34A", "#CDDC39",
"#FFEB3B", "#FFC107", "#FF9800", "#FF5722", "#795548", "#607D8B",
}
validOperations = []string{
"source-over", "source-in", "source-out", "source-atop", "destination-over",
"destination-in", "destination-out", "destination-atop", "lighter", "copy",
"xor", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge",
"color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue",
"saturation", "color", "luminosity",
}
)
// Deprecated: Doesn't work
func GenerateIdenticon(text string) *image.RGBA {
backend := softwarebackend.New(canvasSize, canvasSize)
canvas := canvas.New(backend)
i := identicon{
canvas: canvas,
hash: hash(text),
}
i.Init()
i.Draw()
tmp := *backend.Image
return &tmp
}
func hash(text string) int {
runes := []rune(text)
return sliceutils.Compact(
sliceutils.Map(runes, func(t rune) int { return int(t) }),
func(acc, next int) int {
acc = ((acc << 5) - acc) + next
return acc & acc
},
)
}
func (i *identicon) SetUsername(username string) {
i.hash = hash(username)
}
func (i *identicon) SetPalette(pal []string) error {
if len(pal) < 2 {
return errors.New("palette to small")
}
i.palette = pal
return nil
}
func (i *identicon) SetCompositeOperation(newOp string) error {
newOp = strings.ToLower(newOp)
if !sliceutils.Contains(validOperations, newOp) {
return errors.New("invalid operation")
}
i.compositeOperation = newOp
return nil
}
func (i *identicon) Draw() {
i.Init()
i.canvas.Save()
i.canvas.Translate(float64(canvasSize/2), float64(canvasSize/2))
i.canvas.Rotate(-math.Pi / 4)
i.canvas.Translate(float64(canvasSize/2), float64(canvasSize/2))
i.GenerateBlocks()
i.DrawBlocks()
if i.hasStroke {
i.DrawOutline()
}
i.shape = shape{
canvas: i.canvas,
hash: i.hash,
primary: i.primary,
secondary: i.secondary,
pos: vec2{
X: float64(i.margin*int(i.scale)) + 1.5*float64(i.cellSize),
Y: float64(i.margin)*i.scale + 0.5*float64(i.cellSize),
},
scale: i.scale,
cellSize: i.cellSize,
strokeColor: i.strokeColor,
}
i.shape.Draw(i.hasStroke, i.strokeWeight)
i.canvas.Restore()
}
func (i *identicon) Init() {
i.blocks = []block{}
i.shape = shape{}
i.primary = palette[mathutils.Abs(i.hash%len(palette))]
i.secondary = palette[mathutils.Abs(hash(fmt.Sprint(i.hash))%len(palette))]
// i.canvas.SetGlobalCompositeOperation("source-over")
i.canvas.ClearRect(0, 0, canvasSize, canvasSize)
// i.canvas.SetGlobalCompositeOperation(i.compositeOperation)
}
func (i *identicon) Offset() {
i.canvas.Save()
i.canvas.Translate(0.6*i.scale, -0.6*i.scale)
}
func (i *identicon) ResetOffset() {
i.canvas.Restore()
}
func (i *identicon) GenerateBlocks() {
i.blocks = append(i.blocks, block{
canvas: i.canvas,
blockType: 1,
primary: i.primary,
secondary: i.secondary,
hash: i.hash,
pos: vec2{float64(i.margin) * i.scale, float64(i.margin) * i.scale},
cellSize: i.cellSize,
margin: i.margin,
scale: i.scale,
}, block{
canvas: i.canvas,
blockType: 2,
primary: i.primary,
secondary: i.primary,
hash: i.hash,
pos: vec2{float64(i.margin) * i.scale, float64(i.canvas.Height()) / 2},
cellSize: i.cellSize,
margin: i.margin,
scale: i.scale,
}, block{
canvas: i.canvas,
blockType: 3,
primary: i.primary,
secondary: i.secondary,
hash: i.hash,
pos: vec2{float64(i.canvas.Width()) / 2, float64(i.canvas.Height()) / 2},
cellSize: i.cellSize,
margin: i.margin,
scale: i.scale,
},
)
}
func (i *identicon) DrawOutline() {
i.Offset()
// i.canvas.SetGlobalCompositeOperation("source-over")
i.canvas.BeginPath()
i.canvas.MoveTo(float64(i.margin)*i.scale, float64(i.margin)*i.scale)
i.canvas.LineTo(float64(i.margin)*i.scale, float64(canvasSize)-float64(i.margin)*i.scale)
i.canvas.LineTo(
float64(i.canvas.Width())-float64(i.margin)*i.scale,
float64(i.canvas.Height())-float64(i.margin)*i.scale,
)
i.canvas.LineTo(
float64(i.canvas.Width())-float64(i.margin)*i.scale,
float64(i.canvas.Height())/2,
)
i.canvas.LineTo(float64(i.canvas.Width())/2, float64(i.canvas.Height())/2)
i.canvas.LineTo(float64(i.canvas.Width())/2, float64(i.margin)*i.scale)
i.canvas.ClosePath()
i.canvas.SetStrokeStyle(i.strokeColor)
i.canvas.SetLineWidth(i.scale * (float64(i.strokeWeight) / float64(i.canvas.Width())))
i.canvas.SetLineJoin(canvas.Round)
i.canvas.SetLineCap(canvas.Round)
i.canvas.Stroke()
i.canvas.BeginPath()
i.canvas.MoveTo(float64(i.canvas.Width())/2, float64(i.canvas.Height())/2)
i.canvas.LineTo(float64(i.margin)*i.scale, float64(i.canvas.Height())/2)
i.canvas.MoveTo(float64(i.canvas.Width())/2, float64(i.canvas.Height())/2)
i.canvas.LineTo(
float64(i.canvas.Width())/2,
float64(i.canvas.Height())-(float64(i.margin)*i.scale),
)
i.canvas.Stroke()
i.ResetOffset()
}
func (i *identicon) DrawBlocks() {
for _, b := range i.blocks {
b.Draw()
}
}

View file

@ -1,112 +0,0 @@
package identicon
import (
"math"
"git.mstar.dev/mstar/canvas"
mathutils "git.mstar.dev/mstar/goutils/math"
)
type shape struct {
canvas *canvas.Canvas
hash int
primary string
secondary string
pos vec2
scale float64
cellSize int
strokeColor string
}
func (s *shape) GetColor() string {
if mathutils.Abs(s.hash) == 0 {
return s.primary
} else {
return s.secondary
}
}
func (s *shape) MakePath() {
mod := mathutils.Abs(s.hash+1) % 4
switch mod {
case 0:
// square
s.canvas.BeginPath()
s.canvas.MoveTo(float64(s.pos.X), float64(s.pos.Y))
s.canvas.LineTo(float64(s.pos.X+float64(s.cellSize/2)), float64(s.pos.Y))
s.canvas.LineTo(
float64(s.pos.X+float64(s.cellSize/2)),
float64(s.pos.Y-float64(s.cellSize/2)),
)
s.canvas.LineTo(float64(s.pos.X), float64(s.pos.Y-float64(s.cellSize/2)))
s.canvas.ClosePath()
case 1:
// circle
s.canvas.BeginPath()
s.canvas.Arc(
float64(s.pos.X)+float64(s.cellSize)/math.Pi-5,
float64(s.pos.Y)-float64(s.cellSize)/math.Pi+5,
float64(s.cellSize)/3,
0,
math.Pi*2,
true,
)
case 2:
// triangle
s.canvas.BeginPath()
s.canvas.MoveTo(float64(s.pos.X), float64(s.pos.Y))
s.canvas.LineTo(float64(s.pos.X)+float64(s.cellSize)*0.65, float64(s.pos.Y))
s.canvas.LineTo(float64(s.pos.X), float64(s.pos.Y)-float64(s.cellSize)*0.65)
s.canvas.ClosePath()
case 3:
// oval
s.canvas.BeginPath()
s.canvas.MoveTo(
float64(s.pos.X)-float64(s.cellSize)*0.2,
float64(s.pos.Y)+float64(s.cellSize)*0.2,
)
s.canvas.QuadraticCurveTo(
float64(s.pos.X)+float64(s.cellSize)*0.4,
float64(s.pos.Y),
float64(s.pos.X)+float64(s.cellSize)*0.5,
float64(s.pos.Y)-float64(s.cellSize)*0.5,
)
s.canvas.MoveTo(
float64(s.pos.X)+float64(s.cellSize)*0.5,
float64(s.pos.Y)-float64(s.cellSize)*0.5,
)
s.canvas.QuadraticCurveTo(
float64(s.pos.X),
float64(s.pos.Y)-float64(s.cellSize)*0.4,
float64(s.pos.X)-float64(s.cellSize)*0.2,
float64(s.pos.Y)+float64(s.cellSize)*0.2,
)
default:
// square
s.canvas.BeginPath()
s.canvas.MoveTo(float64(s.pos.X), float64(s.pos.Y))
s.canvas.LineTo(float64(s.pos.X+float64(s.cellSize/2)), float64(s.pos.Y))
s.canvas.LineTo(
float64(s.pos.X+float64(s.cellSize/2)),
float64(s.pos.Y-float64(s.cellSize/2)),
)
s.canvas.LineTo(float64(s.pos.X), float64(s.pos.Y-float64(s.cellSize/2)))
s.canvas.ClosePath()
}
}
func (s *shape) Draw(hasStroke bool, strokeWeight int) {
color := s.GetColor()
// THIS SHIT ISN'T IMPLEMENTED! I hope it will work regardless
// s.canvas.SetGlobalCompositeOperation("source-over")
s.MakePath()
s.canvas.SetFillStyle(color)
s.canvas.SetStrokeStyle(s.strokeColor)
s.canvas.SetLineWidth(s.scale * (4 / 5 * float64(strokeWeight)) / float64(canvasSize))
s.canvas.SetLineJoin(canvas.Round)
s.canvas.SetLineCap(canvas.Round)
s.canvas.Fill()
if hasStroke {
s.canvas.Stroke()
}
}

1
shared/rsaKey.go Normal file
View file

@ -0,0 +1 @@
package shared

View file

@ -1,49 +0,0 @@
package shared
import (
"crypto"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
)
func GenerateKeypair(useEd bool) (publicKey []byte, privateKey []byte, err error) {
if useEd {
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
return nil, nil, err
}
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
return publicKeyBytes, privateKey, nil
} else {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, nil, err
}
return publicKeyBytes, privateKeyBytes, nil
}
}
func Sign(toSign string, keyBytes []byte, keyIsRsa bool) ([]byte, error) {
if keyIsRsa {
key, err := x509.ParsePKCS1PrivateKey(keyBytes)
if err != nil {
return nil, err
}
hash := sha256.Sum256([]byte(toSign))
signed, err := key.Sign(rand.Reader, hash[:], crypto.SHA256)
return signed, err
} else {
key := ed25519.PrivateKey(keyBytes)
signed, err := key.Sign(rand.Reader, []byte(toSign), crypto.SHA256)
return signed, err
}
}

View file

@ -40,16 +40,14 @@ func newUser(db *gorm.DB, opts ...gen.DOOption) user {
_user.BackgroundId = field.NewField(tableName, "background_id")
_user.BannerId = field.NewField(tableName, "banner_id")
_user.Indexable = field.NewBool(tableName, "indexable")
_user.PublicKeyRsa = field.NewBytes(tableName, "public_key_rsa")
_user.PublicKeyEd = field.NewBytes(tableName, "public_key_ed")
_user.PublicKey = field.NewBytes(tableName, "public_key")
_user.RestrictedFollow = field.NewBool(tableName, "restricted_follow")
_user.Location = field.NewField(tableName, "location")
_user.Birthday = field.NewField(tableName, "birthday")
_user.Verified = field.NewBool(tableName, "verified")
_user.PasskeyId = field.NewBytes(tableName, "passkey_id")
_user.FinishedRegistration = field.NewBool(tableName, "finished_registration")
_user.PrivateKeyRsa = field.NewBytes(tableName, "private_key_rsa")
_user.PrivateKeyEd = field.NewBytes(tableName, "private_key_ed")
_user.PrivateKey = field.NewBytes(tableName, "private_key")
_user.RemoteInfo = userHasOneRemoteInfo{
db: db.Session(&gorm.Session{}),
@ -355,16 +353,14 @@ type user struct {
BackgroundId field.Field
BannerId field.Field
Indexable field.Bool
PublicKeyRsa field.Bytes
PublicKeyEd field.Bytes
PublicKey field.Bytes
RestrictedFollow field.Bool
Location field.Field
Birthday field.Field
Verified field.Bool
PasskeyId field.Bytes
FinishedRegistration field.Bool
PrivateKeyRsa field.Bytes
PrivateKeyEd field.Bytes
PrivateKey field.Bytes
RemoteInfo userHasOneRemoteInfo
InfoFields userHasManyInfoFields
@ -417,16 +413,14 @@ func (u *user) updateTableName(table string) *user {
u.BackgroundId = field.NewField(table, "background_id")
u.BannerId = field.NewField(table, "banner_id")
u.Indexable = field.NewBool(table, "indexable")
u.PublicKeyRsa = field.NewBytes(table, "public_key_rsa")
u.PublicKeyEd = field.NewBytes(table, "public_key_ed")
u.PublicKey = field.NewBytes(table, "public_key")
u.RestrictedFollow = field.NewBool(table, "restricted_follow")
u.Location = field.NewField(table, "location")
u.Birthday = field.NewField(table, "birthday")
u.Verified = field.NewBool(table, "verified")
u.PasskeyId = field.NewBytes(table, "passkey_id")
u.FinishedRegistration = field.NewBool(table, "finished_registration")
u.PrivateKeyRsa = field.NewBytes(table, "private_key_rsa")
u.PrivateKeyEd = field.NewBytes(table, "private_key_ed")
u.PrivateKey = field.NewBytes(table, "private_key")
u.fillFieldMap()
@ -443,7 +437,7 @@ func (u *user) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (u *user) fillFieldMap() {
u.fieldMap = make(map[string]field.Expr, 35)
u.fieldMap = make(map[string]field.Expr, 33)
u.fieldMap["id"] = u.ID
u.fieldMap["username"] = u.Username
u.fieldMap["created_at"] = u.CreatedAt
@ -457,16 +451,14 @@ func (u *user) fillFieldMap() {
u.fieldMap["background_id"] = u.BackgroundId
u.fieldMap["banner_id"] = u.BannerId
u.fieldMap["indexable"] = u.Indexable
u.fieldMap["public_key_rsa"] = u.PublicKeyRsa
u.fieldMap["public_key_ed"] = u.PublicKeyEd
u.fieldMap["public_key"] = u.PublicKey
u.fieldMap["restricted_follow"] = u.RestrictedFollow
u.fieldMap["location"] = u.Location
u.fieldMap["birthday"] = u.Birthday
u.fieldMap["verified"] = u.Verified
u.fieldMap["passkey_id"] = u.PasskeyId
u.fieldMap["finished_registration"] = u.FinishedRegistration
u.fieldMap["private_key_rsa"] = u.PrivateKeyRsa
u.fieldMap["private_key_ed"] = u.PrivateKeyEd
u.fieldMap["private_key"] = u.PrivateKey
}

View file

@ -49,8 +49,7 @@ type User struct {
Banner *MediaMetadata ` json:"-"`
BannerId sql.NullString ` json:"banner_id"` // ID of a media file used as banner
Indexable bool ` json:"indexable"` // Whether this account can be found by crawlers
PublicKeyRsa []byte ` json:"public_key_rsa"` // The public RSA key of the account
PublicKeyEd []byte ` json:"public_key_ed"` // The public Ed25519 key of the account
PublicKey []byte ` json:"public_key"` // The public key of the account
// Whether this account restricts following
// If true, the owner must approve of a follow request first
RestrictedFollow bool ` json:"restricted_follow"`
@ -67,8 +66,7 @@ type User struct {
// saved space is worth
PasskeyId []byte `json:"-"`
FinishedRegistration bool `json:"-"` // Whether this account has completed registration yet
PrivateKeyRsa []byte `json:"-"`
PrivateKeyEd []byte `json:"-"`
PrivateKey []byte `json:"-"`
// ---- "Remote" linked values
InfoFields []UserInfoField `json:"-"`

View file

@ -2,6 +2,8 @@ package storage
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"database/sql"
"git.mstar.dev/mstar/goutils/other"
@ -9,7 +11,6 @@ import (
"gorm.io/gorm"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/shared"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
"git.mstar.dev/mstar/linstrom/storage-new/models"
)
@ -26,7 +27,7 @@ func InsertSelf() error {
if err != nil {
return other.Error("storage", "failed to save/update self server", err)
}
user, err := insertUser(server, duck)
user, err := insertUser(server)
if err != nil {
return other.Error("storage", "failed to save/update self user", err)
}
@ -94,10 +95,7 @@ func insertServer(duck *models.MediaMetadata) (*models.RemoteServer, error) {
return &server, nil
}
func insertUser(
server *models.RemoteServer,
duckMedia *models.MediaMetadata,
) (*models.User, error) {
func insertUser(server *models.RemoteServer) (*models.User, error) {
dbUser, err := dbgen.User.GetByUsername("linstrom")
if err == nil {
return dbUser, nil
@ -105,14 +103,16 @@ func insertUser(
if err != gorm.ErrRecordNotFound {
return nil, err
}
publicEdKeyBytes, privateEdKeyBytes, err := shared.GenerateKeypair(true)
// publicKey, privateKey, err := ed25519.GenerateKey(nil)
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
publicRsaKeyBytes, privateRsaKeyBytes, err := shared.GenerateKeypair(false)
if err != nil {
if err = privateKey.Validate(); err != nil {
return nil, err
}
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
publicKeyBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
pkeyId := make([]byte, 64)
_, err = rand.Read(pkeyId)
if err != nil {
@ -129,17 +129,15 @@ func insertUser(
DisplayName: config.GlobalConfig.Self.ServerActorDisplayName,
Description: "The default linstrom server user",
IsBot: true,
Icon: duckMedia,
IconId: sql.NullString{Valid: true, String: duckMedia.ID},
Icon: nil,
IconId: sql.NullString{Valid: false},
Background: nil,
BackgroundId: sql.NullString{Valid: false},
Banner: nil,
BannerId: sql.NullString{Valid: false},
Indexable: false,
PublicKeyEd: publicEdKeyBytes,
PrivateKeyEd: privateEdKeyBytes,
PublicKeyRsa: publicRsaKeyBytes,
PrivateKeyRsa: privateRsaKeyBytes,
PublicKey: publicKeyBytes,
PrivateKey: privateKeyBytes,
Verified: true,
FinishedRegistration: true,
PasskeyId: pkeyId,

View file

@ -4,9 +4,10 @@ import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"net/http"
webutils "git.mstar.dev/mstar/goutils/http"
httputils "git.mstar.dev/mstar/goutils/http"
"git.mstar.dev/mstar/goutils/sliceutils"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
@ -26,28 +27,15 @@ func postAs(w http.ResponseWriter, r *http.Request) {
data := Inbound{}
err := dec.Decode(&data)
if err != nil {
webutils.ProblemDetails(
w,
http.StatusBadRequest,
"/errors/bad-request-data",
"bad request data",
nil,
map[string]any{
"sample": Inbound{
Username: "bob",
Content: "Heya there, this is sample data",
},
},
)
httputils.HttpErr(w, 0, "json decode failed", http.StatusBadRequest)
return
}
user, err := dbgen.User.GetByUsername(data.Username)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
httputils.HttpErr(w, 0, "no user with that name", http.StatusNotFound)
} else {
log.Error().Err(err).Str("name", data.Username).Msg("Failed to find user")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
}
return
}
@ -81,13 +69,13 @@ func notesFrom(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Error().Err(err).Str("name", username).Msg("Failed to get user")
storage.HandleReconnectError(err)
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
httputils.HttpErr(w, 0, "failed to get user", http.StatusInternalServerError)
return
}
notes, err := dbgen.Note.GetNotesPaged(user.ID, 0, uint8(models.NOTE_TARGET_PUBLIC))
if err != nil {
log.Error().Err(err).Str("name", username).Msg("Failed to get notes")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
httputils.HttpErr(w, 0, "failed to get notes", http.StatusInternalServerError)
return
}
publicNotes := sliceutils.Map(notes, func(t models.Note) webshared.Note {
@ -95,5 +83,11 @@ func notesFrom(w http.ResponseWriter, r *http.Request) {
n.FromModel(&t)
return n
})
webutils.SendJson(w, publicNotes)
jsonNotes, err := json.Marshal(publicNotes)
if err != nil {
log.Error().Err(err).Msg("Failed to marshal notes")
httputils.HttpErr(w, 0, "failed to marshal", http.StatusInternalServerError)
return
}
fmt.Fprint(w, string(jsonNotes))
}

View file

@ -2,18 +2,19 @@ package webdebug
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"database/sql"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
webutils "git.mstar.dev/mstar/goutils/http"
"git.mstar.dev/mstar/goutils/other"
httputils "git.mstar.dev/mstar/goutils/http"
"git.mstar.dev/mstar/goutils/sliceutils"
"github.com/rs/zerolog/log"
"git.mstar.dev/mstar/linstrom/shared"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
"git.mstar.dev/mstar/linstrom/storage-new/models"
webshared "git.mstar.dev/mstar/linstrom/web/shared"
@ -26,27 +27,25 @@ func getNonDeletedUsers(w http.ResponseWriter, r *http.Request) {
var err error
page, err = strconv.Atoi(pageStr)
if err != nil {
webutils.HttpErr(w, 0, "page is not a number", http.StatusBadRequest)
httputils.HttpErr(w, 0, "page is not a number", http.StatusBadRequest)
return
}
}
users, err := dbgen.User.GetPagedAllNonDeleted(uint(page))
if err != nil {
webutils.ProblemDetails(
w,
http.StatusInternalServerError,
"/errors/db-failure",
"database failure",
nil,
nil,
)
httputils.HttpErr(w, 0, "failed to get users", http.StatusInternalServerError)
return
}
webutils.SendJson(w, sliceutils.Map(users, func(t models.User) webshared.User {
marshalled, err := json.Marshal(sliceutils.Map(users, func(t models.User) webshared.User {
u := webshared.User{}
u.FromModel(&t)
return u
}))
if err != nil {
httputils.HttpErr(w, 0, "failed to marshal users", http.StatusInternalServerError)
return
}
fmt.Fprint(w, string(marshalled))
}
func createLocalUser(w http.ResponseWriter, r *http.Request) {
@ -62,43 +61,19 @@ func createLocalUser(w http.ResponseWriter, r *http.Request) {
data := Inbound{}
err := jsonDecoder.Decode(&data)
if err != nil {
webutils.ProblemDetails(
w,
http.StatusBadRequest,
"/errors/bad-request-data",
"bad request data",
nil,
map[string]any{
"sample": Inbound{
Username: "bob",
Displayname: "Bob Bobbington",
Description: "Bobbing Bobs bop to Bobs bobbing beats",
Birthday: other.IntoPointer(time.Now()),
Location: nil,
IsBot: false,
},
},
)
httputils.HttpErr(w, 0, "decode failed", http.StatusBadRequest)
return
}
publicKeyEdBytes, privateKeyEdBytes, err := shared.GenerateKeypair(true)
if err != nil {
log.Error().Err(err).Msg("Failed to generate and marshal public key")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
return
}
publicKeyRsaBytes, privateKeyRsaBytes, err := shared.GenerateKeypair(false)
if err != nil {
log.Error().Err(err).Msg("Failed to generate and marshal public key")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
return
}
// publicKey, privateKey, err := ed25519.GenerateKey(nil)
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
publicKeyBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
pkeyId := make([]byte, 64)
_, err = rand.Read(pkeyId)
if err != nil {
log.Error().Err(err).Msg("Failed to generate passkey id")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
httputils.HttpErr(w, 0, "failed to generate passkey id", http.StatusInternalServerError)
return
}
@ -109,10 +84,8 @@ func createLocalUser(w http.ResponseWriter, r *http.Request) {
u.Description,
u.IsBot,
u.ServerId,
u.PrivateKeyEd,
u.PublicKeyEd,
u.PrivateKeyRsa,
u.PublicKeyRsa,
u.PrivateKey,
u.PublicKey,
u.PasskeyId,
)
if data.Birthday != nil {
@ -127,10 +100,8 @@ func createLocalUser(w http.ResponseWriter, r *http.Request) {
Description: data.Description,
IsBot: data.IsBot,
ServerId: 1, // Hardcoded, Self is always first ID
PublicKeyRsa: publicKeyRsaBytes,
PublicKeyEd: publicKeyEdBytes,
PrivateKeyRsa: privateKeyRsaBytes,
PrivateKeyEd: privateKeyEdBytes,
PublicKey: publicKeyBytes,
PrivateKey: privateKeyBytes,
PasskeyId: pkeyId,
}
if data.Birthday != nil {
@ -141,7 +112,7 @@ func createLocalUser(w http.ResponseWriter, r *http.Request) {
}
if err = u.Create(&user); err != nil {
log.Error().Err(err).Msg("failed to create new local user")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
httputils.HttpErr(w, 0, "db failure", http.StatusInternalServerError)
}
}

View file

@ -8,7 +8,6 @@ import (
func BuildActivitypubRouter() http.Handler {
router := http.NewServeMux()
router.HandleFunc("/user/{id}", users)
router.HandleFunc("/user/{id}/inbox", userInbox)
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "in ap")
})

View file

@ -3,17 +3,13 @@ package activitypub
import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
webutils "git.mstar.dev/mstar/goutils/http"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/storage-new"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
webshared "git.mstar.dev/mstar/linstrom/web/shared"
)
var baseLdContext = []any{
@ -27,30 +23,17 @@ func users(w http.ResponseWriter, r *http.Request) {
Owner string `json:"owner"`
Pem string `json:"publicKeyPem"`
}
type OutboundMedia struct {
Type string `json:"type"`
Url string `json:"url"`
MediaType string `json:"mediaType"`
}
type Outbound struct {
Context []any `json:"@context"`
Id string `json:"id"`
Type string `json:"type"`
PreferredUsername string `json:"preferredUsername"`
Inbox string `json:"inbox"`
PublicKey OutboundKey `json:"publicKey"`
Published time.Time `json:"published"`
DisplayName string `json:"name"`
Description *string `json:"summary,omitempty"`
PublicUrl string `json:"url"`
Icon *OutboundMedia `json:"icon,omitempty"`
Banner *OutboundMedia `json:"image,omitempty"`
// FIXME: Public key stuff is borken. Focus on fixing
// PublicKey OutboundKey `json:"publicKey"`
}
log := hlog.FromRequest(r)
userId := r.PathValue("id")
user, err := dbgen.User.Where(dbgen.User.ID.Eq(userId)).
Preload(dbgen.User.Icon).Preload(dbgen.User.Banner).
First()
user, err := dbgen.User.Where(dbgen.User.ID.Eq(userId)).First()
if err != nil {
webutils.ProblemDetails(w, 500, "/errors/db-failure", "internal database failure", nil, nil)
if storage.HandleReconnectError(err) {
@ -60,47 +43,20 @@ func users(w http.ResponseWriter, r *http.Request) {
}
return
}
// fmt.Println(x509.ParsePKCS1PublicKey(user.PublicKey))
apUrl := userIdToApUrl(user.ID)
var keyBytes string
if config.GlobalConfig.Experimental.UseEd25519Keys {
keyBytes = keyBytesToPem(user.PublicKeyEd)
} else {
keyBytes = keyBytesToPem(user.PublicKeyRsa)
}
data := Outbound{
Context: baseLdContext,
Id: apUrl,
Type: "Person",
PreferredUsername: user.Username,
PreferredUsername: user.DisplayName,
Inbox: apUrl + "/inbox",
PublicKey: OutboundKey{
Id: apUrl + "#main-key",
Owner: apUrl,
Pem: keyBytes,
},
Published: user.CreatedAt,
DisplayName: user.DisplayName,
PublicUrl: config.GlobalConfig.General.GetFullPublicUrl() + "/user/" + user.Username,
}
if user.Description != "" {
data.Description = &user.Description
}
if user.Icon != nil {
log.Debug().Msg("icon found")
data.Icon = &OutboundMedia{
Type: "Image",
Url: webshared.EnsurePublicUrl(user.Icon.Location),
MediaType: user.Icon.Type,
}
}
if user.Banner != nil {
log.Debug().Msg("icon banner")
data.Banner = &OutboundMedia{
Type: "Image",
Url: webshared.EnsurePublicUrl(user.Banner.Location),
MediaType: user.Banner.Type,
}
// PublicKey: OutboundKey{
// Id: apUrl + "#main-key",
// Owner: apUrl,
// Pem: keyBytesToPem(user.PublicKey),
// },
}
encoded, err := json.Marshal(data)
@ -108,13 +64,6 @@ func users(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, string(encoded))
}
func userInbox(w http.ResponseWriter, r *http.Request) {
log := hlog.FromRequest(r)
userId := r.PathValue("id")
data, err := io.ReadAll(r.Body)
log.Info().Err(err).Str("userId", userId).Bytes("body", data).Msg("Inbox message")
}
/*
Fine. You win JsonLD. I can't get you to work properly. I'll just treat you like normal json then
Fuck you.

View file

@ -10,7 +10,6 @@ import (
var errorDescriptions = map[string]string{
"webfinger-bad-resource": "The given format for the \"resource\" url parameter was missing or invalid. It must follow the form \"acct:<username>@<domain>\"",
"db-failure": "The database query for this request failed for an undisclosed reason. This is often caused by bad input data conflicting with existing information. Try to submit different data or wait for some time",
"bad-request-data": "The data provided in the request doesn't match the requirements, see problem details' detail field for more information",
}
func errorTypeHandler(w http.ResponseWriter, r *http.Request) {

View file

@ -40,7 +40,7 @@ type Server struct {
server *http.Server
}
func New(addr string, duckImg *string) *Server {
func New(addr string) *Server {
handler := http.NewServeMux()
handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
@ -51,11 +51,6 @@ func New(addr string, duckImg *string) *Server {
handler.HandleFunc("GET /.well-known/nodeinfo", api.WellKnownNodeinfo)
handler.HandleFunc("GET /nodeinfo/2.1", api.Nodeinfo)
handler.HandleFunc("GET /errors/{name}", errorTypeHandler)
handler.HandleFunc("GET /default-image", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "image/web")
w.Header().Add("Content-Disposition", "attachment; filename=\"duck.webp\"")
fmt.Fprint(w, *duckImg)
})
server := http.Server{
Handler: webutils.ChainMiddlewares(handler, webutils.LoggingMiddleware),
Addr: addr,

View file

@ -4,7 +4,6 @@ import (
"slices"
"time"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/shared"
"git.mstar.dev/mstar/linstrom/storage-new/models"
)
@ -98,11 +97,7 @@ func (u *User) FromModel(m *models.User) {
u.BannerId = &m.IconId.String
}
u.Indexable = m.Indexable
if config.GlobalConfig.Experimental.UseEd25519Keys {
u.PublicKey = append(u.PublicKey, m.PublicKeyEd...)
} else {
u.PublicKey = append(u.PublicKey, m.PublicKeyRsa...)
}
u.PublicKey = append(u.PublicKey, m.PublicKey...)
u.RestrictedFollow = m.RestrictedFollow
if m.Location.Valid {
u.Location = &m.Location.String

View file

@ -1,16 +0,0 @@
package webshared
import "strings"
// TODO: Define linstrom uri type
var hardcodedUrls = map[string]string{
"default-media": "/default-image",
}
func EnsurePublicUrl(rawUrl string) string {
if !strings.HasPrefix(rawUrl, "linstrom://") {
return rawUrl
}
return strings.Replace(rawUrl, "linstrom://", "/", 1)
}

View file

@ -1,40 +0,0 @@
package webshared
import (
"strings"
"git.mstar.dev/mstar/linstrom/shared"
)
func CreateSignatureRSA(
method string,
target string,
headers map[string]string,
privateKeyBytes []byte,
) (string, error) {
message := genPreSignatureString(method, target, headers)
signed, err := shared.Sign(message, privateKeyBytes, true)
return string(signed), err
}
func CreateSignatureED(
method string,
target string,
headers map[string]string,
privateKeyBytes []byte,
) (string, error) {
message := genPreSignatureString(method, target, headers)
signed, err := shared.Sign(message, privateKeyBytes, false)
return string(signed), err
}
func genPreSignatureString(method, target string, headers map[string]string) string {
dataBuilder := strings.Builder{}
dataBuilder.WriteString("(request-target) ")
dataBuilder.WriteString(strings.ToLower(method) + " ")
dataBuilder.WriteString(target + "\n")
for k, v := range headers {
dataBuilder.WriteString(k + ": " + v + "\n")
}
return dataBuilder.String()
}