Automatically bridge gemini links to a gateway on http site
This commit is contained in:
parent
aba69d4329
commit
c4520f2c84
@ -43,6 +43,7 @@
|
|||||||
export MEDIOCRE_BLOG_HTTP_PUBLIC_URL="$MEDIOCRE_BLOG_ML_PUBLIC_URL"
|
export MEDIOCRE_BLOG_HTTP_PUBLIC_URL="$MEDIOCRE_BLOG_ML_PUBLIC_URL"
|
||||||
export MEDIOCRE_BLOG_HTTP_LISTEN_PROTO="tcp"
|
export MEDIOCRE_BLOG_HTTP_LISTEN_PROTO="tcp"
|
||||||
export MEDIOCRE_BLOG_HTTP_LISTEN_ADDR=":4000"
|
export MEDIOCRE_BLOG_HTTP_LISTEN_ADDR=":4000"
|
||||||
|
export MEDIOCRE_BLOG_HTTP_GEMINI_GATEWAY_URL="https://nightfall.city/x/"
|
||||||
|
|
||||||
# http auth
|
# http auth
|
||||||
# (password is "bar". This should definitely be changed for prod.)
|
# (password is "bar". This should definitely be changed for prod.)
|
||||||
|
@ -104,6 +104,12 @@ published_posts:
|
|||||||
|
|
||||||
Edgy.
|
Edgy.
|
||||||
|
|
||||||
|
=> / Here's a link within the site
|
||||||
|
|
||||||
|
=> gemini://mediocregopher.com And here's a link to a gemini capsule
|
||||||
|
|
||||||
|
=> https://mediocregopher.com And here's a link to an https site
|
||||||
|
|
||||||
#### Side-note
|
#### Side-note
|
||||||
|
|
||||||
Did you know that the terms "cyberspace" and "matrix" are attributable to a book from 1984 called _Neuromancer_?
|
Did you know that the terms "cyberspace" and "matrix" are attributable to a book from 1984 called _Neuromancer_?
|
||||||
@ -117,8 +123,6 @@ published_posts:
|
|||||||
|
|
||||||
This has been a great post.
|
This has been a great post.
|
||||||
|
|
||||||
=> / Here's a link outa here!
|
|
||||||
|
|
||||||
- id: empty-markdown-test
|
- id: empty-markdown-test
|
||||||
title: Empty Markdown Test
|
title: Empty Markdown Test
|
||||||
description:
|
description:
|
||||||
|
@ -5,8 +5,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -25,7 +26,10 @@ var linkRegexp = regexp.MustCompile(`^=>\s+(\S+)\s*(.*?)\s*$`)
|
|||||||
|
|
||||||
// GemtextToMarkdown reads a gemtext formatted body from the Reader and writes
|
// GemtextToMarkdown reads a gemtext formatted body from the Reader and writes
|
||||||
// the markdown version of that body to the Writer.
|
// the markdown version of that body to the Writer.
|
||||||
func GemtextToMarkdown(dst io.Writer, src io.Reader) error {
|
//
|
||||||
|
// gmiGateway, if given, is used for all `gemini://` links. The `gemini://`
|
||||||
|
// prefix will be stripped, and replaced with the given URL.
|
||||||
|
func GemtextToMarkdown(dst io.Writer, src io.Reader, gmiGateway *url.URL) error {
|
||||||
|
|
||||||
bufSrc := bufio.NewReader(src)
|
bufSrc := bufio.NewReader(src)
|
||||||
|
|
||||||
@ -40,7 +44,20 @@ func GemtextToMarkdown(dst io.Writer, src io.Reader) error {
|
|||||||
|
|
||||||
if match := linkRegexp.FindStringSubmatch(line); len(match) > 0 {
|
if match := linkRegexp.FindStringSubmatch(line); len(match) > 0 {
|
||||||
|
|
||||||
isImg := hasImgExt(match[1])
|
u, err := url.Parse(match[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("link to invalid url %q: %w", match[1], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme == "gemini" && gmiGateway != nil {
|
||||||
|
|
||||||
|
newU := *gmiGateway
|
||||||
|
newU.Path = filepath.Join(newU.Path, u.Host, u.Path)
|
||||||
|
newU.RawQuery = u.RawQuery
|
||||||
|
u = &newU
|
||||||
|
}
|
||||||
|
|
||||||
|
isImg := hasImgExt(u.Path)
|
||||||
|
|
||||||
descr := match[2]
|
descr := match[2]
|
||||||
|
|
||||||
@ -52,9 +69,7 @@ func GemtextToMarkdown(dst io.Writer, src io.Reader) error {
|
|||||||
descr = "Link"
|
descr = "Link"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("descr:%q", descr)
|
line = fmt.Sprintf("[%s](%s)\n", descr, u.String())
|
||||||
|
|
||||||
line = fmt.Sprintf("[%s](%s)\n", descr, match[1])
|
|
||||||
|
|
||||||
if isImg {
|
if isImg {
|
||||||
line = "!" + line
|
line = "!" + line
|
||||||
|
@ -2,6 +2,7 @@ package gmi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -10,6 +11,8 @@ import (
|
|||||||
|
|
||||||
func TestGemtextToMarkdown(t *testing.T) {
|
func TestGemtextToMarkdown(t *testing.T) {
|
||||||
|
|
||||||
|
gmiGateway, _ := url.Parse("https://gateway.com/x/")
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in, exp string
|
in, exp string
|
||||||
}{
|
}{
|
||||||
@ -37,13 +40,25 @@ func TestGemtextToMarkdown(t *testing.T) {
|
|||||||
in: "=> img.png description is here ",
|
in: "=> img.png description is here ",
|
||||||
exp: "![description is here](img.png)\n",
|
exp: "![description is here](img.png)\n",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
in: "=> gemini://somewhere.com/foo Somewhere",
|
||||||
|
exp: "[Somewhere](https://gateway.com/x/somewhere.com/foo)\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: "=> gemini://somewhere.com:420/foo Somewhere",
|
||||||
|
exp: "[Somewhere](https://gateway.com/x/somewhere.com:420/foo)\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: "=> gemini://somewhere.com:420/foo?bar=baz Somewhere",
|
||||||
|
exp: "[Somewhere](https://gateway.com/x/somewhere.com:420/foo?bar=baz)\n",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
|
||||||
got := new(bytes.Buffer)
|
got := new(bytes.Buffer)
|
||||||
err := GemtextToMarkdown(got, bytes.NewBufferString(test.in))
|
err := GemtextToMarkdown(got, bytes.NewBufferString(test.in), gmiGateway)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, test.exp, got.String())
|
assert.Equal(t, test.exp, got.String())
|
||||||
})
|
})
|
||||||
|
@ -56,12 +56,17 @@ type Params struct {
|
|||||||
// AuthRatelimit indicates how much time must pass between subsequent auth
|
// AuthRatelimit indicates how much time must pass between subsequent auth
|
||||||
// attempts.
|
// attempts.
|
||||||
AuthRatelimit time.Duration
|
AuthRatelimit time.Duration
|
||||||
|
|
||||||
|
// GeminiGatewayURL will be used to translate links for `gemini://` into
|
||||||
|
// `http(s)://`. See gmi.GemtextToMarkdown.
|
||||||
|
GeminiGatewayURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupCfg implement the cfg.Cfger interface.
|
// SetupCfg implement the cfg.Cfger interface.
|
||||||
func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
||||||
|
|
||||||
publicURLStr := cfg.String("http-public-url", "http://localhost:4000", "URL this service is accessible at")
|
publicURLStr := cfg.String("http-public-url", "http://localhost:4000", "URL this service is accessible at")
|
||||||
|
geminiGatewayURLStr := cfg.String("http-gemini-gateway-url", "", "Optional URL to prefix to all gemini:// links, to make them accessible over https")
|
||||||
|
|
||||||
cfg.StringVar(&p.ListenProto, "http-listen-proto", "tcp", "Protocol to listen for HTTP requests with")
|
cfg.StringVar(&p.ListenProto, "http-listen-proto", "tcp", "Protocol to listen for HTTP requests with")
|
||||||
cfg.StringVar(&p.ListenAddr, "http-listen-addr", ":4000", "Address/unix socket path to listen for HTTP requests on")
|
cfg.StringVar(&p.ListenAddr, "http-listen-addr", ":4000", "Address/unix socket path to listen for HTTP requests on")
|
||||||
@ -87,6 +92,12 @@ func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
|||||||
return fmt.Errorf("parsing -http-public-url: %w", err)
|
return fmt.Errorf("parsing -http-public-url: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *geminiGatewayURLStr != "" {
|
||||||
|
if p.GeminiGatewayURL, err = url.Parse(*geminiGatewayURLStr); err != nil {
|
||||||
|
return fmt.Errorf("parsing -http-gemini-gateway-url: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,11 @@ func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload,
|
|||||||
prevBodyBuf := bodyBuf
|
prevBodyBuf := bodyBuf
|
||||||
bodyBuf = new(bytes.Buffer)
|
bodyBuf = new(bytes.Buffer)
|
||||||
|
|
||||||
if err := gmi.GemtextToMarkdown(bodyBuf, prevBodyBuf); err != nil {
|
err := gmi.GemtextToMarkdown(
|
||||||
|
bodyBuf, prevBodyBuf, a.params.GeminiGatewayURL,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return postTplPayload{}, fmt.Errorf("converting gemtext to markdown: %w", err)
|
return postTplPayload{}, fmt.Errorf("converting gemtext to markdown: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user