tempspace-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
cli/tempspace.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import requests
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import hashlib
|
|
6
|
+
import multiprocessing
|
|
7
|
+
from tqdm import tqdm
|
|
8
|
+
from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncoderMonitor
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
|
|
12
|
+
# Default configuration
|
|
13
|
+
DEFAULT_SERVER_URL = "https://tempspace.fly.dev/"
|
|
14
|
+
CHUNK_SIZE = 1024 * 1024 # 1MB
|
|
15
|
+
|
|
16
|
+
def parse_time(time_str: str) -> int:
|
|
17
|
+
"""
|
|
18
|
+
Parse a time string (e.g., '7d', '24h', '360') into an integer number of hours.
|
|
19
|
+
Returns the number of hours as an integer, or None if parsing fails.
|
|
20
|
+
"""
|
|
21
|
+
time_str = time_str.lower().strip()
|
|
22
|
+
if time_str.endswith('d'):
|
|
23
|
+
try:
|
|
24
|
+
days = int(time_str[:-1])
|
|
25
|
+
return days * 24
|
|
26
|
+
except ValueError:
|
|
27
|
+
return None
|
|
28
|
+
elif time_str.endswith('h'):
|
|
29
|
+
try:
|
|
30
|
+
return int(time_str[:-1])
|
|
31
|
+
except ValueError:
|
|
32
|
+
return None
|
|
33
|
+
else:
|
|
34
|
+
try:
|
|
35
|
+
return int(time_str)
|
|
36
|
+
except ValueError:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def calculate_file_hash(filepath: str) -> str:
|
|
40
|
+
"""Calculate the SHA256 hash of a file in chunks."""
|
|
41
|
+
sha256_hash = hashlib.sha256()
|
|
42
|
+
try:
|
|
43
|
+
with open(filepath, "rb") as f:
|
|
44
|
+
for byte_block in iter(lambda: f.read(CHUNK_SIZE), b""):
|
|
45
|
+
sha256_hash.update(byte_block)
|
|
46
|
+
return sha256_hash.hexdigest()
|
|
47
|
+
except IOError as e:
|
|
48
|
+
print(f"Error reading file for hashing: {e}", file=sys.stderr)
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
"""Main function to handle argument parsing and file upload."""
|
|
53
|
+
console = Console()
|
|
54
|
+
|
|
55
|
+
parser = argparse.ArgumentParser(
|
|
56
|
+
description="Upload a file to Tempspace.",
|
|
57
|
+
formatter_class=argparse.RawTextHelpFormatter
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
parser.add_argument("filepath", help="The path to the file you want to upload.")
|
|
61
|
+
parser.add_argument("-t", "--time", type=str, default='24', help="Set the file's expiration time. Examples: '24h', '7d', '360' (hours).\nDefault: '24' (24 hours).")
|
|
62
|
+
parser.add_argument("-p", "--password", type=str, help="Protect the file with a password.")
|
|
63
|
+
parser.add_argument("--one-time", action="store_true", help="The file will be deleted after the first download.")
|
|
64
|
+
parser.add_argument("--url", type=str, default=os.environ.get("TEMPSPACE_URL", DEFAULT_SERVER_URL), help=f"The URL of the Tempspace server.\nCan also be set with the TEMPSPACE_URL environment variable.\nDefault: {DEFAULT_SERVER_URL}")
|
|
65
|
+
|
|
66
|
+
args = parser.parse_args()
|
|
67
|
+
|
|
68
|
+
# --- Validate Inputs ---
|
|
69
|
+
if not os.path.isfile(args.filepath):
|
|
70
|
+
console.print(f"[bold red]Error:[/] File not found at '{args.filepath}'")
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
|
|
73
|
+
hours = parse_time(args.time)
|
|
74
|
+
if hours is None:
|
|
75
|
+
console.print(f"[bold red]Error:[/] Invalid time format '{args.time}'. Use formats like '24h', '7d', or '360'.")
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
|
|
78
|
+
# --- Hashing ---
|
|
79
|
+
console.print("[cyan]Calculating file hash...[/]")
|
|
80
|
+
client_hash = calculate_file_hash(args.filepath)
|
|
81
|
+
console.print(f" [cyan]- Hash:[/] {client_hash}")
|
|
82
|
+
|
|
83
|
+
# --- Prepare Upload ---
|
|
84
|
+
upload_url = f"{args.url.rstrip('/')}/upload"
|
|
85
|
+
filename = os.path.basename(args.filepath)
|
|
86
|
+
file_size = os.path.getsize(args.filepath)
|
|
87
|
+
|
|
88
|
+
fields = {
|
|
89
|
+
'hours': str(hours),
|
|
90
|
+
'one_time': str(args.one_time).lower(),
|
|
91
|
+
'client_hash': client_hash,
|
|
92
|
+
'file': (filename, open(args.filepath, 'rb'), 'application/octet-stream')
|
|
93
|
+
}
|
|
94
|
+
if args.password:
|
|
95
|
+
fields['password'] = args.password
|
|
96
|
+
|
|
97
|
+
encoder = MultipartEncoder(fields=fields)
|
|
98
|
+
response = None
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
bar_format = "{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} [Speed: {rate_fmt}, ETA: {remaining}]"
|
|
102
|
+
with tqdm(total=encoder.len, unit='B', unit_scale=True, desc=f"Uploading {filename}", bar_format=bar_format) as pbar:
|
|
103
|
+
monitor = MultipartEncoderMonitor(encoder, lambda m: pbar.update(m.bytes_read - pbar.n))
|
|
104
|
+
response = requests.post(upload_url, data=monitor, headers={'Content-Type': monitor.content_type})
|
|
105
|
+
|
|
106
|
+
except FileNotFoundError:
|
|
107
|
+
console.print(f"[bold red]Error:[/] The file '{args.filepath}' was not found.")
|
|
108
|
+
sys.exit(1)
|
|
109
|
+
except requests.exceptions.RequestException as e:
|
|
110
|
+
console.print("\n[bold red]An error occurred while connecting to the server:[/]")
|
|
111
|
+
console.print(e)
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
console.print(f"\n[bold red]An unexpected error occurred:[/] {e}")
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
finally:
|
|
117
|
+
# Ensure the file handle is closed
|
|
118
|
+
if 'file' in fields and not fields['file'][1].closed:
|
|
119
|
+
fields['file'][1].close()
|
|
120
|
+
|
|
121
|
+
# --- Handle Response ---
|
|
122
|
+
if response is not None:
|
|
123
|
+
if response.status_code == 200:
|
|
124
|
+
console.print("\n[bold green]Upload successful![/]")
|
|
125
|
+
console.print(Panel(response.text.strip(), title="[bold cyan]Download Link[/]", border_style="green"))
|
|
126
|
+
else:
|
|
127
|
+
console.print(f"\n[bold red]Error:[/] Upload failed with status code {response.status_code}")
|
|
128
|
+
try:
|
|
129
|
+
error_details = response.json()
|
|
130
|
+
console.print(f"[red]Server message:[/] {error_details.get('detail', 'No details provided.')}")
|
|
131
|
+
except requests.exceptions.JSONDecodeError:
|
|
132
|
+
console.print(f"[red]Server response:\n[/]{response.text}")
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
multiprocessing.freeze_support()
|
|
137
|
+
main()
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tempspace-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A command-line tool for uploading files to Tempspace.
|
|
5
|
+
Author-email: Tempspace <tempspace@example.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: requests==2.31.0
|
|
12
|
+
Requires-Dist: tqdm==4.66.1
|
|
13
|
+
Requires-Dist: requests-toolbelt==1.0.0
|
|
14
|
+
Requires-Dist: rich==13.7.1
|
|
15
|
+
|
|
16
|
+
# 🚀 FileShare - Enhanced Terminal File Upload Service
|
|
17
|
+
|
|
18
|
+
A modern, terminal-style file sharing service with password protection, rate limiting, and QR code generation.
|
|
19
|
+
|
|
20
|
+
## 📦 Installation
|
|
21
|
+
|
|
22
|
+
### Server
|
|
23
|
+
|
|
24
|
+
To run the web server, you can clone the repository and run it locally.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Clone the repository
|
|
28
|
+
git clone <your-repo>
|
|
29
|
+
cd fileshare
|
|
30
|
+
|
|
31
|
+
# Install dependencies
|
|
32
|
+
pip install -r requirements.txt
|
|
33
|
+
|
|
34
|
+
# Copy environment template
|
|
35
|
+
cp .env.example .env
|
|
36
|
+
|
|
37
|
+
# Edit .env and change admin credentials!
|
|
38
|
+
nano .env
|
|
39
|
+
|
|
40
|
+
# Run the server
|
|
41
|
+
python main.py
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Command-Line Tool (CLI)
|
|
45
|
+
|
|
46
|
+
To use the command-line tool, you can install it directly from PyPI.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install tempspace-cli
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## ✨ Features
|
|
53
|
+
|
|
54
|
+
### 🔒 Security
|
|
55
|
+
- **Rate Limiting**: 10 uploads/hour, 100 downloads/hour per IP
|
|
56
|
+
- **Password Protection**: Optional password-protected uploads
|
|
57
|
+
- **Secure Admin Panel**: HTTP Basic Auth for debug endpoints
|
|
58
|
+
- **File Deduplication**: Automatic duplicate detection (SHA256)
|
|
59
|
+
- **One-Time Downloads**: Self-destructing links after first download
|
|
60
|
+
|
|
61
|
+
### 🎨 UI/UX
|
|
62
|
+
- **Modern Terminal Design**: Green-on-black terminal aesthetic
|
|
63
|
+
- **Drag & Drop**: Intuitive file upload interface
|
|
64
|
+
- **QR Code Generation**: Instant QR codes for mobile sharing
|
|
65
|
+
- **Upload History**: Local storage of recent uploads (browser)
|
|
66
|
+
- **Toast Notifications**: Non-intrusive feedback system
|
|
67
|
+
- **Real-time Progress**: Upload progress with speed indicators
|
|
68
|
+
- **Countdown Timer**: Shows time remaining until expiry
|
|
69
|
+
- **Responsive Design**: Works on mobile, tablet, and desktop
|
|
70
|
+
|
|
71
|
+
### 📦 File Management
|
|
72
|
+
- **Large File Support**: Up to 4GB per file
|
|
73
|
+
- **Image Previews**: Automatic preview for images
|
|
74
|
+
- **Multiple Expiry Options**: 1, 3, 6, 12, or 24 hours
|
|
75
|
+
- **Chunked Uploads**: Efficient streaming for large files
|
|
76
|
+
- **Automatic Cleanup**: Background task removes expired files
|
|
77
|
+
|
|
78
|
+
## 📖 Usage
|
|
79
|
+
|
|
80
|
+
### Web Interface
|
|
81
|
+
|
|
82
|
+
1. **Open** `http://localhost:8000` in your browser
|
|
83
|
+
2. **Drag & drop** a file or click to browse
|
|
84
|
+
3. **Configure** expiry time and optional password
|
|
85
|
+
4. **Upload** and share the generated link
|
|
86
|
+
5. **Scan QR code** for mobile access
|
|
87
|
+
|
|
88
|
+
### CLI Usage
|
|
89
|
+
|
|
90
|
+
Once installed, you can use the `tempspace` command.
|
|
91
|
+
|
|
92
|
+
#### Basic Upload
|
|
93
|
+
```bash
|
|
94
|
+
tempspace /path/to/document.pdf -t 6h
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Password Protected Upload
|
|
98
|
+
```bash
|
|
99
|
+
tempspace /path/to/secret.txt -t 12h -p "mypassword"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### One-Time Download
|
|
103
|
+
```bash
|
|
104
|
+
tempspace /path/to/report.pdf --one-time
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 🔧 Configuration
|
|
108
|
+
|
|
109
|
+
### Environment Variables
|
|
110
|
+
|
|
111
|
+
Create a `.env` file for the server:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Admin credentials (CHANGE THESE!)
|
|
115
|
+
ADMIN_USER=admin
|
|
116
|
+
ADMIN_PASS=your-secure-password-here
|
|
117
|
+
|
|
118
|
+
# Optional: Custom limits
|
|
119
|
+
RATE_LIMIT_UPLOADS=10
|
|
120
|
+
RATE_LIMIT_DOWNLOADS=100
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 🛡️ Admin Endpoints
|
|
124
|
+
|
|
125
|
+
Protected by HTTP Basic Auth (use credentials from `.env`).
|
|
126
|
+
|
|
127
|
+
### View Statistics
|
|
128
|
+
```bash
|
|
129
|
+
curl -u admin:password http://localhost:8000/debug/stats
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Wipe All Files
|
|
133
|
+
```bash
|
|
134
|
+
curl -X POST -u admin:password http://localhost:8000/debug/wipe
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
⚠️ **Warning**: This deletes ALL uploaded files permanently!
|
|
138
|
+
|
|
139
|
+
## 📝 License
|
|
140
|
+
|
|
141
|
+
MIT License - Feel free to use and modify!
|
|
142
|
+
|
|
143
|
+
## 🤝 Contributing
|
|
144
|
+
|
|
145
|
+
Contributions welcome! Please:
|
|
146
|
+
1. Fork the repository
|
|
147
|
+
2. Create a feature branch
|
|
148
|
+
3. Submit a pull request
|
|
149
|
+
|
|
150
|
+
## 📧 Support
|
|
151
|
+
|
|
152
|
+
For issues and questions, please open a GitHub issue.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
cli/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
|
|
2
|
+
cli/tempspace.py,sha256=3pu-xLDhzWoYxxrthzUM5lyCnTES0V7wzveVZY6gNow,5443
|
|
3
|
+
tempspace_cli-1.0.0.dist-info/METADATA,sha256=IZ-40lW57UaEJw9N71K9DWaU65WXMYRq8WNbYrmJyD4,3754
|
|
4
|
+
tempspace_cli-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
+
tempspace_cli-1.0.0.dist-info/entry_points.txt,sha256=DBBYsRS-fK4MSA3-4lrSPyi6oyHxoJLCII7c793nPtw,49
|
|
6
|
+
tempspace_cli-1.0.0.dist-info/top_level.txt,sha256=2ImG917oaVHlm0nP9oJE-Qrgs-fq_fGWgba2H1f8fpE,4
|
|
7
|
+
tempspace_cli-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cli
|