mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-22 12:26:08 +00:00
fix(ci): ruff/prettier format, fix test_importutil assertion
This commit is contained in:
@@ -59,6 +59,7 @@ class BotsRouterGroup(group.RouterGroup):
|
|||||||
return self.success(data={'sent': True})
|
return self.success(data={'sent': True})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return self.http_status(500, -1, f'Failed to send message: {str(e)}')
|
return self.http_status(500, -1, f'Failed to send message: {str(e)}')
|
||||||
|
|
||||||
@@ -81,7 +82,9 @@ class BotsRouterGroup(group.RouterGroup):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.http_status(409, -1, str(e))
|
return self.http_status(409, -1, str(e))
|
||||||
|
|
||||||
@self.route('/<bot_uuid>/admins/<int:admin_id>', methods=['DELETE'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY)
|
@self.route(
|
||||||
|
'/<bot_uuid>/admins/<int:admin_id>', methods=['DELETE'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY
|
||||||
|
)
|
||||||
async def _(bot_uuid: str, admin_id: int) -> str:
|
async def _(bot_uuid: str, admin_id: int) -> str:
|
||||||
await self.ap.bot_service.delete_bot_admin(bot_uuid, admin_id)
|
await self.ap.bot_service.delete_bot_admin(bot_uuid, admin_id)
|
||||||
return self.success()
|
return self.success()
|
||||||
|
|||||||
@@ -204,18 +204,15 @@ class BotService:
|
|||||||
|
|
||||||
async def get_bot_admins(self, bot_uuid: str) -> list[dict]:
|
async def get_bot_admins(self, bot_uuid: str) -> list[dict]:
|
||||||
from ....entity.persistence import bot as persistence_bot
|
from ....entity.persistence import bot as persistence_bot
|
||||||
|
|
||||||
result = await self.ap.persistence_mgr.execute_async(
|
result = await self.ap.persistence_mgr.execute_async(
|
||||||
sqlalchemy.select(persistence_bot.BotAdmin).where(
|
sqlalchemy.select(persistence_bot.BotAdmin).where(persistence_bot.BotAdmin.bot_uuid == bot_uuid)
|
||||||
persistence_bot.BotAdmin.bot_uuid == bot_uuid
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return [
|
return [{'id': r.id, 'launcher_type': r.launcher_type, 'launcher_id': r.launcher_id} for r in result.all()]
|
||||||
{'id': r.id, 'launcher_type': r.launcher_type, 'launcher_id': r.launcher_id}
|
|
||||||
for r in result.all()
|
|
||||||
]
|
|
||||||
|
|
||||||
async def add_bot_admin(self, bot_uuid: str, launcher_type: str, launcher_id: str) -> int:
|
async def add_bot_admin(self, bot_uuid: str, launcher_type: str, launcher_id: str) -> int:
|
||||||
from ....entity.persistence import bot as persistence_bot
|
from ....entity.persistence import bot as persistence_bot
|
||||||
|
|
||||||
result = await self.ap.persistence_mgr.execute_async(
|
result = await self.ap.persistence_mgr.execute_async(
|
||||||
sqlalchemy.insert(persistence_bot.BotAdmin).values(
|
sqlalchemy.insert(persistence_bot.BotAdmin).values(
|
||||||
bot_uuid=bot_uuid,
|
bot_uuid=bot_uuid,
|
||||||
@@ -227,6 +224,7 @@ class BotService:
|
|||||||
|
|
||||||
async def delete_bot_admin(self, bot_uuid: str, admin_id: int) -> None:
|
async def delete_bot_admin(self, bot_uuid: str, admin_id: int) -> None:
|
||||||
from ....entity.persistence import bot as persistence_bot
|
from ....entity.persistence import bot as persistence_bot
|
||||||
|
|
||||||
await self.ap.persistence_mgr.execute_async(
|
await self.ap.persistence_mgr.execute_async(
|
||||||
sqlalchemy.delete(persistence_bot.BotAdmin).where(
|
sqlalchemy.delete(persistence_bot.BotAdmin).where(
|
||||||
persistence_bot.BotAdmin.bot_uuid == bot_uuid,
|
persistence_bot.BotAdmin.bot_uuid == bot_uuid,
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ class CommandManager:
|
|||||||
|
|
||||||
import sqlalchemy as _sa
|
import sqlalchemy as _sa
|
||||||
from ..entity.persistence.bot import BotAdmin as _BotAdmin
|
from ..entity.persistence.bot import BotAdmin as _BotAdmin
|
||||||
|
|
||||||
_admins = await self.ap.persistence_mgr.execute_async(
|
_admins = await self.ap.persistence_mgr.execute_async(
|
||||||
_sa.select(_BotAdmin).where(
|
_sa.select(_BotAdmin).where(
|
||||||
_BotAdmin.bot_uuid == (query.bot_uuid or ''),
|
_BotAdmin.bot_uuid == (query.bot_uuid or ''),
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ class BotAdmin(Base):
|
|||||||
launcher_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
launcher_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
||||||
created_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now())
|
created_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now())
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (sqlalchemy.UniqueConstraint('bot_uuid', 'launcher_type', 'launcher_id', name='uq_bot_admin'),)
|
||||||
sqlalchemy.UniqueConstraint('bot_uuid', 'launcher_type', 'launcher_id', name='uq_bot_admin'),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Bot(Base):
|
class Bot(Base):
|
||||||
|
|||||||
@@ -44,13 +44,12 @@ def upgrade() -> None:
|
|||||||
# Read instance_config metadata key that holds the admins list
|
# Read instance_config metadata key that holds the admins list
|
||||||
if 'metadata' not in tables:
|
if 'metadata' not in tables:
|
||||||
return
|
return
|
||||||
meta_row = conn.execute(
|
meta_row = conn.execute(sa.text("SELECT value FROM metadata WHERE key = 'instance_config'")).first()
|
||||||
sa.text("SELECT value FROM metadata WHERE key = 'instance_config'")
|
|
||||||
).first()
|
|
||||||
if meta_row is None:
|
if meta_row is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cfg = json.loads(meta_row[0])
|
cfg = json.loads(meta_row[0])
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -65,8 +64,7 @@ def upgrade() -> None:
|
|||||||
try:
|
try:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
sa.text(
|
sa.text(
|
||||||
'INSERT OR IGNORE INTO bot_admins (bot_uuid, launcher_type, launcher_id)'
|
'INSERT OR IGNORE INTO bot_admins (bot_uuid, launcher_type, launcher_id) VALUES (:bu, :lt, :li)'
|
||||||
' VALUES (:bu, :lt, :li)'
|
|
||||||
),
|
),
|
||||||
{'bu': first_bot_uuid, 'lt': launcher_type, 'li': launcher_id},
|
{'bu': first_bot_uuid, 'lt': launcher_type, 'li': launcher_id},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class TestReadResourceFile:
|
|||||||
from langbot.pkg.utils import importutil
|
from langbot.pkg.utils import importutil
|
||||||
|
|
||||||
content = importutil.read_resource_file('templates/config.yaml')
|
content = importutil.read_resource_file('templates/config.yaml')
|
||||||
assert 'admins:' in content
|
assert 'api:' in content
|
||||||
assert 'edition: community' in content
|
assert 'edition: community' in content
|
||||||
|
|
||||||
def test_raises_for_nonexistent_file(self):
|
def test_raises_for_nonexistent_file(self):
|
||||||
@@ -157,7 +157,7 @@ class TestReadResourceFileBytes:
|
|||||||
from langbot.pkg.utils import importutil
|
from langbot.pkg.utils import importutil
|
||||||
|
|
||||||
content = importutil.read_resource_file_bytes('templates/config.yaml')
|
content = importutil.read_resource_file_bytes('templates/config.yaml')
|
||||||
assert b'admins:' in content
|
assert b'api:' in content
|
||||||
assert b'edition: community' in content
|
assert b'edition: community' in content
|
||||||
|
|
||||||
def test_raises_for_nonexistent_file_bytes(self):
|
def test_raises_for_nonexistent_file_bytes(self):
|
||||||
|
|||||||
@@ -27,7 +27,14 @@ import type { BotSessionMonitorHandle } from '@/app/home/bots/components/bot-ses
|
|||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
|
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Settings, FileText, Users, RefreshCw, Trash2, ShieldCheck } from 'lucide-react';
|
import {
|
||||||
|
Settings,
|
||||||
|
FileText,
|
||||||
|
Users,
|
||||||
|
RefreshCw,
|
||||||
|
Trash2,
|
||||||
|
ShieldCheck,
|
||||||
|
} from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
@@ -298,7 +305,10 @@ export default function BotDetailContent({ id }: { id: string }) {
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
{/* Tab: Admins */}
|
{/* Tab: Admins */}
|
||||||
<TabsContent value="admins" className="flex-1 min-h-0 overflow-y-auto mt-4">
|
<TabsContent
|
||||||
|
value="admins"
|
||||||
|
className="flex-1 min-h-0 overflow-y-auto mt-4"
|
||||||
|
>
|
||||||
<div className="mx-auto max-w-3xl pb-8">
|
<div className="mx-auto max-w-3xl pb-8">
|
||||||
<BotAdminsPanel botId={id} />
|
<BotAdminsPanel botId={id} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ export default function BotAdminsPanel({ botId }: { botId: string }) {
|
|||||||
}
|
}
|
||||||
}, [botId]);
|
}, [botId]);
|
||||||
|
|
||||||
useEffect(() => { load(); }, [load]);
|
useEffect(() => {
|
||||||
|
load();
|
||||||
|
}, [load]);
|
||||||
|
|
||||||
async function handleAdd() {
|
async function handleAdd() {
|
||||||
if (!newId.trim()) return;
|
if (!newId.trim()) return;
|
||||||
@@ -72,7 +74,9 @@ export default function BotAdminsPanel({ botId }: { botId: string }) {
|
|||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="person">{t('bots.admins.typePerson')}</SelectItem>
|
<SelectItem value="person">
|
||||||
|
{t('bots.admins.typePerson')}
|
||||||
|
</SelectItem>
|
||||||
<SelectItem value="group">{t('bots.admins.typeGroup')}</SelectItem>
|
<SelectItem value="group">{t('bots.admins.typeGroup')}</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
@@ -83,32 +87,49 @@ export default function BotAdminsPanel({ botId }: { botId: string }) {
|
|||||||
onChange={(e) => setNewId(e.target.value)}
|
onChange={(e) => setNewId(e.target.value)}
|
||||||
onKeyDown={(e) => e.key === 'Enter' && handleAdd()}
|
onKeyDown={(e) => e.key === 'Enter' && handleAdd()}
|
||||||
/>
|
/>
|
||||||
<Button size="sm" onClick={handleAdd} disabled={adding || !newId.trim()}>
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={handleAdd}
|
||||||
|
disabled={adding || !newId.trim()}
|
||||||
|
>
|
||||||
<Plus className="size-4 mr-1" />
|
<Plus className="size-4 mr-1" />
|
||||||
{t('bots.admins.addAdmin')}
|
{t('bots.admins.addAdmin')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="text-sm text-muted-foreground py-4 text-center">{t('bots.sessionMonitor.loading')}</div>
|
<div className="text-sm text-muted-foreground py-4 text-center">
|
||||||
|
{t('bots.sessionMonitor.loading')}
|
||||||
|
</div>
|
||||||
) : admins.length === 0 ? (
|
) : admins.length === 0 ? (
|
||||||
<div className="text-sm text-muted-foreground py-4 text-center">{t('bots.admins.noAdmins')}</div>
|
<div className="text-sm text-muted-foreground py-4 text-center">
|
||||||
|
{t('bots.admins.noAdmins')}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="border rounded-md overflow-hidden">
|
<div className="border rounded-md overflow-hidden">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b bg-muted/40">
|
<tr className="border-b bg-muted/40">
|
||||||
<th className="text-left px-3 py-2 font-medium text-muted-foreground w-28">{t('bots.admins.launcherType')}</th>
|
<th className="text-left px-3 py-2 font-medium text-muted-foreground w-28">
|
||||||
<th className="text-left px-3 py-2 font-medium text-muted-foreground">{t('bots.admins.launcherId')}</th>
|
{t('bots.admins.launcherType')}
|
||||||
|
</th>
|
||||||
|
<th className="text-left px-3 py-2 font-medium text-muted-foreground">
|
||||||
|
{t('bots.admins.launcherId')}
|
||||||
|
</th>
|
||||||
<th className="w-10" />
|
<th className="w-10" />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{admins.map((admin) => (
|
{admins.map((admin) => (
|
||||||
<tr key={admin.id} className="border-b last:border-0 hover:bg-muted/30">
|
<tr
|
||||||
|
key={admin.id}
|
||||||
|
className="border-b last:border-0 hover:bg-muted/30"
|
||||||
|
>
|
||||||
<td className="px-3 py-2">
|
<td className="px-3 py-2">
|
||||||
<span className="px-1.5 py-0.5 rounded bg-muted text-xs">
|
<span className="px-1.5 py-0.5 rounded bg-muted text-xs">
|
||||||
{admin.launcher_type === 'person' ? t('bots.admins.typePerson') : t('bots.admins.typeGroup')}
|
{admin.launcher_type === 'person'
|
||||||
|
? t('bots.admins.typePerson')
|
||||||
|
: t('bots.admins.typeGroup')}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 font-mono">{admin.launcher_id}</td>
|
<td className="px-3 py-2 font-mono">{admin.launcher_id}</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user