forked from retoor/devplacepy
chore: remove stale agent reports and add PYTHONDONTWRITEBYTECODE export to Makefile
Delete four stale SEO and style agent report JSON/MD files from agents/reports/ that had zero findings or were superseded. Export PYTHONDONTWRITEBYTECODE=1 in the Makefile so all make targets suppress bytecode generation, and add a `tree` target that lists the repository file tree via git ls-tree. Update AGENTS.md and CLAUDE.md documentation to reflect that routers now form a directory tree mirroring URL paths and that bytecode writing is disabled project-wide.
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import logging
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from devplacepy.database import (
|
||||
get_table,
|
||||
build_pagination,
|
||||
get_news_images_by_uids,
|
||||
soft_delete,
|
||||
_now_iso,
|
||||
)
|
||||
from devplacepy.utils import require_admin, time_ago
|
||||
from devplacepy.seo import base_seo_context, site_url, website_schema
|
||||
from devplacepy.responses import respond, action_result
|
||||
from devplacepy.schemas import AdminNewsOut
|
||||
from devplacepy.services.audit import record as audit
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/news", response_class=HTMLResponse)
|
||||
async def admin_news(request: Request, page: int = 1):
|
||||
admin = require_admin(request)
|
||||
news_table = get_table("news")
|
||||
total = news_table.count(deleted_at=None)
|
||||
pagination = build_pagination(page, total)
|
||||
offset = (pagination["page"] - 1) * pagination["per_page"]
|
||||
page_articles = list(
|
||||
news_table.find(
|
||||
deleted_at=None,
|
||||
order_by=["-synced_at"],
|
||||
_limit=pagination["per_page"],
|
||||
_offset=offset,
|
||||
)
|
||||
)
|
||||
images_by_news = get_news_images_by_uids([a["uid"] for a in page_articles])
|
||||
|
||||
enriched = []
|
||||
for a in page_articles:
|
||||
enriched.append(
|
||||
{
|
||||
"article": a,
|
||||
"time_ago": time_ago(a["synced_at"]),
|
||||
"synced_at": a.get("synced_at", ""),
|
||||
"grade": a.get("grade", 0),
|
||||
"has_image": a["uid"] in images_by_news,
|
||||
}
|
||||
)
|
||||
|
||||
base = site_url(request)
|
||||
seo_ctx = base_seo_context(
|
||||
request,
|
||||
title="News - Admin",
|
||||
description="Manage DevPlace news articles.",
|
||||
robots="noindex,nofollow",
|
||||
breadcrumbs=[
|
||||
{"name": "Home", "url": "/feed"},
|
||||
{"name": "Admin", "url": "/admin"},
|
||||
{"name": "News", "url": "/admin/news"},
|
||||
],
|
||||
schemas=[website_schema(base)],
|
||||
)
|
||||
return respond(
|
||||
request,
|
||||
"admin_news.html",
|
||||
{
|
||||
**seo_ctx,
|
||||
"request": request,
|
||||
"user": admin,
|
||||
"articles": enriched,
|
||||
"pagination": pagination,
|
||||
"admin_section": "news",
|
||||
},
|
||||
model=AdminNewsOut,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/news/{uid}/toggle")
|
||||
async def admin_news_toggle(request: Request, uid: str):
|
||||
admin = require_admin(request)
|
||||
news_table = get_table("news")
|
||||
article = news_table.find_one(uid=uid)
|
||||
if article:
|
||||
current = article.get("featured", 0)
|
||||
new_value = 0 if current else 1
|
||||
news_table.update({"uid": uid, "featured": new_value}, ["uid"])
|
||||
logger.info(
|
||||
f"Admin {admin['username']} toggled news {uid} featured={'off' if current else 'on'}"
|
||||
)
|
||||
audit.record(
|
||||
request,
|
||||
"news.featured.toggle",
|
||||
user=admin,
|
||||
target_type="news",
|
||||
target_uid=uid,
|
||||
target_label=article.get("title"),
|
||||
old_value=current,
|
||||
new_value=new_value,
|
||||
summary=f"admin {admin['username']} toggled featured for news {article.get('title')}",
|
||||
links=[audit.target("news", uid, article.get("title"))],
|
||||
)
|
||||
return action_result(request, "/admin/news")
|
||||
|
||||
|
||||
@router.post("/news/{uid}/publish")
|
||||
async def admin_news_publish(request: Request, uid: str):
|
||||
admin = require_admin(request)
|
||||
news_table = get_table("news")
|
||||
article = news_table.find_one(uid=uid)
|
||||
if article:
|
||||
current = article.get("status", "draft")
|
||||
new_status = "draft" if current == "published" else "published"
|
||||
news_table.update({"uid": uid, "status": new_status}, ["uid"])
|
||||
logger.info(f"Admin {admin['username']} set news {uid} status={new_status}")
|
||||
audit.record(
|
||||
request,
|
||||
"news.publish.toggle",
|
||||
user=admin,
|
||||
target_type="news",
|
||||
target_uid=uid,
|
||||
target_label=article.get("title"),
|
||||
old_value=current,
|
||||
new_value=new_status,
|
||||
summary=f"admin {admin['username']} set news {article.get('title')} to {new_status}",
|
||||
links=[audit.target("news", uid, article.get("title"))],
|
||||
)
|
||||
return action_result(request, "/admin/news")
|
||||
|
||||
|
||||
@router.post("/news/{uid}/landing")
|
||||
async def admin_news_landing(request: Request, uid: str):
|
||||
admin = require_admin(request)
|
||||
news_table = get_table("news")
|
||||
article = news_table.find_one(uid=uid)
|
||||
if article:
|
||||
current = article.get("show_on_landing", 0)
|
||||
new_value = 0 if current else 1
|
||||
news_table.update({"uid": uid, "show_on_landing": new_value}, ["uid"])
|
||||
logger.info(
|
||||
f"Admin {admin['username']} toggled news {uid} landing={'on' if not current else 'off'}"
|
||||
)
|
||||
audit.record(
|
||||
request,
|
||||
"news.landing.toggle",
|
||||
user=admin,
|
||||
target_type="news",
|
||||
target_uid=uid,
|
||||
target_label=article.get("title"),
|
||||
old_value=current,
|
||||
new_value=new_value,
|
||||
summary=f"admin {admin['username']} toggled landing visibility for news {article.get('title')}",
|
||||
links=[audit.target("news", uid, article.get("title"))],
|
||||
)
|
||||
return action_result(request, "/admin/news")
|
||||
|
||||
|
||||
@router.post("/news/{uid}/delete")
|
||||
async def admin_news_delete(request: Request, uid: str):
|
||||
admin = require_admin(request)
|
||||
news_table = get_table("news")
|
||||
article = news_table.find_one(uid=uid, deleted_at=None)
|
||||
if article:
|
||||
stamp = _now_iso()
|
||||
image_count = soft_delete("news_images", admin["uid"], stamp=stamp, news_uid=uid)
|
||||
soft_delete("news", admin["uid"], stamp=stamp, uid=uid)
|
||||
logger.info(f"Admin {admin['username']} soft-deleted news {uid}")
|
||||
audit.record(
|
||||
request,
|
||||
"news.delete",
|
||||
user=admin,
|
||||
target_type="news",
|
||||
target_uid=uid,
|
||||
target_label=article.get("title"),
|
||||
metadata={"image_count": image_count},
|
||||
summary=f"admin {admin['username']} deleted news article {article.get('title')}",
|
||||
links=[audit.target("news", uid, article.get("title"))],
|
||||
)
|
||||
return action_result(request, "/admin/news")
|
||||
Reference in New Issue
Block a user