tempspace-cli 1.0.0__tar.gz

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.
@@ -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,137 @@
1
+ # 🚀 FileShare - Enhanced Terminal File Upload Service
2
+
3
+ A modern, terminal-style file sharing service with password protection, rate limiting, and QR code generation.
4
+
5
+ ## 📦 Installation
6
+
7
+ ### Server
8
+
9
+ To run the web server, you can clone the repository and run it locally.
10
+
11
+ ```bash
12
+ # Clone the repository
13
+ git clone <your-repo>
14
+ cd fileshare
15
+
16
+ # Install dependencies
17
+ pip install -r requirements.txt
18
+
19
+ # Copy environment template
20
+ cp .env.example .env
21
+
22
+ # Edit .env and change admin credentials!
23
+ nano .env
24
+
25
+ # Run the server
26
+ python main.py
27
+ ```
28
+
29
+ ### Command-Line Tool (CLI)
30
+
31
+ To use the command-line tool, you can install it directly from PyPI.
32
+
33
+ ```bash
34
+ pip install tempspace-cli
35
+ ```
36
+
37
+ ## ✨ Features
38
+
39
+ ### 🔒 Security
40
+ - **Rate Limiting**: 10 uploads/hour, 100 downloads/hour per IP
41
+ - **Password Protection**: Optional password-protected uploads
42
+ - **Secure Admin Panel**: HTTP Basic Auth for debug endpoints
43
+ - **File Deduplication**: Automatic duplicate detection (SHA256)
44
+ - **One-Time Downloads**: Self-destructing links after first download
45
+
46
+ ### 🎨 UI/UX
47
+ - **Modern Terminal Design**: Green-on-black terminal aesthetic
48
+ - **Drag & Drop**: Intuitive file upload interface
49
+ - **QR Code Generation**: Instant QR codes for mobile sharing
50
+ - **Upload History**: Local storage of recent uploads (browser)
51
+ - **Toast Notifications**: Non-intrusive feedback system
52
+ - **Real-time Progress**: Upload progress with speed indicators
53
+ - **Countdown Timer**: Shows time remaining until expiry
54
+ - **Responsive Design**: Works on mobile, tablet, and desktop
55
+
56
+ ### 📦 File Management
57
+ - **Large File Support**: Up to 4GB per file
58
+ - **Image Previews**: Automatic preview for images
59
+ - **Multiple Expiry Options**: 1, 3, 6, 12, or 24 hours
60
+ - **Chunked Uploads**: Efficient streaming for large files
61
+ - **Automatic Cleanup**: Background task removes expired files
62
+
63
+ ## 📖 Usage
64
+
65
+ ### Web Interface
66
+
67
+ 1. **Open** `http://localhost:8000` in your browser
68
+ 2. **Drag & drop** a file or click to browse
69
+ 3. **Configure** expiry time and optional password
70
+ 4. **Upload** and share the generated link
71
+ 5. **Scan QR code** for mobile access
72
+
73
+ ### CLI Usage
74
+
75
+ Once installed, you can use the `tempspace` command.
76
+
77
+ #### Basic Upload
78
+ ```bash
79
+ tempspace /path/to/document.pdf -t 6h
80
+ ```
81
+
82
+ #### Password Protected Upload
83
+ ```bash
84
+ tempspace /path/to/secret.txt -t 12h -p "mypassword"
85
+ ```
86
+
87
+ #### One-Time Download
88
+ ```bash
89
+ tempspace /path/to/report.pdf --one-time
90
+ ```
91
+
92
+ ## 🔧 Configuration
93
+
94
+ ### Environment Variables
95
+
96
+ Create a `.env` file for the server:
97
+
98
+ ```bash
99
+ # Admin credentials (CHANGE THESE!)
100
+ ADMIN_USER=admin
101
+ ADMIN_PASS=your-secure-password-here
102
+
103
+ # Optional: Custom limits
104
+ RATE_LIMIT_UPLOADS=10
105
+ RATE_LIMIT_DOWNLOADS=100
106
+ ```
107
+
108
+ ## 🛡️ Admin Endpoints
109
+
110
+ Protected by HTTP Basic Auth (use credentials from `.env`).
111
+
112
+ ### View Statistics
113
+ ```bash
114
+ curl -u admin:password http://localhost:8000/debug/stats
115
+ ```
116
+
117
+ ### Wipe All Files
118
+ ```bash
119
+ curl -X POST -u admin:password http://localhost:8000/debug/wipe
120
+ ```
121
+
122
+ ⚠️ **Warning**: This deletes ALL uploaded files permanently!
123
+
124
+ ## 📝 License
125
+
126
+ MIT License - Feel free to use and modify!
127
+
128
+ ## 🤝 Contributing
129
+
130
+ Contributions welcome! Please:
131
+ 1. Fork the repository
132
+ 2. Create a feature branch
133
+ 3. Submit a pull request
134
+
135
+ ## 📧 Support
136
+
137
+ For issues and questions, please open a GitHub issue.
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -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,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools]
6
+ packages = ["cli"]
7
+
8
+ [project]
9
+ name = "tempspace-cli"
10
+ version = "1.0.0"
11
+ authors = [
12
+ { name="Tempspace", email="tempspace@example.com" },
13
+ ]
14
+ description = "A command-line tool for uploading files to Tempspace."
15
+ readme = "README.md"
16
+ license = { text = "MIT License" }
17
+ requires-python = ">=3.7"
18
+ classifiers = [
19
+ "Programming Language :: Python :: 3",
20
+ "Operating System :: OS Independent",
21
+ ]
22
+ dependencies = [
23
+ "requests==2.31.0",
24
+ "tqdm==4.66.1",
25
+ "requests-toolbelt==1.0.0",
26
+ "rich==13.7.1",
27
+ ]
28
+
29
+ [project.scripts]
30
+ tempspace = "cli.tempspace:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ cli/__init__.py
4
+ cli/tempspace.py
5
+ tempspace_cli.egg-info/PKG-INFO
6
+ tempspace_cli.egg-info/SOURCES.txt
7
+ tempspace_cli.egg-info/dependency_links.txt
8
+ tempspace_cli.egg-info/entry_points.txt
9
+ tempspace_cli.egg-info/requires.txt
10
+ tempspace_cli.egg-info/top_level.txt
11
+ tests/test_cli.py
12
+ tests/test_main.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tempspace = cli.tempspace:main
@@ -0,0 +1,4 @@
1
+ requests==2.31.0
2
+ tqdm==4.66.1
3
+ requests-toolbelt==1.0.0
4
+ rich==13.7.1
@@ -0,0 +1,12 @@
1
+ import pytest
2
+ from cli.tempspace import parse_time
3
+
4
+ def test_parse_time():
5
+ assert parse_time("7d") == 168
6
+ assert parse_time("24h") == 24
7
+ assert parse_time("360") == 360
8
+ assert parse_time("1D") == 24
9
+ assert parse_time("2H") == 2
10
+ assert parse_time(" 7d ") == 168
11
+ assert parse_time("invalid") is None
12
+ assert parse_time("1w") is None
@@ -0,0 +1,168 @@
1
+ import pytest
2
+ from fastapi.testclient import TestClient
3
+ from main import app, UPLOAD_DIR
4
+ import os
5
+ import shutil
6
+ import asyncio
7
+ import json
8
+
9
+ # Create a client for testing
10
+ client = TestClient(app)
11
+
12
+ @pytest.fixture(scope="module", autouse=True)
13
+ def setup_and_teardown():
14
+ """Fixture to set up a clean upload directory for tests and tear it down afterward."""
15
+ # Ensure a clean state before tests run
16
+ if UPLOAD_DIR.exists():
17
+ shutil.rmtree(UPLOAD_DIR)
18
+ UPLOAD_DIR.mkdir()
19
+
20
+ # Yield control to the tests
21
+ yield
22
+
23
+ # Teardown: clean up the uploads directory after all tests in the module are done
24
+ if UPLOAD_DIR.exists():
25
+ shutil.rmtree(UPLOAD_DIR)
26
+
27
+ def test_upload_file():
28
+ """Test basic file upload."""
29
+ response = client.post(
30
+ "/upload",
31
+ files={"file": ("test_file.txt", b"hello world", "text/plain")},
32
+ data={"hours": "1"},
33
+ )
34
+ assert response.status_code == 200
35
+ data = response.json()
36
+ assert data["success"] is True
37
+ assert data["filename"] == "test_file.txt"
38
+ assert "url" in data
39
+ assert "file_id" in data
40
+
41
+ def test_upload_with_password():
42
+ """Test file upload with a password."""
43
+ response = client.post(
44
+ "/upload",
45
+ files={"file": ("test_password.txt", b"secret content", "text/plain")},
46
+ data={"hours": "1", "password": "testpassword"},
47
+ )
48
+ assert response.status_code == 200
49
+ data = response.json()
50
+ assert data["success"] is True
51
+ assert data["password_protected"] is True
52
+
53
+ def test_download_file_no_password():
54
+ """Test downloading a file that doesn't require a password."""
55
+ # First, upload a file
56
+ upload_response = client.post(
57
+ "/upload",
58
+ files={"file": ("download_test.txt", b"download content", "text/plain")},
59
+ data={"hours": "1"},
60
+ )
61
+ upload_data = upload_response.json()
62
+ file_url = upload_data["url"]
63
+
64
+ # Now, try to download it
65
+ # The URL contains the full host, so we need to extract the path
66
+ path = file_url.split("/", 3)[-1]
67
+ download_response = client.get(path)
68
+
69
+ assert download_response.status_code == 200
70
+ assert download_response.content == b"download content"
71
+
72
+ def test_download_file_with_correct_password():
73
+ """Test downloading a password-protected file with the correct password."""
74
+ upload_response = client.post(
75
+ "/upload",
76
+ files={"file": ("protected_download.txt", b"protected", "text/plain")},
77
+ data={"hours": "1", "password": "supersecret"},
78
+ )
79
+ upload_data = upload_response.json()
80
+ path = upload_data["url"].split("/", 3)[-1]
81
+
82
+ download_response = client.get(f"{path}?password=supersecret")
83
+ assert download_response.status_code == 200
84
+ assert download_response.content == b"protected"
85
+
86
+ def test_download_file_with_incorrect_password():
87
+ """Test downloading a password-protected file with an incorrect password."""
88
+ upload_response = client.post(
89
+ "/upload",
90
+ files={"file": ("protected_fail.txt", b"protected fail", "text/plain")},
91
+ data={"hours": "1", "password": "supersecret"},
92
+ )
93
+ upload_data = upload_response.json()
94
+ path = upload_data["url"].split("/", 3)[-1]
95
+
96
+ download_response = client.get(f"{path}?password=wrongpassword")
97
+ assert download_response.status_code == 403 # Forbidden
98
+
99
+ def test_one_time_download():
100
+ """Test that a one-time download file is deleted after being accessed."""
101
+ upload_response = client.post(
102
+ "/upload",
103
+ files={"file": ("one_time.txt", b"one time content", "text/plain")},
104
+ data={"hours": "1", "one_time": "true"},
105
+ )
106
+ upload_data = upload_response.json()
107
+ path = upload_data["url"].split("/", 3)[-1]
108
+
109
+ # First download should succeed
110
+ first_download_response = client.get(path)
111
+ assert first_download_response.status_code == 200
112
+
113
+ # Let the async deletion task run
114
+ async def wait_for_deletion():
115
+ await asyncio.sleep(0.1)
116
+
117
+ asyncio.run(wait_for_deletion())
118
+
119
+ # Second download should fail
120
+ second_download_response = client.get(path)
121
+ assert second_download_response.status_code == 404 # Not Found
122
+
123
+ def test_delete_file():
124
+ """Test deleting a file using the client_id."""
125
+ # Upload a file with a specific client_id
126
+ client_id = "test-client-123"
127
+ upload_response = client.post(
128
+ "/upload",
129
+ files={"file": ("to_be_deleted.txt", b"delete me", "text/plain")},
130
+ data={"hours": "1", "client_id": client_id},
131
+ )
132
+ upload_data = upload_response.json()
133
+ file_id = upload_data["file_id"]
134
+
135
+ # Now, delete the file with the correct client_id
136
+ delete_response = client.request(
137
+ "DELETE",
138
+ f"/delete/{file_id}",
139
+ json={"client_id": client_id},
140
+ )
141
+ assert delete_response.status_code == 200
142
+ assert delete_response.json()["success"] is True
143
+
144
+ # Verify the file is gone
145
+ path = upload_data["url"].split("/", 3)[-1]
146
+ get_response = client.get(path)
147
+ assert get_response.status_code == 404
148
+
149
+ def test_delete_file_unauthorized():
150
+ """Test that deleting a file with the wrong client_id fails."""
151
+ # Upload a file with a specific client_id
152
+ owner_client_id = "owner-client"
153
+ upload_response = client.post(
154
+ "/upload",
155
+ files={"file": ("unauthorized_delete.txt", b"don't delete me", "text/plain")},
156
+ data={"hours": "1", "client_id": owner_client_id},
157
+ )
158
+ upload_data = upload_response.json()
159
+ file_id = upload_data["file_id"]
160
+
161
+ # Attempt to delete with a different client_id
162
+ attacker_client_id = "attacker-client"
163
+ delete_response = client.request(
164
+ "DELETE",
165
+ f"/delete/{file_id}",
166
+ json={"client_id": attacker_client_id},
167
+ )
168
+ assert delete_response.status_code == 403 # Forbidden