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:
2026-01-04 20:29:08 +01:00
parent 343676f085
commit 507af6d0f7
2 changed files with 218 additions and 13 deletions

166
tools/asset_backend.py Executable file
View 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)

View File

@@ -674,29 +674,68 @@
}
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();
setTimeout(() => {
hideLoading();
showToast('✅ Asset izbrisan!');
// Remove from array and re-render
allAssets = allAssets.filter(a => a.id !== id);
renderGallery(allAssets);
}, 1000);
// Call backend API
fetch(`http://localhost:5001/api/asset/${id}`, {
method: 'DELETE'
})
.then(response => response.json())
.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) {
const asset = allAssets.find(a => a.id === id);
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();
setTimeout(() => {
hideLoading();
showToast('🎨 Asset re-generiran!');
}, 2000);
// Call backend API
fetch(`http://localhost:5001/api/asset/${id}/reroll`, {
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