feat: fruit overview with thumbnails and card layout (story #07)
- Auto-crop thumbnails on image upload (Go stdlib, no external deps): find non-background bounding box, pad 5px, scale to ≤300px, encode JPEG - Add thumbnail_data BYTEA column to fruit_images and publication_fruit_images - New GET /api/v1/fruits/:id/images/:imageId/thumbnail endpoint (fallback to full data) - New GET /api/v1/publications/:id/fruit-images/:imgId/thumbnail endpoint - New POST /api/v1/admin/backfill-thumbnails to retroactively generate thumbnails - ListFruits response now includes synonyms and thumbnail_url per fruit - Replace FruitList table with responsive FruitCard grid (thumbnail + name + OSDB ID + type + synonyms) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ func New(pool *pgxpool.Pool) *echo.Echo {
|
||||
g.GET("/:id/images", fruits.ListImages)
|
||||
g.POST("/:id/images", fruits.UploadImage, middleware.BodyLimit("5M"))
|
||||
g.GET("/:id/images/:imageId", fruits.ServeImage)
|
||||
g.GET("/:id/images/:imageId/thumbnail", fruits.ServeThumbnail)
|
||||
g.DELETE("/:id/images/:imageId", fruits.DeleteImage)
|
||||
|
||||
// Publications (story #04)
|
||||
@@ -68,7 +69,13 @@ func New(pool *pgxpool.Pool) *echo.Echo {
|
||||
p.GET("/:id/fruit-images", pubs.ListFruitImages)
|
||||
p.POST("/:id/fruit-images", pubs.UploadFruitImage, middleware.BodyLimit("5M"))
|
||||
p.GET("/:id/fruit-images/:imgId", pubs.ServeFruitImage)
|
||||
p.GET("/:id/fruit-images/:imgId/thumbnail", pubs.ServeFruitImageThumbnail)
|
||||
p.DELETE("/:id/fruit-images/:imgId", pubs.DeleteFruitImage)
|
||||
|
||||
// Admin (story #07)
|
||||
admin := handler.NewAdminHandler(fruitRepo, pubRepo)
|
||||
adminGroup := api.Group("/admin")
|
||||
adminGroup.POST("/backfill-thumbnails", admin.BackfillThumbnails)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user