feat: add account management, email verification, AI fixes, and user guides

- Profile settings, account transfer, delete/leave account flows
- Email verification with JWT tokens and Resend integration
- AI assistant/copilot fixes: markdown rendering, shared RAG helpers,
  token tracking, input refocus, model_validate usage
- User guides hub + detail pages with 13 topic guides
- Sidebar and top bar navigation for guides

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-04 19:18:06 -05:00
parent 1aa60dada2
commit 8d6accaf60
45 changed files with 2255 additions and 126 deletions

View File

@@ -0,0 +1,30 @@
"""Add user profile fields (phone, job_title, timezone, avatar_url, email_verified_at)
Revision ID: 040
Revises: fb1481317ff6
Create Date: 2026-03-03
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers
revision = "040"
down_revision = "e2d81e82ea5e"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column("users", sa.Column("phone", sa.String(50), nullable=True))
op.add_column("users", sa.Column("job_title", sa.String(255), nullable=True))
op.add_column("users", sa.Column("timezone", sa.String(100), nullable=False, server_default="UTC"))
op.add_column("users", sa.Column("avatar_url", sa.String(500), nullable=True))
op.add_column("users", sa.Column("email_verified_at", sa.DateTime(timezone=True), nullable=True))
def downgrade() -> None:
op.drop_column("users", "email_verified_at")
op.drop_column("users", "avatar_url")
op.drop_column("users", "timezone")
op.drop_column("users", "job_title")
op.drop_column("users", "phone")

View File

@@ -0,0 +1,30 @@
"""Add email_verification_tokens table
Revision ID: 041
Revises: 040
Create Date: 2026-03-03
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = "041"
down_revision = "040"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"email_verification_tokens",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
sa.Column("token_hash", sa.String(64), unique=True, nullable=False, index=True),
sa.Column("user_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id"), nullable=False, index=True),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("used_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
def downgrade() -> None:
op.drop_table("email_verification_tokens")

View File

@@ -18,7 +18,6 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Enable pgvector extension
op.execute("CREATE EXTENSION IF NOT EXISTS vector")
op.create_table(
@@ -31,21 +30,17 @@ def upgrade() -> None:
sa.Column("node_id", sa.String(100), nullable=True),
sa.Column("chunk_text", sa.Text(), nullable=False),
sa.Column("embedding_model", sa.String(50), nullable=False, server_default="voyage-3.5"),
sa.Column("embedding", sa.Column.__class__, nullable=True), # placeholder, replaced below
sa.Column("meta", postgresql.JSONB(), nullable=False, server_default=sa.text("'{}'::jsonb")),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()")),
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()")),
)
# Drop the placeholder embedding column and add the vector column
op.drop_column("tree_embeddings", "embedding")
op.execute("ALTER TABLE tree_embeddings ADD COLUMN embedding vector(1024)")
# Indexes
op.create_index("ix_tree_embeddings_account_id", "tree_embeddings", ["account_id"])
op.create_index("ix_tree_embeddings_tree_id", "tree_embeddings", ["tree_id"])
def downgrade() -> None:
op.drop_table("tree_embeddings")
op.execute("DROP EXTENSION IF EXISTS vector")
op.execute("DROP EXTENSION IF EXISTS vector")