Skip to content

Commit 2e52ad8

Browse files
committed
refac: shared chat
1 parent 4d2f189 commit 2e52ad8

7 files changed

Lines changed: 818 additions & 241 deletions

File tree

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""Add shared_chat table and migrate existing shares
2+
3+
Revision ID: c1d2e3f4a5b6
4+
Revises: e1f2a3b4c5d6
5+
Create Date: 2026-04-16 23:00:00.000000
6+
7+
"""
8+
9+
import time
10+
import uuid
11+
12+
from alembic import op
13+
import sqlalchemy as sa
14+
15+
revision = 'c1d2e3f4a5b6'
16+
down_revision = 'e1f2a3b4c5d6'
17+
branch_labels = None
18+
depends_on = None
19+
20+
# Lightweight table references for data migration (no ORM models needed)
21+
chat_t = sa.table(
22+
'chat',
23+
sa.column('id', sa.Text),
24+
sa.column('user_id', sa.Text),
25+
sa.column('title', sa.Text),
26+
sa.column('chat', sa.JSON),
27+
sa.column('share_id', sa.Text),
28+
sa.column('created_at', sa.BigInteger),
29+
sa.column('updated_at', sa.BigInteger),
30+
sa.column('archived', sa.Boolean),
31+
sa.column('meta', sa.JSON),
32+
)
33+
34+
shared_chat_t = sa.table(
35+
'shared_chat',
36+
sa.column('id', sa.Text),
37+
sa.column('chat_id', sa.Text),
38+
sa.column('user_id', sa.Text),
39+
sa.column('title', sa.Text),
40+
sa.column('chat', sa.JSON),
41+
sa.column('created_at', sa.BigInteger),
42+
sa.column('updated_at', sa.BigInteger),
43+
)
44+
45+
chat_message_t = sa.table(
46+
'chat_message',
47+
sa.column('chat_id', sa.Text),
48+
)
49+
50+
access_grant_t = sa.table(
51+
'access_grant',
52+
sa.column('id', sa.Text),
53+
sa.column('resource_type', sa.Text),
54+
sa.column('resource_id', sa.Text),
55+
sa.column('principal_type', sa.Text),
56+
sa.column('principal_id', sa.Text),
57+
sa.column('permission', sa.Text),
58+
sa.column('created_at', sa.BigInteger),
59+
)
60+
61+
62+
def upgrade():
63+
conn = op.get_bind()
64+
65+
# 1. Create shared_chat table
66+
op.create_table(
67+
'shared_chat',
68+
sa.Column('id', sa.Text(), primary_key=True),
69+
sa.Column('chat_id', sa.Text(), sa.ForeignKey('chat.id', ondelete='CASCADE'), nullable=False),
70+
sa.Column('user_id', sa.Text(), nullable=False),
71+
sa.Column('title', sa.Text(), nullable=True),
72+
sa.Column('chat', sa.JSON(), nullable=True),
73+
sa.Column('created_at', sa.BigInteger(), nullable=True),
74+
sa.Column('updated_at', sa.BigInteger(), nullable=True),
75+
)
76+
77+
# 2. Migrate existing shared-* rows
78+
shared_rows = conn.execute(
79+
sa.select(
80+
chat_t.c.id,
81+
chat_t.c.user_id,
82+
chat_t.c.title,
83+
chat_t.c.chat,
84+
chat_t.c.created_at,
85+
chat_t.c.updated_at,
86+
).where(chat_t.c.user_id.like('shared-%'))
87+
).fetchall()
88+
89+
for row in shared_rows:
90+
share_token = row.id
91+
original_chat_id = row.user_id.replace('shared-', '', 1)
92+
93+
# Verify original chat still exists
94+
original = conn.execute(
95+
sa.select(chat_t.c.user_id).where(chat_t.c.id == original_chat_id)
96+
).fetchone()
97+
98+
if not original:
99+
continue
100+
101+
# Insert snapshot into shared_chat
102+
conn.execute(shared_chat_t.insert().values(
103+
id=share_token,
104+
chat_id=original_chat_id,
105+
user_id=original.user_id,
106+
title=row.title,
107+
chat=row.chat,
108+
created_at=row.created_at,
109+
updated_at=row.updated_at,
110+
))
111+
112+
# Create user:*:read grant for backward compat
113+
conn.execute(access_grant_t.insert().values(
114+
id=str(uuid.uuid4()),
115+
resource_type='shared_chat',
116+
resource_id=original_chat_id,
117+
principal_type='user',
118+
principal_id='*',
119+
permission='read',
120+
created_at=row.created_at or int(time.time()),
121+
))
122+
123+
# 3. Clean up old phantom rows
124+
conn.execute(
125+
chat_message_t.delete().where(
126+
chat_message_t.c.chat_id.in_(
127+
sa.select(chat_t.c.id).where(chat_t.c.user_id.like('shared-%'))
128+
)
129+
)
130+
)
131+
conn.execute(chat_t.delete().where(chat_t.c.user_id.like('shared-%')))
132+
133+
134+
def downgrade():
135+
conn = op.get_bind()
136+
137+
shared_rows = conn.execute(
138+
sa.select(
139+
shared_chat_t.c.id,
140+
shared_chat_t.c.chat_id,
141+
shared_chat_t.c.user_id,
142+
shared_chat_t.c.title,
143+
shared_chat_t.c.chat,
144+
shared_chat_t.c.created_at,
145+
shared_chat_t.c.updated_at,
146+
)
147+
).fetchall()
148+
149+
for row in shared_rows:
150+
conn.execute(chat_t.insert().values(
151+
id=row.id,
152+
user_id=f'shared-{row.chat_id}',
153+
title=row.title,
154+
chat=row.chat,
155+
created_at=row.created_at,
156+
updated_at=row.updated_at,
157+
archived=False,
158+
meta={},
159+
))
160+
161+
conn.execute(
162+
access_grant_t.delete().where(access_grant_t.c.resource_type == 'shared_chat')
163+
)
164+
op.drop_table('shared_chat')

0 commit comments

Comments
 (0)