A project is only ever at /p/<id>/<slug>; / is the index of the projects
you own or edit. Every project has an owner, who can name editors;
anyone with the link can view. Every edit saves itself, one request in
flight at a time, as a patch of the leaves that changed, and a websocket
(channels + daphne) carries presence and each committed write to
everyone else in the project. The first write to a leaf wins, and the
loser is told.
Undo is per person: a step undoes only if the leaves it touched still
hold what it left, so it never takes a collaborator's work with it.
Named snapshots replace saving, and restore as an ordinary write.
An empty symbol now survives the leaf round trip with `:nodes {}`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
1.9 KiB
Python
42 lines
1.9 KiB
Python
"""The API, which is nine endpoints and no framework.
|
|
|
|
The shape is RFC 7232 over addressed resources: a leaf is a resource, its version
|
|
is an entity tag, and a conditional write answers 409. docs/architecture.md is
|
|
explicit that this part is not a bespoke invention — "optimistic concurrency
|
|
control over addressed resources with an entity tag" is what HTTP has done for
|
|
thirty years — so the plumbing here is deliberately boring.
|
|
|
|
WRITES ARE ON HTTP AND STAY THERE. When the websocket arrives it carries presence
|
|
and broadcasts, and not writes: auth, idempotency, status codes, retries and
|
|
conditional requests all come for free here, and a dropped socket cannot lose a
|
|
write.
|
|
"""
|
|
from django.urls import path
|
|
|
|
from . import views
|
|
|
|
urlpatterns = [
|
|
path("me", views.me),
|
|
path("login", views.login),
|
|
path("signup", views.signup),
|
|
path("logout", views.logout),
|
|
path("detector", views.detector),
|
|
path("sources", views.sources),
|
|
path("extractions", views.extractions),
|
|
path("extractions/<str:key>", views.extraction_detail),
|
|
path("footage", views.footage_list),
|
|
path("footage/<uuid:footage_id>", views.footage_detail),
|
|
path("projects", views.projects),
|
|
path("symbols", views.symbols),
|
|
path("projects/<uuid:project_id>", views.project_detail),
|
|
path("projects/<uuid:project_id>/leaves/<path:leaf_path>", views.leaf_detail),
|
|
path("projects/<uuid:project_id>/revisions", views.revisions),
|
|
path("projects/<uuid:project_id>/revisions/<int:revision_id>/restore", views.restore),
|
|
path("projects/<uuid:project_id>/editors", views.editors),
|
|
path("projects/<uuid:project_id>/editors/<str:username>", views.editors),
|
|
path("analyses", views.analyses),
|
|
path("analyses/<str:key>", views.analysis_detail),
|
|
path("blocks", views.blocks),
|
|
path("blocks/missing", views.blocks_missing),
|
|
path("blocks/<str:key>", views.block_detail),
|
|
]
|