add saas files

This commit is contained in:
saidsurucu
2025-06-29 14:31:37 +03:00
parent 352969deca
commit 973a25980b
9 changed files with 153 additions and 39 deletions
+10 -5
View File
@@ -6,6 +6,9 @@ HOST=0.0.0.0
PORT=8000
LOG_LEVEL=info
# Authentication Configuration
ENABLE_AUTH=false # Set to true in production for JWT validation
# CORS Configuration
# Comma-separated list of allowed origins
# Use * to allow all origins (not recommended for production)
@@ -42,12 +45,14 @@ EMSAL_TIMEOUT=60
# Sentry DSN for error tracking
# SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
# Clerk Configuration
CLERK_SECRET_KEY=sk_test_YourClerkSecretKey
# ------ Clerk ------
CLERK_PUBLISHABLE_KEY=pk_test_xxx
CLERK_SECRET_KEY=sk_test_xxx
CLERK_ISSUER=https://your-app.clerk.accounts.dev # issuer & JWKS root
# Stripe Configuration
STRIPE_SECRET_KEY=sk_test_YourStripeSecretKey
STRIPE_WEBHOOK_SECRET=whsec_YourStripeWebhookSecret
# ------ Stripe ------
STRIPE_SECRET=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
# Application Configuration
APP_URL=http://localhost:8000
+18
View File
@@ -0,0 +1,18 @@
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
name: Fly Deploy
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
concurrency: deploy-group # optional: ensure only one action runs at a time
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+6 -5
View File
@@ -180,9 +180,10 @@ redis-data/
# Production logs
logs/*.log.*
Dockerfile
Dockerfile
fly.toml
.github/workflows/fly-deploy.yml
Dockerfile
# Remove these lines - we need deployment files in git:
# Dockerfile - NEEDED for SaaS deployment
# fly.toml - NEEDED for Fly.io deployment
# .github/workflows/fly-deploy.yml - NEEDED for GitHub Actions
GEMINI.md
+24 -22
View File
@@ -1,31 +1,33 @@
# ---------- temel imaj ----------
FROM python:3.12-slim
# Playwrightin istediği kitaplıklar
RUN apt-get update && apt-get install -y \
libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
libdrm2 libdbus-1-3 libatspi2.0-0 libx11-6 libxcomposite1 \
libxdamage1 libxext6 libxfixes3 libxrandr2 libgbm1 libxcb1 \
libxkbcommon0 libpango-1.0-0 libcairo2 libasound2 \
&& rm -rf /var/lib/apt/lists/*
# -------- BASE IMAGE (includes Chromium & deps) ----------------------------
FROM mcr.microsoft.com/playwright/python:v1.52.0-jammy # Playwright docs
# -------- Runtime setup ----------------------------------------------------
WORKDIR /app
# Gereksinimler
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt
# Copy dependency manifests first for layer-cache
COPY pyproject.toml poetry.lock* requirements*.txt* ./
# Uygulama dosyaları
# Fast, deterministic install with `uv`
RUN pip install --no-cache-dir uv && \
uv pip install --system --no-cache-dir .[asgi] # installs FastMCP, FastAPI
# Copy application source
COPY . .
# Python path ayarı (import sorunlarını önler)
ENV PYTHONPATH=/app
# Playwright tarayıcılarını kur
RUN playwright install chromium
# -------- Environment ------------------------------------------------------
ENV PYTHONUNBUFFERED=1
ENV ENABLE_AUTH=true # Clerk JWT validation ON by default
ENV PORT=8000
# -------- Health check -----------------------------------------------------
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD python - <<'PY'
import httpx, os, sys; \
r=httpx.get(f"http://localhost:{os.getenv('PORT','8000')}/health"); \
sys.exit(0 if r.status_code==200 else 1)
PY
EXPOSE 8000
CMD ["uvicorn", "fastapi_app:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]
# -------- Entrypoint -------------------------------------------------------
CMD ["uvicorn", "asgi_app:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]
+9 -1
View File
@@ -18,7 +18,9 @@ from starlette.requests import Request
from starlette.responses import JSONResponse, PlainTextResponse
# Import the main MCP app
from mcp_server_main import app as mcp_server
from mcp_factory import create_app
mcp_server = create_app()
# Add a health check endpoint
@mcp_server.custom_route("/health", methods=["GET"])
@@ -83,6 +85,9 @@ custom_middleware = [
),
]
# Import Stripe webhook router
from stripe_webhook import router as stripe_router
# Create ASGI apps with different transports
# Recommended: Streamable HTTP transport
@@ -91,6 +96,9 @@ app = mcp_server.http_app(
middleware=custom_middleware
)
# Add Stripe webhook router
app.include_router(stripe_router, prefix="/api")
# Alternative: SSE transport (for compatibility)
sse_app = mcp_server.http_app(
path="/sse",
+32
View File
@@ -0,0 +1,32 @@
# fly.toml app configuration file generated for yargi-mcp on 2025-06-29T00:23:47+03:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'yargi-mcp'
primary_region = 'fra'
[env]
ENABLE_AUTH = "true"
PORT = "8000"
[build]
[http_service]
internal_port = 8000
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
[checks.http_health] # keep MCP /health live
type = "http"
interval = "30s"
timeout = "10s"
path = "/health"
+28
View File
@@ -0,0 +1,28 @@
import os
from functools import lru_cache
from fastmcp import FastMCP
from fastmcp.server.auth import BearerAuthProvider
@lru_cache
def create_app() -> FastMCP:
"""Return a FastMCP instance; Clerk JWT validation when ENABLE_AUTH=true."""
if os.getenv("ENABLE_AUTH", "false").lower() != "true":
return FastMCP(
name="Yargı MCP DEV",
instructions="MCP server for TR legal databases (Yargitay, Danistay, Emsal, Uyusmazlik, Anayasa-Norm, Anayasa-Bireysel, KIK, Sayistay, Rekabet).",
dependencies=["httpx", "beautifulsoup4", "markitdown", "pydantic", "aiohttp", "playwright"]
)
issuer = os.environ["CLERK_ISSUER"] # e.g. https://cool-app.clerk.accounts.dev
auth = BearerAuthProvider(
jwks_uri=f"{issuer}/.well-known/jwks.json", # Clerk JWKS pattern
issuer=issuer,
audience=os.environ["CLERK_PUBLISHABLE_KEY"], # PK appears in aud claim
required_scopes=["yargi.read"],
)
return FastMCP(
name="Yargı MCP PROD",
auth=auth,
instructions="MCP server for TR legal databases (Yargitay, Danistay, Emsal, Uyusmazlik, Anayasa-Norm, Anayasa-Bireysel, KIK, Sayistay, Rekabet) with JWT authentication.",
dependencies=["httpx", "beautifulsoup4", "markitdown", "pydantic", "aiohttp", "playwright"]
)
+2 -6
View File
@@ -31,7 +31,7 @@ root_logger.addHandler(console_handler)
logger = logging.getLogger(__name__)
# --- Logging Configuration End ---
from fastmcp import FastMCP
from mcp_factory import create_app
# --- Module Imports ---
from yargitay_mcp_module.client import YargitayOfficialApiClient
@@ -97,11 +97,7 @@ from sayistay_mcp_module.models import (
from sayistay_mcp_module.enums import DaireEnum, KamuIdaresiTuruEnum, WebKararKonusuEnum
app = FastMCP(
name="YargiMCP",
instructions="MCP server for TR legal databases (Yargitay, Danistay, Emsal, Uyusmazlik, Anayasa-Norm, Anayasa-Bireysel, KIK, Sayistay).",
dependencies=["httpx", "beautifulsoup4", "markitdown", "pydantic", "aiohttp", "playwright"]
)
app = create_app()
# --- API Client Instances ---
yargitay_client_instance = YargitayOfficialApiClient()
+24
View File
@@ -0,0 +1,24 @@
import os, stripe
from clerk import ClerkClient # Clerk backend SDK
from fastapi import APIRouter, Request, HTTPException
router = APIRouter()
stripe.api_key = os.getenv("STRIPE_SECRET")
clerk = ClerkClient(secret_key=os.getenv("CLERK_SECRET_KEY"))
@router.post("/stripe/webhook")
async def stripe_hook(req: Request):
payload, sig = await req.body(), req.headers["stripe-signature"]
try:
event = stripe.Webhook.construct_event( # Stripe-recommended verify
payload, sig, os.getenv("STRIPE_WEBHOOK_SECRET"))
except stripe.error.SignatureVerificationError:
raise HTTPException(400, "Bad sig")
if event["type"] == "customer.subscription.updated":
item = event["data"]["object"]["items"]["data"][0]
plan = item["price"]["nickname"] # "Pro", "Enterprise"…
userID = event["data"]["object"]["metadata"]["clerk_user_id"]
clerk.users.update_user_metadata( # merge into unsafe_metadata
userID, unsafe_metadata={"plan": plan})
return {"ok": True}