new model viewer fix
Build and Push Docker Image / build (push) Successful in 14s

This commit is contained in:
jason
2026-04-23 09:17:23 -05:00
parent 18b3463487
commit 023344aef0
3 changed files with 105 additions and 4 deletions
+46 -3
View File
@@ -242,10 +242,15 @@ router.get('/admin/models/:id/edit', requireAdmin, (req: Request, res: Response)
const model = q<Model>(`SELECT * FROM models WHERE id = ?`).get(req.params.id) const model = q<Model>(`SELECT * FROM models WHERE id = ?`).get(req.params.id)
if (!model) { res.redirect('/admin'); return } if (!model) { res.redirect('/admin'); return }
const categories = q<Category>(`SELECT * FROM categories ORDER BY sort_order, name`).all() const categories = q<Category>(`SELECT * FROM categories ORDER BY sort_order, name`).all()
const pdfs = q<ModelPdf>(`SELECT * FROM model_pdfs WHERE model_id = ? ORDER BY sort_order`).all(model.id) const pdfs = q<ModelPdf>(`SELECT * FROM model_pdfs WHERE model_id = ? ORDER BY sort_order`).all(model.id)
const absModelPath = path.join(UPLOADS_DIR, model.file_path)
const hasGeometry = (model.file_type === 'step' || model.file_type === 'stp')
? fs.existsSync(geometryOutputPath(absModelPath))
: true
res.render('admin/edit', { res.render('admin/edit', {
model, categories, pdfs, error: null, model, categories, pdfs, error: null, hasGeometry,
baseUrl: process.env.BASE_URL ?? `http://localhost:${process.env.PORT ?? 3000}`, baseUrl: process.env.BASE_URL ?? `http://localhost:${process.env.PORT ?? 3000}`,
}) })
}) })
@@ -277,6 +282,44 @@ router.post('/admin/pdfs/:id/delete', requireAdmin, (req: Request, res: Response
res.redirect(`/admin/models/${pdf.model_id}/edit`) res.redirect(`/admin/models/${pdf.model_id}/edit`)
}) })
// ---- Reconvert STEP/STP geometry -----------------------------------------
router.post('/admin/models/:id/reconvert', requireAdmin, async (req: Request, res: Response) => {
const model = q<Model>(`SELECT * FROM models WHERE id = ?`).get(req.params.id)
if (!model) { res.status(404).json({ error: 'Model not found' }); return }
if (model.file_type !== 'step' && model.file_type !== 'stp') {
res.status(400).json({ error: 'Only STEP/STP models require geometry conversion' }); return
}
const absModelPath = path.join(UPLOADS_DIR, model.file_path)
if (!fs.existsSync(absModelPath)) {
res.status(404).json({ error: 'Source model file not found on disk' }); return
}
const geoOutPath = geometryOutputPath(absModelPath)
try {
await convertStepFile(absModelPath, geoOutPath)
} catch (err) {
res.status(500).json({ error: `Conversion failed: ${(err as Error).message}` }); return
}
// Regenerate thumbnail from new geometry (non-fatal)
try {
const geo = JSON.parse(fs.readFileSync(geoOutPath, 'utf8')) as GeometryFile
const tris = geometryToTriangles(geo)
const thumbPath = thumbnailOutputPath(UPLOADS_DIR, model.id)
await renderThumbnail(tris, thumbPath)
const thumbRel = path.relative(UPLOADS_DIR, thumbPath).replace(/\\/g, '/')
db.prepare(`UPDATE models SET thumbnail_path = ? WHERE id = ?`).run(thumbRel, model.id)
} catch (thumbErr) {
console.error('[thumbnail] reconvert thumbnail failed:', (thumbErr as Error).message)
}
res.json({ ok: true })
})
// ---- JSON list ----------------------------------------------------------- // ---- JSON list -----------------------------------------------------------
router.get('/api/admin/models', requireAdmin, (_req: Request, res: Response) => { router.get('/api/admin/models', requireAdmin, (_req: Request, res: Response) => {
+5 -1
View File
@@ -22,7 +22,11 @@ let _init: Promise<Awaited<ReturnType<typeof occtimport>>> | null = null
async function getOcct() { async function getOcct() {
if (_occt) return _occt if (_occt) return _occt
if (!_init) _init = occtimport().then(m => { _occt = m; return m }) if (!_init) {
_init = occtimport().then(m => { _occt = m; return m })
// Reset on failure so the next upload attempt retries rather than replaying a cached rejection
_init.catch(() => { _init = null })
}
return _init return _init
} }
+54
View File
@@ -61,6 +61,31 @@
</div> </div>
</form> </form>
<!-- Geometry status (STEP/STP only) -->
<% if (model.file_type === 'step' || model.file_type === 'stp') { %>
<div class="bg-surface-900 border border-gray-800 rounded-2xl p-6 mb-6">
<h2 class="text-sm font-semibold text-white mb-3">3D Geometry Processing</h2>
<% if (hasGeometry) { %>
<div class="flex items-center gap-2 text-sm text-green-400">
<svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
Geometry processed successfully
</div>
<% } else { %>
<div class="flex items-center justify-between gap-4">
<div class="flex items-center gap-2 text-sm text-amber-400">
<svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v4m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/></svg>
Geometry processing failed — model cannot be viewed
</div>
<button type="button" id="reconvert-btn"
class="shrink-0 bg-surface-700 hover:bg-surface-600 border border-gray-700 text-sm text-gray-300 rounded-lg px-4 py-2 transition-colors">
Retry Processing
</button>
</div>
<pre id="reconvert-error" class="mt-3 text-xs text-red-400 whitespace-pre-wrap break-all hidden"></pre>
<% } %>
</div>
<% } %>
<!-- Attached PDFs --> <!-- Attached PDFs -->
<div class="bg-surface-900 border border-gray-800 rounded-2xl p-6 mb-6"> <div class="bg-surface-900 border border-gray-800 rounded-2xl p-6 mb-6">
<h2 class="text-sm font-semibold text-white mb-4">Shop Diagrams / PDFs</h2> <h2 class="text-sm font-semibold text-white mb-4">Shop Diagrams / PDFs</h2>
@@ -115,5 +140,34 @@
</div> </div>
<script src="/admin.js"></script> <script src="/admin.js"></script>
<script>
(function () {
const btn = document.getElementById('reconvert-btn')
if (!btn) return
const errEl = document.getElementById('reconvert-error')
btn.addEventListener('click', async () => {
btn.disabled = true
btn.textContent = 'Processing…'
errEl.classList.add('hidden')
try {
const res = await fetch('/admin/models/<%= model.id %>/reconvert', { method: 'POST' })
const data = await res.json()
if (data.ok) {
window.location.reload()
} else {
errEl.textContent = data.error ?? 'Unknown error'
errEl.classList.remove('hidden')
btn.disabled = false
btn.textContent = 'Retry Processing'
}
} catch (err) {
errEl.textContent = String(err)
errEl.classList.remove('hidden')
btn.disabled = false
btn.textContent = 'Retry Processing'
}
})
})()
</script>
</body> </body>
</html> </html>