update: remove docker
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
UPLOAD_SECRET_KEY=your-secret-key-here
|
||||
GITHUB_TOKEN=ghp_your-github-personal-access-token
|
||||
GIT_BRANCH=ta-branch
|
||||
GIT_REPO_URL=https://github.com/your-org/your-repo.git
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
# =============================================================================
|
||||
# Asset Bridge 3D — Dockerfile optimisé pour Coolify
|
||||
# Node 20 Alpine · Git LFS · SSH · Multi-stage build
|
||||
# =============================================================================
|
||||
|
||||
# ─── Stage 1 : Dépendances ───────────────────────────────────────────────────
|
||||
FROM node:20-alpine AS deps
|
||||
|
||||
# Outils système nécessaires : git, git-lfs, openssh (pour push SSH)
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
git-lfs \
|
||||
openssh-client \
|
||||
ca-certificates
|
||||
|
||||
# Initialiser Git LFS globalement
|
||||
RUN git lfs install --system
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copier uniquement les fichiers de dépendances pour profiter du cache Docker
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
RUN npm ci --ignore-scripts
|
||||
|
||||
# ─── Stage 2 : Build ─────────────────────────────────────────────────────────
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache git git-lfs openssh-client ca-certificates
|
||||
RUN git lfs install --system
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Récupérer node_modules depuis le stage deps
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Build Next.js en mode production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV NODE_ENV=production
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# ─── Stage 3 : Production ────────────────────────────────────────────────────
|
||||
FROM node:20-alpine AS runner
|
||||
|
||||
LABEL maintainer="Asset Bridge 3D"
|
||||
LABEL description="Interface d'upload 3D sécurisée avec Git LFS"
|
||||
|
||||
# Outils runtime : git, git-lfs, openssh (pour le git push au runtime)
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
git-lfs \
|
||||
openssh-client \
|
||||
ca-certificates \
|
||||
tini
|
||||
|
||||
# Initialiser Git LFS au niveau système
|
||||
RUN git lfs install --system
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
|
||||
# Créer un utilisateur non-root pour la sécurité
|
||||
# Note : on garde root pour les opérations git/SSH en contexte Coolify
|
||||
# Si vous souhaitez un user non-root, adaptez les permissions SSH en conséquence
|
||||
|
||||
# Copier les artefacts de build
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/.gitattributes ./.gitattributes
|
||||
|
||||
# S'assurer que le dossier models existe (sera écrasé par le volume Coolify)
|
||||
RUN mkdir -p ./public/models
|
||||
|
||||
# Copier le script d'entrée
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# Utiliser tini comme PID 1 pour une gestion correcte des signaux
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/docker-entrypoint.sh"]
|
||||
CMD ["node", "server.js"]
|
||||
@@ -1,23 +1,14 @@
|
||||
# upload-GLTF
|
||||
|
||||
A secure upload interface for 3D assets with automatic deployment to GitHub via Git LFS. Designers can drag-and-drop models and textures, which are automatically versioned and pushed to a GitHub repository for easy collaboration and asset management. Built with Next.js, Tailwind CSS, and React.
|
||||
A secure web interface for uploading 3D assets (GLTF/GLB + textures) and automatically pushing them to a GitHub repository via the GitHub API. Built for La Fabrik Durable.
|
||||
|
||||
## Description
|
||||
## Stack
|
||||
|
||||
This is a Next.js web application that allows designers and developers to upload 3D models and textures via a modern drag-and-drop interface. Uploaded files are automatically committed and pushed to a GitHub repository using Git LFS for efficient version control of large binary files.
|
||||
|
||||
## Features
|
||||
|
||||
- **Drag-and-drop upload** for 3D models (.glb, .gltf, .fbx) and textures (.png, .jpg, .jpeg, .webp, .ktx2)
|
||||
- **2 GB max** file size per upload
|
||||
- **Automatic Git deployment** — files are committed and pushed to GitHub via Git LFS
|
||||
- **Secret key authentication** for secure uploads
|
||||
- **Modern UI** built with Tailwind CSS and React
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 18+
|
||||
- npm or yarn
|
||||
- **Next.js 16** (App Router) + React 19 + TypeScript
|
||||
- **Three.js** (@react-three/fiber + @react-three/drei) for 3D preview
|
||||
- **Tailwind CSS** for styling
|
||||
- **Octokit** for pushing via the GitHub API
|
||||
- **Vercel** for hosting
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -29,42 +20,23 @@ npm install
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Create a `.env.local` file in the root directory:
|
||||
Copy `.env.example` to `.env.local` and fill in the values:
|
||||
|
||||
```env
|
||||
UPLOAD_SECRET_KEY=your-secret-key-here
|
||||
GITHUB_TOKEN=ghp_your-github-personal-access-token
|
||||
GIT_BRANCH=main
|
||||
GIT_REPO_URL=git@github.com:username/repo.git
|
||||
GIT_REPO_URL=https://github.com/your-org/your-repo.git
|
||||
```
|
||||
|
||||
| Variable | Description | Required |
|
||||
|----------|-------------|----------|
|
||||
| `UPLOAD_SECRET_KEY` | Secret key for authentication | Yes |
|
||||
| `GIT_BRANCH` | Git branch to push to (default: main) | No |
|
||||
| `GIT_REPO_URL` | Git repository URL for SSH push | No |
|
||||
| `UPLOAD_SECRET_KEY` | Secret key for upload authentication | Yes |
|
||||
| `GITHUB_TOKEN` | GitHub Personal Access Token (scope `repo`) | Yes |
|
||||
| `GIT_BRANCH` | Target branch (default: main) | No |
|
||||
| `GIT_REPO_URL` | Target GitHub repository URL | Yes |
|
||||
|
||||
### Git LFS Setup
|
||||
|
||||
Install Git LFS and track the asset file types by creating a `.gitattributes` file:
|
||||
|
||||
```bash
|
||||
git lfs install
|
||||
```
|
||||
|
||||
Create `.gitattributes`:
|
||||
|
||||
```
|
||||
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||
*.gltf filter=lfs diff=lfs merge=lfs -text
|
||||
*.fbx filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.webp filter=lfs diff=lfs merge=lfs -text
|
||||
*.ktx2 filter=lfs diff=lfs merge=lfs -text
|
||||
```
|
||||
> To create a token: GitHub > Settings > Developer settings > Personal access tokens > Generate new token (classic) with the `repo` scope.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -76,76 +48,40 @@ npm run dev
|
||||
|
||||
Access the app at `http://localhost:3000`
|
||||
|
||||
### Production
|
||||
### Production (Vercel)
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
Deploy to Vercel and configure environment variables in the dashboard.
|
||||
|
||||
### Lint
|
||||
## How it works
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
### Docker Compose
|
||||
|
||||
Create `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- UPLOAD_SECRET_KEY=your-secret-key
|
||||
- GIT_BRANCH=main
|
||||
- GIT_REPO_URL=git@github.com:username/repo.git
|
||||
volumes:
|
||||
- ./public:/app/public
|
||||
- ~/.ssh:/root/.ssh
|
||||
```
|
||||
|
||||
Build and run:
|
||||
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
### Coolify
|
||||
|
||||
The Dockerfile is optimized for Coolify deployment. Configure environment variables through the Coolify interface.
|
||||
1. The user enters their access key
|
||||
2. They select a folder containing:
|
||||
- `model.glb` or `model.gltf` (required)
|
||||
- Textures: `roughness`, `normal`, `metalness`, `color`, `displace` (`.png/.jpg/.webp`, optional)
|
||||
3. The model is displayed in a 3D preview
|
||||
4. On clicking "Upload & Push to GitHub", each file is sent to the `/api/upload` endpoint
|
||||
5. The API validates the file and pushes it directly to the GitHub repo via the API (no git CLI needed)
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── app/
|
||||
│ ├── api/upload/route.ts # Upload API + git push
|
||||
│ ├── globals.css
|
||||
│ ├── layout.tsx
|
||||
│ └── page.tsx
|
||||
├── components/
|
||||
│ └── UploadZone.tsx # Upload component
|
||||
├── public/
|
||||
│ ├── models/ # Uploaded 3D models
|
||||
│ └── textures/ # Uploaded textures
|
||||
├── Dockerfile
|
||||
├── package.json
|
||||
├── tailwind.config.ts
|
||||
└── tsconfig.json
|
||||
app/
|
||||
├── api/upload/route.ts # API: validation + GitHub push via Octokit
|
||||
├── globals.css # Tailwind + Google Fonts
|
||||
├── layout.tsx # Root layout
|
||||
└── page.tsx # Home page
|
||||
components/
|
||||
├── UploadZone.tsx # UI: key input, folder picker, validation, upload
|
||||
├── ModelViewer.tsx # Lazy wrapper for the 3D viewer
|
||||
└── SceneViewer.tsx # Three.js Canvas
|
||||
```
|
||||
|
||||
## Supported File Formats
|
||||
## Supported Formats
|
||||
|
||||
| Type | Extensions |
|
||||
|------|------------|
|
||||
| 3D Models | .glb, .gltf, .fbx |
|
||||
| Textures | .png, .jpg, .jpeg, .webp, .ktx2 |
|
||||
| 3D Models | `.glb`, `.gltf` |
|
||||
| Textures | `.png`, `.jpg`, `.jpeg`, `.webp` |
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+96
-84
@@ -1,7 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { writeFileSync, mkdirSync, existsSync, rmSync } from 'fs'
|
||||
import { join, extname, basename } from 'path'
|
||||
import { execSync } from 'child_process'
|
||||
import { Octokit } from '@octokit/rest'
|
||||
import { extname, basename } from 'path'
|
||||
|
||||
export const runtime = 'nodejs'
|
||||
export const dynamic = 'force-dynamic'
|
||||
@@ -10,8 +9,6 @@ const MODEL_EXTENSIONS = new Set(['.glb', '.gltf'])
|
||||
const TEXTURE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.webp'])
|
||||
const ALL_ALLOWED_EXTENSIONS = new Set([...MODEL_EXTENSIONS, ...TEXTURE_EXTENSIONS])
|
||||
|
||||
const ASSETS_DIR = join(process.cwd(), 'public', 'assets')
|
||||
|
||||
function sanitizeFilename(name: string): string {
|
||||
return basename(name)
|
||||
.replace(/[^a-zA-Z0-9._-]/g, '_')
|
||||
@@ -19,7 +16,28 @@ function sanitizeFilename(name: string): string {
|
||||
.toLowerCase()
|
||||
}
|
||||
|
||||
async function parseUpload(req: NextRequest): Promise<{ filename: string; savedPath: string; relPath: string; folderName: string }> {
|
||||
function getOctokit(): Octokit {
|
||||
const token = process.env.GITHUB_TOKEN
|
||||
if (!token) throw new Error('GITHUB_TOKEN non configuré')
|
||||
return new Octokit({ auth: token })
|
||||
}
|
||||
|
||||
function parseRepoUrl(): { owner: string; repo: string } {
|
||||
const url = process.env.GIT_REPO_URL
|
||||
if (!url) throw new Error('GIT_REPO_URL non configuré')
|
||||
|
||||
// Support formats: https://github.com/owner/repo.git, owner/repo, git@github.com:owner/repo.git
|
||||
const httpsMatch = url.match(/github\.com\/([^/]+)\/([^/.]+)/)
|
||||
const sshMatch = url.match(/github\.com:([^/]+)\/([^/.]+)/)
|
||||
const shortMatch = url.match(/^([^/]+)\/([^/]+)$/)
|
||||
|
||||
const match = httpsMatch || sshMatch || shortMatch
|
||||
if (!match) throw new Error(`Format GIT_REPO_URL invalide: "${url}"`)
|
||||
|
||||
return { owner: match[1], repo: match[2] }
|
||||
}
|
||||
|
||||
async function parseUpload(req: NextRequest) {
|
||||
const formData = await req.formData()
|
||||
const file = formData.get('file') as File | null
|
||||
const folderName = (formData.get('folderName') as string | null)?.trim() || 'assets'
|
||||
@@ -38,7 +56,6 @@ async function parseUpload(req: NextRequest): Promise<{ filename: string; savedP
|
||||
}
|
||||
|
||||
const safeFolderName = sanitizeFilename(folderName).replace(/[^a-zA-Z0-9-_]/g, '-')
|
||||
const destDir = join(ASSETS_DIR, safeFolderName)
|
||||
|
||||
let filename: string
|
||||
if (fileType === 'texture' && textureName) {
|
||||
@@ -47,82 +64,79 @@ async function parseUpload(req: NextRequest): Promise<{ filename: string; savedP
|
||||
filename = originalSafe
|
||||
}
|
||||
|
||||
mkdirSync(destDir, { recursive: true })
|
||||
|
||||
const savedPath = join(destDir, filename)
|
||||
const buffer = Buffer.from(await file.arrayBuffer())
|
||||
writeFileSync(savedPath, buffer)
|
||||
const content = buffer.toString('base64')
|
||||
const path = `public/assets/${safeFolderName}/${filename}`
|
||||
|
||||
const relPath = join('public', 'assets', safeFolderName, filename)
|
||||
|
||||
return { filename, savedPath, relPath, folderName: safeFolderName }
|
||||
return { filename, content, path, folderName: safeFolderName }
|
||||
}
|
||||
|
||||
function runGitPush(folderName: string): { output: string } {
|
||||
async function pushToGitHub(
|
||||
filePath: string,
|
||||
content: string,
|
||||
folderName: string
|
||||
): Promise<{ commitUrl: string }> {
|
||||
const octokit = getOctokit()
|
||||
const { owner, repo } = parseRepoUrl()
|
||||
const branch = process.env.GIT_BRANCH ?? 'main'
|
||||
const repoUrl = process.env.GIT_REPO_URL
|
||||
|
||||
const execOpts = {
|
||||
cwd: process.cwd(),
|
||||
encoding: 'utf-8' as const,
|
||||
timeout: 120_000,
|
||||
env: {
|
||||
...process.env,
|
||||
GIT_SSH_COMMAND: 'ssh -i /root/.ssh/id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/root/.ssh/known_hosts',
|
||||
GIT_AUTHOR_NAME: 'Asset Bridge 3D',
|
||||
GIT_AUTHOR_EMAIL: 'deploy@assetbridge.local',
|
||||
GIT_COMMITTER_NAME: 'Asset Bridge 3D',
|
||||
GIT_COMMITTER_EMAIL: 'deploy@assetbridge.local',
|
||||
// 1. Get the current commit SHA on the branch
|
||||
const { data: ref } = await octokit.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${branch}`,
|
||||
})
|
||||
const latestCommitSha = ref.object.sha
|
||||
|
||||
// 2. Get the tree of the latest commit
|
||||
const { data: commit } = await octokit.git.getCommit({
|
||||
owner,
|
||||
repo,
|
||||
commit_sha: latestCommitSha,
|
||||
})
|
||||
|
||||
// 3. Create a blob for the file
|
||||
const { data: blob } = await octokit.git.createBlob({
|
||||
owner,
|
||||
repo,
|
||||
content,
|
||||
encoding: 'base64',
|
||||
})
|
||||
|
||||
// 4. Create a new tree with the file added
|
||||
const { data: newTree } = await octokit.git.createTree({
|
||||
owner,
|
||||
repo,
|
||||
base_tree: commit.tree.sha,
|
||||
tree: [
|
||||
{
|
||||
path: filePath,
|
||||
mode: '100644',
|
||||
type: 'blob',
|
||||
sha: blob.sha,
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
execSync('git rev-parse --git-dir', { cwd: process.cwd(), stdio: 'ignore' })
|
||||
} catch {
|
||||
execSync('git init && git lfs install', { ...execOpts, stdio: 'pipe' })
|
||||
if (repoUrl) {
|
||||
execSync(`git remote add origin "${repoUrl}"`, { ...execOpts, stdio: 'pipe' })
|
||||
}
|
||||
try {
|
||||
execSync(`git fetch origin ${branch}`, { ...execOpts, stdio: 'pipe', timeout: 30_000 })
|
||||
execSync(`git checkout -b ${branch} --track origin/${branch}`, { ...execOpts, stdio: 'pipe' })
|
||||
} catch {
|
||||
execSync(`git checkout -b ${branch}`, { ...execOpts, stdio: 'pipe' })
|
||||
}
|
||||
}
|
||||
|
||||
if (repoUrl) {
|
||||
execSync(`git remote set-url origin "${repoUrl}"`, { ...execOpts, stdio: 'pipe' })
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
// 5. Create a new commit
|
||||
const timestamp = new Date().toISOString()
|
||||
let output = ''
|
||||
const { data: newCommit } = await octokit.git.createCommit({
|
||||
owner,
|
||||
repo,
|
||||
message: `Design Update: ${folderName} [${timestamp}]`,
|
||||
tree: newTree.sha,
|
||||
parents: [latestCommitSha],
|
||||
})
|
||||
|
||||
output += execSync(`git fetch origin ${branch}`, execOpts)
|
||||
output += execSync(`git reset --hard origin/${branch}`, execOpts)
|
||||
// 6. Update the branch reference
|
||||
await octokit.git.updateRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${branch}`,
|
||||
sha: newCommit.sha,
|
||||
})
|
||||
|
||||
const folderPath = join('public', 'assets', folderName)
|
||||
if (existsSync(folderPath)) {
|
||||
output += execSync(`git add "${folderPath}"`, execOpts)
|
||||
}
|
||||
|
||||
try {
|
||||
output += execSync(`git commit -m "Design Update: ${folderName} [${timestamp}]"`, execOpts)
|
||||
} catch (err) {
|
||||
const msg = [
|
||||
err instanceof Error ? err.message : '',
|
||||
(err as NodeJS.ErrnoException & { stdout?: string })?.stdout ?? '',
|
||||
(err as NodeJS.ErrnoException & { stderr?: string })?.stderr ?? '',
|
||||
].join(' ')
|
||||
if (!msg.includes('nothing to commit') && !msg.includes('nothing added to commit')) {
|
||||
throw err
|
||||
}
|
||||
output += '[rien à committer]\n'
|
||||
}
|
||||
|
||||
output += execSync(`git push origin ${branch}`, execOpts)
|
||||
|
||||
return { output }
|
||||
return { commitUrl: newCommit.html_url }
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
@@ -131,45 +145,43 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
if (!expectedSecret) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Configuration serveur incomplète' },
|
||||
{ success: false, error: 'Configuration serveur incomplète (UPLOAD_SECRET_KEY manquant)' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
if (!secret || secret !== expectedSecret) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Clé d\'authentification invalide' },
|
||||
{ success: false, error: "Clé d'authentification invalide" },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
let filename: string
|
||||
let savedPath: string
|
||||
let relPath: string
|
||||
let content: string
|
||||
let path: string
|
||||
let folderName: string
|
||||
|
||||
try {
|
||||
;({ filename, savedPath, relPath, folderName } = await parseUpload(req))
|
||||
;({ filename, content, path, folderName } = await parseUpload(req))
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||
return NextResponse.json({ success: false, error: message }, { status: 400 })
|
||||
}
|
||||
|
||||
try {
|
||||
const { output } = runGitPush(folderName)
|
||||
const { commitUrl } = await pushToGitHub(path, content, folderName)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
filename,
|
||||
message: `"${filename}" ajouté au dossier "${folderName}" et poussé sur Git.`,
|
||||
gitOutput: output,
|
||||
message: `"${filename}" ajouté au dossier "${folderName}" et poussé sur GitHub.`,
|
||||
commitUrl,
|
||||
})
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erreur git inconnue'
|
||||
const stderr = (err as NodeJS.ErrnoException & { stderr?: string })?.stderr ?? ''
|
||||
|
||||
const message = err instanceof Error ? err.message : 'Erreur GitHub inconnue'
|
||||
return NextResponse.json(
|
||||
{ success: false, error: `Fichier sauvegardé mais git push a échoué: ${message}`, gitError: stderr },
|
||||
{ success: false, error: `Push GitHub échoué: ${message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ export default function UploadZone() {
|
||||
hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-white/50 border border-white/20"
|
||||
style={{ color: '#000000' }}
|
||||
>
|
||||
Upload & Push to Git
|
||||
Upload & Push to GitHub
|
||||
</button>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/bin/sh
|
||||
# =============================================================================
|
||||
# Asset Bridge 3D — Entrypoint Docker
|
||||
# Configure Git, SSH et LFS au démarrage du container
|
||||
# =============================================================================
|
||||
set -e
|
||||
|
||||
echo "[entrypoint] Démarrage d'Asset Bridge 3D..."
|
||||
|
||||
# ─── 1. Configurer l'identité Git ────────────────────────────────────────────
|
||||
git config --global user.email "${GIT_USER_EMAIL:-deploy@asset-bridge.local}"
|
||||
git config --global user.name "${GIT_USER_NAME:-Asset Bridge Bot}"
|
||||
git config --global init.defaultBranch "${GIT_BRANCH:-main}"
|
||||
|
||||
# Autoriser le dossier /app comme safe.directory (requis depuis Git 2.35.2)
|
||||
git config --global --add safe.directory /app
|
||||
|
||||
echo "[entrypoint] Identité Git configurée."
|
||||
|
||||
# ─── 2. Injecter la clé SSH privée ───────────────────────────────────────────
|
||||
if [ -n "$SSH_PRIVATE_KEY" ]; then
|
||||
mkdir -p /root/.ssh
|
||||
chmod 700 /root/.ssh
|
||||
|
||||
# Écrire la clé — Coolify préserve les vrais sauts de ligne en multiline
|
||||
# On strip les \r éventuels (Windows CRLF) pour éviter "error in libcrypto"
|
||||
printf '%s\n' "$SSH_PRIVATE_KEY" | tr -d '\r' > /root/.ssh/id_rsa
|
||||
chmod 600 /root/.ssh/id_rsa
|
||||
|
||||
# Ajouter github.com aux known_hosts pour éviter l'invite interactive
|
||||
ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> /root/.ssh/known_hosts 2>/dev/null
|
||||
chmod 644 /root/.ssh/known_hosts
|
||||
|
||||
echo "[entrypoint] Clé SSH configurée et github.com ajouté aux known_hosts."
|
||||
else
|
||||
echo "[entrypoint] AVERTISSEMENT : SSH_PRIVATE_KEY non définie — git push SSH ne fonctionnera pas."
|
||||
fi
|
||||
|
||||
# ─── 3. Initialiser Git LFS dans le dossier de travail ───────────────────────
|
||||
cd /app
|
||||
|
||||
git lfs install --local 2>/dev/null || true
|
||||
|
||||
# ─── 4. Initialiser le repo Git si nécessaire ────────────────────────────────
|
||||
# Le volume persistant Coolify monte /app/public/models
|
||||
# Si le repo n'est pas initialisé, on le fait ici
|
||||
|
||||
if [ ! -d ".git" ]; then
|
||||
echo "[entrypoint] Initialisation du dépôt Git local..."
|
||||
|
||||
git init
|
||||
git lfs install
|
||||
|
||||
# Copier .gitattributes si pas déjà présent
|
||||
if [ -f ".gitattributes" ]; then
|
||||
echo "[entrypoint] .gitattributes détecté."
|
||||
fi
|
||||
|
||||
if [ -n "$GIT_REPO_URL" ]; then
|
||||
git remote add origin "$GIT_REPO_URL"
|
||||
echo "[entrypoint] Remote origin configuré : $GIT_REPO_URL"
|
||||
|
||||
BRANCH="${GIT_BRANCH:-main}"
|
||||
|
||||
# Tenter de récupérer l'historique distant
|
||||
if git fetch origin "$BRANCH" 2>/dev/null; then
|
||||
git checkout -b "$BRANCH" --track "origin/$BRANCH" 2>/dev/null || \
|
||||
git checkout "$BRANCH" 2>/dev/null || true
|
||||
echo "[entrypoint] Branch '$BRANCH' récupérée depuis origin."
|
||||
else
|
||||
git checkout -b "$BRANCH" 2>/dev/null || true
|
||||
echo "[entrypoint] Branch locale '$BRANCH' créée."
|
||||
fi
|
||||
else
|
||||
echo "[entrypoint] AVERTISSEMENT : GIT_REPO_URL non définie — les pushes Git échoueront."
|
||||
git checkout -b "${GIT_BRANCH:-main}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "[entrypoint] Dépôt Git initialisé."
|
||||
else
|
||||
echo "[entrypoint] Dépôt Git existant détecté."
|
||||
|
||||
# Vérifier que le remote origin est configuré
|
||||
if [ -n "$GIT_REPO_URL" ]; then
|
||||
if ! git remote get-url origin >/dev/null 2>&1; then
|
||||
git remote add origin "$GIT_REPO_URL"
|
||||
echo "[entrypoint] Remote origin ajouté : $GIT_REPO_URL"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[entrypoint] Configuration terminée. Lancement de l'application..."
|
||||
|
||||
# ─── 5. Lancer la commande principale ────────────────────────────────────────
|
||||
exec "$@"
|
||||
+1
-12
@@ -1,16 +1,5 @@
|
||||
import type { NextConfig } from 'next'
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
// Output standalone pour Docker (inclut uniquement les fichiers nécessaires)
|
||||
output: 'standalone',
|
||||
|
||||
// Augmenter la taille max des réponses pour les gros fichiers
|
||||
// Note : la limite d'upload est gérée par le proxy Nginx (client_max_body_size 500M dans Coolify)
|
||||
experimental: {
|
||||
serverActions: {
|
||||
bodySizeLimit: '2gb',
|
||||
},
|
||||
},
|
||||
}
|
||||
const nextConfig: NextConfig = {}
|
||||
|
||||
export default nextConfig
|
||||
|
||||
Generated
+207
-102
@@ -1,32 +1,30 @@
|
||||
{
|
||||
"name": "asset-bridge-3d",
|
||||
"version": "0.1.0",
|
||||
"name": "upload-gltf",
|
||||
"version": "0.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "asset-bridge-3d",
|
||||
"version": "0.1.0",
|
||||
"name": "upload-gltf",
|
||||
"version": "0.0.2",
|
||||
"dependencies": {
|
||||
"@react-three/drei": "~10.7.0",
|
||||
"@react-three/fiber": "~9.5.0",
|
||||
"next": "~16.2.1",
|
||||
"react": "~19.0.0",
|
||||
"react-dom": "~19.0.0",
|
||||
"react-dropzone": "~14.2.3",
|
||||
"three": "~0.183.0",
|
||||
"zustand": "~5.0.3"
|
||||
"@octokit/rest": "^22.0.1",
|
||||
"@react-three/drei": "^10.7.0",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"next": "^16.2.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"three": "^0.183.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/busboy": "~1.5.4",
|
||||
"@types/node": "~22.13.0",
|
||||
"@types/react": "~19.0.0",
|
||||
"@types/react-dom": "~19.0.0",
|
||||
"@types/three": "~0.183.0",
|
||||
"autoprefixer": "~10.4.20",
|
||||
"postcss": "~8.5.1",
|
||||
"tailwindcss": "~3.4.17",
|
||||
"typescript": "~5.7.3"
|
||||
"@types/node": "^22.13.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/three": "^0.183.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -822,6 +820,161 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
|
||||
"integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
|
||||
"integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.3",
|
||||
"@octokit/request": "^10.0.6",
|
||||
"@octokit/request-error": "^7.0.2",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"before-after-hook": "^4.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz",
|
||||
"integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz",
|
||||
"integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^10.0.6",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "27.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
|
||||
"integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
|
||||
"integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-request-log": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz",
|
||||
"integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz",
|
||||
"integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "10.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz",
|
||||
"integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^11.0.3",
|
||||
"@octokit/request-error": "^7.0.2",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"fast-content-type-parse": "^3.0.0",
|
||||
"json-with-bigint": "^3.5.3",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz",
|
||||
"integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"version": "22.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz",
|
||||
"integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/core": "^7.0.6",
|
||||
"@octokit/plugin-paginate-rest": "^14.0.0",
|
||||
"@octokit/plugin-request-log": "^6.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
|
||||
"integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^27.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/drei": {
|
||||
"version": "10.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz",
|
||||
@@ -925,16 +1078,6 @@
|
||||
"integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/busboy": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.4.tgz",
|
||||
"integrity": "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/draco3d": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
||||
@@ -1064,15 +1207,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/attr-accept": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
|
||||
"integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.27",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
|
||||
@@ -1142,6 +1276,12 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
|
||||
"integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
@@ -1439,6 +1579,22 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
|
||||
"integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
@@ -1485,18 +1641,6 @@
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-selector": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
|
||||
"integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
@@ -1709,10 +1853,10 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"node_modules/json-with-bigint": {
|
||||
"version": "3.5.8",
|
||||
"resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz",
|
||||
"integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lie": {
|
||||
@@ -1744,18 +1888,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/maath": {
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz",
|
||||
@@ -1937,6 +2069,7 @@
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -2186,17 +2319,6 @@
|
||||
"lie": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -2245,29 +2367,6 @@
|
||||
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-dropzone": {
|
||||
"version": "14.2.10",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.10.tgz",
|
||||
"integrity": "sha512-Y98LOCYxGO2jOFWREeKJlL7gbrHcOlTBp+9DCM1dh9XQ8+P/8ThhZT7kFb05C+bPcTXq/rixpU+5+LzwYrFLUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"attr-accept": "^2.2.2",
|
||||
"file-selector": "^0.6.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8 || 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-use-measure": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
|
||||
@@ -2820,6 +2919,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
|
||||
+4
-6
@@ -9,17 +9,15 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/rest": "^22.0.1",
|
||||
"@react-three/drei": "^10.7.0",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"next": "^16.2.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"three": "^0.183.0",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"@react-three/drei": "^10.7.0",
|
||||
"zustand": "^5.0.3"
|
||||
"three": "^0.183.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/busboy": "^1.5.4",
|
||||
"@types/node": "^22.13.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
|
||||
Reference in New Issue
Block a user