LOT 1 - Optimizare script build -Instalare mono comanda

This commit is contained in:
Dezvoltari Evotech 2026-06-27 06:42:02 -07:00
parent 5380c3fc63
commit 42ff22bf85
127 changed files with 16163 additions and 532 deletions

View file

@ -0,0 +1,26 @@
"""
SQLAlchemy Models Package
"""
from app.models.domain import Domain
from app.models.whois import WhoisRecord
from app.models.dns import DnsRecord
from app.models.ssl import SslCertificate
from app.models.reputation import ReputationScore
from app.models.risk import RiskAssessment
from app.models.check_history import CheckHistory
from app.models.batch import BatchOperation
from app.models.blacklist import Blacklist
from app.models.api_usage import ApiUsageLog
__all__ = [
'Domain',
'WhoisRecord',
'DnsRecord',
'SslCertificate',
'ReputationScore',
'RiskAssessment',
'CheckHistory',
'BatchOperation',
'Blacklist',
'ApiUsageLog'
]

View file

@ -0,0 +1,32 @@
"""API Usage Log Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, JSONB
from app import db
class ApiUsageLog(db.Model):
"""API usage logging model"""
__tablename__ = 'api_usage_logs'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
service_name = db.Column(db.String(50), nullable=False, index=True)
endpoint = db.Column(db.String(255))
request_count = db.Column(db.Integer, default=1)
response_time_ms = db.Column(db.Integer)
status_code = db.Column(db.Integer, index=True)
quota_used = db.Column(db.Integer)
quota_remaining = db.Column(db.Integer)
error_message = db.Column(db.Text)
request_params = db.Column(JSONB)
logged_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
def to_dict(self):
return {
'id': str(self.id),
'service_name': self.service_name,
'endpoint': self.endpoint,
'status_code': self.status_code,
'response_time_ms': self.response_time_ms,
'logged_at': self.logged_at.isoformat() if self.logged_at else None
}

View file

@ -0,0 +1,39 @@
"""Batch Operation Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, ARRAY, JSONB
from app import db
class BatchOperation(db.Model):
"""Batch operation model"""
__tablename__ = 'batch_operations'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
batch_id = db.Column(UUID(as_uuid=True), unique=True, nullable=False, index=True)
total_domains = db.Column(db.Integer, nullable=False)
completed_count = db.Column(db.Integer, default=0)
failed_count = db.Column(db.Integer, default=0)
status = db.Column(db.String(20), default='pending', index=True)
priority = db.Column(db.String(20), default='normal', index=True)
started_at = db.Column(db.DateTime(timezone=True))
completed_at = db.Column(db.DateTime(timezone=True))
estimated_completion_at = db.Column(db.DateTime(timezone=True))
requested_by = db.Column(db.String(100))
check_options = db.Column(JSONB)
domain_list = db.Column(ARRAY(db.Text))
error_log = db.Column(JSONB)
summary = db.Column(JSONB)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
def to_dict(self):
return {
'batch_id': str(self.batch_id),
'total_domains': self.total_domains,
'completed_count': self.completed_count,
'failed_count': self.failed_count,
'status': self.status,
'progress_percentage': round((self.completed_count / self.total_domains * 100) if self.total_domains > 0 else 0, 2),
'started_at': self.started_at.isoformat() if self.started_at else None,
'completed_at': self.completed_at.isoformat() if self.completed_at else None
}

View file

