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