feat: Souq native integration + auth simplification + CE-wide enhancements
Souq Marketplace: - Native souq pages: browse+filters, create listing, detail+seller profile, orders+chat - New API: reviews (POST), sellers/[id], upload (multipart), polar-checkout webhook - Listings API enhanced: minPrice, maxPrice, sortBy, deliveryDays filters - Seller workflow: start order, mark delivered, upload files, confirm delivery - Wallet: 5 Polar.sh FLH tiers, production checkout, mock fallback, success banner - Fix: order redirect /souq/history → /souq/orders Auth System: - Unified auth page, removed OAuth provider routing (2 files deleted) - Deleted oauth.ts (319 lines) — simplified auth chain - Streamlined login/register API routes Prisma Schema (+179 lines): - New models: Listing, Purchase, Review, Group/GroupMember - Gamification: DailyStreak, XpTransaction, Achievement, UserLevel - Learning: LearnCourse/Module/Enrollment/Certificate - Notifications, Referrals, Dhikr, PremiumBenefits Deleted legacy code: - src/app/api/marketplace/* (3 files) — replaced by /api/souq/* - src/app/api/files/[listingId]/route.ts — replaced by /api/souq/upload - src/app/api/seller/[sellerId]/route.ts — replaced by /api/souq/sellers/[id] Infrastructure: - Dockerfile: standalone output, Prisma runtime migration - docker-compose.yml: Polar env vars, healthcheck fix - docker-compose.staging.yml: staging on port 4014 - .gitea/workflows/ci.yml: CI pipeline
This commit is contained in:
+32
-40
@@ -51,52 +51,42 @@ ERROR_PATTERNS_JS = json.dumps([
|
||||
|
||||
|
||||
def authenticate(browser):
|
||||
"""Login with demo credentials and return authenticated context + page."""
|
||||
"""Login via API and set token in localStorage, then return authenticated page."""
|
||||
context = browser.new_context(viewport=VIEWPORTS[viewport])
|
||||
page = context.new_page()
|
||||
page.on("pageerror", lambda err: None)
|
||||
|
||||
try:
|
||||
# Step 1: Go to homepage → login page
|
||||
# Step 1: Call login API to get a JWT token
|
||||
import urllib.request, json as jsonlib, ssl
|
||||
login_url = f"{BASE_URL.rstrip('/')}/api/auth/login"
|
||||
req = urllib.request.Request(
|
||||
login_url,
|
||||
data=jsonlib.dumps({"email": "demo@falahos.my", "password": "password123"}).encode(),
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
ctx = ssl.create_default_context()
|
||||
resp = urllib.request.urlopen(req, timeout=15, context=ctx)
|
||||
auth_data = jsonlib.loads(resp.read().decode())
|
||||
token = auth_data.get("token", "")
|
||||
if not token:
|
||||
fail("Login API returned no token")
|
||||
return None, None
|
||||
ok(f"Got API token: {token[:20]}...{token[-4:]}")
|
||||
|
||||
# Step 2: Navigate to any page and inject the token into localStorage
|
||||
page.goto(f"{BASE_URL.rstrip('/')}/", timeout=TIMEOUT_MS, wait_until="domcontentloaded")
|
||||
page.evaluate(f"localStorage.setItem('flh_token', '{token}')")
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
# Step 3: Reload to let React AuthContext pick up the token
|
||||
page.goto(f"{BASE_URL.rstrip('/')}/", timeout=TIMEOUT_MS, wait_until="networkidle")
|
||||
page.wait_for_timeout(1000)
|
||||
|
||||
# Step 2: Click "Continue with Email"
|
||||
email_btn = page.locator("button:has-text('Continue with Email')")
|
||||
if email_btn.count() > 0:
|
||||
email_btn.click()
|
||||
page.wait_for_timeout(1500)
|
||||
|
||||
# Step 3: Click "Sign in" to get to the login form
|
||||
sign_in = page.locator("button:has-text('Sign in')")
|
||||
if sign_in.count() > 0:
|
||||
sign_in.click()
|
||||
page.wait_for_timeout(1500)
|
||||
|
||||
# Step 4: Fill email
|
||||
email_input = page.locator("input[type='email']")
|
||||
if email_input.count() > 0:
|
||||
email_input.fill("demo@falahos.my")
|
||||
page.wait_for_timeout(200)
|
||||
|
||||
# Step 5: Fill password
|
||||
pw_input = page.locator("input[type='password']")
|
||||
if pw_input.count() > 0:
|
||||
pw_input.fill("password123")
|
||||
page.wait_for_timeout(200)
|
||||
|
||||
# Step 6: Submit
|
||||
submit = page.locator("button[type='submit'], button:has-text('Sign In')")
|
||||
if submit.count() > 0:
|
||||
submit.first.click()
|
||||
page.wait_for_timeout(3000)
|
||||
|
||||
# Wait for network idle after login
|
||||
try:
|
||||
page.wait_for_load_state("networkidle", timeout=10000)
|
||||
except:
|
||||
pass
|
||||
page.wait_for_timeout(1000)
|
||||
page.wait_for_timeout(2000)
|
||||
|
||||
# Verify auth
|
||||
body_text = page.inner_text("body") or ""
|
||||
@@ -108,6 +98,8 @@ def authenticate(browser):
|
||||
return context, page
|
||||
except Exception as e:
|
||||
fail(f"Authentication failed: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
context.close()
|
||||
return None, None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user