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) // Only allow specific file extensions if allowed := func(path string) bool { allowedExts := map[string]bool{ ".js": true, ".css": true, ".png": true, ".webp": true, ".jpg": true, ".jpeg": true, ".svg": true, ".ico": true, } for ext := range allowedExts { if strings.HasSuffix(path, ext) { 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), ), ), ) }