git-paid 2.1.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/server.js DELETED
@@ -1,252 +0,0 @@
1
- import http from "node:http";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { scanTargetUrl, searchBuyerIntentRadar, dispatchDirectories } from "./lib/engine.js";
6
- import { createStripeCheckout, createCustomerPortalSession, verifyAndHandleStripeWebhook, configureStripeBrandingAndPortal, getCustomerBillingSummary, cancelCustomerSubscription } from "./lib/stripe.js";
7
- import { authenticateUser, verifyLicenseToken } from "./lib/auth.js";
8
- import { ingestInboundEmail, listInboxMessages, sendTestVerificationEmail } from "./lib/inbox.js";
9
- import { autoVerifyInboxLinks, runStatefulMcpSimulation, executeMcpToolCall } from "./lib/mcp.js";
10
-
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = path.dirname(__filename);
13
- const PORT = Number(process.env.POSTBUILD_PORT) || 8096;
14
-
15
- // Load local .env if present
16
- const envPath = path.join(__dirname, ".env");
17
- if (fs.existsSync(envPath)) {
18
- const envContent = fs.readFileSync(envPath, "utf8");
19
- for (const line of envContent.split(/\r?\n/)) {
20
- const trimmed = line.trim();
21
- if (!trimmed || trimmed.startsWith("#")) continue;
22
- const eqIdx = trimmed.indexOf("=");
23
- if (eqIdx > 0) {
24
- const key = trimmed.slice(0, eqIdx).trim();
25
- const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
26
- if (!process.env[key]) process.env[key] = val;
27
- }
28
- }
29
- }
30
-
31
- const MIME_TYPES = {
32
- ".html": "text/html; charset=utf-8",
33
- ".js": "application/javascript; charset=utf-8",
34
- ".css": "text/css; charset=utf-8",
35
- ".json": "application/json; charset=utf-8",
36
- ".xml": "application/xml; charset=utf-8",
37
- ".txt": "text/plain; charset=utf-8",
38
- ".svg": "image/svg+xml",
39
- ".png": "image/png"
40
- };
41
-
42
- function readRawBuffer(req) {
43
- return new Promise((resolve) => {
44
- const chunks = [];
45
- req.on("data", chunk => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
46
- req.on("end", () => resolve(Buffer.concat(chunks)));
47
- });
48
- }
49
-
50
- async function readJsonBody(req) {
51
- const buf = await readRawBuffer(req);
52
- try {
53
- return buf.length ? JSON.parse(buf.toString("utf8")) : {};
54
- } catch {
55
- return {};
56
- }
57
- }
58
-
59
- const server = http.createServer(async (req, res) => {
60
- try {
61
- const host = req.headers.host || `localhost:${PORT}`;
62
- const parsed = new URL(req.url || "/", `http://${host}`);
63
-
64
- if (parsed.pathname === "/api/scan") {
65
- const target = parsed.searchParams.get("url") || "https://postbuild.dev";
66
- const result = await scanTargetUrl(target);
67
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
68
- res.end(JSON.stringify(result));
69
- return;
70
- }
71
-
72
- if (parsed.pathname === "/api/radar") {
73
- const q = parsed.searchParams.get("q") || "seo marketing indie hacker";
74
- const threads = await searchBuyerIntentRadar(q);
75
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
76
- res.end(JSON.stringify({ query: q, threads }));
77
- return;
78
- }
79
-
80
- if (parsed.pathname === "/api/dispatch") {
81
- const body = req.method === "POST" ? await readJsonBody(req) : {};
82
- const action = body.action || parsed.searchParams.get("action") || "";
83
- const inbox = body.inbox || body.to || parsed.searchParams.get("inbox") || "postbuild-bot@postbuild.dev";
84
- if (action === "inbound_email") {
85
- const msg = await ingestInboundEmail(body);
86
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
87
- res.end(JSON.stringify({ ok: true, message: msg }));
88
- return;
89
- }
90
- if (action === "inbox_test") {
91
- const domain = body.domain || parsed.searchParams.get("domain") || "postbuild.dev";
92
- const msg = await sendTestVerificationEmail(inbox, domain);
93
- const list = await listInboxMessages(inbox, { allowRemoteFallback: false });
94
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
95
- res.end(JSON.stringify({ ok: true, latest: msg, ...list }));
96
- return;
97
- }
98
- if (action === "inbox_autoverify") {
99
- const current = await listInboxMessages(inbox, { allowRemoteFallback: false });
100
- if (current.count === 0) {
101
- const domain = body.domain || parsed.searchParams.get("domain") || "postbuild.dev";
102
- await sendTestVerificationEmail(inbox, domain);
103
- }
104
- const verified = await autoVerifyInboxLinks(inbox);
105
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
106
- res.end(JSON.stringify({ ok: true, ...verified }));
107
- return;
108
- }
109
- if (action === "inbox_list") {
110
- const list = await listInboxMessages(inbox, { allowRemoteFallback: false });
111
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
112
- res.end(JSON.stringify({ ok: true, ...list }));
113
- return;
114
- }
115
- if (action === "mcp_simulate") {
116
- const targetUrl = body.url || body.targetUrl || parsed.searchParams.get("url") || "https://postbuild.dev";
117
- const sim = await runStatefulMcpSimulation({
118
- url: targetUrl,
119
- sessionId: body.sessionId || "local-studio-mcp"
120
- });
121
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
122
- res.end(JSON.stringify({ ok: true, ...sim }));
123
- return;
124
- }
125
- if (action === "mcp_call") {
126
- const targetUrl = body.url || body.targetUrl || parsed.searchParams.get("url") || "https://postbuild.dev";
127
- const toolName = body.tool || parsed.searchParams.get("tool") || "postbuild_directories";
128
- const toolArgs = body.args || { url: targetUrl };
129
- const output = await executeMcpToolCall(toolName, toolArgs, body.sessionId || "local-studio-session");
130
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
131
- res.end(JSON.stringify({ ok: true, tool: toolName, result: output }));
132
- return;
133
- }
134
- const targetUrl = body.url || body.targetUrl || parsed.searchParams.get("url") || "https://postbuild.dev";
135
- const directoryIds = Array.isArray(body.directoryIds) ? body.directoryIds : [];
136
- const result = await dispatchDirectories({ targetUrl, directoryIds });
137
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
138
- res.end(JSON.stringify(result));
139
- return;
140
- }
141
-
142
- if (parsed.pathname === "/api/checkout") {
143
- const body = req.method === "POST" ? await readJsonBody(req) : {};
144
- const planId = body.plan || parsed.searchParams.get("plan") || "launch_pass";
145
- const targetDomain = body.domain || parsed.searchParams.get("domain") || "postbuild.dev";
146
- const customerEmail = body.email || parsed.searchParams.get("email") || "";
147
- const coupon = body.coupon || parsed.searchParams.get("coupon") || "";
148
- const origin = `http://${host}`;
149
- const result = await createStripeCheckout({ planId, targetDomain, customerEmail, coupon, origin });
150
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
151
- res.end(JSON.stringify(result));
152
- return;
153
- }
154
-
155
- if (parsed.pathname === "/api/portal") {
156
- const body = req.method === "POST" ? await readJsonBody(req) : {};
157
- const action = body.action || parsed.searchParams.get("action") || "";
158
- const token = body.token || parsed.searchParams.get("token") || "";
159
- const subscriptionId = body.subscriptionId || parsed.searchParams.get("subscriptionId") || "";
160
- if (action === "setup_branding") {
161
- const iconPath = path.join(__dirname, "public", "brand", "postbuild-icon.png");
162
- const iconBuf = fs.existsSync(iconPath) ? fs.readFileSync(iconPath) : null;
163
- const brandingResult = await configureStripeBrandingAndPortal(iconBuf);
164
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
165
- res.end(JSON.stringify(brandingResult));
166
- return;
167
- }
168
-
169
- const verified = verifyLicenseToken(token);
170
- if (!verified.valid || !verified.email) {
171
- res.writeHead(401, { "Content-Type": "application/json; charset=utf-8" });
172
- res.end(
173
- JSON.stringify({
174
- ok: false,
175
- error: "Unauthorized: A verified pb_live_ license token is required to view receipts or manage billing."
176
- })
177
- );
178
- return;
179
- }
180
-
181
- if (action === "billing_summary") {
182
- const summary = await getCustomerBillingSummary({ customerEmail: verified.email });
183
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
184
- res.end(JSON.stringify(summary));
185
- return;
186
- }
187
- if (action === "cancel_subscription") {
188
- const cancelled = await cancelCustomerSubscription({
189
- subscriptionId,
190
- customerEmail: verified.email
191
- });
192
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
193
- res.end(JSON.stringify(cancelled));
194
- return;
195
- }
196
- const returnUrl = `http://${host}/?tab=account`;
197
- const result = await createCustomerPortalSession({ customerEmail: verified.email, returnUrl });
198
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
199
- res.end(JSON.stringify(result));
200
- return;
201
- }
202
-
203
- if (parsed.pathname === "/api/webhook" && req.method === "POST") {
204
- const rawBody = await readRawBuffer(req);
205
- const sig = req.headers["stripe-signature"] || "";
206
- const result = await verifyAndHandleStripeWebhook(rawBody, sig);
207
- res.writeHead(result.status || 200, { "Content-Type": "application/json; charset=utf-8" });
208
- res.end(JSON.stringify(result));
209
- return;
210
- }
211
-
212
- if (parsed.pathname === "/api/auth") {
213
- const body = req.method === "POST" ? await readJsonBody(req) : {};
214
- const email = body.email || parsed.searchParams.get("email") || "";
215
- const token = body.token || parsed.searchParams.get("token") || "";
216
- const sessionId = body.sessionId || parsed.searchParams.get("session_id") || "";
217
- const otpCode = body.otpCode || parsed.searchParams.get("otpCode") || "";
218
- const challengeToken = body.challengeToken || parsed.searchParams.get("challengeToken") || "";
219
- const result = await authenticateUser({ email, token, sessionId, otpCode, challengeToken });
220
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
221
- res.end(JSON.stringify(result));
222
- return;
223
- }
224
-
225
- let safePath = parsed.pathname === "/" ? "/index.html" : parsed.pathname;
226
- let filePath = path.join(__dirname, "public", safePath);
227
- if (!fs.existsSync(filePath) && fs.existsSync(`${filePath}.html`)) {
228
- filePath = `${filePath}.html`;
229
- }
230
-
231
- if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
232
- const ext = path.extname(filePath).toLowerCase();
233
- res.writeHead(200, { "Content-Type": MIME_TYPES[ext] || "application/octet-stream" });
234
- fs.createReadStream(filePath).pipe(res);
235
- return;
236
- }
237
-
238
- // Fallback SPA route
239
- const indexHtml = path.join(__dirname, "public", "index.html");
240
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
241
- fs.createReadStream(indexHtml).pipe(res);
242
- } catch (err) {
243
- res.writeHead(500, { "Content-Type": "application/json; charset=utf-8" });
244
- res.end(JSON.stringify({ error: err.message }));
245
- }
246
- });
247
-
248
- server.listen(PORT, "0.0.0.0", () => {
249
- console.log(`\n postbuild (postbuild.dev) live at:`);
250
- console.log(` -> Local: http://localhost:${PORT}`);
251
- console.log(` -> Tailscale: http://100.117.7.27:${PORT}\n`);
252
- });