Add multi-user auth, settings area, and unified label feed
Turns Daybook into a multi-user tool and reworks the feed UI: - Multi-user: Django session login (username/password + remember-me), per-user personal API tokens (DRF authtoken), whole app gated behind login. Each artifact has an owner; feeds are private per user. - Rename persona -> source (free-text label for whatever posted an artifact: agent-profile, script, or person). source + tags form one unified label system; single label filter matches tags, source has its own filter/column/sidebar. - Settings area (gear/user menu, top-right): Account (view/regenerate token) and Demo Data (in-app button seeding one artifact per kind). User account management via Django admin (superusers only). - Feed rework: rolling 7-day window with sticky inline date separators and infinite scroll; left sidebar is now a Sources list with per-source unread counts; artifact type shown as text after title; source in its own column. - Unread tracking: read_at on Artifact, marked read when previewed, bold titles for unread, sidebar unread badges, old banner removed. - Tag colours: global shared registry, hash-based default with optional per-tag override via tag_colors on POST/PATCH. - Ingest: token resolves owner; same source name under two users' tokens stays isolated. daybook_post.py renamed persona->source, requires a real token, --check accepts a bearer token. Docs updated to match. Migrations 0004-0008 rename persona, add owner (nullable -> backfill to first superuser -> required), and add read_at. Fresh installs no-op the backfill. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
203f9b578e
commit
37cd96fa7a
33 changed files with 1614 additions and 367 deletions
87
artifacts/demo_data.py
Normal file
87
artifacts/demo_data.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
"""Sample artifact content for the Settings -> Demo Data button.
|
||||
|
||||
Creates one artifact of every supported kind, owned by the requesting user,
|
||||
tagged "demo", with source "demo".
|
||||
"""
|
||||
|
||||
SOURCE = "demo"
|
||||
|
||||
TEXT_SAMPLES = [
|
||||
(
|
||||
"markdown",
|
||||
"Seed: Markdown Sample",
|
||||
"# Markdown Sample\n\nThis is a **seed** artifact demonstrating markdown rendering.\n\n- item one\n- item two\n",
|
||||
"markdown",
|
||||
),
|
||||
(
|
||||
"html_fragment",
|
||||
"Seed: HTML Fragment Sample",
|
||||
"<h2>HTML Fragment</h2><p>This is a seed HTML fragment artifact.</p>",
|
||||
"html-fragment",
|
||||
),
|
||||
(
|
||||
"html_full",
|
||||
"Seed: Full HTML Sample",
|
||||
"<!doctype html><html><head><title>Seed Full HTML</title></head>"
|
||||
"<body><h1>Full HTML Doc</h1><p>Seed artifact.</p></body></html>",
|
||||
"html-full",
|
||||
),
|
||||
(
|
||||
"deck",
|
||||
"Seed: Deck Sample",
|
||||
"<section><h1>Slide One</h1><p>Seed deck artifact.</p></section>"
|
||||
"<section><h1>Slide Two</h1><p>More content.</p></section>",
|
||||
"deck",
|
||||
),
|
||||
]
|
||||
|
||||
SEED_PNG_BYTES = bytes.fromhex(
|
||||
"89504e470d0a1a0a0000000d494844520000000100000001080600000"
|
||||
"01f15c4890000000a49444154789c6360000002000155e59a1a0000000049454e44ae426082"
|
||||
)
|
||||
|
||||
SEED_PDF_BYTES = (
|
||||
b"%PDF-1.1\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n"
|
||||
b"2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj\n"
|
||||
b"3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 200 200]>>endobj\n"
|
||||
b"trailer<</Root 1 0 R>>\n"
|
||||
)
|
||||
|
||||
SEED_BIN_BYTES = b"\x00\x01\x02\x03seed-binary-data\xff\xfe"
|
||||
|
||||
BINARY_SAMPLES = [
|
||||
("Seed: Image Sample", "seed_pixel.png", "image/png", SEED_PNG_BYTES, "image"),
|
||||
("Seed: PDF Sample", "seed_doc.pdf", "application/pdf", SEED_PDF_BYTES, "pdf"),
|
||||
("Seed: Binary Sample", "seed_data.bin", "application/octet-stream", SEED_BIN_BYTES, "binary"),
|
||||
]
|
||||
|
||||
|
||||
def create_demo_artifacts(user, request):
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
from .views import create_binary_artifact, create_text_artifact
|
||||
|
||||
created = 0
|
||||
for kind, title, content, tag in TEXT_SAMPLES:
|
||||
data = {
|
||||
"source": SOURCE,
|
||||
"title": title,
|
||||
"kind": kind,
|
||||
"content": content,
|
||||
"tags": ["demo", tag],
|
||||
}
|
||||
create_text_artifact(data, request, user)
|
||||
created += 1
|
||||
|
||||
for title, filename, content_type, file_bytes, tag in BINARY_SAMPLES:
|
||||
uploaded = SimpleUploadedFile(filename, file_bytes, content_type=content_type)
|
||||
data = {
|
||||
"source": SOURCE,
|
||||
"title": title,
|
||||
"file": uploaded,
|
||||
"tags": ["demo", tag],
|
||||
}
|
||||
create_binary_artifact(data, user)
|
||||
created += 1
|
||||
|
||||
return created
|
||||
Loading…
Add table
Add a link
Reference in a new issue