Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
417 changes: 417 additions & 0 deletions app.py

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions clerk_public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzspx8CSwc9+7DZL2CaVR
ksT6DU+Zx0E1itDSjRE6DtJiIFCODHST6rBSsR8T2Xeub7p6Zb+0fH4lWFkIJbud
E3uhlnXSRNAit6l81KZgz2PMCpwYtAhnYFnJ9XgRxrDLzLq+XXTkN9lFTef+8cg6
CQWwiOUfcVAQsY/ZSCz83CrxdKe9lMYERBNQh/C/mxVo6u2UfccE8YikErr5Y8jD
JWGL+1pufeT1R9byNfPJM1nUvGhb+gqdxmhdHztc0YR1rUghoeJbZCO0VYWkgYZH
xb2pxiN0J08env3TXe1dD6bh+4BHRoh6WnExBKZD9mkYwyaZ4/RZCna2d+N7VGsv
nQIDAQAB
-----END PUBLIC KEY-----
24 changes: 24 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional
from datetime import datetime

class UserUsage(BaseModel):
model_config = ConfigDict(extra='ignore', populate_by_name=True)

user_id: str
date: str # ISO format YYYY-MM-DD
count: int = Field(default=0, ge=0)

class UserData(BaseModel):
model_config = ConfigDict(extra='ignore', populate_by_name=True)

user_id: str
email: Optional[str] = None
credits: int = Field(default=0, ge=0)
plan: str = "free" # "free", "pro", etc.
created_at: datetime = Field(default_factory=datetime.utcnow)

# Ad Tracking Limits
ads_watched_today: int = Field(default=0, ge=0)
ad_credits_earned_today: int = Field(default=0, ge=0)
last_ad_date: Optional[str] = None # YYYY-MM-DD
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ aiofiles>=23.2.1
httpx>=0.27.0
python-dotenv>=1.0.1
websockets>=12.0
fastapi>=0.115.0
uvicorn>=0.30.0
motor>=3.6.0
python-jose[cryptography]>=3.3.0
pydantic-settings>=2.6.0
66 changes: 66 additions & 0 deletions runware_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import asyncio
import os
from runware import Runware, IImageInference
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

async def text_to_image(runware: Runware):
print("\n--- Running Text-to-Image ---")
request_image = IImageInference(
positivePrompt="a futuristic city with neon lights, highly detailed, digital art",
model="runware:400@6", # FLUX.2 [klein] 9B KV
numberResults=1,
height=1024,
width=1024,
)

images = await runware.imageInference(requestImage=request_image)
for image in images:
print(f"Generated Image URL: {image.imageURL}")
return images[0].imageURL if images else None

async def image_to_image(runware: Runware, source_image_url: str):
print("\n--- Running Image-to-Image ---")
# Image-to-Image uses seedImage and strength
# strength: 0.0 to 1.0 (lower means closer to source image, higher means closer to prompt)
request_image = IImageInference(
positivePrompt="same futuristic city but in daytime with bright sunlight",
model="runware:400@6", # FLUX.2 [klein] 9B KV
seedImage=source_image_url,
strength=0.6,
numberResults=1,
height=1024,
width=1024,
)

images = await runware.imageInference(requestImage=request_image)
for image in images:
print(f"Img2Img Result URL: {image.imageURL}")

async def main():
api_key = os.getenv("RUNWARE_API_KEY")
if not api_key:
print("Error: RUNWARE_API_KEY not found in .env file.")
return

runware = Runware(api_key=api_key)
await runware.connect()

try:
# 1. Text to Image
generated_url = await text_to_image(runware)

# 2. Image to Image (using the result of the first generation as source)
if generated_url:
await image_to_image(runware, generated_url)
else:
# Fallback if text-to-image failed to return a URL
print("Skipping Img2Img as no source image was generated.")

finally:
await runware.disconnect()

if __name__ == "__main__":
asyncio.run(main())
22 changes: 22 additions & 0 deletions scratch/check_clerk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
from dotenv import load_dotenv

load_dotenv()

async def check_clerk_users():
mongo_url = os.getenv("MONGO_URL")
client = AsyncIOMotorClient(mongo_url)
db = client.get_database()

count = await db.users.count_documents({"user_id": {"$exists": True}})
print(f"Users with user_id: {count}")

sample = await db.users.find_one({"user_id": {"$exists": True}})
print(f"Sample Clerk User: {sample}")