@ -0,0 +1,33 @@
"""Blacklist Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, JSONB
from app import db
class Blacklist(db.Model):
"""Blacklist model"""
__tablename__ = 'blacklists'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain = db.Column(db.String(255), unique=True, nullable=False, index=True)
reason = db.Column(db.Text)
category = db.Column(db.String(50), index=True)
source = db.Column(db.String(100))
severity = db.Column(db.String(20), default='medium', index=True)
added_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
updated_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
is_active = db.Column(db.Boolean, default=True, index=True)
expires_at = db.Column(db.DateTime(timezone=True))
extra_data = db.Column(JSONB)
def to_dict(self):
return {
'id': str(self.id),
'domain': self.domain,
'reason': self.reason,
'category': self.category,
'severity': self.severity,
'is_active': self.is_active,
'added_at': self.added_at.isoformat() if self.added_at else None
}

View file

@ -0,0 +1,38 @@
"""Check History Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, JSONB
from app import db
class CheckHistory(db.Model):
"""Check history model"""
__tablename__ = 'check_history'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
check_id = db.Column(UUID(as_uuid=True), unique=True, nullable=False, index=True)
domain_id = db.Column(UUID(as_uuid=True), db.ForeignKey('domains.id', ondelete='CASCADE'), nullable=False, index=True)
risk_assessment_id = db.Column(UUID(as_uuid=True), db.ForeignKey('risk_assessments.id', ondelete='SET NULL'))
requested_by = db.Column(db.String(100), default='api', index=True)
request_ip = db.Column(db.String(45))
user_agent = db.Column(db.Text)
check_options = db.Column(JSONB)
processing_time_ms = db.Column(db.Integer)
cache_hit = db.Column(db.Boolean, default=False)
changes_detected = db.Column(db.Boolean, default=False)
change_summary = db.Column(JSONB)
status = db.Column(db.String(20), default='completed', index=True)
error_message = db.Column(db.Text)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
domain = db.relationship('Domain', back_populates='check_history')
def to_dict(self):
return {
'id': str(self.id),
'check_id': str(self.check_id),
'status': self.status,
'cache_hit': self.cache_hit,
'processing_time_ms': self.processing_time_ms,
'created_at': self.created_at.isoformat() if self.created_at else None
}

View file

