commit
This commit is contained in:
58
cmd/api/main.go
Normal file
58
cmd/api/main.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"go-htmx/internal/server"
|
||||
)
|
||||
|
||||
func gracefulShutdown(apiServer *http.Server, done chan bool) {
|
||||
// Create context that listens for the interrupt signal from the OS.
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
// Listen for the interrupt signal.
|
||||
<-ctx.Done()
|
||||
|
||||
log.Println("shutting down gracefully, press Ctrl+C again to force")
|
||||
stop() // Allow Ctrl+C to force shutdown
|
||||
|
||||
// The context is used to inform the server it has 5 seconds to finish
|
||||
// the request it is currently handling
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := apiServer.Shutdown(ctx); err != nil {
|
||||
log.Printf("Server forced to shutdown with error: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Server exiting")
|
||||
|
||||
// Notify the main goroutine that the shutdown is complete
|
||||
done <- true
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
server := server.NewServer()
|
||||
|
||||
// Create a done channel to signal when the shutdown is complete
|
||||
done := make(chan bool, 1)
|
||||
|
||||
// Run graceful shutdown in a separate goroutine
|
||||
go gracefulShutdown(server, done)
|
||||
|
||||
err := server.ListenAndServe()
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
panic(fmt.Sprintf("http server error: %s", err))
|
||||
}
|
||||
|
||||
// Wait for the graceful shutdown to complete
|
||||
<-done
|
||||
log.Println("Graceful shutdown complete.")
|
||||
}
|
||||
1133
cmd/web/assets/css/output.css
Normal file
1133
cmd/web/assets/css/output.css
Normal file
File diff suppressed because it is too large
Load Diff
3521
cmd/web/assets/js/htmx.min.js
vendored
Normal file
3521
cmd/web/assets/js/htmx.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
57
cmd/web/base.templ
Normal file
57
cmd/web/base.templ
Normal file
@@ -0,0 +1,57 @@
|
||||
package web
|
||||
|
||||
templ Base() {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-screen bg-crema-50">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<title>Latin Garden</title>
|
||||
<link href="assets/css/output.css" rel="stylesheet"/>
|
||||
<script src="assets/js/htmx.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="min-h-screen font-latin text-ink-800">
|
||||
|
||||
<!-- STICKY NAVBAR -->
|
||||
<nav class="sticky top-0 z-50 bg-crema-100 border-b border-crema-200 shadow-md transition-shadow duration-300">
|
||||
<div class="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||
|
||||
<a href="/" class="text-xl font-semibold tracking-wide text-terracotta-500 hover:text-terracotta-600 transition-colors duration-200">
|
||||
Latin Garden
|
||||
</a>
|
||||
|
||||
<div class="hidden md:flex space-x-8 text-ink-700">
|
||||
<a href="/" class="hover:text-terracotta-500 transition-colors duration-200 relative group">
|
||||
Home
|
||||
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-terracotta-500 group-hover:w-full transition-all duration-300"></span>
|
||||
</a>
|
||||
<a href="#plants" class="hover:text-terracotta-500 transition-colors duration-200 relative group">
|
||||
Plants
|
||||
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-terracotta-500 group-hover:w-full transition-all duration-300"></span>
|
||||
</a>
|
||||
<a href="#about" class="hover:text-terracotta-500 transition-colors duration-200 relative group">
|
||||
About
|
||||
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-terracotta-500 group-hover:w-full transition-all duration-300"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile toggle -->
|
||||
<div class="md:hidden">
|
||||
<button
|
||||
hx-get="/"
|
||||
class="text-terracotta-500 text-2xl hover:text-terracotta-600 transition-colors duration-200">
|
||||
☰
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- CONTENT -->
|
||||
<main class="max-w-6xl mx-auto px-6 py-10">
|
||||
{ children... }
|
||||
</main>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
48
cmd/web/base_templ.go
Normal file
48
cmd/web/base_templ.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.977
|
||||
package web
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
func Base() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\" class=\"h-screen bg-crema-50\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Latin Garden</title><link href=\"assets/css/output.css\" rel=\"stylesheet\"><script src=\"assets/js/htmx.min.js\"></script></head><body class=\"min-h-screen font-latin text-ink-800\"><!-- STICKY NAVBAR --><nav class=\"sticky top-0 z-50 bg-crema-100 border-b border-crema-200 shadow-md transition-shadow duration-300\"><div class=\"max-w-6xl mx-auto px-6 py-4 flex items-center justify-between\"><a href=\"/\" class=\"text-xl font-semibold tracking-wide text-terracotta-500 hover:text-terracotta-600 transition-colors duration-200\">Latin Garden</a><div class=\"hidden md:flex space-x-8 text-ink-700\"><a href=\"/\" class=\"hover:text-terracotta-500 transition-colors duration-200 relative group\">Home <span class=\"absolute bottom-0 left-0 w-0 h-0.5 bg-terracotta-500 group-hover:w-full transition-all duration-300\"></span></a> <a href=\"#plants\" class=\"hover:text-terracotta-500 transition-colors duration-200 relative group\">Plants <span class=\"absolute bottom-0 left-0 w-0 h-0.5 bg-terracotta-500 group-hover:w-full transition-all duration-300\"></span></a> <a href=\"#about\" class=\"hover:text-terracotta-500 transition-colors duration-200 relative group\">About <span class=\"absolute bottom-0 left-0 w-0 h-0.5 bg-terracotta-500 group-hover:w-full transition-all duration-300\"></span></a></div><!-- Mobile toggle --><div class=\"md:hidden\"><button hx-get=\"/\" class=\"text-terracotta-500 text-2xl hover:text-terracotta-600 transition-colors duration-200\">☰</button></div></div></nav><!-- CONTENT --><main class=\"max-w-6xl mx-auto px-6 py-10\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</main></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
6
cmd/web/efs.go
Normal file
6
cmd/web/efs.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package web
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed "assets"
|
||||
var Files embed.FS
|
||||
21
cmd/web/hello.go
Normal file
21
cmd/web/hello.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func HelloWebHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
name := r.FormValue("name")
|
||||
component := HelloPost(name)
|
||||
err = component.Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Fatalf("Error rendering in HelloWebHandler: %e", err)
|
||||
}
|
||||
}
|
||||
169
cmd/web/hello.templ
Normal file
169
cmd/web/hello.templ
Normal file
@@ -0,0 +1,169 @@
|
||||
package web
|
||||
|
||||
templ HelloForm() {
|
||||
@Base() {
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="mb-16 text-center py-8 md:py-12">
|
||||
<h1 class="text-4xl md:text-6xl font-semibold text-ink-800 mb-4 animate-fadeIn">
|
||||
Welcome to the Garden
|
||||
</h1>
|
||||
<p class="text-lg text-ink-700 max-w-2xl mx-auto leading-relaxed">
|
||||
A calm collection of plants inspired by southern light and warm earth. Discover the beauty of nature's finest specimens.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- BENTO GRID -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-3 gap-6 auto-rows-[250px] mb-16">
|
||||
|
||||
<!-- Large Feature Image -->
|
||||
<div class="md:col-span-2 md:row-span-2 rounded-2xl overflow-hidden shadow-sm bg-white group cursor-pointer">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1501004318641-b39e6451bec6"
|
||||
alt="Garden landscape"
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110 group-hover:brightness-110"/>
|
||||
</div>
|
||||
|
||||
<!-- Card 1 -->
|
||||
<div class="rounded-2xl bg-oliva-100 p-6 shadow-sm flex flex-col justify-between hover:shadow-lg transition-shadow duration-300 hover:bg-oliva-200">
|
||||
<h2 class="text-xl font-semibold text-ink-800">Sage</h2>
|
||||
<p class="text-ink-700 text-sm">
|
||||
Soft greens that cool the room and bring serenity to your space.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Image Card -->
|
||||
<div class="rounded-2xl overflow-hidden shadow-sm group cursor-pointer">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1465146344425-f00d5f5c8f07"
|
||||
alt="Green plants"
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110 group-hover:brightness-110"/>
|
||||
</div>
|
||||
|
||||
<!-- Form Card -->
|
||||
<div class="md:col-span-2 rounded-2xl bg-crema-100 p-8 shadow-sm hover:shadow-lg transition-shadow duration-300">
|
||||
<h2 class="text-2xl font-semibold text-ink-800 mb-4">
|
||||
Say Hello
|
||||
</h2>
|
||||
|
||||
<form
|
||||
hx-post="/hello"
|
||||
method="POST"
|
||||
hx-target="#hello-container"
|
||||
class="flex flex-col sm:flex-row gap-4">
|
||||
|
||||
<input
|
||||
class="flex-1 bg-white border border-crema-200 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-oliva-300 transition-all duration-300"
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
placeholder="Your name"/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-terracotta-500 hover:bg-terracotta-600 text-white px-6 py-3 rounded-lg transition-all duration-300 hover:shadow-lg active:scale-95">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div id="hello-container" class="mt-6"></div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- FEATURED PLANTS SECTION -->
|
||||
<section id="plants" class="mb-16">
|
||||
<div class="mb-12">
|
||||
<h2 class="text-4xl font-semibold text-ink-800 mb-4">Featured Plants</h2>
|
||||
<p class="text-ink-700 text-lg">Curated selections for your garden</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<!-- Plant Card 1 -->
|
||||
<div class="rounded-2xl overflow-hidden shadow-sm bg-white hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2">
|
||||
<div class="relative h-48 overflow-hidden group">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1520763185298-1b434c919abe"
|
||||
alt="Monstera plant"
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"/>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<h3 class="text-xl font-semibold text-ink-800 mb-2">Monstera Deliciosa</h3>
|
||||
<p class="text-ink-700 text-sm mb-4">A stunning tropical plant with iconic split leaves. Perfect for bright, indirect light.</p>
|
||||
<button class="w-full bg-terracotta-500 hover:bg-terracotta-600 text-white py-2 rounded-lg transition-all duration-300 hover:shadow-md active:scale-95">
|
||||
Learn More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plant Card 2 -->
|
||||
<div class="rounded-2xl overflow-hidden shadow-sm bg-white hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2">
|
||||
<div class="relative h-48 overflow-hidden group">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e"
|
||||
alt="Snake plant"
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"/>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<h3 class="text-xl font-semibold text-ink-800 mb-2">Snake Plant</h3>
|
||||
<p class="text-ink-700 text-sm mb-4">Low-maintenance and air-purifying. Thrives in various light conditions with minimal care.</p>
|
||||
<button class="w-full bg-terracotta-500 hover:bg-terracotta-600 text-white py-2 rounded-lg transition-all duration-300 hover:shadow-md active:scale-95">
|
||||
Learn More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plant Card 3 -->
|
||||
<div class="rounded-2xl overflow-hidden shadow-sm bg-white hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2">
|
||||
<div class="relative h-48 overflow-hidden group">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1518895949257-7621c3c786d7"
|
||||
alt="Pothos plant"
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"/>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<h3 class="text-xl font-semibold text-ink-800 mb-2">Pothos</h3>
|
||||
<p class="text-ink-700 text-sm mb-4">The ultimate climbing vine plant. Beautiful trailing foliage that adapts to any environment.</p>
|
||||
<button class="w-full bg-terracotta-500 hover:bg-terracotta-600 text-white py-2 rounded-lg transition-all duration-300 hover:shadow-md active:scale-95">
|
||||
Learn More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ABOUT SECTION -->
|
||||
<section id="about" class="mb-16 rounded-2xl bg-gradient-to-br from-oliva-50 to-crema-100 p-12 shadow-sm">
|
||||
<div class="max-w-3xl">
|
||||
<h2 class="text-4xl font-semibold text-ink-800 mb-6">About Our Garden</h2>
|
||||
<p class="text-ink-700 text-lg leading-relaxed mb-4">
|
||||
We believe that plants aren't just decorations—they're living companions that transform your space and improve your wellbeing. Our mission is to help you discover the perfect plants for your home or office.
|
||||
</p>
|
||||
<p class="text-ink-700 text-lg leading-relaxed mb-4">
|
||||
Each plant in our collection is carefully selected for its aesthetic beauty and resilience. Whether you're a seasoned gardener or just starting your plant journey, we have something for everyone.
|
||||
</p>
|
||||
<p class="text-ink-700 text-lg leading-relaxed">
|
||||
From tropical wonders to hardy succulents, explore the natural beauty of our curated selection and bring life to your surroundings.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA SECTION -->
|
||||
<section class="text-center py-16 border-t border-crema-200">
|
||||
<h2 class="text-3xl font-semibold text-ink-800 mb-4">Ready to Start Your Garden?</h2>
|
||||
<p class="text-ink-700 mb-8 max-w-xl mx-auto">
|
||||
Join our community of plant enthusiasts and receive care tips, exclusive plant recommendations, and seasonal updates.
|
||||
</p>
|
||||
<button class="bg-terracotta-500 hover:bg-terracotta-600 text-white px-8 py-3 rounded-lg transition-all duration-300 hover:shadow-lg active:scale-95 text-lg font-semibold">
|
||||
Subscribe Now
|
||||
</button>
|
||||
</section>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
templ HelloPost(name string) {
|
||||
<div class="bg-oliva-100 p-4 rounded-xl mt-4 shadow-sm">
|
||||
<p class="text-ink-800">Hello, { name }</p>
|
||||
</div>
|
||||
}
|
||||
100
cmd/web/hello_templ.go
Normal file
100
cmd/web/hello_templ.go
Normal file
File diff suppressed because one or more lines are too long
3
cmd/web/styles/input.css
Normal file
3
cmd/web/styles/input.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
Reference in New Issue
Block a user