Pablan, as it stands

Self-hosted knowledge management for SMEs: a split-screen Markdown editor
whose sections an LLM refines while you write, and RAG question answering
over the documents that result. FastAPI + Postgres/pgvector on the back,
SvelteKit on the front, everything OpenAI-compatible and self-hostable.

Squashed into a single commit; the development history stays local.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CA43ZJda8Rbp2hKXNy8f6b
This commit is contained in:
ProfessorNova
2026-09-04 08:36:17 +02:00
co-authored by Claude Opus 5
commit 97dbff309c
346 changed files with 43430 additions and 0 deletions
@@ -0,0 +1,94 @@
"""document events audit trail
An append-only history of who changed or reviewed a document, and when.
Content-bearing events (created / edited) snapshot the Markdown source of truth
so a past version can be viewed or diffed; the disposable chunks are never
snapshotted. `actor_id` is SET NULL so the trail outlives its actor's account;
events cascade with their document.
Revision ID: d5a9c1e3b7f2
Revises: c4f7a1b2e9d3
Create Date: 2026-07-22 00:00:00.000000
"""
from typing import Sequence, Union
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "d5a9c1e3b7f2"
down_revision: Union[str, Sequence[str], None] = "c4f7a1b2e9d3"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
op.create_table(
"document_events",
sa.Column("document_id", sa.Uuid(), nullable=False),
sa.Column("actor_id", sa.Uuid(), nullable=True),
sa.Column(
"action",
sa.Enum(
"created",
"edited",
"published",
"archived",
"visibility_changed",
"review_requested",
"review_resolved",
name="documenteventaction",
native_enum=False,
length=32,
),
nullable=False,
),
sa.Column("content_md", sa.Text(), nullable=True),
sa.Column("title", sa.String(length=500), nullable=True),
sa.Column(
"visibility",
sa.Enum(
"public",
"department",
"restricted",
name="documentvisibility",
native_enum=False,
length=32,
),
nullable=True,
),
sa.Column("meta", postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column("id", sa.Uuid(), nullable=False),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.ForeignKeyConstraint(["document_id"], ["documents.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["actor_id"], ["users.id"], ondelete="SET NULL"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_document_events_document_id"),
"document_events",
["document_id"],
unique=False,
)
def downgrade() -> None:
"""Downgrade schema."""
op.drop_index(op.f("ix_document_events_document_id"), table_name="document_events")
op.drop_table("document_events")