updates
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"""add_page_content_table
|
||||
|
||||
Revision ID: 163657e72e93
|
||||
Revises: 6a126cc5b23c
|
||||
Create Date: 2025-11-18 18:02:03.480951
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '163657e72e93'
|
||||
down_revision = '6a126cc5b23c'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
# Only create the page_contents table, skip other schema changes
|
||||
op.create_table('page_contents',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('page_type', sa.Enum('home', 'contact', 'about', 'footer', 'seo', name='pagetype'), nullable=False),
|
||||
sa.Column('title', sa.String(length=500), nullable=True),
|
||||
sa.Column('subtitle', sa.String(length=1000), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('content', sa.Text(), nullable=True),
|
||||
sa.Column('meta_title', sa.String(length=500), nullable=True),
|
||||
sa.Column('meta_description', sa.Text(), nullable=True),
|
||||
sa.Column('meta_keywords', sa.String(length=1000), nullable=True),
|
||||
sa.Column('og_title', sa.String(length=500), nullable=True),
|
||||
sa.Column('og_description', sa.Text(), nullable=True),
|
||||
sa.Column('og_image', sa.String(length=1000), nullable=True),
|
||||
sa.Column('canonical_url', sa.String(length=1000), nullable=True),
|
||||
sa.Column('contact_info', sa.Text(), nullable=True),
|
||||
sa.Column('social_links', sa.Text(), nullable=True),
|
||||
sa.Column('footer_links', sa.Text(), nullable=True),
|
||||
sa.Column('hero_title', sa.String(length=500), nullable=True),
|
||||
sa.Column('hero_subtitle', sa.String(length=1000), nullable=True),
|
||||
sa.Column('hero_image', sa.String(length=1000), nullable=True),
|
||||
sa.Column('story_content', sa.Text(), nullable=True),
|
||||
sa.Column('values', sa.Text(), nullable=True),
|
||||
sa.Column('features', sa.Text(), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_page_contents_id'), 'page_contents', ['id'], unique=False)
|
||||
op.create_index(op.f('ix_page_contents_page_type'), 'page_contents', ['page_type'], unique=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_page_contents_page_type'), table_name='page_contents')
|
||||
op.drop_index(op.f('ix_page_contents_id'), table_name='page_contents')
|
||||
op.drop_table('page_contents')
|
||||
op.execute("DROP TYPE IF EXISTS pagetype")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
"""add_map_url_to_page_content
|
||||
|
||||
Revision ID: cce764ef7a50
|
||||
Revises: 163657e72e93
|
||||
Create Date: 2025-11-18 18:11:41.071053
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'cce764ef7a50'
|
||||
down_revision = '163657e72e93'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
# Only add the map_url column to page_contents table
|
||||
op.add_column('page_contents', sa.Column('map_url', sa.String(length=1000), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('page_contents', 'map_url')
|
||||
# ### end Alembic commands ###
|
||||
Binary file not shown.
@@ -42,15 +42,17 @@ if settings.is_development:
|
||||
logger.info("Creating database tables (development mode)")
|
||||
Base.metadata.create_all(bind=engine)
|
||||
else:
|
||||
# Ensure new cookie-related tables exist even if full migrations haven't been run yet.
|
||||
# Ensure new tables exist even if full migrations haven't been run yet.
|
||||
try:
|
||||
from .models.cookie_policy import CookiePolicy
|
||||
from .models.cookie_integration_config import CookieIntegrationConfig
|
||||
logger.info("Ensuring cookie-related tables exist")
|
||||
from .models.page_content import PageContent
|
||||
logger.info("Ensuring required tables exist")
|
||||
CookiePolicy.__table__.create(bind=engine, checkfirst=True)
|
||||
CookieIntegrationConfig.__table__.create(bind=engine, checkfirst=True)
|
||||
PageContent.__table__.create(bind=engine, checkfirst=True)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to ensure cookie tables exist: {e}")
|
||||
logger.error(f"Failed to ensure required tables exist: {e}")
|
||||
|
||||
from .routes import auth_routes
|
||||
from .routes import privacy_routes
|
||||
@@ -125,9 +127,11 @@ app.add_exception_handler(Exception, general_exception_handler)
|
||||
|
||||
# Enhanced Health check with database connectivity
|
||||
@app.get("/health", tags=["health"])
|
||||
@app.get("/api/health", tags=["health"])
|
||||
async def health_check(db: Session = Depends(get_db)):
|
||||
"""
|
||||
Enhanced health check endpoint with database connectivity test
|
||||
Available at both /health and /api/health for consistency
|
||||
"""
|
||||
health_status = {
|
||||
"status": "healthy",
|
||||
@@ -196,7 +200,7 @@ from .routes import (
|
||||
room_routes, booking_routes, payment_routes, invoice_routes, banner_routes,
|
||||
favorite_routes, service_routes, service_booking_routes, promotion_routes, report_routes,
|
||||
review_routes, user_routes, audit_routes, admin_privacy_routes,
|
||||
system_settings_routes, contact_routes
|
||||
system_settings_routes, contact_routes, page_content_routes
|
||||
)
|
||||
|
||||
# Legacy routes (maintain backward compatibility)
|
||||
@@ -234,6 +238,8 @@ app.include_router(audit_routes.router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(admin_privacy_routes.router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(system_settings_routes.router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(contact_routes.router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(page_content_routes.router, prefix="/api")
|
||||
app.include_router(page_content_routes.router, prefix=settings.API_V1_PREFIX)
|
||||
|
||||
logger.info("All routes registered successfully")
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ from .cookie_policy import CookiePolicy
|
||||
from .cookie_integration_config import CookieIntegrationConfig
|
||||
from .system_settings import SystemSettings
|
||||
from .invoice import Invoice, InvoiceItem
|
||||
from .page_content import PageContent, PageType
|
||||
|
||||
__all__ = [
|
||||
"Role",
|
||||
@@ -48,5 +49,7 @@ __all__ = [
|
||||
"SystemSettings",
|
||||
"Invoice",
|
||||
"InvoiceItem",
|
||||
"PageContent",
|
||||
"PageType",
|
||||
]
|
||||
|
||||
|
||||
Binary file not shown.
BIN
Backend/src/models/__pycache__/page_content.cpython-312.pyc
Normal file
BIN
Backend/src/models/__pycache__/page_content.cpython-312.pyc
Normal file
Binary file not shown.
60
Backend/src/models/page_content.py
Normal file
60
Backend/src/models/page_content.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean, Enum as SQLEnum
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
import enum
|
||||
|
||||
from ..config.database import Base
|
||||
|
||||
|
||||
class PageType(str, enum.Enum):
|
||||
HOME = "home"
|
||||
CONTACT = "contact"
|
||||
ABOUT = "about"
|
||||
FOOTER = "footer"
|
||||
SEO = "seo"
|
||||
|
||||
|
||||
class PageContent(Base):
|
||||
__tablename__ = "page_contents"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
page_type = Column(SQLEnum(PageType), nullable=False, unique=True, index=True)
|
||||
|
||||
# General content fields
|
||||
title = Column(String(500), nullable=True)
|
||||
subtitle = Column(String(1000), nullable=True)
|
||||
description = Column(Text, nullable=True)
|
||||
content = Column(Text, nullable=True) # Rich text content
|
||||
|
||||
# SEO fields
|
||||
meta_title = Column(String(500), nullable=True)
|
||||
meta_description = Column(Text, nullable=True)
|
||||
meta_keywords = Column(String(1000), nullable=True)
|
||||
og_title = Column(String(500), nullable=True)
|
||||
og_description = Column(Text, nullable=True)
|
||||
og_image = Column(String(1000), nullable=True)
|
||||
canonical_url = Column(String(1000), nullable=True)
|
||||
|
||||
# Contact/Footer specific fields (stored as JSON strings)
|
||||
contact_info = Column(Text, nullable=True) # JSON: phone, email, address
|
||||
map_url = Column(String(1000), nullable=True) # Google Maps embed URL
|
||||
social_links = Column(Text, nullable=True) # JSON: facebook, twitter, instagram, etc.
|
||||
footer_links = Column(Text, nullable=True) # JSON: quick links, support links
|
||||
|
||||
# Home page specific
|
||||
hero_title = Column(String(500), nullable=True)
|
||||
hero_subtitle = Column(String(1000), nullable=True)
|
||||
hero_image = Column(String(1000), nullable=True)
|
||||
|
||||
# About page specific
|
||||
story_content = Column(Text, nullable=True)
|
||||
values = Column(Text, nullable=True) # JSON array of values
|
||||
features = Column(Text, nullable=True) # JSON array of features
|
||||
|
||||
# Status
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
Binary file not shown.
413
Backend/src/routes/page_content_routes.py
Normal file
413
Backend/src/routes/page_content_routes.py
Normal file
@@ -0,0 +1,413 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
from ..config.database import get_db
|
||||
from ..middleware.auth import get_current_user, authorize_roles
|
||||
from ..models.user import User
|
||||
from ..models.page_content import PageContent, PageType
|
||||
|
||||
router = APIRouter(prefix="/page-content", tags=["page-content"])
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def get_all_page_contents(
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get all page contents"""
|
||||
try:
|
||||
contents = db.query(PageContent).all()
|
||||
result = []
|
||||
for content in contents:
|
||||
content_dict = {
|
||||
"id": content.id,
|
||||
"page_type": content.page_type.value,
|
||||
"title": content.title,
|
||||
"subtitle": content.subtitle,
|
||||
"description": content.description,
|
||||
"content": content.content,
|
||||
"meta_title": content.meta_title,
|
||||
"meta_description": content.meta_description,
|
||||
"meta_keywords": content.meta_keywords,
|
||||
"og_title": content.og_title,
|
||||
"og_description": content.og_description,
|
||||
"og_image": content.og_image,
|
||||
"canonical_url": content.canonical_url,
|
||||
"contact_info": json.loads(content.contact_info) if content.contact_info else None,
|
||||
"map_url": content.map_url,
|
||||
"social_links": json.loads(content.social_links) if content.social_links else None,
|
||||
"footer_links": json.loads(content.footer_links) if content.footer_links else None,
|
||||
"hero_title": content.hero_title,
|
||||
"hero_subtitle": content.hero_subtitle,
|
||||
"hero_image": content.hero_image,
|
||||
"story_content": content.story_content,
|
||||
"values": json.loads(content.values) if content.values else None,
|
||||
"features": json.loads(content.features) if content.features else None,
|
||||
"is_active": content.is_active,
|
||||
"created_at": content.created_at.isoformat() if content.created_at else None,
|
||||
"updated_at": content.updated_at.isoformat() if content.updated_at else None,
|
||||
}
|
||||
result.append(content_dict)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"page_contents": result
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Error fetching page contents: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{page_type}")
|
||||
async def get_page_content(
|
||||
page_type: PageType,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get content for a specific page"""
|
||||
try:
|
||||
content = db.query(PageContent).filter(PageContent.page_type == page_type).first()
|
||||
|
||||
if not content:
|
||||
# Return default structure if not found
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"page_content": None
|
||||
}
|
||||
}
|
||||
|
||||
content_dict = {
|
||||
"id": content.id,
|
||||
"page_type": content.page_type.value,
|
||||
"title": content.title,
|
||||
"subtitle": content.subtitle,
|
||||
"description": content.description,
|
||||
"content": content.content,
|
||||
"meta_title": content.meta_title,
|
||||
"meta_description": content.meta_description,
|
||||
"meta_keywords": content.meta_keywords,
|
||||
"og_title": content.og_title,
|
||||
"og_description": content.og_description,
|
||||
"og_image": content.og_image,
|
||||
"canonical_url": content.canonical_url,
|
||||
"contact_info": json.loads(content.contact_info) if content.contact_info else None,
|
||||
"social_links": json.loads(content.social_links) if content.social_links else None,
|
||||
"footer_links": json.loads(content.footer_links) if content.footer_links else None,
|
||||
"hero_title": content.hero_title,
|
||||
"hero_subtitle": content.hero_subtitle,
|
||||
"hero_image": content.hero_image,
|
||||
"story_content": content.story_content,
|
||||
"values": json.loads(content.values) if content.values else None,
|
||||
"features": json.loads(content.features) if content.features else None,
|
||||
"is_active": content.is_active,
|
||||
"created_at": content.created_at.isoformat() if content.created_at else None,
|
||||
"updated_at": content.updated_at.isoformat() if content.updated_at else None,
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"page_content": content_dict
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Error fetching page content: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{page_type}")
|
||||
async def create_or_update_page_content(
|
||||
page_type: PageType,
|
||||
title: Optional[str] = None,
|
||||
subtitle: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
content: Optional[str] = None,
|
||||
meta_title: Optional[str] = None,
|
||||
meta_description: Optional[str] = None,
|
||||
meta_keywords: Optional[str] = None,
|
||||
og_title: Optional[str] = None,
|
||||
og_description: Optional[str] = None,
|
||||
og_image: Optional[str] = None,
|
||||
canonical_url: Optional[str] = None,
|
||||
contact_info: Optional[str] = None,
|
||||
map_url: Optional[str] = None,
|
||||
social_links: Optional[str] = None,
|
||||
footer_links: Optional[str] = None,
|
||||
hero_title: Optional[str] = None,
|
||||
hero_subtitle: Optional[str] = None,
|
||||
hero_image: Optional[str] = None,
|
||||
story_content: Optional[str] = None,
|
||||
values: Optional[str] = None,
|
||||
features: Optional[str] = None,
|
||||
is_active: bool = True,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Create or update page content (admin only)"""
|
||||
try:
|
||||
authorize_roles(current_user, ["admin"])
|
||||
|
||||
# Validate JSON fields if provided
|
||||
if contact_info:
|
||||
try:
|
||||
json.loads(contact_info)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid JSON in contact_info"
|
||||
)
|
||||
|
||||
if social_links:
|
||||
try:
|
||||
json.loads(social_links)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid JSON in social_links"
|
||||
)
|
||||
|
||||
if footer_links:
|
||||
try:
|
||||
json.loads(footer_links)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid JSON in footer_links"
|
||||
)
|
||||
|
||||
if values:
|
||||
try:
|
||||
json.loads(values)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid JSON in values"
|
||||
)
|
||||
|
||||
if features:
|
||||
try:
|
||||
json.loads(features)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid JSON in features"
|
||||
)
|
||||
|
||||
# Check if content exists
|
||||
existing_content = db.query(PageContent).filter(PageContent.page_type == page_type).first()
|
||||
|
||||
if existing_content:
|
||||
# Update existing
|
||||
if title is not None:
|
||||
existing_content.title = title
|
||||
if subtitle is not None:
|
||||
existing_content.subtitle = subtitle
|
||||
if description is not None:
|
||||
existing_content.description = description
|
||||
if content is not None:
|
||||
existing_content.content = content
|
||||
if meta_title is not None:
|
||||
existing_content.meta_title = meta_title
|
||||
if meta_description is not None:
|
||||
existing_content.meta_description = meta_description
|
||||
if meta_keywords is not None:
|
||||
existing_content.meta_keywords = meta_keywords
|
||||
if og_title is not None:
|
||||
existing_content.og_title = og_title
|
||||
if og_description is not None:
|
||||
existing_content.og_description = og_description
|
||||
if og_image is not None:
|
||||
existing_content.og_image = og_image
|
||||
if canonical_url is not None:
|
||||
existing_content.canonical_url = canonical_url
|
||||
if contact_info is not None:
|
||||
existing_content.contact_info = contact_info
|
||||
if map_url is not None:
|
||||
existing_content.map_url = map_url
|
||||
if social_links is not None:
|
||||
existing_content.social_links = social_links
|
||||
if footer_links is not None:
|
||||
existing_content.footer_links = footer_links
|
||||
if hero_title is not None:
|
||||
existing_content.hero_title = hero_title
|
||||
if hero_subtitle is not None:
|
||||
existing_content.hero_subtitle = hero_subtitle
|
||||
if hero_image is not None:
|
||||
existing_content.hero_image = hero_image
|
||||
if story_content is not None:
|
||||
existing_content.story_content = story_content
|
||||
if values is not None:
|
||||
existing_content.values = values
|
||||
if features is not None:
|
||||
existing_content.features = features
|
||||
if is_active is not None:
|
||||
existing_content.is_active = is_active
|
||||
|
||||
existing_content.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
db.refresh(existing_content)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Page content updated successfully",
|
||||
"data": {
|
||||
"page_content": {
|
||||
"id": existing_content.id,
|
||||
"page_type": existing_content.page_type.value,
|
||||
"title": existing_content.title,
|
||||
"updated_at": existing_content.updated_at.isoformat() if existing_content.updated_at else None,
|
||||
}
|
||||
}
|
||||
}
|
||||
else:
|
||||
# Create new
|
||||
new_content = PageContent(
|
||||
page_type=page_type,
|
||||
title=title,
|
||||
subtitle=subtitle,
|
||||
description=description,
|
||||
content=content,
|
||||
meta_title=meta_title,
|
||||
meta_description=meta_description,
|
||||
meta_keywords=meta_keywords,
|
||||
og_title=og_title,
|
||||
og_description=og_description,
|
||||
og_image=og_image,
|
||||
canonical_url=canonical_url,
|
||||
contact_info=contact_info,
|
||||
map_url=map_url,
|
||||
social_links=social_links,
|
||||
footer_links=footer_links,
|
||||
hero_title=hero_title,
|
||||
hero_subtitle=hero_subtitle,
|
||||
hero_image=hero_image,
|
||||
story_content=story_content,
|
||||
values=values,
|
||||
features=features,
|
||||
is_active=is_active,
|
||||
)
|
||||
|
||||
db.add(new_content)
|
||||
db.commit()
|
||||
db.refresh(new_content)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Page content created successfully",
|
||||
"data": {
|
||||
"page_content": {
|
||||
"id": new_content.id,
|
||||
"page_type": new_content.page_type.value,
|
||||
"title": new_content.title,
|
||||
"created_at": new_content.created_at.isoformat() if new_content.created_at else None,
|
||||
}
|
||||
}
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Error saving page content: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.put("/{page_type}")
|
||||
async def update_page_content(
|
||||
page_type: PageType,
|
||||
page_data: dict,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Create or update page content using JSON body (admin only)"""
|
||||
try:
|
||||
authorize_roles(current_user, ["admin"])
|
||||
|
||||
existing_content = db.query(PageContent).filter(PageContent.page_type == page_type).first()
|
||||
|
||||
if not existing_content:
|
||||
# Create new content if it doesn't exist
|
||||
existing_content = PageContent(
|
||||
page_type=page_type,
|
||||
is_active=True,
|
||||
)
|
||||
db.add(existing_content)
|
||||
|
||||
# Update fields from request body
|
||||
for key, value in page_data.items():
|
||||
if hasattr(existing_content, key):
|
||||
# Handle JSON fields - convert dict/list to JSON string
|
||||
if key in ["contact_info", "social_links", "footer_links", "values", "features"] and value is not None:
|
||||
if isinstance(value, str):
|
||||
# Already a string, validate it's valid JSON
|
||||
try:
|
||||
json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Invalid JSON in {key}"
|
||||
)
|
||||
elif isinstance(value, (dict, list)):
|
||||
# Convert dict/list to JSON string for storage
|
||||
value = json.dumps(value)
|
||||
|
||||
# Skip None values to allow partial updates
|
||||
if value is not None:
|
||||
setattr(existing_content, key, value)
|
||||
|
||||
existing_content.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
db.refresh(existing_content)
|
||||
|
||||
content_dict = {
|
||||
"id": existing_content.id,
|
||||
"page_type": existing_content.page_type.value,
|
||||
"title": existing_content.title,
|
||||
"subtitle": existing_content.subtitle,
|
||||
"description": existing_content.description,
|
||||
"content": existing_content.content,
|
||||
"meta_title": existing_content.meta_title,
|
||||
"meta_description": existing_content.meta_description,
|
||||
"meta_keywords": existing_content.meta_keywords,
|
||||
"og_title": existing_content.og_title,
|
||||
"og_description": existing_content.og_description,
|
||||
"og_image": existing_content.og_image,
|
||||
"canonical_url": existing_content.canonical_url,
|
||||
"contact_info": json.loads(existing_content.contact_info) if existing_content.contact_info else None,
|
||||
"social_links": json.loads(existing_content.social_links) if existing_content.social_links else None,
|
||||
"footer_links": json.loads(existing_content.footer_links) if existing_content.footer_links else None,
|
||||
"hero_title": existing_content.hero_title,
|
||||
"hero_subtitle": existing_content.hero_subtitle,
|
||||
"hero_image": existing_content.hero_image,
|
||||
"story_content": existing_content.story_content,
|
||||
"values": json.loads(existing_content.values) if existing_content.values else None,
|
||||
"features": json.loads(existing_content.features) if existing_content.features else None,
|
||||
"is_active": existing_content.is_active,
|
||||
"updated_at": existing_content.updated_at.isoformat() if existing_content.updated_at else None,
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Page content updated successfully",
|
||||
"data": {
|
||||
"page_content": content_dict
|
||||
}
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Error updating page content: {str(e)}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user