|
|
""" |
|
|
Flask Web Application for InklyAI Signature Verification UI |
|
|
""" |
|
|
|
|
|
from flask import Flask, render_template, request, jsonify, send_from_directory |
|
|
import os |
|
|
import uuid |
|
|
from datetime import datetime |
|
|
import logging |
|
|
from werkzeug.utils import secure_filename |
|
|
|
|
|
from agentai_integration import AgentAISignatureManager, AgentAISignatureAPI |
|
|
from src.models.siamese_network import SignatureVerifier |
|
|
from src.data.preprocessing import SignaturePreprocessor |
|
|
|
|
|
|
|
|
app = Flask(__name__) |
|
|
app.config['SECRET_KEY'] = 'inklyai-secret-key-2024' |
|
|
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 |
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
signature_manager = AgentAISignatureManager( |
|
|
threshold=0.75, |
|
|
device='auto' |
|
|
) |
|
|
|
|
|
|
|
|
api = AgentAISignatureAPI(signature_manager) |
|
|
|
|
|
|
|
|
UPLOAD_FOLDER = 'uploads' |
|
|
REFERENCE_FOLDER = 'uploads/reference' |
|
|
VERIFICATION_FOLDER = 'uploads/verification' |
|
|
os.makedirs(UPLOAD_FOLDER, exist_ok=True) |
|
|
os.makedirs(REFERENCE_FOLDER, exist_ok=True) |
|
|
os.makedirs(VERIFICATION_FOLDER, exist_ok=True) |
|
|
|
|
|
|
|
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff'} |
|
|
|
|
|
def allowed_file(filename): |
|
|
"""Check if file extension is allowed.""" |
|
|
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS |
|
|
|
|
|
def save_uploaded_file(file, folder): |
|
|
"""Save uploaded file and return the path.""" |
|
|
if file and allowed_file(file.filename): |
|
|
filename = secure_filename(file.filename) |
|
|
|
|
|
name, ext = os.path.splitext(filename) |
|
|
filename = f"{name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}" |
|
|
filepath = os.path.join(folder, filename) |
|
|
file.save(filepath) |
|
|
return filepath |
|
|
return None |
|
|
|
|
|
@app.route('/') |
|
|
def index(): |
|
|
"""Main page with signature verification UI.""" |
|
|
return render_template('index.html') |
|
|
|
|
|
@app.route('/agents') |
|
|
def agents(): |
|
|
"""Agent management page.""" |
|
|
return render_template('agents.html') |
|
|
|
|
|
@app.route('/api/agents', methods=['GET']) |
|
|
def get_agents(): |
|
|
"""Get list of registered agents.""" |
|
|
try: |
|
|
agents = [] |
|
|
for agent_id, agent_signature in signature_manager.agent_signatures.items(): |
|
|
agents.append({ |
|
|
'agent_id': agent_id, |
|
|
'created_at': agent_signature.created_at.isoformat(), |
|
|
'last_verified': agent_signature.last_verified.isoformat() if agent_signature.last_verified else None, |
|
|
'verification_count': agent_signature.verification_count, |
|
|
'is_active': agent_signature.is_active |
|
|
}) |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'agents': agents, |
|
|
'total_agents': len(agents) |
|
|
}) |
|
|
except Exception as e: |
|
|
logger.error(f"Error getting agents: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/register-agent', methods=['POST']) |
|
|
def register_agent(): |
|
|
"""Register a new agent with signature template.""" |
|
|
try: |
|
|
if 'signature_template' not in request.files: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'No signature template file provided' |
|
|
}), 400 |
|
|
|
|
|
file = request.files['signature_template'] |
|
|
agent_id = request.form.get('agent_id') |
|
|
|
|
|
if not agent_id: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Agent ID is required' |
|
|
}), 400 |
|
|
|
|
|
|
|
|
filepath = save_uploaded_file(file, REFERENCE_FOLDER) |
|
|
if not filepath: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Invalid file type. Please upload an image file.' |
|
|
}), 400 |
|
|
|
|
|
|
|
|
success = signature_manager.register_agent_signature(agent_id, filepath) |
|
|
|
|
|
if success: |
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'agent_id': agent_id, |
|
|
'message': 'Agent registered successfully' |
|
|
}) |
|
|
else: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Failed to register agent' |
|
|
}), 400 |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error registering agent: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/verify', methods=['POST']) |
|
|
def verify_signatures(): |
|
|
"""Verify two signatures.""" |
|
|
try: |
|
|
if 'signature1' not in request.files or 'signature2' not in request.files: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Both signature files are required' |
|
|
}), 400 |
|
|
|
|
|
agent_id = request.form.get('agent_id') |
|
|
if not agent_id: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Agent ID is required' |
|
|
}), 400 |
|
|
|
|
|
|
|
|
file1 = request.files['signature1'] |
|
|
file2 = request.files['signature2'] |
|
|
|
|
|
file1_path = save_uploaded_file(file1, VERIFICATION_FOLDER) |
|
|
file2_path = save_uploaded_file(file2, VERIFICATION_FOLDER) |
|
|
|
|
|
if not file1_path or not file2_path: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Invalid file types. Please upload image files.' |
|
|
}), 400 |
|
|
|
|
|
|
|
|
similarity, is_genuine = signature_manager.verifier.verify_signatures( |
|
|
file1_path, file2_path, threshold=signature_manager.threshold |
|
|
) |
|
|
|
|
|
|
|
|
confidence = similarity |
|
|
|
|
|
|
|
|
verification_id = str(uuid.uuid4())[:12] |
|
|
result = { |
|
|
'success': True, |
|
|
'is_verified': is_genuine, |
|
|
'similarity_score': float(similarity), |
|
|
'confidence': float(confidence), |
|
|
'verification_id': verification_id, |
|
|
'timestamp': datetime.now().isoformat(), |
|
|
'agent_id': agent_id |
|
|
} |
|
|
|
|
|
|
|
|
logger.info(f"Verification completed: {result}") |
|
|
|
|
|
return jsonify(result) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error verifying signatures: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/verify-agent', methods=['POST']) |
|
|
def verify_agent_signature(): |
|
|
"""Verify signature against registered agent template.""" |
|
|
try: |
|
|
if 'signature_image' not in request.files: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Signature image file is required' |
|
|
}), 400 |
|
|
|
|
|
agent_id = request.form.get('agent_id') |
|
|
if not agent_id: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Agent ID is required' |
|
|
}), 400 |
|
|
|
|
|
|
|
|
if agent_id not in signature_manager.agent_signatures: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Agent not found' |
|
|
}), 404 |
|
|
|
|
|
|
|
|
file = request.files['signature_image'] |
|
|
file_path = save_uploaded_file(file, VERIFICATION_FOLDER) |
|
|
|
|
|
if not file_path: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Invalid file type. Please upload an image file.' |
|
|
}), 400 |
|
|
|
|
|
|
|
|
result = signature_manager.verify_agent_signature(agent_id, file_path) |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'is_verified': result.is_verified, |
|
|
'similarity_score': result.similarity_score, |
|
|
'confidence': result.confidence, |
|
|
'verification_id': result.verification_id, |
|
|
'timestamp': result.timestamp.isoformat(), |
|
|
'agent_id': agent_id |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error verifying agent signature: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/stats', methods=['GET']) |
|
|
def get_stats(): |
|
|
"""Get verification statistics.""" |
|
|
try: |
|
|
stats = {} |
|
|
for agent_id in signature_manager.agent_signatures.keys(): |
|
|
agent_stats = signature_manager.get_agent_verification_stats(agent_id) |
|
|
stats[agent_id] = agent_stats |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'stats': stats |
|
|
}) |
|
|
except Exception as e: |
|
|
logger.error(f"Error getting stats: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/agent-stats/<agent_id>', methods=['GET']) |
|
|
def get_agent_stats(agent_id): |
|
|
"""Get statistics for a specific agent.""" |
|
|
try: |
|
|
stats = signature_manager.get_agent_verification_stats(agent_id) |
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'agent_id': agent_id, |
|
|
'stats': stats |
|
|
}) |
|
|
except Exception as e: |
|
|
logger.error(f"Error getting agent stats: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/deactivate-agent/<agent_id>', methods=['POST']) |
|
|
def deactivate_agent(agent_id): |
|
|
"""Deactivate an agent.""" |
|
|
try: |
|
|
success = signature_manager.deactivate_agent(agent_id) |
|
|
return jsonify({ |
|
|
'success': success, |
|
|
'agent_id': agent_id, |
|
|
'action': 'deactivated' |
|
|
}) |
|
|
except Exception as e: |
|
|
logger.error(f"Error deactivating agent: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/reactivate-agent/<agent_id>', methods=['POST']) |
|
|
def reactivate_agent(agent_id): |
|
|
"""Reactivate an agent.""" |
|
|
try: |
|
|
success = signature_manager.reactivate_agent(agent_id) |
|
|
return jsonify({ |
|
|
'success': success, |
|
|
'agent_id': agent_id, |
|
|
'action': 'reactivated' |
|
|
}) |
|
|
except Exception as e: |
|
|
logger.error(f"Error reactivating agent: {e}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': str(e) |
|
|
}), 500 |
|
|
|
|
|
@app.route('/api/health', methods=['GET']) |
|
|
def health_check(): |
|
|
"""Health check endpoint.""" |
|
|
return jsonify({ |
|
|
'status': 'healthy', |
|
|
'timestamp': datetime.now().isoformat(), |
|
|
'service': 'InklyAI Web Application', |
|
|
'agents_registered': len(signature_manager.agent_signatures) |
|
|
}) |
|
|
|
|
|
@app.errorhandler(413) |
|
|
def too_large(e): |
|
|
"""Handle file too large error.""" |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'File too large. Maximum size is 16MB.' |
|
|
}), 413 |
|
|
|
|
|
@app.errorhandler(404) |
|
|
def not_found(e): |
|
|
"""Handle 404 errors.""" |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Endpoint not found' |
|
|
}), 404 |
|
|
|
|
|
@app.errorhandler(500) |
|
|
def internal_error(e): |
|
|
"""Handle 500 errors.""" |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'error': 'Internal server error' |
|
|
}), 500 |
|
|
|
|
|
def initialize_demo_agents(): |
|
|
"""Initialize demo agents if sample data exists.""" |
|
|
try: |
|
|
|
|
|
demo_agents = [ |
|
|
('Agent_01', 'data/samples/john_doe_1.png'), |
|
|
('Agent_02', 'data/samples/jane_smith_1.png'), |
|
|
('Agent_03', 'data/samples/bob_wilson_1.png'), |
|
|
('Agent_04', 'data/samples/alice_brown_1.png') |
|
|
] |
|
|
|
|
|
for agent_id, signature_template in demo_agents: |
|
|
if os.path.exists(signature_template): |
|
|
signature_manager.register_agent_signature(agent_id, signature_template) |
|
|
logger.info(f"Registered agent: {agent_id}") |
|
|
|
|
|
logger.info("Demo agents initialized successfully") |
|
|
|
|
|
except Exception as e: |
|
|
logger.warning(f"Could not initialize demo agents: {e}") |
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
|
initialize_demo_agents() |
|
|
|
|
|
|
|
|
port = int(os.environ.get('PORT', 8080)) |
|
|
debug = os.environ.get('DEBUG', 'False').lower() == 'true' |
|
|
|
|
|
logger.info(f"Starting InklyAI Web Application on port {port}") |
|
|
logger.info(f"Access the application at: http://localhost:{port}") |
|
|
|
|
|
app.run(host='0.0.0.0', port=port, debug=debug) |
|
|
|