upatde: dockerfile init blender
This commit is contained in:
+105
-23
@@ -88,23 +88,85 @@ function validateFolder(files: File[]): { model?: File; textures: TextureFile[];
|
||||
return result
|
||||
}
|
||||
|
||||
async function checkFolderExists(
|
||||
folderName: string,
|
||||
/** Compute the git blob SHA1 for a file (same as `git hash-object`) */
|
||||
async function computeGitBlobSha(file: File): Promise<string> {
|
||||
const buffer = await file.arrayBuffer()
|
||||
const content = new Uint8Array(buffer)
|
||||
const header = new TextEncoder().encode(`blob ${content.length}\0`)
|
||||
const store = new Uint8Array(header.length + content.length)
|
||||
store.set(header)
|
||||
store.set(content, header.length)
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-1', store)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
|
||||
interface FileDiff {
|
||||
name: string
|
||||
status: 'changed' | 'new' | 'deleted'
|
||||
}
|
||||
|
||||
interface CheckResult {
|
||||
exists: boolean
|
||||
diffs: FileDiff[]
|
||||
}
|
||||
|
||||
async function checkFolderDiffs(
|
||||
folder: FolderEntry,
|
||||
destination: string,
|
||||
secret: string,
|
||||
): Promise<{ exists: boolean; files: string[] }> {
|
||||
): Promise<CheckResult> {
|
||||
try {
|
||||
const params = new URLSearchParams({ folderName, destination })
|
||||
const params = new URLSearchParams({ folderName: folder.folderName, destination })
|
||||
const res = await fetch(`/api/upload?${params}`, {
|
||||
headers: { 'x-upload-secret': secret.trim() },
|
||||
})
|
||||
const data = await res.json()
|
||||
if (data.success && data.exists) {
|
||||
return { exists: true, files: data.files || [] }
|
||||
if (!data.success || !data.exists) {
|
||||
return { exists: false, diffs: [] }
|
||||
}
|
||||
return { exists: false, files: [] }
|
||||
|
||||
const remoteFiles: { name: string; sha: string }[] = data.files || []
|
||||
const remoteMap = new Map(remoteFiles.map(f => [f.name.toLowerCase(), f.sha]))
|
||||
|
||||
// Compute SHA for all local files
|
||||
const localFiles: { name: string; sha: string }[] = []
|
||||
localFiles.push({
|
||||
name: folder.modelFile.name,
|
||||
sha: await computeGitBlobSha(folder.modelFile),
|
||||
})
|
||||
for (const tex of folder.textures) {
|
||||
localFiles.push({
|
||||
name: tex.name,
|
||||
sha: await computeGitBlobSha(tex.file),
|
||||
})
|
||||
}
|
||||
|
||||
const diffs: FileDiff[] = []
|
||||
const localNames = new Set<string>()
|
||||
|
||||
for (const local of localFiles) {
|
||||
const key = local.name.toLowerCase()
|
||||
localNames.add(key)
|
||||
const remoteSha = remoteMap.get(key)
|
||||
if (!remoteSha) {
|
||||
diffs.push({ name: local.name, status: 'new' })
|
||||
} else if (remoteSha !== local.sha) {
|
||||
diffs.push({ name: local.name, status: 'changed' })
|
||||
}
|
||||
// unchanged → not in diffs
|
||||
}
|
||||
|
||||
// Files on remote but not in local → deleted
|
||||
for (const [name] of remoteMap) {
|
||||
if (!localNames.has(name)) {
|
||||
diffs.push({ name, status: 'deleted' })
|
||||
}
|
||||
}
|
||||
|
||||
return { exists: true, diffs }
|
||||
} catch {
|
||||
return { exists: false, files: [] }
|
||||
return { exists: false, diffs: [] }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +225,7 @@ export default function UploadZone() {
|
||||
const [secretError, setSecretError] = useState<string | null>(null)
|
||||
const [destination, setDestination] = useState<typeof DESTINATIONS[number]['value']>(DESTINATIONS[0].value)
|
||||
const [abortController, setAbortController] = useState<AbortController | null>(null)
|
||||
const [overwriteConfirm, setOverwriteConfirm] = useState<{ folderName: string; files: string[] } | null>(null)
|
||||
const [overwriteConfirm, setOverwriteConfirm] = useState<{ folderName: string; diffs: FileDiff[] } | null>(null)
|
||||
|
||||
const isSecretEmpty = !secret.trim()
|
||||
|
||||
@@ -181,11 +243,16 @@ export default function UploadZone() {
|
||||
setSecretError(null)
|
||||
setGlobalError(null)
|
||||
|
||||
// Check if folder already exists on remote
|
||||
// Check if folder already exists on remote and compute diffs
|
||||
const folder = files[0]
|
||||
const check = await checkFolderExists(folder.folderName, destination, secret)
|
||||
const check = await checkFolderDiffs(folder, destination, secret)
|
||||
if (check.exists) {
|
||||
setOverwriteConfirm({ folderName: folder.folderName, files: check.files })
|
||||
if (check.diffs.length === 0) {
|
||||
// Nothing changed at all
|
||||
setGlobalError('Aucun fichier modifie — le dossier distant est identique.')
|
||||
return
|
||||
}
|
||||
setOverwriteConfirm({ folderName: folder.folderName, diffs: check.diffs })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -532,26 +599,41 @@ export default function UploadZone() {
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-100">Dossier deja existant</h3>
|
||||
<p className="text-xs text-gray-400 mt-0.5">
|
||||
<span className="font-mono text-yellow-400">{destination}/{overwriteConfirm.folderName}</span> existe deja sur le repo.
|
||||
<span className="font-mono text-yellow-400">{destination}/{overwriteConfirm.folderName}</span> existe deja sur le repo
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{overwriteConfirm.files.length > 0 && (
|
||||
<div className="bg-black-800 border border-white/10 rounded-xl p-3 max-h-32 overflow-y-auto">
|
||||
<p className="text-xs text-gray-500 mb-1.5">Fichiers existants qui seront remplaces :</p>
|
||||
<ul className="space-y-0.5">
|
||||
{overwriteConfirm.files.map((f) => (
|
||||
<li key={f} className="text-xs text-gray-400 font-mono">{f}</li>
|
||||
{overwriteConfirm.diffs.length > 0 && (
|
||||
<div className="bg-black-800 border border-white/10 rounded-xl p-3 max-h-40 overflow-y-auto">
|
||||
<p className="text-xs text-gray-500 mb-2">Modifications detectees :</p>
|
||||
<ul className="space-y-1">
|
||||
{overwriteConfirm.diffs.map((d) => (
|
||||
<li key={d.name} className="flex items-center gap-2 text-xs font-mono">
|
||||
{d.status === 'changed' && (
|
||||
<>
|
||||
<span className="text-yellow-400">🔄</span>
|
||||
<span className="text-gray-300">{d.name}</span>
|
||||
</>
|
||||
)}
|
||||
{d.status === 'new' && (
|
||||
<>
|
||||
<span className="text-green-400">✅</span>
|
||||
<span className="text-gray-300">{d.name}</span>
|
||||
</>
|
||||
)}
|
||||
{d.status === 'deleted' && (
|
||||
<>
|
||||
<span className="text-red-400">❌</span>
|
||||
<span className="text-gray-500 line-through">{d.name}</span>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-xs text-gray-400">
|
||||
Les anciens fichiers seront supprimes et remplaces par les nouveaux. Cette action est irreversible.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => setOverwriteConfirm(null)}
|
||||
|
||||
Reference in New Issue
Block a user