Skip to content

Commit 8298fa2

Browse files
committed
Feature: Auto-close tabs + Fix session persistence
- Add auto-close functionality for download links (?close=1 parameter) - Fix TELETHON_SESSION_BASE64 restoration with consistent paths - Add enhanced logging for session debugging - Update bot to generate links with auto-close by default - Add base64 dependency for download page generation
1 parent b84a309 commit 8298fa2

File tree

5 files changed

+84
-10
lines changed

5 files changed

+84
-10
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mime_guess = "2.0.4"
2222
structopt = "0.3.26"
2323
teloxide = { version = "0.12", features = ["full"] }
2424
reqwest = { version = "0.12", features = ["blocking"] }
25+
base64 = "0.22"
2526
cli = { path = "cli" }
2627
shared = { path = "shared" }
2728
bot = { path = "bot" }

bot/src/queue.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -451,19 +451,19 @@ async fn edit_message_with_file_link(
451451
let file_domain = Config::instance().await.file_domain();
452452
// Build full URL path using shared util (id + url-safe filename)
453453
let full_path = build_url_path(unique_id, file_name);
454-
info!("Generated download link: {}{}", file_domain, full_path);
454+
// Add auto-close parameter for better UX (closes tab after download starts)
455+
let full_url_with_close = format!("{}{}?close=1", file_domain, full_path);
456+
info!("Generated download link: {}", full_url_with_close);
455457
let size_str = human_size(file_size as u64);
456458
let edit_result = bot.get_teloxide_bot().edit_message_text(
457459
queue_item.message.chat.id,
458460
queue_item.queue_message.id,
459461
format!(
460-
"✅ <b>File uploaded successfully!</b>\n\n📁 <b>File:</b> {}\n📊 <b>Size:</b> {}\n\n🔗 <b>Download Link:</b>\n<a href=\"{}{}\">{}{}</a>",
462+
"✅ <b>File uploaded successfully!</b>\n\n📁 <b>File:</b> {}\n📊 <b>Size:</b> {}\n\n🔗 <b>Download Link:</b>\n<a href=\"{}\">{}</a>",
461463
file_name,
462464
size_str,
463-
file_domain,
464-
full_path,
465-
file_domain,
466-
full_path
465+
full_url_with_close,
466+
full_url_with_close
467467
),
468468
)
469469
.parse_mode(ParseMode::Html)

python_service/docker-entrypoint-enhanced.sh

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
set -e
55

66
SESSION_DIR="/app/sessions"
7-
SESSION_FILE="${SESSION_NAME:-/app/sessions/fasttelethon_session}.session"
7+
SESSION_FILE="/app/sessions/fasttelethon_session.session"
88
BACKUP_DIR="/app/sessions/backups"
99

1010
# Create necessary directories
@@ -17,18 +17,23 @@ echo "📄 Session file: $SESSION_FILE"
1717
# 🔑 RESTORE SESSION FROM ENVIRONMENT VARIABLE (for free tier without persistent disk)
1818
if [ ! -z "$TELETHON_SESSION_BASE64" ]; then
1919
echo "🔓 Restoring session from TELETHON_SESSION_BASE64 environment variable..."
20+
echo "📊 Session length: ${#TELETHON_SESSION_BASE64} chars"
2021
echo "$TELETHON_SESSION_BASE64" | base64 -d > "$SESSION_FILE"
2122
if [ $? -eq 0 ]; then
2223
echo "✅ Session restored successfully from environment variable!"
24+
echo "📏 Session file size: $(stat -c%s "$SESSION_FILE") bytes"
2325
chmod 600 "$SESSION_FILE"
2426
else
2527
echo "❌ Failed to decode session from environment variable"
2628
fi
29+
else
30+
echo "⚠️ TELETHON_SESSION_BASE64 environment variable not set"
2731
fi
2832

2933
# Check if session file exists
3034
if [ -f "$SESSION_FILE" ]; then
3135
echo "✅ Found existing session file"
36+
echo "📏 Session file size: $(stat -c%s "$SESSION_FILE") bytes"
3237

3338
# Create backup with timestamp
3439
BACKUP_FILE="$BACKUP_DIR/session_$(date +%Y%m%d_%H%M%S).session"
@@ -40,7 +45,7 @@ if [ -f "$SESSION_FILE" ]; then
4045
ls -t session_*.session 2>/dev/null | tail -n +6 | xargs -r rm
4146
echo "🧹 Cleaned old backups (kept last 5)"
4247
else
43-
echo "⚠️ No existing session file found"
48+
echo "⚠️ No existing session file found at $SESSION_FILE"
4449
echo " Authorization will be required on startup"
4550
echo " Visit /auth endpoint after service starts"
4651
fi
@@ -56,5 +61,7 @@ echo "🚀 Starting FastTelethon service..."
5661
echo "📍 Working directory: $(pwd)"
5762
echo "📂 Files in /app:"
5863
ls -la /app
64+
echo "📂 Files in /app/sessions:"
65+
ls -la /app/sessions || echo " (sessions directory empty or doesn't exist)"
5966

6067
exec python -m uvicorn main:app --host 0.0.0.0 --port 8001

python_service/main.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
API_ID = int(os.getenv("TELEGRAM_API_ID", "0"))
3333
API_HASH = os.getenv("TELEGRAM_API_HASH", "")
3434
PHONE = os.getenv("TELEGRAM_PHONE", "")
35-
SESSION_NAME = os.getenv("SESSION_NAME", "fasttelethon_session")
35+
SESSION_NAME = os.getenv("SESSION_NAME", "/app/sessions/fasttelethon_session")
3636
CHANNEL_ID = os.getenv("STORAGE_CHANNEL_ID", "") # Channel to store files
3737

3838
# Global client and authorization state
@@ -381,8 +381,11 @@ async def backup_session():
381381
import base64
382382
session_file = f"{SESSION_NAME}.session"
383383

384+
logger.info(f"Looking for session file at: {session_file}")
385+
384386
if not os.path.exists(session_file):
385-
raise HTTPException(status_code=404, detail="Session file not found")
387+
logger.error(f"Session file not found at {session_file}")
388+
raise HTTPException(status_code=404, detail=f"Session file not found at {session_file}")
386389

387390
try:
388391
with open(session_file, 'rb') as f:

src/server.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use http::{header::CONTENT_TYPE, StatusCode};
1313
use log::{debug, error, info, warn};
1414
use teloxide::net::Download;
1515
use teloxide::prelude::Requester;
16+
use base64::Engine;
1617

1718
use shared::file_storage::{get_file_metadata, list_all_files, FileMetadata};
1819
use crate::config::Config;
@@ -219,6 +220,9 @@ async fn files_id(
219220
}
220221
}
221222

