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
19const InternalServerErrorMsg = "500: Internal Server Error"
20
21func renderError(w http.ResponseWriter, errorMsg string, statusCode int) {
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 authUser := "alex"
65 fileName := filepath.Clean(r.URL.Path[len("/edit/"):])
66 filePath := path.Join(c.FilesDirectory, authUser, fileName)
67 if r.Method == "GET" {
68 err := checkIfValidFile(filePath, nil)
69 if err != nil {
70 log.Println(err)
71 renderError(w, err.Error(), 400)
72 return
73 }
74 f, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644)
75 defer f.Close()
76 fileBytes, err := ioutil.ReadAll(f)
77 if err != nil {
78 log.Println(err)
79 renderError(w, InternalServerErrorMsg, 500)
80 return
81 }
82 data := struct {
83 FileName string
84 FileText string
85 PageTitle string
86 }{fileName, string(fileBytes), c.SiteTitle}
87 err = t.ExecuteTemplate(w, "edit_file.html", data)
88 if err != nil {
89 log.Println(err)
90 renderError(w, InternalServerErrorMsg, 500)
91 return
92 }
93 } else if r.Method == "POST" {
94 // get post body
95 r.ParseForm()
96 fileBytes := []byte(r.Form.Get("file_text"))
97 err := checkIfValidFile(filePath, fileBytes)
98 if err != nil {
99 log.Println(err)
100 renderError(w, err.Error(), 400)
101 return
102 }
103 err = ioutil.WriteFile(filePath, fileBytes, 0644)
104 if err != nil {
105 log.Println(err)
106 renderError(w, InternalServerErrorMsg, 500)
107 return
108 }
109 http.Redirect(w, r, "/my_site", 302)
110 }
111}
112
113func deleteFileHandler(w http.ResponseWriter, r *http.Request) {
114 authUser := "alex"
115 fileName := filepath.Clean(r.URL.Path[len("/delete/"):])
116 filePath := path.Join(c.FilesDirectory, authUser, fileName)
117 if r.Method == "POST" {
118 os.Remove(filePath) // suppress error
119 http.Redirect(w, r, "/my_site", 302)
120 }
121}
122
123func mySiteHandler(w http.ResponseWriter, r *http.Request) {
124 authUser := "alex"
125 // check auth
126 files, _ := getUserFiles(authUser)
127 data := struct {
128 Domain string
129 PageTitle string
130 AuthUser string
131 Files []*File
132 }{c.RootDomain, c.SiteTitle, authUser, files}
133 _ = t.ExecuteTemplate(w, "my_site.html", data)
134}
135
136func loginHandler(w http.ResponseWriter, r *http.Request) {
137 if r.Method == "GET" {
138 // show page
139 data := struct {
140 Error string
141 PageTitle string
142 }{"", "Login"}
143 err := t.ExecuteTemplate(w, "login.html", data)
144 if err != nil {
145 log.Println(err)
146 renderError(w, InternalServerErrorMsg, 500)
147 return
148 }
149 } else if r.Method == "POST" {
150 r.ParseForm()
151 name := r.Form.Get("username")
152 password := r.Form.Get("password")
153 err := checkAuth(name, password)
154 if err == nil {
155 log.Println("logged in")
156 // redirect home
157 } else {
158 data := struct {
159 Error string
160 PageTitle string
161 }{"Invalid login or password", c.SiteTitle}
162 err := t.ExecuteTemplate(w, "login.html", data)
163 if err != nil {
164 log.Println(err)
165 renderError(w, InternalServerErrorMsg, 500)
166 return
167 }
168 }
169 // create session
170 // redirect home
171 // verify login
172 // check for errors
173 }
174}
175
176func registerHandler(w http.ResponseWriter, r *http.Request) {
177 if r.Method == "GET" {
178 data := struct {
179 Domain string
180 Errors []string
181 PageTitle string
182 }{c.RootDomain, nil, "Register"}
183 err := t.ExecuteTemplate(w, "register.html", data)
184 if err != nil {
185 log.Println(err)
186 renderError(w, InternalServerErrorMsg, 500)
187 return
188 }
189 } else if r.Method == "POST" {
190 }
191}
192
193// Server a user's file
194func userFile(w http.ResponseWriter, r *http.Request) {
195 userName := strings.Split(r.Host, ".")[0]
196 fileName := path.Join(c.FilesDirectory, userName, filepath.Clean(r.URL.Path))
197 extension := path.Ext(fileName)
198 if r.URL.Path == "/static/style.css" {
199 http.ServeFile(w, r, path.Join(c.TemplatesDirectory, "static/style.css"))
200 }
201 if extension == ".gmi" || extension == ".gemini" {
202 // covert to html
203 stat, _ := os.Stat(fileName)
204 file, _ := os.Open(fileName)
205 htmlString := gmi.Parse(file).HTML()
206 reader := strings.NewReader(htmlString)
207 w.Header().Set("Content-Type", "text/html")
208 http.ServeContent(w, r, fileName, stat.ModTime(), reader)
209 } else {
210 http.ServeFile(w, r, fileName)
211 }
212}
213
214func runHTTPServer() {
215 log.Println("Running http server")
216 var err error
217 t, err = template.ParseGlob(path.Join(c.TemplatesDirectory, "*.html"))
218 if err != nil {
219 log.Fatal(err)
220 }
221 serveMux := http.NewServeMux()
222
223 serveMux.HandleFunc(c.RootDomain+"/", rootHandler)
224 serveMux.HandleFunc(c.RootDomain+"/my_site", mySiteHandler)
225 serveMux.HandleFunc(c.RootDomain+"/edit/", editFileHandler)
226 // serveMux.HandleFunc(c.RootDomain+"/upload/", uploadFilesHandler)
227 serveMux.HandleFunc(c.RootDomain+"/login", loginHandler)
228 serveMux.HandleFunc(c.RootDomain+"/register", registerHandler)
229 serveMux.HandleFunc(c.RootDomain+"/delete/", deleteFileHandler)
230
231 // TODO rate limit login https://github.com/ulule/limiter
232
233 wrapped := handlers.LoggingHandler(os.Stdout, serveMux)
234
235 // handle user files based on subdomain
236 serveMux.HandleFunc("/", userFile)
237 // login+register functions
238 srv := &http.Server{
239 ReadTimeout: 5 * time.Second,
240 WriteTimeout: 10 * time.Second,
241 IdleTimeout: 120 * time.Second,
242 Addr: ":8080",
243 // TLSConfig: tlsConfig,
244 Handler: wrapped,
245 }
246 log.Fatal(srv.ListenAndServe())
247}