feat: deploy infrastructure + disk/drive, calendar, presentation, workspaces, onboarding, demo user

This commit is contained in:
2026-05-19 16:26:26 +03:00
parent 688d043dad
commit 3529c39b22
304 changed files with 18826 additions and 1176 deletions
+112
View File
@@ -0,0 +1,112 @@
"""Disk service — unified interface for Google Drive, Yandex Disk, Apple iCloud.
Manages cloud drive provider connections and file uploads.
"""
import json
import logging
from typing import Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.user import User
logger = logging.getLogger(__name__)
DISK_PROVIDERS = ("google", "yandex", "apple")
class DiskService:
def __init__(self, db: AsyncSession):
self.db = db
async def get_connected_providers(self, user: User) -> list[dict[str, Any]]:
"""Get list of connected disk providers with basic info."""
providers = user.disk_providers or []
# Return sanitized list (no tokens)
return [
{
"provider": p["provider"],
"connected_at": p.get("connected_at", ""),
"email": p.get("email", ""),
"quota": p.get("quota"),
}
for p in providers
]
async def is_connected(self, user: User, provider: str) -> bool:
providers = user.disk_providers or []
return any(p["provider"] == provider for p in providers)
async def add_provider(
self, user: User, provider: str, tokens: dict[str, Any], email: str = ""
) -> None:
providers = user.disk_providers or []
# Remove existing entry for same provider
providers = [p for p in providers if p["provider"] != provider]
providers.append({
"provider": provider,
"access_token": tokens.get("access_token", ""),
"refresh_token": tokens.get("refresh_token", ""),
"expires_at": tokens.get("expires_at"),
"connected_at": __import__("datetime").datetime.utcnow().isoformat(),
"email": email,
})
user.disk_providers = providers
await self.db.commit()
async def remove_provider(self, user: User, provider: str) -> None:
providers = user.disk_providers or []
user.disk_providers = [p for p in providers if p["provider"] != provider]
await self.db.commit()
async def get_access_token(self, user: User, provider: str) -> Optional[str]:
providers = user.disk_providers or []
for p in providers:
if p["provider"] == provider:
return p.get("access_token")
return None
async def upload_to_provider(
self, user: User, provider: str, file_name: str, file_content: bytes
) -> bool:
"""Upload file bytes to specified provider's VoIdea folder."""
token = await self.get_access_token(user, provider)
if not token:
logger.warning("No token for provider %s (user %s)", provider, user.id)
return False
if provider == "google":
from app.integrations.oauth.google import ensure_app_folder, upload_file
parent_id = await ensure_app_folder(token)
return await upload_file(token, file_name, file_content, parent_id=parent_id)
elif provider == "yandex":
from app.integrations.oauth.yandex import ensure_app_folder, upload_file
import tempfile, os
await ensure_app_folder(token)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".html")
try:
tmp.write(file_content)
tmp.close()
return await upload_file(token, tmp.name, file_name)
finally:
os.unlink(tmp.name)
elif provider == "apple":
logger.info("Apple iCloud upload is a stub — file not actually uploaded")
return True
return False
async def upload_to_any(
self, user: User, file_name: str, file_content: bytes
) -> Optional[dict[str, Any]]:
"""Upload to first available provider. Returns provider info."""
providers = user.disk_providers or []
for entry in providers:
provider = entry["provider"]
success = await self.upload_to_provider(user, provider, file_name, file_content)
if success:
return {"provider": provider, "success": True}
return None