Implement complete API routes and mock authentication for full CMS functionality

- Add comprehensive nested route structure with proper authentication layers
- Implement UpdateContent and ReorderCollectionItems handlers with repository pattern
- Add automatic mock JWT token fetching for seamless development workflow
- Restore content editing and collection reordering functionality broken after database refactoring
- Provide production-ready authentication architecture with development convenience
- Enable full CMS operations in browser with proper CRUD and bulk transaction support
This commit is contained in:
2025-10-16 21:23:17 +02:00
parent bbf728d110
commit 87b78a4a69
11 changed files with 1095 additions and 218 deletions

View File

@@ -264,6 +264,54 @@ func (r *PostgreSQLRepository) CreateCollectionItemAtomic(ctx context.Context, s
return nil, fmt.Errorf("CreateCollectionItemAtomic not yet implemented for PostgreSQL")
}
// UpdateContent updates an existing content item
func (r *PostgreSQLRepository) UpdateContent(ctx context.Context, siteID, contentID, htmlContent, lastEditedBy string) (*ContentItem, error) {
content, err := r.queries.UpdateContent(ctx, postgresql.UpdateContentParams{
HtmlContent: htmlContent,
LastEditedBy: lastEditedBy,
ID: contentID,
SiteID: siteID,
})
if err != nil {
return nil, err
}
return &ContentItem{
ID: content.ID,
SiteID: content.SiteID,
HTMLContent: content.HtmlContent,
OriginalTemplate: FromNullString(content.OriginalTemplate),
UpdatedAt: fmt.Sprintf("%d", content.UpdatedAt),
LastEditedBy: content.LastEditedBy,
}, nil
}
// ReorderCollectionItems reorders collection items in bulk
func (r *PostgreSQLRepository) ReorderCollectionItems(ctx context.Context, siteID, collectionID string, items []CollectionItemPosition, lastEditedBy string) error {
// Use transaction for atomic bulk updates
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
qtx := r.queries.WithTx(tx)
for _, item := range items {
err = qtx.UpdateCollectionItemPosition(ctx, postgresql.UpdateCollectionItemPositionParams{
ItemID: item.ItemID,
CollectionID: collectionID,
SiteID: siteID,
Position: int32(item.Position),
LastEditedBy: lastEditedBy,
})
if err != nil {
return fmt.Errorf("failed to update position for item %s: %w", item.ItemID, err)
}
}
return tx.Commit()
}
// WithTransaction executes a function within a database transaction
func (r *PostgreSQLRepository) WithTransaction(ctx context.Context, fn func(ContentRepository) error) error {
tx, err := r.db.BeginTx(ctx, nil)

View File

@@ -11,6 +11,7 @@ type ContentRepository interface {
GetBulkContent(ctx context.Context, siteID string, contentIDs []string) (map[string]ContentItem, error)
GetAllContent(ctx context.Context, siteID string) (map[string]ContentItem, error)
CreateContent(ctx context.Context, siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error)
UpdateContent(ctx context.Context, siteID, contentID, htmlContent, lastEditedBy string) (*ContentItem, error)
// Collection operations
GetCollection(ctx context.Context, siteID, collectionID string) (*CollectionItem, error)
@@ -20,6 +21,7 @@ type ContentRepository interface {
CreateCollectionTemplate(ctx context.Context, siteID, collectionID, name, htmlTemplate string, isDefault bool) (*CollectionTemplateItem, error)
CreateCollectionItem(ctx context.Context, siteID, collectionID, itemID string, templateID int, htmlContent string, position int, lastEditedBy string) (*CollectionItemWithTemplate, error)
CreateCollectionItemAtomic(ctx context.Context, siteID, collectionID string, templateID int, lastEditedBy string) (*CollectionItemWithTemplate, error)
ReorderCollectionItems(ctx context.Context, siteID, collectionID string, items []CollectionItemPosition, lastEditedBy string) error
// Transaction support
WithTransaction(ctx context.Context, fn func(ContentRepository) error) error
@@ -77,6 +79,12 @@ type CollectionItemWithTemplate struct {
IsDefault bool `json:"is_default"`
}
// CollectionItemPosition represents item position for reordering
type CollectionItemPosition struct {
ItemID string `json:"itemId"`
Position int `json:"position"`
}
// Helper function to convert sql.NullString to string
func getStringFromNullString(ns sql.NullString) string {
if ns.Valid {

View File

@@ -269,6 +269,54 @@ func (r *SQLiteRepository) CreateCollectionItemAtomic(ctx context.Context, siteI
return nil, fmt.Errorf("CreateCollectionItemAtomic not yet implemented for SQLite")
}
// UpdateContent updates an existing content item
func (r *SQLiteRepository) UpdateContent(ctx context.Context, siteID, contentID, htmlContent, lastEditedBy string) (*ContentItem, error) {
content, err := r.queries.UpdateContent(ctx, sqlite.UpdateContentParams{
HtmlContent: htmlContent,
LastEditedBy: lastEditedBy,
ID: contentID,
SiteID: siteID,
})
if err != nil {
return nil, err
}
return &ContentItem{
ID: content.ID,
SiteID: content.SiteID,
HTMLContent: content.HtmlContent,
OriginalTemplate: FromNullString(content.OriginalTemplate),
UpdatedAt: fmt.Sprintf("%d", content.UpdatedAt),
LastEditedBy: content.LastEditedBy,
}, nil
}
// ReorderCollectionItems reorders collection items in bulk
func (r *SQLiteRepository) ReorderCollectionItems(ctx context.Context, siteID, collectionID string, items []CollectionItemPosition, lastEditedBy string) error {
// Use transaction for atomic bulk updates
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
qtx := r.queries.WithTx(tx)
for _, item := range items {
err = qtx.UpdateCollectionItemPosition(ctx, sqlite.UpdateCollectionItemPositionParams{
ItemID: item.ItemID,
CollectionID: collectionID,
SiteID: siteID,
Position: int64(item.Position),
LastEditedBy: lastEditedBy,
})
if err != nil {
return fmt.Errorf("failed to update position for item %s: %w", item.ItemID, err)
}
}
return tx.Commit()
}
// WithTransaction executes a function within a database transaction
func (r *SQLiteRepository) WithTransaction(ctx context.Context, fn func(ContentRepository) error) error {
tx, err := r.db.BeginTx(ctx, nil)