Add secure admin panel for content moderation and contribution flagging
Adds an admin interface with authentication for manual content deletion and flagging. Implements a flagging system for user contributions and secures the admin panel with a secret token. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 923ae0e3-a363-4db8-b04a-e8baca2a1330 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 7e5834b1-796d-4a9e-bbde-cd91012292de Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8af7d2ec-2cc3-4ece-8af3-9f071488d072/923ae0e3-a363-4db8-b04a-e8baca2a1330/nghZcOj Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -46,10 +46,10 @@ def init_db():
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
""")
|
||||
cur.execute("""
|
||||
ALTER TABLE ideas
|
||||
ADD COLUMN IF NOT EXISTS legal_basis TEXT
|
||||
""")
|
||||
cur.execute("ALTER TABLE ideas ADD COLUMN IF NOT EXISTS legal_basis TEXT")
|
||||
cur.execute("ALTER TABLE ideas ADD COLUMN IF NOT EXISTS flagged BOOLEAN NOT NULL DEFAULT FALSE")
|
||||
cur.execute("ALTER TABLE ideas ADD COLUMN IF NOT EXISTS flag_count INTEGER NOT NULL DEFAULT 0")
|
||||
cur.execute("ALTER TABLE ideas ADD COLUMN IF NOT EXISTS admin_note TEXT")
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS synthesis (
|
||||
id SERIAL PRIMARY KEY,
|
||||
@@ -91,6 +91,97 @@ def get_all_ideas(limit: int = 50) -> list[dict]:
|
||||
return [dict(row) for row in cur.fetchall()]
|
||||
|
||||
|
||||
def get_ideas_admin(status: str = "all", page: int = 1,
|
||||
per_page: int = 50, search: str = "") -> tuple[list[dict], int]:
|
||||
offset = (page - 1) * per_page
|
||||
conditions = []
|
||||
params: list = []
|
||||
|
||||
if status == "accepted":
|
||||
conditions.append("accepted = TRUE")
|
||||
elif status == "rejected":
|
||||
conditions.append("accepted = FALSE AND flagged = FALSE")
|
||||
elif status == "flagged":
|
||||
conditions.append("flagged = TRUE")
|
||||
|
||||
if search:
|
||||
conditions.append("content ILIKE %s")
|
||||
params.append(f"%{search}%")
|
||||
|
||||
where = ("WHERE " + " AND ".join(conditions)) if conditions else ""
|
||||
|
||||
with db_cursor() as cur:
|
||||
cur.execute(f"SELECT COUNT(*) as total FROM ideas {where}", params)
|
||||
total = cur.fetchone()["total"]
|
||||
cur.execute(
|
||||
f"SELECT * FROM ideas {where} ORDER BY flagged DESC, created_at DESC LIMIT %s OFFSET %s",
|
||||
params + [per_page, offset],
|
||||
)
|
||||
rows = [dict(row) for row in cur.fetchall()]
|
||||
return rows, total
|
||||
|
||||
|
||||
def delete_idea(idea_id: int) -> bool:
|
||||
with db_cursor() as cur:
|
||||
cur.execute("DELETE FROM ideas WHERE id = %s RETURNING id", (idea_id,))
|
||||
return cur.fetchone() is not None
|
||||
|
||||
|
||||
def bulk_delete_ideas(idea_ids: list[int]) -> int:
|
||||
if not idea_ids:
|
||||
return 0
|
||||
with db_cursor() as cur:
|
||||
cur.execute(
|
||||
"DELETE FROM ideas WHERE id = ANY(%s) RETURNING id",
|
||||
(idea_ids,),
|
||||
)
|
||||
return len(cur.fetchall())
|
||||
|
||||
|
||||
def override_idea(idea_id: int, accepted: bool,
|
||||
reason: str | None, note: str | None) -> dict | None:
|
||||
with db_cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE ideas
|
||||
SET accepted = %s,
|
||||
rejection_reason = %s,
|
||||
flagged = FALSE,
|
||||
admin_note = %s
|
||||
WHERE id = %s
|
||||
RETURNING *
|
||||
""",
|
||||
(accepted, reason, note, idea_id),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def flag_idea(idea_id: int) -> dict | None:
|
||||
with db_cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE ideas
|
||||
SET flagged = TRUE, flag_count = flag_count + 1
|
||||
WHERE id = %s AND accepted = TRUE
|
||||
RETURNING *
|
||||
""",
|
||||
(idea_id,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def unflag_idea(idea_id: int) -> dict | None:
|
||||
with db_cursor() as cur:
|
||||
cur.execute(
|
||||
"UPDATE ideas SET flagged = FALSE WHERE id = %s RETURNING *",
|
||||
(idea_id,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def get_stats() -> dict:
|
||||
with db_cursor() as cur:
|
||||
cur.execute("SELECT COUNT(*) as total FROM ideas")
|
||||
@@ -99,7 +190,9 @@ def get_stats() -> dict:
|
||||
accepted = cur.fetchone()["accepted"]
|
||||
cur.execute("SELECT COUNT(*) as rejected FROM ideas WHERE accepted = FALSE")
|
||||
rejected = cur.fetchone()["rejected"]
|
||||
return {"total": total, "accepted": accepted, "rejected": rejected}
|
||||
cur.execute("SELECT COUNT(*) as flagged FROM ideas WHERE flagged = TRUE")
|
||||
flagged = cur.fetchone()["flagged"]
|
||||
return {"total": total, "accepted": accepted, "rejected": rejected, "flagged": flagged}
|
||||
|
||||
|
||||
def upsert_synthesis(text: str, idea_count: int) -> dict:
|
||||
|
||||
Reference in New Issue
Block a user