ai slop ah
This commit is contained in:
@@ -29,7 +29,7 @@ func main() {
|
||||
if err := seed(ctx, database); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("seeded %s with the Northline Studio demo gallery", demoEmail)
|
||||
log.Printf("seeded %s with the Noah Bianchi demo gallery", demoEmail)
|
||||
}
|
||||
|
||||
func seed(ctx context.Context, database *sql.DB) error {
|
||||
@@ -41,7 +41,7 @@ func seed(ctx context.Context, database *sql.DB) error {
|
||||
INSERT INTO users (id, email, password_hash, name)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (email) DO UPDATE SET password_hash = EXCLUDED.password_hash, name = EXCLUDED.name, updated_at = CURRENT_TIMESTAMP
|
||||
`, demoUserID, demoEmail, string(passwordHash), "Northline Studio"); err != nil {
|
||||
`, demoUserID, demoEmail, string(passwordHash), "Noah Bianchi"); err != nil {
|
||||
return fmt.Errorf("seed demo user: %w", err)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func seed(ctx context.Context, database *sql.DB) error {
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
`, demoGalleryID, demoUserID, "emma-james-wedding", "Emma & James", "Emma & James", "An early summer wedding, held close to the water and the people who make it home.", demoCoverID,
|
||||
`{"mode":"light","layout":"editorial","accent":"#ad695b","font":"serif"}`,
|
||||
`{"studioName":"Northline Studio","tagline":"Photographs for keeps.","websiteUrl":"https://example.com","instagramUrl":"https://instagram.com"}`); err != nil {
|
||||
`{"studioName":"Noah Bianchi","tagline":"Photographs for keeps.","websiteUrl":"https://example.com","instagramUrl":"https://instagram.com"}`); err != nil {
|
||||
return fmt.Errorf("seed demo gallery: %w", err)
|
||||
}
|
||||
|
||||
|
||||
+55
-36
@@ -10,17 +10,29 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
_ "github.com/example/sndit/backend/docs"
|
||||
"github.com/example/sndit/backend/internal/auth"
|
||||
"github.com/example/sndit/backend/internal/config"
|
||||
"github.com/example/sndit/backend/internal/db"
|
||||
devtools "github.com/example/sndit/backend/internal/dev"
|
||||
"github.com/example/sndit/backend/internal/downloads"
|
||||
"github.com/example/sndit/backend/internal/galleries"
|
||||
"github.com/example/sndit/backend/internal/gifts"
|
||||
"github.com/example/sndit/backend/internal/media"
|
||||
"github.com/example/sndit/backend/internal/storage"
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
)
|
||||
|
||||
// @title Noah Bianchi Studio API
|
||||
// @version 1.0
|
||||
// @description REST API for photographer galleries and client delivery.
|
||||
// @schemes http https
|
||||
// @BasePath /
|
||||
// @securityDefinitions.apikey studioSession
|
||||
// @in header
|
||||
// @name Cookie
|
||||
// @description Use the studio_session cookie as studio_session=<value>.
|
||||
func main() {
|
||||
cfg := config.Load()
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
@@ -73,23 +85,25 @@ func main() {
|
||||
CookieSecure: cfg.CookieSecure,
|
||||
})
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /health", health)
|
||||
authHandler.RegisterRoutes(mux)
|
||||
galleryHandler.RegisterProtectedRoutes(mux, authService.Require)
|
||||
mediaHandler.RegisterRoutes(mux, authService.Require)
|
||||
galleryHandler.RegisterPublicRoutes(mux)
|
||||
downloadHandler.RegisterRoutes(mux)
|
||||
devHandler.RegisterRoutes(mux, authService.Require)
|
||||
|
||||
// Keep the original public gift endpoint available while the gallery product
|
||||
// uses /api/public/galleries/:slug.
|
||||
legacyGifts := gifts.NewHandler(gifts.NewService(gifts.NewRepository(database)))
|
||||
mux.Handle("/api/gifts/", legacyGifts.Routes())
|
||||
router := gin.New()
|
||||
router.Use(gin.Recovery(), withCORS(cfg.CORSOrigin), withLogging())
|
||||
router.GET("/health", health)
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler,
|
||||
ginSwagger.DocExpansion("none"),
|
||||
ginSwagger.PersistAuthorization(true),
|
||||
))
|
||||
require := authService.Require()
|
||||
authHandler.RegisterRoutes(router)
|
||||
authHandler.RegisterProtectedRoutes(router, require)
|
||||
galleryHandler.RegisterProtectedRoutes(router, require)
|
||||
mediaHandler.RegisterRoutes(router, require)
|
||||
galleryHandler.RegisterPublicRoutes(router)
|
||||
downloadHandler.RegisterRoutes(router)
|
||||
devHandler.RegisterRoutes(router, require)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: ":" + cfg.Port,
|
||||
Handler: withCORS(withLogging(mux), cfg.CORSOrigin),
|
||||
Handler: router,
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
@@ -111,28 +125,33 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func health(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`{"status":"ok"}` + "\n"))
|
||||
// health godoc
|
||||
// @Summary Check API health
|
||||
// @Tags system
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]string
|
||||
// @Router /health [get]
|
||||
func health(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
}
|
||||
|
||||
func withCORS(next http.Handler, allowedOrigin string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
origin := r.Header.Get("Origin")
|
||||
func withCORS(allowedOrigin string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
origin := c.GetHeader("Origin")
|
||||
if origin != "" && originAllowed(origin, allowedOrigin) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Header().Set("Vary", "Origin")
|
||||
c.Header("Access-Control-Allow-Origin", origin)
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
c.Header("Vary", "Origin")
|
||||
}
|
||||
if r.Method == http.MethodOptions {
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
c.Header("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
|
||||
c.Header("Access-Control-Allow-Headers", "Content-Type")
|
||||
c.Status(http.StatusNoContent)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func originAllowed(origin, configured string) bool {
|
||||
@@ -144,10 +163,10 @@ func originAllowed(origin, configured string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func withLogging(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
func withLogging() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
started := time.Now()
|
||||
next.ServeHTTP(w, r)
|
||||
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(started).Round(time.Millisecond))
|
||||
})
|
||||
c.Next()
|
||||
log.Printf("%s %s %d %s", c.Request.Method, c.Request.URL.Path, c.Writer.Status(), time.Since(started).Round(time.Millisecond))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user