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