refac: async db

This commit is contained in:
Timothy Jaeryang Baek
2026-04-12 14:22:11 -05:00
parent b618d84065
commit 27169124f2
74 changed files with 4831 additions and 4479 deletions
+244 -242
View File
@@ -4,8 +4,9 @@ import time
import uuid
from typing import Optional
from sqlalchemy.orm import Session
from open_webui.internal.db import Base, JSONField, get_db, get_db_context
from sqlalchemy import select, delete, update, func, case, or_, and_
from sqlalchemy.ext.asyncio import AsyncSession
from open_webui.internal.db import Base, JSONField, get_async_db_context
from open_webui.models.groups import Groups
from open_webui.models.access_grants import (
AccessGrantModel,
@@ -25,11 +26,7 @@ from sqlalchemy import (
Text,
JSON,
UniqueConstraint,
case,
cast,
)
from sqlalchemy import or_, func, select, and_, text
from sqlalchemy.sql import exists
####################
# Channel DB Schema
@@ -249,22 +246,22 @@ class ChannelWebhookForm(BaseModel):
class ChannelTable:
def _get_access_grants(self, channel_id: str, db: Optional[Session] = None) -> list[AccessGrantModel]:
return AccessGrants.get_grants_by_resource('channel', channel_id, db=db)
async def _get_access_grants(self, channel_id: str, db: Optional[AsyncSession] = None) -> list[AccessGrantModel]:
return await AccessGrants.get_grants_by_resource('channel', channel_id, db=db)
def _to_channel_model(
async def _to_channel_model(
self,
channel: Channel,
access_grants: Optional[list[AccessGrantModel]] = None,
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> ChannelModel:
channel_data = ChannelModel.model_validate(channel).model_dump(exclude={'access_grants'})
channel_data['access_grants'] = (
access_grants if access_grants is not None else self._get_access_grants(channel_data['id'], db=db)
access_grants if access_grants is not None else await self._get_access_grants(channel_data['id'], db=db)
)
return ChannelModel.model_validate(channel_data)
def _collect_unique_user_ids(
async def _collect_unique_user_ids(
self,
invited_by: str,
user_ids: Optional[list[str]] = None,
@@ -281,7 +278,8 @@ class ChannelTable:
users.add(invited_by)
for group_id in group_ids or []:
users.update(Groups.get_group_user_ids_by_id(group_id))
group_user_ids = await Groups.get_group_user_ids_by_id(group_id)
users.update(group_user_ids)
return users
@@ -321,10 +319,20 @@ class ChannelTable:
return memberships
def insert_new_channel(
self, form_data: CreateChannelForm, user_id: str, db: Optional[Session] = None
def _has_permission(self, db, query, filter: dict, permission: str = 'read'):
return AccessGrants.has_permission_filter(
db=db,
query=query,
DocumentModel=Channel,
filter=filter,
resource_type='channel',
permission=permission,
)
async def insert_new_channel(
self, form_data: CreateChannelForm, user_id: str, db: Optional[AsyncSession] = None
) -> Optional[ChannelModel]:
with get_db_context(db) as db:
async with get_async_db_context(db) as db:
channel = ChannelModel(
**{
**form_data.model_dump(exclude={'access_grants'}),
@@ -340,7 +348,7 @@ class ChannelTable:
new_channel = Channel(**channel.model_dump(exclude={'access_grants'}))
if form_data.type in ['group', 'dm']:
users = self._collect_unique_user_ids(
users = await self._collect_unique_user_ids(
invited_by=user_id,
user_ids=form_data.user_ids,
group_ids=form_data.group_ids,
@@ -353,17 +361,18 @@ class ChannelTable:
db.add_all(memberships)
db.add(new_channel)
db.commit()
AccessGrants.set_access_grants('channel', new_channel.id, form_data.access_grants, db=db)
return self._to_channel_model(new_channel, db=db)
await db.commit()
await AccessGrants.set_access_grants('channel', new_channel.id, form_data.access_grants, db=db)
return await self._to_channel_model(new_channel, db=db)
def get_channels(self, db: Optional[Session] = None) -> list[ChannelModel]:
with get_db_context(db) as db:
channels = db.query(Channel).all()
async def get_channels(self, db: Optional[AsyncSession] = None) -> list[ChannelModel]:
async with get_async_db_context(db) as db:
result = await db.execute(select(Channel))
channels = result.scalars().all()
channel_ids = [channel.id for channel in channels]
grants_map = AccessGrants.get_grants_by_resources('channel', channel_ids, db=db)
grants_map = await AccessGrants.get_grants_by_resources('channel', channel_ids, db=db)
return [
self._to_channel_model(
await self._to_channel_model(
channel,
access_grants=grants_map.get(channel.id, []),
db=db,
@@ -371,22 +380,12 @@ class ChannelTable:
for channel in channels
]
def _has_permission(self, db, query, filter: dict, permission: str = 'read'):
return AccessGrants.has_permission_filter(
db=db,
query=query,
DocumentModel=Channel,
filter=filter,
resource_type='channel',
permission=permission,
)
async def get_channels_by_user_id(self, user_id: str, db: Optional[AsyncSession] = None) -> list[ChannelModel]:
async with get_async_db_context(db) as db:
user_group_ids = [group.id for group in await Groups.get_groups_by_member_id(user_id, db=db)]
def get_channels_by_user_id(self, user_id: str, db: Optional[Session] = None) -> list[ChannelModel]:
with get_db_context(db) as db:
user_group_ids = [group.id for group in Groups.get_groups_by_member_id(user_id, db=db)]
membership_channels = (
db.query(Channel)
result = await db.execute(
select(Channel)
.join(ChannelMember, Channel.id == ChannelMember.channel_id)
.filter(
Channel.deleted_at.is_(None),
@@ -395,10 +394,10 @@ class ChannelTable:
ChannelMember.user_id == user_id,
ChannelMember.is_active.is_(True),
)
.all()
)
membership_channels = result.scalars().all()
query = db.query(Channel).filter(
stmt = select(Channel).filter(
Channel.deleted_at.is_(None),
Channel.archived_at.is_(None),
or_(
@@ -407,17 +406,18 @@ class ChannelTable:
and_(Channel.type != 'group', Channel.type != 'dm'),
),
)
query = self._has_permission(db, query, {'user_id': user_id, 'group_ids': user_group_ids})
stmt = self._has_permission(db, stmt, {'user_id': user_id, 'group_ids': user_group_ids})
standard_channels = query.all()
result = await db.execute(stmt)
standard_channels = result.scalars().all()
all_channels = membership_channels + standard_channels
all_channels = list(membership_channels) + list(standard_channels)
channel_ids = [c.id for c in all_channels]
grants_map = AccessGrants.get_grants_by_resources('channel', channel_ids, db=db)
return [self._to_channel_model(c, access_grants=grants_map.get(c.id, []), db=db) for c in all_channels]
grants_map = await AccessGrants.get_grants_by_resources('channel', channel_ids, db=db)
return [await self._to_channel_model(c, access_grants=grants_map.get(c.id, []), db=db) for c in all_channels]
def get_dm_channel_by_user_ids(self, user_ids: list[str], db: Optional[Session] = None) -> Optional[ChannelModel]:
with get_db_context(db) as db:
async def get_dm_channel_by_user_ids(self, user_ids: list[str], db: Optional[AsyncSession] = None) -> Optional[ChannelModel]:
async with get_async_db_context(db) as db:
# Ensure uniqueness in case a list with duplicates is passed
unique_user_ids = list(set(user_ids))
@@ -429,7 +429,7 @@ class ChannelTable:
)
subquery = (
db.query(ChannelMember.channel_id)
select(ChannelMember.channel_id)
.group_by(ChannelMember.channel_id)
# 1. Channel must have exactly len(user_ids) members
.having(func.count(ChannelMember.user_id) == len(unique_user_ids))
@@ -438,33 +438,34 @@ class ChannelTable:
.subquery()
)
channel = (
db.query(Channel)
result = await db.execute(
select(Channel)
.filter(
Channel.id.in_(subquery),
Channel.id.in_(select(subquery.c.channel_id)),
Channel.type == 'dm',
)
.first()
.limit(1)
)
channel = result.scalars().first()
return self._to_channel_model(channel, db=db) if channel else None
return await self._to_channel_model(channel, db=db) if channel else None
def add_members_to_channel(
async def add_members_to_channel(
self,
channel_id: str,
invited_by: str,
user_ids: Optional[list[str]] = None,
group_ids: Optional[list[str]] = None,
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> list[ChannelMemberModel]:
with get_db_context(db) as db:
async with get_async_db_context(db) as db:
# 1. Collect all user_ids including groups + inviter
requested_users = self._collect_unique_user_ids(invited_by, user_ids, group_ids)
requested_users = await self._collect_unique_user_ids(invited_by, user_ids, group_ids)
existing_users = {
row.user_id
for row in db.query(ChannelMember.user_id).filter(ChannelMember.channel_id == channel_id).all()
}
result = await db.execute(
select(ChannelMember.user_id).filter(ChannelMember.channel_id == channel_id)
)
existing_users = {row[0] for row in result.all()}
new_user_ids = requested_users - existing_users
if not new_user_ids:
@@ -473,59 +474,54 @@ class ChannelTable:
new_memberships = self._create_membership_models(channel_id, invited_by, new_user_ids)
db.add_all(new_memberships)
db.commit()
await db.commit()
return [ChannelMemberModel.model_validate(membership) for membership in new_memberships]
def remove_members_from_channel(
async def remove_members_from_channel(
self,
channel_id: str,
user_ids: list[str],
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> int:
with get_db_context(db) as db:
result = (
db.query(ChannelMember)
.filter(
async with get_async_db_context(db) as db:
result = await db.execute(
delete(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id.in_(user_ids),
)
.delete(synchronize_session=False)
)
db.commit()
return result # number of rows deleted
await db.commit()
return result.rowcount # number of rows deleted
def is_user_channel_manager(self, channel_id: str, user_id: str, db: Optional[Session] = None) -> bool:
with get_db_context(db) as db:
# Check if the user is the creator of the channel
# or has a 'manager' role in ChannelMember
channel = db.query(Channel).filter(Channel.id == channel_id).first()
async def is_user_channel_manager(self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None) -> bool:
async with get_async_db_context(db) as db:
result = await db.execute(select(Channel).filter(Channel.id == channel_id))
channel = result.scalars().first()
if channel and channel.user_id == user_id:
return True
membership = (
db.query(ChannelMember)
.filter(
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
ChannelMember.is_active.is_(True),
ChannelMember.role == 'manager',
)
.first()
)
membership = result.scalars().first()
return membership is not None
def join_channel(self, channel_id: str, user_id: str, db: Optional[Session] = None) -> Optional[ChannelMemberModel]:
with get_db_context(db) as db:
async def join_channel(self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None) -> Optional[ChannelMemberModel]:
async with get_async_db_context(db) as db:
# Check if the membership already exists
existing_membership = (
db.query(ChannelMember)
.filter(
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
existing_membership = result.scalars().first()
if existing_membership:
return ChannelMemberModel.model_validate(existing_membership)
@@ -549,19 +545,18 @@ class ChannelTable:
new_membership = ChannelMember(**channel_member.model_dump())
db.add(new_membership)
db.commit()
await db.commit()
return channel_member
def leave_channel(self, channel_id: str, user_id: str, db: Optional[Session] = None) -> bool:
with get_db_context(db) as db:
membership = (
db.query(ChannelMember)
.filter(
async def leave_channel(self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None) -> bool:
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
membership = result.scalars().first()
if not membership:
return False
@@ -570,126 +565,127 @@ class ChannelTable:
membership.left_at = int(time.time_ns())
membership.updated_at = int(time.time_ns())
db.commit()
await db.commit()
return True
def get_member_by_channel_and_user_id(
self, channel_id: str, user_id: str, db: Optional[Session] = None
async def get_member_by_channel_and_user_id(
self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None
) -> Optional[ChannelMemberModel]:
with get_db_context(db) as db:
membership = (
db.query(ChannelMember)
.filter(
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
membership = result.scalars().first()
return ChannelMemberModel.model_validate(membership) if membership else None
def get_members_by_channel_id(self, channel_id: str, db: Optional[Session] = None) -> list[ChannelMemberModel]:
with get_db_context(db) as db:
memberships = db.query(ChannelMember).filter(ChannelMember.channel_id == channel_id).all()
async def get_members_by_channel_id(self, channel_id: str, db: Optional[AsyncSession] = None) -> list[ChannelMemberModel]:
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelMember).filter(ChannelMember.channel_id == channel_id)
)
memberships = result.scalars().all()
return [ChannelMemberModel.model_validate(membership) for membership in memberships]
def pin_channel(
async def pin_channel(
self,
channel_id: str,
user_id: str,
is_pinned: bool,
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> bool:
with get_db_context(db) as db:
membership = (
db.query(ChannelMember)
.filter(
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
membership = result.scalars().first()
if not membership:
return False
membership.is_channel_pinned = is_pinned
membership.updated_at = int(time.time_ns())
db.commit()
await db.commit()
return True
def update_member_last_read_at(self, channel_id: str, user_id: str, db: Optional[Session] = None) -> bool:
with get_db_context(db) as db:
membership = (
db.query(ChannelMember)
.filter(
async def update_member_last_read_at(self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None) -> bool:
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
membership = result.scalars().first()
if not membership:
return False
membership.last_read_at = int(time.time_ns())
membership.updated_at = int(time.time_ns())
db.commit()
await db.commit()
return True
def update_member_active_status(
async def update_member_active_status(
self,
channel_id: str,
user_id: str,
is_active: bool,
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> bool:
with get_db_context(db) as db:
membership = (
db.query(ChannelMember)
.filter(
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
membership = result.scalars().first()
if not membership:
return False
membership.is_active = is_active
membership.updated_at = int(time.time_ns())
db.commit()
await db.commit()
return True
def is_user_channel_member(self, channel_id: str, user_id: str, db: Optional[Session] = None) -> bool:
with get_db_context(db) as db:
membership = (
db.query(ChannelMember)
.filter(
async def is_user_channel_member(self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None) -> bool:
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
ChannelMember.is_active.is_(True),
)
.first()
).limit(1)
)
membership = result.scalars().first()
return membership is not None
def get_channel_by_id(self, id: str, db: Optional[Session] = None) -> Optional[ChannelModel]:
async def get_channel_by_id(self, id: str, db: Optional[AsyncSession] = None) -> Optional[ChannelModel]:
try:
with get_db_context(db) as db:
channel = db.query(Channel).filter(Channel.id == id).first()
return self._to_channel_model(channel, db=db) if channel else None
async with get_async_db_context(db) as db:
result = await db.execute(select(Channel).filter(Channel.id == id))
channel = result.scalars().first()
return await self._to_channel_model(channel, db=db) if channel else None
except Exception:
return None
def get_channels_by_file_id(self, file_id: str, db: Optional[Session] = None) -> list[ChannelModel]:
with get_db_context(db) as db:
channel_files = db.query(ChannelFile).filter(ChannelFile.file_id == file_id).all()
async def get_channels_by_file_id(self, file_id: str, db: Optional[AsyncSession] = None) -> list[ChannelModel]:
async with get_async_db_context(db) as db:
result = await db.execute(select(ChannelFile).filter(ChannelFile.file_id == file_id))
channel_files = result.scalars().all()
channel_ids = [cf.channel_id for cf in channel_files]
channels = db.query(Channel).filter(Channel.id.in_(channel_ids)).all()
grants_map = AccessGrants.get_grants_by_resources('channel', channel_ids, db=db)
result = await db.execute(select(Channel).filter(Channel.id.in_(channel_ids)))
channels = result.scalars().all()
grants_map = await AccessGrants.get_grants_by_resources('channel', channel_ids, db=db)
return [
self._to_channel_model(
await self._to_channel_model(
channel,
access_grants=grants_map.get(channel.id, []),
db=db,
@@ -697,123 +693,123 @@ class ChannelTable:
for channel in channels
]
def get_channels_by_file_id_and_user_id(
self, file_id: str, user_id: str, db: Optional[Session] = None
async def get_channels_by_file_id_and_user_id(
self, file_id: str, user_id: str, db: Optional[AsyncSession] = None
) -> list[ChannelModel]:
with get_db_context(db) as db:
async with get_async_db_context(db) as db:
# 1. Determine which channels have this file
channel_file_rows = db.query(ChannelFile).filter(ChannelFile.file_id == file_id).all()
result = await db.execute(select(ChannelFile).filter(ChannelFile.file_id == file_id))
channel_file_rows = result.scalars().all()
channel_ids = [row.channel_id for row in channel_file_rows]
if not channel_ids:
return []
# 2. Load all channel rows that still exist
channels = (
db.query(Channel)
.filter(
result = await db.execute(
select(Channel).filter(
Channel.id.in_(channel_ids),
Channel.deleted_at.is_(None),
Channel.archived_at.is_(None),
)
.all()
)
channels = result.scalars().all()
if not channels:
return []
# Preload user's group membership
user_group_ids = [g.id for g in Groups.get_groups_by_member_id(user_id, db=db)]
user_group_ids = [g.id for g in await Groups.get_groups_by_member_id(user_id, db=db)]
allowed_channels = []
for channel in channels:
# --- Case A: group or dm => user must be an active member ---
if channel.type in ['group', 'dm']:
membership = (
db.query(ChannelMember)
.filter(
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == channel.id,
ChannelMember.user_id == user_id,
ChannelMember.is_active.is_(True),
)
.first()
).limit(1)
)
membership = result.scalars().first()
if membership:
allowed_channels.append(self._to_channel_model(channel, db=db))
allowed_channels.append(await self._to_channel_model(channel, db=db))
continue
# --- Case B: standard channel => rely on ACL permissions ---
query = db.query(Channel).filter(Channel.id == channel.id)
stmt = select(Channel).filter(Channel.id == channel.id)
query = self._has_permission(
stmt = self._has_permission(
db,
query,
stmt,
{'user_id': user_id, 'group_ids': user_group_ids},
permission='read',
)
allowed = query.first()
result = await db.execute(stmt)
allowed = result.scalars().first()
if allowed:
allowed_channels.append(self._to_channel_model(allowed, db=db))
allowed_channels.append(await self._to_channel_model(allowed, db=db))
return allowed_channels
def get_channel_by_id_and_user_id(
self, id: str, user_id: str, db: Optional[Session] = None
async def get_channel_by_id_and_user_id(
self, id: str, user_id: str, db: Optional[AsyncSession] = None
) -> Optional[ChannelModel]:
with get_db_context(db) as db:
async with get_async_db_context(db) as db:
# Fetch the channel
channel: Channel = (
db.query(Channel)
.filter(
result = await db.execute(
select(Channel).filter(
Channel.id == id,
Channel.deleted_at.is_(None),
Channel.archived_at.is_(None),
)
.first()
)
channel = result.scalars().first()
if not channel:
return None
# If the channel is a group or dm, read access requires membership (active)
if channel.type in ['group', 'dm']:
membership = (
db.query(ChannelMember)
.filter(
result = await db.execute(
select(ChannelMember).filter(
ChannelMember.channel_id == id,
ChannelMember.user_id == user_id,
ChannelMember.is_active.is_(True),
)
.first()
).limit(1)
)
membership = result.scalars().first()
if membership:
return self._to_channel_model(channel, db=db)
return await self._to_channel_model(channel, db=db)
else:
return None
# For channels that are NOT group/dm, fall back to ACL-based read access
query = db.query(Channel).filter(Channel.id == id)
stmt = select(Channel).filter(Channel.id == id)
# Determine user groups
user_group_ids = [group.id for group in Groups.get_groups_by_member_id(user_id, db=db)]
user_group_ids = [group.id for group in await Groups.get_groups_by_member_id(user_id, db=db)]
# Apply ACL rules
query = self._has_permission(
stmt = self._has_permission(
db,
query,
stmt,
{'user_id': user_id, 'group_ids': user_group_ids},
permission='read',
)
channel_allowed = query.first()
return self._to_channel_model(channel_allowed, db=db) if channel_allowed else None
result = await db.execute(stmt)
channel_allowed = result.scalars().first()
return await self._to_channel_model(channel_allowed, db=db) if channel_allowed else None
def update_channel_by_id(
self, id: str, form_data: ChannelForm, db: Optional[Session] = None
async def update_channel_by_id(
self, id: str, form_data: ChannelForm, db: Optional[AsyncSession] = None
) -> Optional[ChannelModel]:
with get_db_context(db) as db:
channel = db.query(Channel).filter(Channel.id == id).first()
async with get_async_db_context(db) as db:
result = await db.execute(select(Channel).filter(Channel.id == id))
channel = result.scalars().first()
if not channel:
return None
@@ -825,16 +821,16 @@ class ChannelTable:
channel.meta = form_data.meta
if form_data.access_grants is not None:
AccessGrants.set_access_grants('channel', id, form_data.access_grants, db=db)
await AccessGrants.set_access_grants('channel', id, form_data.access_grants, db=db)
channel.updated_at = int(time.time_ns())
db.commit()
return self._to_channel_model(channel, db=db) if channel else None
await db.commit()
return await self._to_channel_model(channel, db=db) if channel else None
def add_file_to_channel_by_id(
self, channel_id: str, file_id: str, user_id: str, db: Optional[Session] = None
async def add_file_to_channel_by_id(
self, channel_id: str, file_id: str, user_id: str, db: Optional[AsyncSession] = None
) -> Optional[ChannelFileModel]:
with get_db_context(db) as db:
async with get_async_db_context(db) as db:
channel_file = ChannelFileModel(
**{
'id': str(uuid.uuid4()),
@@ -849,8 +845,8 @@ class ChannelTable:
try:
result = ChannelFile(**channel_file.model_dump())
db.add(result)
db.commit()
db.refresh(result)
await db.commit()
await db.refresh(result)
if result:
return ChannelFileModel.model_validate(result)
else:
@@ -858,55 +854,58 @@ class ChannelTable:
except Exception:
return None
def set_file_message_id_in_channel_by_id(
async def set_file_message_id_in_channel_by_id(
self,
channel_id: str,
file_id: str,
message_id: str,
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> bool:
try:
with get_db_context(db) as db:
channel_file = db.query(ChannelFile).filter_by(channel_id=channel_id, file_id=file_id).first()
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelFile).filter_by(channel_id=channel_id, file_id=file_id)
)
channel_file = result.scalars().first()
if not channel_file:
return False
channel_file.message_id = message_id
channel_file.updated_at = int(time.time())
db.commit()
await db.commit()
return True
except Exception:
return False
def remove_file_from_channel_by_id(self, channel_id: str, file_id: str, db: Optional[Session] = None) -> bool:
async def remove_file_from_channel_by_id(self, channel_id: str, file_id: str, db: Optional[AsyncSession] = None) -> bool:
try:
with get_db_context(db) as db:
db.query(ChannelFile).filter_by(channel_id=channel_id, file_id=file_id).delete()
db.commit()
async with get_async_db_context(db) as db:
await db.execute(delete(ChannelFile).filter_by(channel_id=channel_id, file_id=file_id))
await db.commit()
return True
except Exception:
return False
def delete_channel_by_id(self, id: str, db: Optional[Session] = None) -> bool:
with get_db_context(db) as db:
AccessGrants.revoke_all_access('channel', id, db=db)
db.query(Channel).filter(Channel.id == id).delete()
db.commit()
async def delete_channel_by_id(self, id: str, db: Optional[AsyncSession] = None) -> bool:
async with get_async_db_context(db) as db:
await AccessGrants.revoke_all_access('channel', id, db=db)
await db.execute(delete(Channel).filter(Channel.id == id))
await db.commit()
return True
####################
# Webhook Methods
####################
def insert_webhook(
async def insert_webhook(
self,
channel_id: str,
user_id: str,
form_data: ChannelWebhookForm,
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> Optional[ChannelWebhookModel]:
with get_db_context(db) as db:
async with get_async_db_context(db) as db:
webhook = ChannelWebhookModel(
id=str(uuid.uuid4()),
channel_id=channel_id,
@@ -919,63 +918,66 @@ class ChannelTable:
updated_at=int(time.time_ns()),
)
db.add(ChannelWebhook(**webhook.model_dump()))
db.commit()
await db.commit()
return webhook
def get_webhooks_by_channel_id(self, channel_id: str, db: Optional[Session] = None) -> list[ChannelWebhookModel]:
with get_db_context(db) as db:
webhooks = db.query(ChannelWebhook).filter(ChannelWebhook.channel_id == channel_id).all()
async def get_webhooks_by_channel_id(self, channel_id: str, db: Optional[AsyncSession] = None) -> list[ChannelWebhookModel]:
async with get_async_db_context(db) as db:
result = await db.execute(select(ChannelWebhook).filter(ChannelWebhook.channel_id == channel_id))
webhooks = result.scalars().all()
return [ChannelWebhookModel.model_validate(w) for w in webhooks]
def get_webhook_by_id(self, webhook_id: str, db: Optional[Session] = None) -> Optional[ChannelWebhookModel]:
with get_db_context(db) as db:
webhook = db.query(ChannelWebhook).filter(ChannelWebhook.id == webhook_id).first()
async def get_webhook_by_id(self, webhook_id: str, db: Optional[AsyncSession] = None) -> Optional[ChannelWebhookModel]:
async with get_async_db_context(db) as db:
result = await db.execute(select(ChannelWebhook).filter(ChannelWebhook.id == webhook_id))
webhook = result.scalars().first()
return ChannelWebhookModel.model_validate(webhook) if webhook else None
def get_webhook_by_id_and_token(
self, webhook_id: str, token: str, db: Optional[Session] = None
async def get_webhook_by_id_and_token(
self, webhook_id: str, token: str, db: Optional[AsyncSession] = None
) -> Optional[ChannelWebhookModel]:
with get_db_context(db) as db:
webhook = (
db.query(ChannelWebhook)
.filter(
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChannelWebhook).filter(
ChannelWebhook.id == webhook_id,
ChannelWebhook.token == token,
)
.first()
)
webhook = result.scalars().first()
return ChannelWebhookModel.model_validate(webhook) if webhook else None
def update_webhook_by_id(
async def update_webhook_by_id(
self,
webhook_id: str,
form_data: ChannelWebhookForm,
db: Optional[Session] = None,
db: Optional[AsyncSession] = None,
) -> Optional[ChannelWebhookModel]:
with get_db_context(db) as db:
webhook = db.query(ChannelWebhook).filter(ChannelWebhook.id == webhook_id).first()
async with get_async_db_context(db) as db:
result = await db.execute(select(ChannelWebhook).filter(ChannelWebhook.id == webhook_id))
webhook = result.scalars().first()
if not webhook:
return None
webhook.name = form_data.name
webhook.profile_image_url = form_data.profile_image_url
webhook.updated_at = int(time.time_ns())
db.commit()
await db.commit()
return ChannelWebhookModel.model_validate(webhook)
def update_webhook_last_used_at(self, webhook_id: str, db: Optional[Session] = None) -> bool:
with get_db_context(db) as db:
webhook = db.query(ChannelWebhook).filter(ChannelWebhook.id == webhook_id).first()
async def update_webhook_last_used_at(self, webhook_id: str, db: Optional[AsyncSession] = None) -> bool:
async with get_async_db_context(db) as db:
result = await db.execute(select(ChannelWebhook).filter(ChannelWebhook.id == webhook_id))
webhook = result.scalars().first()
if not webhook:
return False
webhook.last_used_at = int(time.time_ns())
db.commit()
await db.commit()
return True
def delete_webhook_by_id(self, webhook_id: str, db: Optional[Session] = None) -> bool:
with get_db_context(db) as db:
result = db.query(ChannelWebhook).filter(ChannelWebhook.id == webhook_id).delete()
db.commit()
return result > 0
async def delete_webhook_by_id(self, webhook_id: str, db: Optional[AsyncSession] = None) -> bool:
async with get_async_db_context(db) as db:
result = await db.execute(delete(ChannelWebhook).filter(ChannelWebhook.id == webhook_id))
await db.commit()
return result.rowcount > 0
Channels = ChannelTable()