Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build
*.egg-info
.pypirc

.aider*
.idea/*

.venv
uploads/*
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,30 +164,32 @@ scheduler.start()

## Example

You can find a complete working basic example in the [examples](https://github/edihasaj/tuspyserver/tree/main/examples) folder.
You can find a complete working basic example in the [example](https://github/edihasaj/tuspyserver/tree/main/examples) folder.

The example consists of the following:
the example consists of a `backend` serving fastapi with uvicorn, and a `frontend` npm project.

### Running the example

To run the example, you need to install [`uv`](https://docs.astral.sh/uv/) and run the following in the `example/backend` folder:
```bash
basic.py # backend: a tus router added to a fastapi app and runs it with uvicorn
static/index.html # frontend: a simple static HTML file using uppy (based on tus-js-client)
uv run server.py
```

To run it, you need to install [`uv`](https://docs.astral.sh/uv/) and run:
Then, in another terminal window, run the following in `example/frontend`:
```bash
uv run basic.py
npm run dev
```

This should launch the server, and you should now be able to test uploads by browsing to http://localhost:8000/static/index.html.
This should launch the server, and you should now be able to test uploads by browsing to http://localhost:5173.

Uploaded files get placed in the `examples/uploads` folder.
Uploaded files get placed in the `example/backend/uploads` folder.

## Developing

Contributions welcome! Please open issues or PRs on [GitHub](https://github.com/edihasaj/tuspyserver).

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/)
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)
where the root is the [library](https://docs.astral.sh/uv/concepts/projects/init/#libraries) and the example directory is an [unpackaged app](https://docs.astral.sh/uv/concepts/projects/init/#applications)

### Releasing

Expand Down
15 changes: 15 additions & 0 deletions example/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim

WORKDIR /app

# Copy the entire repository to install tuspyserver from workspace
COPY . .

# Set working directory for the backend
WORKDIR /app/example/backend

# Expose port
EXPOSE 8000

# Run the server
CMD ["uv", "run", "server.py"]
12 changes: 4 additions & 8 deletions examples/basic.py → example/backend/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles

import uvicorn

Expand All @@ -17,19 +16,16 @@
allow_headers=["*"],
expose_headers=[
"Location",
"Upload-Offset",
"Tus-Resumable",
"Tus-Version",
"Tus-Extension",
"Tus-Max-Size",
"Upload-Expires",
"Upload-Offset",
"Upload-Length",
"Upload-Expires",
],
)

# serve an html frontend from the static folder
app.mount("/static", StaticFiles(directory="examples/static"), name="static")


# use completion hook to log uploads
def on_upload_complete(file_path: str, metadata: dict):
Expand All @@ -42,7 +38,6 @@ def on_upload_complete(file_path: str, metadata: dict):
app.include_router(
create_tus_router(
files_dir="./uploads",
max_size=128849018880,
on_upload_complete=on_upload_complete,
prefix="files",
)
Expand All @@ -51,7 +46,8 @@ def on_upload_complete(file_path: str, metadata: dict):
# run the app with uvicorn
if __name__ == "__main__":
uvicorn.run(
"basic:app",
"server:app",
host="0.0.0.0",
reload=True,
use_colors=True,
)
2 changes: 2 additions & 0 deletions example/backend/uploads/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
23 changes: 23 additions & 0 deletions example/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
services:
backend:
build:
context: ..
dockerfile: example/backend/Dockerfile
volumes:
- ./backend/uploads:/app/example/backend/uploads

frontend:
build:
context: frontend
dockerfile: Dockerfile
depends_on:
- nginx

nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./proxy/nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- backend
24 changes: 24 additions & 0 deletions example/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
18 changes: 18 additions & 0 deletions example/frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:22-alpine

WORKDIR /app

# Copy package files
COPY . .

# inject env var - use localhost:8000 since the browser needs to access it
RUN echo "VITE_BACKEND_URL=http://localhost" >> .env.production

# Build the app
RUN npm run build

# Expose port
EXPOSE 4173

# Start the preview server
CMD ["npm", "run", "preview", "--", "--host", "0.0.0.0"]
13 changes: 8 additions & 5 deletions examples/static/index.html → example/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<head>
<meta charset="utf-8">
<title>Uppy</title>
<link href="https://releases.transloadit.com/uppy/v3.3.1/uppy.min.css" rel="stylesheet">
<link href="https://releases.transloadit.com/uppy/v4.13.3/uppy.min.css" rel="stylesheet">
</head>

<body>
Expand All @@ -14,20 +14,23 @@ <h1>tuspyserver example</h1>
<div id="drag-drop-area"></div>

<script type="module">
const url = import.meta.env.VITE_BACKEND_URL ?? 'http://localhost:8000'
console.log(`Resolved API url: ${url}`)
import {
Uppy, Dashboard, Tus
} from "https://releases.transloadit.com/uppy/v3.3.1/uppy.min.mjs"
} from "https://releases.transloadit.com/uppy/v4.13.3/uppy.min.mjs"
const uppy = new Uppy()
.use(Dashboard, {
inline: true,
target: '#drag-drop-area'
})
.use(Tus, {
endpoint: 'http://127.0.0.1:8000/files/'
endpoint: `${url}/files/`,
chunkSize: 100_000_000, // 100MB
})

uppy.on('complete', (result) => {
console.log('Upload complete! Files uploaded:', result.successful)
console.log(result)
})
</script>
</body>
Expand All @@ -44,4 +47,4 @@ <h1>tuspyserver example</h1>
}
</style>

</html>
</html>
Loading