all repos — flounder @ b18554dca962686cbb07d660ec3bf2875bcd8f84

A small site builder for the Gemini protocol

http.go (view raw)

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