Files
portfolio/main.go

114 lines
3.1 KiB
Go

package main
import (
_ "embed"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"github.com/a-h/templ"
"github.com/moh682/portfolio/components"
"github.com/moh682/portfolio/domain"
"github.com/moh682/portfolio/middlewares"
)
func templHandlerWithOptions(component templ.Component) http.Handler {
return templ.Handler(component, templ.WithStatus(200), templ.WithContentType("text/html"))
}
func rootHandler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler.ServeHTTP(w, r)
})
}
// SafeFileServer wraps http.FileServer to only serve specific file types from specific directories
func SafeFileServer(root http.FileSystem) http.Handler {
fileServer := http.FileServer(root)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Set security headers
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-XSS-Protection", "1; mode=block")
log.Println("assets handler", r.URL.Path)
log.Println(w.Header())
// Only allow specific file extensions
if allowed := func(path string) bool {
allowedExts := map[string]string{
".js": "text/javascript;charset=utf-8",
".css": "text/css;charset=utf-8",
".png": "image/png",
".webp": "image/webp",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".svg": "image/svg+xml",
".ico": "image/x-icon",
}
for ext, contentType := range allowedExts {
if strings.HasSuffix(path, ext) {
w.Header().Set("Content-Type", contentType)
return true
}
}
return false
}(r.URL.Path); !allowed {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
fileServer.ServeHTTP(w, r)
})
}
//go:embed assets/json/experiences.json
var embeddedExperiences []byte
//go:embed assets/json/projects.json
var embeddedProjects []byte
func main() {
server := http.NewServeMux()
// Serve static files with restrictions
staticFS := http.Dir("assets")
server.Handle("GET /assets/", http.StripPrefix("/assets/", SafeFileServer(staticFS)))
server.Handle("GET /about", templHandlerWithOptions(components.AboutPage()))
var exps []domain.Experience
err := json.Unmarshal(embeddedExperiences, &exps)
if err != nil {
log.Panic(err)
}
server.Handle("GET /experiences", templHandlerWithOptions(components.ExperiencesPage(exps)))
var projects []domain.Project
err = json.Unmarshal(embeddedProjects, &projects)
if err != nil {
log.Panic(err)
}
server.Handle("GET /projects", templHandlerWithOptions(components.ProjectsPage(projects)))
server.Handle("GET /contact", templHandlerWithOptions(components.ContactPage()))
server.Handle("GET /{$}", templHandlerWithOptions(components.LandingPage()))
// 404 fallback page
server.Handle("GET /", templ.Handler(components.NotFound(), templ.WithStatus(404), templ.WithContentType("text/html")))
// TODO: add Blogs to my portfolio website
// server.Handle("GET /blogs", templHandlerWithOptions(components.AboutPage()))
fmt.Println("Listening on :8080")
log.Panic(
http.ListenAndServe(":8080",
middlewares.Combine(
middlewares.Logger(server),
),
),
)
}