feat: initialize blog app with blueprints and templates
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Extract blog-specific code from the coop monolith into a standalone repository. Includes auth, blog, post, admin, menu_items, snippets blueprints, associated templates, Dockerfile (APP_MODULE=app:app), entrypoint, and Gitea CI workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
58
bp/post/services/post_operations.py
Normal file
58
bp/post/services/post_operations.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import select, func, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from models.ghost_content import Post, PostLike
|
||||
|
||||
|
||||
async def toggle_post_like(
|
||||
session: AsyncSession,
|
||||
user_id: int,
|
||||
post_id: int,
|
||||
) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Toggle a post like for a given user using soft deletes.
|
||||
Returns (liked_state, error_message).
|
||||
- If error_message is not None, an error occurred.
|
||||
- liked_state indicates whether post is now liked (True) or unliked (False).
|
||||
"""
|
||||
|
||||
# Verify post exists
|
||||
post_exists = await session.scalar(
|
||||
select(Post.id).where(Post.id == post_id, Post.deleted_at.is_(None))
|
||||
)
|
||||
if not post_exists:
|
||||
return False, "Post not found"
|
||||
|
||||
# Check if like exists (not deleted)
|
||||
existing = await session.scalar(
|
||||
select(PostLike).where(
|
||||
PostLike.user_id == user_id,
|
||||
PostLike.post_id == post_id,
|
||||
PostLike.deleted_at.is_(None),
|
||||
)
|
||||
)
|
||||
|
||||
if existing:
|
||||
# Unlike: soft delete the like
|
||||
await session.execute(
|
||||
update(PostLike)
|
||||
.where(
|
||||
PostLike.user_id == user_id,
|
||||
PostLike.post_id == post_id,
|
||||
PostLike.deleted_at.is_(None),
|
||||
)
|
||||
.values(deleted_at=func.now())
|
||||
)
|
||||
return False, None
|
||||
else:
|
||||
# Like: add a new like
|
||||
new_like = PostLike(
|
||||
user_id=user_id,
|
||||
post_id=post_id,
|
||||
)
|
||||
session.add(new_like)
|
||||
return True, None
|
||||
Reference in New Issue
Block a user