fix: wrap AsyncSession in context manager across all API blueprints
Replace all bare `session = AsyncSession(bind=conn)` assignments with `async with AsyncSession(bind=conn) as session:` in downloads.py, credentials.py, settings.py, sources.py, and subscriptions.py to ensure sessions are properly closed on all code paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,18 +19,17 @@ VALID_PLATFORMS = ["patreon", "subscribestar", "hentaifoundry", "discord", "pixi
|
||||
async def list_credentials():
|
||||
"""List all credential status (without sensitive data)."""
|
||||
async with current_app.db_engine.connect() as conn:
|
||||
session = AsyncSession(bind=conn)
|
||||
async with AsyncSession(bind=conn) as session:
|
||||
result = await session.execute(select(Credential))
|
||||
credentials = result.scalars().all()
|
||||
|
||||
result = await session.execute(select(Credential))
|
||||
credentials = result.scalars().all()
|
||||
# Also return a simple map of platform -> has_credentials for UI convenience
|
||||
platforms_with_creds = {c.platform: True for c in credentials}
|
||||
|
||||
# Also return a simple map of platform -> has_credentials for UI convenience
|
||||
platforms_with_creds = {c.platform: True for c in credentials}
|
||||
|
||||
return jsonify({
|
||||
"items": [c.to_dict(include_data=False) for c in credentials],
|
||||
"platforms": platforms_with_creds,
|
||||
})
|
||||
return jsonify({
|
||||
"items": [c.to_dict(include_data=False) for c in credentials],
|
||||
"platforms": platforms_with_creds,
|
||||
})
|
||||
|
||||
|
||||
@bp.route("", methods=["POST"])
|
||||
@@ -48,15 +47,15 @@ async def upload_credentials():
|
||||
if extension_key:
|
||||
# Look up API key from database
|
||||
async with current_app.db_engine.connect() as conn:
|
||||
session = AsyncSession(bind=conn)
|
||||
result = await session.execute(
|
||||
select(Setting).where(Setting.key == EXTENSION_API_KEY_SETTING)
|
||||
)
|
||||
setting = result.scalar()
|
||||
stored_key = setting.value if setting else None
|
||||
async with AsyncSession(bind=conn) as session:
|
||||
result = await session.execute(
|
||||
select(Setting).where(Setting.key == EXTENSION_API_KEY_SETTING)
|
||||
)
|
||||
setting = result.scalar()
|
||||
stored_key = setting.value if setting else None
|
||||
|
||||
if not stored_key or extension_key != stored_key:
|
||||
return jsonify({"error": "Invalid extension API key"}), 401
|
||||
if not stored_key or extension_key != stored_key:
|
||||
return jsonify({"error": "Invalid extension API key"}), 401
|
||||
|
||||
data = await request.get_json()
|
||||
|
||||
@@ -81,43 +80,42 @@ async def upload_credentials():
|
||||
return jsonify({"error": "Invalid credential type. Must be 'cookies' or 'token'"}), 400
|
||||
|
||||
async with current_app.db_engine.connect() as conn:
|
||||
session = AsyncSession(bind=conn)
|
||||
|
||||
# Check for existing credential
|
||||
result = await session.execute(
|
||||
select(Credential).where(Credential.platform == platform)
|
||||
)
|
||||
credential = result.scalar()
|
||||
|
||||
# Encrypt the credential data
|
||||
encrypted = encrypt_data(cred_data, settings.secret_key)
|
||||
|
||||
if credential:
|
||||
# Update existing
|
||||
credential.credential_type = cred_type
|
||||
credential.data = encrypted
|
||||
credential.expires_at = data.get("expires_at")
|
||||
credential.last_verified = None # Reset verification
|
||||
else:
|
||||
# Create new
|
||||
credential = Credential(
|
||||
platform=platform,
|
||||
credential_type=cred_type,
|
||||
data=encrypted,
|
||||
expires_at=data.get("expires_at"),
|
||||
async with AsyncSession(bind=conn) as session:
|
||||
# Check for existing credential
|
||||
result = await session.execute(
|
||||
select(Credential).where(Credential.platform == platform)
|
||||
)
|
||||
session.add(credential)
|
||||
credential = result.scalar()
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(credential)
|
||||
# Encrypt the credential data
|
||||
encrypted = encrypt_data(cred_data, settings.secret_key)
|
||||
|
||||
current_app.logger.info(f"Updated credentials for platform: {platform}")
|
||||
return jsonify({
|
||||
"message": "Credentials updated",
|
||||
"platform": platform,
|
||||
"credential_type": cred_type,
|
||||
"expires_at": credential.expires_at.isoformat() if credential.expires_at else None,
|
||||
})
|
||||
if credential:
|
||||
# Update existing
|
||||
credential.credential_type = cred_type
|
||||
credential.data = encrypted
|
||||
credential.expires_at = data.get("expires_at")
|
||||
credential.last_verified = None # Reset verification
|
||||
else:
|
||||
# Create new
|
||||
credential = Credential(
|
||||
platform=platform,
|
||||
credential_type=cred_type,
|
||||
data=encrypted,
|
||||
expires_at=data.get("expires_at"),
|
||||
)
|
||||
session.add(credential)
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(credential)
|
||||
|
||||
current_app.logger.info(f"Updated credentials for platform: {platform}")
|
||||
return jsonify({
|
||||
"message": "Credentials updated",
|
||||
"platform": platform,
|
||||
"credential_type": cred_type,
|
||||
"expires_at": credential.expires_at.isoformat() if credential.expires_at else None,
|
||||
})
|
||||
|
||||
|
||||
@bp.route("/<platform>", methods=["DELETE"])
|
||||
@@ -127,21 +125,20 @@ async def delete_credentials(platform: str):
|
||||
return jsonify({"error": f"Invalid platform. Must be one of: {', '.join(VALID_PLATFORMS)}"}), 400
|
||||
|
||||
async with current_app.db_engine.connect() as conn:
|
||||
session = AsyncSession(bind=conn)
|
||||
async with AsyncSession(bind=conn) as session:
|
||||
result = await session.execute(
|
||||
select(Credential).where(Credential.platform == platform)
|
||||
)
|
||||
credential = result.scalar()
|
||||
|
||||
result = await session.execute(
|
||||
select(Credential).where(Credential.platform == platform)
|
||||
)
|
||||
credential = result.scalar()
|
||||
if not credential:
|
||||
return jsonify({"error": "Credentials not found for this platform"}), 404
|
||||
|
||||
if not credential:
|
||||
return jsonify({"error": "Credentials not found for this platform"}), 404
|
||||
await session.delete(credential)
|
||||
await session.commit()
|
||||
|
||||
await session.delete(credential)
|
||||
await session.commit()
|
||||
|
||||
current_app.logger.info(f"Deleted credentials for platform: {platform}")
|
||||
return "", 204
|
||||
current_app.logger.info(f"Deleted credentials for platform: {platform}")
|
||||
return "", 204
|
||||
|
||||
|
||||
@bp.route("/verify", methods=["POST"])
|
||||
@@ -160,28 +157,27 @@ async def verify_credentials():
|
||||
return jsonify({"error": f"Invalid platform. Must be one of: {', '.join(VALID_PLATFORMS)}"}), 400
|
||||
|
||||
async with current_app.db_engine.connect() as conn:
|
||||
session = AsyncSession(bind=conn)
|
||||
async with AsyncSession(bind=conn) as session:
|
||||
result = await session.execute(
|
||||
select(Credential).where(Credential.platform == platform)
|
||||
)
|
||||
credential = result.scalar()
|
||||
|
||||
result = await session.execute(
|
||||
select(Credential).where(Credential.platform == platform)
|
||||
)
|
||||
credential = result.scalar()
|
||||
if not credential:
|
||||
return jsonify({
|
||||
"platform": platform,
|
||||
"is_valid": False,
|
||||
"error": "No credentials stored for this platform",
|
||||
})
|
||||
|
||||
# TODO: Implement actual verification by making test request
|
||||
# For now, just mark as verified
|
||||
verified_at = utcnow()
|
||||
credential.last_verified = verified_at
|
||||
await session.commit()
|
||||
|
||||
if not credential:
|
||||
return jsonify({
|
||||
"platform": platform,
|
||||
"is_valid": False,
|
||||
"error": "No credentials stored for this platform",
|
||||
"is_valid": True,
|
||||
"last_verified": verified_at.isoformat(),
|
||||
})
|
||||
|
||||
# TODO: Implement actual verification by making test request
|
||||
# For now, just mark as verified
|
||||
verified_at = utcnow()
|
||||
credential.last_verified = verified_at
|
||||
await session.commit()
|
||||
|
||||
return jsonify({
|
||||
"platform": platform,
|
||||
"is_valid": True,
|
||||
"last_verified": verified_at.isoformat(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user