add basic templating
alex wennerberg alex@alexwennerberg.com
Mon, 19 Oct 2020 21:16:21 -0700
3 files changed,
138 insertions(+),
5 deletions(-)
M
gemini.go
→
gemini.go
@@ -1,19 +1,132 @@
package main import ( - "fmt" + "bytes" + "crypto/tls" + "crypto/x509" // todo move into cert file + "encoding/pem" + // "fmt" "git.sr.ht/~adnano/gmi" + "log" + "os" + // "path" + "text/template" + "time" ) func gmiIndex(w *gmi.ResponseWriter, r *gmi.Request) { - fmt.Fprintf(w, "index") + t, err := template.ParseFiles("templates/index.gmi") + if err != nil { + log.Fatal(err) + } + files, _ := getIndexFiles() + users, _ := getUsers() + data := struct { + Domain string + Files []*File + Users []string + }{ + Domain: "flounder.online", + Files: files, + Users: users, + } + t.Execute(w, data) } func gmiPage(w *gmi.ResponseWriter, r *gmi.Request) { } -func runGeminiServer() { +func runGeminiServer(config *Config) { var server gmi.Server - server.HandleFunc("flounder.online", gmiIndex) + + if err := server.CertificateStore.Load("./tmpcerts"); err != nil { + log.Fatal(err) + } + server.GetCertificate = func(hostname string, store *gmi.CertificateStore) *tls.Certificate { + cert, err := store.Lookup(hostname) + if err != nil { + switch err { + case gmi.ErrCertificateExpired: + // Generate a new certificate if the current one is expired. + log.Print("Old certificate expired, creating new one") + fallthrough + case gmi.ErrCertificateUnknown: + // Generate a certificate if one does not exist. + cert, err := gmi.NewCertificate(hostname, time.Minute) + if err != nil { + // Failed to generate new certificate, abort + return nil + } + // Store and return the new certificate + err = writeCertificate("./tmpcerts/"+hostname, cert) + if err != nil { + return nil + } + store.Add(hostname, cert) + return &cert + } + } + return cert + } + + // replace with wildcard cert + server.HandleFunc("localhost", gmiIndex) + server.ListenAndServe() } + +// writeCertificate writes the provided certificate and private key +// to path.crt and path.key respectively. +func writeCertificate(path string, cert tls.Certificate) error { + crt, err := marshalX509Certificate(cert.Leaf.Raw) + if err != nil { + return err + } + key, err := marshalPrivateKey(cert.PrivateKey) + if err != nil { + return err + } + + // Write the certificate + crtPath := path + ".crt" + crtOut, err := os.OpenFile(crtPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + if _, err := crtOut.Write(crt); err != nil { + return err + } + + // Write the private key + keyPath := path + ".key" + keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + if _, err := keyOut.Write(key); err != nil { + return err + } + return nil +} + +// marshalX509Certificate returns a PEM-encoded version of the given raw certificate. +func marshalX509Certificate(cert []byte) ([]byte, error) { + var b bytes.Buffer + if err := pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: cert}); err != nil { + return nil, err + } + return b.Bytes(), nil +} + +// marshalPrivateKey returns PEM encoded versions of the given certificate and private key. +func marshalPrivateKey(priv interface{}) ([]byte, error) { + var b bytes.Buffer + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return nil, err + } + if err := pem.Encode(&b, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { + return nil, err + } + return b.Bytes(), nil +}
M
main.go
→
main.go
@@ -18,6 +18,10 @@ Name string
UpdatedTime string } +func getUsers() ([]string, error) { + return []string{"me", "other guy"}, nil +} + func getIndexFiles() ([]*File, error) { // cache this function result := []*File{} err := filepath.Walk(userFilesPath, func(path string, info os.FileInfo, err error) error {@@ -58,10 +62,11 @@ return result, nil
} func main() { + config := Config{} // http functions // go serve gemini // go serve http -- not // runHTTPServer() - runGeminiServer() + runGeminiServer(&config) // go log.Fatal(gmi.ListenAndServe(":8080", nil)) }
A
templates/index.gmi
@@ -0,0 +1,15 @@
+{{$domain := .Domain}} +# 🐟Flounder! + +Welcome to flounder, a home for Gemini sites. Flounder hosts small Gemini web pages over https and Gemini. Right now, the only way to make an account is via the https portal, but I'm working on adding alternatives. Feel free to make an account and join if you'd like! + +=> gemini://admin.{{$domain}} Admin page +=> https://{{$domain}} View on HTTPS + +## All Users: +{{range .Users}}=> gemini://{{.}}.{{$domain}} +{{end}} + +## Recently updated files: +{{range .Files}}=> gemini://{{.Creator}}.{{$domain}}/{{.Name}} {{.Name}} ({{.UpdatedTime}}) +{{end}}