all repos — flounder @ 5c687e0a49cd53db58b2a13728594c7460000443

A small site builder for the Gemini protocol

a bunch of misc changes
alex wennerberg alex@alexwennerberg.com
Sat, 24 Oct 2020 01:13:39 -0700
commit

5c687e0a49cd53db58b2a13728594c7460000443

parent

1fab64591b2c09823f1ef7fb24e713d7bf7488d4

M flounder.tomlflounder.toml

@@ -1,3 +1,4 @@

+# Used in HTML templates and titles SiteTitle="🐟flounder" RootDomain="localhost" FilesDirectory="./files"

@@ -5,3 +6,5 @@ # handles templates and static files

# everything in the static subfolder will be served at root TemplatesDirectory="./templates" DBFile="./flounder.db" + +# Log file
M gemini.gogemini.go

@@ -12,6 +12,7 @@ "io/ioutil"

"log" "os" "path" + "path/filepath" "text/template" "time" )

@@ -24,21 +25,24 @@ }

files, _ := getIndexFiles() users, _ := getUsers() data := struct { - Domain string - Files []*File - Users []string + Domain string + SiteTitle string + Files []*File + Users []string }{ - Domain: "flounder.online", - Files: files, - Users: users, + Domain: c.RootDomain, + SiteTitle: c.SiteTitle, + Files: files, + Users: users, } t.Execute(w, data) } func gmiPage(w *gmi.ResponseWriter, r *gmi.Request) { userName := strings.Split(r.URL.Host, ".")[0] - fileName := path.Join(c.FilesDirectory, userName, r.URL.Path) + fileName := path.Join(c.FilesDirectory, userName, filepath.Clean(r.URL.Path)) data, err := ioutil.ReadFile(fileName) + // serve file? // TODO write mimetype if err != nil { // TODO return 404 equivalent

@@ -91,6 +95,8 @@ server.HandleFunc("*."+c.RootDomain, gmiPage)

server.ListenAndServe() } + +// TODO log request // writeCertificate writes the provided certificate and private key // to path.crt and path.key respectively.
M go.modgo.mod

@@ -5,5 +5,7 @@

require ( git.sr.ht/~adnano/gmi v0.1.0-alpha.2 github.com/BurntSushi/toml v0.3.1 + github.com/gorilla/handlers v1.5.1 golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d + golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e )
M go.sumgo.sum

@@ -2,6 +2,10 @@ git.sr.ht/~adnano/gmi v0.1.0-alpha.2 h1:5/wzImYT3mJmZ27lazJ8YAdpiVN3QNJruLX7PXOITeo=

git.sr.ht/~adnano/gmi v0.1.0-alpha.2/go.mod h1:t/m2KtH+7lXIF7jjVN+bNvwPbE0nxHOpvlA/WZ/KeLQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

@@ -9,3 +13,5 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=

golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
M http.gohttp.go

@@ -2,12 +2,15 @@ package main

import ( "git.sr.ht/~adnano/gmi" + "github.com/gorilla/handlers" "html/template" "log" "net/http" "os" "path" + "path/filepath" "strings" + "time" ) var t *template.Template

@@ -26,7 +29,7 @@

