package main import ( "context" "html/template" "log" "net/http" "os" "os/signal" "strings" "syscall" "time" ) var templates = make(map[string]*template.Template) // pageTitles maps each edition page to its SEO title. var pageTitles = map[string]string{ "server": "Arcline Server — Hardened SOC / Production Host", "workstation": "Arcline Workstation — Security Analyst / Blue-Team Desktop", "cloud": "Arcline Cloud — Security-First Cloud Images", } // mustParse parses a template set and fails fast at startup on any error. func mustParse(name string, files ...string) *template.Template { t, err := template.ParseFiles(files...) if err != nil { log.Fatalf("failed to parse %s template: %v", name, err) } return t } // contextKey is used to pass per-request log extras from handlers to the logging middleware. type contextKey struct{} type logContext struct{ extra string } func main() { port := os.Getenv("PORT") if port == "" { port = "8080" } // Parse templates once at startup. templates["index"] = mustParse("index", "templates/base.html", "templates/index.html", "templates/partials/nav.html", "templates/partials/footer.html", "templates/partials/waitlist_form.html", ) for _, name := range []string{"server", "workstation", "cloud"} { templates[name] = mustParse(name, "templates/base.html", "templates/"+name+".html", "templates/partials/nav.html", "templates/partials/footer.html", "templates/partials/waitlist_form.html", ) } waitlist, err := template.ParseFiles("templates/partials/waitlist_confirmation.html") if err != nil { log.Fatalf("failed to parse waitlist template: %v", err) } templates["waitlist"] = waitlist mux := http.NewServeMux() // Serve static assets fs := http.FileServer(http.Dir("static")) mux.Handle("/static/", http.StripPrefix("/static/", fs)) // Page routes mux.HandleFunc("/", handleIndex) mux.HandleFunc("/server", handlePage("server")) mux.HandleFunc("/workstation", handlePage("workstation")) mux.HandleFunc("/cloud", handlePage("cloud")) mux.HandleFunc("/healthz", handleHealthz) // HTMX partial routes mux.HandleFunc("/partials/waitlist", handleWaitlist) // Wrap with logging (outermost) and security headers (innermost). handler := loggingMiddleware(securityHeaders(mux)) srv := &http.Server{ Addr: ":" + port, Handler: handler, ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } // Graceful shutdown on SIGINT / SIGTERM. go func() { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) <-sig log.Println("shutting down…") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Printf("shutdown error: %v", err) } }() log.Printf("Arcline Project server starting on :%s", port) if err := srv.ListenAndServe(); err != http.ErrServerClosed { log.Fatalf("server failed: %v", err) } log.Println("server stopped") } // securityHeaders adds basic security headers to every response. func securityHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Frame-Options", "DENY") w.Header().Set("Referrer-Policy", "no-referrer") w.Header().Set("Permissions-Policy", "camera=(), microphone=(), geolocation=()") next.ServeHTTP(w, r) }) } // responseWriter wraps http.ResponseWriter to capture the status code and bytes written. type responseWriter struct { http.ResponseWriter status int bytes int } func (rw *responseWriter) WriteHeader(code int) { rw.status = code rw.ResponseWriter.WriteHeader(code) } func (rw *responseWriter) Write(b []byte) (int, error) { if rw.status == 0 { rw.status = http.StatusOK } n, err := rw.ResponseWriter.Write(b) rw.bytes += n return n, err } // loggingMiddleware logs every request with IP, method, path, status, size, duration, referer, and user-agent. func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { lc := &logContext{} ctx := context.WithValue(r.Context(), contextKey{}, lc) r = r.WithContext(ctx) start := time.Now() rw := &responseWriter{ResponseWriter: w, status: 0} next.ServeHTTP(rw, r) ip := r.RemoteAddr if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" { ip = fwd } ref := r.Referer() if ref == "" { ref = "-" } ua := r.UserAgent() if ua == "" { ua = "-" } duration := time.Since(start) if lc.extra != "" { log.Printf("%s %s %s → %d (%dB) %v ref:%q ua:%q | %s", ip, r.Method, r.URL.Path, rw.status, rw.bytes, duration, ref, ua, lc.extra) } else { log.Printf("%s %s %s → %d (%dB) %v ref:%q ua:%q", ip, r.Method, r.URL.Path, rw.status, rw.bytes, duration, ref, ua) } }) } func handleHealthz(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) } func handleIndex(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") data := map[string]interface{}{ "Title": "Arcline OS — Hardened Linux for Security Practitioners", "Active": "", "Edition": "server", } if err := templates["index"].ExecuteTemplate(w, "base", data); err != nil { log.Printf("render error: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } // handlePage renders one of the edition pages (server, workstation, cloud). func handlePage(page string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/"+page { http.NotFound(w, r) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") data := map[string]interface{}{ "Title": pageTitles[page], "Active": page, "Edition": page, } if err := templates[page].ExecuteTemplate(w, "base", data); err != nil { log.Printf("render error: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } } func handleWaitlist(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } if err := r.ParseForm(); err != nil { http.Error(w, "Bad Request", http.StatusBadRequest) return } email := strings.TrimSpace(r.FormValue("email")) if email == "" || !strings.Contains(email, "@") { http.Error(w, "Invalid email address", http.StatusBadRequest) return } // Signal the logging middleware that this was a waitlist signup. if lc, ok := r.Context().Value(contextKey{}).(*logContext); ok { lc.extra = "signup" } w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := templates["waitlist"].Execute(w, map[string]string{"Email": email}); err != nil { log.Printf("render error: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }