Compare commits
2 Commits
c23030733f
...
84c1322c44
Author | SHA1 | Date | |
---|---|---|---|
|
84c1322c44 | ||
|
0bd8bd6f23 |
12
flake.nix
12
flake.nix
@ -17,7 +17,7 @@
|
|||||||
version = "dev";
|
version = "dev";
|
||||||
src = ./src;
|
src = ./src;
|
||||||
|
|
||||||
vendorSha256 = "sha256:1vazrrg8rs9n8x40c9r53h9qnyxw59xkp0aq7jl15fliigk6q0cr";
|
vendorSha256 = "sha256-02LW4zscNKoIfzcBhOQwObh/04oRl/6hRsFMfCycWzA=";
|
||||||
|
|
||||||
subPackages = [ "cmd/mediocre-blog" ];
|
subPackages = [ "cmd/mediocre-blog" ];
|
||||||
|
|
||||||
@ -26,7 +26,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
devShell = pkgs.mkShell {
|
devShell = pkgs.mkShell {
|
||||||
buildInputs = [ pkgs.go pkgs.sqlite ];
|
buildInputs = [ pkgs.go pkgs.sqlite pkgs.amfora ];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
|
||||||
export MEDIOCRE_BLOG_DATA_DIR="/tmp/mediocre-blog/data"
|
export MEDIOCRE_BLOG_DATA_DIR="/tmp/mediocre-blog/data"
|
||||||
@ -49,13 +49,19 @@
|
|||||||
export MEDIOCRE_BLOG_HTTP_AUTH_USERS='{"foo":"$2a$13$0JdWlUfHc.3XimEMpEu1cuu6RodhUvzD9l7iiAqa4YkM3mcFV5Pxi"}'
|
export MEDIOCRE_BLOG_HTTP_AUTH_USERS='{"foo":"$2a$13$0JdWlUfHc.3XimEMpEu1cuu6RodhUvzD9l7iiAqa4YkM3mcFV5Pxi"}'
|
||||||
export MEDIOCRE_BLOG_HTTP_AUTH_RATELIMIT="1s"
|
export MEDIOCRE_BLOG_HTTP_AUTH_RATELIMIT="1s"
|
||||||
|
|
||||||
|
# gmi
|
||||||
|
export MEDIOCRE_BLOG_GEMINI_PUBLIC_URL="gemini://localhost:2096"
|
||||||
|
export MEDIOCRE_BLOG_GEMINI_LISTEN_ADDR=":2065"
|
||||||
|
export MEDIOCRE_BLOG_GEMINI_CERTIFICATES_PATH="$MEDIOCRE_BLOG_DATA_DIR/gmi/certs"
|
||||||
|
|
||||||
cd src
|
cd src
|
||||||
|
|
||||||
echo 'Loading test data...'
|
echo 'Loading test data...'
|
||||||
(cd cmd/load-test-data && go run main.go)
|
(cd cmd/load-test-data && go run main.go)
|
||||||
|
|
||||||
echo -e "\n\nTest data has been loaded into $MEDIOCRE_BLOG_DATA_DIR\n"
|
echo -e "\n\nTest data has been loaded into $MEDIOCRE_BLOG_DATA_DIR\n"
|
||||||
echo -e "You can do 'go run ./cmd/mediocre-blog/main.go' to start a dev instance on http://localhost:4000\n\n"
|
echo -e "You can do 'go run ./cmd/mediocre-blog/main.go' to start a dev instance on http://localhost:4000\n"
|
||||||
|
echo -e "You can then do 'amfora gemini://localhost:2065' to test the gemini server\n"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
cfgpkg "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
cfgpkg "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/gmi"
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/http"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/http"
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist"
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/post"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/post"
|
||||||
@ -44,6 +45,10 @@ func main() {
|
|||||||
httpParams.SetupCfg(cfg)
|
httpParams.SetupCfg(cfg)
|
||||||
ctx = mctx.WithAnnotator(ctx, &httpParams)
|
ctx = mctx.WithAnnotator(ctx, &httpParams)
|
||||||
|
|
||||||
|
var gmiParams gmi.Params
|
||||||
|
gmiParams.SetupCfg(cfg)
|
||||||
|
ctx = mctx.WithAnnotator(ctx, &gmiParams)
|
||||||
|
|
||||||
// initialization
|
// initialization
|
||||||
err := cfg.Init(ctx)
|
err := cfg.Init(ctx)
|
||||||
|
|
||||||
@ -104,10 +109,10 @@ func main() {
|
|||||||
httpParams.PostDraftStore = postDraftStore
|
httpParams.PostDraftStore = postDraftStore
|
||||||
httpParams.MailingList = ml
|
httpParams.MailingList = ml
|
||||||
|
|
||||||
logger.Info(ctx, "listening")
|
logger.Info(ctx, "starting http api")
|
||||||
httpAPI, err := http.New(httpParams)
|
httpAPI, err := http.New(httpParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(ctx, "initializing http api", err)
|
logger.Fatal(ctx, "starting http api", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
@ -118,6 +123,23 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
gmiParams.Logger = logger.WithNamespace("gmi")
|
||||||
|
|
||||||
|
logger.Info(ctx, "starting gmi api")
|
||||||
|
gmiAPI, err := gmi.New(gmiParams)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(ctx, "starting gmi api", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := gmiAPI.Shutdown(shutdownCtx); err != nil {
|
||||||
|
logger.Fatal(ctx, "shutting down gmi api", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// wait
|
// wait
|
||||||
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
122
src/gmi/gmi.go
122
src/gmi/gmi.go
@ -1,2 +1,122 @@
|
|||||||
// Package gmi contains utilities for working with gemini and gemtext
|
// Package gmi implements the gemini-based api for the mediocre-blog.
|
||||||
package gmi
|
package gmi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.sr.ht/~adnano/go-gemini"
|
||||||
|
"git.sr.ht/~adnano/go-gemini/certificate"
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
||||||
|
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
||||||
|
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Params are used to instantiate a new API instance. All fields are required
|
||||||
|
// unless otherwise noted.
|
||||||
|
type Params struct {
|
||||||
|
Logger *mlog.Logger
|
||||||
|
PublicURL *url.URL
|
||||||
|
ListenAddr string
|
||||||
|
CertificatesPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupCfg implement the cfg.Cfger interface.
|
||||||
|
func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
||||||
|
|
||||||
|
publicURLStr := cfg.String("gemini-public-url", "gemini://localhost:2065", "URL this service is accessible at")
|
||||||
|
|
||||||
|
cfg.StringVar(&p.ListenAddr, "gemini-listen-addr", ":2065", "Address to listen for HTTP requests on")
|
||||||
|
|
||||||
|
cfg.StringVar(&p.CertificatesPath, "gemini-certificates-path", "", "Path to directory where gemini certs should be created/stored")
|
||||||
|
|
||||||
|
cfg.OnInit(func(context.Context) error {
|
||||||
|
|
||||||
|
if p.CertificatesPath == "" {
|
||||||
|
return errors.New("-gemini-certificates-path is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
*publicURLStr = strings.TrimSuffix(*publicURLStr, "/")
|
||||||
|
|
||||||
|
if p.PublicURL, err = url.Parse(*publicURLStr); err != nil {
|
||||||
|
return fmt.Errorf("parsing -gemini-public-url: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotate implements mctx.Annotator interface.
|
||||||
|
func (p *Params) Annotate(a mctx.Annotations) {
|
||||||
|
a["geminiPublicURL"] = p.PublicURL
|
||||||
|
a["geminiListenAddr"] = p.ListenAddr
|
||||||
|
a["geminiCertificatesPath"] = p.CertificatesPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// API will listen on the port configured for it, and serve gemini requests for
|
||||||
|
// the mediocre-blog.
|
||||||
|
type API interface {
|
||||||
|
Shutdown(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
params Params
|
||||||
|
srv *gemini.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
// New initializes and returns a new API instance, including setting up all
|
||||||
|
// listening ports.
|
||||||
|
func New(params Params) (API, error) {
|
||||||
|
|
||||||
|
if err := os.MkdirAll(params.CertificatesPath, 0700); err != nil {
|
||||||
|
return nil, fmt.Errorf("creating certificate directory %q: %w", params.CertificatesPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certStore := new(certificate.Store)
|
||||||
|
certStore.Load(params.CertificatesPath)
|
||||||
|
certStore.Register(params.PublicURL.Hostname())
|
||||||
|
|
||||||
|
a := &api{
|
||||||
|
params: params,
|
||||||
|
}
|
||||||
|
|
||||||
|
a.srv = &gemini.Server{
|
||||||
|
Addr: params.ListenAddr,
|
||||||
|
Handler: a.handler(),
|
||||||
|
GetCertificate: certStore.Get,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
ctx := mctx.WithAnnotator(context.Background(), &a.params)
|
||||||
|
|
||||||
|
err := a.srv.ListenAndServe(ctx)
|
||||||
|
if err != nil && !errors.Is(err, context.Canceled) {
|
||||||
|
a.params.Logger.Fatal(ctx, "serving gemini server", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) Shutdown(ctx context.Context) error {
|
||||||
|
return a.srv.Shutdown(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) handler() gemini.Handler {
|
||||||
|
return gemini.HandlerFunc(func(
|
||||||
|
ctx context.Context,
|
||||||
|
rw gemini.ResponseWriter,
|
||||||
|
r *gemini.Request,
|
||||||
|
) {
|
||||||
|
fmt.Fprintf(rw, "# Test\n\n")
|
||||||
|
fmt.Fprintf(rw, "HELLO WORLD\n\n")
|
||||||
|
fmt.Fprintf(rw, "=> gemini://midnight.pub Hit the pub\n\n")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ module github.com/mediocregopher/blog.mediocregopher.com/srv
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.sr.ht/~adnano/go-gemini v0.2.3 // indirect
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
||||||
github.com/emersion/go-smtp v0.15.0
|
github.com/emersion/go-smtp v0.15.0
|
||||||
github.com/gomarkdown/markdown v0.0.0-20220510115730-2372b9aa33e5
|
github.com/gomarkdown/markdown v0.0.0-20220510115730-2372b9aa33e5
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
git.sr.ht/~adnano/go-gemini v0.2.3 h1:oJ+Y0/mheZ4Vg0ABjtf5dlmvq1yoONStiaQvmWWkofc=
|
||||||
|
git.sr.ht/~adnano/go-gemini v0.2.3/go.mod h1:hQ75Y0i5jSFL+FQ7AzWVAYr5LQsaFC7v3ZviNyj46dY=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
@ -193,6 +195,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -216,6 +220,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+
|
|||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Package api implements the HTTP-based api for the mediocre-blog.
|
// Package http implements the HTTP-based api for the mediocre-blog.
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -64,7 +64,7 @@ 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")
|
||||||
|
|
||||||
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/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")
|
||||||
|
|
||||||
httpAuthUsersStr := cfg.String("http-auth-users", "{}", "JSON object with usernames as values and password hashes (produced by the hash-password binary) as values. Denotes users which are able to edit server-side data")
|
httpAuthUsersStr := cfg.String("http-auth-users", "{}", "JSON object with usernames as values and password hashes (produced by the hash-password binary) as values. Denotes users which are able to edit server-side data")
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ func New(params Params) (API, error) {
|
|||||||
|
|
||||||
err := a.srv.Serve(l)
|
err := a.srv.Serve(l)
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
ctx := mctx.Annotate(context.Background(), a.params)
|
ctx := mctx.WithAnnotator(context.Background(), &a.params)
|
||||||
params.Logger.Fatal(ctx, "serving http server", err)
|
params.Logger.Fatal(ctx, "serving http server", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
Loading…
Reference in New Issue
Block a user