mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 14:04:08 +01:00
feat: add image_url attribute and handle method
This commit is contained in:
parent
8af97c0150
commit
5eec21a2b1
@ -81,13 +81,14 @@ type GetKnowledgeArticleByIDOutput struct {
|
|||||||
|
|
||||||
type CreateOrUpdateKnowledgeArticleInput struct {
|
type CreateOrUpdateKnowledgeArticleInput struct {
|
||||||
Body struct {
|
Body struct {
|
||||||
UUID string `json:"uuid,omitempty"` // Optional for create, required for update
|
UUID string `json:"uuid,omitempty"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Author string `json:"author"`
|
Author string `json:"author"`
|
||||||
PublishDate time.Time `json:"publish_date"`
|
PublishDate time.Time `json:"publish_date"`
|
||||||
ReadTime string `json:"read_time"`
|
ReadTime string `json:"read_time"`
|
||||||
Categories []string `json:"categories"`
|
Categories []string `json:"categories"`
|
||||||
|
ImageURL string `json:"image_url"`
|
||||||
} `json:"body"`
|
} `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +131,7 @@ func (a *api) getAllKnowledgeArticlesHandler(ctx context.Context, input *struct{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) getKnowledgeArticleByIDHandler(ctx context.Context, input *struct {
|
func (a *api) getKnowledgeArticleByIDHandler(ctx context.Context, input *struct {
|
||||||
UUID string `path:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
|
UUID string `path:"uuid"`
|
||||||
}) (*GetKnowledgeArticleByIDOutput, error) {
|
}) (*GetKnowledgeArticleByIDOutput, error) {
|
||||||
resp := &GetKnowledgeArticleByIDOutput{}
|
resp := &GetKnowledgeArticleByIDOutput{}
|
||||||
|
|
||||||
@ -138,8 +139,7 @@ func (a *api) getKnowledgeArticleByIDHandler(ctx context.Context, input *struct
|
|||||||
return nil, huma.Error400BadRequest("UUID parameter is required")
|
return nil, huma.Error400BadRequest("UUID parameter is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := uuid.FromString(input.UUID)
|
if _, err := uuid.FromString(input.UUID); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, huma.Error400BadRequest("invalid UUID format")
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ func (a *api) getKnowledgeArticleByIDHandler(ctx context.Context, input *struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) getKnowledgeArticlesByCategoryHandler(ctx context.Context, input *struct {
|
func (a *api) getKnowledgeArticlesByCategoryHandler(ctx context.Context, input *struct {
|
||||||
Category string `path:"category" example:"Sustainability"`
|
Category string `path:"category"`
|
||||||
}) (*GetKnowledgeArticlesOutput, error) {
|
}) (*GetKnowledgeArticlesOutput, error) {
|
||||||
resp := &GetKnowledgeArticlesOutput{}
|
resp := &GetKnowledgeArticlesOutput{}
|
||||||
|
|
||||||
@ -190,8 +190,7 @@ func (a *api) createOrUpdateKnowledgeArticleHandler(ctx context.Context, input *
|
|||||||
}
|
}
|
||||||
|
|
||||||
if input.Body.UUID != "" {
|
if input.Body.UUID != "" {
|
||||||
_, err := uuid.FromString(input.Body.UUID)
|
if _, err := uuid.FromString(input.Body.UUID); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, huma.Error400BadRequest("invalid UUID format")
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,10 +203,10 @@ func (a *api) createOrUpdateKnowledgeArticleHandler(ctx context.Context, input *
|
|||||||
PublishDate: input.Body.PublishDate,
|
PublishDate: input.Body.PublishDate,
|
||||||
ReadTime: input.Body.ReadTime,
|
ReadTime: input.Body.ReadTime,
|
||||||
Categories: input.Body.Categories,
|
Categories: input.Body.Categories,
|
||||||
|
ImageURL: input.Body.ImageURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := a.knowledgeHubRepo.CreateOrUpdateArticle(ctx, article)
|
if err := a.knowledgeHubRepo.CreateOrUpdateArticle(ctx, article); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +215,7 @@ func (a *api) createOrUpdateKnowledgeArticleHandler(ctx context.Context, input *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) getArticleTableOfContentsHandler(ctx context.Context, input *struct {
|
func (a *api) getArticleTableOfContentsHandler(ctx context.Context, input *struct {
|
||||||
UUID string `path:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
|
UUID string `path:"uuid"`
|
||||||
}) (*GetTableOfContentsOutput, error) {
|
}) (*GetTableOfContentsOutput, error) {
|
||||||
resp := &GetTableOfContentsOutput{}
|
resp := &GetTableOfContentsOutput{}
|
||||||
|
|
||||||
@ -224,8 +223,7 @@ func (a *api) getArticleTableOfContentsHandler(ctx context.Context, input *struc
|
|||||||
return nil, huma.Error400BadRequest("UUID parameter is required")
|
return nil, huma.Error400BadRequest("UUID parameter is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := uuid.FromString(input.UUID)
|
if _, err := uuid.FromString(input.UUID); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, huma.Error400BadRequest("invalid UUID format")
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +240,7 @@ func (a *api) getArticleTableOfContentsHandler(ctx context.Context, input *struc
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) getArticleRelatedArticlesHandler(ctx context.Context, input *struct {
|
func (a *api) getArticleRelatedArticlesHandler(ctx context.Context, input *struct {
|
||||||
UUID string `path:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
|
UUID string `path:"uuid"`
|
||||||
}) (*GetRelatedArticlesOutput, error) {
|
}) (*GetRelatedArticlesOutput, error) {
|
||||||
resp := &GetRelatedArticlesOutput{}
|
resp := &GetRelatedArticlesOutput{}
|
||||||
|
|
||||||
@ -250,8 +248,7 @@ func (a *api) getArticleRelatedArticlesHandler(ctx context.Context, input *struc
|
|||||||
return nil, huma.Error400BadRequest("UUID parameter is required")
|
return nil, huma.Error400BadRequest("UUID parameter is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := uuid.FromString(input.UUID)
|
if _, err := uuid.FromString(input.UUID); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, huma.Error400BadRequest("invalid UUID format")
|
return nil, huma.Error400BadRequest("invalid UUID format")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,5 +283,5 @@ func (a *api) createRelatedArticleHandler(
|
|||||||
return nil, huma.Error500InternalServerError("failed to create related article")
|
return nil, huma.Error500InternalServerError("failed to create related article")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil // HTTP 204 No Content
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package domain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
@ -15,6 +17,7 @@ type KnowledgeArticle struct {
|
|||||||
PublishDate time.Time
|
PublishDate time.Time
|
||||||
ReadTime string
|
ReadTime string
|
||||||
Categories []string
|
Categories []string
|
||||||
|
ImageURL string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
@ -25,6 +28,16 @@ func (k *KnowledgeArticle) Validate() error {
|
|||||||
validation.Field(&k.Content, validation.Required),
|
validation.Field(&k.Content, validation.Required),
|
||||||
validation.Field(&k.Author, validation.Required),
|
validation.Field(&k.Author, validation.Required),
|
||||||
validation.Field(&k.PublishDate, validation.Required),
|
validation.Field(&k.PublishDate, validation.Required),
|
||||||
|
validation.Field(&k.ImageURL,
|
||||||
|
validation.By(func(value interface{}) error {
|
||||||
|
if url, ok := value.(string); ok && url != "" {
|
||||||
|
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||||
|
return fmt.Errorf("must be a valid URL starting with http:// or https://")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,6 +34,7 @@ func (p *postgresKnowledgeHubRepository) fetchArticles(ctx context.Context, quer
|
|||||||
&a.PublishDate,
|
&a.PublishDate,
|
||||||
&a.ReadTime,
|
&a.ReadTime,
|
||||||
&a.Categories,
|
&a.Categories,
|
||||||
|
&a.ImageURL,
|
||||||
&a.CreatedAt,
|
&a.CreatedAt,
|
||||||
&a.UpdatedAt,
|
&a.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -46,7 +47,7 @@ func (p *postgresKnowledgeHubRepository) fetchArticles(ctx context.Context, quer
|
|||||||
|
|
||||||
func (p *postgresKnowledgeHubRepository) GetArticleByID(ctx context.Context, uuid string) (domain.KnowledgeArticle, error) {
|
func (p *postgresKnowledgeHubRepository) GetArticleByID(ctx context.Context, uuid string) (domain.KnowledgeArticle, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT uuid, title, content, author, publish_date, read_time, categories, created_at, updated_at
|
SELECT uuid, title, content, author, publish_date, read_time, categories, image_url, created_at, updated_at
|
||||||
FROM knowledge_articles
|
FROM knowledge_articles
|
||||||
WHERE uuid = $1`
|
WHERE uuid = $1`
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ func (p *postgresKnowledgeHubRepository) GetArticleByID(ctx context.Context, uui
|
|||||||
|
|
||||||
func (p *postgresKnowledgeHubRepository) GetArticlesByCategory(ctx context.Context, category string) ([]domain.KnowledgeArticle, error) {
|
func (p *postgresKnowledgeHubRepository) GetArticlesByCategory(ctx context.Context, category string) ([]domain.KnowledgeArticle, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT uuid, title, content, author, publish_date, read_time, categories, created_at, updated_at
|
SELECT uuid, title, content, author, publish_date, read_time, categories, image_url, created_at, updated_at
|
||||||
FROM knowledge_articles
|
FROM knowledge_articles
|
||||||
WHERE $1 = ANY(categories)`
|
WHERE $1 = ANY(categories)`
|
||||||
|
|
||||||
@ -71,20 +72,24 @@ func (p *postgresKnowledgeHubRepository) GetArticlesByCategory(ctx context.Conte
|
|||||||
|
|
||||||
func (p *postgresKnowledgeHubRepository) GetAllArticles(ctx context.Context) ([]domain.KnowledgeArticle, error) {
|
func (p *postgresKnowledgeHubRepository) GetAllArticles(ctx context.Context) ([]domain.KnowledgeArticle, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT uuid, title, content, author, publish_date, read_time, categories, created_at, updated_at
|
SELECT uuid, title, content, author, publish_date, read_time, categories, image_url, created_at, updated_at
|
||||||
FROM knowledge_articles`
|
FROM knowledge_articles`
|
||||||
|
|
||||||
return p.fetchArticles(ctx, query)
|
return p.fetchArticles(ctx, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *postgresKnowledgeHubRepository) CreateOrUpdateArticle(ctx context.Context, article *domain.KnowledgeArticle) error {
|
func (p *postgresKnowledgeHubRepository) CreateOrUpdateArticle(
|
||||||
|
ctx context.Context,
|
||||||
|
article *domain.KnowledgeArticle,
|
||||||
|
) error {
|
||||||
if strings.TrimSpace(article.UUID) == "" {
|
if strings.TrimSpace(article.UUID) == "" {
|
||||||
article.UUID = uuid.New().String()
|
article.UUID = uuid.New().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
INSERT INTO knowledge_articles (uuid, title, content, author, publish_date, read_time, categories, created_at, updated_at)
|
INSERT INTO knowledge_articles
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
|
(uuid, title, content, author, publish_date, read_time, categories, image_url, created_at, updated_at)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
|
||||||
ON CONFLICT (uuid) DO UPDATE
|
ON CONFLICT (uuid) DO UPDATE
|
||||||
SET title = EXCLUDED.title,
|
SET title = EXCLUDED.title,
|
||||||
content = EXCLUDED.content,
|
content = EXCLUDED.content,
|
||||||
@ -92,6 +97,7 @@ func (p *postgresKnowledgeHubRepository) CreateOrUpdateArticle(ctx context.Conte
|
|||||||
publish_date = EXCLUDED.publish_date,
|
publish_date = EXCLUDED.publish_date,
|
||||||
read_time = EXCLUDED.read_time,
|
read_time = EXCLUDED.read_time,
|
||||||
categories = EXCLUDED.categories,
|
categories = EXCLUDED.categories,
|
||||||
|
image_url = EXCLUDED.image_url,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
RETURNING uuid, created_at, updated_at`
|
RETURNING uuid, created_at, updated_at`
|
||||||
|
|
||||||
@ -105,6 +111,7 @@ func (p *postgresKnowledgeHubRepository) CreateOrUpdateArticle(ctx context.Conte
|
|||||||
article.PublishDate,
|
article.PublishDate,
|
||||||
article.ReadTime,
|
article.ReadTime,
|
||||||
article.Categories,
|
article.Categories,
|
||||||
|
article.ImageURL,
|
||||||
).Scan(&article.UUID, &article.CreatedAt, &article.UpdatedAt)
|
).Scan(&article.UUID, &article.CreatedAt, &article.UpdatedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,8 +195,8 @@ func (p *postgresKnowledgeHubRepository) CreateRelatedArticle(
|
|||||||
articleID string,
|
articleID string,
|
||||||
related *domain.RelatedArticle,
|
related *domain.RelatedArticle,
|
||||||
) error {
|
) error {
|
||||||
related.UUID = uuid.New().String() // Generate UUID
|
related.UUID = uuid.New().String()
|
||||||
related.ArticleID = articleID // Link to main article
|
related.ArticleID = articleID
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
INSERT INTO related_articles
|
INSERT INTO related_articles
|
||||||
|
|||||||
7
backend/migrations/00005_add_image_url_to_articles.sql
Normal file
7
backend/migrations/00005_add_image_url_to_articles.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
-- +goose Up
|
||||||
|
ALTER TABLE knowledge_articles
|
||||||
|
ADD COLUMN image_url TEXT;
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
ALTER TABLE knowledge_articles
|
||||||
|
DROP COLUMN image_url;
|
||||||
Loading…
Reference in New Issue
Block a user