Update TLS
This commit is contained in:
parent
cf800df2a9
commit
3b57f4cbf6
28
rbox/mail.py
28
rbox/mail.py
@ -79,15 +79,25 @@ class EmailService:
|
|||||||
msg.attach(html_part)
|
msg.attach(html_part)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with aiosmtplib.SMTP(
|
if settings.SMTP_PORT == 465:
|
||||||
hostname=settings.SMTP_SERVER,
|
async with aiosmtplib.SMTP(
|
||||||
port=settings.SMTP_PORT,
|
hostname=settings.SMTP_SERVER,
|
||||||
username=settings.SMTP_USERNAME,
|
port=settings.SMTP_PORT,
|
||||||
password=settings.SMTP_PASSWORD,
|
username=settings.SMTP_USERNAME,
|
||||||
use_tls=True
|
password=settings.SMTP_PASSWORD,
|
||||||
) as smtp:
|
use_tls=True
|
||||||
await smtp.send_message(msg)
|
) as smtp:
|
||||||
print(f"Email sent to {task.to_email}")
|
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:
|
except Exception as e:
|
||||||
print(f"Failed to send email to {task.to_email}: {e}")
|
print(f"Failed to send email to {task.to_email}: {e}")
|
||||||
raise # Re-raise to let caller handle
|
raise # Re-raise to let caller handle
|
||||||
|
|||||||
@ -9,6 +9,8 @@ from ..models import User, File, Folder, Share
|
|||||||
from ..schemas import ShareCreate, ShareOut, FileOut, FolderOut
|
from ..schemas import ShareCreate, ShareOut, FileOut, FolderOut
|
||||||
from ..auth import get_password_hash, verify_password
|
from ..auth import get_password_hash, verify_password
|
||||||
from ..storage import storage_manager
|
from ..storage import storage_manager
|
||||||
|
from ..mail import send_email
|
||||||
|
from ..settings import settings
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/shares",
|
prefix="/shares",
|
||||||
@ -52,6 +54,52 @@ async def create_share_link(share_in: ShareCreate, current_user: User = Depends(
|
|||||||
hashed_password=hashed_password,
|
hashed_password=hashed_password,
|
||||||
permission_level=share_in.permission_level,
|
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)
|
return await ShareOut.from_tortoise_orm(share)
|
||||||
|
|
||||||
@router.get("/my", response_model=List[ShareOut])
|
@router.get("/my", response_model=List[ShareOut])
|
||||||
|
|||||||
@ -47,6 +47,7 @@ class ShareCreate(BaseModel):
|
|||||||
expires_at: Optional[datetime] = None
|
expires_at: Optional[datetime] = None
|
||||||
password: Optional[str] = None
|
password: Optional[str] = None
|
||||||
permission_level: str = "viewer"
|
permission_level: str = "viewer"
|
||||||
|
invite_email: Optional[EmailStr] = None
|
||||||
|
|
||||||
class TeamCreate(BaseModel):
|
class TeamCreate(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
|
|||||||
@ -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/', {
|
return this.request('shares/', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
@ -264,7 +264,8 @@ class APIClient {
|
|||||||
folder_id: folderId,
|
folder_id: folderId,
|
||||||
expires_at: expiresAt,
|
expires_at: expiresAt,
|
||||||
password,
|
password,
|
||||||
permission_level: permissionLevel
|
permission_level: permissionLevel,
|
||||||
|
invite_email: inviteEmail
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,6 +113,7 @@ export class FileList extends HTMLElement {
|
|||||||
` : ''}
|
` : ''}
|
||||||
|
|
||||||
<div class="file-grid">
|
<div class="file-grid">
|
||||||
|
${totalItems === 0 ? '<p class="empty-state">No files found.</p>' : ''}
|
||||||
${this.folders.map(folder => this.renderFolder(folder)).join('')}
|
${this.folders.map(folder => this.renderFolder(folder)).join('')}
|
||||||
${this.files.map(file => this.renderFile(file)).join('')}
|
${this.files.map(file => this.renderFile(file)).join('')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class PhotoGallery extends HTMLElement {
|
|||||||
async renderPhotos() {
|
async renderPhotos() {
|
||||||
const grid = this.querySelector('.gallery-grid');
|
const grid = this.querySelector('.gallery-grid');
|
||||||
if (this.photos.length === 0) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,11 @@ export class ShareModal extends HTMLElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="share-form">
|
<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">
|
<div class="form-group">
|
||||||
<label>Permission Level</label>
|
<label>Permission Level</label>
|
||||||
<select name="permission_level" class="input-field">
|
<select name="permission_level" class="input-field">
|
||||||
@ -101,6 +106,7 @@ export class ShareModal extends HTMLElement {
|
|||||||
const errorDiv = this.querySelector('#share-error');
|
const errorDiv = this.querySelector('#share-error');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const inviteEmail = formData.get('invite_email') || null;
|
||||||
const permissionLevel = formData.get('permission_level');
|
const permissionLevel = formData.get('permission_level');
|
||||||
const password = formData.get('password') || null;
|
const password = formData.get('password') || null;
|
||||||
const expiresAt = formData.get('expires_at') || null;
|
const expiresAt = formData.get('expires_at') || null;
|
||||||
@ -110,13 +116,20 @@ export class ShareModal extends HTMLElement {
|
|||||||
this.folderId,
|
this.folderId,
|
||||||
expiresAt,
|
expiresAt,
|
||||||
password,
|
password,
|
||||||
permissionLevel
|
permissionLevel,
|
||||||
|
inviteEmail
|
||||||
);
|
);
|
||||||
|
|
||||||
const shareLink = `${window.location.origin}/share/${share.token}`;
|
const shareLink = `${window.location.origin}/share/${share.token}`;
|
||||||
this.querySelector('#share-link').value = shareLink;
|
this.querySelector('#share-link').value = shareLink;
|
||||||
this.querySelector('#share-result').style.display = 'block';
|
this.querySelector('#share-result').style.display = 'block';
|
||||||
errorDiv.textContent = '';
|
errorDiv.textContent = '';
|
||||||
|
|
||||||
|
if (inviteEmail) {
|
||||||
|
document.dispatchEvent(new CustomEvent('show-toast', {
|
||||||
|
detail: { message: `Invitation sent to ${inviteEmail}`, type: 'success' }
|
||||||
|
}));
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorDiv.textContent = 'Failed to create share link: ' + error.message;
|
errorDiv.textContent = 'Failed to create share link: ' + error.message;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user