BRIDGE_Modul-S_prod / server.ts
BRIDGE Platform Agent
feat(platform): BRIDGE Modules hub with /modules/e and /modules/s routed workspaces
1955a0f
Raw History Blame Contribute Delete
44.1 kB
import express from "express";
import cors from "cors";
import morgan from "morgan";
import axios from "axios";
import path from "path";
import { getAiCompletionServer } from "./src/lib/aiServiceServer";
// ...
// Initial realistic state for fallback / offline / testing mode
let mockDb = {
campaigns: [
{
id: "camp_9901",
name: "Enterprise SaaS Outbound Q3",
status: "ACTIVE",
created_at: "2026-07-15T10:00:00.000Z",
lead_count: 1420,
sent_count: 3840,
open_count: 2150,
reply_count: 168,
bounce_count: 24,
click_count: 430,
timezone: "America/New_York",
start_schedule: "09:00",
end_schedule: "18:00",
track_settings: "TRACK_EMAIL_OPEN_AND_CLICK",
send_as_plain_text: false,
follow_up_percentage: 85,
unsubscribe_text: "Reply unsubscribe to stop receiving emails.",
stop_lead_settings: "REPLIED",
sequences: [
{
id: "seq_1",
seq_number: 1,
delay_days: 0,
subject: "Quick question regarding {{company_name}}'s outbound infrastructure",
email_body: "<p>Hi {{first_name}},</p><p>Noticed you are scaling your growth team at {{company_name}}. Are you currently looking to optimize your cold outreach deliverability and warmup automation?</p><p>Best,<br>Alex</p>",
variant_distribution_percentage: 50,
variants: [
{
id: "var_1a",
variant_label: "Variant A",
subject: "Quick question regarding {{company_name}}'s outbound infrastructure",
email_body: "<p>Hi {{first_name}},</p><p>Noticed you are scaling your growth team at {{company_name}}. Are you currently looking to optimize your cold outreach deliverability and warmup automation?</p><p>Best,<br>Alex</p>"
},
{
id: "var_1b",
variant_label: "Variant B (Shorter)",
subject: "{{first_name}} - {{company_name}} outbound efficiency",
email_body: "<p>Hey {{first_name}},</p><p>We helped similar SaaS leaders double their positive response rates without hitting spam filters. Open to a 3-min loom?</p><p>Alex</p>"
}
]
},
{
id: "seq_2",
seq_number: 2,
delay_days: 3,
subject: "Re: Quick question regarding {{company_name}}",
email_body: "<p>Hi {{first_name}}, following up on my previous note. Did you have a chance to look at this?</p>",
variant_distribution_percentage: 100,
variants: []
}
],
email_account_ids: ["acc_101", "acc_102"]
},
{
id: "camp_9902",
name: "Fintech Growth & Compliance Sequence",
status: "PAUSED",
created_at: "2026-07-20T14:30:00.000Z",
lead_count: 850,
sent_count: 1200,
open_count: 620,
reply_count: 42,
bounce_count: 11,
click_count: 98,
timezone: "Europe/London",
start_schedule: "08:30",
end_schedule: "17:30",
track_settings: "TRACK_EMAIL_OPEN",
send_as_plain_text: true,
follow_up_percentage: 90,
unsubscribe_text: "Click here to unsubscribe",
stop_lead_settings: "REPLIED",
sequences: [
{
id: "seq_21",
seq_number: 1,
delay_days: 0,
subject: "Compliance streamline for {{company_name}}",
email_body: "<p>Hi {{first_name}}, wanted to share our automated compliance blueprint tailored for {{company_name}}.</p>",
variant_distribution_percentage: 100,
variants: []
}
],
email_account_ids: ["acc_103"]
},
{
id: "camp_9903",
name: "AI Tech Founders Pilot",
status: "ACTIVE",
created_at: "2026-08-01T09:15:00.000Z",
lead_count: 2400,
sent_count: 6100,
open_count: 3890,
reply_count: 310,
bounce_count: 45,
click_count: 820,
timezone: "America/Los_Angeles",
start_schedule: "09:00",
end_schedule: "17:00",
track_settings: "TRACK_EMAIL_OPEN_AND_CLICK",
send_as_plain_text: false,
follow_up_percentage: 100,
unsubscribe_text: "Unsubscribe",
stop_lead_settings: "REPLIED",
sequences: [
{
id: "seq_31",
seq_number: 1,
delay_days: 0,
subject: "{{first_name}}, accelerating inference pipelines",
email_body: "<p>Hi {{first_name}}, saw your launch on Product Hunt! Curious how {{company_name}} is handling model inference latency?</p>",
variant_distribution_percentage: 100,
variants: []
}
],
email_account_ids: ["acc_101", "acc_102", "acc_104"]
}
],
leads: [
{
id: "lead_1001",
campaign_id: "camp_9901",
first_name: "Sarah",
last_name: "Jenkins",
email: "sarah.jenkins@acmepartner.io",
phone_number: "+1 415-555-0182",
company_name: "Acme Partner Labs",
website: "https://acmepartner.io",
location: "San Francisco, CA",
linkedin_profile: "https://linkedin.com/in/sarahjenkins-sample",
status: "INTERESTED",
open_count: 4,
click_count: 2,
created_at: "2026-07-16T11:20:00.000Z",
custom_fields: { tech_stack: "Node/React", team_size: "45", funding_round: "Series B" }
},
{
id: "lead_1002",
campaign_id: "camp_9901",
first_name: "Marcus",
last_name: "Vance",
email: "m.vance@vanguardflow.co",
phone_number: "+1 212-555-0199",
company_name: "Vanguard Flow Systems",
website: "https://vanguardflow.co",
location: "New York, NY",
linkedin_profile: "https://linkedin.com/in/marcusvance-sample",
status: "MEETING_BOOKED",
open_count: 6,
click_count: 3,
created_at: "2026-07-16T12:45:00.000Z",
custom_fields: { tech_stack: "AWS/Python", team_size: "120", funding_round: "Series C" }
},
{
id: "lead_1003",
campaign_id: "camp_9901",
first_name: "Elena",
last_name: "Rostova",
email: "elena@cybercorebiotech.com",
phone_number: "+44 20 7946 0912",
company_name: "CyberCore Biotech",
website: "https://cybercorebiotech.com",
location: "London, UK",
linkedin_profile: "https://linkedin.com/in/elenarostova",
status: "REPLIED",
open_count: 3,
click_count: 1,
created_at: "2026-07-17T09:10:00.000Z",
custom_fields: { tech_stack: "NextJS", team_size: "80", funding_round: "Series A" }
},
{
id: "lead_1004",
campaign_id: "camp_9902",
first_name: "David",
last_name: "Chen",
email: "david.chen@horizonpay.net",
phone_number: "+1 650-555-0144",
company_name: "Horizon Pay",
website: "https://horizonpay.net",
location: "Palo Alto, CA",
linkedin_profile: "https://linkedin.com/in/davidchen-sample",
status: "NOT_INTERESTED",
open_count: 1,
click_count: 0,
created_at: "2026-07-21T15:00:00.000Z",
custom_fields: { tech_stack: "Go/Postgres", team_size: "200", funding_round: "Series D" }
},
{
id: "lead_1005",
campaign_id: "camp_9903",
first_name: "Amina",
last_name: "Diallo",
email: "amina@synapseai.tech",
phone_number: "+1 312-555-0177",
company_name: "Synapse AI Global",
website: "https://synapseai.tech",
location: "Chicago, IL",
linkedin_profile: "https://linkedin.com/in/aminadiallo",
status: "INTERESTED",
open_count: 5,
click_count: 4,
created_at: "2026-08-02T10:14:00.000Z",
custom_fields: { tech_stack: "PyTorch/Ray", team_size: "30", funding_round: "Seed" }
}
],
email_accounts: [
{
id: "acc_101",
from_name: "Alex Rivera",
from_email: "alex@outbound-alchemist.io",
provider: "GOOGLE",
status: "CONNECTED",
daily_limit: 50,
sent_today: 34,
warmup_enabled: true,
warmup_reputation: "EXCELLENT",
warmup_score: 98,
warmup_daily_count: 25,
ramp_day: 14,
ramp_up_increment: 2,
reply_rate_percentage: 45,
campaigns_assigned: ["Enterprise SaaS Outbound Q3", "AI Tech Founders Pilot"],
created_at: "2026-05-10T08:00:00.000Z"
},
{
id: "acc_102",
from_name: "Alex Rivera (Secondary)",
from_email: "alex.rivera@growthmatrix.cc",
provider: "MICROSOFT",
status: "CONNECTED",
daily_limit: 45,
sent_today: 28,
warmup_enabled: true,
warmup_reputation: "GOOD",
warmup_score: 89,
warmup_daily_count: 20,
ramp_day: 9,
ramp_up_increment: 3,
reply_rate_percentage: 40,
campaigns_assigned: ["Enterprise SaaS Outbound Q3", "AI Tech Founders Pilot"],
created_at: "2026-06-01T08:00:00.000Z"
},
{
id: "acc_103",
from_name: "Elena Rostova",
from_email: "elena@fintechoutreach.com",
provider: "SMTP",
smtp_host: "mail.fintechoutreach.com",
smtp_port: 587,
status: "CONNECTED",
daily_limit: 60,
sent_today: 18,
warmup_enabled: true,
warmup_reputation: "EXCELLENT",
warmup_score: 96,
warmup_daily_count: 30,
ramp_day: 22,
ramp_up_increment: 2,
reply_rate_percentage: 50,
campaigns_assigned: ["Fintech Growth & Compliance Sequence"],
created_at: "2026-06-15T08:00:00.000Z"
},
{
id: "acc_104",
from_name: "Growth Ops Lead",
from_email: "ops@aifoundryoutreach.io",
provider: "GOOGLE",
status: "RECONNECTING",
daily_limit: 40,
sent_today: 0,
warmup_enabled: false,
warmup_reputation: "POOR",
warmup_score: 62,
warmup_daily_count: 0,
ramp_day: 1,
ramp_up_increment: 1,
reply_rate_percentage: 30,
campaigns_assigned: ["AI Tech Founders Pilot"],
created_at: "2026-07-28T08:00:00.000Z"
}
],
inbox: [
{
id: "msg_501",
campaign_id: "camp_9901",
lead_id: "lead_1001",
lead_name: "Sarah Jenkins",
lead_email: "sarah.jenkins@acmepartner.io",
company_name: "Acme Partner Labs",
subject: "Re: Quick question regarding Acme Partner Labs's outbound infrastructure",
category: "INTERESTED",
unread: true,
received_at: "2026-08-14T19:30:00.000Z",
messages: [
{
id: "m_sub_1",
from: "alex@outbound-alchemist.io",
to: "sarah.jenkins@acmepartner.io",
direction: "OUTBOUND",
timestamp: "2026-08-14T14:00:00.000Z",
body_html: "<p>Hi Sarah, Noticed you are scaling your growth team at Acme Partner Labs. Are you currently looking to optimize your cold outreach deliverability and warmup automation?</p>"
},
{
id: "m_sub_2",
from: "sarah.jenkins@acmepartner.io",
to: "alex@outbound-alchemist.io",
direction: "INBOUND",
timestamp: "2026-08-14T19:30:00.000Z",
body_html: "<p>Hi Alex,</p><p>Yes, perfect timing. We are actually evaluating mailbox warmup tools right now since our SDR team just doubled. Can you send over a brief demo or pricing overview?</p><p>Thanks,<br>Sarah Jenkins | Head of Growth</p>"
}
]
},
{
id: "msg_502",
campaign_id: "camp_9901",
lead_id: "lead_1002",
lead_name: "Marcus Vance",
lead_email: "m.vance@vanguardflow.co",
company_name: "Vanguard Flow Systems",
subject: "Re: Quick question regarding Vanguard Flow Systems",
category: "MEETING_BOOKED",
unread: false,
received_at: "2026-08-14T16:15:00.000Z",
messages: [
{
id: "m_sub_3",
from: "alex@outbound-alchemist.io",
to: "m.vance@vanguardflow.co",
direction: "OUTBOUND",
timestamp: "2026-08-13T10:00:00.000Z",
body_html: "<p>Hey Marcus, We helped similar SaaS leaders double their positive response rates without hitting spam filters. Open to a 3-min loom?</p>"
},
{
id: "m_sub_4",
from: "m.vance@vanguardflow.co",
to: "alex@outbound-alchemist.io",
direction: "INBOUND",
timestamp: "2026-08-14T16:15:00.000Z",
body_html: "<p>Alex, grabbed a slot on your calendar for Tuesday at 2 PM EST. Talk soon!</p>"
}
]
},
{
id: "msg_503",
campaign_id: "camp_9903",
lead_id: "lead_1005",
lead_name: "Amina Diallo",
lead_email: "amina@synapseai.tech",
company_name: "Synapse AI Global",
subject: "Re: Amina, accelerating inference pipelines",
category: "INTERESTED",
unread: true,
received_at: "2026-08-14T21:05:00.000Z",
messages: [
{
id: "m_sub_5",
from: "ops@aifoundryoutreach.io",
to: "amina@synapseai.tech",
direction: "OUTBOUND",
timestamp: "2026-08-14T15:00:00.000Z",
body_html: "<p>Hi Amina, saw your launch on Product Hunt! Curious how Synapse AI Global is handling model inference latency?</p>"
},
{
id: "m_sub_6",
from: "amina@synapseai.tech",
to: "ops@aifoundryoutreach.io",
direction: "INBOUND",
timestamp: "2026-08-14T21:05:00.000Z",
body_html: "<p>Thanks! We've built an in-house caching proxy, but always curious to learn about specialized acceleration clusters. Let's connect next week.</p>"
}
]
}
],
webhooks: [
{
id: "wh_801",
name: "Slack Lead Alerts & Zapier Sync",
webhook_url: "https://hooks.zapier.com/hooks/catch/912837/b8120a",
event_types: ["REPLY", "LEAD_CATEGORY_UPDATED", "CAMPAIGN_COMPLETED"],
campaign_id: null,
status: "ACTIVE",
last_triggered: "2026-08-14T21:05:12.000Z",
created_at: "2026-06-20T10:00:00.000Z"
},
{
id: "wh_802",
name: "HubSpot CRM Bi-directional Sync",
webhook_url: "https://api.hubapi.com/webhooks/v3/custom-lead-sync",
event_types: ["REPLY", "BOUNCE", "UNSUBSCRIBE", "LEAD_CATEGORY_UPDATED"],
campaign_id: "camp_9901",
status: "ACTIVE",
last_triggered: "2026-08-14T19:30:22.000Z",
created_at: "2026-07-01T12:00:00.000Z"
}
],
webhook_logs: [
{
id: "whlog_1",
webhook_id: "wh_801",
event_type: "REPLY",
status_code: 200,
timestamp: "2026-08-14T21:05:12.000Z",
payload_preview: { lead_email: "amina@synapseai.tech", category: "INTERESTED", sentiment: "positive" }
},
{
id: "whlog_2",
webhook_id: "wh_802",
event_type: "LEAD_CATEGORY_UPDATED",
status_code: 200,
timestamp: "2026-08-14T19:30:22.000Z",
payload_preview: { lead_email: "sarah.jenkins@acmepartner.io", status: "INTERESTED" }
}
],
clients: [
{
id: "cli_101",
name: "Nexus AI Ventures",
email: "growth@nexusventures.ai",
company: "Nexus AI Ventures Inc.",
campaigns_count: 2,
leads_count: 3820,
created_at: "2026-05-15T09:00:00.000Z"
},
{
id: "cli_102",
name: "Apex Velocity B2B",
email: "contact@apexvelocity.io",
company: "Apex Velocity Marketing",
campaigns_count: 1,
leads_count: 1200,
created_at: "2026-06-10T11:30:00.000Z"
}
],
block_list: {
emails: [
{ id: "bl_1", email: "competitor-bot@spamtrap.org", reason: "Spam trap detected", added_at: "2026-07-01T10:00:00.000Z" },
{ id: "bl_2", email: "ceo@opt-out-forever.com", reason: "Direct legal opt-out", added_at: "2026-07-12T14:20:00.000Z" }
],
domains: [
{ id: "bld_1", domain: "mailinator.com", reason: "Disposable email service", added_at: "2026-06-10T09:00:00.000Z" },
{ id: "bld_2", domain: "guerrillamail.com", reason: "Temporary inbox", added_at: "2026-06-11T11:00:00.000Z" },
{ id: "bld_3", domain: "10minutemail.com", reason: "Temporary inbox", added_at: "2026-06-15T15:00:00.000Z" }
]
},
billing: {
credits: 0,
auto_topup: { enabled: false, threshold: 500, amount: 10000 },
invoices: [] as any[],
packages: [
{ id: "pkg_2k", name: "2 000 cr", credits: 2000, price: 4.90, unit_price: 0.00245, features: ["Zero Expiry", "Verified results only consume credits"] },
{ id: "pkg_10k", name: "10 000 cr", credits: 10000, price: 39.00, unit_price: 0.00390, features: ["Zero Expiry"] },
{ id: "pkg_25k", name: "25 000 cr", credits: 25000, price: 59.00, unit_price: 0.00236, popular: true, features: ["Most Popular", "Zero Expiry"] },
{ id: "pkg_50k", name: "50 000 cr", credits: 50000, price: 89.00, unit_price: 0.00178, popular: true, features: ["Most Popular", "Zero Expiry"] },
{ id: "pkg_100k", name: "100 000 cr", credits: 100000, price: 149.00, unit_price: 0.00149, features: ["Zero Expiry"] },
{ id: "pkg_500k", name: "500 000 cr", credits: 500000, price: 299.00, unit_price: 0.00060, features: ["Zero Expiry"] },
{ id: "pkg_1m", name: "1 000 000 cr", credits: 1000000, price: 449.00, unit_price: 0.00045, features: ["Zero Expiry"] }
]
}
};
let stripeClient: any = null;
function getStripe() {
if (!stripeClient && process.env.STRIPE_SECRET_KEY) {
import("stripe").then(({ default: Stripe }) => {
stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY as string);
});
}
return stripeClient;
}
async function startServer() {
const app = express();
const PORT = Number(process.env.PORT) || 3000;
app.use(cors({
origin: process.env.CORS_ORIGIN ? process.env.CORS_ORIGIN.split(',') : false,
credentials: true
}));
app.use(morgan("dev"));
app.use(express.json());
app.get(["/api/health", "/api/v1/health"], (req, res) => {
res.json({ status: "ok", timestamp: new Date().toISOString(), uptime: process.uptime() });
});
app.get(["/api/ping", "/api/v1/ping"], (req, res) => {
res.json({ pong: true, timestamp: new Date().toISOString() });
});
// Platform health probe (carried over from Module S prod)
app.get("/api/health", (req, res) => {
res.json({ status: "ok", platform: "BRIDGE Modules", modules: ["e", "s"], timestamp: new Date().toISOString() });
});
// Helper to check if real Smartlead API call should be attempted
const shouldProxyToSmartlead = (apiKey: string | undefined) => {
return Boolean(apiKey && apiKey.length > 10 && !apiKey.startsWith("demo_") && !apiKey.startsWith("mock_"));
};
// ==========================================
// MODULE 1: CAMPAIGNS API
// ==========================================
app.get("/api/campaigns", async (req, res) => {
const apiKey = req.headers["x-api-key"] as string;
if (shouldProxyToSmartlead(apiKey)) {
try {
const response = await axios.get("https://server.smartlead.ai/api/v1/campaigns", {
params: { ...req.query, api_key: apiKey },
timeout: 8000
});
return res.json(response.data);
} catch (err: any) {
console.warn("Smartlead API live proxy failed, falling back to database mode:", err.message);
}
}
return res.json(mockDb.campaigns);
});
app.post("/api/campaigns", async (req, res) => {
const apiKey = req.headers["x-api-key"] as string;
if (shouldProxyToSmartlead(apiKey)) {
try {
const response = await axios.post("https://server.smartlead.ai/api/v1/campaigns", req.body, {
params: { api_key: apiKey },
timeout: 8000
});
return res.status(201).json(response.data);
} catch (err: any) {
console.warn("Live create campaign failed, recording in local database:", err.message);
}
}
const newCampaign = {
id: "camp_" + Math.random().toString(36).substring(2, 9),
name: req.body.name || "Untitled Campaign",
status: "PAUSED",
created_at: new Date().toISOString(),
lead_count: 0,
sent_count: 0,
open_count: 0,
reply_count: 0,
bounce_count: 0,
click_count: 0,
timezone: req.body.timezone || "America/New_York",
start_schedule: req.body.start_schedule || "09:00",
end_schedule: req.body.end_schedule || "17:00",
track_settings: req.body.track_settings || "TRACK_EMAIL_OPEN",
send_as_plain_text: Boolean(req.body.send_as_plain_text),
follow_up_percentage: req.body.follow_up_percentage || 80,
unsubscribe_text: req.body.unsubscribe_text || "Unsubscribe",
stop_lead_settings: req.body.stop_lead_settings || "REPLIED",
sequences: [
{
id: "seq_" + Math.random().toString(36).substring(2, 7),
seq_number: 1,
delay_days: 0,
subject: req.body.initial_subject || "Connecting with {{first_name}}",
email_body: req.body.initial_body || "<p>Hi {{first_name}}, let's connect!</p>",
variant_distribution_percentage: 100,
variants: []
}
],
email_account_ids: req.body.email_account_ids || []
};
mockDb.campaigns.unshift(newCampaign);
return res.status(201).json(newCampaign);
});
app.get("/api/campaigns/:id", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
return res.json(campaign);
});
app.post("/api/campaigns/:id", (req, res) => {
const index = mockDb.campaigns.findIndex(c => c.id === req.params.id);
if (index === -1) return res.status(404).json({ error: "Campaign not found" });
mockDb.campaigns[index] = { ...mockDb.campaigns[index], ...req.body };
return res.json(mockDb.campaigns[index]);
});
app.delete("/api/campaigns/:id", (req, res) => {
mockDb.campaigns = mockDb.campaigns.filter(c => c.id !== req.params.id);
return res.json({ success: true, message: "Campaign deleted successfully" });
});
app.post("/api/campaigns/:id/status", (req, res) => {
const { status } = req.body; // START / PAUSE / STOP
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
campaign.status = status === "START" ? "ACTIVE" : status === "PAUSE" ? "PAUSED" : "STOPPED";
return res.json({ success: true, status: campaign.status, campaign });
});
app.post("/api/campaigns/:id/schedule", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
campaign.start_schedule = req.body.start_schedule || campaign.start_schedule;
campaign.end_schedule = req.body.end_schedule || campaign.end_schedule;
campaign.timezone = req.body.timezone || campaign.timezone;
return res.json({ success: true, campaign });
});
app.get("/api/campaigns/:id/sequences", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
return res.json(campaign.sequences || []);
});
app.post("/api/campaigns/:id/sequences", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
campaign.sequences = req.body.sequences || [];
return res.json({ success: true, sequences: campaign.sequences });
});
app.get("/api/campaigns/:id/email-accounts", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
const accounts = mockDb.email_accounts.filter(a => campaign.email_account_ids?.includes(a.id));
return res.json(accounts);
});
app.post("/api/campaigns/:id/email-accounts", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
const { email_account_id } = req.body;
if (!campaign.email_account_ids) campaign.email_account_ids = [];
if (!campaign.email_account_ids.includes(email_account_id)) {
campaign.email_account_ids.push(email_account_id);
}
return res.json({ success: true, email_account_ids: campaign.email_account_ids });
});
app.delete("/api/campaigns/:id/email-accounts/:eid", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
campaign.email_account_ids = (campaign.email_account_ids || []).filter(id => id !== req.params.eid);
return res.json({ success: true, email_account_ids: campaign.email_account_ids });
});
app.get("/api/campaigns/:id/analytics", (req, res) => {
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (!campaign) return res.status(404).json({ error: "Campaign not found" });
return res.json({
sent: campaign.sent_count,
opens: campaign.open_count,
replies: campaign.reply_count,
bounces: campaign.bounce_count,
clicks: campaign.click_count,
open_rate: ((campaign.open_count / (campaign.sent_count || 1)) * 100).toFixed(1),
reply_rate: ((campaign.reply_count / (campaign.sent_count || 1)) * 100).toFixed(1),
bounce_rate: ((campaign.bounce_count / (campaign.sent_count || 1)) * 100).toFixed(1)
});
});
app.get("/api/campaigns/:id/analytics-by-date", (req, res) => {
const days = 14;
const history = [];
const now = new Date();
for (let i = days; i >= 0; i--) {
const d = new Date(now.getTime() - i * 24 * 60 * 60 * 1000);
const dayLabel = d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
history.push({
date: dayLabel,
sent: Math.floor(120 + Math.random() * 80),
opens: Math.floor(65 + Math.random() * 40),
clicks: Math.floor(18 + Math.random() * 15),
replies: Math.floor(8 + Math.random() * 10),
bounces: Math.floor(1 + Math.random() * 3)
});
}
return res.json(history);
});
app.get("/api/campaigns/:id/leads-export", (req, res) => {
const campaignLeads = mockDb.leads.filter(l => l.campaign_id === req.params.id);
let csv = "first_name,last_name,email,company_name,status,open_count,reply_count\n";
campaignLeads.forEach(l => {
csv += `"${l.first_name}","${l.last_name}","${l.email}","${l.company_name}","${l.status}",${l.open_count},${l.click_count}\n`;
});
res.setHeader("Content-Type", "text/csv");
res.setHeader("Content-Disposition", `attachment; filename=leads-${req.params.id}.csv`);
return res.send(csv);
});
// ==========================================
// MODULE 2: LEADS API
// ==========================================
app.get("/api/campaigns/:id/leads", (req, res) => {
const leads = mockDb.leads.filter(l => l.campaign_id === req.params.id);
return res.json(leads);
});
app.post("/api/campaigns/:id/leads", (req, res) => {
const leadsToAdd = Array.isArray(req.body) ? req.body : req.body.leads || [req.body];
const created = leadsToAdd.map((item: any) => {
const newLead = {
id: "lead_" + Math.random().toString(36).substring(2, 9),
campaign_id: req.params.id,
first_name: item.first_name || "",
last_name: item.last_name || "",
email: item.email || "lead@sample.io",
phone_number: item.phone_number || "",
company_name: item.company_name || "",
website: item.website || "",
location: item.location || "",
linkedin_profile: item.linkedin_profile || "",
status: item.status || "INTERESTED",
open_count: 0,
click_count: 0,
created_at: new Date().toISOString(),
custom_fields: item.custom_fields || {}
};
mockDb.leads.unshift(newLead);
return newLead;
});
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (campaign) campaign.lead_count += created.length;
return res.status(201).json({ success: true, count: created.length, leads: created });
});
app.get("/api/leads/search", (req, res) => {
const query = (req.query.q as string || "").toLowerCase();
const status = req.query.status as string;
let results = mockDb.leads;
if (query) {
results = results.filter(l =>
l.first_name.toLowerCase().includes(query) ||
l.last_name.toLowerCase().includes(query) ||
l.email.toLowerCase().includes(query) ||
l.company_name.toLowerCase().includes(query)
);
}
if (status && status !== "ALL") {
results = results.filter(l => l.status === status);
}
return res.json(results);
});
app.post("/api/campaigns/:id/leads/status", (req, res) => {
const { lead_id, status } = req.body;
const lead = mockDb.leads.find(l => l.id === lead_id);
if (!lead) return res.status(404).json({ error: "Lead not found" });
lead.status = status;
return res.json({ success: true, lead });
});
app.delete("/api/campaigns/:id/leads/:lead_id", (req, res) => {
mockDb.leads = mockDb.leads.filter(l => l.id !== req.params.lead_id);
const campaign = mockDb.campaigns.find(c => c.id === req.params.id);
if (campaign && campaign.lead_count > 0) campaign.lead_count--;
return res.json({ success: true });
});
// Blocklist routes
app.get("/api/leads/block-list", (req, res) => res.json(mockDb.block_list.emails));
app.post("/api/leads/block-list", (req, res) => {
const item = { id: "bl_" + Date.now(), email: req.body.email, reason: req.body.reason || "Manual add", added_at: new Date().toISOString() };
mockDb.block_list.emails.unshift(item);
res.status(201).json(item);
});
app.delete("/api/leads/block-list/:id", (req, res) => {
mockDb.block_list.emails = mockDb.block_list.emails.filter(e => e.id !== req.params.id && e.email !== req.params.id);
res.json({ success: true });
});
app.get("/api/leads/domain-block-list", (req, res) => res.json(mockDb.block_list.domains));
app.post("/api/leads/domain-block-list", (req, res) => {
const item = { id: "bld_" + Date.now(), domain: req.body.domain, reason: req.body.reason || "Manual add", added_at: new Date().toISOString() };
mockDb.block_list.domains.unshift(item);
res.status(201).json(item);
});
app.delete("/api/leads/domain-block-list/:id", (req, res) => {
mockDb.block_list.domains = mockDb.block_list.domains.filter(d => d.id !== req.params.id && d.domain !== req.params.id);
res.json({ success: true });
});
// ==========================================
// MODULE 3: EMAIL ACCOUNTS API
// ==========================================
app.get("/api/email-accounts", (req, res) => res.json(mockDb.email_accounts));
app.post("/api/email-accounts", (req, res) => {
const newAcc = {
id: "acc_" + Math.random().toString(36).substring(2, 8),
from_name: req.body.from_name || "Outbound Rep",
from_email: req.body.from_email || "rep@sampledomain.com",
provider: req.body.provider || "SMTP",
smtp_host: req.body.smtp_host,
smtp_port: req.body.smtp_port,
status: "CONNECTED",
daily_limit: req.body.max_email_per_day || 50,
sent_today: 0,
warmup_enabled: true,
warmup_reputation: "GOOD",
warmup_score: 85,
warmup_daily_count: 10,
ramp_day: 1,
ramp_up_increment: 2,
reply_rate_percentage: 40,
campaigns_assigned: [],
created_at: new Date().toISOString()
};
mockDb.email_accounts.push(newAcc);
res.status(201).json(newAcc);
});
app.post("/api/email-accounts/reconnect", (req, res) => {
const { account_id } = req.body;
const acc = mockDb.email_accounts.find(a => a.id === account_id);
if (acc) acc.status = "CONNECTED";
res.json({ success: true, message: "Account reconnected successfully", account: acc });
});
app.delete("/api/email-accounts/:id", (req, res) => {
mockDb.email_accounts = mockDb.email_accounts.filter(a => a.id !== req.params.id);
res.json({ success: true });
});
// ==========================================
// MODULE 4: EMAIL WARMUP API
// ==========================================
app.post("/api/email-accounts/:id/warmup/enable", (req, res) => {
const acc = mockDb.email_accounts.find(a => a.id === req.params.id);
if (!acc) return res.status(404).json({ error: "Account not found" });
acc.warmup_enabled = true;
res.json({ success: true, account: acc });
});
app.post("/api/email-accounts/:id/warmup/disable", (req, res) => {
const acc = mockDb.email_accounts.find(a => a.id === req.params.id);
if (!acc) return res.status(404).json({ error: "Account not found" });
acc.warmup_enabled = false;
res.json({ success: true, account: acc });
});
app.get("/api/email-accounts/:id/warmup/stats", (req, res) => {
const acc = mockDb.email_accounts.find(a => a.id === req.params.id);
if (!acc) return res.status(404).json({ error: "Account not found" });
res.json({
score: acc.warmup_score,
reputation: acc.warmup_reputation,
sent_warmup: acc.warmup_daily_count,
received_warmup: Math.floor(acc.warmup_daily_count * 0.45),
spam_rescued: 2,
ramp_day: acc.ramp_day,
history: [
{ day: "Day 1", score: 70 },
{ day: "Day 3", score: 78 },
{ day: "Day 7", score: 85 },
{ day: "Day 10", score: 92 },
{ day: "Day 14", score: acc.warmup_score }
]
});
});
app.post("/api/email-accounts/:id/warmup/settings", (req, res) => {
const acc = mockDb.email_accounts.find(a => a.id === req.params.id);
if (!acc) return res.status(404).json({ error: "Account not found" });
acc.ramp_up_increment = req.body.ramp_up_increment || acc.ramp_up_increment;
acc.reply_rate_percentage = req.body.reply_rate_percentage || acc.reply_rate_percentage;
res.json({ success: true, account: acc });
});
// ==========================================
// MODULE 5: UNIFIED INBOX API
// ==========================================
app.get("/api/inbox", (req, res) => {
const { category, campaign_id } = req.query;
let threads = mockDb.inbox;
if (category && category !== "ALL") {
threads = threads.filter(t => t.category === category);
}
if (campaign_id) {
threads = threads.filter(t => t.campaign_id === campaign_id);
}
res.json(threads);
});
app.get("/api/inbox/unread-count", (req, res) => {
const unread = mockDb.inbox.filter(t => t.unread).length;
res.json({ count: unread });
});
app.post("/api/campaigns/:id/reply-email-thread", (req, res) => {
const { thread_id, reply_body } = req.body;
const thread = mockDb.inbox.find(t => t.id === thread_id || t.campaign_id === req.params.id);
if (thread) {
thread.unread = false;
thread.messages.push({
id: "m_rep_" + Date.now(),
from: "alex@outbound-alchemist.io",
to: thread.lead_email,
direction: "OUTBOUND",
timestamp: new Date().toISOString(),
body_html: `<p>${reply_body}</p>`
});
return res.json({ success: true, message: "Reply sent successfully", thread });
}
res.json({ success: true, message: "Reply delivered" });
});
app.post("/api/inbox/categorize", (req, res) => {
const { thread_id, category } = req.body;
const thread = mockDb.inbox.find(t => t.id === thread_id);
if (thread) {
thread.category = category;
const lead = mockDb.leads.find(l => l.email === thread.lead_email);
if (lead) lead.status = category;
}
res.json({ success: true, category });
});
// ==========================================
// MODULE 6: WEBHOOKS API
// ==========================================
app.get("/api/webhooks", (req, res) => res.json(mockDb.webhooks));
app.post("/api/webhooks", (req, res) => {
const newWh = {
id: "wh_" + Math.random().toString(36).substring(2, 8),
name: req.body.name || "Custom Webhook Endpoint",
webhook_url: req.body.webhook_url || "https://api.domain.com/webhook",
event_types: req.body.event_types || ["REPLY"],
campaign_id: req.body.campaign_id || null,
status: "ACTIVE",
last_triggered: null,
created_at: new Date().toISOString()
};
mockDb.webhooks.unshift(newWh);
res.status(201).json(newWh);
});
app.delete("/api/webhooks/:id", (req, res) => {
mockDb.webhooks = mockDb.webhooks.filter(w => w.id !== req.params.id);
res.json({ success: true });
});
app.post("/api/webhooks/:id/test", (req, res) => {
const wh = mockDb.webhooks.find(w => w.id === req.params.id);
if (!wh) return res.status(404).json({ error: "Webhook not found" });
const logItem = {
id: "whlog_" + Date.now(),
webhook_id: wh.id,
event_type: wh.event_types[0] || "REPLY",
status_code: 200,
timestamp: new Date().toISOString(),
payload_preview: { lead_email: "test.lead@domain.com", category: "INTERESTED", sentiment: "positive" }
} as const;
mockDb.webhook_logs.unshift(logItem);
wh.last_triggered = new Date().toISOString();
res.json({ success: true, message: "Test payload sent to endpoint", log: logItem });
});
app.get("/api/webhooks/logs", (req, res) => res.json(mockDb.webhook_logs));
// ==========================================
// MODULE 7: ANALYTICS API
// ==========================================
app.get("/api/analytics/campaigns", (req, res) => {
const totalSent = mockDb.campaigns.reduce((acc, c) => acc + c.sent_count, 0);
const totalOpens = mockDb.campaigns.reduce((acc, c) => acc + c.open_count, 0);
const totalReplies = mockDb.campaigns.reduce((acc, c) => acc + c.reply_count, 0);
const totalBounces = mockDb.campaigns.reduce((acc, c) => acc + c.bounce_count, 0);
const totalClicks = mockDb.campaigns.reduce((acc, c) => acc + c.click_count, 0);
const totalLeads = mockDb.campaigns.reduce((acc, c) => acc + c.lead_count, 0);
res.json({
total_campaigns: mockDb.campaigns.length,
total_leads: totalLeads,
total_sent: totalSent,
total_opens: totalOpens,
total_replies: totalReplies,
total_bounces: totalBounces,
total_clicks: totalClicks,
open_rate: ((totalOpens / (totalSent || 1)) * 100).toFixed(1),
reply_rate: ((totalReplies / (totalSent || 1)) * 100).toFixed(1),
click_rate: ((totalClicks / (totalSent || 1)) * 100).toFixed(1),
bounce_rate: ((totalBounces / (totalSent || 1)) * 100).toFixed(1)
});
});
// ==========================================
// MODULE 8: CLIENT MANAGEMENT API
// ==========================================
app.get("/api/clients", (req, res) => res.json(mockDb.clients));
// ==========================================
// MODULE 9: BILLING & STRIPE API
// ==========================================
app.get("/api/billing", (req, res) => res.json(mockDb.billing));
app.post("/api/billing/purchase", (req, res) => {
const { package_id } = req.body;
const pkg = mockDb.billing.packages.find(p => p.id === package_id);
if (!pkg) return res.status(404).json({ error: "Package not found" });
// In a real app, this would be handled via Stripe Webhook after payment
// For this environment, we'll simulate the credit addition
mockDb.billing.credits += pkg.credits;
const invoice = {
id: "inv_" + Math.random().toString(36).substring(2, 9),
description: `${pkg.name} Package Purchase`,
credits: pkg.credits,
amount: pkg.price,
status: "PAID",
created_at: new Date().toISOString()
};
mockDb.billing.invoices.unshift(invoice);
res.json({ success: true, message: `Successfully added ${pkg.credits} credits.`, credits: mockDb.billing.credits });
});
app.post("/api/billing/autotopup", (req, res) => {
mockDb.billing.auto_topup = { ...mockDb.billing.auto_topup, ...req.body };
res.json({ success: true, auto_topup: mockDb.billing.auto_topup });
});
app.post("/api/billing/create-checkout-session", async (req, res) => {
const { package_id } = req.body;
const pkg = mockDb.billing.packages.find(p => p.id === package_id);
if (!pkg) return res.status(404).json({ error: "Package not found" });
const stripe = getStripe();
if (!stripe) {
// Fallback for demo mode if no key is set
return res.json({
url: `${req.headers.origin}/pricing?success=true&package_id=${package_id}`,
mode: "demo"
});
}
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [{
price_data: {
currency: 'usd',
product_data: {
name: `BRIDGEModulE - ${pkg.name}`,
description: `${pkg.credits.toLocaleString()} credits for email verification.`,
},
unit_amount: Math.round(pkg.price * 100),
},
quantity: 1,
}],
mode: 'payment',
success_url: `${req.headers.origin}/pricing?success=true&session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${req.headers.origin}/pricing?canceled=true`,
});
res.json({ url: session.url });
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
app.post("/api/ai/completion", async (req, res) => {
try {
const { prompt } = req.body;
if (!prompt) {
return res.status(400).json({ error: "Prompt is required" });
}
const response = await getAiCompletionServer(prompt);
res.json({ response });
} catch (error) {
res.status(500).json({ error: "Failed to get AI completion" });
}
});
app.post("/api/clients", (req, res) => {
const newClient = {
id: "cli_" + Math.random().toString(36).substring(2, 7),
name: req.body.name || "New Client",
email: req.body.email || "client@company.com",
company: req.body.company || "Client Company Ltd",
campaigns_count: 0,
leads_count: 0,
created_at: new Date().toISOString()
};
mockDb.clients.push(newClient);
res.status(201).json(newClient);
});
app.delete("/api/clients/:id", (req, res) => {
mockDb.clients = mockDb.clients.filter(c => c.id !== req.params.id);
res.json({ success: true });
});
// Proxy Catch-All for any other direct Smartlead API calls
app.use("/api", async (req, res) => {
const apiKey = req.headers["x-api-key"] as string;
if (!apiKey) {
return res.status(401).json({ error: "Missing x-api-key header" });
}
try {
const targetUrl = `https://server.smartlead.ai/api/v1${req.path}`;
const response = await axios({
method: req.method,
url: targetUrl,
data: Object.keys(req.body).length > 0 ? req.body : undefined,
params: { ...req.query, api_key: apiKey },
timeout: 10000
});
res.status(response.status).json(response.data);
} catch (error: any) {
if (error.response) {
res.status(error.response.status).json(error.response.data);
} else {
res.status(500).json({ error: "Proxy connection error", details: error.message });
}
}
});
// Vite development middleware or production static serving
if (process.env.NODE_ENV !== "production") {
const { createServer: createViteServer } = await import("vite");
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "spa",
});
app.use(vite.middlewares);
} else {
const distPath = path.join(process.cwd(), "dist");
app.use(express.static(distPath));
app.get("*", (req, res) => {
res.sendFile(path.join(distPath, "index.html"));
});
}
app.listen(PORT, "0.0.0.0", () => {
console.log(`Smartlead Command Hub Server listening on http://0.0.0.0:${PORT}`);
});
}
startServer();