body=json.dumps(payload,sort_keys=True,default=str).encode();self.send_response(status);self.send_header("Content-Type","application/json; charset=utf-8");self.send_header("Access-Control-Allow-Origin",os.environ.get("CORS_ORIGINS","http://localhost:8080"));self.send_header("Access-Control-Allow-Credentials","true");self.send_header("Access-Control-Allow-Methods","GET, POST, PATCH, OPTIONS");self.send_header("Access-Control-Allow-Headers","Content-Type")
returndb.execute("SELECT u.id,u.email,u.role,u.organization_id FROM sessions s JOIN users u ON u.id=s.user_id WHERE s.token_hash=? AND s.expires_at>?",(h,now)).fetchone()
returnself.send_json(200,{"items":[dict(r)forrindb.execute("SELECT id,email,role,organization_id,created_at FROM users WHERE organization_id=? ORDER BY id",(org,))]})
ifpath=="/api/v1/dashboard/summary":
row=db.execute("SELECT COUNT(*) businesses,COALESCE(AVG(score),0) average_score FROM businesses WHERE organization_id=?",(org,)).fetchone();returnself.send_json(200,{"organization_id":org,"businesses":row["businesses"],"average_score":round(row["average_score"],2),"suppressed":db.execute("SELECT COUNT(*) FROM suppressions WHERE organization_id=?",(org,)).fetchone()[0]})
ifq:where.append("(b.name LIKE ? OR b.website_domain LIKE ? OR b.email LIKE ?)");params+=[f"%{q}%"]*3
ifstage:where.append("EXISTS (SELECT 1 FROM pipeline_entries p WHERE p.business_id=b.id AND p.organization_id=b.organization_id AND p.stage=?)");params.append(stage)
offset=(number("cursor",0)or0)+(page-1)*size
rows=db.execute("SELECT b.* FROM businesses b WHERE "+" AND ".join(where)+" ORDER BY b.score DESC,b.id LIMIT ? OFFSET ?",params+[size+1,offset]).fetchall();more=len(rows)>size;rows=rows[:size]
db=self.db();email=str(payload.get("email"," ")).strip().lower();password=str(payload.get("password",""));user=db.execute("SELECT * FROM users WHERE email=?",(email,)).fetchone()
token=secrets.token_urlsafe(32);expires=datetime.now(timezone.utc)+timedelta(days=SESSION_DAYS);db.execute("INSERT INTO sessions(user_id,token_hash,expires_at) VALUES(?,?,?)",(user["id"],hashlib.sha256(token.encode()).hexdigest(),expires.replace(microsecond=0).isoformat()));self.audit(db,user,"login");db.commit();returnself.send_json(200,{"id":user["id"],"email":user["email"],"role":user["role"],"organization_id":user["organization_id"]},{"Set-Cookie":self.auth_cookie(token,int(timedelta(days=SESSION_DAYS).total_seconds()))})
iffieldsanddb.execute("SELECT id FROM businesses WHERE organization_id=? AND ("+" OR ".join(f"{c}=?"forc,_infields)+")",[org]+[vfor_,vinfields]).fetchone():returnself.send_json(409,{"error":"duplicate"})
scored=score_business(b);cur=db.execute("INSERT INTO businesses(organization_id,name,website,website_domain,email,phone,description,score,score_version,score_factors,website_class) VALUES(?,?,?,?,?,?,?,?,?,?,?)",(org,b["name"],b["website"],b["website_domain"],b["email"],b["phone"],str(b.get("description","")),scored["score"],scored["score_version"],json.dumps(scored["factors"]),scored["website_class"]));self.audit(db,user,"business.created",str(cur.lastrowid));db.commit();returnself.send_json(201,row_json(db.execute("SELECT * FROM businesses WHERE id=?",(cur.lastrowid,)).fetchone()))
try:db.execute("INSERT INTO suppressions(organization_id,kind,value) VALUES(?,?,?)",(user["organization_id"],kind,value))
exceptsqlite3.IntegrityError:pass
self.audit(db,user,"suppression.created",kind);db.commit();returnself.send_json(201,dict(db.execute("SELECT * FROM suppressions WHERE organization_id=? AND kind=? AND value=?",(user["organization_id"],kind,value)).fetchone()))
suppressed=is_suppressed({"email":email,"phone":phone},[dict(r)forrindb.execute("SELECT kind,value FROM suppressions WHERE organization_id=?",(user["organization_id"],))]);values=(str(payload.get("name","")).strip(),email,phone,str(payload.get("title","")).strip(),int(bool(payload.get("do_not_contact")))orint(suppressed))
columns=CHILD_TABLES[table];db.execute(f"INSERT INTO {table}(business_id,organization_id,{','.join(columns)}) VALUES(?, ?, {','.join('?'for_incolumns)})",(bid,user["organization_id"])+values);rid=db.execute("SELECT last_insert_rowid()").fetchone()[0];self.audit(db,user,f"{table}.created",str(rid));db.commit();returnself.send_json(201,row_json(db.execute(f"SELECT * FROM {table} WHERE id=?",(rid,)).fetchone()))
stage=str(payload["stage"]).strip();status=str(payload.get("status","active")).strip()or"active";db.execute("INSERT INTO pipeline_entries(business_id,organization_id,stage,status) VALUES(?,?,?,?)",(bid,user["organization_id"],stage,status));rid=db.execute("SELECT last_insert_rowid()").fetchone()[0];self.audit(db,user,"pipeline.updated",stage);db.commit();returnself.send_json(200,row_json(db.execute("SELECT * FROM pipeline_entries WHERE id=?",(rid,)).fetchone()))
verified=bool(payload.get("verified",True));now=datetime.now(timezone.utc).replace(microsecond=0).isoformat();db.execute("UPDATE businesses SET verified=?,verified_at=?,updated_at=CURRENT_TIMESTAMP WHERE id=? AND organization_id=?",(int(verified),nowifverifiedelseNone,bid,user["organization_id"]));self.audit(db,user,"business.verified",str(verified));db.commit();row=self.business(db,bid,user["organization_id"]);returnself.send_json(200,row_json(row))
normalized=deduplicate_businesses([rforrinrowsifisinstance(r,dict)andstr(r.get("name","")).strip()]);suppressions=[dict(r)forrindb.execute("SELECT kind,value FROM suppressions WHERE organization_id=?",(org,))];existing=[row_json(r)forrindb.execute("SELECT * FROM businesses WHERE organization_id=?",(org,))];seen=set();accepted=[];suppressed=0;existing_keys={deduplication_key(x)forxinexisting}
parser=argparse.ArgumentParser();parser.add_argument("--host",default="127.0.0.1");parser.add_argument("--port",type=int,default=int(os.environ.get("PROSPECT_API_PORT","8000")));parser.add_argument("--db",default=os.environ.get("PROSPECT_API_DB","prospects.db"));args=parser.parse_args();server=create_server(args.host,args.port,args.db);print(f"Prospect API listening on http://{args.host}:{args.port}",flush=True)