diff --git a/api/v1/admin/admin.go b/api/v1/admin/admin.go index b1c2244..69570bb 100644 --- a/api/v1/admin/admin.go +++ b/api/v1/admin/admin.go @@ -2,27 +2,22 @@ package admin import ( "context" - "encoding/json" "fmt" "math/rand" "net/http" - "net/url" "os" "strings" "time" "arimelody.me/arimelody.me/discord" + "arimelody.me/arimelody.me/global" ) type ( Session struct { UserID string Token string - } - - loginData struct { - UserID string - Password string + Expires int64 } ) @@ -45,16 +40,23 @@ func CreateSession(UserID string) Session { return Session{ UserID: UserID, Token: string(generateToken()), + Expires: time.Now().Add(24 * time.Hour).Unix(), } } func Handler() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Println(r.URL.Path) + mux := http.NewServeMux() + mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) - w.Write([]byte("hello admin!")) - }) + w.Write([]byte("hello /admin!")) + })) + mux.Handle("/callback", global.HTTPLog(OAuthCallbackHandler())) + mux.Handle("/login", global.HTTPLog(LoginHandler())) + mux.Handle("/verify", global.HTTPLog(AuthorisedHandler(VerifyHandler()))) + mux.Handle("/logout", global.HTTPLog(AuthorisedHandler(LogoutHandler()))) + + return mux } func AuthorisedHandler(next http.Handler) http.Handler { @@ -73,6 +75,17 @@ func AuthorisedHandler(next http.Handler) http.Handler { var session *Session for _, s := range sessions { + if s.Expires < time.Now().Unix() { + // expired session. remove it from the list! + new_sessions := []*Session{} + for _, ns := range sessions { + if ns.Token == s.Token { + continue + } + new_sessions = append(new_sessions, ns) + } + continue + } if s.Token == auth { session = s break diff --git a/global/global.go b/global/global.go index bfc9db8..f2916d5 100644 --- a/global/global.go +++ b/global/global.go @@ -1,23 +1,14 @@ package global import ( + "fmt" "net/http" + "strconv" "time" ) var LAST_MODIFIED = time.Now() -var MimeTypes = map[string]string{ - "css": "text/css; charset=utf-8", - "png": "image/png", - "jpg": "image/jpg", - "webp": "image/webp", - "html": "text/html", - "asc": "text/plain", - "pub": "text/plain", - "js": "application/javascript", -} - func IsModified(req *http.Request, last_modified time.Time) bool { if len(req.Header["If-Modified-Since"]) == 0 || len(req.Header["If-Modified-Since"][0]) == 0 { return true @@ -32,3 +23,37 @@ func IsModified(req *http.Request, last_modified time.Time) bool { return false } +type loggingResponseWriter struct { + http.ResponseWriter + code int +} + +func (lw *loggingResponseWriter) WriteHeader(code int) { + lw.code = code + lw.ResponseWriter.WriteHeader(code) +} + +func HTTPLog(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + lw := loggingResponseWriter{w, http.StatusOK} + + next.ServeHTTP(&lw, r) + + after := time.Now() + difference := (after.Nanosecond() - start.Nanosecond()) / 1_000_000 + elapsed := "<1" + if difference >= 1 { + elapsed = strconv.Itoa(difference) + } + + fmt.Printf("[%s] %s %s - %d (%sms) (%s)\n", + after.Format(time.UnixDate), + r.Method, + r.URL.Path, + lw.code, + elapsed, + r.Header["User-Agent"][0]) + }) +} diff --git a/main.go b/main.go index 8cb355f..5ac2ce9 100644 --- a/main.go +++ b/main.go @@ -5,89 +5,15 @@ import ( "html/template" "log" "net/http" - "strconv" - "time" + "path/filepath" "arimelody.me/arimelody.me/api/v1/admin" "arimelody.me/arimelody.me/api/v1/music" + "arimelody.me/arimelody.me/global" ) const DEFAULT_PORT int = 8080 -var base_template = template.Must(template.ParseFiles( - "views/base.html", - "views/header.html", - "views/footer.html", - "views/prideflag.html", -)) - -func log_request(req *http.Request, code int, start_time time.Time) { - now := time.Now() - difference := (now.Nanosecond() - start_time.Nanosecond()) / 1_000_000 - elapsed := "<1" - if difference >= 1 { - elapsed = strconv.Itoa(difference) - } - - fmt.Printf("[%s] %s %s - %d (%sms) (%s)\n", - now.Format(time.UnixDate), - req.Method, - req.URL.Path, - code, - elapsed, - req.Header["User-Agent"][0]) -} - -// func handle_request(res http.ResponseWriter, req *http.Request) { -// uri := req.URL.Path -// start_time := time.Now() -// -// res.Header().Set("Server", "arimelody.me") -// -// code := func(res http.ResponseWriter, req *http.Request) int { -// var root = template.Must(base_template.Clone()) -// -// if req.URL.Path == "/" { -// return handle_index(res, req, root) -// } -// -// if uri == "/music" || uri == "/music/" { -// return handle_music(res, req, root) -// } -// -// if strings.HasPrefix(uri, "/music/") { -// return handle_music_gateway(res, req, root) -// } -// -// if strings.HasPrefix(uri, "/admin") { -// return admin.Handle(res, req, root) -// } -// -// if strings.HasPrefix(uri, "/api") { -// return api.Handle(res, req, root) -// } -// -// return static_handler(res, req, root) -// }(res, req) -// -// log_request(req, code, start_time) -// } -// -// func handle_index(res http.ResponseWriter, req *http.Request, root *template.Template) int { -// if !global.IsModified(req, global.LAST_MODIFIED) { -// res.WriteHeader(304) -// return 304 -// } -// -// index_template := template.Must(root.ParseFiles("views/index.html")) -// err := index_template.Execute(res, nil) -// if err != nil { -// http.Error(res, err.Error(), http.StatusInternalServerError) -// return 500 -// } -// return 200 -// } -// // func handle_music(res http.ResponseWriter, req *http.Request, root *template.Template) int { // if !global.IsModified(req, global.LAST_MODIFIED) { // res.WriteHeader(304) @@ -102,7 +28,7 @@ func log_request(req *http.Request, code int, start_time time.Time) { // } // return 200 // } -// + // func handle_music_gateway(res http.ResponseWriter, req *http.Request, root *template.Template) int { // if !global.IsModified(req, global.LAST_MODIFIED) { // res.WriteHeader(304) @@ -122,69 +48,6 @@ func log_request(req *http.Request, code int, start_time time.Time) { // } // return 200 // } -// -// func static_handler(res http.ResponseWriter, req *http.Request, root *template.Template) int { -// filename := "public/" + req.URL.Path[1:] -// -// // check the file's metadata -// info, err := os.Stat(filename) -// if err != nil { -// return handle_not_found(res, req, root) -// } -// -// if !global.IsModified(req, info.ModTime()) { -// res.WriteHeader(304) -// return 304 -// } -// -// // set Last-Modified to file modification date -// res.Header().Set("Last-Modified", info.ModTime().Format(http.TimeFormat)) -// -// // read the file -// body, err := os.ReadFile(filename) -// if err != nil { -// http.Error(res, err.Error(), http.StatusInternalServerError) -// return 500 -// } -// -// // setting MIME types -// filetype := filename[strings.LastIndex(filename, ".")+1:] -// if mime_type, ok := global.MimeTypes[filetype]; ok { -// res.Header().Set("Content-Type", mime_type) -// } else { -// res.Header().Set("Content-Type", "text/plain; charset=utf-8") -// } -// -// res.Write([]byte(body)) -// return 200 -// } -// -// func handle_not_found(res http.ResponseWriter, req *http.Request, root *template.Template) int { -// type ErrorData struct { -// Target string -// } -// error_data := ErrorData{ Target: req.URL.Path } -// res.WriteHeader(404); -// error_template := template.Must(root.ParseFiles("views/404.html")) -// err := error_template.Execute(res, error_data) -// if err != nil { -// http.Error(res, err.Error(), http.StatusInternalServerError) -// return 500 -// } -// return 404 -// } -// -// func parse_markdown(md []byte) []byte { -// extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock -// p := parser.NewWithExtensions(extensions) -// doc := p.Parse(md) -// -// htmlFlags := html.CommonFlags -// opts := html.RendererOptions{Flags: htmlFlags} -// renderer := html.NewRenderer(opts) -// -// return markdown.Render(doc, renderer) -// } func main() { db := InitDatabase() @@ -204,13 +67,22 @@ func main() { mux := http.NewServeMux() - mux.Handle("/api/v1/admin", admin.Handler()) - mux.Handle("/api/v1/admin/login", admin.LoginHandler()) - mux.Handle("/api/v1/admin/callback", admin.OAuthCallbackHandler()) - mux.Handle("/api/v1/admin/verify", admin.AuthorisedHandler(admin.VerifyHandler())) - mux.Handle("/api/v1/admin/logout", admin.AuthorisedHandler(admin.LogoutHandler())) + mux.Handle("/api/v1/admin/", global.HTTPLog(http.StripPrefix("/api/v1/admin", admin.Handler()))) + mux.Handle("/", http.FileServer(http.Dir("./public"))) + // mux.Handle("/", global.HTTPLog(http.HandlerFunc(serveTemplate))) port := DEFAULT_PORT fmt.Printf("now serving at http://127.0.0.1:%d\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), mux)) } + +func serveTemplate(w http.ResponseWriter, r *http.Request) { + lp_base := filepath.Join("views", "layout.html") + lp_header := filepath.Join("views", "header.html") + lp_footer := filepath.Join("views", "footer.html") + lp_prideflag := filepath.Join("views", "prideflag.html") + fp := filepath.Join("views", filepath.Clean(r.URL.Path)) + + template, _ := template.ParseFiles(lp_base, lp_header, lp_footer, lp_prideflag, fp) + template.ExecuteTemplate(w, "layout", nil) +} diff --git a/schema.sql b/schema.sql index 5262ae9..3a4bf98 100644 --- a/schema.sql +++ b/schema.sql @@ -19,7 +19,7 @@ ALTER TABLE musicreleases ADD CONSTRAINT musicreleases_pk PRIMARY KEY (id); CREATE TABLE IF NOT EXISTS musiclinks ( release character varying(64) NOT NULL, name text NOT NULL, - url text, + url text ); ALTER TABLE musiclinks ADD CONSTRAINT musiclinks_pk PRIMARY KEY (release, name); @@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS musiccredits ( release character varying(64) NOT NULL, artist text NOT NULL, role text, - is_primary boolean, + is_primary boolean ); ALTER TABLE musiccredits ADD CONSTRAINT musiccredits_pk PRIMARY KEY (release, artist); @@ -37,7 +37,7 @@ CREATE TABLE IF NOT EXISTS musictracks ( title text NOT NULL, description text, lyrics text, - preview_url text, + preview_url text ); ALTER TABLE musictracks ADD CONSTRAINT musictracks_pk PRIMARY KEY (release, number); diff --git a/views/base.html b/views/layout.html similarity index 100% rename from views/base.html rename to views/layout.html