Files
gapido-grpc-auth/docs/diagrams/generate.py

216 lines
6.2 KiB
Python

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()