from pathlib import Path from diagrams import Cluster, Diagram, Edge from diagrams.generic.blank import Blank from diagrams.onprem.database import Mongodb from diagrams.onprem.inmemory import Redis from diagrams.onprem.network import Internet from diagrams.onprem.queue import Rabbitmq ROOT = Path(__file__).resolve().parents[2] OUT = ROOT / "docs" / "assets" / "diagrams" def graph_attr() -> dict[str, str]: return { "bgcolor": "transparent", "pad": "0.35", "ranksep": "0.8", "nodesep": "0.55", "fontname": "Inter", } def node_attr() -> dict[str, str]: return { "fontname": "Inter", "fontsize": "13", "style": "rounded,filled", "fillcolor": "#111827", "fontcolor": "#E5E7EB", "color": "#334155", } def edge_attr() -> dict[str, str]: return {"fontname": "Inter", "fontsize": "11", "color": "#64748B", "fontcolor": "#475569"} def render_service_architecture() -> None: with Diagram( "Service Architecture", filename=str(OUT / "service_architecture"), show=False, direction="LR", graph_attr=graph_attr(), node_attr=node_attr(), edge_attr=edge_attr(), ): browser = Internet("Browser") demo = Blank("demo-app\nFastAPI UI") auth = Blank("auth-service\ngRPC") worker = Blank("sms-worker") mongo = Mongodb("MongoDB") redis = Redis("Redis") rabbit = Rabbitmq("RabbitMQ") providers = Blank("Kavenegar / SMS.ir") browser >> Edge(label="HTTPS / local HTTP") >> demo demo >> Edge(label="gRPC") >> auth auth >> Edge(label="users + refresh sessions") >> mongo auth >> Edge(label="OTP hash + rate limits") >> redis auth >> Edge(label="SMS job") >> rabbit >> worker >> providers def render_clean_architecture() -> None: with Diagram( "Clean Architecture Layers", filename=str(OUT / "clean_architecture"), show=False, direction="TB", graph_attr=graph_attr(), node_attr=node_attr(), edge_attr=edge_attr(), ): with Cluster("Transport"): grpc = Blank("gRPC servicer") demo = Blank("Demo BFF") with Cluster("Application"): auth = Blank("AuthService\nuse cases") security = Blank("Token + OTP helpers") with Cluster("Domain"): entities = Blank("Entities") ports = Blank("Ports") with Cluster("Infrastructure"): mongo = Blank("Mongo repositories") redis = Blank("Redis OTP store") rabbit = Blank("RabbitMQ publisher") sms = Blank("SMS strategies") grpc >> auth demo >> grpc auth >> entities auth >> ports auth >> security ports >> mongo ports >> redis ports >> rabbit ports >> sms def render_otp_flow() -> None: with Diagram( "OTP Login Flow", filename=str(OUT / "otp_login_flow"), show=False, direction="LR", graph_attr=graph_attr(), node_attr=node_attr(), edge_attr=edge_attr(), ): user = Blank("User") demo = Blank("demo-app") auth = Blank("auth-service") redis = Redis("Redis") rabbit = Rabbitmq("RabbitMQ") worker = Blank("sms-worker") sms = Blank("SMS provider") mongo = Mongodb("MongoDB") user >> Edge(label="mobile") >> demo >> Edge(label="RequestOtp") >> auth auth >> Edge(label="store OTP hash") >> redis auth >> Edge(label="publish job") >> rabbit >> worker >> sms user >> Edge(label="code") >> demo >> Edge(label="VerifyOtp") >> auth auth >> Edge(label="compare hash") >> redis auth >> Edge(label="upsert user + session") >> mongo auth >> Edge(label="token pair") >> demo >> user def render_refresh_flow() -> None: with Diagram( "Refresh Token Rotation", filename=str(OUT / "refresh_rotation_flow"), show=False, direction="LR", graph_attr=graph_attr(), node_attr=node_attr(), edge_attr=edge_attr(), ): client = Blank("Client") auth = Blank("auth-service") mongo = Mongodb("MongoDB") old = Blank("Old session\nrevoked") new = Blank("New session\nactive") client >> Edge(label="refresh token") >> auth auth >> Edge(label="hash lookup") >> mongo mongo >> Edge(label="active session") >> auth auth >> old auth >> new new >> Edge(label="new token pair") >> client def render_sms_strategy() -> None: with Diagram( "SMS Provider Strategy", filename=str(OUT / "sms_provider_strategy"), show=False, direction="LR", graph_attr=graph_attr(), node_attr=node_attr(), edge_attr=edge_attr(), ): worker = Blank("sms-worker") factory = Blank("create_sms_client") port = Blank("SmsClient port") kav = Blank("Kavenegar") smsir = Blank("SMS.ir") debug = Blank("Debug local") worker >> factory >> port port >> kav port >> smsir port >> debug def render_production_deployment() -> None: with Diagram( "Production Deployment", filename=str(OUT / "production_deployment"), show=False, direction="LR", graph_attr=graph_attr(), node_attr=node_attr(), edge_attr=edge_attr(), ): internet = Internet("Internet") caddy = Blank("Caddy\nHTTPS") demo = Blank("demo-app") auth = Blank("auth-service") private = Blank("Private Docker network") mongo = Mongodb("MongoDB") redis = Redis("Redis") rabbit = Rabbitmq("RabbitMQ") worker = Blank("sms-worker") internet >> Edge(label="gapido.amiirkhl.ir") >> caddy >> demo >> auth auth >> private private >> mongo private >> redis private >> rabbit >> worker def main() -> None: OUT.mkdir(parents=True, exist_ok=True) render_service_architecture() render_clean_architecture() render_otp_flow() render_refresh_flow() render_sms_strategy() render_production_deployment() if __name__ == "__main__": main()