File size: 7,469 Bytes
50925ca
5019340
50925ca
 
 
 
 
5019340
 
 
 
50925ca
 
 
 
 
 
 
 
 
 
 
5019340
50925ca
 
 
 
 
5019340
 
50925ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5019340
 
50925ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5019340
50925ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5019340
50925ca
 
 
 
 
 
 
 
 
 
 
5019340
50925ca
 
 
 
 
 
 
 
 
 
 
5019340
 
50925ca
 
 
 
 
 
 
 
 
 
5019340
50925ca
 
 
 
 
 
 
5019340
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import uuid
from datetime import datetime, timezone
from sqlalchemy import Column, String, Integer, Boolean, DateTime, ForeignKey, Text, Float, JSON
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import relationship
from app.database import Base

# Timezone-aware UTC timestamp factory — avoids naive/aware comparison TypeError
def utcnow():
    return datetime.now(timezone.utc)

class User(Base):
    __tablename__ = "users"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    firebase_uid = Column(String, unique=True, index=True, nullable=False)
    email = Column(String, unique=True, index=True, nullable=False)
    name = Column(String, nullable=True)
    avatar_url = Column(String, nullable=True)
    plan = Column(String, default="free")  # 'free', 'starter', 'pro'
    display_mode = Column(String, default="founder")  # 'founder', 'engineer'
    credits_remaining = Column(Integer, default=2)
    credits_reset_at = Column(DateTime(timezone=True), default=utcnow)
    stripe_customer_id = Column(String, nullable=True)
    github_connected = Column(Boolean, default=False)
    total_audits = Column(Integer, default=0)
    total_designs = Column(Integer, default=0)
    is_active = Column(Boolean, default=True)
    created_at = Column(DateTime(timezone=True), default=utcnow)
    updated_at = Column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)

    projects = relationship("Project", back_populates="user", cascade="all, delete-orphan")
    github_connection = relationship("GitHubConnection", back_populates="user", uselist=False, cascade="all, delete-orphan")

class Project(Base):
    __tablename__ = "projects"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), index=True, nullable=False)
    name = Column(String(100), nullable=False)
    type = Column(String, nullable=False)  # 'audit', 'system_design'
    source = Column(String, nullable=False)  # 'upload', 'github'
    status = Column(String, index=True, default="queued")  # 'queued', 'processing', 'complete', 'failed'
    job_id = Column(String, index=True, nullable=True)
    error_msg = Column(Text, nullable=True)
    github_repo = Column(String, nullable=True)
    github_branch = Column(String, nullable=True)
    created_at = Column(DateTime(timezone=True), default=utcnow)
    updated_at = Column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)

    user = relationship("User", back_populates="projects")
    audit_report = relationship("AuditReport", back_populates="project", uselist=False, cascade="all, delete-orphan")
    system_design = relationship("SystemDesign", back_populates="project", uselist=False, cascade="all, delete-orphan")

class AuditReport(Base):
    __tablename__ = "audit_reports"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), index=True, unique=True, nullable=False)
    overall_score = Column(Integer, nullable=False)
    confidence = Column(JSONB, nullable=False)  # JSON: level, score, label, based_on, limitations, to_increase_confidence
    capacity_estimate = Column(JSONB, nullable=False)  # JSON: safe_range, peak_range, description, reasoning, confidence
    agents = Column(JSONB, nullable=False)  # JSON: SRE, backend, infrastructure, cloud_architect reports
    top_critical_issues = Column(JSONB, nullable=False)  # Array of issues
    quick_wins = Column(JSONB, nullable=False)  # Array of quick wins
    benchmark_percentile = Column(Float, nullable=True)
    score_disclaimer = Column(Text, nullable=False)
    files_analyzed = Column(Integer, default=0)
    files_skipped = Column(Integer, default=0)
    was_truncated = Column(Boolean, default=False)
    created_at = Column(DateTime(timezone=True), default=utcnow)

    project = relationship("Project", back_populates="audit_report")

class SystemDesign(Base):
    __tablename__ = "system_designs"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    project_id = Column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), index=True, unique=True, nullable=False)
    idea_prompt = Column(Text, nullable=False)
    title = Column(String, nullable=False)
    founder_summary = Column(Text, nullable=False)
    engineer_summary = Column(Text, nullable=False)
    architecture_type = Column(String, nullable=False)  # 'Monolith', 'Microservices', 'Hybrid'
    reasoning = Column(Text, nullable=False)
    stack = Column(JSONB, nullable=False)  # JSON: Frontend, Backend, Database, Infrastructure chips with reasons
    database_design = Column(JSONB, nullable=False)  # JSON: primary_db, cache, key_tables
    api_design = Column(JSONB, nullable=False)  # JSON: style, auth_strategy, core_endpoints
    infrastructure = Column(JSONB, nullable=False)  # JSON: cloud_provider, components, scaling_strategy
    reliability = Column(JSONB, nullable=False)  # JSON: uptime_target, strategies, backup_dr
    cost_estimates = Column(JSONB, nullable=False)  # JSON: 1k, 100k, 1m tier costs and drivers
    diagram = Column(JSONB, nullable=False)  # JSON: nodes, edges
    created_at = Column(DateTime(timezone=True), default=utcnow)

    project = relationship("Project", back_populates="system_design")
    shared_link = relationship("SharedLink", back_populates="design", uselist=False, cascade="all, delete-orphan")

class SharedLink(Base):
    __tablename__ = "shared_links"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    design_id = Column(UUID(as_uuid=True), ForeignKey("system_designs.id", ondelete="CASCADE"), unique=True, nullable=False)
    slug = Column(String, unique=True, index=True, nullable=False)
    view_count = Column(Integer, default=0)
    created_at = Column(DateTime(timezone=True), default=utcnow)

    design = relationship("SystemDesign", back_populates="shared_link")

class GitHubConnection(Base):
    __tablename__ = "github_connections"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), unique=True, index=True, nullable=False)
    github_username = Column(String, nullable=False)
    access_token = Column(String, nullable=False)  # Encrypted text
    token_expires_at = Column(DateTime(timezone=True), nullable=True)
    connected_at = Column(DateTime(timezone=True), default=utcnow)
    last_used_at = Column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)

    user = relationship("User", back_populates="github_connection")

class BenchmarkData(Base):
    __tablename__ = "benchmark_data"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    score_range = Column(String, nullable=False)  # e.g., '80-89'
    industry_tag = Column(String, nullable=False)  # e.g., 'E-Commerce'
    arch_type = Column(String, nullable=False)  # e.g., 'Serverless'
    created_at = Column(DateTime(timezone=True), default=utcnow)

class WebhookEvent(Base):
    __tablename__ = "webhook_events"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    event_id = Column(String, unique=True, index=True, nullable=False)
    event_type = Column(String, nullable=False)
    processed_at = Column(DateTime(timezone=True), default=utcnow)