Update app.py
Browse files
app.py
CHANGED
|
@@ -407,8 +407,8 @@ TMA_DASHBOARD_HTML_TEMPLATE = '''
|
|
| 407 |
<p title="{{ item.original_filename }}">{{ item.original_filename | truncate(25, True) }}</p>
|
| 408 |
<p style="font-size: 0.8em; color: #888;">{{ item.upload_date }}</p>
|
| 409 |
<div class="item-actions">
|
| 410 |
-
<
|
| 411 |
-
{% if previewable %}<button class="btn" style="background: var(--accent);" onclick="openModal('{{ hf_file_url_jinja(item.path) if item.file_type != 'text' else url_for('get_text_content_tma', file_id=item.id) }}', '{{ item.file_type }}', '{{ item.id }}')">Просмотр</button>{% endif %}
|
| 412 |
<form method="POST" action="{{ url_for('delete_file_tma', file_id=item.id) }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите удалить файл {{ item.original_filename }}?');">
|
| 413 |
<input type="hidden" name="current_view_folder_id" value="{{ current_folder_id }}"><button type="submit" class="btn delete-btn">Удалить</button>
|
| 414 |
</form>
|
|
@@ -443,7 +443,8 @@ TMA_DASHBOARD_HTML_TEMPLATE = '''
|
|
| 443 |
else if (type === 'pdf') modalContent.innerHTML = `<iframe src="${srcOrUrl}" title="Просмотр PDF"></iframe>`;
|
| 444 |
else if (type === 'text') {
|
| 445 |
const response = await fetch(srcOrUrl); if (!response.ok) throw new Error(`Ошибка загрузки текста: ${response.statusText}`);
|
| 446 |
-
const text = await response.text();
|
|
|
|
| 447 |
modalContent.innerHTML = `<pre>${escapedText}</pre>`;
|
| 448 |
} else modalContent.innerHTML = '<p>Предпросмотр для этого типа файла не поддерживается.</p>';
|
| 449 |
} catch (error) { modalContent.innerHTML = `<p>Не удалось загрузить содержимое для предпросмотра. ${error.message}</p>`; }
|
|
@@ -455,6 +456,22 @@ TMA_DASHBOARD_HTML_TEMPLATE = '''
|
|
| 455 |
const iframe = modal.querySelector('iframe'); if (iframe) iframe.src = 'about:blank';
|
| 456 |
document.getElementById('modalContent').innerHTML = '';
|
| 457 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
const form = document.getElementById('upload-form'); const fileInput = document.getElementById('file-input');
|
| 459 |
const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text');
|
| 460 |
const progressContainer = document.getElementById('progress-container'); const uploadBtn = document.getElementById('upload-btn');
|
|
@@ -653,14 +670,19 @@ def download_tma(file_id):
|
|
| 653 |
break
|
| 654 |
else:
|
| 655 |
flash('Пожалуйста, авторизуйтесь.', 'error')
|
| 656 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 657 |
|
| 658 |
-
|
| 659 |
-
if
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
|
| 665 |
if not file_node or file_node.get('type') != 'file':
|
| 666 |
flash('Файл не найден или доступ запрещен!', 'error')
|
|
@@ -697,11 +719,17 @@ def download_tma(file_id):
|
|
| 697 |
|
| 698 |
resp = Response(generate_chunks(), mimetype=mimetype)
|
| 699 |
resp.headers['Content-Disposition'] = f'attachment; filename="{original_filename}"'
|
|
|
|
|
|
|
|
|
|
|
|
|
| 700 |
return resp
|
| 701 |
|
| 702 |
except requests.exceptions.RequestException as e:
|
|
|
|
| 703 |
flash(f'Ошибка скачивания файла: {e}', 'error')
|
| 704 |
except Exception as e:
|
|
|
|
| 705 |
flash(f'Внутренняя ошибка при скачивании: {e}', 'error')
|
| 706 |
return redirect(redirect_url)
|
| 707 |
|
|
@@ -946,7 +974,7 @@ ADMIN_USER_FILES_HTML_TEMPLATE = '''
|
|
| 946 |
</div><div class="admin-file-actions">
|
| 947 |
<a href="{{ url_for('download_tma', file_id=file_item.id) }}" class="btn download-btn" download="{{ file_item.original_filename }}">Скачать</a>
|
| 948 |
{% set previewable = file_item.file_type in ['image', 'video', 'pdf', 'text'] %}
|
| 949 |
-
{% if previewable %}<button class="btn" style="background: var(--accent);" onclick="openModalAdmin('{{ hf_file_url_jinja(file_item.path) if file_item.file_type != 'text' else url_for('get_text_content_tma', file_id=file_item.id) }}', '{{ file_item.file_type }}', '{{ file_item.id }}')">Просмотр</button>{% endif %}
|
| 950 |
<form method="POST" action="{{ url_for('admin_delete_file', tma_user_id_str_form=tma_user_id_str_admin_view, file_id=file_item.id) }}" style="display: inline-block;" onsubmit="return confirm('Удалить файл {{ file_item.original_filename }}?');">
|
| 951 |
<button type="submit" class="btn delete-btn">Удалить</button></form>
|
| 952 |
</div></div>{% else %} <p>У пользователя нет файлов.</p> {% endfor %}</div></div>
|
|
@@ -962,9 +990,9 @@ ADMIN_USER_FILES_HTML_TEMPLATE = '''
|
|
| 962 |
if (type === 'image') modalContent.innerHTML = `<img src="${srcOrUrl}" alt="Просмотр изображения">`;
|
| 963 |
else if (type === 'video') modalContent.innerHTML = `<video controls autoplay style='max-width: 95%; max-height: 85vh;'><source src="${srcOrUrl}" type="video/mp4"></video>`;
|
| 964 |
else if (type === 'pdf') modalContent.innerHTML = `<iframe src="${srcOrUrl}" title="Просмотр PDF"></iframe>`;
|
| 965 |
-
else if (type === 'text') { const response = await fetch(srcOrUrl); if (!response.ok) throw new Error(); const text = await response.text(); const esc = text.replace(/</g, "<").replace(/>/g, ">"); modalContent.innerHTML = `<pre>${esc}</pre>`;}
|
| 966 |
else modalContent.innerHTML = '<p>Предпросмотр не поддерживается.</p>';
|
| 967 |
-
} catch (error) { modalContent.innerHTML = `<p>Не удалось
|
| 968 |
}
|
| 969 |
function closeModalAdminEv(event) { if (event.target === document.getElementById('mediaModalAdmin')) closeModalAdminManual(); }
|
| 970 |
function closeModalAdminManual() {
|
|
|
|
| 407 |
<p title="{{ item.original_filename }}">{{ item.original_filename | truncate(25, True) }}</p>
|
| 408 |
<p style="font-size: 0.8em; color: #888;">{{ item.upload_date }}</p>
|
| 409 |
<div class="item-actions">
|
| 410 |
+
<button type="button" class="btn download-btn" onclick="tmaDownloadFile('{{ url_for('download_tma', file_id=item.id, _external=True) }}', '{{ item.original_filename }}')">Скачать</button>
|
| 411 |
+
{% if previewable %}<button type="button" class="btn" style="background: var(--accent);" onclick="openModal('{{ hf_file_url_jinja(item.path) if item.file_type != 'text' else url_for('get_text_content_tma', file_id=item.id) }}', '{{ item.file_type }}', '{{ item.id }}')">Просмотр</button>{% endif %}
|
| 412 |
<form method="POST" action="{{ url_for('delete_file_tma', file_id=item.id) }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите удалить файл {{ item.original_filename }}?');">
|
| 413 |
<input type="hidden" name="current_view_folder_id" value="{{ current_folder_id }}"><button type="submit" class="btn delete-btn">Удалить</button>
|
| 414 |
</form>
|
|
|
|
| 443 |
else if (type === 'pdf') modalContent.innerHTML = `<iframe src="${srcOrUrl}" title="Просмотр PDF"></iframe>`;
|
| 444 |
else if (type === 'text') {
|
| 445 |
const response = await fetch(srcOrUrl); if (!response.ok) throw new Error(`Ошибка загрузки текста: ${response.statusText}`);
|
| 446 |
+
const text = await response.text();
|
| 447 |
+
const escapedText = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
| 448 |
modalContent.innerHTML = `<pre>${escapedText}</pre>`;
|
| 449 |
} else modalContent.innerHTML = '<p>Предпросмотр для этого типа файла не поддерживается.</p>';
|
| 450 |
} catch (error) { modalContent.innerHTML = `<p>Не удалось загрузить содержимое для предпросмотра. ${error.message}</p>`; }
|
|
|
|
| 456 |
const iframe = modal.querySelector('iframe'); if (iframe) iframe.src = 'about:blank';
|
| 457 |
document.getElementById('modalContent').innerHTML = '';
|
| 458 |
}
|
| 459 |
+
|
| 460 |
+
function tmaDownloadFile(downloadUrl, filename) {
|
| 461 |
+
if (window.Telegram && window.Telegram.WebApp && Telegram.WebApp.openLink) {
|
| 462 |
+
console.log(`Attempting TMA download for: ${filename} via URL: ${downloadUrl}`);
|
| 463 |
+
Telegram.WebApp.openLink(downloadUrl);
|
| 464 |
+
} else {
|
| 465 |
+
console.warn("Telegram.WebApp.openLink not available, or not in TMA environment. Attempting fallback download.");
|
| 466 |
+
const link = document.createElement('a');
|
| 467 |
+
link.href = downloadUrl;
|
| 468 |
+
link.setAttribute('download', filename);
|
| 469 |
+
document.body.appendChild(link);
|
| 470 |
+
link.click();
|
| 471 |
+
document.body.removeChild(link);
|
| 472 |
+
}
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
const form = document.getElementById('upload-form'); const fileInput = document.getElementById('file-input');
|
| 476 |
const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text');
|
| 477 |
const progressContainer = document.getElementById('progress-container'); const uploadBtn = document.getElementById('upload-btn');
|
|
|
|
| 670 |
break
|
| 671 |
else:
|
| 672 |
flash('Пожалуйста, авторизуйтесь.', 'error')
|
| 673 |
+
# For direct access to download URL without session, a proper response is needed
|
| 674 |
+
if request.referrer: # if accessed via a click in-app
|
| 675 |
+
return redirect(url_for('tma_entry_page'))
|
| 676 |
+
else: # if direct URL access
|
| 677 |
+
return Response("Unauthorized", status=401, mimetype='text/plain')
|
| 678 |
+
|
| 679 |
|
| 680 |
+
redirect_url_fallback = url_for('tma_dashboard')
|
| 681 |
+
if is_browser_admin_session:
|
| 682 |
+
redirect_url_fallback = url_for('admin_panel')
|
| 683 |
+
|
| 684 |
+
redirect_url = request.referrer or redirect_url_fallback
|
| 685 |
+
|
| 686 |
|
| 687 |
if not file_node or file_node.get('type') != 'file':
|
| 688 |
flash('Файл не найден или доступ запрещен!', 'error')
|
|
|
|
| 719 |
|
| 720 |
resp = Response(generate_chunks(), mimetype=mimetype)
|
| 721 |
resp.headers['Content-Disposition'] = f'attachment; filename="{original_filename}"'
|
| 722 |
+
# Add cache control headers to prevent aggressive caching if files can be updated
|
| 723 |
+
resp.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
| 724 |
+
resp.headers['Pragma'] = 'no-cache'
|
| 725 |
+
resp.headers['Expires'] = '0'
|
| 726 |
return resp
|
| 727 |
|
| 728 |
except requests.exceptions.RequestException as e:
|
| 729 |
+
logging.error(f"Error downloading file {original_filename} (ID: {file_id}) from HF: {e}")
|
| 730 |
flash(f'Ошибка скачивания файла: {e}', 'error')
|
| 731 |
except Exception as e:
|
| 732 |
+
logging.error(f"Internal error during download {original_filename} (ID: {file_id}): {e}")
|
| 733 |
flash(f'Внутренняя ошибка при скачивании: {e}', 'error')
|
| 734 |
return redirect(redirect_url)
|
| 735 |
|
|
|
|
| 974 |
</div><div class="admin-file-actions">
|
| 975 |
<a href="{{ url_for('download_tma', file_id=file_item.id) }}" class="btn download-btn" download="{{ file_item.original_filename }}">Скачать</a>
|
| 976 |
{% set previewable = file_item.file_type in ['image', 'video', 'pdf', 'text'] %}
|
| 977 |
+
{% if previewable %}<button type="button" class="btn" style="background: var(--accent);" onclick="openModalAdmin('{{ hf_file_url_jinja(file_item.path) if file_item.file_type != 'text' else url_for('get_text_content_tma', file_id=file_item.id) }}', '{{ file_item.file_type }}', '{{ file_item.id }}')">Просмотр</button>{% endif %}
|
| 978 |
<form method="POST" action="{{ url_for('admin_delete_file', tma_user_id_str_form=tma_user_id_str_admin_view, file_id=file_item.id) }}" style="display: inline-block;" onsubmit="return confirm('Удалить файл {{ file_item.original_filename }}?');">
|
| 979 |
<button type="submit" class="btn delete-btn">Удалить</button></form>
|
| 980 |
</div></div>{% else %} <p>У пользователя нет файлов.</p> {% endfor %}</div></div>
|
|
|
|
| 990 |
if (type === 'image') modalContent.innerHTML = `<img src="${srcOrUrl}" alt="Просмотр изображения">`;
|
| 991 |
else if (type === 'video') modalContent.innerHTML = `<video controls autoplay style='max-width: 95%; max-height: 85vh;'><source src="${srcOrUrl}" type="video/mp4"></video>`;
|
| 992 |
else if (type === 'pdf') modalContent.innerHTML = `<iframe src="${srcOrUrl}" title="Просмотр PDF"></iframe>`;
|
| 993 |
+
else if (type === 'text') { const response = await fetch(srcOrUrl); if (!response.ok) throw new Error('Network response was not ok for text file.'); const text = await response.text(); const esc = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); modalContent.innerHTML = `<pre>${esc}</pre>`;}
|
| 994 |
else modalContent.innerHTML = '<p>Предпросмотр не поддерживается.</p>';
|
| 995 |
+
} catch (error) { modalContent.innerHTML = `<p>Не удалось загрузить содержимое для предпросмотра. ${error.message}</p>`;}
|
| 996 |
}
|
| 997 |
function closeModalAdminEv(event) { if (event.target === document.getElementById('mediaModalAdmin')) closeModalAdminManual(); }
|
| 998 |
function closeModalAdminManual() {
|