Vanty App: headless content & blog (pages, collections, tags, authors, sitemap).
Project description
Vanty Content
Headless content & blog toolkit for FastAPI with Tortoise ORM. Pages with
BlockNote bodies, collections, tags, authors, content calendars, public
blog endpoints, signed preview tokens, and an XML sitemap — in a single
pip install.
Installation
pip install vanty-content
# or
uv pip install vanty-content
Quick start
from contextlib import asynccontextmanager
from fastapi import FastAPI
from vanty_content import ContentSettings, mount_content_router
settings = ContentSettings(
database_url="sqlite://./vanty-content.db",
base_url="https://example.com",
preview_secret="change-me",
)
app = FastAPI()
kit = mount_content_router(app, settings)
app.include_router(kit.admin_router)
@asynccontextmanager
async def lifespan(_: FastAPI):
await kit.init_orm(generate_schemas=True)
try:
yield
finally:
await kit.close_orm()
app.router.lifespan_context = lifespan
The package depends on vanty-core for events and the
OrganizationScopedModel base, and integrates with vanty-auth only via
the AuthContext resolver — never via direct ORM imports.
For documentation, see your hosted Docs site (Fumadocs / external) — this package no longer ships any docs surface itself.
BlockNote round-trip
Page bodies are opaque BlockNote JSON: a list[dict] of block nodes. The
package never parses inner blocks; bodies round-trip exactly between
write and read.
from uuid import uuid4
from vanty_content import ContentApp, ContentSettings
from vanty_content.schemas.page import PageCreate
org_id = uuid4()
kit = ContentApp(ContentSettings(database_url="sqlite://:memory:"))
await kit.init_orm(generate_schemas=True)
body = [
{"type": "paragraph", "content": [{"type": "text", "text": "Hello"}]},
{
"type": "heading",
"props": {"level": 2},
"content": [{"type": "text", "text": "World"}],
},
]
page = await kit.page_service.create(
org_id, PageCreate(title="Hello, world", slug="hello-world", body=body)
)
await kit.page_service.publish(org_id, page.id)
# GET /content/blog/hello-world returns identical body bytes.
fresh = await kit.page_service.get_by_slug(org_id, "hello-world", published_only=True)
assert fresh.body == body
Public endpoints
Mounted under prefix="/content" by default:
| Method | Path | Description |
|---|---|---|
GET |
/content/blog |
Paginated published posts. Filters: collection, tag, page, size. |
GET |
/content/blog/{slug} |
Single published post by slug. |
GET |
/content/preview/{page_id} |
Preview a draft page using ?token=. |
GET |
/content/sitemap.xml |
XML sitemap of all published pages for the resolved organization. |
The organization is resolved from the X-Organization-ID header, the
current vanty-auth AuthContext, or app.state.content_organization_id_override
(used in tests).
Admin endpoints
ContentApp.admin_router is mounted by the host with
require_superuser enforced. Routes live under /admin/content/:
pages, collections, tags, authors, and
projects/{id}/calendar.
Events
All events are typed dataclasses on the shared vanty-core event bus.
from vanty_core.events import on
from vanty_content.events import PagePublished
@on(PagePublished)
async def push_to_cdn(event: PagePublished) -> None:
...
| Event name | Dataclass |
|---|---|
vanty_content.page.created |
PageCreated |
vanty_content.page.updated |
PageUpdated |
vanty_content.page.published |
PagePublished |
vanty_content.page.unpublished |
PageUnpublished |
vanty_content.page.scheduled |
PageScheduled |
vanty_content.page.deleted |
PageDeleted |
vanty_content.collection.created |
CollectionCreated |
vanty_content.author.created |
AuthorCreated |
vanty_content.calendar.item_scheduled |
CalendarItemScheduled |
Publish to GitHub
This package lives inside the Vanty monorepo at
vanty-all/vanty-content/. To publish a standalone, history-preserving
GitHub repo using git filter-repo:
make publish-github REPO=git@github.com:advantch/vanty-content.git
The Makefile clones the monorepo to a scratch directory, filters to the
vanty-content/ subdirectory, points origin at the requested repo,
and prints the final git push command — it never pushes for you.
License
MIT — Themba themba@advantch.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vanty_content-0.3.0.tar.gz.
File metadata
- Download URL: vanty_content-0.3.0.tar.gz
- Upload date:
- Size: 91.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fa2b0fb283f56089ded50d5ab885e625c5c1d4da793fc1e4d51e82a1a36e99b
|
|
| MD5 |
160317a57a718ce942f62c0450b22d02
|
|
| BLAKE2b-256 |
b17922649d87f3dee8fef2a259bc418da027ff19b7a5818d956d7a65ff8ec5d5
|
Provenance
The following attestation bundles were made for vanty_content-0.3.0.tar.gz:
Publisher:
release.yml on advantch/vanty-content
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vanty_content-0.3.0.tar.gz -
Subject digest:
3fa2b0fb283f56089ded50d5ab885e625c5c1d4da793fc1e4d51e82a1a36e99b - Sigstore transparency entry: 1409607030
- Sigstore integration time:
-
Permalink:
advantch/vanty-content@09a701ffddc66180177d4349243d1839a68dc194 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/advantch
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@09a701ffddc66180177d4349243d1839a68dc194 -
Trigger Event:
push
-
Statement type:
File details
Details for the file vanty_content-0.3.0-py3-none-any.whl.
File metadata
- Download URL: vanty_content-0.3.0-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
907c26f39a2894b817d09d1e69abf0a0e51df0190e6614c6cd49f721820aecb0
|
|
| MD5 |
33f375a05eb03de7f8cf8570b7e3b735
|
|
| BLAKE2b-256 |
c019adba9dbb18f85a5c48f913e058eb06974789007bf951a31d7a1f8d611726
|
Provenance
The following attestation bundles were made for vanty_content-0.3.0-py3-none-any.whl:
Publisher:
release.yml on advantch/vanty-content
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vanty_content-0.3.0-py3-none-any.whl -
Subject digest:
907c26f39a2894b817d09d1e69abf0a0e51df0190e6614c6cd49f721820aecb0 - Sigstore transparency entry: 1409607063
- Sigstore integration time:
-
Permalink:
advantch/vanty-content@09a701ffddc66180177d4349243d1839a68dc194 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/advantch
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@09a701ffddc66180177d4349243d1839a68dc194 -
Trigger Event:
push
-
Statement type: