หน้าแรกก่อนเข้า Dashboard — หน้าเว็บอื่น ๆ จะ redirect มาที่นี่ถ้ายังไม่ได้ล็อกอิน
| Method | URL | Body | สำเร็จ | ผิดพลาด |
|---|---|---|---|---|
| POST | /api/login | { "username", "password" } | 200 { "ok": true } + Set-Cookie fp_session | 401 { "error": "invalid_credentials" } |
LOGIN_USERNAME / LOGIN_PASSWORDfp_session (HttpOnly, Path=/, SameSite=Lax, อายุ 7 วัน)/loginGET /api/collect?sourceId=<id> ไล่ทีละ active sourcesrc/routes/collect.js): อ่าน source_configs (active=true) → จัดกลุ่มตาม platform → getCollector() + ตรวจ isAvailable() → collector.collect() → step summary → saveRunLog() ลง lead_logssrc/collectors/facebook.js): สั่ง Apify รัน apify/facebook-groups-scraper (proxy residential) → poll ทุก 3 วิ (งบ 55 วิ) → mapApifyItem() แปลงฟิลด์ → processItems() ทีละ 5 postsextractProperty) — confidence < 0.3 → drop; (3) โหลดรูป max 10 → อัปโหลด bucket lead-images; (4) extractGoogleMapsUrl(); (5) assignAgentTeam(); (6) insertLead(); (7) processLeadForResult()src/lib/groq.js): system prompt (prompts/extract-property.md) + ข้อความ post (ตัด 4000 ตัวอักษร) → typhoon (default) หรือ groq (JSON mode), temp 0.1, retry 3 → AI คืน JSON → computePrices() คำนวณราคา per_sqm → raw × pricing_area_sqmextract-property.md): "You extract industrial property listing information from Facebook posts." → คืน JSON เท่านั้น, กฎ per-sqm ≠ ราคารวม, schema 25+ ฟิลด์, ไม่ใช่อสังหาฯ → confidence 0src/workflow/result-leads.js): isComplete() → snapshot result_leads → หา duplicate → vision (Groq qwen/qwen3.6-27b, prompt compare-properties.md, ≤2 รูป/ฝั่ง) → merge เก็บโพสต์ที่สมบูรณ์กว่า.env.localLOGIN_USERNAME / LOGIN_PASSWORD)