Start work on lua based plugin system
Some checks failed
/ docker (push) Has been cancelled

This commit is contained in:
Melody Becker 2025-06-02 17:40:53 +02:00
parent bf0aaaca8f
commit 94106bb82f
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
8 changed files with 430 additions and 1 deletions

17
go.mod
View file

@ -5,16 +5,24 @@ go 1.24.2
require (
git.mstar.dev/mstar/goutils v1.14.2
github.com/BurntSushi/toml v1.5.0
github.com/PeerDB-io/gluabit32 v1.0.2
github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9
github.com/cosmotek/loguago v1.0.0
github.com/go-acme/lego/v4 v4.23.1
github.com/go-webauthn/webauthn v0.12.3
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.7.4
github.com/kohkimakimoto/gluatemplate v0.0.0-20160815033744-d9e2c9d6b00f
github.com/layeh/gopher-json v0.0.0-20201124131017-552bb3c4c3bf
github.com/mitchellh/mapstructure v1.5.0
github.com/nrednav/cuid2 v1.0.1
github.com/pquerna/otp v1.4.0
github.com/quic-go/quic-go v0.52.0
github.com/rs/xid v1.6.0
github.com/rs/zerolog v1.34.0
github.com/yaronf/httpsign v0.3.2
github.com/yuin/gluare v0.0.0-20170607022532-d7c94f1a80ed
github.com/yuin/gopher-lua v1.1.1
golang.org/x/crypto v0.37.0
golang.org/x/sys v0.32.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
@ -45,6 +53,7 @@ require (
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/lestrrat-go/blackmagic v1.0.3 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.6 // indirect
@ -54,11 +63,16 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.64 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.52.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.39.0 // indirect
@ -68,4 +82,5 @@ require (
gorm.io/datatypes v1.2.5 // indirect
gorm.io/driver/mysql v1.5.7 // indirect
gorm.io/hints v1.1.2 // indirect
lukechampine.com/blake3 v1.1.6 // indirect
)

39
go.sum
View file

@ -4,6 +4,8 @@ git.mstar.dev/mstar/goutils v1.14.2 h1:2W9AtsAAGR9OeztPnyVCkxiuZDe7h1DlXzil35wU+
git.mstar.dev/mstar/goutils v1.14.2/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/PeerDB-io/gluabit32 v1.0.2 h1:AGI1Z7dwDVotakpuOOuyTX4/QGi5HUYsipL/VfodmO4=
github.com/PeerDB-io/gluabit32 v1.0.2/go.mod h1:tsHStN1XG5uGVWEA8d/RameB7el3PE3sVkvk8e3+FJg=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
@ -13,7 +15,11 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 h1:rdWOzitWlNYeUsXmz+IQfa9NkGEq3gA/qQ3mOEqBU6o=
github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9/go.mod h1:X97UjDTXp+7bayQSFZk2hPvCTmTZIicUjZQRtkwgAKY=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cosmotek/loguago v1.0.0 h1:cM6xoMPoIL1hRPicMenFNVohylundRIPz+OfpadJyY0=
github.com/cosmotek/loguago v1.0.0/go.mod h1:M/3wRiTLODLY6ufA9sVxOgSvnkYv53sYuDTQEqX0lZ4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -28,6 +34,8 @@ github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV
github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
@ -46,6 +54,8 @@ 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/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
@ -67,6 +77,13 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/kohkimakimoto/gluatemplate v0.0.0-20160815033744-d9e2c9d6b00f h1:CXJzfe/zhkWjXLAZItKA4BPHo4d8Fh7Hc4gqcLVyrWQ=
github.com/kohkimakimoto/gluatemplate v0.0.0-20160815033744-d9e2c9d6b00f/go.mod h1:mepZlGlueX0FYzgC3KQMEuBBQuaAvdp8RUY+ZEe2fbI=
github.com/layeh/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:bg6J/5S/AeTz7K9i/luJRj31BJ8f+LgYwKQBSOZxSEM=
github.com/layeh/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:E/q28EyUVBgBQnONAVPIdwvEsv4Ve0vaCA9JWim4+3I=
github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
@ -93,12 +110,22 @@ github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ=
github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/nrednav/cuid2 v1.0.1 h1:aYLDCmGxEij7xCdiV6GVSPSlqFOS6sqHKKvBeKjddVY=
github.com/nrednav/cuid2 v1.0.1/go.mod h1:nH9lUYqbtoVsnpy20etw5q1guTjE99Xy4EpmnK5nKm0=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
@ -117,6 +144,8 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -128,7 +157,13 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yaronf/httpsign v0.3.2 h1:rBYYx9eHm60noI4oC24JAD2tJuM8AUDh9ErJO/FfzBs=
github.com/yaronf/httpsign v0.3.2/go.mod h1:cYB/6toJrJnf4JTLVoo6IHzFH9/Zu1dcKmah4xfX2WA=
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38DwcXWEPldrTjIZ8FPNKx8mYMGnqjs=
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7/go.mod h1:bbMEM6aU1WDF1ErA5YJ0p91652pGv140gGw4Ww3RGp8=
github.com/yuin/gluare v0.0.0-20170607022532-d7c94f1a80ed h1:I1vcLHWU9m30rA90rMrKPu0eD3NDA4FBlkB8WMaDyUw=
github.com/yuin/gluare v0.0.0-20170607022532-d7c94f1a80ed/go.mod h1:9w6KSdZh23UWqOywWsRLUcJUrUNjRh4Ql3z9uVgnSP4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -179,6 +214,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
@ -206,3 +243,5 @@ gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

12
plugins/api.go Normal file
View file

@ -0,0 +1,12 @@
package plugins
import lua "github.com/yuin/gopher-lua"
// TODO: Decide on the API made available to plugins
// Everything has to be a function, assume no internal state
// since the used lua state may vary between calls
type linstromApi struct{}
func insertLinstromApiIntoState(l *lua.LState) error {
panic("not implemented")
}

62
plugins/init.go Normal file
View file

@ -0,0 +1,62 @@
package plugins
import (
"fmt"
"git.mstar.dev/mstar/goutils/other"
"github.com/PeerDB-io/gluabit32"
"github.com/cjoudrey/gluahttp"
"github.com/cosmotek/loguago"
"github.com/kohkimakimoto/gluatemplate"
gluajson "github.com/layeh/gopher-json"
"github.com/rs/zerolog/log"
"github.com/yuin/gluare"
lua "github.com/yuin/gopher-lua"
webshared "git.mstar.dev/mstar/linstrom/web/shared"
)
// "github.com/CuberL/glua-async" // For async support, see module readme for how to
// Use "github.com/layeh/gopher-luar" for implementing interface
// Create a new "blank" lua state for future use.
// Each created state has to be properly closed.
// A "blank" state has the base libraries preloaded and is technically ready to use.
// Plugin specific functions and data is not yet included
func NewBlankState() (*lua.LState, error) {
l := lua.NewState()
for _, pair := range []struct {
n string
f lua.LGFunction
}{
{lua.LoadLibName, lua.OpenPackage}, // Must always be first
{lua.BaseLibName, lua.OpenBase},
{lua.TabLibName, lua.OpenTable},
// {lua.IoLibName, lua.OpenIo},
{lua.OsLibName, lua.OpenOs},
{lua.StringLibName, lua.OpenString},
{lua.MathLibName, lua.OpenMath},
// {lua.DebugLibName, lua.OpenDebug},
{lua.ChannelLibName, lua.OpenChannel},
{lua.CoroutineLibName, lua.OpenCoroutine},
{"bit32", gluabit32.Loader},
{"json", gluajson.Loader},
{"log", loguago.NewLogger(log.Logger).Loader}, // TODO: Decide if used logger should be passed in via context
{"http", gluahttp.NewHttpModule(&webshared.RequestClient).Loader}, // TODO: Modify this to support signing
{"re", gluare.Loader},
{"texttemplate", gluatemplate.Loader}, // TODO: Copy this lib, but modify to use html/template instead
} {
if err := l.CallByParam(lua.P{
Fn: l.NewFunction(pair.f),
NRet: 0,
Protect: true,
}, lua.LString(pair.n)); err != nil {
return nil, other.Error(
"plugins",
fmt.Sprintf("Failed to preload lua library %q into state", pair.n),
err,
)
}
}
return l, nil
}

67
plugins/pluginHolder.go Normal file
View file

@ -0,0 +1,67 @@
package plugins
import lua "github.com/yuin/gopher-lua"
/*
Plugin hooks:
- Pre local message
- Post local message
- Pre external message (received but not processed by default stuff)
- Post external message (received, internal processing done)
- Post new local account
- Pre new remote account discovered
- Post new remote account discovered
*/
type pluginHolder struct {
meta *PluginMetadata
table *lua.LTable
// ---- Section hooks
postLocalMessage *lua.LFunction
postExternalMessage *lua.LFunction
postNewLocalAccount *lua.LFunction
postRemoteAccountDiscovered *lua.LFunction
// preLocalMessage *lua.LFunction
// preExternalMessage *lua.LFunction // Might not want this
// preRemoteAccountDiscovered *lua.LFunction
}
type PluginMetadata struct {
Name string `json:"name"`
Version string `json:"version"`
// Don't load major and minor versions from meta file, calc from full version instead
Major int `json:"-"`
Minor int `json:"-"`
Source string `json:"source"`
License string `json:"license"`
Hashes map[string]string `json:"hashes"`
EntryFile string `json:"entry"`
HookNames map[string]string `json:"hook_names"`
}
const (
HookPostLocalMessage = "post-local-message"
HookPostExternalMessage = "post-external-message"
HookPostNewLocalAccount = "post-new-local-account"
HookPostRemoteAccountDiscovered = "post-remote-account-discovered"
)
// TODO: Figure out signature
func (p *pluginHolder) PostLocalMessage(l *lua.LState) error {
// Only act on the hook if a hook exists
if p.postLocalMessage != nil {
err := l.CallByParam(lua.P{
Fn: p.postLocalMessage,
NRet: 1, // TODO: Adjust based on signature
Protect: true,
}) // TODO: Translate signature arguments into LValues and include here
if err != nil {
return err
}
// TODO: Translate args on stack back into whats expected from the signature
panic("not implemented")
}
return nil
}

200
plugins/pluginLoader.go Normal file
View file

@ -0,0 +1,200 @@
package plugins
import (
"bufio"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path"
"sync"
"git.mstar.dev/mstar/goutils/other"
lua "github.com/yuin/gopher-lua"
"github.com/yuin/gopher-lua/parse"
)
/*
Plugin structure expectation:
- meta.json for plugin metadata:
- version
- name
- source location (where the plugin can be sourced from)
- license
- file hashes (every file in the plugin dir, sha256, hex encoded)
- entry file name
- Hook to table key name list (which function in the returned table is for what hook)
- Entry script returns a table
*/
const PluginMetadataFileName = "meta.json"
var (
ErrNoHashForFile = errors.New("no hash for file in the metadata")
ErrInvalidHash = errors.New("hashes don't match")
ErrMissingFile = errors.New("a required file is missing")
ErrInvalidEntryReturnValue = errors.New(
"entry script returned an invalid value. Must be a table",
)
)
var compCache = map[string]*lua.FunctionProto{}
var compCacheLock = sync.RWMutex{}
// Load the plugin located in a provided folder into the given state.
// Validates the data, compiles it for future reuse and links up the hooks
func LoadPlugin(location string, lState *lua.LState) (*pluginHolder, error) {
metaFileData, err := os.ReadFile(location + "/" + PluginMetadataFileName)
if err != nil {
return nil, err
}
metadata := PluginMetadata{}
err = json.Unmarshal(metaFileData, &metadata)
if err != nil {
return nil, err
}
compCacheLock.RLock()
precompiled, ok := compCache[metadata.Name+metadata.Version]
compCacheLock.RUnlock()
if !ok {
// This version of a plugin not in the cache yet, validate the plugin dir, compile it and add it to the cache
precompiled, err = compilePluginDir(&metadata, location)
if err != nil {
return nil, err
}
compCacheLock.Lock()
compCache[metadata.Name+metadata.Version] = precompiled
compCacheLock.Unlock()
}
lFunc := lState.NewFunctionFromProto(precompiled)
lState.Push(lFunc)
if err = lState.PCall(0, lua.MultRet, nil); err != nil {
return nil, err
}
return generatePluginHolder(lState, &metadata)
}
// Compiles a given lua file for future use
func compileFile(filePath string) (*lua.FunctionProto, error) {
file, err := os.Open(filePath)
defer func() { _ = file.Close() }()
if err != nil {
return nil, err
}
reader := bufio.NewReader(file)
chunk, err := parse.Parse(reader, filePath)
if err != nil {
return nil, err
}
proto, err := lua.Compile(chunk, filePath)
if err != nil {
return nil, err
}
return proto, nil
}
// Validates the given plugin dir and compiles it for future use
func compilePluginDir(metadata *PluginMetadata, location string) (*lua.FunctionProto, error) {
if err := checkDir(location, "", metadata.Hashes); err != nil {
return nil, err
}
entryFileName := path.Join(location, metadata.EntryFile)
if _, err := os.Stat(entryFileName); err != nil {
return nil, other.Error("plugins", fmt.Sprintf("missing entry file %s", entryFileName), err)
}
compiled, err := compileFile(entryFileName)
if err != nil {
return nil, err
}
return compiled, nil
}
// Ensures that all files in the given root directory (plus suffix) match the expected hashes provided in the metadata
func checkDir(rootPath string, suffix string, expectedHashes map[string]string) error {
rootEntries, err := os.ReadDir(path.Join(rootPath, suffix))
if err != nil {
return err
}
for _, entry := range rootEntries {
if suffix == "" && entry.Name() == PluginMetadataFileName {
continue
}
if entry.IsDir() {
if err = checkDir(rootPath, path.Join(suffix, entry.Name()), expectedHashes); err != nil {
return err
}
}
fileName := path.Join(rootPath, suffix, entry.Name())
expectedHash, ok := expectedHashes[fileName]
if !ok {
return other.Error(
"plugins",
fmt.Sprintf("No hash found in metadata for file %s", fileName),
ErrNoHashForFile,
)
}
f, err := os.Open(fileName)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
if err = checkHash(f, expectedHash); err != nil {
return err
}
}
return nil
}
// Checks if a hash matches the given file
func checkHash(file *os.File, expected string) error {
hasher := sha256.New()
_, err := io.Copy(hasher, file)
if err != nil {
return err
}
sum := hasher.Sum([]byte{})
if expected != hex.EncodeToString(sum) {
return ErrInvalidHash
}
return nil
}
// Properly inserts the plugin into the state's global context.
// Also gets links to all functions found that are named as hook in the metadata.
// Assumptions: Called after the entry file has been run, but before any other operations
func generatePluginHolder(l *lua.LState, meta *PluginMetadata) (*pluginHolder, error) {
pluginTable, ok := l.Get(1).(*lua.LTable)
if !ok || pluginTable.MaxN() != 0 {
return nil, ErrInvalidEntryReturnValue
}
l.SetGlobal(meta.Name, pluginTable)
pMap := lTableToMap(pluginTable)
holder := pluginHolder{
meta: meta,
table: pluginTable,
}
if postLocalMessageHookName, ok := meta.HookNames[HookPostLocalMessage]; ok {
if lV, ok := pMap[lua.LString(postLocalMessageHookName)].(*lua.LFunction); ok {
holder.postLocalMessage = lV
}
}
if postExternalMessageHookName, ok := meta.HookNames[HookPostExternalMessage]; ok {
if lV, ok := pMap[lua.LString(postExternalMessageHookName)].(*lua.LFunction); ok {
holder.postExternalMessage = lV
}
}
if postNewLocalAccountName, ok := meta.HookNames[HookPostNewLocalAccount]; ok {
if lV, ok := pMap[lua.LString(postNewLocalAccountName)].(*lua.LFunction); ok {
holder.postNewLocalAccount = lV
}
}
if postRemoteAccountDiscoveredName, ok := meta.HookNames[HookPostRemoteAccountDiscovered]; ok {
if lV, ok := pMap[lua.LString(postRemoteAccountDiscoveredName)].(*lua.LFunction); ok {
holder.postRemoteAccountDiscovered = lV
}
}
return &holder, nil
}

23
plugins/runner.go Normal file
View file

@ -0,0 +1,23 @@
package plugins
import lua "github.com/yuin/gopher-lua"
type Runner struct {
state *lua.LState
inUse bool
}
func NewRunner() (*Runner, error) {
lState, err := NewBlankState()
if err != nil {
return nil, err
}
return &Runner{
lState,
false,
}, nil
}
func (r *Runner) IsInUse() bool {
return r.inUse
}

11
plugins/util.go Normal file
View file

@ -0,0 +1,11 @@
package plugins
import lua "github.com/yuin/gopher-lua"
func lTableToMap(t *lua.LTable) map[lua.LValue]lua.LValue {
m := map[lua.LValue]lua.LValue{}
t.ForEach(func(l1, l2 lua.LValue) {
m[l1] = l2
})
return m
}