all repos — flounder @ 7052b40538b59228f4ad763396682b8989ec0ceb

A small site builder for the Gemini protocol

http.go (view raw)

  1package main
  2
  3import (
  4	"git.sr.ht/~adnano/gmi"
  5	"github.com/gorilla/handlers"
  6	"html/template"
  7	"log"
  8	"net/http"
  9	"os"
 10	"path"
 11	"path/filepath"
 12	"strings"
 13	"time"
 14)
 15
 16var t *template.Template
 17
 18// TODO somewhat better error handling
 19const InternalServerErrorMsg = "500: Internal Server Error"
 20
 21func renderError(w http.ResponseWriter, errorMsg string, statusCode int) { // TODO think about pointers
 22	data := struct{ ErrorMsg string }{errorMsg}
 23	err := t.ExecuteTemplate(w, "error.html", data)
 24	if err != nil { // shouldn't happen probably
 25		http.Error(w, errorMsg, statusCode)
 26	}
 27}
 28
 29func rootHandler(w http.ResponseWriter, r *http.Request) {
 30	// serve everything inside static directory
 31	if r.URL.Path != "/" {
 32		fileName := path.Join(c.TemplatesDirectory, "static", filepath.Clean(r.URL.Path))
 33		http.ServeFile(w, r, fileName)
 34		return
 35	}
 36	indexFiles, err := getIndexFiles()
 37	if err != nil {
 38		log.Println(err)
 39		renderError(w, InternalServerErrorMsg, 500)
 40		return
 41	}
 42	allUsers, err := getUsers()
 43	if err != nil {
 44		log.Println(err)
 45		renderError(w, InternalServerErrorMsg, 500)
 46		return
 47	}
 48	data := struct {
 49		Domain    string
 50		PageTitle string
 51		Files     []*File
 52		Users     []string
 53	}{c.RootDomain, c.SiteTitle, indexFiles, allUsers}
 54	err = t.ExecuteTemplate(w, "index.html", data)
 55	if err != nil {
 56		log.Println(err)
 57		renderError(w, InternalServerErrorMsg, 500)
 58		return
 59	}
 60
 61}
 62
 63func editFileHandler(w http.ResponseWriter, r *http.Request) {
 64	// read file content. create if dne
 65	// authUser := "alex"
 66	if r.Method == "GET" {
 67		data := struct {
 68			FileName  string
 69			FileText  string
 70			PageTitle string
 71		}{"filename", "filetext", c.SiteTitle}
 72		err := t.ExecuteTemplate(w, "edit_file.html", data)
 73		if err != nil {
 74			log.Println(err)
 75			renderError(w, InternalServerErrorMsg, 500)
 76			return
 77		}
 78	} else if r.Method == "POST" {
 79	}
 80}
 81
 82func deleteFileHandler(w http.ResponseWriter, r *http.Request) {
 83	if r.Method == "POST" {
 84	}
 85}
 86
 87func mySiteHandler(w http.ResponseWriter, r *http.Request) {
 88	authUser := "alex"
 89	// check auth
 90	files, _ := getUserFiles(authUser)
 91	data := struct {
 92		Domain    string
 93		PageTitle string
 94		AuthUser  string
 95		Files     []*File
 96	}{c.RootDomain, c.SiteTitle, authUser, files}
 97	_ = t.ExecuteTemplate(w, "my_site.html", data)
 98}
 99
100func loginHandler(w http.ResponseWriter, r *http.Request) {
101	if r.Method == "GET" {
102		// show page
103		data := struct {
104			Error     string
105			PageTitle string
106		}{"", "Login"}
107		err := t.ExecuteTemplate(w, "login.html", data)
108		if err != nil {
109			log.Println(err)
110			renderError(w, InternalServerErrorMsg, 500)
111			return
112		}
113	} else if r.Method == "POST" {
114		r.ParseForm()
115		name := r.Form.Get("username")
116		password := r.Form.Get("password")
117		err := checkAuth(name, password)
118		if err == nil {
119			log.Println("logged in")
120			// redirect home
121		} else {
122			data := struct {
123				Error     string
124				PageTitle string
125			}{"Invalid login or password", c.SiteTitle}
126			err := t.ExecuteTemplate(w, "login.html", data)
127			if err != nil {
128				log.Println(err)
129				renderError(w, InternalServerErrorMsg, 500)
130				return
131			}
132		}
133		// create session
134		// redirect home
135		// verify login
136		// check for errors
137	}
138}
139
140func registerHandler(w http.ResponseWriter, r *http.Request) {
141	if r.Method == "GET" {
142		data := struct {
143			Domain    string
144			Errors    []string
145			PageTitle string
146		}{c.RootDomain, nil, "Register"}
147		err := t.ExecuteTemplate(w, "register.html", data)
148		if err != nil {
149			log.Println(err)
150			renderError(w, InternalServerErrorMsg, 500)
151			return
152		}
153	} else if r.Method == "POST" {
154	}
155}
156
157// Server a user's file
158func userFile(w http.ResponseWriter, r *http.Request) {
159	userName := strings.Split(r.Host, ".")[0]
160	fileName := path.Join(c.FilesDirectory, userName, filepath.Clean(r.URL.Path))
161	extension := path.Ext(fileName)
162	if r.URL.Path == "/static/style.css" {
163		http.ServeFile(w, r, path.Join(c.TemplatesDirectory, "static/style.css"))
164	}
165	if extension == ".gmi" || extension == ".gemini" {
166		// covert to html
167		stat, _ := os.Stat(fileName)
168		file, _ := os.Open(fileName)
169		htmlString := gmi.Parse(file).HTML()
170		reader := strings.NewReader(htmlString)
171		w.Header().Set("Content-Type", "text/html")
172		http.ServeContent(w, r, fileName, stat.ModTime(), reader)
173	} else {
174		http.ServeFile(w, r, fileName)
175	}
176}
177
178func runHTTPServer() {
179	log.Println("Running http server")
180	var err error
181	t, err = template.ParseGlob("./templates/*.html") // TODO make template dir configruable
182	if err != nil {
183		log.Fatal(err)
184	}
185	serveMux := http.NewServeMux()
186
187	serveMux.HandleFunc(c.RootDomain+"/", rootHandler)
188	serveMux.HandleFunc(c.RootDomain+"/my_site", mySiteHandler)
189	serveMux.HandleFunc(c.RootDomain+"/edit/", editFileHandler)
190	// serveMux.HandleFunc(c.RootDomain+"/upload/", uploadFilesHandler)
191	serveMux.HandleFunc(c.RootDomain+"/login", loginHandler)
192	serveMux.HandleFunc(c.RootDomain+"/register", registerHandler)
193	serveMux.HandleFunc(c.RootDomain+"/delete/", deleteFileHandler)
194
195	// TODO rate limit login https://github.com/ulule/limiter
196
197	wrapped := handlers.LoggingHandler(os.Stdout, serveMux)
198
199	// handle user files based on subdomain
200	serveMux.HandleFunc("/", userFile)
201	// login+register functions
202	srv := &http.Server{
203		ReadTimeout:  5 * time.Second,
204		WriteTimeout: 10 * time.Second,
205		IdleTimeout:  120 * time.Second,
206		Addr:         ":8080",
207		// TLSConfig:    tlsConfig,
208		Handler: wrapped,
209	}
210	log.Fatal(srv.ListenAndServe())
211}