Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,6 +10,7 @@ import tempfile
|
|
| 10 |
import smtplib
|
| 11 |
from email.message import EmailMessage
|
| 12 |
import mimetypes
|
|
|
|
| 13 |
|
| 14 |
from flask import Flask, render_template_string, request, redirect, url_for, flash, jsonify, session, Response
|
| 15 |
from huggingface_hub import HfApi, hf_hub_download
|
|
@@ -20,7 +21,7 @@ import requests
|
|
| 20 |
load_dotenv()
|
| 21 |
|
| 22 |
app = Flask(__name__)
|
| 23 |
-
app.secret_key = '
|
| 24 |
app.config.update(
|
| 25 |
SESSION_COOKIE_SAMESITE='None',
|
| 26 |
SESSION_COOKIE_SECURE=True,
|
|
@@ -176,12 +177,12 @@ def load_data():
|
|
| 176 |
if 'organization_name' not in settings: settings['organization_name'] = f'Mail Client {env_id}'; changed = True
|
| 177 |
if 'admin_password_enabled' not in settings: settings['admin_password_enabled'] = False; changed = True
|
| 178 |
if 'admin_password' not in settings: settings['admin_password'] = ''; changed = True
|
| 179 |
-
if 'smtp_host' not in settings: settings['smtp_host'] = 'smtp.gmail.com'; changed = True
|
| 180 |
-
if 'smtp_port' not in settings: settings['smtp_port'] = 587; changed = True
|
| 181 |
if 'smtp_user' not in settings: settings['smtp_user'] = ''; changed = True
|
| 182 |
-
if 'smtp_pass' not in settings: settings['smtp_pass'] = ''; changed = True
|
| 183 |
if 'sender_name' not in settings: settings['sender_name'] = ''; changed = True
|
| 184 |
if 'is_deleted' not in settings: settings['is_deleted'] = False; changed = True
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
if changed or not os.path.exists(DATA_FILE):
|
| 187 |
try:
|
|
@@ -217,10 +218,7 @@ def get_env_data(env_id):
|
|
| 217 |
'organization_name': f'Mail Client {env_id}',
|
| 218 |
'admin_password_enabled': False,
|
| 219 |
'admin_password': '',
|
| 220 |
-
'smtp_host': 'smtp.gmail.com',
|
| 221 |
-
'smtp_port': 587,
|
| 222 |
'smtp_user': '',
|
| 223 |
-
'smtp_pass': '',
|
| 224 |
'sender_name': '',
|
| 225 |
'is_deleted': False
|
| 226 |
}
|
|
@@ -243,13 +241,16 @@ def check_deleted_env():
|
|
| 243 |
if env_id in all_data and all_data[env_id].get('settings', {}).get('is_deleted', False):
|
| 244 |
return "<h1 style='text-align:center; margin-top:20vh; font-family:sans-serif;'>Клиент отключен</h1>", 403
|
| 245 |
|
| 246 |
-
def
|
| 247 |
msg = EmailMessage()
|
| 248 |
msg['Subject'] = subject
|
| 249 |
|
| 250 |
sender_name = settings.get('sender_name', '').strip()
|
| 251 |
smtp_user = settings.get('smtp_user', '').strip()
|
| 252 |
|
|
|
|
|
|
|
|
|
|
| 253 |
if sender_name:
|
| 254 |
msg['From'] = f"{sender_name} <{smtp_user}>"
|
| 255 |
else:
|
|
@@ -268,14 +269,22 @@ def send_email(settings, to_email, subject, body, files):
|
|
| 268 |
msg.add_attachment(file_data, maintype=maintype, subtype=subtype, filename=f.filename)
|
| 269 |
f.seek(0)
|
| 270 |
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
LOGIN_TEMPLATE = '''
|
| 281 |
<!DOCTYPE html>
|
|
@@ -448,7 +457,7 @@ ADMIN_TEMPLATE = '''
|
|
| 448 |
.btn-success { background: var(--success); }
|
| 449 |
.btn-danger { background: var(--danger); }
|
| 450 |
.card { background: var(--surface); padding: 20px; border-radius: 16px; box-shadow: 0 4px 15px rgba(0,0,0,0.03); margin-bottom: 20px; }
|
| 451 |
-
input[type="
|
| 452 |
input:focus { border-color: var(--info); background: #fff; }
|
| 453 |
.settings-row { display: flex; align-items: center; gap: 15px; flex-wrap: wrap; margin-bottom: 15px; }
|
| 454 |
.settings-row label { flex: 1; min-width: 150px; font-weight: 600; font-size: 1.05rem; }
|
|
@@ -490,27 +499,15 @@ ADMIN_TEMPLATE = '''
|
|
| 490 |
<label>Название клиента:</label>
|
| 491 |
<input type="text" name="organization_name" value="{{ settings.organization_name }}" required>
|
| 492 |
</div>
|
| 493 |
-
<div class="settings-row">
|
| 494 |
-
<label>Имя отправителя:</label>
|
| 495 |
-
<input type="text" name="sender_name" value="{{ settings.sender_name }}" placeholder="Иван Иванов">
|
| 496 |
-
</div>
|
| 497 |
|
| 498 |
-
<h2 style="margin-top:30px; margin-bottom:20px; font-size:1.2rem; color:var(--primary);"><i class="fas fa-server"></i>
|
| 499 |
-
<div class="settings-row">
|
| 500 |
-
<label>SMTP Host:</label>
|
| 501 |
-
<input type="text" name="smtp_host" value="{{ settings.smtp_host }}" required placeholder="smtp.gmail.com">
|
| 502 |
-
</div>
|
| 503 |
<div class="settings-row">
|
| 504 |
-
<label>
|
| 505 |
-
<input type="
|
| 506 |
</div>
|
| 507 |
<div class="settings-row">
|
| 508 |
-
<label>
|
| 509 |
-
<input type="text" name="
|
| 510 |
-
</div>
|
| 511 |
-
<div class="settings-row">
|
| 512 |
-
<label>Пароль приложения:</label>
|
| 513 |
-
<input type="password" name="smtp_pass" value="{{ settings.smtp_pass }}" placeholder="Пароль">
|
| 514 |
</div>
|
| 515 |
|
| 516 |
<h2 style="margin-top:30px; margin-bottom:20px; font-size:1.2rem; color:var(--primary);"><i class="fas fa-lock"></i> Безопасность</h2>
|
|
@@ -867,10 +864,7 @@ def create_environment():
|
|
| 867 |
"organization_name": f"Mail Client {new_id}",
|
| 868 |
"admin_password_enabled": False,
|
| 869 |
"admin_password": "",
|
| 870 |
-
"smtp_host": "smtp.gmail.com",
|
| 871 |
-
"smtp_port": 587,
|
| 872 |
"smtp_user": "",
|
| 873 |
-
"smtp_pass": "",
|
| 874 |
"sender_name": "",
|
| 875 |
"is_deleted": False
|
| 876 |
}
|
|
@@ -954,11 +948,7 @@ def admin(env_id):
|
|
| 954 |
if request.form.get('action') == 'update_settings':
|
| 955 |
settings['organization_name'] = request.form.get('organization_name', '').strip()
|
| 956 |
settings['sender_name'] = request.form.get('sender_name', '').strip()
|
| 957 |
-
settings['smtp_host'] = request.form.get('smtp_host', '').strip()
|
| 958 |
-
settings['smtp_port'] = int(request.form.get('smtp_port', 587))
|
| 959 |
settings['smtp_user'] = request.form.get('smtp_user', '').strip()
|
| 960 |
-
if request.form.get('smtp_pass'):
|
| 961 |
-
settings['smtp_pass'] = request.form.get('smtp_pass', '').strip()
|
| 962 |
|
| 963 |
settings['admin_password_enabled'] = 'admin_password_enabled' in request.form
|
| 964 |
settings['admin_password'] = request.form.get('admin_password', '').strip()
|
|
@@ -1028,11 +1018,11 @@ def send_mail_route(env_id):
|
|
| 1028 |
body = request.form.get('body', '').strip()
|
| 1029 |
files = request.files.getlist('attachments')
|
| 1030 |
|
| 1031 |
-
if not settings.get('
|
| 1032 |
-
return jsonify({'success': False, 'error': '
|
| 1033 |
|
| 1034 |
try:
|
| 1035 |
-
|
| 1036 |
|
| 1037 |
uploaded_attachments = []
|
| 1038 |
for f in files:
|
|
|
|
| 10 |
import smtplib
|
| 11 |
from email.message import EmailMessage
|
| 12 |
import mimetypes
|
| 13 |
+
import dns.resolver
|
| 14 |
|
| 15 |
from flask import Flask, render_template_string, request, redirect, url_for, flash, jsonify, session, Response
|
| 16 |
from huggingface_hub import HfApi, hf_hub_download
|
|
|
|
| 21 |
load_dotenv()
|
| 22 |
|
| 23 |
app = Flask(__name__)
|
| 24 |
+
app.secret_key = 'super_secret_key_mail_app_123_direct_smtp'
|
| 25 |
app.config.update(
|
| 26 |
SESSION_COOKIE_SAMESITE='None',
|
| 27 |
SESSION_COOKIE_SECURE=True,
|
|
|
|
| 177 |
if 'organization_name' not in settings: settings['organization_name'] = f'Mail Client {env_id}'; changed = True
|
| 178 |
if 'admin_password_enabled' not in settings: settings['admin_password_enabled'] = False; changed = True
|
| 179 |
if 'admin_password' not in settings: settings['admin_password'] = ''; changed = True
|
|
|
|
|
|
|
| 180 |
if 'smtp_user' not in settings: settings['smtp_user'] = ''; changed = True
|
|
|
|
| 181 |
if 'sender_name' not in settings: settings['sender_name'] = ''; changed = True
|
| 182 |
if 'is_deleted' not in settings: settings['is_deleted'] = False; changed = True
|
| 183 |
+
if 'smtp_host' in settings: del settings['smtp_host']; changed = True
|
| 184 |
+
if 'smtp_port' in settings: del settings['smtp_port']; changed = True
|
| 185 |
+
if 'smtp_pass' in settings: del settings['smtp_pass']; changed = True
|
| 186 |
|
| 187 |
if changed or not os.path.exists(DATA_FILE):
|
| 188 |
try:
|
|
|
|
| 218 |
'organization_name': f'Mail Client {env_id}',
|
| 219 |
'admin_password_enabled': False,
|
| 220 |
'admin_password': '',
|
|
|
|
|
|
|
| 221 |
'smtp_user': '',
|
|
|
|
| 222 |
'sender_name': '',
|
| 223 |
'is_deleted': False
|
| 224 |
}
|
|
|
|
| 241 |
if env_id in all_data and all_data[env_id].get('settings', {}).get('is_deleted', False):
|
| 242 |
return "<h1 style='text-align:center; margin-top:20vh; font-family:sans-serif;'>Клиент отключен</h1>", 403
|
| 243 |
|
| 244 |
+
def send_email_direct(settings, to_email, subject, body, files):
|
| 245 |
msg = EmailMessage()
|
| 246 |
msg['Subject'] = subject
|
| 247 |
|
| 248 |
sender_name = settings.get('sender_name', '').strip()
|
| 249 |
smtp_user = settings.get('smtp_user', '').strip()
|
| 250 |
|
| 251 |
+
if not smtp_user:
|
| 252 |
+
raise Exception("Email отправителя не настроен в админ панели.")
|
| 253 |
+
|
| 254 |
if sender_name:
|
| 255 |
msg['From'] = f"{sender_name} <{smtp_user}>"
|
| 256 |
else:
|
|
|
|
| 269 |
msg.add_attachment(file_data, maintype=maintype, subtype=subtype, filename=f.filename)
|
| 270 |
f.seek(0)
|
| 271 |
|
| 272 |
+
try:
|
| 273 |
+
domain = to_email.split('@')[1]
|
| 274 |
+
records = dns.resolver.resolve(domain, 'MX')
|
| 275 |
+
mx_record = sorted(records, key=lambda x: x.preference)[0].exchange.to_text()
|
| 276 |
+
except Exception as e:
|
| 277 |
+
raise Exception(f"Не удалось определить MX сервер для домена {to_email.split('@')[-1]}: {str(e)}")
|
| 278 |
|
| 279 |
+
try:
|
| 280 |
+
with smtplib.SMTP(mx_record, 25, timeout=15) as server:
|
| 281 |
+
server.ehlo()
|
| 282 |
+
if server.has_extn('STARTTLS'):
|
| 283 |
+
server.starttls()
|
| 284 |
+
server.ehlo()
|
| 285 |
+
server.send_message(msg)
|
| 286 |
+
except Exception as e:
|
| 287 |
+
raise Exception(f"Ошибка при доставке письма на сервер {mx_record}: {str(e)}")
|
| 288 |
|
| 289 |
LOGIN_TEMPLATE = '''
|
| 290 |
<!DOCTYPE html>
|
|
|
|
| 457 |
.btn-success { background: var(--success); }
|
| 458 |
.btn-danger { background: var(--danger); }
|
| 459 |
.card { background: var(--surface); padding: 20px; border-radius: 16px; box-shadow: 0 4px 15px rgba(0,0,0,0.03); margin-bottom: 20px; }
|
| 460 |
+
input[type="email"], input[type="text"], input[type="password"] { width: 100%; padding: 16px 20px; border: 1px solid var(--border); border-radius: 12px; font-size: 16px; outline: none; transition: border-color 0.2s; background: #fafafa; box-sizing: border-box; }
|
| 461 |
input:focus { border-color: var(--info); background: #fff; }
|
| 462 |
.settings-row { display: flex; align-items: center; gap: 15px; flex-wrap: wrap; margin-bottom: 15px; }
|
| 463 |
.settings-row label { flex: 1; min-width: 150px; font-weight: 600; font-size: 1.05rem; }
|
|
|
|
| 499 |
<label>Название клиента:</label>
|
| 500 |
<input type="text" name="organization_name" value="{{ settings.organization_name }}" required>
|
| 501 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
|
| 503 |
+
<h2 style="margin-top:30px; margin-bottom:20px; font-size:1.2rem; color:var(--primary);"><i class="fas fa-server"></i> Настройки отправителя (Встроенный SMTP)</h2>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
<div class="settings-row">
|
| 505 |
+
<label>Email отправителя (от кого):</label>
|
| 506 |
+
<input type="email" name="smtp_user" value="{{ settings.smtp_user }}" required placeholder="your@email.com">
|
| 507 |
</div>
|
| 508 |
<div class="settings-row">
|
| 509 |
+
<label>Имя отправителя:</label>
|
| 510 |
+
<input type="text" name="sender_name" value="{{ settings.sender_name }}" placeholder="Иван Иванов">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 511 |
</div>
|
| 512 |
|
| 513 |
<h2 style="margin-top:30px; margin-bottom:20px; font-size:1.2rem; color:var(--primary);"><i class="fas fa-lock"></i> Безопасность</h2>
|
|
|
|
| 864 |
"organization_name": f"Mail Client {new_id}",
|
| 865 |
"admin_password_enabled": False,
|
| 866 |
"admin_password": "",
|
|
|
|
|
|
|
| 867 |
"smtp_user": "",
|
|
|
|
| 868 |
"sender_name": "",
|
| 869 |
"is_deleted": False
|
| 870 |
}
|
|
|
|
| 948 |
if request.form.get('action') == 'update_settings':
|
| 949 |
settings['organization_name'] = request.form.get('organization_name', '').strip()
|
| 950 |
settings['sender_name'] = request.form.get('sender_name', '').strip()
|
|
|
|
|
|
|
| 951 |
settings['smtp_user'] = request.form.get('smtp_user', '').strip()
|
|
|
|
|
|
|
| 952 |
|
| 953 |
settings['admin_password_enabled'] = 'admin_password_enabled' in request.form
|
| 954 |
settings['admin_password'] = request.form.get('admin_password', '').strip()
|
|
|
|
| 1018 |
body = request.form.get('body', '').strip()
|
| 1019 |
files = request.files.getlist('attachments')
|
| 1020 |
|
| 1021 |
+
if not settings.get('smtp_user'):
|
| 1022 |
+
return jsonify({'success': False, 'error': 'Email отправителя не настроен в админ панели.'}), 400
|
| 1023 |
|
| 1024 |
try:
|
| 1025 |
+
send_email_direct(settings, to_email, subject, body, files)
|
| 1026 |
|
| 1027 |
uploaded_attachments = []
|
| 1028 |
for f in files:
|