From d1dae84b090e8a90c10c5535a0580cd66abb1bcb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 19 Jun 2024 11:47:35 +0300 Subject: [PATCH] Add MSC3916-compatible giphy media repo proxy --- .gitlab-ci.yml | 10 ++++++ giphyproxy/Dockerfile | 16 +++++++++ giphyproxy/example-config.yaml | 17 ++++++++++ giphyproxy/go.mod | 24 +++++++++++++ giphyproxy/go.sum | 47 ++++++++++++++++++++++++++ giphyproxy/main.go | 62 ++++++++++++++++++++++++++++++++++ 6 files changed, 176 insertions(+) create mode 100644 .gitlab-ci.yml create mode 100644 giphyproxy/Dockerfile create mode 100644 giphyproxy/example-config.yaml create mode 100644 giphyproxy/go.mod create mode 100644 giphyproxy/go.sum create mode 100644 giphyproxy/main.go diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..d5d8838 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,10 @@ +build giphy proxy docker: + image: docker:stable + stage: build + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - docker build -t $CI_REGISTRY_IMAGE/giphyproxy:latest giphyproxy + - docker push $CI_REGISTRY_IMAGE/giphyproxy:latest + only: + - master diff --git a/giphyproxy/Dockerfile b/giphyproxy/Dockerfile new file mode 100644 index 0000000..6088f99 --- /dev/null +++ b/giphyproxy/Dockerfile @@ -0,0 +1,16 @@ +FROM golang:1-alpine AS builder + +RUN apk add --no-cache ca-certificates +WORKDIR /build/giphyproxy +COPY . /build/giphyproxy +ENV CGO_ENABLED=0 +RUN go build -o /usr/bin/giphyproxy + +FROM scratch + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /usr/bin/giphyproxy /usr/bin/giphyproxy + +VOLUME /data +WORKDIR /data +CMD ["/usr/bin/giphyproxy"] diff --git a/giphyproxy/example-config.yaml b/giphyproxy/example-config.yaml new file mode 100644 index 0000000..3bed981 --- /dev/null +++ b/giphyproxy/example-config.yaml @@ -0,0 +1,17 @@ +# The server name to use for the custom mxc:// URIs. +# This server name will effectively be a real Matrix server, it just won't implement anything other than media. +# You must either set up .well-known delegation from this domain to this program, or proxy the domain directly to this program. +server_name: giphy.example.com +# Optionally a custom .well-known response. This defaults to `server_name:443` if empty. +well_known_response: +# The proxy will use MSC3860/MSC3916 media download redirects if the requester supports it. +# Optionally, you can force redirects and not allow proxying at all by setting this to false. +allow_proxy: false +# Matrix server signing key to make the federation tester pass, same format as synapse's .signing.key file. +# You can generate one using `giphyproxy -generate-key`. +server_key: CHANGE ME + +# Hostname where the proxy should listen on +hostname: 0.0.0.0 +# Port where the proxy should listen on +port: 8008 diff --git a/giphyproxy/go.mod b/giphyproxy/go.mod new file mode 100644 index 0000000..8c26d75 --- /dev/null +++ b/giphyproxy/go.mod @@ -0,0 +1,24 @@ +module go.mau.fi/stickerpicker/giphyproxy + +go 1.22.3 + +require ( + go.mau.fi/util v0.5.0 + gopkg.in/yaml.v3 v3.0.1 + maunium.net/go/mautrix v0.19.0-beta.1.0.20240619084603-3e302fb46fdb +) + +require ( + github.com/gorilla/mux v1.8.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/sjson v1.2.5 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect +) diff --git a/giphyproxy/go.sum b/giphyproxy/go.sum new file mode 100644 index 0000000..47a7ac3 --- /dev/null +++ b/giphyproxy/go.sum @@ -0,0 +1,47 @@ +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +go.mau.fi/util v0.5.0 h1:8yELAl+1CDRrwGe9NUmREgVclSs26Z68pTWePHVxuDo= +go.mau.fi/util v0.5.0/go.mod h1:DsJzUrJAG53lCZnnYvq9/mOyLuPScWwYhvETiTrpdP4= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +maunium.net/go/mautrix v0.19.0-beta.1.0.20240619084603-3e302fb46fdb h1:xOe8J6rG2ADTVl56+SFDBDJkwmfCzNpVHLDxHZS+c0w= +maunium.net/go/mautrix v0.19.0-beta.1.0.20240619084603-3e302fb46fdb/go.mod h1:cxv1w6+syudmEpOewHYIQT9yO7TM5UOWmf6xEBVI4H4= diff --git a/giphyproxy/main.go b/giphyproxy/main.go new file mode 100644 index 0000000..279afcb --- /dev/null +++ b/giphyproxy/main.go @@ -0,0 +1,62 @@ +// maunium-stickerpicker - A fast and simple Matrix sticker picker widget. +// Copyright (C) 2024 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import ( + "context" + "flag" + "fmt" + "os" + "regexp" + + "go.mau.fi/util/exerrors" + "gopkg.in/yaml.v3" + "maunium.net/go/mautrix/federation" + "maunium.net/go/mautrix/mediaproxy" +) + +type Config struct { + mediaproxy.BasicConfig `yaml:",inline"` + mediaproxy.ServerConfig `yaml:",inline"` +} + +var configPath = flag.String("config", "config.yaml", "config file path") +var generateServerKey = flag.Bool("generate-key", false, "generate a new server key and exit") + +var giphyIDRegex = regexp.MustCompile(`^[a-zA-Z0-9-_]+$`) + +func main() { + flag.Parse() + if *generateServerKey { + fmt.Println(federation.GenerateSigningKey().SynapseString()) + } else { + cfgFile := exerrors.Must(os.ReadFile(*configPath)) + var cfg Config + exerrors.PanicIfNotNil(yaml.Unmarshal(cfgFile, &cfg)) + mp := exerrors.Must(mediaproxy.NewFromConfig(cfg.BasicConfig, getMedia)) + exerrors.PanicIfNotNil(mp.Listen(cfg.ServerConfig)) + } +} + +func getMedia(_ context.Context, id string) (response mediaproxy.GetMediaResponse, err error) { + if !giphyIDRegex.MatchString(id) { + return nil, mediaproxy.ErrInvalidMediaIDSyntax + } + return &mediaproxy.GetMediaResponseURL{ + URL: fmt.Sprintf("https://i.giphy.com/%s.webp", id), + }, nil +}