client.close()

if __name__ == "__main__":
asyncio.run(check_clerk_users())
23 changes: 23 additions & 0 deletions scratch/check_db_credits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
from dotenv import load_dotenv

load_dotenv()

async def check_credits():
mongo_url = os.getenv("MONGO_URL")
client = AsyncIOMotorClient(mongo_url)
db = client.get_database()

# Get all users to see their credits
users = await db.users.find().to_list(length=10)
print("--- User Credits in DB ---")
for user in users:
print(f"User ID: {user.get('user_id')} | Email: {user.get('email')} | Credits: {user.get('credits')}")
print("--------------------------")

client.close()

if __name__ == "__main__":
asyncio.run(check_credits())
57 changes: 57 additions & 0 deletions scratch/diagnose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import os
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
from runware import Runware
from dotenv import load_dotenv

load_dotenv()

async def diagnose():
print("--- Diagnostics Started ---")

# 1. Check Runware
api_key = os.getenv("RUNWARE_API_KEY")
print(f"Runware API Key present: {bool(api_key)}")
if api_key:
try:
runware = Runware(api_key=api_key)
await runware.connect()
print("✅ Runware connection successful")
await runware.disconnect()
except Exception as e:
print(f"❌ Runware connection failed: {e}")

# 2. Check MongoDB
mongo_url = os.getenv("MONGO_URL")
print(f"MongoDB URL present: {bool(mongo_url)}")
if mongo_url:
try:
client = AsyncIOMotorClient(mongo_url)
# Try to ping the database
await client.admin.command('ping')
print("✅ MongoDB connection successful")

db = client.get_database()
print(f"Using database: {db.name}")

# Check collections
collections = await db.list_collection_names()
print(f"Collections found: {collections}")

client.close()
except Exception as e:
print(f"❌ MongoDB connection failed: {e}")

# 3. Check Clerk Key
clerk_key = os.getenv("CLERK_JWT_PUBLIC_KEY")
print(f"Clerk Public Key present: {bool(clerk_key)}")
if clerk_key:
if "BEGIN PUBLIC KEY" in clerk_key and "END PUBLIC KEY" in clerk_key:
print("✅ Clerk Public Key format looks valid (PEM)")
else:
print("❌ Clerk Public Key format invalid (missing PEM headers)")

print("--- Diagnostics Finished ---")

if __name__ == "__main__":
asyncio.run(diagnose())
28 changes: 28 additions & 0 deletions scratch/find_clerk_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
from dotenv import load_dotenv

load_dotenv()

async def find_clerk_user():
mongo_url = os.getenv("MONGO_URL")
client = AsyncIOMotorClient(mongo_url)
db = client.get_database()

# Find any user that has a non-null user_id
clerk_users = await db.users.find({"user_id": {"$exists": True, "$ne": None}}).to_list(length=5)
print(f"Found {len(clerk_users)} Clerk users.")
for u in clerk_users:
print(f"User: {u.get('email')} | Credits: {u.get('credits')} | ID: {u.get('user_id')}")

# Also check the 'usage' collection
usage = await db.usage.find().to_list(length=5)
print(f"\nFound {len(usage)} usage records.")
for res in usage:
print(f"Usage: {res}")

client.close()

if __name__ == "__main__":
asyncio.run(find_clerk_user())
30 changes: 30 additions & 0 deletions scratch/find_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import asyncio
import os
from runware import Runware, IModelSearch
from dotenv import load_dotenv

load_dotenv()

async def main():
api_key = os.getenv("RUNWARE_API_KEY")
if not api_key:
print("RUNWARE_API_KEY not found in .env")
return

runware = Runware(api_key=api_key)
await runware.connect()

print("Searching for 'flux klein' models...")
search_results = await runware.modelSearch(
payload=IModelSearch(search="flux 2")
)

for model in search_results.results:
print(f"Name: {model.name}")
print(f"AIR: {model.air}")
print("-" * 20)

await runware.disconnect()

if __name__ == "__main__":
asyncio.run(main())
19 changes: 19 additions & 0 deletions scratch/inspect_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
from dotenv import load_dotenv

load_dotenv()

async def inspect():
mongo_url = os.getenv("MONGO_URL")
client = AsyncIOMotorClient(mongo_url)
db = client.get_database()

user = await db.users.find_one()
print(f"Sample User Document: {user}")

client.close()

if __name__ == "__main__":
asyncio.run(inspect())