update: add gestion erreur si dossier est existant
This commit is contained in:
+120
-15
@@ -15,6 +15,7 @@ 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 REQUIRED_TEXTURES = ['roughness', 'normal', 'metalness', 'color', 'displace']
|
||||
const VALID_DESTINATIONS = new Set(['farm', 'map', 'powergrid', 'workshop', 'general', 'environment'])
|
||||
|
||||
const TMP_DIR = join('/tmp', 'assets')
|
||||
|
||||
@@ -89,11 +90,12 @@ async function compressWithBlender(
|
||||
|
||||
function buildCommitMessage(
|
||||
folderName: string,
|
||||
destination: string,
|
||||
modelFilename: string,
|
||||
textureNames: string[],
|
||||
compressed: boolean,
|
||||
): string {
|
||||
const title = `update: from upload-gltf add a new model -> ${folderName}`
|
||||
const title = `update: upload-gltf add a new model -> ${destination}/${folderName}`
|
||||
|
||||
const foundTextures = new Set(
|
||||
textureNames.map(t => t.toLowerCase().replace(/\.[^.]+$/, ''))
|
||||
@@ -111,8 +113,7 @@ function buildCommitMessage(
|
||||
title,
|
||||
'',
|
||||
'📦 Model',
|
||||
` ✅ ${modelFilename}${compressed ? ' (Draco)' : ''}`,
|
||||
'',
|
||||
` ✅ ${modelFilename}${compressed ? ' (compressed)' : ''}`,
|
||||
'🎨 Textures',
|
||||
...textureLines,
|
||||
]
|
||||
@@ -133,12 +134,19 @@ interface ParsedFile {
|
||||
|
||||
async function parseMultiUpload(req: NextRequest): Promise<{
|
||||
folderName: string
|
||||
destination: string
|
||||
files: ParsedFile[]
|
||||
}> {
|
||||
const formData = await req.formData()
|
||||
const folderName = (formData.get('folderName') as string | null)?.trim() || 'assets'
|
||||
const safeFolderName = sanitizeFilename(folderName).replace(/[^a-zA-Z0-9-_]/g, '-')
|
||||
|
||||
const rawDestination = (formData.get('destination') as string | null)?.trim() || 'general'
|
||||
if (!VALID_DESTINATIONS.has(rawDestination)) {
|
||||
throw new Error(`Destination invalide: "${rawDestination}"`)
|
||||
}
|
||||
const destination = rawDestination
|
||||
|
||||
const fileEntries = formData.getAll('files') as File[]
|
||||
const fileTypes = formData.getAll('fileTypes') as string[]
|
||||
const textureNames = formData.getAll('textureNames') as string[]
|
||||
@@ -176,7 +184,7 @@ async function parseMultiUpload(req: NextRequest): Promise<{
|
||||
parsed.push({ filename, buffer, isModel, textureName: texName || undefined })
|
||||
}
|
||||
|
||||
return { folderName: safeFolderName, files: parsed }
|
||||
return { folderName: safeFolderName, destination, files: parsed }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -186,6 +194,7 @@ async function parseMultiUpload(req: NextRequest): Promise<{
|
||||
async function pushAllToGitHub(
|
||||
folderName: string,
|
||||
files: { path: string; contentBase64: string }[],
|
||||
deletePaths: string[],
|
||||
commitMessage: string
|
||||
): Promise<{ commitUrl: string }> {
|
||||
const octokit = getOctokit()
|
||||
@@ -219,17 +228,30 @@ async function pushAllToGitHub(
|
||||
)
|
||||
)
|
||||
|
||||
// 4. Create a single tree with all files
|
||||
// 4. Create a single tree with all files (add new + delete orphans)
|
||||
const newFilePaths = new Set(files.map(f => f.path))
|
||||
const deleteEntries = deletePaths
|
||||
.filter(p => !newFilePaths.has(p))
|
||||
.map(p => ({
|
||||
path: p,
|
||||
mode: '100644' as const,
|
||||
type: 'blob' as const,
|
||||
sha: null,
|
||||
}))
|
||||
|
||||
const { data: newTree } = await octokit.git.createTree({
|
||||
owner,
|
||||
repo,
|
||||
base_tree: commit.tree.sha,
|
||||
tree: files.map((f, i) => ({
|
||||
path: f.path,
|
||||
mode: '100644' as const,
|
||||
type: 'blob' as const,
|
||||
sha: blobResults[i].data.sha,
|
||||
})),
|
||||
tree: [
|
||||
...files.map((f, i) => ({
|
||||
path: f.path,
|
||||
mode: '100644' as const,
|
||||
type: 'blob' as const,
|
||||
sha: blobResults[i].data.sha,
|
||||
})),
|
||||
...deleteEntries,
|
||||
],
|
||||
})
|
||||
|
||||
// 5. Create a single commit
|
||||
@@ -252,6 +274,65 @@ async function pushAllToGitHub(
|
||||
return { commitUrl: newCommit.html_url }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GET handler — check if folder already exists on remote
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const { searchParams } = new URL(req.url)
|
||||
const destination = searchParams.get('destination')?.trim()
|
||||
const folderName = searchParams.get('folderName')?.trim()
|
||||
const secret = req.headers.get('x-upload-secret')
|
||||
const expectedSecret = process.env.UPLOAD_SECRET_KEY
|
||||
|
||||
if (!expectedSecret || !secret || secret !== expectedSecret) {
|
||||
return NextResponse.json({ success: false, error: 'Non autorise' }, { status: 401 })
|
||||
}
|
||||
|
||||
if (!destination || !folderName) {
|
||||
return NextResponse.json({ success: false, error: 'Parametres manquants' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!VALID_DESTINATIONS.has(destination)) {
|
||||
return NextResponse.json({ success: false, error: 'Destination invalide' }, { status: 400 })
|
||||
}
|
||||
|
||||
const safeFolderName = sanitizeFilename(folderName).replace(/[^a-zA-Z0-9-_]/g, '-')
|
||||
const folderPath = `public/models/${destination}/${safeFolderName}`
|
||||
|
||||
try {
|
||||
const octokit = getOctokit()
|
||||
const { owner, repo } = parseRepoUrl()
|
||||
const branch = process.env.GIT_BRANCH ?? 'main'
|
||||
|
||||
const { data } = await octokit.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: folderPath,
|
||||
ref: branch,
|
||||
})
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
const existingFiles = data.map(f => f.name)
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
exists: true,
|
||||
path: folderPath,
|
||||
files: existingFiles,
|
||||
})
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true, exists: false })
|
||||
} catch (err: unknown) {
|
||||
const status = (err as { status?: number })?.status
|
||||
if (status === 404) {
|
||||
return NextResponse.json({ success: true, exists: false })
|
||||
}
|
||||
const message = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||
return NextResponse.json({ success: false, error: message }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POST handler
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -277,10 +358,11 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
// --- Parse all files ---
|
||||
let folderName: string
|
||||
let destination: string
|
||||
let parsedFiles: ParsedFile[]
|
||||
|
||||
try {
|
||||
;({ folderName, files: parsedFiles } = await parseMultiUpload(req))
|
||||
;({ folderName, destination, files: parsedFiles } = await parseMultiUpload(req))
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||
return NextResponse.json({ success: false, error: message }, { status: 400 })
|
||||
@@ -325,17 +407,40 @@ export async function POST(req: NextRequest) {
|
||||
}
|
||||
|
||||
filesToPush.push({
|
||||
path: `public/assets/${folderName}/${pf.filename}`,
|
||||
path: `public/models/${destination}/${folderName}/${pf.filename}`,
|
||||
contentBase64: content.toString('base64'),
|
||||
})
|
||||
}
|
||||
|
||||
// --- Build commit message ---
|
||||
const commitMessage = buildCommitMessage(folderName, modelFilename, textureNames, compressed)
|
||||
const commitMessage = buildCommitMessage(folderName, destination, modelFilename, textureNames, compressed)
|
||||
|
||||
// --- Detect existing files to clean up orphans ---
|
||||
const folderPath = `public/models/${destination}/${folderName}`
|
||||
let existingFilePaths: string[] = []
|
||||
|
||||
try {
|
||||
const octokit = getOctokit()
|
||||
const { owner, repo } = parseRepoUrl()
|
||||
const branch = process.env.GIT_BRANCH ?? 'main'
|
||||
|
||||
const { data } = await octokit.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: folderPath,
|
||||
ref: branch,
|
||||
})
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
existingFilePaths = data.map(f => `${folderPath}/${f.name}`)
|
||||
}
|
||||
} catch {
|
||||
// 404 = folder doesn't exist yet, no cleanup needed
|
||||
}
|
||||
|
||||
// --- Push all in one commit ---
|
||||
try {
|
||||
const { commitUrl } = await pushAllToGitHub(folderName, filesToPush, commitMessage)
|
||||
const { commitUrl } = await pushAllToGitHub(folderName, filesToPush, existingFilePaths, commitMessage)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
|
||||
Reference in New Issue
Block a user