upadte: clean code + add next cloud
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
# upload-GLTF
|
||||
|
||||
A secure web interface for uploading 3D assets (GLTF/GLB + textures) with automatic Draco compression and GitHub push. Built for La Fabrik Durable.
|
||||
A secure web interface for uploading 3D assets (GLTF/GLB + textures) to two destinations:
|
||||
|
||||
- **Nextcloud Drive** — Archives the original files with automatic versioning (VF/V1/V2...), so artists always have a history of past versions.
|
||||
- **GitHub** — Delivers compressed models (Draco via Blender) to the dev team's repository, ready for integration.
|
||||
|
||||
Built for La Fabrik Durable.
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -8,6 +13,7 @@ A secure web interface for uploading 3D assets (GLTF/GLB + textures) with automa
|
||||
- **Three.js** (@react-three/fiber + @react-three/drei) for 3D preview
|
||||
- **Tailwind CSS** for styling
|
||||
- **Octokit** for pushing via the GitHub API
|
||||
- **Nextcloud WebDAV** for Drive archiving with automatic versioning
|
||||
- **Blender** (headless) for Draco mesh compression
|
||||
- **Coolify** (Docker) for hosting
|
||||
|
||||
@@ -29,6 +35,12 @@ GITHUB_TOKEN=ghp_your-github-personal-access-token
|
||||
GIT_BRANCH=main
|
||||
GIT_REPO_URL=https://github.com/your-org/your-repo.git
|
||||
BLENDER_PATH=/Applications/Blender.app/Contents/MacOS/Blender
|
||||
|
||||
# Nextcloud Drive (WebDAV)
|
||||
NEXTCLOUD_URL=https://cloud.example.com
|
||||
NEXTCLOUD_USER=your-nextcloud-username
|
||||
NEXTCLOUD_PASSWORD=your-nextcloud-password
|
||||
NEXTCLOUD_BASE_PATH=Models
|
||||
```
|
||||
|
||||
| Variable | Description | Required |
|
||||
@@ -38,8 +50,12 @@ BLENDER_PATH=/Applications/Blender.app/Contents/MacOS/Blender
|
||||
| `GIT_BRANCH` | Target branch (default: main) | No |
|
||||
| `GIT_REPO_URL` | Target GitHub repository URL | Yes |
|
||||
| `BLENDER_PATH` | Path to Blender binary (default: `blender`) | No |
|
||||
| `NEXTCLOUD_URL` | Nextcloud instance URL | Yes |
|
||||
| `NEXTCLOUD_USER` | Nextcloud username (Basic auth) | Yes |
|
||||
| `NEXTCLOUD_PASSWORD` | Nextcloud password (Basic auth) | Yes |
|
||||
| `NEXTCLOUD_BASE_PATH` | Root folder on the Drive (default: `Models`) | No |
|
||||
|
||||
> To create a token: GitHub > Settings > Developer settings > Fine-grained personal access tokens > select the target repo > Permissions > Contents: Read and write.
|
||||
> To create a GitHub token: GitHub > Settings > Developer settings > Fine-grained personal access tokens > select the target repo > Permissions > Contents: Read and write.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -61,6 +77,9 @@ docker run -p 3000:3000 \
|
||||
-e UPLOAD_SECRET_KEY=your-key \
|
||||
-e GITHUB_TOKEN=ghp_xxx \
|
||||
-e GIT_REPO_URL=https://github.com/org/repo.git \
|
||||
-e NEXTCLOUD_URL=https://cloud.example.com \
|
||||
-e NEXTCLOUD_USER=user \
|
||||
-e NEXTCLOUD_PASSWORD=pass \
|
||||
upload-gltf
|
||||
```
|
||||
|
||||
@@ -74,39 +93,66 @@ The Docker image includes Blender headless (installed once at build time). On st
|
||||
- `model.glb` or `model.gltf` (**required**)
|
||||
- Textures: `roughness`, `normal`, `metalness`, `color`, `displace` (`.png/.jpg/.webp`, **optional** — missing textures show a warning but don't block the upload)
|
||||
4. The model is displayed in a 3D preview
|
||||
5. On clicking "Envoyer sur GitHub":
|
||||
- The app computes the git SHA of each local file and compares with the remote repo
|
||||
5. On clicking "Envoyer":
|
||||
- The app checks the remote Git repo for existing files and computes diffs (textures by size, models always re-pushed)
|
||||
- If the folder doesn't exist, upload proceeds directly
|
||||
- If the folder exists and files differ, a confirmation dialog shows **only the actual changes** (modified, new, or deleted files)
|
||||
- If the folder exists and nothing changed, the upload is skipped entirely ("Aucun fichier modifie")
|
||||
6. For models: the file is written to `/tmp`, compressed with Blender Draco, then the compressed version is pushed
|
||||
7. For textures: pushed directly without compression
|
||||
8. **Only changed and new files are pushed** — unchanged files are skipped to save bandwidth and API calls
|
||||
9. All changes are pushed in a **single commit** with a formatted message that only lists what changed:
|
||||
- If the folder exists and files differ, a confirmation dialog shows **only the actual changes**
|
||||
- If nothing changed, the upload is skipped entirely
|
||||
|
||||
**New folder:**
|
||||
```
|
||||
update: upload-gltf add a new model -> farm/my-model
|
||||
### Upload flow: Drive first, then Git
|
||||
|
||||
📦 Model
|
||||
✅ model.gltf (compressed)
|
||||
🎨 Textures
|
||||
✅ color.jpg
|
||||
❌ metalness (manquant)
|
||||
```
|
||||
6. **Drive upload (archiving)** — Original files (before Blender compression) are uploaded to the Nextcloud Drive with automatic versioning (see below). This serves as the artists' source of truth and version history. If the Drive upload fails, a modal asks the user whether to send to Git only or cancel entirely.
|
||||
7. **Git upload (delivery to devs)** — Models are compressed with Blender Draco, then all changed files are pushed to GitHub in a single commit. This is what the dev team consumes in the application.
|
||||
|
||||
**Update (only metalness changed):**
|
||||
```
|
||||
update: upload-gltf update -> general/coffeetest
|
||||
### Drive versioning (Nextcloud WebDAV)
|
||||
|
||||
🎨 Textures
|
||||
🔄 metalness.jpg
|
||||
```
|
||||
The Drive uses a `VF` (version finale) / `Vx` (archived versions) structure:
|
||||
|
||||
Symbols: `✅` new — `🔄` modified — `❌` missing or deleted
|
||||
```
|
||||
Models/
|
||||
VF/ ← latest version
|
||||
coffeetest/
|
||||
model.gltf
|
||||
color.jpg
|
||||
V1/ ← first archive
|
||||
coffeetest/
|
||||
V2/ ← second archive
|
||||
coffeetest/
|
||||
```
|
||||
|
||||
10. Orphan files (present on remote but not in the new upload) are deleted in the same commit
|
||||
11. If Blender is unavailable, the original model is pushed as-is (graceful fallback)
|
||||
- **New folder** (doesn't exist in `VF/`): files are uploaded directly to `VF/{folderName}/`
|
||||
- **Replace** (folder exists in `VF/` with diffs): `VF/{folderName}` is moved to `Vx/{folderName}` (next available version), then all files are re-uploaded to `VF/{folderName}/`
|
||||
- **No changes**: nothing happens on the Drive
|
||||
|
||||
All files are uploaded to `VF/` (not just diffs), because the move operation empties the previous folder.
|
||||
|
||||
### Commit messages
|
||||
|
||||
All changes are pushed in a **single commit** with a formatted message:
|
||||
|
||||
**New folder:**
|
||||
```
|
||||
update: upload-gltf add a new model -> farm/my-model
|
||||
|
||||
📦 Model
|
||||
✅ model.gltf (compressed)
|
||||
🎨 Textures
|
||||
✅ color.jpg
|
||||
❌ metalness (manquant)
|
||||
```
|
||||
|
||||
**Update (only metalness changed):**
|
||||
```
|
||||
update: upload-gltf update -> general/coffeetest
|
||||
|
||||
🎨 Textures
|
||||
🔄 metalness.jpg
|
||||
```
|
||||
|
||||
Symbols: `✅` new — `🔄` modified — `❌` missing or deleted
|
||||
|
||||
8. Orphan files (present on remote but not in the new upload) are deleted in the same commit
|
||||
9. If Blender is unavailable, the original model is pushed as-is (graceful fallback)
|
||||
|
||||
## Destinations
|
||||
|
||||
@@ -125,14 +171,43 @@ Uploaded models are pushed to `public/models/<destination>/<folderName>/` in the
|
||||
|
||||
```
|
||||
app/
|
||||
├── api/upload/route.ts # API: GET (check + SHA diff) + POST (compress + smart push)
|
||||
├── globals.css # Tailwind + Google Fonts
|
||||
├── layout.tsx # Root layout
|
||||
├── api/upload/
|
||||
│ ├── check/route.ts # GET: check remote folder + file sizes for diff
|
||||
│ ├── drive/route.ts # POST: upload originals to Nextcloud Drive (VF/Vx versioning)
|
||||
│ └── git/route.ts # POST: compress with Blender + push to GitHub
|
||||
├── globals.css # Tailwind + CSS variable fonts
|
||||
├── layout.tsx # Root layout (next/font/google)
|
||||
└── page.tsx # Home page
|
||||
components/
|
||||
├── UploadZone.tsx # UI: key input, destination picker, folder picker, SHA diff, overwrite confirmation, upload
|
||||
├── ModelViewer.tsx # Lazy wrapper for the 3D viewer
|
||||
└── SceneViewer.tsx # Three.js Canvas
|
||||
├── upload/
|
||||
│ ├── SecretInput.tsx # Access key input
|
||||
│ ├── DestinationPicker.tsx # Destination selector
|
||||
│ ├── FolderDropzone.tsx # Folder drag & drop / picker
|
||||
│ ├── FolderCard.tsx # Folder status card (Drive + Git)
|
||||
│ ├── WarningBanner.tsx # Missing texture warnings
|
||||
│ ├── OverwriteConfirmModal.tsx # Diff confirmation dialog
|
||||
│ ├── NoChangesModal.tsx # "No changes detected" dialog
|
||||
│ ├── DriveErrorModal.tsx # "Drive failed, continue?" dialog
|
||||
│ └── ActionButtons.tsx # Upload / Cancel / Reset buttons
|
||||
├── UploadZone.tsx # Main orchestrator (Drive → Git flow)
|
||||
├── ModelViewer.tsx # Lazy wrapper for 3D viewer
|
||||
└── SceneViewer.tsx # Three.js Canvas
|
||||
hooks/
|
||||
├── useSecret.ts # Secret key state management
|
||||
└── useFolderEntries.ts # Folder entries state management
|
||||
lib/
|
||||
├── constants.ts # Shared constants, destinations, extensions
|
||||
├── types.ts # Server types (ParsedFile, FileDiff, etc.)
|
||||
├── client-types.ts # Client types (FolderEntry, DriveStatus, etc.)
|
||||
├── sanitize.ts # Filename sanitization
|
||||
├── auth.ts # Upload secret validation (timing-safe)
|
||||
├── github.ts # Octokit helpers (getRemoteFolder, pushAllToGitHub)
|
||||
├── nextcloud.ts # Nextcloud WebDAV client (native fetch)
|
||||
├── blender.ts # Blender Draco compression
|
||||
├── commit-message.ts # Commit message builder
|
||||
├── parse-upload.ts # FormData parser + validation
|
||||
├── validate-folder.ts # Client-side folder validation
|
||||
└── format-bytes.ts # Byte formatting utility
|
||||
scripts/
|
||||
└── compress.py # Blender Draco compression script
|
||||
Dockerfile # Multi-stage build: Node 20 slim + Blender headless + tini
|
||||
|
||||
Reference in New Issue
Block a user