func rootHandler(w http.ResponseWriter, r *http.Request) { // serve everything inside static directory if r.URL.Path != "/" { - fileName := path.Join(c.TemplatesDirectory, "static", r.URL.Path) + fileName := path.Join(c.TemplatesDirectory, "static", filepath.Clean(r.URL.Path)) http.ServeFile(w, r, fileName) return }

@@ -154,25 +157,19 @@

// Server a user's file func userFile(w http.ResponseWriter, r *http.Request) { userName := strings.Split(r.Host, ".")[0] - fileName := path.Join(c.FilesDirectory, userName, r.URL.Path) + fileName := path.Join(c.FilesDirectory, userName, filepath.Clean(r.URL.Path)) extension := path.Ext(fileName) if r.URL.Path == "/static/style.css" { http.ServeFile(w, r, path.Join(c.TemplatesDirectory, "static/style.css")) } if extension == ".gmi" || extension == ".gemini" { - if strings.Contains(fileName, "..") { - // prevent directory traversal TODO verify - http.Error(w, "invalid URL path", http.StatusBadRequest) - } else { - // covert to html - stat, _ := os.Stat(fileName) - file, _ := os.Open(fileName) - htmlString := gmi.Parse(file).HTML() - reader := strings.NewReader(htmlString) - w.Header().Set("Content-Type", "text/html") - http.ServeContent(w, r, fileName, stat.ModTime(), reader) - } - // TODO clean + // covert to html + stat, _ := os.Stat(fileName) + file, _ := os.Open(fileName) + htmlString := gmi.Parse(file).HTML() + reader := strings.NewReader(htmlString) + w.Header().Set("Content-Type", "text/html") + http.ServeContent(w, r, fileName, stat.ModTime(), reader) } else { http.ServeFile(w, r, fileName) }

@@ -185,22 +182,27 @@ t, err = template.ParseGlob("./templates/*.html") // TODO make template dir configruable

if err != nil { log.Fatal(err) } - http.HandleFunc(c.RootDomain+"/", rootHandler) - http.HandleFunc(c.RootDomain+"/my_site", mySiteHandler) - http.HandleFunc(c.RootDomain+"/edit/", editFileHandler) - http.HandleFunc(c.RootDomain+"/login", loginHandler) - http.HandleFunc(c.RootDomain+"/register", registerHandler) - http.HandleFunc(c.RootDomain+"/delete/", deleteFileHandler) - // login+register functions + serveMux := http.NewServeMux() + + serveMux.HandleFunc(c.RootDomain+"/", rootHandler) + serveMux.HandleFunc(c.RootDomain+"/my_site", mySiteHandler) + serveMux.HandleFunc(c.RootDomain+"/edit/", editFileHandler) + serveMux.HandleFunc(c.RootDomain+"/login", loginHandler) + serveMux.HandleFunc(c.RootDomain+"/register", registerHandler) + serveMux.HandleFunc(c.RootDomain+"/delete/", deleteFileHandler) + + wrapped := handlers.LoggingHandler(os.Stdout, serveMux) // handle user files based on subdomain - http.HandleFunc("/", userFile) - log.Fatal(http.ListenAndServe(":8080", logRequest(http.DefaultServeMux))) -} - -func logRequest(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL) - handler.ServeHTTP(w, r) - }) + serveMux.HandleFunc("/", userFile) + // login+register functions + srv := &http.Server{ + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 120 * time.Second, + Addr: ":8080", + // TLSConfig: tlsConfig, + Handler: wrapped, + } + log.Fatal(srv.ListenAndServe()) }
M main.gomain.go

@@ -7,6 +7,7 @@ "log"

"os" "path" "path/filepath" + "sync" ) var c Config // global var to hold static configuration

@@ -71,7 +72,16 @@ c, err = getConfig(*configPath)

if err != nil { log.Fatal(err) } - runHTTPServer() - // runGeminiServer() - // go log.Fatal(gmi.ListenAndServe(":8080", nil)) + + wg := new(sync.WaitGroup) + wg.Add(2) + go func() { + runHTTPServer() + wg.Done() + }() + go func() { + runGeminiServer() + wg.Done() + }() + wg.Wait() }
M templates/index.gmitemplates/index.gmi

@@ -1,5 +1,5 @@

{{$domain := .Domain}} -# 🐟Flounder! +# {{.SiteTitle}}! 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!
M templates/static/style.csstemplates/static/style.css

@@ -4,12 +4,7 @@ padding: 2ch;

margin: auto; font-family: Helvetica, Arial, monospace; word-wrap: break-word; - border: 1px solid black; background-color: white; -} - -body { - background-color: #cfe6fc; } .inline {
A templates/user_page.html

@@ -0,0 +1,20 @@

+{{$domain := .Domain}} +{{template "header" .}} +<h1>{{.PageTitle}}!</h1> +{{template "nav.html" .}} +<h2>All users:</h2> +{{ range .Users}} +<a href="https://{{.}}.{{$domain}}" class='person-link'>{{.}}</a> +{{end}} +<h2>Recently updated files:</h2> +{{ range .Files }} +<div> + <a href="https://{{.Creator}}.{{$domain}}" class='person-link'> + {{ .Creator }}</a> + <em>{{.UpdatedTime}}</em> + <a href="https://{{.Creator}}.{{$domain}}/{{.Name}}"> + {{ .Name}} + </a> +</div> +{{end}} +{{template "footer" .}}