Update TLS

This commit is contained in:
retoor 2025-11-11 15:06:02 +01:00
parent cf800df2a9
commit 3b57f4cbf6
7 changed files with 87 additions and 13 deletions

View File

@ -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

View File

@ -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"""
<html>
<body>
<p>Hello,</p>
<p><strong>{current_user.username}</strong> has shared a {item_type} with you: <strong>{item_name}</strong></p>
<p><strong>Permission level:</strong> {share_in.permission_level}</p>
<p><a href="{share_url}" style="background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; display: inline-block;">Access {item_type.capitalize()}</a></p>
{f'<p><strong>Password:</strong> {share_in.password}</p>' if share_in.password else ''}
<p><small>This link is valid{expiry_text}.</small></p>
<hr>
<p><small>RBox File Sharing Service</small></p>
</body>
</html>
"""
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])

View File

@ -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

View File

@ -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
}
});
}

View File

@ -113,6 +113,7 @@ export class FileList extends HTMLElement {
` : ''}
<div class="file-grid">
${totalItems === 0 ? '<p class="empty-state">No files found.</p>' : ''}
${this.folders.map(folder => this.renderFolder(folder)).join('')}
${this.files.map(file => this.renderFile(file)).join('')}
</div>

View File

@ -30,7 +30,7 @@ class PhotoGallery extends HTMLElement {
async renderPhotos() {
const grid = this.querySelector('.gallery-grid');
if (this.photos.length === 0) {
grid.innerHTML = '<p class="empty-state">No photos yet</p>';
grid.innerHTML = '<p class="empty-state">No photos found.</p>';
return;
}

View File

@ -20,6 +20,11 @@ export class ShareModal extends HTMLElement {
</div>
<form id="share-form">
<div class="form-group">
<label>Invite Email (optional)</label>
<input type="email" name="invite_email" class="input-field" placeholder="Send invitation to email">
</div>
<div class="form-group">
<label>Permission Level</label>
<select name="permission_level" class="input-field">
@ -101,6 +106,7 @@ export class ShareModal extends HTMLElement {
const errorDiv = this.querySelector('#share-error');
try {
const inviteEmail = formData.get('invite_email') || null;
const permissionLevel = formData.get('permission_level');
const password = formData.get('password') || null;
const expiresAt = formData.get('expires_at') || null;
@ -110,13 +116,20 @@ export class ShareModal extends HTMLElement {
this.folderId,
expiresAt,
password,
permissionLevel
permissionLevel,
inviteEmail
);
const shareLink = `${window.location.origin}/share/${share.token}`;
this.querySelector('#share-link').value = shareLink;
this.querySelector('#share-result').style.display = 'block';
errorDiv.textContent = '';
if (inviteEmail) {
document.dispatchEvent(new CustomEvent('show-toast', {
detail: { message: `Invitation sent to ${inviteEmail}`, type: 'success' }
}));
}
} catch (error) {
errorDiv.textContent = 'Failed to create share link: ' + error.message;
}