tuspyserver 2.2.2__tar.gz → 3.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.
- tuspyserver-3.0.0/PKG-INFO +211 -0
- tuspyserver-3.0.0/README.md +201 -0
- tuspyserver-3.0.0/pyproject.toml +22 -0
- tuspyserver-3.0.0/src/tuspyserver/__init__.py +4 -0
- tuspyserver-3.0.0/src/tuspyserver/py.typed +0 -0
- tuspyserver-2.2.2/tusserver/tus.py → tuspyserver-3.0.0/src/tuspyserver/router.py +6 -12
- tuspyserver-2.2.2/LICENSE +0 -21
- tuspyserver-2.2.2/PKG-INFO +0 -183
- tuspyserver-2.2.2/README.md +0 -165
- tuspyserver-2.2.2/setup.cfg +0 -4
- tuspyserver-2.2.2/setup.py +0 -38
- tuspyserver-2.2.2/tuspyserver.egg-info/PKG-INFO +0 -183
- tuspyserver-2.2.2/tuspyserver.egg-info/SOURCES.txt +0 -11
- tuspyserver-2.2.2/tuspyserver.egg-info/dependency_links.txt +0 -1
- tuspyserver-2.2.2/tuspyserver.egg-info/requires.txt +0 -2
- tuspyserver-2.2.2/tuspyserver.egg-info/top_level.txt +0 -1
- tuspyserver-2.2.2/tusserver/__init__.py +0 -1
- {tuspyserver-2.2.2/tusserver → tuspyserver-3.0.0/src/tuspyserver}/metadata.py +0 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: tuspyserver
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: A Python tus server implementation as a FastAPI router
|
|
5
|
+
Author: Edi Hasaj
|
|
6
|
+
Author-email: Edi Hasaj <edi.hasaj@applifyer.com>
|
|
7
|
+
Requires-Dist: fastapi>=0.110.0
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
<a href="https://pypi.org/project/tuspyserver/"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/tuspyserver" align="right"></a>
|
|
12
|
+
|
|
13
|
+
# tuspyserver
|
|
14
|
+
|
|
15
|
+
A FastAPI router implementing a [tus upload protocol](https://tus.io/) server, with optional dependency-injected hooks for post-upload processing.
|
|
16
|
+
|
|
17
|
+
Only depends on `fastapi>=0.110` and `python>=3.8`.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
* **⏸️ Resumable uploads** via TUS protocol
|
|
22
|
+
* **🍰 Chunked transfer** with configurable max size
|
|
23
|
+
* **🗃️ Metadata storage** (filename, filetype)
|
|
24
|
+
* **🧹 Expiration & cleanup** of old uploads (default retention: 5 days)
|
|
25
|
+
* **💉 Dependency injection** for seamless validation (optional)
|
|
26
|
+
* **📡 Comprehensive API** with *download*, *HEAD*, *DELETE*, and *OPTIONS* endpoints
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Install the [latest release from PyPI](https://pypi.org/project/tuspyserver/):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# with uv
|
|
34
|
+
uv add tuspyserver
|
|
35
|
+
# with poetry
|
|
36
|
+
poetry add tuspyserver
|
|
37
|
+
# with pip
|
|
38
|
+
pip install tuspyserver
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or install directly from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/edihasaj/tuspyserver
|
|
45
|
+
cd tuspyserver
|
|
46
|
+
pip install .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### API
|
|
52
|
+
|
|
53
|
+
The main API is a single constructor that initializes the tus router. All arguments are optional, and these are their default values:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from tuspyserver import create_tus_router
|
|
57
|
+
|
|
58
|
+
tus_router = create_tus_router(
|
|
59
|
+
prefix="files", # route prefix (default: 'files')
|
|
60
|
+
files_dir="/tmp/files", # path to store files
|
|
61
|
+
max_size=128_849_018_880, # max upload size in bytes (default is ~128GB)
|
|
62
|
+
auth=noop, # authentication dependency
|
|
63
|
+
days_to_keep=5, # retention period
|
|
64
|
+
on_upload_complete=None, # upload callback
|
|
65
|
+
upload_complete_dep=None, # upload callback (dependency injector)
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Basic setup
|
|
70
|
+
|
|
71
|
+
In your `main.py`:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from tuspyserver import create_tus_router
|
|
75
|
+
|
|
76
|
+
from fastapi import FastAPI
|
|
77
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
78
|
+
from fastapi.staticfiles import StaticFiles
|
|
79
|
+
|
|
80
|
+
import uvicorn
|
|
81
|
+
|
|
82
|
+
# initialize a FastAPI app
|
|
83
|
+
app = FastAPI()
|
|
84
|
+
|
|
85
|
+
# configure cross-origin middleware
|
|
86
|
+
app.add_middleware(
|
|
87
|
+
CORSMiddleware,
|
|
88
|
+
allow_origins=["*"],
|
|
89
|
+
allow_methods=["*"],
|
|
90
|
+
allow_headers=["*"],
|
|
91
|
+
expose_headers=[
|
|
92
|
+
"Location",
|
|
93
|
+
"Upload-Offset",
|
|
94
|
+
"Tus-Resumable",
|
|
95
|
+
"Tus-Version",
|
|
96
|
+
"Tus-Extension",
|
|
97
|
+
"Tus-Max-Size",
|
|
98
|
+
"Upload-Expires",
|
|
99
|
+
],
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# use completion hook to log uploads
|
|
103
|
+
def log_upload(file_path: str, metadata: dict):
|
|
104
|
+
print("Upload complete")
|
|
105
|
+
print(file_path)
|
|
106
|
+
print(metadata)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# mount the tus router to our
|
|
110
|
+
app.include_router(
|
|
111
|
+
create_tus_router(
|
|
112
|
+
files_dir="./uploads",
|
|
113
|
+
on_upload_complete=log_upload,
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
>[!IMPORTANT]
|
|
119
|
+
>Headers must be exposed for chunked uploads to work correctly.
|
|
120
|
+
|
|
121
|
+
For a comprehensive working example, see the [tuspyserver example](#example).
|
|
122
|
+
|
|
123
|
+
### Dependency injection
|
|
124
|
+
|
|
125
|
+
For applications using FastAPI's [dependency injection](https://fastapi.tiangolo.com/tutorial/dependencies/), you can supply a factory function that returns a callback with injected dependencies. The factory can `Depends()` on any of your services (database session, current user, etc.).
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
# Define a factory dependency that injects your own services
|
|
129
|
+
from fastapi import Depends
|
|
130
|
+
from your_app.dependencies import get_db, get_current_user
|
|
131
|
+
|
|
132
|
+
# factory function
|
|
133
|
+
def log_user_upload(
|
|
134
|
+
db=Depends(get_db),
|
|
135
|
+
current_user=Depends(get_current_user),
|
|
136
|
+
) -> Callable[[str, dict], None]:
|
|
137
|
+
# callback function
|
|
138
|
+
async def handler(file_path: str, metadata: dict):
|
|
139
|
+
# perform validation or post-processing
|
|
140
|
+
await db.log_upload(current_user.id, metadata)
|
|
141
|
+
await process_file(file_path)
|
|
142
|
+
return handler
|
|
143
|
+
|
|
144
|
+
# Include router with the DI hook
|
|
145
|
+
app.include_router(
|
|
146
|
+
create_api_router(
|
|
147
|
+
upload_complete_dep=log_user_upload,
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Expiration & cleanup
|
|
153
|
+
|
|
154
|
+
Expired files are removed when `remove_expired_files()` is called. You can schedule it using your preferred background scheduler (e.g., `APScheduler`, `cron`).
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from tuspyserver import create_tus_router
|
|
158
|
+
|
|
159
|
+
from apscheduler.schedulers.background import BackgroundScheduler
|
|
160
|
+
|
|
161
|
+
tus_router = create_tus_router(
|
|
162
|
+
days_to_keep = 23 # configure retention period; defaults to 5 days
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
scheduler = BackgroundScheduler()
|
|
166
|
+
scheduler.add_job(
|
|
167
|
+
lambda: tus_router.remove_expired_files(),
|
|
168
|
+
trigger='cron',
|
|
169
|
+
hour=1,
|
|
170
|
+
)
|
|
171
|
+
scheduler.start()
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Example
|
|
175
|
+
|
|
176
|
+
You can find a complete working basic example in the [examples](https://github/edihasaj/tuspyserver/tree/main/examples) folder.
|
|
177
|
+
|
|
178
|
+
The example consists of the following:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
basic.py # backend: a tus router added to a fastapi app and runs it with uvicorn
|
|
182
|
+
static/index.html # frontend: a simple static HTML file using uppy (based on tus-js-client)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
To run it, you need to install [`uv`](https://docs.astral.sh/uv/) and run:
|
|
186
|
+
```bash
|
|
187
|
+
uv run basic.py
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
This should launch the server, and you should now be able to test uploads by browsing to http://localhost:8000/static/index.html.
|
|
191
|
+
|
|
192
|
+
Uploaded files get placed in the `examples/uploads` folder.
|
|
193
|
+
|
|
194
|
+
## Developing
|
|
195
|
+
|
|
196
|
+
Contributions welcome! Please open issues or PRs on [GitHub](https://github.com/edihasaj/tuspyserver).
|
|
197
|
+
|
|
198
|
+
You need [`uv`](https://docs.astral.sh/uv/) to develop the project. The project is setup as a [uv workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/)
|
|
199
|
+
where the root is the [library](https://docs.astral.sh/uv/concepts/projects/init/#libraries) and the examples directory is an [unpackagedapplication](https://docs.astral.sh/uv/concepts/projects/init/#applications)
|
|
200
|
+
|
|
201
|
+
### Releasing
|
|
202
|
+
|
|
203
|
+
To release the package, follow the following steps:
|
|
204
|
+
|
|
205
|
+
1. Update the version in `pyproject.toml` using [semver](https://semver.org/)
|
|
206
|
+
2. Merge PR to main or push directly to main
|
|
207
|
+
3. Open a PR to merge `main` → `production`.
|
|
208
|
+
4. Upon merge, CI/CD will publish to PyPI.
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
*© 2025 Edi Hasaj [X](https://x.com/hasajedi)*
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<a href="https://pypi.org/project/tuspyserver/"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/tuspyserver" align="right"></a>
|
|
2
|
+
|
|
3
|
+
# tuspyserver
|
|
4
|
+
|
|
5
|
+
A FastAPI router implementing a [tus upload protocol](https://tus.io/) server, with optional dependency-injected hooks for post-upload processing.
|
|
6
|
+
|
|
7
|
+
Only depends on `fastapi>=0.110` and `python>=3.8`.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
* **⏸️ Resumable uploads** via TUS protocol
|
|
12
|
+
* **🍰 Chunked transfer** with configurable max size
|
|
13
|
+
* **🗃️ Metadata storage** (filename, filetype)
|
|
14
|
+
* **🧹 Expiration & cleanup** of old uploads (default retention: 5 days)
|
|
15
|
+
* **💉 Dependency injection** for seamless validation (optional)
|
|
16
|
+
* **📡 Comprehensive API** with *download*, *HEAD*, *DELETE*, and *OPTIONS* endpoints
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Install the [latest release from PyPI](https://pypi.org/project/tuspyserver/):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# with uv
|
|
24
|
+
uv add tuspyserver
|
|
25
|
+
# with poetry
|
|
26
|
+
poetry add tuspyserver
|
|
27
|
+
# with pip
|
|
28
|
+
pip install tuspyserver
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or install directly from source:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/edihasaj/tuspyserver
|
|
35
|
+
cd tuspyserver
|
|
36
|
+
pip install .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### API
|
|
42
|
+
|
|
43
|
+
The main API is a single constructor that initializes the tus router. All arguments are optional, and these are their default values:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from tuspyserver import create_tus_router
|
|
47
|
+
|
|
48
|
+
tus_router = create_tus_router(
|
|
49
|
+
prefix="files", # route prefix (default: 'files')
|
|
50
|
+
files_dir="/tmp/files", # path to store files
|
|
51
|
+
max_size=128_849_018_880, # max upload size in bytes (default is ~128GB)
|
|
52
|
+
auth=noop, # authentication dependency
|
|
53
|
+
days_to_keep=5, # retention period
|
|
54
|
+
on_upload_complete=None, # upload callback
|
|
55
|
+
upload_complete_dep=None, # upload callback (dependency injector)
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Basic setup
|
|
60
|
+
|
|
61
|
+
In your `main.py`:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from tuspyserver import create_tus_router
|
|
65
|
+
|
|
66
|
+
from fastapi import FastAPI
|
|
67
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
68
|
+
from fastapi.staticfiles import StaticFiles
|
|
69
|
+
|
|
70
|
+
import uvicorn
|
|
71
|
+
|
|
72
|
+
# initialize a FastAPI app
|
|
73
|
+
app = FastAPI()
|
|
74
|
+
|
|
75
|
+
# configure cross-origin middleware
|
|
76
|
+
app.add_middleware(
|
|
77
|
+
CORSMiddleware,
|
|
78
|
+
allow_origins=["*"],
|
|
79
|
+
allow_methods=["*"],
|
|
80
|
+
allow_headers=["*"],
|
|
81
|
+
expose_headers=[
|
|
82
|
+
"Location",
|
|
83
|
+
"Upload-Offset",
|
|
84
|
+
"Tus-Resumable",
|
|
85
|
+
"Tus-Version",
|
|
86
|
+
"Tus-Extension",
|
|
87
|
+
"Tus-Max-Size",
|
|
88
|
+
"Upload-Expires",
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# use completion hook to log uploads
|
|
93
|
+
def log_upload(file_path: str, metadata: dict):
|
|
94
|
+
print("Upload complete")
|
|
95
|
+
print(file_path)
|
|
96
|
+
print(metadata)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# mount the tus router to our
|
|
100
|
+
app.include_router(
|
|
101
|
+
create_tus_router(
|
|
102
|
+
files_dir="./uploads",
|
|
103
|
+
on_upload_complete=log_upload,
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
>[!IMPORTANT]
|
|
109
|
+
>Headers must be exposed for chunked uploads to work correctly.
|
|
110
|
+
|
|
111
|
+
For a comprehensive working example, see the [tuspyserver example](#example).
|
|
112
|
+
|
|
113
|
+
### Dependency injection
|
|
114
|
+
|
|
115
|
+
For applications using FastAPI's [dependency injection](https://fastapi.tiangolo.com/tutorial/dependencies/), you can supply a factory function that returns a callback with injected dependencies. The factory can `Depends()` on any of your services (database session, current user, etc.).
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Define a factory dependency that injects your own services
|
|
119
|
+
from fastapi import Depends
|
|
120
|
+
from your_app.dependencies import get_db, get_current_user
|
|
121
|
+
|
|
122
|
+
# factory function
|
|
123
|
+
def log_user_upload(
|
|
124
|
+
db=Depends(get_db),
|
|
125
|
+
current_user=Depends(get_current_user),
|
|
126
|
+
) -> Callable[[str, dict], None]:
|
|
127
|
+
# callback function
|
|
128
|
+
async def handler(file_path: str, metadata: dict):
|
|
129
|
+
# perform validation or post-processing
|
|
130
|
+
await db.log_upload(current_user.id, metadata)
|
|
131
|
+
await process_file(file_path)
|
|
132
|
+
return handler
|
|
133
|
+
|
|
134
|
+
# Include router with the DI hook
|
|
135
|
+
app.include_router(
|
|
136
|
+
create_api_router(
|
|
137
|
+
upload_complete_dep=log_user_upload,
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Expiration & cleanup
|
|
143
|
+
|
|
144
|
+
Expired files are removed when `remove_expired_files()` is called. You can schedule it using your preferred background scheduler (e.g., `APScheduler`, `cron`).
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from tuspyserver import create_tus_router
|
|
148
|
+
|
|
149
|
+
from apscheduler.schedulers.background import BackgroundScheduler
|
|
150
|
+
|
|
151
|
+
tus_router = create_tus_router(
|
|
152
|
+
days_to_keep = 23 # configure retention period; defaults to 5 days
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
scheduler = BackgroundScheduler()
|
|
156
|
+
scheduler.add_job(
|
|
157
|
+
lambda: tus_router.remove_expired_files(),
|
|
158
|
+
trigger='cron',
|
|
159
|
+
hour=1,
|
|
160
|
+
)
|
|
161
|
+
scheduler.start()
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Example
|
|
165
|
+
|
|
166
|
+
You can find a complete working basic example in the [examples](https://github/edihasaj/tuspyserver/tree/main/examples) folder.
|
|
167
|
+
|
|
168
|
+
The example consists of the following:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
basic.py # backend: a tus router added to a fastapi app and runs it with uvicorn
|
|
172
|
+
static/index.html # frontend: a simple static HTML file using uppy (based on tus-js-client)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
To run it, you need to install [`uv`](https://docs.astral.sh/uv/) and run:
|
|
176
|
+
```bash
|
|
177
|
+
uv run basic.py
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
This should launch the server, and you should now be able to test uploads by browsing to http://localhost:8000/static/index.html.
|
|
181
|
+
|
|
182
|
+
Uploaded files get placed in the `examples/uploads` folder.
|
|
183
|
+
|
|
184
|
+
## Developing
|
|
185
|
+
|
|
186
|
+
Contributions welcome! Please open issues or PRs on [GitHub](https://github.com/edihasaj/tuspyserver).
|
|
187
|
+
|
|
188
|
+
You need [`uv`](https://docs.astral.sh/uv/) to develop the project. The project is setup as a [uv workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/)
|
|
189
|
+
where the root is the [library](https://docs.astral.sh/uv/concepts/projects/init/#libraries) and the examples directory is an [unpackagedapplication](https://docs.astral.sh/uv/concepts/projects/init/#applications)
|
|
190
|
+
|
|
191
|
+
### Releasing
|
|
192
|
+
|
|
193
|
+
To release the package, follow the following steps:
|
|
194
|
+
|
|
195
|
+
1. Update the version in `pyproject.toml` using [semver](https://semver.org/)
|
|
196
|
+
2. Merge PR to main or push directly to main
|
|
197
|
+
3. Open a PR to merge `main` → `production`.
|
|
198
|
+
4. Upon merge, CI/CD will publish to PyPI.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
*© 2025 Edi Hasaj [X](https://x.com/hasajedi)*
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tuspyserver"
|
|
3
|
+
version = "3.0.0"
|
|
4
|
+
description ="A Python tus server implementation as a FastAPI router"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Edi Hasaj", email = "edi.hasaj@applifyer.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.8"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"fastapi>=0.110.0"
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["uv_build>=0.6,<0.7"]
|
|
16
|
+
build-backend = "uv_build"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
[tool.uv.workspace]
|
|
20
|
+
members = [
|
|
21
|
+
"examples",
|
|
22
|
+
]
|
|
File without changes
|
|
@@ -18,20 +18,20 @@ from fastapi import (
|
|
|
18
18
|
)
|
|
19
19
|
from starlette.responses import FileResponse
|
|
20
20
|
|
|
21
|
-
from
|
|
21
|
+
from tuspyserver.metadata import FileMetadata
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
async def
|
|
24
|
+
async def noop():
|
|
25
25
|
pass
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def
|
|
28
|
+
def create_tus_router(
|
|
29
|
+
prefix: str = "files",
|
|
29
30
|
files_dir="/tmp/files",
|
|
30
31
|
max_size=128849018880,
|
|
31
|
-
|
|
32
|
-
auth: Optional[Callable[[], None]] = default_auth,
|
|
32
|
+
auth: Optional[Callable[[], None]] = noop,
|
|
33
33
|
days_to_keep: int = 5,
|
|
34
|
-
|
|
34
|
+
on_upload_complete: Optional[Callable[[str, dict], None]] = None,
|
|
35
35
|
upload_complete_dep: Optional[Callable[..., Callable[[str, dict], None]]] = None,
|
|
36
36
|
):
|
|
37
37
|
if prefix and prefix[0] == "/":
|
|
@@ -357,10 +357,4 @@ def create_api_router(
|
|
|
357
357
|
proto, host = _get_host_and_proto(request=request)
|
|
358
358
|
return f"{proto}://{host}/{prefix}/{uuid}"
|
|
359
359
|
|
|
360
|
-
# # Create a scheduler for deleting the files
|
|
361
|
-
# scheduler = sched.scheduler(time.time, time.sleep)
|
|
362
|
-
# run_time = time.mktime(time.strptime("01:00", "%H:%M"))
|
|
363
|
-
# scheduler.enterabs(run_time, 1, remove_expired_files)
|
|
364
|
-
# scheduler.run()
|
|
365
|
-
|
|
366
360
|
return router
|
tuspyserver-2.2.2/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022 Tus Server for FastAPI Python
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
tuspyserver-2.2.2/PKG-INFO
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: tuspyserver
|
|
3
|
-
Version: 2.2.2
|
|
4
|
-
Summary: TUS py protocol implementation in FastAPI
|
|
5
|
-
Home-page: https://github.com/edihasaj/tuspy-fast-api
|
|
6
|
-
Author: Edi Hasaj
|
|
7
|
-
Author-email: edi.hasaj@applifyer.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Platform: any
|
|
10
|
-
Classifier: Environment :: Web Environment
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python
|
|
15
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
|
|
19
|
-
# FastAPI Tus
|
|
20
|
-
|
|
21
|
-
A FastAPI extension implementing the [Tus.io](https://tus.io/) upload protocol, with optional dependency-injected hooks for post-upload processing.
|
|
22
|
-
|
|
23
|
-
## Prerequisites
|
|
24
|
-
|
|
25
|
-
* **Python** 3.8+
|
|
26
|
-
* **FastAPI** 0.70+
|
|
27
|
-
* **Starlette** (installed as FastAPI dependency)
|
|
28
|
-
|
|
29
|
-
## Installation
|
|
30
|
-
|
|
31
|
-
Install the latest stable release from PyPI:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pip install tuspyserver
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Or install directly from source:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
git clone https://github.com/edihasaj/tuspy-fast-api
|
|
41
|
-
cd tuspy-fast-api
|
|
42
|
-
pip install .
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Usage
|
|
46
|
-
|
|
47
|
-
### Basic setup
|
|
48
|
-
|
|
49
|
-
In your `main.py`:
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
from fastapi import FastAPI
|
|
53
|
-
from starlette.middleware.cors import CORSMiddleware
|
|
54
|
-
from starlette.staticfiles import StaticFiles
|
|
55
|
-
|
|
56
|
-
from tusserver.tus import create_api_router
|
|
57
|
-
|
|
58
|
-
app = FastAPI()
|
|
59
|
-
app.add_middleware(
|
|
60
|
-
CORSMiddleware,
|
|
61
|
-
allow_origins=["*"],
|
|
62
|
-
allow_methods=["*"],
|
|
63
|
-
allow_headers=["*"],
|
|
64
|
-
expose_headers=[
|
|
65
|
-
"Location",
|
|
66
|
-
"Upload-Offset",
|
|
67
|
-
"Tus-Resumable",
|
|
68
|
-
"Tus-Version",
|
|
69
|
-
"Tus-Extension",
|
|
70
|
-
"Tus-Max-Size",
|
|
71
|
-
"Upload-Expires",
|
|
72
|
-
],
|
|
73
|
-
)
|
|
74
|
-
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
75
|
-
|
|
76
|
-
# Optional: define a simple completion hook
|
|
77
|
-
def on_upload_complete(file_path: str, metadata: dict):
|
|
78
|
-
print("Upload complete:", file_path)
|
|
79
|
-
print("Metadata:", metadata)
|
|
80
|
-
|
|
81
|
-
# Include the router
|
|
82
|
-
app.include_router(
|
|
83
|
-
create_api_router(
|
|
84
|
-
files_dir="/tmp/uploads", # OPTIONAL: directory to store files
|
|
85
|
-
max_size=128_849_018_880, # OPTIONAL: max upload size in bytes (~120GB)
|
|
86
|
-
on_upload_complete=on_upload_complete, # OPTIONAL: callback when upload finishes
|
|
87
|
-
prefix="files", # OPTIONAL: URL prefix (default: 'files')
|
|
88
|
-
),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### !Warning If you include your own router then it is necessary that your app allows these Headers:
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
app.add_middleware(
|
|
97
|
-
CORSMiddleware, (# OPTIONAL: add CORS middleware if needed)
|
|
98
|
-
allow_origins=["*"], # OPTIONAL: allow all origins
|
|
99
|
-
allow_methods=["*"], # OPTIONAL: allow all methods
|
|
100
|
-
allow_headers=["*"], # OPTIONAL: allow all headers
|
|
101
|
-
expose_headers=[ # REQUIRED: expose these headers to the client
|
|
102
|
-
"Location",
|
|
103
|
-
"Upload-Offset",
|
|
104
|
-
"Tus-Resumable",
|
|
105
|
-
"Tus-Version",
|
|
106
|
-
"Tus-Extension",
|
|
107
|
-
"Tus-Max-Size",
|
|
108
|
-
"Upload-Expires",
|
|
109
|
-
],
|
|
110
|
-
)
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Dependency‑Injected Hook (Advanced)
|
|
114
|
-
|
|
115
|
-
For applications using FastAPI's dependency injection, you can supply a factory that returns a DI‑enabled callback. This factory can `Depends()` on any of your services (database session, current user, etc.).
|
|
116
|
-
|
|
117
|
-
```python
|
|
118
|
-
# Define a factory dependency that injects your own services
|
|
119
|
-
from fastapi import Depends
|
|
120
|
-
from your_app.dependencies import get_db, get_current_user
|
|
121
|
-
|
|
122
|
-
def get_upload_handler(
|
|
123
|
-
db=Depends(get_db),
|
|
124
|
-
current_user=Depends(get_current_user),
|
|
125
|
-
) -> Callable[[str, dict], None]:
|
|
126
|
-
def handler(file_path: str, metadata: dict):
|
|
127
|
-
# perform validation or post-processing
|
|
128
|
-
db.log_upload(current_user.id, metadata)
|
|
129
|
-
process_file(file_path)
|
|
130
|
-
return handler
|
|
131
|
-
|
|
132
|
-
# Include router with the DI hook
|
|
133
|
-
app.include_router(
|
|
134
|
-
create_api_router(
|
|
135
|
-
on_upload_complete=None, # keep default
|
|
136
|
-
upload_complete_dep=get_upload_handler, # factory dependency
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Features
|
|
142
|
-
|
|
143
|
-
* **Resumable uploads** via TUS protocol
|
|
144
|
-
* **Chunked transfer** with configurable max size
|
|
145
|
-
* **Metadata storage** (filename, filetype)
|
|
146
|
-
* **Expiration & cleanup** of old uploads (default retention: 5 days)
|
|
147
|
-
* **Download**, **HEAD**, **DELETE**, and **OPTIONS** endpoints
|
|
148
|
-
* **Optional** DI‑friendly `upload_complete_dep` for seamless integration with FastAPI
|
|
149
|
-
|
|
150
|
-
## Scheduler (Cleanup)
|
|
151
|
-
|
|
152
|
-
Expired files are removed when `remove_expired_files()` is called. You can schedule it using your preferred background scheduler (e.g., `APScheduler`, `cron`).
|
|
153
|
-
|
|
154
|
-
```python
|
|
155
|
-
from apscheduler.schedulers.background import BackgroundScheduler
|
|
156
|
-
|
|
157
|
-
scheduler = BackgroundScheduler()
|
|
158
|
-
scheduler.add_job(
|
|
159
|
-
lambda: router.remove_expired_files(),
|
|
160
|
-
trigger='cron',
|
|
161
|
-
hour=1,
|
|
162
|
-
)
|
|
163
|
-
scheduler.start()
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
## Versioning & Publishing
|
|
167
|
-
|
|
168
|
-
**Before releasing:**
|
|
169
|
-
|
|
170
|
-
1. Update the version in `setup.py`:
|
|
171
|
-
|
|
172
|
-
* **Patch** for bug fixes
|
|
173
|
-
* **Minor** for new features
|
|
174
|
-
* **Major** for breaking changes
|
|
175
|
-
2. Commit and push to `main`.
|
|
176
|
-
3. Open a PR to merge `main` → `production`.
|
|
177
|
-
4. Upon merge, CI/CD will publish to PyPI.
|
|
178
|
-
|
|
179
|
-
## Contributing
|
|
180
|
-
|
|
181
|
-
Contributions welcome! Please open issues or PRs on [GitHub](https://github.com/edihasaj/tuspy-fast-api).
|
|
182
|
-
|
|
183
|
-
*© 2025 Edi Hasaj [X](https://x.com/hasajedi)*
|
tuspyserver-2.2.2/README.md
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
# FastAPI Tus
|
|
2
|
-
|
|
3
|
-
A FastAPI extension implementing the [Tus.io](https://tus.io/) upload protocol, with optional dependency-injected hooks for post-upload processing.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
* **Python** 3.8+
|
|
8
|
-
* **FastAPI** 0.70+
|
|
9
|
-
* **Starlette** (installed as FastAPI dependency)
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
Install the latest stable release from PyPI:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
pip install tuspyserver
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Or install directly from source:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
git clone https://github.com/edihasaj/tuspy-fast-api
|
|
23
|
-
cd tuspy-fast-api
|
|
24
|
-
pip install .
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Usage
|
|
28
|
-
|
|
29
|
-
### Basic setup
|
|
30
|
-
|
|
31
|
-
In your `main.py`:
|
|
32
|
-
|
|
33
|
-
```python
|
|
34
|
-
from fastapi import FastAPI
|
|
35
|
-
from starlette.middleware.cors import CORSMiddleware
|
|
36
|
-
from starlette.staticfiles import StaticFiles
|
|
37
|
-
|
|
38
|
-
from tusserver.tus import create_api_router
|
|
39
|
-
|
|
40
|
-
app = FastAPI()
|
|
41
|
-
app.add_middleware(
|
|
42
|
-
CORSMiddleware,
|
|
43
|
-
allow_origins=["*"],
|
|
44
|
-
allow_methods=["*"],
|
|
45
|
-
allow_headers=["*"],
|
|
46
|
-
expose_headers=[
|
|
47
|
-
"Location",
|
|
48
|
-
"Upload-Offset",
|
|
49
|
-
"Tus-Resumable",
|
|
50
|
-
"Tus-Version",
|
|
51
|
-
"Tus-Extension",
|
|
52
|
-
"Tus-Max-Size",
|
|
53
|
-
"Upload-Expires",
|
|
54
|
-
],
|
|
55
|
-
)
|
|
56
|
-
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
57
|
-
|
|
58
|
-
# Optional: define a simple completion hook
|
|
59
|
-
def on_upload_complete(file_path: str, metadata: dict):
|
|
60
|
-
print("Upload complete:", file_path)
|
|
61
|
-
print("Metadata:", metadata)
|
|
62
|
-
|
|
63
|
-
# Include the router
|
|
64
|
-
app.include_router(
|
|
65
|
-
create_api_router(
|
|
66
|
-
files_dir="/tmp/uploads", # OPTIONAL: directory to store files
|
|
67
|
-
max_size=128_849_018_880, # OPTIONAL: max upload size in bytes (~120GB)
|
|
68
|
-
on_upload_complete=on_upload_complete, # OPTIONAL: callback when upload finishes
|
|
69
|
-
prefix="files", # OPTIONAL: URL prefix (default: 'files')
|
|
70
|
-
),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### !Warning If you include your own router then it is necessary that your app allows these Headers:
|
|
76
|
-
|
|
77
|
-
```python
|
|
78
|
-
app.add_middleware(
|
|
79
|
-
CORSMiddleware, (# OPTIONAL: add CORS middleware if needed)
|
|
80
|
-
allow_origins=["*"], # OPTIONAL: allow all origins
|
|
81
|
-
allow_methods=["*"], # OPTIONAL: allow all methods
|
|
82
|
-
allow_headers=["*"], # OPTIONAL: allow all headers
|
|
83
|
-
expose_headers=[ # REQUIRED: expose these headers to the client
|
|
84
|
-
"Location",
|
|
85
|
-
"Upload-Offset",
|
|
86
|
-
"Tus-Resumable",
|
|
87
|
-
"Tus-Version",
|
|
88
|
-
"Tus-Extension",
|
|
89
|
-
"Tus-Max-Size",
|
|
90
|
-
"Upload-Expires",
|
|
91
|
-
],
|
|
92
|
-
)
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Dependency‑Injected Hook (Advanced)
|
|
96
|
-
|
|
97
|
-
For applications using FastAPI's dependency injection, you can supply a factory that returns a DI‑enabled callback. This factory can `Depends()` on any of your services (database session, current user, etc.).
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
# Define a factory dependency that injects your own services
|
|
101
|
-
from fastapi import Depends
|
|
102
|
-
from your_app.dependencies import get_db, get_current_user
|
|
103
|
-
|
|
104
|
-
def get_upload_handler(
|
|
105
|
-
db=Depends(get_db),
|
|
106
|
-
current_user=Depends(get_current_user),
|
|
107
|
-
) -> Callable[[str, dict], None]:
|
|
108
|
-
def handler(file_path: str, metadata: dict):
|
|
109
|
-
# perform validation or post-processing
|
|
110
|
-
db.log_upload(current_user.id, metadata)
|
|
111
|
-
process_file(file_path)
|
|
112
|
-
return handler
|
|
113
|
-
|
|
114
|
-
# Include router with the DI hook
|
|
115
|
-
app.include_router(
|
|
116
|
-
create_api_router(
|
|
117
|
-
on_upload_complete=None, # keep default
|
|
118
|
-
upload_complete_dep=get_upload_handler, # factory dependency
|
|
119
|
-
)
|
|
120
|
-
)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Features
|
|
124
|
-
|
|
125
|
-
* **Resumable uploads** via TUS protocol
|
|
126
|
-
* **Chunked transfer** with configurable max size
|
|
127
|
-
* **Metadata storage** (filename, filetype)
|
|
128
|
-
* **Expiration & cleanup** of old uploads (default retention: 5 days)
|
|
129
|
-
* **Download**, **HEAD**, **DELETE**, and **OPTIONS** endpoints
|
|
130
|
-
* **Optional** DI‑friendly `upload_complete_dep` for seamless integration with FastAPI
|
|
131
|
-
|
|
132
|
-
## Scheduler (Cleanup)
|
|
133
|
-
|
|
134
|
-
Expired files are removed when `remove_expired_files()` is called. You can schedule it using your preferred background scheduler (e.g., `APScheduler`, `cron`).
|
|
135
|
-
|
|
136
|
-
```python
|
|
137
|
-
from apscheduler.schedulers.background import BackgroundScheduler
|
|
138
|
-
|
|
139
|
-
scheduler = BackgroundScheduler()
|
|
140
|
-
scheduler.add_job(
|
|
141
|
-
lambda: router.remove_expired_files(),
|
|
142
|
-
trigger='cron',
|
|
143
|
-
hour=1,
|
|
144
|
-
)
|
|
145
|
-
scheduler.start()
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Versioning & Publishing
|
|
149
|
-
|
|
150
|
-
**Before releasing:**
|
|
151
|
-
|
|
152
|
-
1. Update the version in `setup.py`:
|
|
153
|
-
|
|
154
|
-
* **Patch** for bug fixes
|
|
155
|
-
* **Minor** for new features
|
|
156
|
-
* **Major** for breaking changes
|
|
157
|
-
2. Commit and push to `main`.
|
|
158
|
-
3. Open a PR to merge `main` → `production`.
|
|
159
|
-
4. Upon merge, CI/CD will publish to PyPI.
|
|
160
|
-
|
|
161
|
-
## Contributing
|
|
162
|
-
|
|
163
|
-
Contributions welcome! Please open issues or PRs on [GitHub](https://github.com/edihasaj/tuspy-fast-api).
|
|
164
|
-
|
|
165
|
-
*© 2025 Edi Hasaj [X](https://x.com/hasajedi)*
|
tuspyserver-2.2.2/setup.cfg
DELETED
tuspyserver-2.2.2/setup.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
FastAPI Tus implementation
|
|
3
|
-
-------------
|
|
4
|
-
Implements the tus.io server-side file-upload protocol
|
|
5
|
-
visit https://tus.io for more information
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from setuptools import find_packages, setup
|
|
9
|
-
|
|
10
|
-
with open("README.md", "r") as f:
|
|
11
|
-
long_description = f.read()
|
|
12
|
-
|
|
13
|
-
setup(
|
|
14
|
-
name="tuspyserver",
|
|
15
|
-
version="2.2.2",
|
|
16
|
-
description="TUS py protocol implementation in FastAPI",
|
|
17
|
-
long_description=long_description,
|
|
18
|
-
long_description_content_type="text/markdown",
|
|
19
|
-
author="Edi Hasaj",
|
|
20
|
-
license="MIT",
|
|
21
|
-
author_email="edi.hasaj@applifyer.com",
|
|
22
|
-
url="https://github.com/edihasaj/tuspy-fast-api",
|
|
23
|
-
packages=find_packages(),
|
|
24
|
-
platforms="any",
|
|
25
|
-
include_package_data=True,
|
|
26
|
-
install_requires=[
|
|
27
|
-
"fastapi>=0.110.0",
|
|
28
|
-
"pydantic>=2.6.2",
|
|
29
|
-
],
|
|
30
|
-
classifiers=[
|
|
31
|
-
"Environment :: Web Environment",
|
|
32
|
-
"Intended Audience :: Developers",
|
|
33
|
-
"License :: OSI Approved :: MIT License",
|
|
34
|
-
"Operating System :: OS Independent",
|
|
35
|
-
"Programming Language :: Python",
|
|
36
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
37
|
-
],
|
|
38
|
-
)
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: tuspyserver
|
|
3
|
-
Version: 2.2.2
|
|
4
|
-
Summary: TUS py protocol implementation in FastAPI
|
|
5
|
-
Home-page: https://github.com/edihasaj/tuspy-fast-api
|
|
6
|
-
Author: Edi Hasaj
|
|
7
|
-
Author-email: edi.hasaj@applifyer.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Platform: any
|
|
10
|
-
Classifier: Environment :: Web Environment
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python
|
|
15
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
|
|
19
|
-
# FastAPI Tus
|
|
20
|
-
|
|
21
|
-
A FastAPI extension implementing the [Tus.io](https://tus.io/) upload protocol, with optional dependency-injected hooks for post-upload processing.
|
|
22
|
-
|
|
23
|
-
## Prerequisites
|
|
24
|
-
|
|
25
|
-
* **Python** 3.8+
|
|
26
|
-
* **FastAPI** 0.70+
|
|
27
|
-
* **Starlette** (installed as FastAPI dependency)
|
|
28
|
-
|
|
29
|
-
## Installation
|
|
30
|
-
|
|
31
|
-
Install the latest stable release from PyPI:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pip install tuspyserver
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Or install directly from source:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
git clone https://github.com/edihasaj/tuspy-fast-api
|
|
41
|
-
cd tuspy-fast-api
|
|
42
|
-
pip install .
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Usage
|
|
46
|
-
|
|
47
|
-
### Basic setup
|
|
48
|
-
|
|
49
|
-
In your `main.py`:
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
from fastapi import FastAPI
|
|
53
|
-
from starlette.middleware.cors import CORSMiddleware
|
|
54
|
-
from starlette.staticfiles import StaticFiles
|
|
55
|
-
|
|
56
|
-
from tusserver.tus import create_api_router
|
|
57
|
-
|
|
58
|
-
app = FastAPI()
|
|
59
|
-
app.add_middleware(
|
|
60
|
-
CORSMiddleware,
|
|
61
|
-
allow_origins=["*"],
|
|
62
|
-
allow_methods=["*"],
|
|
63
|
-
allow_headers=["*"],
|
|
64
|
-
expose_headers=[
|
|
65
|
-
"Location",
|
|
66
|
-
"Upload-Offset",
|
|
67
|
-
"Tus-Resumable",
|
|
68
|
-
"Tus-Version",
|
|
69
|
-
"Tus-Extension",
|
|
70
|
-
"Tus-Max-Size",
|
|
71
|
-
"Upload-Expires",
|
|
72
|
-
],
|
|
73
|
-
)
|
|
74
|
-
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
75
|
-
|
|
76
|
-
# Optional: define a simple completion hook
|
|
77
|
-
def on_upload_complete(file_path: str, metadata: dict):
|
|
78
|
-
print("Upload complete:", file_path)
|
|
79
|
-
print("Metadata:", metadata)
|
|
80
|
-
|
|
81
|
-
# Include the router
|
|
82
|
-
app.include_router(
|
|
83
|
-
create_api_router(
|
|
84
|
-
files_dir="/tmp/uploads", # OPTIONAL: directory to store files
|
|
85
|
-
max_size=128_849_018_880, # OPTIONAL: max upload size in bytes (~120GB)
|
|
86
|
-
on_upload_complete=on_upload_complete, # OPTIONAL: callback when upload finishes
|
|
87
|
-
prefix="files", # OPTIONAL: URL prefix (default: 'files')
|
|
88
|
-
),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### !Warning If you include your own router then it is necessary that your app allows these Headers:
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
app.add_middleware(
|
|
97
|
-
CORSMiddleware, (# OPTIONAL: add CORS middleware if needed)
|
|
98
|
-
allow_origins=["*"], # OPTIONAL: allow all origins
|
|
99
|
-
allow_methods=["*"], # OPTIONAL: allow all methods
|
|
100
|
-
allow_headers=["*"], # OPTIONAL: allow all headers
|
|
101
|
-
expose_headers=[ # REQUIRED: expose these headers to the client
|
|
102
|
-
"Location",
|
|
103
|
-
"Upload-Offset",
|
|
104
|
-
"Tus-Resumable",
|
|
105
|
-
"Tus-Version",
|
|
106
|
-
"Tus-Extension",
|
|
107
|
-
"Tus-Max-Size",
|
|
108
|
-
"Upload-Expires",
|
|
109
|
-
],
|
|
110
|
-
)
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Dependency‑Injected Hook (Advanced)
|
|
114
|
-
|
|
115
|
-
For applications using FastAPI's dependency injection, you can supply a factory that returns a DI‑enabled callback. This factory can `Depends()` on any of your services (database session, current user, etc.).
|
|
116
|
-
|
|
117
|
-
```python
|
|
118
|
-
# Define a factory dependency that injects your own services
|
|
119
|
-
from fastapi import Depends
|
|
120
|
-
from your_app.dependencies import get_db, get_current_user
|
|
121
|
-
|
|
122
|
-
def get_upload_handler(
|
|
123
|
-
db=Depends(get_db),
|
|
124
|
-
current_user=Depends(get_current_user),
|
|
125
|
-
) -> Callable[[str, dict], None]:
|
|
126
|
-
def handler(file_path: str, metadata: dict):
|
|
127
|
-
# perform validation or post-processing
|
|
128
|
-
db.log_upload(current_user.id, metadata)
|
|
129
|
-
process_file(file_path)
|
|
130
|
-
return handler
|
|
131
|
-
|
|
132
|
-
# Include router with the DI hook
|
|
133
|
-
app.include_router(
|
|
134
|
-
create_api_router(
|
|
135
|
-
on_upload_complete=None, # keep default
|
|
136
|
-
upload_complete_dep=get_upload_handler, # factory dependency
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Features
|
|
142
|
-
|
|
143
|
-
* **Resumable uploads** via TUS protocol
|
|
144
|
-
* **Chunked transfer** with configurable max size
|
|
145
|
-
* **Metadata storage** (filename, filetype)
|
|
146
|
-
* **Expiration & cleanup** of old uploads (default retention: 5 days)
|
|
147
|
-
* **Download**, **HEAD**, **DELETE**, and **OPTIONS** endpoints
|
|
148
|
-
* **Optional** DI‑friendly `upload_complete_dep` for seamless integration with FastAPI
|
|
149
|
-
|
|
150
|
-
## Scheduler (Cleanup)
|
|
151
|
-
|
|
152
|
-
Expired files are removed when `remove_expired_files()` is called. You can schedule it using your preferred background scheduler (e.g., `APScheduler`, `cron`).
|
|
153
|
-
|
|
154
|
-
```python
|
|
155
|
-
from apscheduler.schedulers.background import BackgroundScheduler
|
|
156
|
-
|
|
157
|
-
scheduler = BackgroundScheduler()
|
|
158
|
-
scheduler.add_job(
|
|
159
|
-
lambda: router.remove_expired_files(),
|
|
160
|
-
trigger='cron',
|
|
161
|
-
hour=1,
|
|
162
|
-
)
|
|
163
|
-
scheduler.start()
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
## Versioning & Publishing
|
|
167
|
-
|
|
168
|
-
**Before releasing:**
|
|
169
|
-
|
|
170
|
-
1. Update the version in `setup.py`:
|
|
171
|
-
|
|
172
|
-
* **Patch** for bug fixes
|
|
173
|
-
* **Minor** for new features
|
|
174
|
-
* **Major** for breaking changes
|
|
175
|
-
2. Commit and push to `main`.
|
|
176
|
-
3. Open a PR to merge `main` → `production`.
|
|
177
|
-
4. Upon merge, CI/CD will publish to PyPI.
|
|
178
|
-
|
|
179
|
-
## Contributing
|
|
180
|
-
|
|
181
|
-
Contributions welcome! Please open issues or PRs on [GitHub](https://github.com/edihasaj/tuspy-fast-api).
|
|
182
|
-
|
|
183
|
-
*© 2025 Edi Hasaj [X](https://x.com/hasajedi)*
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
setup.py
|
|
4
|
-
tuspyserver.egg-info/PKG-INFO
|
|
5
|
-
tuspyserver.egg-info/SOURCES.txt
|
|
6
|
-
tuspyserver.egg-info/dependency_links.txt
|
|
7
|
-
tuspyserver.egg-info/requires.txt
|
|
8
|
-
tuspyserver.egg-info/top_level.txt
|
|
9
|
-
tusserver/__init__.py
|
|
10
|
-
tusserver/metadata.py
|
|
11
|
-
tusserver/tus.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
tusserver
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.0.0"
|
|
File without changes
|