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}