Files
devplacepy/devplacepy/routers/gists.py
T

226 lines
6.1 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
from devplacepy.database import (
get_table,
get_users_by_uids,
get_gist_languages,
paginate,
)
from devplacepy.content import (
load_detail,
edit_content_item,
delete_content_item,
enrich_items,
create_content_item,
detail_context,
canonical_redirect,
first_image_url,
)
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,
next_page_url,
)
from devplacepy.responses import respond, action_result
from devplacepy.schemas import GistsOut, GistDetailOut
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, before=None, viewer=None):
gists_table = get_table("gists")
filters = {}
if user_uid:
filters["user_uid"] = user_uid
if language:
filters["language"] = language
total = gists_table.count(**filters)
gists, next_cursor = paginate(gists_table, before=before, **filters)
if not gists:
return [], next_cursor, total
users_map = get_users_by_uids([g["user_uid"] for g in gists])
return enrich_items(gists, "gist", users_map, user=viewer), next_cursor, total
@router.get("", response_class=HTMLResponse)
async def gists_page(
request: Request, language: str = None, user_uid: str = None, before: str = None
):
user = get_current_user(request)
gists_data, next_cursor, total_count = get_gists_list(
user_uid, language, before, viewer=user
)
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"},
],
next_url=next_page_url(request, next_cursor),
)
return respond(
request,
"gists.html",
{
**seo_ctx,
"request": request,
"user": user,
"gists": gists_data,
"total_count": total_count,
"next_cursor": next_cursor,
"current_language": language,
"languages": LANGUAGES,
"gist_language_codes": get_gist_languages(),
},
model=GistsOut,
)
@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"]
redirect = canonical_redirect("gists", gist, gist_slug)
if redirect:
return redirect
base = site_url(request)
seo_ctx = base_seo_context(
request,
title=gist.get("title", "Gist"),
description=gist.get("description", "")[:160],
og_type="article",
og_image=first_image_url(gist, detail["attachments"]),
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 respond(
request,
"gist_detail.html",
detail_context(
request,
user,
detail,
"gist",
seo_ctx,
{
"languages": LANGUAGES,
},
),
model=GistDetailOut,
)
@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 = {lang[0] for lang 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,
)
url = f"/gists/{gist_slug}"
return action_result(request, url, data={"uid": uid, "slug": gist_slug, "url": url})
@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 {lang[0] for lang in LANGUAGES}:
language = "plaintext"
return edit_content_item(
request,
"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(request, "gists", "gist", user, gist_slug, "/gists")