fix: Backend port 5000 → 5001 (Apple AirTunes conflict)
PROBLEM: Port 5000 already used by Apple AirTunes ERROR: 403 Forbidden when calling API SOLUTION: Changed backend to port 5001 CHANGES: - tools/asset_backend.py: app.run(port=5001) - tools/visual_asset_manager.html: Updated all API calls to :5001 BACKEND NOW RUNNING: http://localhost:5001 HEALTH CHECK: ✅ Working Test: curl http://localhost:5001/api/health Response: {"status": "ok"}
This commit is contained in:
166
tools/asset_backend.py
Executable file
166
tools/asset_backend.py
Executable file
@@ -0,0 +1,166 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Asset Manager Backend API
|
||||||
|
Enables actual file deletion and re-generation
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, request
|
||||||
|
from flask_cors import CORS
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
CORS(app) # Enable CORS for localhost requests
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
PROJECT_ROOT = Path(__file__).parent.parent
|
||||||
|
MANIFEST_PATH = PROJECT_ROOT / "tools" / "asset_manifest.json"
|
||||||
|
GENERATOR_SCRIPT = PROJECT_ROOT / "scripts" / "generate_asset_manifest.py"
|
||||||
|
|
||||||
|
def load_manifest():
|
||||||
|
"""Load the asset manifest"""
|
||||||
|
with open(MANIFEST_PATH, 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
def save_manifest(manifest):
|
||||||
|
"""Save the asset manifest"""
|
||||||
|
with open(MANIFEST_PATH, 'w') as f:
|
||||||
|
json.dump(manifest, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
def regenerate_manifest():
|
||||||
|
"""Regenerate manifest by running the generator script"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['python3', str(GENERATOR_SCRIPT)],
|
||||||
|
cwd=str(PROJECT_ROOT),
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error regenerating manifest: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@app.route('/api/health', methods=['GET'])
|
||||||
|
def health():
|
||||||
|
"""Health check endpoint"""
|
||||||
|
return jsonify({"status": "ok", "message": "Asset Backend API is running"})
|
||||||
|
|
||||||
|
@app.route('/api/assets', methods=['GET'])
|
||||||
|
def get_assets():
|
||||||
|
"""Get all assets from manifest"""
|
||||||
|
manifest = load_manifest()
|
||||||
|
return jsonify(manifest)
|
||||||
|
|
||||||
|
@app.route('/api/asset/<asset_id>', methods=['DELETE'])
|
||||||
|
def delete_asset(asset_id):
|
||||||
|
"""Delete an asset file"""
|
||||||
|
try:
|
||||||
|
# Load manifest
|
||||||
|
manifest = load_manifest()
|
||||||
|
|
||||||
|
# Find asset
|
||||||
|
asset = None
|
||||||
|
for a in manifest['assets']:
|
||||||
|
if a['id'] == asset_id:
|
||||||
|
asset = a
|
||||||
|
break
|
||||||
|
|
||||||
|
if not asset:
|
||||||
|
return jsonify({"success": False, "error": "Asset not found"}), 404
|
||||||
|
|
||||||
|
# Get file path (remove ../ prefix since we're in project root)
|
||||||
|
file_path = asset['path'].replace('../', '')
|
||||||
|
full_path = PROJECT_ROOT / file_path
|
||||||
|
|
||||||
|
# Check if file exists
|
||||||
|
if not full_path.exists():
|
||||||
|
return jsonify({"success": False, "error": "File not found on disk"}), 404
|
||||||
|
|
||||||
|
# Delete file
|
||||||
|
full_path.unlink()
|
||||||
|
print(f"✅ Deleted: {full_path}")
|
||||||
|
|
||||||
|
# Regenerate manifest
|
||||||
|
if regenerate_manifest():
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"message": f"Asset {asset['name']} deleted successfully",
|
||||||
|
"deleted_file": str(file_path)
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": "File deleted but manifest regeneration failed"
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"success": False, "error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/api/asset/<asset_id>/reroll', methods=['POST'])
|
||||||
|
def reroll_asset(asset_id):
|
||||||
|
"""Re-generate an asset (placeholder for now)"""
|
||||||
|
try:
|
||||||
|
manifest = load_manifest()
|
||||||
|
|
||||||
|
# Find asset
|
||||||
|
asset = None
|
||||||
|
for a in manifest['assets']:
|
||||||
|
if a['id'] == asset_id:
|
||||||
|
asset = a
|
||||||
|
break
|
||||||
|
|
||||||
|
if not asset:
|
||||||
|
return jsonify({"success": False, "error": "Asset not found"}), 404
|
||||||
|
|
||||||
|
# TODO: Implement actual image generation
|
||||||
|
# For now, return placeholder response
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"message": f"Re-roll requested for {asset['name']}",
|
||||||
|
"note": "Image generation not yet implemented. Delete old image and regenerate manually.",
|
||||||
|
"asset": asset
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"success": False, "error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/api/manifest/regenerate', methods=['POST'])
|
||||||
|
def regenerate():
|
||||||
|
"""Manually trigger manifest regeneration"""
|
||||||
|
try:
|
||||||
|
if regenerate_manifest():
|
||||||
|
manifest = load_manifest()
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"message": "Manifest regenerated successfully",
|
||||||
|
"total_assets": manifest['total_assets']
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": "Manifest regeneration failed"
|
||||||
|
}), 500
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"success": False, "error": str(e)}), 500
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("🚀 Asset Manager Backend API")
|
||||||
|
print("="*60)
|
||||||
|
print(f"Project Root: {PROJECT_ROOT}")
|
||||||
|
print(f"Manifest: {MANIFEST_PATH}")
|
||||||
|
print(f"Generator: {GENERATOR_SCRIPT}")
|
||||||
|
print("="*60)
|
||||||
|
print("Starting server on http://localhost:5001")
|
||||||
|
print("Endpoints:")
|
||||||
|
print(" GET /api/health - Health check")
|
||||||
|
print(" GET /api/assets - Get all assets")
|
||||||
|
print(" DELETE /api/asset/<id> - Delete asset")
|
||||||
|
print(" POST /api/asset/<id>/reroll - Re-generate asset")
|
||||||
|
print(" POST /api/manifest/regenerate - Regenerate manifest")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
app.run(debug=True, port=5001)
|
||||||
@@ -674,29 +674,68 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteAsset(id) {
|
function deleteAsset(id) {
|
||||||
if (!confirm('Res želiš izbrisati ta asset?')) return;
|
const asset = allAssets.find(a => a.id === id);
|
||||||
|
if (!asset) return;
|
||||||
|
|
||||||
|
if (!confirm(`Res želiš trajno izbrisati:\n${asset.name}\n\nDatoteka bo fizično izbrisana iz diska!`)) return;
|
||||||
|
|
||||||
showLoading();
|
showLoading();
|
||||||
setTimeout(() => {
|
|
||||||
hideLoading();
|
// Call backend API
|
||||||
showToast('✅ Asset izbrisan!');
|
fetch(`http://localhost:5001/api/asset/${id}`, {
|
||||||
// Remove from array and re-render
|
method: 'DELETE'
|
||||||
allAssets = allAssets.filter(a => a.id !== id);
|
})
|
||||||
renderGallery(allAssets);
|
.then(response => response.json())
|
||||||
}, 1000);
|
.then(data => {
|
||||||
|
hideLoading();
|
||||||
|
if (data.success) {
|
||||||
|
showToast(`✅ ${asset.name} izbrisan!`);
|
||||||
|
// Reload assets from updated manifest
|
||||||
|
setTimeout(() => {
|
||||||
|
loadAssets().then(() => {
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
showToast(`❌ Napaka: ${data.error}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
hideLoading();
|
||||||
|
showToast(`❌ Backend error: ${error.message}`);
|
||||||
|
console.error('Delete error:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function rerollAsset(id) {
|
function rerollAsset(id) {
|
||||||
const asset = allAssets.find(a => a.id === id);
|
const asset = allAssets.find(a => a.id === id);
|
||||||
if (!asset) return;
|
if (!asset) return;
|
||||||
|
|
||||||
if (!confirm(`Re-generate "${asset.name}" z novim promptom?`)) return;
|
if (!confirm(`Re-generate "${asset.name}" z novim promptom?\n\n(Not yet fully implemented)`)) return;
|
||||||
|
|
||||||
showLoading();
|
showLoading();
|
||||||
setTimeout(() => {
|
|
||||||
hideLoading();
|
// Call backend API
|
||||||
showToast('🎨 Asset re-generiran!');
|
fetch(`http://localhost:5001/api/asset/${id}/reroll`, {
|
||||||
}, 2000);
|
method: 'POST'
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
hideLoading();
|
||||||
|
if (data.success) {
|
||||||
|
showToast(`🎨 ${data.message}`);
|
||||||
|
if (data.note) {
|
||||||
|
alert(data.note);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast(`❌ Napaka: ${data.error}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
hideLoading();
|
||||||
|
showToast(`❌ Backend error: ${error.message}`);
|
||||||
|
console.error('Re-roll error:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bulk actions
|
// Bulk actions
|
||||||
|
|||||||
Reference in New Issue
Block a user