Skip to content
Merged

Dev #234

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
145 commits
Select commit Hold shift + click to select a range
69722ba
fix/refac: workspace shared model list
tjbck Nov 25, 2025
1bfe2c9
Merge pull request #19464 from aleixdorca/dev
aleixdorca Nov 25, 2025
c7eb713
fix: user preview profile image
tjbck Nov 25, 2025
c5b73d7
refac/fix: function name filter type
tjbck Nov 25, 2025
477097c
refac
tjbck Nov 25, 2025
8b2015a
refac
tjbck Nov 25, 2025
4df5b7e
fix: update dependency to prevent rediss:// failure (#19488)
Classic298 Nov 25, 2025
c631659
i18n: de-de (#19471)
Classic298 Nov 25, 2025
4370dee
fix: async save docs to vector db
tjbck Nov 25, 2025
9fca496
chore: dep bump pypdf to ver 6.4.0 (#19508)
Classic298 Nov 26, 2025
4b21704
chore: Update pymilvus dep (#19507)
Classic298 Nov 26, 2025
d071cdf
chore: update transformers dependency to fix issue #19512 (#19513)
Classic298 Nov 26, 2025
f2d6a42
feat: also consider OAUTH_ROLES_SEPARATOR for string claims themselve…
gerhardj-b Nov 26, 2025
fa0efae
i18n: improve Chinese translation (#19497)
ShirasawaSama Nov 26, 2025
9f89cc5
refac
tjbck Nov 27, 2025
d1bbf6b
refac
tjbck Nov 27, 2025
3fe5a47
refac/enh: knowledge base name on icon hover
tjbck Nov 27, 2025
384753c
refac/enh: drop profile_image_url field in responses
tjbck Nov 27, 2025
04b3373
fix: correct role check on OAuth login (#19476)
tobiasge Nov 27, 2025
457af65
enh/feat: toggle folders & user perm
tjbck Nov 27, 2025
5a32ea9
refac
tjbck Nov 27, 2025
86cdcda
fix: button without type (#19534)
stevessr Nov 27, 2025
ff4b1b9
refac: chat history data structure
tjbck Nov 27, 2025
b5e5617
enh: redis dict for internal models state
tjbck Nov 27, 2025
c7a48c5
Update catalan translation.json (#19536)
aleixdorca Nov 27, 2025
64b4d5d
feat/enh: channels unread messages count
tjbck Nov 27, 2025
28659f6
refac/fix: files batch/add endpoint
tjbck Nov 27, 2025
09b6ea3
feat/enh: group export endpoint
tjbck Nov 27, 2025
421aba7
refac: hide channel add button for users
tjbck Nov 27, 2025
7a374ca
refac
tjbck Nov 27, 2025
dd6b808
refac
tjbck Nov 27, 2025
f2c56fc
refac
tjbck Nov 27, 2025
acccb9a
feat: dm channels
tjbck Nov 27, 2025
f1a7de9
refac
tjbck Nov 27, 2025
d5d0e72
refac
tjbck Nov 27, 2025
3b4d7d5
refac
tjbck Nov 27, 2025
d645cdb
refac
tjbck Nov 27, 2025
6752772
chore: format
tjbck Nov 27, 2025
560702a
refac
tjbck Nov 27, 2025
6bb204e
refac
tjbck Nov 27, 2025
289801b
refac: styling
tjbck Nov 27, 2025
ad86707
refac
tjbck Nov 27, 2025
022f9ff
Update french translation.json (#19547)
RomualdYT Nov 27, 2025
99a7823
refac: db
tjbck Nov 28, 2025
32c888c
refac
tjbck Nov 28, 2025
15dc607
refac: rm print
tjbck Nov 28, 2025
6ee5077
refac
tjbck Nov 28, 2025
453ea9b
refac/fix: db migration issue
tjbck Nov 28, 2025
848f3fd
refac: hide active user count in sidebar user menu
tjbck Nov 28, 2025
d232e43
refac: profile preview
tjbck Nov 28, 2025
4b67738
enh: dm active user indicator
tjbck Nov 28, 2025
b99c9b2
refac: styling
tjbck Nov 28, 2025
369298a
refac: user table db migration
tjbck Nov 28, 2025
0a4358c
refac: oauth_sub -> oauth migration
tjbck Nov 28, 2025
742832a
refac
tjbck Nov 28, 2025
dcf50c4
refac: api_key table migration
tjbck Nov 28, 2025
8ef482a
refac: user oauth display
tjbck Nov 28, 2025
c2634d4
refac
tjbck Nov 28, 2025
70948f8
enh/refac: deprecate USER_POOL
tjbck Nov 28, 2025
33b59ad
refac
tjbck Nov 28, 2025
fc06c16
Merge pull request #19573 from open-webui/update-user-table
tjbck Nov 28, 2025
0518749
refac: pin icons
tjbck Nov 28, 2025
1b095d1
refac: admin user list active indicator
tjbck Nov 28, 2025
451907c
refac
tjbck Nov 28, 2025
aae2fce
feat/enh: pinned messages in channels
tjbck Nov 28, 2025
7b1895e
refac
tjbck Nov 28, 2025
80fbb29
refac: styling
tjbck Nov 28, 2025
b9e849f
refac: styling
tjbck Nov 28, 2025
25994dd
refac/enh: channel message
tjbck Nov 28, 2025
c8071a3
refac
tjbck Nov 28, 2025
0f31566
refac/fix: ollama model delete
tjbck Nov 28, 2025
a7c7993
refac/fix: temp chat image generation
tjbck Nov 28, 2025
c1d7606
refac: db group
tjbck Nov 29, 2025
ff12141
refac
tjbck Nov 29, 2025
6c53bf7
refac: styling
tjbck Nov 29, 2025
20340c3
refac
tjbck Nov 29, 2025
bb4b547
Update middleware.py
Classic298 Nov 29, 2025
356e982
refac
tjbck Nov 29, 2025
b56adf0
Merge pull request #19584 from Classic298/patch-1
tjbck Nov 29, 2025
fb6b18f
refac: knowledge file delete behaviour
tjbck Nov 29, 2025
05e79bd
enh: message reaction user names
tjbck Nov 29, 2025
c9185aa
refac
tjbck Nov 29, 2025
f3c8c70
refac
tjbck Nov 29, 2025
e65d92f
refac
tjbck Nov 30, 2025
9d39b9b
refac: styling
tjbck Nov 30, 2025
69b82ed
refac
tjbck Nov 30, 2025
3ebb3e2
refac: styling
tjbck Nov 30, 2025
4d74e6c
refac: styling
tjbck Nov 30, 2025
515f85f
refac
tjbck Nov 30, 2025
696f356
refac
tjbck Nov 30, 2025
f589b7c
feat/enh: group channel
tjbck Nov 30, 2025
781aeeb
refac
tjbck Nov 30, 2025
3f1d9cc
feat/enh: add/remove users from group channel
tjbck Nov 30, 2025
3964510
refac
tjbck Nov 30, 2025
3c84661
refac
tjbck Nov 30, 2025
a0826ec
feat/enh: dm from user profile preview
tjbck Nov 30, 2025
1818f2b
Update translation.json (pt-BR) (#19603)
joaoback Nov 30, 2025
277f3a9
refac
tjbck Nov 30, 2025
d499c3a
refac
tjbck Nov 30, 2025
88decab
refac
tjbck Nov 30, 2025
c62609f
refac
tjbck Nov 30, 2025
9791c9b
refac
tjbck Nov 30, 2025
25f0c26
chore: otel bump
tjbck Dec 1, 2025
91473c7
chore: otel bump
tjbck Dec 1, 2025
21f3411
i18n: improve Chinese translation (#19651)
ShirasawaSama Dec 1, 2025
dba86bc
fix: audit
tjbck Dec 1, 2025
51621ba
feat/enh: user status
tjbck Dec 1, 2025
f5e8d4d
refac
tjbck Dec 1, 2025
52ccab8
refac
tjbck Dec 1, 2025
4f50571
Chore: dep bump (#19667)
Classic298 Dec 2, 2025
734c04e
refac
tjbck Dec 2, 2025
7b16637
feat: signin rate limit
tjbck Dec 2, 2025
0a14196
Update milvus_multitenancy.py (#19680)
Classic298 Dec 2, 2025
5388cc1
refac
tjbck Dec 2, 2025
562f229
refac
tjbck Dec 2, 2025
6e53167
fix/adjust web search to properly block domains (#19670)
kjpoccia Dec 2, 2025
143d3fb
refac
tjbck Dec 2, 2025
9f42b93
refac
tjbck Dec 2, 2025
aa589fc
refac
tjbck Dec 2, 2025
954aaa6
refac: styling
tjbck Dec 2, 2025
39f7575
refac: show connection type for custom models
tjbck Dec 2, 2025
6ce9afd
refac
tjbck Dec 2, 2025
29236ae
refac
tjbck Dec 2, 2025
d190232
feat/enh: kb files db migration
tjbck Dec 2, 2025
9f6c919
refac
tjbck Dec 2, 2025
e301d19
refac/perf: has_access_to_file optimization
tjbck Dec 2, 2025
01868e8
enh: group members endpoint
tjbck Dec 2, 2025
34169b3
refac
tjbck Dec 2, 2025
e5c6b73
refac
tjbck Dec 2, 2025
a7e614c
feat: Adds document intelligence model configuration (#19692)
HennieLP Dec 2, 2025
17bfd38
Fix dropdown backgrounds (#19693)
matthew-kusz Dec 2, 2025
192c2af
refac
tjbck Dec 2, 2025
12f237f
fix: Update milvus.py (#19602)
Classic298 Dec 2, 2025
b29fdc2
Update milvus_multitenancy.py (#19695)
Classic298 Dec 2, 2025
864d540
Update translation.json (#19696)
Classic298 Dec 2, 2025
9a65ed2
chore: format
tjbck Dec 2, 2025
a49e1d8
fix: Default Group ID assignment on SSO/OAUTH and LDAP (#19685)
Classic298 Dec 2, 2025
4f9677f
Update translation.json (#19697)
Classic298 Dec 2, 2025
9d87688
chore: bump
tjbck Dec 2, 2025
11efb98
refac
tjbck Dec 2, 2025
8361f73
chore: 0.6.41 Changelog (#19473)
Classic298 Dec 2, 2025
73f7e91
chore: format
tjbck Dec 2, 2025
6f1486f
Merge pull request #19466 from open-webui/dev
tjbck Dec 2, 2025
296904e
Merge branch 'oui_main' into dev
OrenZhang Dec 3, 2025
fc5aab4
chore(repo): merge from remote
OrenZhang Dec 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions CHANGELOG_EXTRA.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.6.41.1] - 2025.12.03

### Changed

- 合并官方 0.6.41 改动

## [0.6.40.2] - 2025.11.27

### Changed
Expand Down
36 changes: 32 additions & 4 deletions backend/open_webui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,14 +634,16 @@ def __getattr__(self, key):
os.environ.get("OAUTH_ROLES_CLAIM", "roles"),
)

SEP = os.environ.get("OAUTH_ROLES_SEPARATOR", ",")
OAUTH_ROLES_SEPARATOR = os.environ.get("OAUTH_ROLES_SEPARATOR", ",")

OAUTH_ALLOWED_ROLES = PersistentConfig(
"OAUTH_ALLOWED_ROLES",
"oauth.allowed_roles",
[
role.strip()
for role in os.environ.get("OAUTH_ALLOWED_ROLES", f"user{SEP}admin").split(SEP)
for role in os.environ.get(
"OAUTH_ALLOWED_ROLES", f"user{OAUTH_ROLES_SEPARATOR}admin"
).split(OAUTH_ROLES_SEPARATOR)
if role
],
)
Expand All @@ -651,7 +653,9 @@ def __getattr__(self, key):
"oauth.admin_roles",
[
role.strip()
for role in os.environ.get("OAUTH_ADMIN_ROLES", "admin").split(SEP)
for role in os.environ.get("OAUTH_ADMIN_ROLES", "admin").split(
OAUTH_ROLES_SEPARATOR
)
if role
],
)
Expand Down Expand Up @@ -1480,10 +1484,18 @@ def feishu_oauth_register(oauth: OAuth):
== "true"
)

USER_PERMISSIONS_FEATURES_FOLDERS = (
os.environ.get("USER_PERMISSIONS_FEATURES_FOLDERS", "True").lower() == "true"
)

USER_PERMISSIONS_FEATURES_NOTES = (
os.environ.get("USER_PERMISSIONS_FEATURES_NOTES", "True").lower() == "true"
)

USER_PERMISSIONS_FEATURES_CHANNELS = (
os.environ.get("USER_PERMISSIONS_FEATURES_CHANNELS", "True").lower() == "true"
)

USER_PERMISSIONS_FEATURES_API_KEYS = (
os.environ.get("USER_PERMISSIONS_FEATURES_API_KEYS", "False").lower() == "true"
)
Expand Down Expand Up @@ -1535,12 +1547,16 @@ def feishu_oauth_register(oauth: OAuth):
"temporary_enforced": USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED,
},
"features": {
# General features
"api_keys": USER_PERMISSIONS_FEATURES_API_KEYS,
"notes": USER_PERMISSIONS_FEATURES_NOTES,
"folders": USER_PERMISSIONS_FEATURES_FOLDERS,
"channels": USER_PERMISSIONS_FEATURES_CHANNELS,
"direct_tool_servers": USER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERS,
# Chat features
"web_search": USER_PERMISSIONS_FEATURES_WEB_SEARCH,
"image_generation": USER_PERMISSIONS_FEATURES_IMAGE_GENERATION,
"code_interpreter": USER_PERMISSIONS_FEATURES_CODE_INTERPRETER,
"notes": USER_PERMISSIONS_FEATURES_NOTES,
},
}

Expand All @@ -1550,6 +1566,12 @@ def feishu_oauth_register(oauth: OAuth):
DEFAULT_USER_PERMISSIONS,
)

ENABLE_FOLDERS = PersistentConfig(
"ENABLE_FOLDERS",
"folders.enable",
os.environ.get("ENABLE_FOLDERS", "True").lower() == "true",
)

ENABLE_CHANNELS = PersistentConfig(
"ENABLE_CHANNELS",
"channels.enable",
Expand Down Expand Up @@ -2589,6 +2611,12 @@ class BannerModel(BaseModel):
os.getenv("DOCUMENT_INTELLIGENCE_KEY", ""),
)

DOCUMENT_INTELLIGENCE_MODEL = PersistentConfig(
"DOCUMENT_INTELLIGENCE_MODEL",
"rag.document_intelligence_model",
os.getenv("DOCUMENT_INTELLIGENCE_MODEL", "prebuilt-layout"),
)

MISTRAL_OCR_API_BASE_URL = PersistentConfig(
"MISTRAL_OCR_API_BASE_URL",
"rag.MISTRAL_OCR_API_BASE_URL",
Expand Down
25 changes: 17 additions & 8 deletions backend/open_webui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@
from open_webui.utils.credit.utils import is_free_request, check_credit_by_user_id
from open_webui.utils.logger import start_logger
from open_webui.socket.main import (
MODELS,
app as socket_app,
periodic_usage_pool_cleanup,
get_event_emitter,
get_models_in_use,
get_active_user_ids,
)
from open_webui.routers import (
audio,
Expand Down Expand Up @@ -263,6 +263,7 @@
DOCLING_PARAMS,
DOCUMENT_INTELLIGENCE_ENDPOINT,
DOCUMENT_INTELLIGENCE_KEY,
DOCUMENT_INTELLIGENCE_MODEL,
MISTRAL_OCR_API_BASE_URL,
MISTRAL_OCR_API_KEY,
RAG_TEXT_SPLITTER,
Expand Down Expand Up @@ -342,6 +343,7 @@
ENABLE_API_KEYS,
ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS,
API_KEYS_ALLOWED_ENDPOINTS,
ENABLE_FOLDERS,
ENABLE_CHANNELS,
ENABLE_NOTES,
ENABLE_COMMUNITY_SHARING,
Expand Down Expand Up @@ -791,6 +793,8 @@ async def lifespan(app: FastAPI):
app.state.config.WEBHOOK_URL = WEBHOOK_URL
app.state.config.BANNERS = WEBUI_BANNERS


app.state.config.ENABLE_FOLDERS = ENABLE_FOLDERS
app.state.config.ENABLE_CHANNELS = ENABLE_CHANNELS
app.state.config.ENABLE_NOTES = ENABLE_NOTES
app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING
Expand Down Expand Up @@ -892,6 +896,7 @@ async def lifespan(app: FastAPI):
app.state.config.DOCLING_PARAMS = DOCLING_PARAMS
app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = DOCUMENT_INTELLIGENCE_ENDPOINT
app.state.config.DOCUMENT_INTELLIGENCE_KEY = DOCUMENT_INTELLIGENCE_KEY
app.state.config.DOCUMENT_INTELLIGENCE_MODEL = DOCUMENT_INTELLIGENCE_MODEL
app.state.config.MISTRAL_OCR_API_BASE_URL = MISTRAL_OCR_API_BASE_URL
app.state.config.MISTRAL_OCR_API_KEY = MISTRAL_OCR_API_KEY
app.state.config.MINERU_API_MODE = MINERU_API_MODE
Expand Down Expand Up @@ -1000,9 +1005,7 @@ async def lifespan(app: FastAPI):

try:
app.state.ef = get_ef(
app.state.config.RAG_EMBEDDING_ENGINE,
app.state.config.RAG_EMBEDDING_MODEL,
RAG_EMBEDDING_MODEL_AUTO_UPDATE,
app.state.config.RAG_EMBEDDING_ENGINE, app.state.config.RAG_EMBEDDING_MODEL
)
if (
app.state.config.ENABLE_RAG_HYBRID_SEARCH
Expand All @@ -1013,7 +1016,6 @@ async def lifespan(app: FastAPI):
app.state.config.RAG_RERANKING_MODEL,
app.state.config.RAG_EXTERNAL_RERANKER_URL,
app.state.config.RAG_EXTERNAL_RERANKER_API_KEY,
RAG_RERANKING_MODEL_AUTO_UPDATE,
)
else:
app.state.rf = None
Expand Down Expand Up @@ -1269,7 +1271,7 @@ async def lifespan(app: FastAPI):
#
########################################

app.state.MODELS = {}
app.state.MODELS = MODELS

# Add the middleware to the app
if ENABLE_COMPRESSION_MIDDLEWARE:
Expand Down Expand Up @@ -1642,6 +1644,7 @@ async def chat_completion(
"user_id": user.id,
"chat_id": form_data.pop("chat_id", None),
"message_id": form_data.pop("id", None),
"parent_message_id": form_data.pop("parent_id", None),
"session_id": form_data.pop("session_id", None),
"filter_ids": form_data.pop("filter_ids", []),
"tool_ids": form_data.get("tool_ids", None),
Expand Down Expand Up @@ -1702,6 +1705,7 @@ async def process_chat(request, form_data, user, metadata, model):
metadata["chat_id"],
metadata["message_id"],
{
"parentId": metadata.get("parent_message_id", None),
"model": model_id,
},
)
Expand Down Expand Up @@ -1734,6 +1738,7 @@ async def process_chat(request, form_data, user, metadata, model):
metadata["chat_id"],
metadata["message_id"],
{
"parentId": metadata.get("parent_message_id", None),
"error": {"content": str(e)},
},
)
Expand Down Expand Up @@ -1914,6 +1919,7 @@ async def get_app_config(request: Request):
**(
{
"enable_direct_connections": app.state.config.ENABLE_DIRECT_CONNECTIONS,
"enable_folders": app.state.config.ENABLE_FOLDERS,
"enable_channels": app.state.config.ENABLE_CHANNELS,
"enable_notes": app.state.config.ENABLE_NOTES,
"enable_web_search": app.state.config.ENABLE_WEB_SEARCH,
Expand Down Expand Up @@ -2086,7 +2092,10 @@ async def get_current_usage(user=Depends(get_verified_user)):
This is an experimental endpoint and subject to change.
"""
try:
return {"model_ids": get_models_in_use(), "user_ids": get_active_user_ids()}
return {
"model_ids": get_models_in_use(),
"user_count": Users.get_active_user_count(),
}
except Exception as e:
log.error(f"Error getting usage statistics: {e}")
raise HTTPException(status_code=500, detail="Internal Server Error")
Expand Down Expand Up @@ -2149,7 +2158,7 @@ async def get_current_usage(user=Depends(get_verified_user)):
)


async def register_client(self, request, client_id: str) -> bool:
async def register_client(request, client_id: str) -> bool:
server_type, server_id = client_id.split(":", 1)

connection = None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Update messages and channel member table

Revision ID: 2f1211949ecc
Revises: 37f288994c47
Create Date: 2025-11-27 03:07:56.200231

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
import open_webui.internal.db


# revision identifiers, used by Alembic.
revision: str = "2f1211949ecc"
down_revision: Union[str, None] = "37f288994c47"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# New columns to be added to channel_member table
op.add_column("channel_member", sa.Column("status", sa.Text(), nullable=True))
op.add_column(
"channel_member",
sa.Column(
"is_active",
sa.Boolean(),
nullable=False,
default=True,
server_default=sa.sql.expression.true(),
),
)

op.add_column(
"channel_member",
sa.Column(
"is_channel_muted",
sa.Boolean(),
nullable=False,
default=False,
server_default=sa.sql.expression.false(),
),
)
op.add_column(
"channel_member",
sa.Column(
"is_channel_pinned",
sa.Boolean(),
nullable=False,
default=False,
server_default=sa.sql.expression.false(),
),
)

op.add_column("channel_member", sa.Column("data", sa.JSON(), nullable=True))
op.add_column("channel_member", sa.Column("meta", sa.JSON(), nullable=True))

op.add_column(
"channel_member", sa.Column("joined_at", sa.BigInteger(), nullable=False)
)
op.add_column(
"channel_member", sa.Column("left_at", sa.BigInteger(), nullable=True)
)

op.add_column(
"channel_member", sa.Column("last_read_at", sa.BigInteger(), nullable=True)
)

op.add_column(
"channel_member", sa.Column("updated_at", sa.BigInteger(), nullable=True)
)

# New columns to be added to message table
op.add_column(
"message",
sa.Column(
"is_pinned",
sa.Boolean(),
nullable=False,
default=False,
server_default=sa.sql.expression.false(),
),
)
op.add_column("message", sa.Column("pinned_at", sa.BigInteger(), nullable=True))
op.add_column("message", sa.Column("pinned_by", sa.Text(), nullable=True))


def downgrade() -> None:
op.drop_column("channel_member", "updated_at")
op.drop_column("channel_member", "last_read_at")

op.drop_column("channel_member", "meta")
op.drop_column("channel_member", "data")

op.drop_column("channel_member", "is_channel_pinned")
op.drop_column("channel_member", "is_channel_muted")

op.drop_column("message", "pinned_by")
op.drop_column("message", "pinned_at")
op.drop_column("message", "is_pinned")
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,46 @@


def upgrade() -> None:
# Ensure 'id' column in 'user' table is unique and primary key (ForeignKey constraint)
inspector = sa.inspect(op.get_bind())
columns = inspector.get_columns("user")

pk_columns = inspector.get_pk_constraint("user")["constrained_columns"]
id_column = next((col for col in columns if col["name"] == "id"), None)

if id_column and not id_column.get("unique", False):
unique_constraints = inspector.get_unique_constraints("user")
unique_columns = {tuple(u["column_names"]) for u in unique_constraints}

with op.batch_alter_table("user") as batch_op:
# If primary key is wrong, drop it
if pk_columns and pk_columns != ["id"]:
batch_op.drop_constraint(
inspector.get_pk_constraint("user")["name"], type_="primary"
)

# Add unique constraint if missing
if ("id",) not in unique_columns:
batch_op.create_unique_constraint("uq_user_id", ["id"])

# Re-create correct primary key
batch_op.create_primary_key("pk_user_id", ["id"])

# Create oauth_session table
op.create_table(
"oauth_session",
sa.Column("id", sa.Text(), nullable=False),
sa.Column("user_id", sa.Text(), nullable=False),
sa.Column("id", sa.Text(), primary_key=True, nullable=False, unique=True),
sa.Column(
"user_id",
sa.Text(),
sa.ForeignKey("user.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("provider", sa.Text(), nullable=False),
sa.Column("token", sa.Text(), nullable=False),
sa.Column("expires_at", sa.BigInteger(), nullable=False),
sa.Column("created_at", sa.BigInteger(), nullable=False),
sa.Column("updated_at", sa.BigInteger(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"),
)

# Create indexes for better performance
Expand Down
Loading
Loading