larxius commited on
Commit
2dc0cc0
·
verified ·
1 Parent(s): 3be0c8b

Update backend_structured/routes.py

Browse files
Files changed (1) hide show
  1. backend_structured/routes.py +142 -91
backend_structured/routes.py CHANGED
@@ -1,13 +1,53 @@
1
- from flask import Blueprint
 
 
 
2
  api_bp = Blueprint("api", __name__)
 
3
  import sys
4
  import os
5
- sys.path.insert(0, os.path.abspath('backend'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  from bs4 import BeautifulSoup
8
  from celery import Celery
9
  from celery.schedules import crontab
10
- from collections import defaultdict
11
  from scanners.base_scanner import (
12
  active_scan_logs, add_log, get_scan_logs, parse_domain,
13
  cleanup_scan_logs, schedule_log_cleanup, emit_scan_progress
@@ -16,80 +56,45 @@ from scanners import get_pipeline, get_phases, build_scanner, apply_scan_options
16
  from utils.fuzzer_engine import ContextAwareFuzzer
17
  from cryptography import x509
18
  from cryptography.hazmat.backends import default_backend
19
- from datetime import datetime, timezone
20
- from datetime import datetime, timezone, timedelta
21
- from datetime import datetime, timezone, timezone
22
- from dotenv import load_dotenv
23
- load_dotenv()
24
 
25
  import stripe
26
- from flask import Blueprint, request, jsonify, current_app, send_from_directory
27
  from werkzeug.utils import secure_filename
28
- from flask import Blueprint, send_file, jsonify, request
29
- from flask import Flask
30
- from flask import jsonify
31
- from flask import render_template
32
- from flask import request, abort, g, Response, make_response
33
  from flask_cors import CORS
34
  from flask_limiter import Limiter
35
  from flask_limiter.util import get_remote_address
36
  from flask_socketio import SocketIO, emit, join_room, leave_room
37
  from flask_sqlalchemy import SQLAlchemy
38
- from functools import wraps
39
- from markupsafe import escape # always available with Flask
40
  from reportlab.lib import colors
41
  from reportlab.lib.pagesizes import letter
42
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
43
  from reportlab.pdfgen import canvas
44
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Image, Flowable
 
 
45
  from reportlab.graphics.shapes import Drawing
46
  from reportlab.graphics.charts.barcharts import VerticalBarChart
47
- from sqlalchemy import event
48
- from sqlalchemy import func
49
- from sqlalchemy import inspect, text
50
- from sqlalchemy import text
51
  from sqlalchemy.engine import Engine
52
- from typing import Any
53
- from typing import Any, Callable
54
- from typing import Callable
55
- from typing import Literal
56
- from urllib.parse import urljoin, urlparse
57
- from urllib.parse import urlparse
58
- import base64
59
- import bcrypt
60
- import concurrent.futures
61
- from backend.utils.email_service import (
62
- send_welcome_email,
63
- send_scan_started,
64
- send_scan_completed,
65
- send_scan_failed,
66
- send_critical_alert
67
- )
68
 
69
- import hashlib
70
- import html
71
- import io
72
- import itertools
73
- import json
74
- import jwt
75
- import math
76
- import os
77
- import re
78
- import re, time, ipaddress, os, hashlib, threading
79
- import requests
80
- import socket
81
- import sqlite3
82
- import ssl
83
- import statistics
84
- import threading
85
- import time
86
- import traceback
87
- import urllib.error
88
- import urllib.parse
89
- import urllib.request
90
- import urllib3
91
- import uuid
92
- import ipaddress
93
 
94
 
95
 
@@ -814,39 +819,85 @@ def upload_organization_logo(current_user):
814
  if not org:
815
  return jsonify({'message': 'Organization not found'}), 404
816
 
817
- if 'logo' not in request.files:
818
- return jsonify({'message': 'No file part'}), 400
819
-
820
- file = request.files['logo']
821
- if file.filename == '':
822
- return jsonify({'message': 'No selected file'}), 400
823
-
824
- if file:
825
- from utils.firebase_storage import is_available, upload_bytes
826
-
827
- filename = secure_filename(f"org_{org.id}_{file.filename}")
828
  file_bytes = file.read()
829
  content_type = file.content_type or 'image/png'
 
 
 
 
 
 
830
 
831
- if is_available():
832
- # Upload to Firebase Cloud Storage
833
- blob_path = f"logos/{org.id}/{filename}"
834
- logo_url = upload_bytes(file_bytes, content_type, blob_path)
835
- if not logo_url:
836
- return jsonify({'message': 'Firebase upload failed'}), 500
837
- else:
838
- # Fallback: save to local disk
839
- upload_folder = current_app.config['UPLOAD_FOLDER']
840
- os.makedirs(upload_folder, exist_ok=True)
841
- file_path = os.path.join(upload_folder, filename)
842
- with open(file_path, 'wb') as f:
843
- f.write(file_bytes)
844
- logo_url = f"/uploads/logos/{filename}"
845
-
846
- org.report_logo_url = logo_url
847
- db.session.commit()
848
-
849
- return jsonify({'message': 'Logo uploaded successfully', 'report_logo_url': logo_url}), 200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
850
 
851
  @auth_bp.route('/organizations/webhook', methods=['GET', 'PUT'])
852
  @token_required
 
1
+ from flask import (
2
+ Flask, Blueprint, request, jsonify, current_app, send_from_directory,
3
+ send_file, render_template, abort, g, Response, make_response
4
+ )
5
  api_bp = Blueprint("api", __name__)
6
+
7
  import sys
8
  import os
9
+ import re
10
+ import time
11
+ import json
12
+ import math
13
+ import uuid
14
+ import html
15
+ import io
16
+ import hashlib
17
+ import sqlite3
18
+ import socket
19
+ import ssl
20
+ import base64
21
+ import bcrypt
22
+ import jwt
23
+ import requests
24
+ import urllib3
25
+ import ipaddress
26
+ import queue
27
+ import threading
28
+ import statistics
29
+ import itertools
30
+ import traceback
31
+ import concurrent.futures
32
+ from datetime import datetime, timezone, timedelta
33
+ from typing import Any, Callable, Literal
34
+ from collections import defaultdict
35
+ from functools import wraps
36
+ from urllib.parse import urljoin, urlparse
37
+ from dotenv import load_dotenv
38
+
39
+ load_dotenv()
40
+
41
+ BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
42
+ BACKEND_DIR = os.path.join(BASE_DIR, 'backend')
43
+ if BASE_DIR not in sys.path:
44
+ sys.path.insert(0, BASE_DIR)
45
+ if BACKEND_DIR not in sys.path:
46
+ sys.path.insert(0, BACKEND_DIR)
47
 
48
  from bs4 import BeautifulSoup
49
  from celery import Celery
50
  from celery.schedules import crontab
 
51
  from scanners.base_scanner import (
52
  active_scan_logs, add_log, get_scan_logs, parse_domain,
53
  cleanup_scan_logs, schedule_log_cleanup, emit_scan_progress
 
56
  from utils.fuzzer_engine import ContextAwareFuzzer
57
  from cryptography import x509
58
  from cryptography.hazmat.backends import default_backend
 
 
 
 
 
59
 
60
  import stripe
 
61
  from werkzeug.utils import secure_filename
 
 
 
 
 
62
  from flask_cors import CORS
63
  from flask_limiter import Limiter
64
  from flask_limiter.util import get_remote_address
65
  from flask_socketio import SocketIO, emit, join_room, leave_room
66
  from flask_sqlalchemy import SQLAlchemy
67
+ from markupsafe import escape
68
+
69
  from reportlab.lib import colors
70
  from reportlab.lib.pagesizes import letter
71
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
72
  from reportlab.pdfgen import canvas
73
+ from reportlab.platypus import (
74
+ SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Image, Flowable
75
+ )
76
  from reportlab.graphics.shapes import Drawing
77
  from reportlab.graphics.charts.barcharts import VerticalBarChart
78
+
79
+ from sqlalchemy import event, func, inspect, text
 
 
80
  from sqlalchemy.engine import Engine
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ try:
83
+ from backend.utils.email_service import (
84
+ send_welcome_email,
85
+ send_scan_started,
86
+ send_scan_completed,
87
+ send_scan_failed,
88
+ send_critical_alert
89
+ )
90
+ except ImportError:
91
+ from utils.email_service import (
92
+ send_welcome_email,
93
+ send_scan_started,
94
+ send_scan_completed,
95
+ send_scan_failed,
96
+ send_critical_alert
97
+ )
 
 
 
 
 
 
 
 
98
 
99
 
100
 
 
819
  if not org:
820
  return jsonify({'message': 'Organization not found'}), 404
821
 
822
+ file_bytes = None
823
+ content_type = 'image/png'
824
+ ext = 'png'
825
+
826
+ # 1. Direct File Upload (Multipart Form Data)
827
+ if 'logo' in request.files and request.files['logo'].filename != '':
828
+ file = request.files['logo']
829
+ filename_raw = secure_filename(file.filename) or "logo.png"
 
 
 
830
  file_bytes = file.read()
831
  content_type = file.content_type or 'image/png'
832
+ if '.' in filename_raw:
833
+ ext = filename_raw.rsplit('.', 1)[1].lower()
834
+ else:
835
+ # 2. Web Image URL Download Fallback (JSON or Form Data)
836
+ data = request.get_json(silent=True) or request.form or {}
837
+ logo_url_input = data.get('logo_url') or data.get('image_url')
838
 
839
+ if logo_url_input and (logo_url_input.startswith('http://') or logo_url_input.startswith('https://')):
840
+ try:
841
+ resp = requests.get(
842
+ logo_url_input,
843
+ timeout=10,
844
+ headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'}
845
+ )
846
+ if resp.status_code == 200 and resp.content:
847
+ file_bytes = resp.content
848
+ content_type = resp.headers.get('Content-Type', 'image/png').split(';')[0].strip()
849
+ if 'jpeg' in content_type: ext = 'jpg'
850
+ elif 'webp' in content_type: ext = 'webp'
851
+ elif 'svg' in content_type: ext = 'svg'
852
+ elif 'gif' in content_type: ext = 'gif'
853
+ else: ext = 'png'
854
+ except Exception as e:
855
+ current_app.logger.warn(f"Failed to fetch logo_url: {e}")
856
+
857
+ if not file_bytes:
858
+ return jsonify({'message': 'No valid logo file or URL provided'}), 400
859
+
860
+ from utils.firebase_storage import is_available, upload_bytes
861
+
862
+ filename = secure_filename(f"org_{org.id}_{int(time.time())}.{ext}")
863
+ logo_url = None
864
+
865
+ if is_available():
866
+ # Upload to Firebase Cloud Storage
867
+ blob_path = f"logos/{org.id}/{filename}"
868
+ logo_url = upload_bytes(file_bytes, content_type, blob_path)
869
+ if not logo_url:
870
+ current_app.logger.warning(f"[Firebase] Upload failed for {blob_path}. Falling back to local disk storage.")
871
+
872
+ if not logo_url:
873
+ # Fallback: save to local disk
874
+ upload_folder = current_app.config.get('UPLOAD_FOLDER', os.path.join(os.getcwd(), 'uploads', 'logos'))
875
+ os.makedirs(upload_folder, exist_ok=True)
876
+ file_path = os.path.join(upload_folder, filename)
877
+ with open(file_path, 'wb') as f:
878
+ f.write(file_bytes)
879
+ logo_url = f"/uploads/logos/{filename}"
880
+
881
+ org.report_logo_url = logo_url
882
+ db.session.commit()
883
+
884
+ return jsonify({'message': 'Logo uploaded successfully', 'report_logo_url': logo_url}), 200
885
+
886
+
887
+ @auth_bp.route('/uploads/logos/<filename>', methods=['GET'])
888
+ @auth_bp.route('/uploads/<path:filename>', methods=['GET'])
889
+ def serve_uploaded_logo(filename):
890
+ upload_folder = current_app.config.get('UPLOAD_FOLDER', os.path.join(os.getcwd(), 'uploads', 'logos'))
891
+ # Direct check in logos directory
892
+ if os.path.exists(os.path.join(upload_folder, filename)):
893
+ return send_from_directory(upload_folder, filename)
894
+
895
+ # Parent uploads directory check
896
+ base_uploads = os.path.abspath(os.path.join(upload_folder, '..'))
897
+ if os.path.exists(os.path.join(base_uploads, filename)):
898
+ return send_from_directory(base_uploads, filename)
899
+
900
+ return jsonify({'message': 'File not found'}), 404
901
 
902
  @auth_bp.route('/organizations/webhook', methods=['GET', 'PUT'])
903
  @token_required