Monorepo: consolidate 7 repos into one
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s
Combines shared, blog, market, cart, events, federation, and account into a single repository. Eliminates submodule sync, sibling model copying at build time, and per-app CI orchestration. Changes: - Remove per-app .git, .gitmodules, .gitea, submodule shared/ dirs - Remove stale sibling model copies from each app - Update all 6 Dockerfiles for monorepo build context (root = .) - Add build directives to docker-compose.yml - Add single .gitea/workflows/ci.yml with change detection - Add .dockerignore for monorepo build context - Create __init__.py for federation and account (cross-app imports)
This commit is contained in:
18
blog/bp/blog/services/pages_data.py
Normal file
18
blog/bp/blog/services/pages_data.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from ..ghost_db import DBClient
|
||||
|
||||
|
||||
async def pages_data(session, page, search):
|
||||
client = DBClient(session)
|
||||
|
||||
pages, pagination = await client.list_pages(
|
||||
limit=10,
|
||||
page=page,
|
||||
search=search,
|
||||
)
|
||||
|
||||
return {
|
||||
"pages": pages,
|
||||
"page": pagination.get("page", page),
|
||||
"total_pages": pagination.get("pages", 1),
|
||||
"search": search,
|
||||
}
|
||||
142
blog/bp/blog/services/posts_data.py
Normal file
142
blog/bp/blog/services/posts_data.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import re
|
||||
|
||||
from ..ghost_db import DBClient # adjust import path
|
||||
from sqlalchemy import select
|
||||
from models.ghost_content import PostLike
|
||||
from shared.infrastructure.fragments import fetch_fragment
|
||||
from quart import g
|
||||
|
||||
async def posts_data(
|
||||
session,
|
||||
page, search, sort, selected_tags, selected_authors, liked,
|
||||
drafts=False, drafts_user_id=None, count_drafts_for_user_id=None,
|
||||
selected_groups=(),
|
||||
):
|
||||
client = DBClient(session)
|
||||
|
||||
# --- Tag-group resolution ---
|
||||
tag_groups = await client.list_tag_groups_with_counts()
|
||||
|
||||
# Collect all assigned tag IDs across groups
|
||||
all_assigned_tag_ids = []
|
||||
for grp in tag_groups:
|
||||
all_assigned_tag_ids.extend(grp["tag_ids"])
|
||||
|
||||
# Build slug-lookup for groups
|
||||
group_by_slug = {grp["slug"]: grp for grp in tag_groups}
|
||||
|
||||
# Resolve selected group → post filtering
|
||||
# Groups and tags are mutually exclusive — groups override tags when set
|
||||
effective_tags = selected_tags
|
||||
etc_mode_tag_ids = None # set when "etc" is selected
|
||||
if selected_groups:
|
||||
group_slug = selected_groups[0]
|
||||
if group_slug == "etc":
|
||||
# etc = posts NOT covered by any group (includes untagged)
|
||||
etc_mode_tag_ids = all_assigned_tag_ids
|
||||
effective_tags = ()
|
||||
elif group_slug in group_by_slug:
|
||||
effective_tags = tuple(group_by_slug[group_slug]["tag_slugs"])
|
||||
|
||||
# Compute "etc" virtual group
|
||||
etc_count = await client.count_etc_posts(all_assigned_tag_ids)
|
||||
if etc_count > 0 or (selected_groups and selected_groups[0] == "etc"):
|
||||
tag_groups.append({
|
||||
"id": None,
|
||||
"name": "etc",
|
||||
"slug": "etc",
|
||||
"feature_image": None,
|
||||
"colour": None,
|
||||
"sort_order": 999999,
|
||||
"post_count": etc_count,
|
||||
"tag_slugs": [],
|
||||
"tag_ids": [],
|
||||
})
|
||||
|
||||
posts, pagination = await client.list_posts(
|
||||
limit=10,
|
||||
page=page,
|
||||
selected_tags=effective_tags,
|
||||
selected_authors=selected_authors,
|
||||
search=search,
|
||||
drafts=drafts,
|
||||
drafts_user_id=drafts_user_id,
|
||||
exclude_covered_tag_ids=etc_mode_tag_ids,
|
||||
)
|
||||
|
||||
# Get all post IDs in this batch
|
||||
post_ids = [p["id"] for p in posts]
|
||||
|
||||
# Add is_liked field to each post for current user
|
||||
if g.user:
|
||||
# Fetch all likes for this user and these posts in one query
|
||||
liked_posts = await session.execute(
|
||||
select(PostLike.post_id).where(
|
||||
PostLike.user_id == g.user.id,
|
||||
PostLike.post_id.in_(post_ids),
|
||||
PostLike.deleted_at.is_(None),
|
||||
)
|
||||
)
|
||||
liked_post_ids = {row[0] for row in liked_posts}
|
||||
|
||||
# Add is_liked to each post
|
||||
for post in posts:
|
||||
post["is_liked"] = post["id"] in liked_post_ids
|
||||
else:
|
||||
# Not logged in - no posts are liked
|
||||
for post in posts:
|
||||
post["is_liked"] = False
|
||||
|
||||
# Fetch card decoration fragments from events
|
||||
card_widgets_html = {}
|
||||
if post_ids:
|
||||
post_slugs = [p.get("slug", "") for p in posts]
|
||||
cards_html = await fetch_fragment("events", "container-cards", params={
|
||||
"post_ids": ",".join(str(pid) for pid in post_ids),
|
||||
"post_slugs": ",".join(post_slugs),
|
||||
})
|
||||
if cards_html:
|
||||
card_widgets_html = _parse_card_fragments(cards_html)
|
||||
|
||||
tags=await client.list_tags(
|
||||
limit=50000
|
||||
)
|
||||
authors=await client.list_authors(
|
||||
limit=50000
|
||||
)
|
||||
|
||||
# Draft count for the logged-in user (None → admin sees all)
|
||||
draft_count = 0
|
||||
if count_drafts_for_user_id is not False:
|
||||
draft_count = await client.count_drafts(user_id=count_drafts_for_user_id)
|
||||
|
||||
return {
|
||||
"posts": posts,
|
||||
"page": pagination.get("page", page),
|
||||
"total_pages": pagination.get("pages", 1),
|
||||
"search_count": pagination.get("search_count"),
|
||||
"tags": tags,
|
||||
"authors": authors,
|
||||
"draft_count": draft_count,
|
||||
"tag_groups": tag_groups,
|
||||
"selected_groups": selected_groups,
|
||||
"card_widgets_html": card_widgets_html,
|
||||
}
|
||||
|
||||
|
||||
# Regex to extract per-post blocks delimited by comment markers
|
||||
_CARD_MARKER_RE = re.compile(
|
||||
r'<!-- card-widget:(\d+) -->(.*?)<!-- /card-widget:\1 -->',
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
|
||||
def _parse_card_fragments(html: str) -> dict[str, str]:
|
||||
"""Parse the container-cards fragment into {post_id_str: html} dict."""
|
||||
result = {}
|
||||
for m in _CARD_MARKER_RE.finditer(html):
|
||||
post_id_str = m.group(1)
|
||||
inner = m.group(2).strip()
|
||||
if inner:
|
||||
result[post_id_str] = inner
|
||||
return result
|
||||
Reference in New Issue
Block a user