223+
// Check if auto-close is requested via ?close=1
224+
let auto_close = params.get("close").is_some();
225+
222226
// Determine content type, allow force download via ?dl=1
223227
let force_download = params.get("dl").is_some();
224228
let content_type = if force_download {
@@ -234,6 +238,65 @@ async fn files_id(
234238
info!("Serving file: {} ({} bytes) with content type: {}",
235239
metadata.file_name, file_bytes.len(), content_type);
236240

241+
// If auto-close requested, return HTML with auto-download and close script
242+
if auto_close {
243+
let html = format!(
244+
r#"<!DOCTYPE html>
245+
<html>
246+
<head>
247+
<title>Downloading {}</title>
248+
<style>
249+
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
250+
.loader {{ border: 5px solid #f3f3f3; border-top: 5px solid #3498db;
251+
border-radius: 50%; width: 50px; height: 50px;
252+
animation: spin 1s linear infinite; margin: 20px auto; }}
253+
@keyframes spin {{ 0% {{ transform: rotate(0deg); }} 100% {{ transform: rotate(360deg); }} }}
254+
</style>
255+
</head>
256+
<body>
257+
<h2>Downloading {}</h2>
258+
<div class="loader"></div>
259+
<p>Your download will begin shortly...</p>
260+
<p><small>This window will close automatically.</small></p>
261+
<script>
262+
// Create blob from base64 data and trigger download
263+
const base64Data = "{}";
264+
const byteCharacters = atob(base64Data);
265+
const byteNumbers = new Array(byteCharacters.length);
266+
for (let i = 0; i < byteCharacters.length; i++) {{
267+
byteNumbers[i] = byteCharacters.charCodeAt(i);
268+
}}
269+
const byteArray = new Uint8Array(byteNumbers);
270+
const blob = new Blob([byteArray], {{ type: '{}' }});
271+
const url = window.URL.createObjectURL(blob);
272+
const a = document.createElement('a');
273+
a.href = url;
274+
a.download = "{}";
275+
document.body.appendChild(a);
276+
a.click();
277+
window.URL.revokeObjectURL(url);
278+
279+
// Close window after 2 seconds
280+
setTimeout(function() {{
281+
window.close();
282+
}}, 2000);
283+
</script>
284+
</body>
285+
</html>"#,
286+
metadata.file_name,
287+
metadata.file_name,
288+
base64::engine::general_purpose::STANDARD.encode(&file_bytes),
289+
content_type,
290+
metadata.file_name
291+
);
292+
293+
return Ok(Response::builder()
294+
.status(StatusCode::OK)
295+
.header(CONTENT_TYPE, "text/html; charset=utf-8")
296+
.body(html.into())
297+
.unwrap());
298+
}
299+
237300
Ok(Response::builder()
238301
.status(StatusCode::OK)
239302
.header(CONTENT_TYPE, content_type)

0 commit comments

Comments
 (0)