Files
devplacepy/devplacepy/routers/gists.py
T

134 lines
5.0 KiB
Python
Raw Normal View History

import logging
from typing import Annotated
from fastapi import APIRouter, Request, Form
from devplacepy.models import GistForm, GistEditForm
from fastapi.responses import HTMLResponse, RedirectResponse
from devplacepy.database import get_table, get_users_by_uids, get_gist_languages
from devplacepy.content import load_detail, edit_content_item, delete_content_item, enrich_items, create_content_item, detail_context
from devplacepy.templating import templates
from devplacepy.utils import get_current_user, require_user, not_found, XP_GIST
from devplacepy.seo import base_seo_context, site_url, website_schema, software_source_code_schema, list_page_seo
logger = logging.getLogger(__name__)
router = APIRouter()
LANGUAGES = [
("python", "Python"), ("javascript", "JavaScript"), ("typescript", "TypeScript"),
("html", "HTML"), ("css", "CSS"), ("c", "C"), ("cpp", "C++"), ("java", "Java"),
("go", "Go"), ("rust", "Rust"), ("sql", "SQL"), ("bash", "Bash"),
("yaml", "YAML"), ("json", "JSON"), ("markdown", "Markdown"),
("swift", "Swift"), ("php", "PHP"), ("ruby", "Ruby"), ("kotlin", "Kotlin"),
("lua", "Lua"), ("perl", "Perl"), ("haskell", "Haskell"), ("elixir", "Elixir"),
("r", "R"), ("dart", "Dart"), ("scala", "Scala"), ("plaintext", "Plain Text"),
]
def get_gists_list(user_uid=None, language=None, viewer=None):
gists_table = get_table("gists")
filters = {}
if user_uid:
filters["user_uid"] = user_uid
if language:
filters["language"] = language
all_gists = list(gists_table.find(**filters, order_by=["-created_at"]))
if not all_gists:
return []
users_map = get_users_by_uids([g["user_uid"] for g in all_gists])
return enrich_items(all_gists, "gist", users_map, user=viewer)
@router.get("", response_class=HTMLResponse)
async def gists_page(request: Request, language: str = None, user_uid: str = None):
user = get_current_user(request)
gists_data = get_gists_list(user_uid, language, viewer=user)
total_count = len(gists_data)
seo_ctx = list_page_seo(
request,
title="Gists",
description=f"Browse {total_count} code snippets on DevPlace.",
breadcrumbs=[
{"name": "Home", "url": "/feed"},
{"name": "Gists", "url": "/gists"},
],
)
return templates.TemplateResponse(request, "gists.html", {
**seo_ctx,
"request": request,
"user": user,
"gists": gists_data,
"total_count": total_count,
"current_language": language,
"languages": LANGUAGES,
"gist_language_codes": get_gist_languages(),
})
@router.get("/{gist_slug}", response_class=HTMLResponse)
async def gist_detail(request: Request, gist_slug: str):
user = get_current_user(request)
detail = load_detail("gists", "gist", gist_slug, user)
if not detail:
raise not_found("Gist not found")
gist = detail["item"]
base = site_url(request)
seo_ctx = base_seo_context(
request,
title=gist.get("title", "Gist"),
description=gist.get("description", "")[:160],
og_type="article",
breadcrumbs=[
{"name": "Home", "url": "/feed"},
{"name": "Gists", "url": "/gists"},
{"name": gist.get("title", "Gist"), "url": f"/gists/{gist['slug'] or gist['uid']}"},
],
schemas=[website_schema(base), software_source_code_schema(gist, base)],
)
return templates.TemplateResponse(request, "gist_detail.html", detail_context(request, user, detail, "gist", seo_ctx, {
"languages": LANGUAGES,
}))
@router.post("/create")
async def create_gist(request: Request, data: Annotated[GistForm, Form()]):
user = require_user(request)
title = data.title.strip()
description = data.description.strip()
source_code = data.source_code.strip()
language = data.language
valid_languages = {l[0] for l in LANGUAGES}
if language not in valid_languages:
language = "plaintext"
uid, gist_slug = create_content_item("gists", "gist", user, {
"title": title,
"description": description or None,
"source_code": source_code,
"language": language,
}, title, XP_GIST, "First Gist", description or "", data.attachment_uids)
return RedirectResponse(url=f"/gists/{gist_slug}", status_code=302)
@router.post("/edit/{gist_slug}")
async def edit_gist(request: Request, gist_slug: str, data: Annotated[GistEditForm, Form()]):
user = require_user(request)
language = data.language
if language not in {l[0] for l in LANGUAGES}:
language = "plaintext"
return edit_content_item("gists", user, gist_slug, {
"title": data.title.strip(),
"description": data.description.strip() or None,
"source_code": data.source_code.strip(),
"language": language,
}, "/gists")
@router.post("/delete/{gist_slug}")
async def delete_gist(request: Request, gist_slug: str):
user = require_user(request)
return delete_content_item("gists", "gist", user, gist_slug, "/gists")