Skip to content

Commit f396dc5

Browse files
committed
Download page
1 parent 89c556b commit f396dc5

File tree

5 files changed

+112
-3
lines changed

5 files changed

+112
-3
lines changed

static/css/style.css

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ body {
8383
position: relative;
8484
}
8585

86+
.header h1 a {
87+
color: inherit;
88+
text-decoration: none;
89+
}
90+
8691
.beta-badge {
8792
position: absolute;
8893
top: -8px;
@@ -264,6 +269,43 @@ body {
264269
transition: width 0.3s ease;
265270
}
266271

272+
/* Download Page */
273+
.download-container {
274+
background-color: var(--bg-secondary);
275+
border: 1px solid var(--border-default);
276+
border-radius: var(--radius-md);
277+
padding: var(--space-lg);
278+
text-align: center;
279+
}
280+
281+
.file-info {
282+
margin-bottom: var(--space-lg);
283+
}
284+
285+
.file-info p {
286+
margin-bottom: var(--space-sm);
287+
color: var(--text-secondary);
288+
}
289+
290+
.file-info strong {
291+
color: var(--text-primary);
292+
}
293+
294+
.button-download {
295+
display: inline-block;
296+
background-color: var(--accent-primary);
297+
color: var(--text-primary);
298+
padding: var(--space-sm) var(--space-lg);
299+
border-radius: var(--radius-sm);
300+
text-decoration: none;
301+
font-weight: 600;
302+
transition: background-color 0.3s ease;
303+
}
304+
305+
.button-download:hover {
306+
background-color: #388bfd; /* A lighter shade of accent-primary */
307+
}
308+
267309
/* Code Blocks */
268310
.code-section {
269311
background-color: var(--bg-secondary);

static/download.html

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>Transit.sh - Download</title>
7+
<meta name="description" content="Direct file transfer with no intermediary storage">
8+
<meta property="og:title" content="Transit.sh">
9+
<meta property="og:site_name" content="Transit.sh">
10+
<meta property="og:description" content="Direct file transfer with no intermediary storage">
11+
<link rel="icon" href="data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAF0lEQVRIx2NgGAWjYBSMglEwCkbBSAcACBAAAeaR9cIAAAAASUVORK5CYII=">
12+
<link rel="stylesheet" href="/css/style.css">
13+
<script src="https://browser.sentry-cdn.com/9.29.0/bundle.js" crossorigin="anonymous"></script>
14+
<script>Sentry.onLoad(function() {{ Sentry.init({{dsn: "https://8c5e2a9a92b4f237e7faa20d6827aa37@o4509473999224832.ingest.us.sentry.io/4509493121712128"}}); }});</script>
15+
</head>
16+
<body>
17+
<div class="container">
18+
<header class="header">
19+
<h1><a href="/">Transit.sh</a></h1>
20+
<p>Direct file transfer with no intermediary storage</p>
21+
</header>
22+
23+
<main>
24+
<section class="section">
25+
<h2>Ready to download</h2>
26+
<div class="download-container">
27+
<div class="file-info">
28+
<p><strong>File:</strong> {file_name}</p>
29+
<p><strong>Size:</strong> {file_size}</p>
30+
</div>
31+
<a href="{download_url}" class="button-download" id="download-button">Download File</a>
32+
</div>
33+
</section>
34+
</main>
35+
36+
<footer class="footer">
37+
<p><a href="https://github.com/codeSamuraii/transit.sh/blob/main/LICENSE">Free for non-commercial applications</a><br>
38+
<strong>©</strong> <a href="https://github.com/codeSamuraii/">Rémi Héneault</a></p>
39+
</footer>
40+
</div>
41+
</body>
42+
</html>

static/index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
<meta property="og:description" content="Direct file transfer with no intermediary storage">
1111
<link rel="icon" href="data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAF0lEQVRIx2NgGAWjYBSMglEwCkbBSAcACBAAAeaR9cIAAAAASUVORK5CYII=">
1212
<link rel="stylesheet" href="/css/style.css">
13+
<script src="https://browser.sentry-cdn.com/9.29.0/bundle.js" crossorigin="anonymous"></script>
14+
<script>Sentry.onLoad(function() { Sentry.init({dsn: "https://8c5e2a9a92b4f237e7faa20d6827aa37@o4509473999224832.ingest.us.sentry.io/4509493121712128"}); });</script>
1315
</head>
1416
<body>
1517
<div class="container">
1618
<header class="header">
17-
<h1>Transit.sh<span class="beta-badge">Beta</span></h1>
19+
<h1><a href="/">Transit.sh</a><span class="beta-badge">Beta</span></h1>
1820
<p>Direct file transfer with no intermediary storage</p>
1921
<div class="beta-warning">
2022
<strong>Notice:</strong> This service is in beta. If you encounter any bug, please report it <a href="https://github.com/codeSamuraii/transit.sh/issues">here</a>.<br>Sending files via mobile browsers is not supported yet.
@@ -80,6 +82,7 @@ <h3>Important Information</h3>
8082
<li>Files are <strong>not stored</strong> by the service at any point and only exist in memory one chunk at a time.</li>
8183
<li>You can use one method to upload and another to download (e.g., upload via browser, download via cURL).</li>
8284
<li>For large files, the browser upload method using WebSockets is recommended as HTTP transfers have limitations.</li>
85+
<li>When visiting the download link with a web browser, a preview page will load with a button to start the download. You can skip this and access the download by adding <code class="inline-highlight">?download=true</code> at the end of the URL.</li>
8386
<li>This service is hosted as a proof-of-concept with no guarantees. See the <a href="https://github.com/codeSamuraii/transit.sh/">open source project</a> for more details.</li>
8487
</ul>
8588
</div>

tests/test_endpoints.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ async def test_http_download_success(self, test_client, test_file, mock_redis):
157157
mock_redis.brpop.side_effect = [(uid, chunk) for chunk in chunks]
158158

159159
# Mock the background task completion
160-
response = test_client.get(f"/{uid}")
160+
response = test_client.get(f"/{uid}?download=true")
161161

162162
assert response.status_code == 200
163163
assert response.content == file_content
@@ -351,7 +351,7 @@ def upload_file():
351351
# Step 2: Download via HTTP
352352
def download_file():
353353
client = TestClient(app)
354-
response = client.get(f"/{uid}")
354+
response = client.get(f"/{uid}?download=true")
355355
assert response.status_code == 200
356356
return response.content
357357

views/http.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ def get_preview_html(**kwargs):
8282
return html_content.format(**kwargs)
8383

8484

85+
def get_download_html(**kwargs):
86+
try:
87+
template_path = pathlib.Path(__file__).parent.parent / 'static' / 'download.html'
88+
with open(template_path, 'r', encoding='utf-8') as f:
89+
html_content = f.read()
90+
except FileNotFoundError:
91+
return PlainTextResponse("Download page template not found.", status_code=500)
92+
93+
return html_content.format(**kwargs)
94+
95+
8596
@router.get("/{uid}")
8697
@router.get("/{uid}/")
8798
async def http_download(request: Request, uid: str):
@@ -107,12 +118,23 @@ async def http_download(request: Request, uid: str):
107118
file_name, file_size, file_type = transfer.get_file_info()
108119
user_agent = request.headers.get('user-agent', '').lower()
109120
is_prefetcher = any(prefetch_ua in user_agent for prefetch_ua in PREFETCHER_USER_AGENTS)
121+
is_curl = 'curl' in user_agent
110122

111123
if is_prefetcher:
112124
log.info(f"▼ Prefetch request detected, serving preview. UA: ({request.headers.get('user-agent')})")
113125
html_preview = get_preview_html(file_name=file_name, file_size=file_size, file_type=file_type)
114126
return HTMLResponse(content=html_preview, status_code=200)
115127

128+
if not is_curl and not request.query_params.get('download'):
129+
log.info(f"▼ Browser request detected, serving download page. UA: ({request.headers.get('user-agent')})")
130+
download_url = f"/{uid}?download=true"
131+
html_download = get_download_html(
132+
file_name=file_name,
133+
file_size=f"{file_size / (1024**2):.1f} MiB",
134+
download_url=download_url
135+
)
136+
return HTMLResponse(content=html_download, status_code=200)
137+
116138
await transfer.set_client_connected()
117139

118140
transfer.info("▼ Starting download...")

0 commit comments

Comments
 (0)