add saas files
This commit is contained in:
+10
-5
@@ -6,6 +6,9 @@ HOST=0.0.0.0
|
|||||||
PORT=8000
|
PORT=8000
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
|
|
||||||
|
# Authentication Configuration
|
||||||
|
ENABLE_AUTH=false # Set to true in production for JWT validation
|
||||||
|
|
||||||
# CORS Configuration
|
# CORS Configuration
|
||||||
# Comma-separated list of allowed origins
|
# Comma-separated list of allowed origins
|
||||||
# Use * to allow all origins (not recommended for production)
|
# Use * to allow all origins (not recommended for production)
|
||||||
@@ -42,12 +45,14 @@ EMSAL_TIMEOUT=60
|
|||||||
# Sentry DSN for error tracking
|
# Sentry DSN for error tracking
|
||||||
# SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
# SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
||||||
|
|
||||||
# Clerk Configuration
|
# ------ Clerk ------
|
||||||
CLERK_SECRET_KEY=sk_test_YourClerkSecretKey
|
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 ------
|
||||||
STRIPE_SECRET_KEY=sk_test_YourStripeSecretKey
|
STRIPE_SECRET=sk_live_xxx
|
||||||
STRIPE_WEBHOOK_SECRET=whsec_YourStripeWebhookSecret
|
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
||||||
|
|
||||||
# Application Configuration
|
# Application Configuration
|
||||||
APP_URL=http://localhost:8000
|
APP_URL=http://localhost:8000
|
||||||
|
|||||||
@@ -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
@@ -180,9 +180,10 @@ redis-data/
|
|||||||
|
|
||||||
# Production logs
|
# Production logs
|
||||||
logs/*.log.*
|
logs/*.log.*
|
||||||
Dockerfile
|
|
||||||
Dockerfile
|
# Remove these lines - we need deployment files in git:
|
||||||
fly.toml
|
# Dockerfile - NEEDED for SaaS deployment
|
||||||
.github/workflows/fly-deploy.yml
|
# fly.toml - NEEDED for Fly.io deployment
|
||||||
Dockerfile
|
# .github/workflows/fly-deploy.yml - NEEDED for GitHub Actions
|
||||||
|
|
||||||
GEMINI.md
|
GEMINI.md
|
||||||
|
|||||||
+24
-22
@@ -1,31 +1,33 @@
|
|||||||
# ---------- temel imaj ----------
|
# -------- BASE IMAGE (includes Chromium & deps) ----------------------------
|
||||||
FROM python:3.12-slim
|
FROM mcr.microsoft.com/playwright/python:v1.52.0-jammy # Playwright docs
|
||||||
|
|
||||||
# Playwright’in 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/*
|
|
||||||
|
|
||||||
|
# -------- Runtime setup ----------------------------------------------------
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Gereksinimler
|
# Copy dependency manifests first for layer-cache
|
||||||
COPY requirements.txt .
|
COPY pyproject.toml poetry.lock* requirements*.txt* ./
|
||||||
RUN pip install --no-cache-dir --upgrade pip
|
|
||||||
RUN pip install --no-cache-dir -r 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 . .
|
COPY . .
|
||||||
|
|
||||||
# Python path ayarı (import sorunlarını önler)
|
# -------- Environment ------------------------------------------------------
|
||||||
ENV PYTHONPATH=/app
|
|
||||||
|
|
||||||
# Playwright tarayıcılarını kur
|
|
||||||
RUN playwright install chromium
|
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
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
|
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
@@ -18,7 +18,9 @@ from starlette.requests import Request
|
|||||||
from starlette.responses import JSONResponse, PlainTextResponse
|
from starlette.responses import JSONResponse, PlainTextResponse
|
||||||
|
|
||||||
# Import the main MCP app
|
# 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
|
# Add a health check endpoint
|
||||||
@mcp_server.custom_route("/health", methods=["GET"])
|
@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
|
# Create ASGI apps with different transports
|
||||||
|
|
||||||
# Recommended: Streamable HTTP transport
|
# Recommended: Streamable HTTP transport
|
||||||
@@ -91,6 +96,9 @@ app = mcp_server.http_app(
|
|||||||
middleware=custom_middleware
|
middleware=custom_middleware
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add Stripe webhook router
|
||||||
|
app.include_router(stripe_router, prefix="/api")
|
||||||
|
|
||||||
# Alternative: SSE transport (for compatibility)
|
# Alternative: SSE transport (for compatibility)
|
||||||
sse_app = mcp_server.http_app(
|
sse_app = mcp_server.http_app(
|
||||||
path="/sse",
|
path="/sse",
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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
@@ -31,7 +31,7 @@ root_logger.addHandler(console_handler)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
# --- Logging Configuration End ---
|
# --- Logging Configuration End ---
|
||||||
|
|
||||||
from fastmcp import FastMCP
|
from mcp_factory import create_app
|
||||||
|
|
||||||
# --- Module Imports ---
|
# --- Module Imports ---
|
||||||
from yargitay_mcp_module.client import YargitayOfficialApiClient
|
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
|
from sayistay_mcp_module.enums import DaireEnum, KamuIdaresiTuruEnum, WebKararKonusuEnum
|
||||||
|
|
||||||
|
|
||||||
app = FastMCP(
|
app = create_app()
|
||||||
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"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- API Client Instances ---
|
# --- API Client Instances ---
|
||||||
yargitay_client_instance = YargitayOfficialApiClient()
|
yargitay_client_instance = YargitayOfficialApiClient()
|
||||||
|
|||||||
@@ -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}
|
||||||
Reference in New Issue
Block a user