@rubytech/create-maxy-code 0.1.119 → 0.1.120
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/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +93 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts +12 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +34 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -1
- package/payload/platform/plugins/docs/references/visitor-graph.md +82 -0
- package/payload/platform/plugins/memory/references/schema-base.md +44 -0
- package/payload/server/{chunk-FJCI6X3H.js → chunk-HCYM5FLU.js} +2 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/privacy.html +66 -0
- package/payload/server/public/v.js +191 -0
- package/payload/server/server.js +473 -85
package/payload/server/server.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
PLATFORM_ROOT,
|
|
14
14
|
RFB_PORT,
|
|
15
15
|
USERS_FILE,
|
|
16
|
+
VISITOR_TOKEN_SECRET_FILE,
|
|
16
17
|
VNC_DISPLAY,
|
|
17
18
|
WEBSOCKIFY_PORT,
|
|
18
19
|
autoDeliverPremiumPlugins,
|
|
@@ -82,7 +83,7 @@ import {
|
|
|
82
83
|
vncLog,
|
|
83
84
|
walkPremiumBundles,
|
|
84
85
|
writeAdminUserAndPerson
|
|
85
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-HCYM5FLU.js";
|
|
86
87
|
import {
|
|
87
88
|
__commonJS,
|
|
88
89
|
__toESM
|
|
@@ -587,8 +588,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
587
588
|
};
|
|
588
589
|
|
|
589
590
|
// server/index.ts
|
|
590
|
-
import { readFileSync as
|
|
591
|
-
import { resolve as resolve21, join as
|
|
591
|
+
import { readFileSync as readFileSync21, existsSync as existsSync24, watchFile } from "fs";
|
|
592
|
+
import { resolve as resolve21, join as join15, basename as basename5 } from "path";
|
|
592
593
|
import { homedir as homedir4 } from "os";
|
|
593
594
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
594
595
|
|
|
@@ -2025,8 +2026,8 @@ import { resolve } from "path";
|
|
|
2025
2026
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2026
2027
|
var cache = /* @__PURE__ */ new Map();
|
|
2027
2028
|
function enumerateValidAccountIds(accountsDir) {
|
|
2028
|
-
const
|
|
2029
|
-
if (
|
|
2029
|
+
const cached3 = cache.get(accountsDir);
|
|
2030
|
+
if (cached3 !== void 0) return cached3;
|
|
2030
2031
|
let names;
|
|
2031
2032
|
try {
|
|
2032
2033
|
names = readdirSync(accountsDir);
|
|
@@ -2913,8 +2914,8 @@ function watchForDisconnect(conn) {
|
|
|
2913
2914
|
});
|
|
2914
2915
|
}
|
|
2915
2916
|
async function getGroupMeta(conn, jid) {
|
|
2916
|
-
const
|
|
2917
|
-
if (
|
|
2917
|
+
const cached3 = conn.groupMetaCache.get(jid);
|
|
2918
|
+
if (cached3 && cached3.expires > Date.now()) return cached3;
|
|
2918
2919
|
if (!conn.sock) return null;
|
|
2919
2920
|
console.error(`${TAG12} group metadata cache miss for ${jid}, fetching from Baileys account=${conn.accountId}`);
|
|
2920
2921
|
try {
|
|
@@ -12174,6 +12175,83 @@ app34.get("/:rel{.*}", (c) => {
|
|
|
12174
12175
|
});
|
|
12175
12176
|
var sites_default = app34;
|
|
12176
12177
|
|
|
12178
|
+
// app/lib/visitor-token.ts
|
|
12179
|
+
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
12180
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync7 } from "fs";
|
|
12181
|
+
import { dirname as dirname5 } from "path";
|
|
12182
|
+
var TOKEN_PREFIX = "v1.";
|
|
12183
|
+
var SECRET_BYTES = 32;
|
|
12184
|
+
var DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
12185
|
+
var cachedSecret = null;
|
|
12186
|
+
function getSecret() {
|
|
12187
|
+
if (cachedSecret) return cachedSecret;
|
|
12188
|
+
try {
|
|
12189
|
+
const hex2 = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
12190
|
+
if (hex2.length === SECRET_BYTES * 2) {
|
|
12191
|
+
cachedSecret = Buffer.from(hex2, "hex");
|
|
12192
|
+
return cachedSecret;
|
|
12193
|
+
}
|
|
12194
|
+
} catch {
|
|
12195
|
+
}
|
|
12196
|
+
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
12197
|
+
try {
|
|
12198
|
+
mkdirSync4(dirname5(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
12199
|
+
writeFileSync7(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
12200
|
+
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
12201
|
+
} catch {
|
|
12202
|
+
}
|
|
12203
|
+
const hex = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
12204
|
+
cachedSecret = Buffer.from(hex, "hex");
|
|
12205
|
+
return cachedSecret;
|
|
12206
|
+
}
|
|
12207
|
+
function base64urlEncode(buf) {
|
|
12208
|
+
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
12209
|
+
}
|
|
12210
|
+
function base64urlDecode(s) {
|
|
12211
|
+
const padded = s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - s.length % 4) % 4);
|
|
12212
|
+
return Buffer.from(padded, "base64");
|
|
12213
|
+
}
|
|
12214
|
+
function signPayload(payloadJson, secret) {
|
|
12215
|
+
return base64urlEncode(createHmac("sha256", secret).update(payloadJson).digest());
|
|
12216
|
+
}
|
|
12217
|
+
function verifyVisitorToken(token, now = Date.now()) {
|
|
12218
|
+
if (!token || typeof token !== "string" || !token.startsWith(TOKEN_PREFIX)) {
|
|
12219
|
+
return { ok: false, reason: "malformed" };
|
|
12220
|
+
}
|
|
12221
|
+
const rest = token.slice(TOKEN_PREFIX.length);
|
|
12222
|
+
const dot = rest.indexOf(".");
|
|
12223
|
+
if (dot === -1) return { ok: false, reason: "malformed" };
|
|
12224
|
+
const payloadB64 = rest.slice(0, dot);
|
|
12225
|
+
const sigB64 = rest.slice(dot + 1);
|
|
12226
|
+
if (!payloadB64 || !sigB64) return { ok: false, reason: "malformed" };
|
|
12227
|
+
let payloadJson;
|
|
12228
|
+
try {
|
|
12229
|
+
payloadJson = base64urlDecode(payloadB64).toString("utf-8");
|
|
12230
|
+
} catch {
|
|
12231
|
+
return { ok: false, reason: "malformed" };
|
|
12232
|
+
}
|
|
12233
|
+
const expected = signPayload(payloadJson, getSecret());
|
|
12234
|
+
const expectedBuf = Buffer.from(expected, "utf-8");
|
|
12235
|
+
const provBuf = Buffer.from(sigB64, "utf-8");
|
|
12236
|
+
if (expectedBuf.length !== provBuf.length || !timingSafeEqual(expectedBuf, provBuf)) {
|
|
12237
|
+
return { ok: false, reason: "bad-sig" };
|
|
12238
|
+
}
|
|
12239
|
+
let payload;
|
|
12240
|
+
try {
|
|
12241
|
+
payload = JSON.parse(payloadJson);
|
|
12242
|
+
} catch {
|
|
12243
|
+
return { ok: false, reason: "malformed" };
|
|
12244
|
+
}
|
|
12245
|
+
if (!payload || typeof payload !== "object" || typeof payload.p !== "string" || typeof payload.e !== "number") {
|
|
12246
|
+
return { ok: false, reason: "malformed" };
|
|
12247
|
+
}
|
|
12248
|
+
const { p, e } = payload;
|
|
12249
|
+
if (now > e) return { ok: false, reason: "expired" };
|
|
12250
|
+
return { ok: true, personId: p, expiryMs: e };
|
|
12251
|
+
}
|
|
12252
|
+
var VISITOR_COOKIE_NAME = "mxy_v";
|
|
12253
|
+
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
12254
|
+
|
|
12177
12255
|
// server/routes/listings.ts
|
|
12178
12256
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
12179
12257
|
var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
@@ -12182,6 +12260,7 @@ app35.get("/:slug/click", async (c) => {
|
|
|
12182
12260
|
const slug = c.req.param("slug") ?? "";
|
|
12183
12261
|
const rawSession = c.req.query("session") ?? "";
|
|
12184
12262
|
const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
|
|
12263
|
+
const tokenParam = c.req.query("v") ?? "";
|
|
12185
12264
|
if (!SAFE_SLUG_RE.test(slug)) {
|
|
12186
12265
|
console.error(`[property-card-click] reject reason=bad-slug-shape slug=${JSON.stringify(slug)} status=400`);
|
|
12187
12266
|
return c.text("Bad Request", 400);
|
|
@@ -12218,11 +12297,318 @@ app35.get("/:slug/click", async (c) => {
|
|
|
12218
12297
|
console.error(`[property-card-click] reject reason=listing-not-found slug=${slug} sessionKey=${sessionKey} accountId=${account.accountId} status=404`);
|
|
12219
12298
|
return c.text("Not Found", 404);
|
|
12220
12299
|
}
|
|
12300
|
+
if (tokenParam) {
|
|
12301
|
+
const verify = verifyVisitorToken(tokenParam);
|
|
12302
|
+
if (verify.ok) {
|
|
12303
|
+
c.header(
|
|
12304
|
+
"Set-Cookie",
|
|
12305
|
+
`${VISITOR_COOKIE_NAME}=${tokenParam}; Max-Age=${VISITOR_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Lax; Secure; HttpOnly`
|
|
12306
|
+
);
|
|
12307
|
+
console.log(`[token-bind] sessionKey=${sessionKey} person=${verify.personId} listingSlug=${slug}`);
|
|
12308
|
+
} else {
|
|
12309
|
+
console.error(`[token-bind] reject reason=${verify.reason} sessionKey=${sessionKey} listingSlug=${slug}`);
|
|
12310
|
+
}
|
|
12311
|
+
}
|
|
12221
12312
|
console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
12222
12313
|
return c.redirect(pageUrl, 302);
|
|
12223
12314
|
});
|
|
12224
12315
|
var listings_default = app35;
|
|
12225
12316
|
|
|
12317
|
+
// app/lib/brand-config.ts
|
|
12318
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
|
|
12319
|
+
import { join as join14 } from "path";
|
|
12320
|
+
var cached2 = null;
|
|
12321
|
+
var cachedAttempted = false;
|
|
12322
|
+
function readBrandConfig() {
|
|
12323
|
+
if (cachedAttempted) return cached2;
|
|
12324
|
+
cachedAttempted = true;
|
|
12325
|
+
const platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
12326
|
+
if (!platformRoot) return null;
|
|
12327
|
+
const brandPath = join14(platformRoot, "config", "brand.json");
|
|
12328
|
+
if (!existsSync22(brandPath)) return null;
|
|
12329
|
+
try {
|
|
12330
|
+
cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
|
|
12331
|
+
return cached2;
|
|
12332
|
+
} catch {
|
|
12333
|
+
return null;
|
|
12334
|
+
}
|
|
12335
|
+
}
|
|
12336
|
+
|
|
12337
|
+
// server/routes/visitor-event.ts
|
|
12338
|
+
var app36 = new Hono();
|
|
12339
|
+
var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
|
|
12340
|
+
var buckets = /* @__PURE__ */ new Map();
|
|
12341
|
+
var RATE_LIMIT = 60;
|
|
12342
|
+
var RATE_WINDOW_MS = 6e4;
|
|
12343
|
+
function rateLimit(ip, now = Date.now()) {
|
|
12344
|
+
const b = buckets.get(ip);
|
|
12345
|
+
if (!b || now >= b.resetAt) {
|
|
12346
|
+
buckets.set(ip, { count: 1, resetAt: now + RATE_WINDOW_MS });
|
|
12347
|
+
return { ok: true, resetIn: RATE_WINDOW_MS };
|
|
12348
|
+
}
|
|
12349
|
+
b.count += 1;
|
|
12350
|
+
if (b.count > RATE_LIMIT) return { ok: false, resetIn: b.resetAt - now };
|
|
12351
|
+
return { ok: true, resetIn: b.resetAt - now };
|
|
12352
|
+
}
|
|
12353
|
+
setInterval(() => {
|
|
12354
|
+
const now = Date.now();
|
|
12355
|
+
for (const [ip, b] of buckets) {
|
|
12356
|
+
if (now >= b.resetAt) buckets.delete(ip);
|
|
12357
|
+
}
|
|
12358
|
+
}, 5 * RATE_WINDOW_MS).unref?.();
|
|
12359
|
+
var VALID_TYPES = /* @__PURE__ */ new Set(["pageview", "click", "scroll", "dwell"]);
|
|
12360
|
+
var ID_RE = /^[A-Za-z0-9_-]{6,128}$/;
|
|
12361
|
+
function parseEvent(body) {
|
|
12362
|
+
if (!body || typeof body !== "object") return null;
|
|
12363
|
+
const e = body;
|
|
12364
|
+
if (typeof e.type !== "string" || !VALID_TYPES.has(e.type)) return null;
|
|
12365
|
+
if (typeof e.sid !== "string" || !ID_RE.test(e.sid)) return null;
|
|
12366
|
+
if (typeof e.vid !== "string" || !ID_RE.test(e.vid)) return null;
|
|
12367
|
+
if (typeof e.url !== "string" || e.url.length === 0 || e.url.length > 2048) return null;
|
|
12368
|
+
if (typeof e.ts !== "number" || !Number.isFinite(e.ts)) return null;
|
|
12369
|
+
return e;
|
|
12370
|
+
}
|
|
12371
|
+
function getCookie(c, name) {
|
|
12372
|
+
const raw = c.req.header("cookie");
|
|
12373
|
+
if (!raw) return null;
|
|
12374
|
+
for (const part of raw.split(";")) {
|
|
12375
|
+
const [k, ...rest] = part.trim().split("=");
|
|
12376
|
+
if (k === name) return rest.join("=");
|
|
12377
|
+
}
|
|
12378
|
+
return null;
|
|
12379
|
+
}
|
|
12380
|
+
function getOrigin(c) {
|
|
12381
|
+
return c.req.header("origin") ?? c.req.header("referer") ?? "";
|
|
12382
|
+
}
|
|
12383
|
+
function setCorsHeaders(c, origin) {
|
|
12384
|
+
if (origin) c.header("Access-Control-Allow-Origin", origin);
|
|
12385
|
+
c.header("Access-Control-Allow-Credentials", "true");
|
|
12386
|
+
c.header("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
12387
|
+
c.header("Access-Control-Allow-Headers", "Content-Type");
|
|
12388
|
+
c.header("Vary", "Origin");
|
|
12389
|
+
}
|
|
12390
|
+
function originAllowed(origin, allowlist) {
|
|
12391
|
+
if (!allowlist || allowlist.length === 0) return true;
|
|
12392
|
+
if (!origin) return true;
|
|
12393
|
+
try {
|
|
12394
|
+
const u = new URL(origin);
|
|
12395
|
+
const host = u.host;
|
|
12396
|
+
return allowlist.some((entry) => {
|
|
12397
|
+
if (entry === host) return true;
|
|
12398
|
+
if (entry.startsWith("*.")) return host.endsWith(entry.slice(1));
|
|
12399
|
+
return false;
|
|
12400
|
+
});
|
|
12401
|
+
} catch {
|
|
12402
|
+
return false;
|
|
12403
|
+
}
|
|
12404
|
+
}
|
|
12405
|
+
app36.options("/event", (c) => {
|
|
12406
|
+
const origin = getOrigin(c);
|
|
12407
|
+
setCorsHeaders(c, origin);
|
|
12408
|
+
return c.body(null, 204);
|
|
12409
|
+
});
|
|
12410
|
+
app36.post("/event", async (c) => {
|
|
12411
|
+
const origin = getOrigin(c);
|
|
12412
|
+
setCorsHeaders(c, origin);
|
|
12413
|
+
const ua = c.req.header("user-agent") ?? "";
|
|
12414
|
+
if (BOT_UA_RE.test(ua)) {
|
|
12415
|
+
console.error(`[v-event-error] reason=bot-ua ua=${JSON.stringify(ua.slice(0, 120))}`);
|
|
12416
|
+
return c.body(null, 204);
|
|
12417
|
+
}
|
|
12418
|
+
const ip = c.req.header("x-forwarded-for")?.split(",")[0].trim() || c.req.header("x-real-ip") || "unknown";
|
|
12419
|
+
const rl = rateLimit(ip);
|
|
12420
|
+
if (!rl.ok) {
|
|
12421
|
+
console.error(`[v-event-error] reason=rate-limit ip=${ip} resetInMs=${rl.resetIn}`);
|
|
12422
|
+
c.header("Retry-After", String(Math.ceil(rl.resetIn / 1e3)));
|
|
12423
|
+
return c.text("Too Many Requests", 429);
|
|
12424
|
+
}
|
|
12425
|
+
const account = resolveAccount();
|
|
12426
|
+
if (!account) {
|
|
12427
|
+
console.error(`[v-event-error] reason=no-account ip=${ip}`);
|
|
12428
|
+
return c.text("Not Found", 404);
|
|
12429
|
+
}
|
|
12430
|
+
const brand = readBrandConfig();
|
|
12431
|
+
if (!originAllowed(origin, brand?.publishedSiteOrigins)) {
|
|
12432
|
+
console.error(`[v-event-error] reason=origin-mismatch origin=${JSON.stringify(origin)} ip=${ip}`);
|
|
12433
|
+
return c.text("Forbidden", 403);
|
|
12434
|
+
}
|
|
12435
|
+
let raw;
|
|
12436
|
+
try {
|
|
12437
|
+
raw = await c.req.json();
|
|
12438
|
+
} catch {
|
|
12439
|
+
console.error(`[v-event-error] reason=bad-json ip=${ip}`);
|
|
12440
|
+
return c.text("Bad Request", 400);
|
|
12441
|
+
}
|
|
12442
|
+
const event = parseEvent(raw);
|
|
12443
|
+
if (!event) {
|
|
12444
|
+
console.error(`[v-event-error] reason=bad-event ip=${ip}`);
|
|
12445
|
+
return c.text("Bad Request", 400);
|
|
12446
|
+
}
|
|
12447
|
+
const cookieVal = getCookie(c, VISITOR_COOKIE_NAME);
|
|
12448
|
+
let personId = null;
|
|
12449
|
+
if (cookieVal) {
|
|
12450
|
+
const v = verifyVisitorToken(cookieVal);
|
|
12451
|
+
if (v.ok) {
|
|
12452
|
+
personId = v.personId;
|
|
12453
|
+
} else {
|
|
12454
|
+
console.error(`[token-bind] reject reason=${v.reason} sessionId=${event.sid} via=cookie`);
|
|
12455
|
+
}
|
|
12456
|
+
}
|
|
12457
|
+
const session = getSession();
|
|
12458
|
+
try {
|
|
12459
|
+
await writeEvent({
|
|
12460
|
+
session,
|
|
12461
|
+
accountId: account.accountId,
|
|
12462
|
+
personId,
|
|
12463
|
+
event,
|
|
12464
|
+
origin
|
|
12465
|
+
});
|
|
12466
|
+
} catch (err) {
|
|
12467
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12468
|
+
console.error(`[v-event-error] reason=neo4j sessionId=${event.sid} err=${JSON.stringify(message)}`);
|
|
12469
|
+
await session.close().catch(() => {
|
|
12470
|
+
});
|
|
12471
|
+
return c.text("Service Unavailable", 503);
|
|
12472
|
+
} finally {
|
|
12473
|
+
await session.close().catch(() => {
|
|
12474
|
+
});
|
|
12475
|
+
}
|
|
12476
|
+
console.log(
|
|
12477
|
+
`[visitor-event] type=${event.type} sessionId=${event.sid} visitorId=${event.vid} personId=${personId ?? "-"} path=${event.path ?? ""} label=${event.label ?? "-"} depth=${event.depth ?? "-"} ms=${event.ms ?? "-"}`
|
|
12478
|
+
);
|
|
12479
|
+
return c.body(null, 204);
|
|
12480
|
+
});
|
|
12481
|
+
async function writeEvent(opts) {
|
|
12482
|
+
const { session, accountId, personId, event } = opts;
|
|
12483
|
+
const occurredAt = new Date(event.ts).toISOString();
|
|
12484
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12485
|
+
if (event.type === "pageview") {
|
|
12486
|
+
await session.run(
|
|
12487
|
+
`MERGE (s:Session {accountId: $accountId, sessionId: $sid})
|
|
12488
|
+
ON CREATE SET s.startedAt = datetime($occurredAt), s.lastSeenAt = datetime($occurredAt)
|
|
12489
|
+
ON MATCH SET s.lastSeenAt = datetime($occurredAt)
|
|
12490
|
+
MERGE (pg:Page {accountId: $accountId, url: $url})
|
|
12491
|
+
ON CREATE SET pg.firstSeenAt = datetime($occurredAt), pg.title = $title, pg.site = $site
|
|
12492
|
+
ON MATCH SET pg.title = $title
|
|
12493
|
+
CREATE (pv:PageView {
|
|
12494
|
+
accountId: $accountId,
|
|
12495
|
+
pageViewId: $pvId,
|
|
12496
|
+
occurredAt: datetime($occurredAt),
|
|
12497
|
+
referrer: $referrer,
|
|
12498
|
+
path: $path,
|
|
12499
|
+
createdAt: datetime($now)
|
|
12500
|
+
})
|
|
12501
|
+
MERGE (s)-[:HAS_EVENT]->(pv)
|
|
12502
|
+
MERGE (pv)-[:OF_PAGE]->(pg)
|
|
12503
|
+
WITH s, pv
|
|
12504
|
+
OPTIONAL MATCH (l:Listing {accountId: $accountId, slug: $site})
|
|
12505
|
+
WHERE l.scope = 'public'
|
|
12506
|
+
FOREACH (_ IN CASE WHEN l IS NULL THEN [] ELSE [1] END |
|
|
12507
|
+
MERGE (pv)-[:OF_LISTING]->(l)
|
|
12508
|
+
)`,
|
|
12509
|
+
{
|
|
12510
|
+
accountId,
|
|
12511
|
+
sid: event.sid,
|
|
12512
|
+
url: event.url,
|
|
12513
|
+
title: event.title ?? "",
|
|
12514
|
+
site: event.site ?? "",
|
|
12515
|
+
pvId: event.pvId ?? `${event.sid}_${event.ts}`,
|
|
12516
|
+
path: event.path ?? "",
|
|
12517
|
+
referrer: event.referrer ?? "",
|
|
12518
|
+
occurredAt,
|
|
12519
|
+
now,
|
|
12520
|
+
personId
|
|
12521
|
+
}
|
|
12522
|
+
);
|
|
12523
|
+
} else if (event.type === "click") {
|
|
12524
|
+
await session.run(
|
|
12525
|
+
`MERGE (s:Session {accountId: $accountId, sessionId: $sid})
|
|
12526
|
+
ON CREATE SET s.startedAt = datetime($occurredAt), s.lastSeenAt = datetime($occurredAt)
|
|
12527
|
+
ON MATCH SET s.lastSeenAt = datetime($occurredAt)
|
|
12528
|
+
CREATE (cl:Click {
|
|
12529
|
+
accountId: $accountId,
|
|
12530
|
+
label: $label,
|
|
12531
|
+
href: $href,
|
|
12532
|
+
pageViewId: $pvId,
|
|
12533
|
+
occurredAt: datetime($occurredAt)
|
|
12534
|
+
})
|
|
12535
|
+
MERGE (s)-[:HAS_EVENT]->(cl)`,
|
|
12536
|
+
{
|
|
12537
|
+
accountId,
|
|
12538
|
+
sid: event.sid,
|
|
12539
|
+
label: (event.label ?? "unknown").slice(0, 64),
|
|
12540
|
+
href: (event.href ?? "").slice(0, 2048),
|
|
12541
|
+
pvId: event.pvId ?? "",
|
|
12542
|
+
occurredAt
|
|
12543
|
+
}
|
|
12544
|
+
);
|
|
12545
|
+
} else if (event.type === "scroll") {
|
|
12546
|
+
await session.run(
|
|
12547
|
+
`MERGE (s:Session {accountId: $accountId, sessionId: $sid})
|
|
12548
|
+
ON CREATE SET s.startedAt = datetime($occurredAt), s.lastSeenAt = datetime($occurredAt)
|
|
12549
|
+
ON MATCH SET s.lastSeenAt = datetime($occurredAt)
|
|
12550
|
+
MERGE (sm:ScrollMilestone {accountId: $accountId, sessionId: $sid, pageViewId: $pvId})
|
|
12551
|
+
ON CREATE SET sm.maxDepth = $depth, sm.firstAt = datetime($occurredAt), sm.lastAt = datetime($occurredAt)
|
|
12552
|
+
ON MATCH SET sm.maxDepth = CASE WHEN sm.maxDepth >= $depth THEN sm.maxDepth ELSE $depth END,
|
|
12553
|
+
sm.lastAt = datetime($occurredAt)
|
|
12554
|
+
MERGE (s)-[:HAS_EVENT]->(sm)`,
|
|
12555
|
+
{ accountId, sid: event.sid, pvId: event.pvId ?? "", depth: event.depth ?? 0, occurredAt }
|
|
12556
|
+
);
|
|
12557
|
+
} else if (event.type === "dwell") {
|
|
12558
|
+
await session.run(
|
|
12559
|
+
`MATCH (s:Session {accountId: $accountId, sessionId: $sid})
|
|
12560
|
+
MATCH (pv:PageView {accountId: $accountId, pageViewId: $pvId})
|
|
12561
|
+
SET pv.dwellMs = coalesce(pv.dwellMs, 0) + $ms,
|
|
12562
|
+
pv.dwellLastAt = datetime($occurredAt)
|
|
12563
|
+
SET s.lastSeenAt = datetime($occurredAt)`,
|
|
12564
|
+
{ accountId, sid: event.sid, pvId: event.pvId ?? "", ms: event.ms ?? 0, occurredAt }
|
|
12565
|
+
);
|
|
12566
|
+
}
|
|
12567
|
+
if (personId) {
|
|
12568
|
+
await session.run(
|
|
12569
|
+
`MATCH (p:Person {accountId: $accountId}) WHERE elementId(p) = $personId
|
|
12570
|
+
MATCH (s:Session {accountId: $accountId, sessionId: $sid})
|
|
12571
|
+
MERGE (p)-[:VISITED {accountId: $accountId}]->(s)`,
|
|
12572
|
+
{ accountId, personId, sid: event.sid }
|
|
12573
|
+
);
|
|
12574
|
+
const mergeResult = await session.run(
|
|
12575
|
+
`MATCH (p:Person {accountId: $accountId}) WHERE elementId(p) = $personId
|
|
12576
|
+
OPTIONAL MATCH (av:AnonVisitor {accountId: $accountId, visitorId: $vid})
|
|
12577
|
+
WITH p, av
|
|
12578
|
+
WHERE av IS NOT NULL
|
|
12579
|
+
MERGE (p)-[:OWNS_VISITOR {accountId: $accountId}]->(av)
|
|
12580
|
+
WITH p, av
|
|
12581
|
+
OPTIONAL MATCH (av)-[r:VISITED]->(prior:Session)
|
|
12582
|
+
FOREACH (_ IN CASE WHEN prior IS NULL THEN [] ELSE [1] END |
|
|
12583
|
+
MERGE (p)-[:VISITED {accountId: $accountId}]->(prior)
|
|
12584
|
+
)
|
|
12585
|
+
WITH r, prior
|
|
12586
|
+
FOREACH (_ IN CASE WHEN r IS NULL THEN [] ELSE [1] END |
|
|
12587
|
+
DELETE r
|
|
12588
|
+
)
|
|
12589
|
+
RETURN count(prior) AS reattributed`,
|
|
12590
|
+
{ accountId, personId, vid: event.vid }
|
|
12591
|
+
);
|
|
12592
|
+
const reattributed = mergeResult.records[0]?.get("reattributed") ?? 0;
|
|
12593
|
+
if (typeof reattributed === "number" ? reattributed > 0 : Number(reattributed) > 0) {
|
|
12594
|
+
console.log(
|
|
12595
|
+
`[anonvisitor-merge] anonVisitorId=${event.vid} person=${personId} sessionsReattributed=${reattributed}`
|
|
12596
|
+
);
|
|
12597
|
+
}
|
|
12598
|
+
} else {
|
|
12599
|
+
await session.run(
|
|
12600
|
+
`MERGE (av:AnonVisitor {accountId: $accountId, visitorId: $vid})
|
|
12601
|
+
ON CREATE SET av.firstSeenAt = datetime($occurredAt), av.lastSeenAt = datetime($occurredAt)
|
|
12602
|
+
ON MATCH SET av.lastSeenAt = datetime($occurredAt)
|
|
12603
|
+
WITH av
|
|
12604
|
+
MATCH (s:Session {accountId: $accountId, sessionId: $sid})
|
|
12605
|
+
MERGE (av)-[:VISITED {accountId: $accountId}]->(s)`,
|
|
12606
|
+
{ accountId, vid: event.vid, sid: event.sid, occurredAt }
|
|
12607
|
+
);
|
|
12608
|
+
}
|
|
12609
|
+
}
|
|
12610
|
+
var visitor_event_default = app36;
|
|
12611
|
+
|
|
12226
12612
|
// app/lib/graph-health.ts
|
|
12227
12613
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
12228
12614
|
var timer = null;
|
|
@@ -12365,7 +12751,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
12365
12751
|
|
|
12366
12752
|
// ../lib/entitlement/src/index.ts
|
|
12367
12753
|
import { createPublicKey, createHash as createHash3, verify as cryptoVerify } from "crypto";
|
|
12368
|
-
import { existsSync as
|
|
12754
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
|
|
12369
12755
|
import { resolve as resolve20 } from "path";
|
|
12370
12756
|
|
|
12371
12757
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -12414,7 +12800,7 @@ function resolveEntitlement(brand, account) {
|
|
|
12414
12800
|
return logResolved(implicitTrust(account), null);
|
|
12415
12801
|
}
|
|
12416
12802
|
const entitlementPath = resolve20(brand.configDir, "entitlement.json");
|
|
12417
|
-
if (!
|
|
12803
|
+
if (!existsSync23(entitlementPath)) {
|
|
12418
12804
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
12419
12805
|
}
|
|
12420
12806
|
const stat7 = statSync8(entitlementPath);
|
|
@@ -12429,7 +12815,7 @@ function resolveEntitlement(brand, account) {
|
|
|
12429
12815
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
12430
12816
|
let pubkeyPem;
|
|
12431
12817
|
try {
|
|
12432
|
-
pubkeyPem =
|
|
12818
|
+
pubkeyPem = readFileSync20(pubkeyPath(brand), "utf-8");
|
|
12433
12819
|
} catch (err) {
|
|
12434
12820
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
12435
12821
|
reason: "pubkey-missing"
|
|
@@ -12443,7 +12829,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
12443
12829
|
}
|
|
12444
12830
|
let envelope;
|
|
12445
12831
|
try {
|
|
12446
|
-
envelope = JSON.parse(
|
|
12832
|
+
envelope = JSON.parse(readFileSync20(entitlementPath, "utf-8"));
|
|
12447
12833
|
} catch {
|
|
12448
12834
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
12449
12835
|
}
|
|
@@ -12604,14 +12990,14 @@ function clientFrom(c) {
|
|
|
12604
12990
|
);
|
|
12605
12991
|
}
|
|
12606
12992
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
12607
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT7 ?
|
|
12993
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join15(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
12608
12994
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
12609
|
-
if (BRAND_JSON_PATH && !
|
|
12995
|
+
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
12610
12996
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
12611
12997
|
}
|
|
12612
|
-
if (BRAND_JSON_PATH &&
|
|
12998
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
12613
12999
|
try {
|
|
12614
|
-
const parsed = JSON.parse(
|
|
13000
|
+
const parsed = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
12615
13001
|
BRAND = { ...BRAND, ...parsed };
|
|
12616
13002
|
} catch (err) {
|
|
12617
13003
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -12630,11 +13016,11 @@ var brandLoginOpts = {
|
|
|
12630
13016
|
bodyFont: BRAND.defaultFonts?.body,
|
|
12631
13017
|
logoContainsName: !!BRAND.logoContainsName
|
|
12632
13018
|
};
|
|
12633
|
-
var ALIAS_DOMAINS_PATH =
|
|
13019
|
+
var ALIAS_DOMAINS_PATH = join15(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
12634
13020
|
function loadAliasDomains() {
|
|
12635
13021
|
try {
|
|
12636
|
-
if (!
|
|
12637
|
-
const parsed = JSON.parse(
|
|
13022
|
+
if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
|
|
13023
|
+
const parsed = JSON.parse(readFileSync21(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
12638
13024
|
if (!Array.isArray(parsed)) {
|
|
12639
13025
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
12640
13026
|
return null;
|
|
@@ -12658,9 +13044,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
12658
13044
|
function isPublicHost(host) {
|
|
12659
13045
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
12660
13046
|
}
|
|
12661
|
-
var
|
|
12662
|
-
|
|
12663
|
-
|
|
13047
|
+
var app37 = new Hono();
|
|
13048
|
+
app37.use("*", clientIpMiddleware);
|
|
13049
|
+
app37.use("*", async (c, next) => {
|
|
12664
13050
|
await next();
|
|
12665
13051
|
c.header("X-Content-Type-Options", "nosniff");
|
|
12666
13052
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -12670,7 +13056,7 @@ app36.use("*", async (c, next) => {
|
|
|
12670
13056
|
);
|
|
12671
13057
|
});
|
|
12672
13058
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
12673
|
-
|
|
13059
|
+
app37.use("*", async (c, next) => {
|
|
12674
13060
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
12675
13061
|
await next();
|
|
12676
13062
|
return;
|
|
@@ -12703,7 +13089,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
|
|
|
12703
13089
|
"/sites/"
|
|
12704
13090
|
];
|
|
12705
13091
|
var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
|
|
12706
|
-
|
|
13092
|
+
app37.use("*", async (c, next) => {
|
|
12707
13093
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12708
13094
|
if (!isPublicHost(host)) {
|
|
12709
13095
|
await next();
|
|
@@ -12743,7 +13129,7 @@ function resolveRemoteAuthOpts() {
|
|
|
12743
13129
|
return brandLoginOpts;
|
|
12744
13130
|
}
|
|
12745
13131
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
12746
|
-
|
|
13132
|
+
app37.post("/__remote-auth/login", async (c) => {
|
|
12747
13133
|
const client = clientFrom(c);
|
|
12748
13134
|
const clientIp = client.ip || "unknown";
|
|
12749
13135
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -12788,7 +13174,7 @@ app36.post("/__remote-auth/login", async (c) => {
|
|
|
12788
13174
|
}
|
|
12789
13175
|
});
|
|
12790
13176
|
});
|
|
12791
|
-
|
|
13177
|
+
app37.get("/__remote-auth/logout", (c) => {
|
|
12792
13178
|
const client = clientFrom(c);
|
|
12793
13179
|
const clientIp = client.ip || "unknown";
|
|
12794
13180
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -12801,7 +13187,7 @@ app36.get("/__remote-auth/logout", (c) => {
|
|
|
12801
13187
|
}
|
|
12802
13188
|
});
|
|
12803
13189
|
});
|
|
12804
|
-
|
|
13190
|
+
app37.post("/__remote-auth/change-password", async (c) => {
|
|
12805
13191
|
const client = clientFrom(c);
|
|
12806
13192
|
const clientIp = client.ip || "unknown";
|
|
12807
13193
|
const rateLimited = checkRateLimit(client);
|
|
@@ -12852,13 +13238,13 @@ app36.post("/__remote-auth/change-password", async (c) => {
|
|
|
12852
13238
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
12853
13239
|
}
|
|
12854
13240
|
});
|
|
12855
|
-
|
|
13241
|
+
app37.get("/__remote-auth/setup", (c) => {
|
|
12856
13242
|
if (isRemoteAuthConfigured()) {
|
|
12857
13243
|
return c.redirect("/");
|
|
12858
13244
|
}
|
|
12859
13245
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
12860
13246
|
});
|
|
12861
|
-
|
|
13247
|
+
app37.post("/__remote-auth/set-initial-password", async (c) => {
|
|
12862
13248
|
if (isRemoteAuthConfigured()) {
|
|
12863
13249
|
return c.redirect("/");
|
|
12864
13250
|
}
|
|
@@ -12896,10 +13282,10 @@ app36.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
12896
13282
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
12897
13283
|
}
|
|
12898
13284
|
});
|
|
12899
|
-
|
|
13285
|
+
app37.get("/api/remote-auth/status", (c) => {
|
|
12900
13286
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
12901
13287
|
});
|
|
12902
|
-
|
|
13288
|
+
app37.post("/api/remote-auth/set-password", async (c) => {
|
|
12903
13289
|
let body;
|
|
12904
13290
|
try {
|
|
12905
13291
|
body = await c.req.json();
|
|
@@ -12930,9 +13316,9 @@ app36.post("/api/remote-auth/set-password", async (c) => {
|
|
|
12930
13316
|
return c.json({ error: "Failed to save password" }, 500);
|
|
12931
13317
|
}
|
|
12932
13318
|
});
|
|
12933
|
-
|
|
13319
|
+
app37.route("/api/_client-error", client_error_default);
|
|
12934
13320
|
console.log("[client-error-route] mounted");
|
|
12935
|
-
|
|
13321
|
+
app37.use("*", async (c, next) => {
|
|
12936
13322
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12937
13323
|
const path2 = c.req.path;
|
|
12938
13324
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -12965,11 +13351,11 @@ app36.use("*", async (c, next) => {
|
|
|
12965
13351
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
|
|
12966
13352
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
12967
13353
|
});
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
13354
|
+
app37.route("/api/health", health_default);
|
|
13355
|
+
app37.route("/api/chat", chat_default);
|
|
13356
|
+
app37.route("/api/whatsapp", whatsapp_default);
|
|
13357
|
+
app37.route("/api/onboarding", onboarding_default);
|
|
13358
|
+
app37.route("/api/admin", admin_default);
|
|
12973
13359
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
12974
13360
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12975
13361
|
var IMAGE_MIME = {
|
|
@@ -12981,7 +13367,7 @@ var IMAGE_MIME = {
|
|
|
12981
13367
|
".svg": "image/svg+xml",
|
|
12982
13368
|
".ico": "image/x-icon"
|
|
12983
13369
|
};
|
|
12984
|
-
|
|
13370
|
+
app37.get("/agent-assets/:slug/:filename", (c) => {
|
|
12985
13371
|
const slug = c.req.param("slug");
|
|
12986
13372
|
const filename = c.req.param("filename");
|
|
12987
13373
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -13003,20 +13389,20 @@ app36.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
13003
13389
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
13004
13390
|
return c.text("Forbidden", 403);
|
|
13005
13391
|
}
|
|
13006
|
-
if (!
|
|
13392
|
+
if (!existsSync24(filePath)) {
|
|
13007
13393
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
13008
13394
|
return c.text("Not found", 404);
|
|
13009
13395
|
}
|
|
13010
13396
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13011
13397
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13012
13398
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
13013
|
-
const body =
|
|
13399
|
+
const body = readFileSync21(filePath);
|
|
13014
13400
|
return c.body(body, 200, {
|
|
13015
13401
|
"Content-Type": contentType,
|
|
13016
13402
|
"Cache-Control": "public, max-age=3600"
|
|
13017
13403
|
});
|
|
13018
13404
|
});
|
|
13019
|
-
|
|
13405
|
+
app37.get("/generated/:filename", (c) => {
|
|
13020
13406
|
const filename = c.req.param("filename");
|
|
13021
13407
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
13022
13408
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -13033,27 +13419,28 @@ app36.get("/generated/:filename", (c) => {
|
|
|
13033
13419
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
13034
13420
|
return c.text("Forbidden", 403);
|
|
13035
13421
|
}
|
|
13036
|
-
if (!
|
|
13422
|
+
if (!existsSync24(filePath)) {
|
|
13037
13423
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
13038
13424
|
return c.text("Not found", 404);
|
|
13039
13425
|
}
|
|
13040
13426
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13041
13427
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13042
13428
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
13043
|
-
const body =
|
|
13429
|
+
const body = readFileSync21(filePath);
|
|
13044
13430
|
return c.body(body, 200, {
|
|
13045
13431
|
"Content-Type": contentType,
|
|
13046
13432
|
"Cache-Control": "public, max-age=86400"
|
|
13047
13433
|
});
|
|
13048
13434
|
});
|
|
13049
|
-
|
|
13050
|
-
|
|
13435
|
+
app37.route("/sites", sites_default);
|
|
13436
|
+
app37.route("/listings", listings_default);
|
|
13437
|
+
app37.route("/v", visitor_event_default);
|
|
13051
13438
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
13052
13439
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
13053
13440
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
13054
|
-
if (BRAND_JSON_PATH &&
|
|
13441
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
13055
13442
|
try {
|
|
13056
|
-
const fullBrand = JSON.parse(
|
|
13443
|
+
const fullBrand = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
13057
13444
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
13058
13445
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
13059
13446
|
} catch {
|
|
@@ -13070,9 +13457,9 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
13070
13457
|
function readInstalledVersion() {
|
|
13071
13458
|
try {
|
|
13072
13459
|
if (!PLATFORM_ROOT7) return "unknown";
|
|
13073
|
-
const versionFile =
|
|
13074
|
-
if (!
|
|
13075
|
-
const content =
|
|
13460
|
+
const versionFile = join15(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
|
|
13461
|
+
if (!existsSync24(versionFile)) return "unknown";
|
|
13462
|
+
const content = readFileSync21(versionFile, "utf-8").trim();
|
|
13076
13463
|
return content || "unknown";
|
|
13077
13464
|
} catch {
|
|
13078
13465
|
return "unknown";
|
|
@@ -13113,7 +13500,7 @@ var clientErrorReporterScript = `<script>
|
|
|
13113
13500
|
function cachedHtml(file) {
|
|
13114
13501
|
let html = htmlCache.get(file);
|
|
13115
13502
|
if (!html) {
|
|
13116
|
-
html =
|
|
13503
|
+
html = readFileSync21(resolve21(process.cwd(), "public", file), "utf-8");
|
|
13117
13504
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
13118
13505
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
13119
13506
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -13129,26 +13516,26 @@ ${clientErrorReporterScript}
|
|
|
13129
13516
|
}
|
|
13130
13517
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
13131
13518
|
function loadBrandingCache(agentSlug) {
|
|
13132
|
-
const configDir2 =
|
|
13519
|
+
const configDir2 = join15(homedir4(), BRAND.configDir);
|
|
13133
13520
|
try {
|
|
13134
|
-
const accountJsonPath =
|
|
13135
|
-
if (!
|
|
13136
|
-
const account = JSON.parse(
|
|
13521
|
+
const accountJsonPath = join15(configDir2, "account.json");
|
|
13522
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13523
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
13137
13524
|
const accountId = account.accountId;
|
|
13138
13525
|
if (!accountId) return null;
|
|
13139
|
-
const cachePath =
|
|
13140
|
-
if (!
|
|
13141
|
-
return JSON.parse(
|
|
13526
|
+
const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
13527
|
+
if (!existsSync24(cachePath)) return null;
|
|
13528
|
+
return JSON.parse(readFileSync21(cachePath, "utf-8"));
|
|
13142
13529
|
} catch {
|
|
13143
13530
|
return null;
|
|
13144
13531
|
}
|
|
13145
13532
|
}
|
|
13146
13533
|
function resolveDefaultSlug() {
|
|
13147
13534
|
try {
|
|
13148
|
-
const configDir2 =
|
|
13149
|
-
const accountJsonPath =
|
|
13150
|
-
if (!
|
|
13151
|
-
const account = JSON.parse(
|
|
13535
|
+
const configDir2 = join15(homedir4(), BRAND.configDir);
|
|
13536
|
+
const accountJsonPath = join15(configDir2, "account.json");
|
|
13537
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13538
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
13152
13539
|
return account.defaultAgent || null;
|
|
13153
13540
|
} catch {
|
|
13154
13541
|
return null;
|
|
@@ -13157,11 +13544,11 @@ function resolveDefaultSlug() {
|
|
|
13157
13544
|
function brandedPublicHtml(agentSlug) {
|
|
13158
13545
|
const baseHtml = cachedHtml("public.html");
|
|
13159
13546
|
if (!agentSlug) return baseHtml;
|
|
13160
|
-
const
|
|
13547
|
+
const cached3 = brandedHtmlCache.get(agentSlug);
|
|
13161
13548
|
const branding = loadBrandingCache(agentSlug);
|
|
13162
13549
|
if (!branding) return baseHtml;
|
|
13163
13550
|
const brandHash = JSON.stringify(branding).length;
|
|
13164
|
-
if (
|
|
13551
|
+
if (cached3 && cached3.mtime === brandHash) return cached3.html;
|
|
13165
13552
|
const title = escapeHtml(branding.name);
|
|
13166
13553
|
const description = branding.tagline ? escapeHtml(branding.tagline) : "";
|
|
13167
13554
|
const themeColor = branding.primaryColor || "";
|
|
@@ -13184,7 +13571,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
13184
13571
|
function escapeHtml(s) {
|
|
13185
13572
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
13186
13573
|
}
|
|
13187
|
-
|
|
13574
|
+
app37.get("/", (c) => {
|
|
13188
13575
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13189
13576
|
if (isPublicHost(host)) {
|
|
13190
13577
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -13192,12 +13579,12 @@ app36.get("/", (c) => {
|
|
|
13192
13579
|
}
|
|
13193
13580
|
return c.html(cachedHtml("index.html"));
|
|
13194
13581
|
});
|
|
13195
|
-
|
|
13582
|
+
app37.get("/public", (c) => {
|
|
13196
13583
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13197
13584
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13198
13585
|
return c.html(cachedHtml("public.html"));
|
|
13199
13586
|
});
|
|
13200
|
-
|
|
13587
|
+
app37.get("/chat", (c) => {
|
|
13201
13588
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13202
13589
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13203
13590
|
return c.html(cachedHtml("public.html"));
|
|
@@ -13216,12 +13603,12 @@ async function logViewerFetch(c, next) {
|
|
|
13216
13603
|
duration_ms: Date.now() - start
|
|
13217
13604
|
});
|
|
13218
13605
|
}
|
|
13219
|
-
|
|
13220
|
-
|
|
13221
|
-
|
|
13606
|
+
app37.use("/vnc-viewer.html", logViewerFetch);
|
|
13607
|
+
app37.use("/vnc-popout.html", logViewerFetch);
|
|
13608
|
+
app37.get("/vnc-popout.html", (c) => {
|
|
13222
13609
|
let html = htmlCache.get("vnc-popout.html");
|
|
13223
13610
|
if (!html) {
|
|
13224
|
-
html =
|
|
13611
|
+
html = readFileSync21(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
13225
13612
|
const name = escapeHtml(BRAND.productName);
|
|
13226
13613
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
13227
13614
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -13231,7 +13618,7 @@ app36.get("/vnc-popout.html", (c) => {
|
|
|
13231
13618
|
}
|
|
13232
13619
|
return c.html(html);
|
|
13233
13620
|
});
|
|
13234
|
-
|
|
13621
|
+
app37.post("/api/vnc/client-event", async (c) => {
|
|
13235
13622
|
let body;
|
|
13236
13623
|
try {
|
|
13237
13624
|
body = await c.req.json();
|
|
@@ -13252,25 +13639,25 @@ app36.post("/api/vnc/client-event", async (c) => {
|
|
|
13252
13639
|
});
|
|
13253
13640
|
return c.json({ ok: true });
|
|
13254
13641
|
});
|
|
13255
|
-
|
|
13642
|
+
app37.get("/g/:slug", (c) => {
|
|
13256
13643
|
return c.html(brandedPublicHtml());
|
|
13257
13644
|
});
|
|
13258
|
-
|
|
13645
|
+
app37.get("/graph", (c) => {
|
|
13259
13646
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13260
13647
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13261
13648
|
return c.html(cachedHtml("graph.html"));
|
|
13262
13649
|
});
|
|
13263
|
-
|
|
13650
|
+
app37.get("/sessions", (c) => {
|
|
13264
13651
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13265
13652
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13266
13653
|
return c.html(cachedHtml("sessions.html"));
|
|
13267
13654
|
});
|
|
13268
|
-
|
|
13655
|
+
app37.get("/data", (c) => {
|
|
13269
13656
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13270
13657
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13271
13658
|
return c.html(cachedHtml("data.html"));
|
|
13272
13659
|
});
|
|
13273
|
-
|
|
13660
|
+
app37.get("/:slug", async (c, next) => {
|
|
13274
13661
|
const slug = c.req.param("slug");
|
|
13275
13662
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
13276
13663
|
const branding = loadBrandingCache(slug);
|
|
@@ -13280,15 +13667,15 @@ app36.get("/:slug", async (c, next) => {
|
|
|
13280
13667
|
await next();
|
|
13281
13668
|
});
|
|
13282
13669
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
13283
|
-
|
|
13670
|
+
app37.get("/favicon.ico", (c) => {
|
|
13284
13671
|
c.header("Cache-Control", "public, max-age=300");
|
|
13285
13672
|
return c.redirect(brandFaviconPath, 302);
|
|
13286
13673
|
});
|
|
13287
13674
|
}
|
|
13288
|
-
|
|
13675
|
+
app37.use("/*", serveStatic({ root: "./public" }));
|
|
13289
13676
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
13290
13677
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
13291
|
-
var httpServer = serve({ fetch:
|
|
13678
|
+
var httpServer = serve({ fetch: app37.fetch, port, hostname });
|
|
13292
13679
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
13293
13680
|
{
|
|
13294
13681
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
@@ -13313,7 +13700,8 @@ var SUBAPP_MANIFEST = [
|
|
|
13313
13700
|
{ prefix: "/api/onboarding", file: "server/routes/onboarding.ts", subapp: onboarding_default },
|
|
13314
13701
|
{ prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
|
|
13315
13702
|
{ prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
|
|
13316
|
-
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default }
|
|
13703
|
+
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
|
|
13704
|
+
{ prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default }
|
|
13317
13705
|
];
|
|
13318
13706
|
for (const m of SUBAPP_MANIFEST) {
|
|
13319
13707
|
const routeCount = Array.isArray(m.subapp.routes) ? m.subapp.routes.length : 0;
|
|
@@ -13321,7 +13709,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
13321
13709
|
}
|
|
13322
13710
|
try {
|
|
13323
13711
|
const registered = [];
|
|
13324
|
-
for (const r of
|
|
13712
|
+
for (const r of app37.routes ?? []) {
|
|
13325
13713
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
13326
13714
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
13327
13715
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -13343,8 +13731,8 @@ try {
|
|
|
13343
13731
|
}
|
|
13344
13732
|
(async () => {
|
|
13345
13733
|
try {
|
|
13346
|
-
if (!
|
|
13347
|
-
const usersRaw =
|
|
13734
|
+
if (!existsSync24(USERS_FILE)) return;
|
|
13735
|
+
const usersRaw = readFileSync21(USERS_FILE, "utf-8").trim();
|
|
13348
13736
|
if (!usersRaw) return;
|
|
13349
13737
|
const users = JSON.parse(usersRaw);
|
|
13350
13738
|
const userId = users[0]?.userId;
|
|
@@ -13436,7 +13824,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
13436
13824
|
}
|
|
13437
13825
|
init({
|
|
13438
13826
|
configDir: configDirForWhatsApp,
|
|
13439
|
-
platformRoot: resolve21(process.env.MAXY_PLATFORM_ROOT ??
|
|
13827
|
+
platformRoot: resolve21(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
|
|
13440
13828
|
accountConfig: bootAccountConfig,
|
|
13441
13829
|
onMessage: async (msg) => {
|
|
13442
13830
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|