@ -0,0 +1,31 @@
"""DNS Record Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID
from app import db
class DnsRecord(db.Model):
"""DNS record model"""
__tablename__ = 'dns_records'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_id = db.Column(UUID(as_uuid=True), db.ForeignKey('domains.id', ondelete='CASCADE'), nullable=False, index=True)
record_type = db.Column(db.String(10), nullable=False, index=True)
record_value = db.Column(db.Text, nullable=False)
ttl = db.Column(db.Integer)
priority = db.Column(db.Integer)
fetched_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
domain = db.relationship('Domain', back_populates='dns_records')
def to_dict(self):
return {
'id': str(self.id),
'record_type': self.record_type,
'record_value': self.record_value,
'ttl': self.ttl,
'priority': self.priority,
'fetched_at': self.fetched_at.isoformat() if self.fetched_at else None
}

View file

@ -0,0 +1,118 @@
"""
Domain Model
"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, ARRAY
from app import db
class Domain(db.Model):
"""Domain model - stores basic domain information"""
__tablename__ = 'domains'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain = db.Column(db.String(255), nullable=False, index=True)
subdomain = db.Column(db.String(255), nullable=True)
tld = db.Column(db.String(50), nullable=False, index=True)
full_domain = db.Column(db.String(255), unique=True, nullable=False, index=True)
first_seen_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
last_checked_at = db.Column(db.DateTime(timezone=True), nullable=True, index=True)
check_count = db.Column(db.Integer, default=0)
is_active = db.Column(db.Boolean, default=True, index=True)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
updated_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
whois_records = db.relationship('WhoisRecord', back_populates='domain', lazy='dynamic', cascade='all, delete-orphan')
dns_records = db.relationship('DnsRecord', back_populates='domain', lazy='dynamic', cascade='all, delete-orphan')
ssl_certificates = db.relationship('SslCertificate', back_populates='domain', lazy='dynamic', cascade='all, delete-orphan')
reputation_scores = db.relationship('ReputationScore', back_populates='domain', lazy='dynamic', cascade='all, delete-orphan')
risk_assessments = db.relationship('RiskAssessment', back_populates='domain', lazy='dynamic', cascade='all, delete-orphan')
check_history = db.relationship('CheckHistory', back_populates='domain', lazy='dynamic', cascade='all, delete-orphan')
def __repr__(self):
return f'<Domain {self.full_domain}>'
def to_dict(self, include_relationships=False):
"""Convert model to dictionary"""
data = {
'id': str(self.id),
'domain': self.domain,
'subdomain': self.subdomain,
'tld': self.tld,
'full_domain': self.full_domain,
'first_seen_at': self.first_seen_at.isoformat() if self.first_seen_at else None,
'last_checked_at': self.last_checked_at.isoformat() if self.last_checked_at else None,
'check_count': self.check_count,
'is_active': self.is_active,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
if include_relationships:
# Get latest records
latest_whois = self.whois_records.order_by(WhoisRecord.fetched_at.desc()).first()
latest_risk = self.risk_assessments.order_by(RiskAssessment.assessed_at.desc()).first()
data['latest_whois'] = latest_whois.to_dict() if latest_whois else None
data['latest_risk'] = latest_risk.to_dict() if latest_risk else None
return data
@staticmethod
def parse_domain(full_domain: str) -> dict:
"""
Parse a full domain into components
Args:
full_domain: Full domain name (e.g., 'www.example.com')
Returns:
dict with 'domain', 'subdomain', 'tld', 'full_domain'
"""
import tldextract
extracted = tldextract.extract(full_domain)
return {
'domain': extracted.domain,
'subdomain': extracted.subdomain if extracted.subdomain else None,
'tld': extracted.suffix,
'full_domain': full_domain.lower().strip()
}
@classmethod
def get_or_create(cls, full_domain: str):
"""
Get existing domain or create new one
Args:
full_domain: Full domain name
Returns:
Tuple of (Domain instance, created boolean)
"""
domain = cls.query.filter_by(full_domain=full_domain.lower()).first()
if domain:
return domain, False
# Parse domain components
parsed = cls.parse_domain(full_domain)
# Create new domain
domain = cls(
domain=parsed['domain'],
subdomain=parsed['subdomain'],
tld=parsed['tld'],
full_domain=parsed['full_domain']
)
db.session.add(domain)
db.session.commit()
return domain, True

View file

@ -0,0 +1,42 @@
"""Reputation Score Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, ARRAY, JSONB
from app import db
class ReputationScore(db.Model):
"""Reputation score from various sources"""
__tablename__ = 'reputation_scores'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_id = db.Column(UUID(as_uuid=True), db.ForeignKey('domains.id', ondelete='CASCADE'), nullable=False, index=True)
source = db.Column(db.String(50), nullable=False, index=True)
score = db.Column(db.Integer)
malicious_count = db.Column(db.Integer, default=0)
suspicious_count = db.Column(db.Integer, default=0)
harmless_count = db.Column(db.Integer, default=0)
undetected_count = db.Column(db.Integer, default=0)
is_blacklisted = db.Column(db.Boolean, default=False, index=True)
blacklist_names = db.Column(ARRAY(db.Text))
is_typosquatting = db.Column(db.Boolean, default=False, index=True)
typosquatting_target = db.Column(db.String(255))
raw_response = db.Column(JSONB)
checked_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
domain = db.relationship('Domain', back_populates='reputation_scores')
def to_dict(self):
return {
'id': str(self.id),
'source': self.source,
'score': self.score,
'malicious_count': self.malicious_count,
'suspicious_count': self.suspicious_count,
'harmless_count': self.harmless_count,
'is_blacklisted': self.is_blacklisted,
'blacklist_names': self.blacklist_names,
'is_typosquatting': self.is_typosquatting,
'checked_at': self.checked_at.isoformat() if self.checked_at else None
}

View file

@ -0,0 +1,43 @@
"""Risk Assessment Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, JSONB
from app import db
class RiskAssessment(db.Model):
"""Risk assessment model"""
__tablename__ = 'risk_assessments'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_id = db.Column(UUID(as_uuid=True), db.ForeignKey('domains.id', ondelete='CASCADE'), nullable=False, index=True)
check_id = db.Column(UUID(as_uuid=True), unique=True, nullable=False, index=True)
total_score = db.Column(db.Integer, nullable=False, index=True)
risk_level = db.Column(db.String(20), nullable=False, index=True)
domain_age_score = db.Column(db.Integer, default=0)
domain_age_days = db.Column(db.Integer)
ssl_score = db.Column(db.Integer, default=0)
dns_score = db.Column(db.Integer, default=0)
reputation_score = db.Column(db.Integer, default=0)
whois_score = db.Column(db.Integer, default=0)
factors = db.Column(JSONB)
is_new_domain = db.Column(db.Boolean, default=False, index=True)
is_suspicious = db.Column(db.Boolean, default=False, index=True)
requires_manual_review = db.Column(db.Boolean, default=False)
assessed_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
domain = db.relationship('Domain', back_populates='risk_assessments')
def to_dict(self):
return {
'id': str(self.id),
'check_id': str(self.check_id),
'total_score': self.total_score,
'risk_level': self.risk_level,
'domain_age_days': self.domain_age_days,
'is_new_domain': self.is_new_domain,
'is_suspicious': self.is_suspicious,
'factors': self.factors,
'assessed_at': self.assessed_at.isoformat() if self.assessed_at else None
}

View file

@ -0,0 +1,40 @@
"""SSL Certificate Model"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, JSONB
from app import db
class SslCertificate(db.Model):
"""SSL Certificate model"""
__tablename__ = 'ssl_certificates'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_id = db.Column(UUID(as_uuid=True), db.ForeignKey('domains.id', ondelete='CASCADE'), nullable=False, index=True)
issuer = db.Column(db.String(255), index=True)
subject = db.Column(db.String(255))
valid_from = db.Column(db.DateTime(timezone=True))
valid_until = db.Column(db.DateTime(timezone=True), index=True)
serial_number = db.Column(db.String(255))
signature_algorithm = db.Column(db.String(100))
key_size = db.Column(db.Integer)
is_wildcard = db.Column(db.Boolean, default=False)
is_self_signed = db.Column(db.Boolean, default=False)
is_valid = db.Column(db.Boolean, default=True, index=True)
certificate_chain = db.Column(JSONB)
fetched_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
domain = db.relationship('Domain', back_populates='ssl_certificates')
def to_dict(self):
return {
'id': str(self.id),
'issuer': self.issuer,
'subject': self.subject,
'valid_from': self.valid_from.isoformat() if self.valid_from else None,
'valid_until': self.valid_until.isoformat() if self.valid_until else None,
'is_valid': self.is_valid,
'is_self_signed': self.is_self_signed,
'days_until_expiry': (self.valid_until - datetime.utcnow()).days if self.valid_until and self.valid_until > datetime.utcnow() else 0
}

View file

@ -0,0 +1,62 @@
"""
WHOIS Record Model
"""
import uuid
from datetime import datetime
from sqlalchemy.dialects.postgresql import UUID, ARRAY, JSONB
from app import db
class WhoisRecord(db.Model):
"""WHOIS/RDAP record model"""
__tablename__ = 'whois_records'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_id = db.Column(UUID(as_uuid=True), db.ForeignKey('domains.id', ondelete='CASCADE'), nullable=False, index=True)
creation_date = db.Column(db.DateTime(timezone=True), index=True)
expiration_date = db.Column(db.DateTime(timezone=True))
updated_date = db.Column(db.DateTime(timezone=True))
registrar = db.Column(db.String(255), index=True)
registrar_url = db.Column(db.String(500))
registrant_org = db.Column(db.String(255))
registrant_country = db.Column(db.String(2))
admin_email = db.Column(db.String(255))
name_servers = db.Column(ARRAY(db.Text))
status = db.Column(ARRAY(db.Text))
dnssec = db.Column(db.Boolean)
raw_whois_data = db.Column(JSONB)
data_source = db.Column(db.String(50), default='rdap', index=True)
fetched_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, index=True)
created_at = db.Column(db.DateTime(timezone=True), default=datetime.utcnow)
# Relationship
domain = db.relationship('Domain', back_populates='whois_records')
def __repr__(self):
return f'<WhoisRecord domain_id={self.domain_id} source={self.data_source}>'
def to_dict(self):
"""Convert to dictionary"""
return {
'id': str(self.id),
'domain_id': str(self.domain_id),
'creation_date': self.creation_date.isoformat() if self.creation_date else None,
'expiration_date': self.expiration_date.isoformat() if self.expiration_date else None,
'updated_date': self.updated_date.isoformat() if self.updated_date else None,
'registrar': self.registrar,
'registrar_url': self.registrar_url,
'registrant_org': self.registrant_org,
'registrant_country': self.registrant_country,
'admin_email': self.admin_email,
'name_servers': self.name_servers,
'status': self.status,
'dnssec': self.dnssec,
'data_source': self.data_source,
'fetched_at': self.fetched_at.isoformat() if self.fetched_at else None,
'age_days': (datetime.utcnow() - self.creation_date).days if self.creation_date else None
}