过期时间

This commit is contained in:
朱潮 2026-02-01 23:40:29 +08:00
parent 35f91a7d03
commit a22211da32

View File

@ -196,9 +196,9 @@ async def check_bot_access(bot_id: str, user_id: str, required_permission: str)
if await cursor.fetchone():
return True
# 检查是否在分享列表中
# 检查是否在分享列表中<EFBFBD><EFBFBD>检查过期时间
await cursor.execute("""
SELECT role FROM bot_shares
SELECT role, expires_at FROM bot_shares
WHERE bot_id = %s AND user_id = %s
""", (bot_id, user_id))
row = await cursor.fetchone()
@ -206,7 +206,16 @@ async def check_bot_access(bot_id: str, user_id: str, required_permission: str)
if not row:
return False
role = row[0]
role, expires_at = row
# 检查是否已过期
if expires_at is not None:
# 获取当前时间(考虑时区)
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
if expires_at < now:
# 分享已过期,拒绝访问
return False
# 权限矩阵
permissions = {
@ -401,6 +410,7 @@ class BotResponse(BaseModel):
owner: Optional[dict] = None # {id, username}
role: Optional[str] = None # 'viewer', 'editor', None for owner
shared_at: Optional[str] = None
expires_at: Optional[str] = None # 分享过期时间
description: Optional[str] = None # 从 settings 中提取
avatar_url: Optional[str] = None # 从 settings 中提取
created_at: str
@ -502,6 +512,7 @@ class BotShareCreate(BaseModel):
"""创建分享请求"""
user_ids: List[str]
role: str = "viewer" # 'viewer' or 'editor'
expires_at: Optional[str] = None # ISO 8601 格式的过期时间None 表示永不过期
class BotShareResponse(BaseModel):
@ -514,6 +525,7 @@ class BotShareResponse(BaseModel):
role: str
shared_at: str
shared_by: Optional[str] = None
expires_at: Optional[str] = None # 过期时间
class BotSharesListResponse(BaseModel):
@ -609,6 +621,7 @@ async def migrate_bot_owner_and_shares():
shared_by UUID NOT NULL REFERENCES agent_user(id) ON DELETE SET NULL,
role VARCHAR(50) DEFAULT 'viewer' CHECK (role IN ('viewer', 'editor')),
shared_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
expires_at TIMESTAMP WITH TIME ZONE,
UNIQUE(bot_id, user_id)
)
""")
@ -618,6 +631,21 @@ async def migrate_bot_owner_and_shares():
await cursor.execute("CREATE INDEX idx_bot_shares_shared_by ON bot_shares(shared_by)")
logger.info("bot_shares table created successfully")
else:
# 为已存在的表添加 expires_at 字段
await cursor.execute("""
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'bot_shares' AND column_name = 'expires_at'
""")
has_expires_column = await cursor.fetchone()
if not has_expires_column:
logger.info("Adding expires_at column to bot_shares table")
await cursor.execute("""
ALTER TABLE bot_shares
ADD COLUMN expires_at TIMESTAMP WITH TIME ZONE
""")
logger.info("expires_at column added successfully")
# 4. 创建 agent_user_tokens 表
await cursor.execute("""
@ -1235,15 +1263,16 @@ async def get_bots(authorization: Optional[str] = Header(None)):
for row in rows
]
else:
# 用户只能看到拥有的 Bot 和分享给自己的 Bot
# 用户只能看到拥有的 Bot 和分享给自己的 Bot(且未过期)
await cursor.execute("""
SELECT b.id, b.name, b.bot_id, b.created_at, b.updated_at, b.settings,
u.id as owner_id, u.username as owner_username,
s.role, s.shared_at
s.role, s.shared_at, s.expires_at
FROM agent_bots b
LEFT JOIN agent_user u ON b.owner_id = u.id
LEFT JOIN bot_shares s ON b.id = s.bot_id AND s.user_id = %s
WHERE b.owner_id = %s OR s.user_id = %s
WHERE b.owner_id = %s
OR (s.user_id = %s AND (s.expires_at IS NULL OR s.expires_at > NOW()))
ORDER BY b.created_at DESC
""", (user_id, user_id, user_id))
rows = await cursor.fetchall()
@ -1258,6 +1287,7 @@ async def get_bots(authorization: Optional[str] = Header(None)):
owner={"id": str(row[6]), "username": row[7]} if row[6] else None,
role=row[8] if row[8] else None,
shared_at=datetime_to_str(row[9]) if row[9] else None,
expires_at=row[10].isoformat() if row[10] else None,
description=row[5].get('description') if row[5] else None,
avatar_url=row[5].get('avatar_url') if row[5] else None,
created_at=datetime_to_str(row[3]),
@ -2823,7 +2853,7 @@ async def get_bot_shares(
async with pool.connection() as conn:
async with conn.cursor() as cursor:
await cursor.execute("""
SELECT s.id, s.bot_id, s.user_id, u.username, u.email, s.role, s.shared_at, su.username
SELECT s.id, s.bot_id, s.user_id, u.username, u.email, s.role, s.shared_at, s.expires_at, su.username
FROM bot_shares s
JOIN agent_user u ON s.user_id = u.id
LEFT JOIN agent_user su ON s.shared_by = su.id
@ -2841,7 +2871,8 @@ async def get_bot_shares(
email=row[4],
role=row[5],
shared_at=row[6].isoformat() if row[6] else "",
shared_by=row[7] if row[7] is None else str(row[7])
expires_at=row[7].isoformat() if row[7] else None,
shared_by=row[8] if row[8] is None else str(row[8])
)
for row in rows
]
@ -2904,12 +2935,13 @@ async def add_bot_share(
for target_user_id in request.user_ids:
try:
await cursor.execute("""
INSERT INTO bot_shares (bot_id, user_id, shared_by, role)
VALUES (%s, %s, %s, %s)
INSERT INTO bot_shares (bot_id, user_id, shared_by, role, expires_at)
VALUES (%s, %s, %s, %s, %s)
ON CONFLICT (bot_id, user_id) DO UPDATE SET
role = EXCLUDED.role,
shared_by = EXCLUDED.shared_by
""", (bot_uuid, target_user_id, user_id, request.role))
shared_by = EXCLUDED.shared_by,
expires_at = EXCLUDED.expires_at
""", (bot_uuid, target_user_id, user_id, request.role, request.expires_at))
added_count += 1
except Exception:
pass # 忽略重复的