all repos — flounder @ b4c7e88d90f608c2127995eeffdc3d3820b2b276

A small site builder for the Gemini protocol

gemini.go (view raw)

  1package main
  2
  3import (
  4	"bytes"
  5	"crypto/tls"
  6	"crypto/x509/pkix"
  7	gmi "git.sr.ht/~adnano/go-gemini"
  8	"io/ioutil"
  9	"log"
 10	"path"
 11	"path/filepath"
 12	"strings"
 13	"text/template"
 14	"time"
 15)
 16
 17var gt *template.Template
 18
 19func generateGemfeedPage(user string) string {
 20	feedItems := generateFeedFromUser(user)
 21	data := struct {
 22		Host        string
 23		Title       string
 24		FeedEntries []FeedEntry
 25	}{c.Host, user + "'s Gemlog", feedItems}
 26	var buff bytes.Buffer
 27	gt.ExecuteTemplate(&buff, "gemfeed.gmi", data)
 28	return buff.String()
 29}
 30
 31func generateFolderPage(fullpath string) string {
 32	files, _ := ioutil.ReadDir(fullpath)
 33	var renderedFiles = []File{}
 34	for _, file := range files {
 35		// Very awkward
 36		res := fileFromPath(path.Join(fullpath, file.Name()))
 37		renderedFiles = append(renderedFiles, res)
 38	}
 39	var buff bytes.Buffer
 40	data := struct {
 41		Host   string
 42		Folder string
 43		Files  []File
 44	}{c.Host, getLocalPath(fullpath), renderedFiles}
 45	err := gt.ExecuteTemplate(&buff, "folder.gmi", data)
 46	if err != nil {
 47		log.Println(err)
 48		return ""
 49	}
 50	return buff.String()
 51}
 52
 53func gmiIndex(w *gmi.ResponseWriter, r *gmi.Request) {
 54	log.Println("Index request")
 55	t, err := template.ParseFiles("templates/index.gmi")
 56	if err != nil {
 57		log.Fatal(err)
 58	}
 59	files, err := getIndexFiles(false)
 60	users, err := getActiveUserNames()
 61	if err != nil {
 62		log.Println(err)
 63		w.WriteHeader(40, "Internal server error")
 64	}
 65	data := struct {
 66		Host      string
 67		SiteTitle string
 68		Files     []*File
 69		Users     []string
 70	}{
 71		Host:      c.Host,
 72		SiteTitle: c.SiteTitle,
 73		Files:     files,
 74		Users:     users,
 75	}
 76	t.Execute(w, data)
 77}
 78
 79func gmiPage(w *gmi.ResponseWriter, r *gmi.Request) {
 80	userName := filepath.Clean(strings.Split(r.URL.Host, ".")[0]) // clean probably unnecessary
 81	fileName := filepath.Clean(r.URL.Path)
 82	if fileName == "/" {
 83		fileName = "index.gmi"
 84	} else if strings.HasPrefix(fileName, "/.hidden") {
 85		w.WriteStatus(gmi.StatusNotFound)
 86		return
 87	}
 88	log.Println("Request for gemini file", fileName, "for user", userName)
 89
 90	gmi.ServeFile(w, gmi.Dir(path.Join(c.FilesDirectory, userName)), fileName)
 91}
 92
 93func runGeminiServer() {
 94	log.Println("Starting gemini server")
 95	var err error
 96	gt, err = template.ParseGlob(path.Join(c.TemplatesDirectory, "*.gmi"))
 97	if err != nil {
 98		log.Fatal(err)
 99	}
100	var server gmi.Server
101	server.ReadTimeout = 1 * time.Minute
102	server.WriteTimeout = 2 * time.Minute
103
104	hostname := strings.SplitN(c.Host, ":", 2)[0]
105	// is this necc?
106	err = server.Certificates.Load(c.GeminiCertStore)
107	if err != nil {
108	}
109	server.CreateCertificate = func(h string) (tls.Certificate, error) {
110		log.Println("Generating certificate for", h)
111		return gmi.CreateCertificate(gmi.CertificateOptions{
112			Subject: pkix.Name{
113				CommonName: hostname,
114			},
115			DNSNames: []string{h},
116			Duration: time.Hour * 760, // one month
117		})
118	}
119
120	var mux gmi.ServeMux
121	// replace with wildcard cert
122	mux.HandleFunc("/", gmiIndex)
123
124	var wildcardMux gmi.ServeMux
125	wildcardMux.HandleFunc("/", gmiPage)
126	server.Register(hostname, &mux)
127	server.Register("*."+hostname, &wildcardMux)
128
129	err = server.ListenAndServe()
130	if err != nil {
131		log.Fatal(err)
132	}
133}