diff --git a/rbox/mail.py b/rbox/mail.py index 429f0e6..8287bb9 100644 --- a/rbox/mail.py +++ b/rbox/mail.py @@ -79,15 +79,25 @@ class EmailService: msg.attach(html_part) try: - async with aiosmtplib.SMTP( - hostname=settings.SMTP_SERVER, - port=settings.SMTP_PORT, - username=settings.SMTP_USERNAME, - password=settings.SMTP_PASSWORD, - use_tls=True - ) as smtp: - await smtp.send_message(msg) - print(f"Email sent to {task.to_email}") + if settings.SMTP_PORT == 465: + async with aiosmtplib.SMTP( + hostname=settings.SMTP_SERVER, + port=settings.SMTP_PORT, + username=settings.SMTP_USERNAME, + password=settings.SMTP_PASSWORD, + use_tls=True + ) as smtp: + await smtp.send_message(msg) + print(f"Email sent to {task.to_email}") + else: + async with aiosmtplib.SMTP( + hostname=settings.SMTP_SERVER, + port=settings.SMTP_PORT, + ) as smtp: + await smtp.starttls() + await smtp.login(settings.SMTP_USERNAME, settings.SMTP_PASSWORD) + await smtp.send_message(msg) + print(f"Email sent to {task.to_email}") except Exception as e: print(f"Failed to send email to {task.to_email}: {e}") raise # Re-raise to let caller handle diff --git a/rbox/routers/shares.py b/rbox/routers/shares.py index 3ae1ba7..fc879be 100644 --- a/rbox/routers/shares.py +++ b/rbox/routers/shares.py @@ -9,6 +9,8 @@ from ..models import User, File, Folder, Share from ..schemas import ShareCreate, ShareOut, FileOut, FolderOut from ..auth import get_password_hash, verify_password from ..storage import storage_manager +from ..mail import send_email +from ..settings import settings router = APIRouter( prefix="/shares", @@ -52,6 +54,52 @@ async def create_share_link(share_in: ShareCreate, current_user: User = Depends( hashed_password=hashed_password, permission_level=share_in.permission_level, ) + + if share_in.invite_email: + share_url = f"https://{settings.DOMAIN_NAME}/share/{token}" + item_type = "file" if file else "folder" + item_name = file.name if file else folder.name + + expiry_text = f" until {share_in.expires_at.strftime('%Y-%m-%d %H:%M')}" if share_in.expires_at else "" + password_text = f"\n\nPassword: {share_in.password}" if share_in.password else "" + + email_body = f"""Hello, + +{current_user.username} has shared a {item_type} with you: {item_name} + +Permission level: {share_in.permission_level} +Access link: {share_url}{password_text} + +This link is valid{expiry_text}. + +-- +RBox File Sharing Service""" + + email_html = f""" + +
+Hello,
+{current_user.username} has shared a {item_type} with you: {item_name}
+Permission level: {share_in.permission_level}
+Access {item_type.capitalize()}
+ {f'Password: {share_in.password}
' if share_in.password else ''} +This link is valid{expiry_text}.
+RBox File Sharing Service
+ + +""" + + try: + await send_email( + to_email=share_in.invite_email, + subject=f"{current_user.username} shared {item_name} with you", + body=email_body, + html=email_html + ) + except Exception as e: + print(f"Failed to send invitation email: {e}") + return await ShareOut.from_tortoise_orm(share) @router.get("/my", response_model=List[ShareOut]) diff --git a/rbox/schemas.py b/rbox/schemas.py index cd7630b..6e3b9c2 100644 --- a/rbox/schemas.py +++ b/rbox/schemas.py @@ -47,6 +47,7 @@ class ShareCreate(BaseModel): expires_at: Optional[datetime] = None password: Optional[str] = None permission_level: str = "viewer" + invite_email: Optional[EmailStr] = None class TeamCreate(BaseModel): name: str diff --git a/static/js/api.js b/static/js/api.js index b31a7b7..6e3e696 100644 --- a/static/js/api.js +++ b/static/js/api.js @@ -256,7 +256,7 @@ class APIClient { }); } - async createShare(fileId = null, folderId = null, expiresAt = null, password = null, permissionLevel = 'viewer') { + async createShare(fileId = null, folderId = null, expiresAt = null, password = null, permissionLevel = 'viewer', inviteEmail = null) { return this.request('shares/', { method: 'POST', body: { @@ -264,7 +264,8 @@ class APIClient { folder_id: folderId, expires_at: expiresAt, password, - permission_level: permissionLevel + permission_level: permissionLevel, + invite_email: inviteEmail } }); } diff --git a/static/js/components/file-list.js b/static/js/components/file-list.js index 88e14d4..0e02de2 100644 --- a/static/js/components/file-list.js +++ b/static/js/components/file-list.js @@ -113,6 +113,7 @@ export class FileList extends HTMLElement { ` : ''}No files found.
' : ''} ${this.folders.map(folder => this.renderFolder(folder)).join('')} ${this.files.map(file => this.renderFile(file)).join('')}No photos yet
'; + grid.innerHTML = 'No photos found.
'; return; } diff --git a/static/js/components/share-modal.js b/static/js/components/share-modal.js index 76253fa..5010f04 100644 --- a/static/js/components/share-modal.js +++ b/static/js/components/share-modal.js @@ -20,6 +20,11 @@ export class ShareModal extends HTMLElement {