@oneclick.dev/cms-core-modules 0.0.122 → 0.0.124
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/dist/{ContentEditor-CoJjsV-m.js → ContentEditor-BcIUumah.js} +63 -59
- package/dist/{ContentEditor-DIwqplfB.mjs → ContentEditor-DX1bTQKs.mjs} +27894 -26319
- package/dist/{Detail-Dk8E6_uF.mjs → Detail-B7pe808q.mjs} +15 -14
- package/dist/Detail-BESGmY7z.js +1 -0
- package/dist/{Detail-Cn9Bk0u0.mjs → Detail-B_Q7Qkuz.mjs} +26 -25
- package/dist/Detail-CNiLIEh9.js +1 -0
- package/dist/{Detail-B_scnidb.mjs → Detail-DNxAzeCs.mjs} +35 -34
- package/dist/Detail-t3tOcy3X.js +1 -0
- package/dist/Editor-D4evcdhv.mjs +768 -0
- package/dist/Editor-gLLtuJbY.js +1 -0
- package/dist/FormStatusBadge.vue_vue_type_script_setup_true_lang-BjKpsqT9.js +1 -0
- package/dist/FormStatusBadge.vue_vue_type_script_setup_true_lang-D5v5AhLe.mjs +76 -0
- package/dist/Knowledge-B66vxz--.js +1 -0
- package/dist/Knowledge-BnMTTuJ7.mjs +742 -0
- package/dist/{NewReservationDialog.vue_vue_type_script_setup_true_lang-bZidw6RT.mjs → NewReservationDialog.vue_vue_type_script_setup_true_lang-8I_Z9cD_.mjs} +530 -530
- package/dist/NewReservationDialog.vue_vue_type_script_setup_true_lang-BVE_uURZ.js +1 -0
- package/dist/OrderDetailDialog.vue_vue_type_script_setup_true_lang-BdFa-xNf.mjs +3566 -0
- package/dist/OrderDetailDialog.vue_vue_type_script_setup_true_lang-DzPOadcU.js +925 -0
- package/dist/Overview-BE4uYPx0.mjs +745 -0
- package/dist/Overview-CQRpFhbJ.mjs +286 -0
- package/dist/Overview-CTs7sX4d.js +1 -0
- package/dist/Overview-Cj89XXKN.js +1 -0
- package/dist/Overview-D_c18mbR.mjs +248 -0
- package/dist/Overview-g4Rcj245.js +1 -0
- package/dist/TableCalculationWidget-CkQPrVUd.js +59 -0
- package/dist/TableCalculationWidget-CrqcLGfU.mjs +254 -0
- package/dist/TableDataWidget-7qvC3Egd.js +1 -0
- package/dist/TableDataWidget-DMTSoGRO.mjs +121 -0
- package/dist/TableOverviewCard-AFq-puJs.js +1 -0
- package/dist/TableOverviewCard-Ci0d4o4w.mjs +391 -0
- package/dist/TableResultView.vue_vue_type_script_setup_true_lang-CBn-Oxl9.mjs +534 -0
- package/dist/TableResultView.vue_vue_type_script_setup_true_lang-C_Lv-SmJ.js +1 -0
- package/dist/TableRowActionCard-BhdnnPaq.mjs +266 -0
- package/dist/TableRowActionCard-IzB6tNN-.js +1 -0
- package/dist/TableView-6mdrL3zx.js +4 -0
- package/dist/TableView-Du5nOE6R.mjs +8228 -0
- package/dist/Test-CzmOmi53.mjs +447 -0
- package/dist/Test-USDtNFY1.js +1 -0
- package/dist/UserDetail-DpUL3mhk.mjs +1483 -0
- package/dist/UserDetail-KRIfxaYd.js +1 -0
- package/dist/{agenda-BCKDYOeF.mjs → agenda-DWdbWUbb.mjs} +10 -9
- package/dist/agenda-r9Gl85GL.js +1 -0
- package/dist/cms-core-modules.css +1 -1
- package/dist/dateRangePresets-B-l5b_wR.mjs +48 -0
- package/dist/dateRangePresets-CPWqst_x.js +1 -0
- package/dist/fuse-Deb-Iq39.mjs +1473 -0
- package/dist/fuse-_CGUd5Zq.js +1 -0
- package/dist/{index-DU3Ib10Z.mjs → index-B_qaX29C.mjs} +7 -6
- package/dist/index-CQuN5rKx.js +1 -0
- package/dist/index-CnDGeInm.js +1 -0
- package/dist/index-CqDTc5Ig.mjs +20 -0
- package/dist/index-CyvvAdt4.js +1 -0
- package/dist/index-DIRD7t5_.mjs +20 -0
- package/dist/index-OJuBYR60.mjs +57 -0
- package/dist/{index-Bk3yLv_G.js → index-yHLT6gaZ.js} +3 -3
- package/dist/index.cjs.js +1 -1
- package/dist/index.mjs +671 -118
- package/dist/{orders-CElKNc1v.mjs → orders-BfTLIm7U.mjs} +10 -9
- package/dist/orders-DUa2PVoa.js +1 -0
- package/dist/reports-ChCsd1Gk.mjs +1107 -0
- package/dist/reports-DmJPm8uP.js +158 -0
- package/dist/row-item-C1dUyimV.mjs +204 -0
- package/dist/row-item-Cyo3m3MZ.js +1 -0
- package/dist/server-handlers.cjs.js +1 -1
- package/dist/server-handlers.mjs +1764 -605
- package/dist/src/appointments/components/edit/EventDialog/BookingsList.vue.d.ts +9 -3
- package/dist/src/appointments/components/edit/EventDialog/OrderDetailDialog.vue.d.ts +2 -0
- package/dist/src/appointments/components/edit/EventDialog/ReservationDetailDialog.vue.d.ts +4 -2
- package/dist/src/appointments/components/edit/dashboard/BookingsList.vue.d.ts +9 -3
- package/dist/src/appointments/pages/edit/orders.vue.d.ts +9 -3
- package/dist/src/appointments/pages/edit/reports.vue.d.ts +229 -1
- package/dist/src/contentManager/components/content-editor/tiptap-extensions/Container.d.ts +1 -1
- package/dist/src/contentManager/components/content-editor/tiptap-extensions/Row.d.ts +7 -0
- package/dist/src/contentManager/components/content-editor/tiptap-extensions/Stack.d.ts +7 -0
- package/dist/src/contentManager/components/content-editor/tiptap-extensions/helpers/resizeHistory.d.ts +9 -0
- package/dist/src/contentManager/components/content-editor/tiptap-menus/ElementEditorMenu.vue.d.ts +3 -1
- package/dist/src/firebaseAuthManager/components/DeleteUserDialog.vue.d.ts +25 -0
- package/dist/src/firebaseAuthManager/components/ReasonActionDialog.vue.d.ts +23 -0
- package/dist/src/firebaseAuthManager/components/UserStatusBadge.vue.d.ts +5 -0
- package/dist/src/firebaseAuthManager/config.d.ts +151 -0
- package/dist/src/firebaseAuthManager/index.d.ts +163 -0
- package/dist/src/firebaseAuthManager/index.vue.d.ts +14 -0
- package/dist/src/firebaseAuthManager/metadata.d.ts +2 -0
- package/dist/src/firebaseAuthManager/pages/Overview.vue.d.ts +2 -0
- package/dist/src/firebaseAuthManager/pages/UserDetail.vue.d.ts +2 -0
- package/dist/src/firebaseAuthManager/permissions.d.ts +5 -0
- package/dist/src/firebaseAuthManager/routes.d.ts +5 -0
- package/dist/src/firebaseAuthManager/server.d.ts +162 -0
- package/dist/src/firebaseAuthManager/types.d.ts +84 -0
- package/dist/src/formBuilder/components/FormPreviewDialog.vue.d.ts +12 -0
- package/dist/src/formBuilder/components/FormStatusBadge.vue.d.ts +9 -0
- package/dist/src/formBuilder/config.d.ts +31 -0
- package/dist/src/formBuilder/index.d.ts +44 -0
- package/dist/src/formBuilder/index.vue.d.ts +14 -0
- package/dist/src/formBuilder/lib/forms.d.ts +11 -0
- package/dist/src/formBuilder/metadata.d.ts +2 -0
- package/dist/src/formBuilder/pages/Editor.vue.d.ts +2 -0
- package/dist/src/formBuilder/pages/Overview.vue.d.ts +2 -0
- package/dist/src/formBuilder/permissions.d.ts +5 -0
- package/dist/src/formBuilder/routes.d.ts +5 -0
- package/dist/src/formBuilder/server.d.ts +87 -0
- package/dist/src/formBuilder/tools.d.ts +1 -0
- package/dist/src/formBuilder/types.d.ts +46 -0
- package/dist/src/helpbot/composables/useHelpbotApi.d.ts +18 -0
- package/dist/src/helpbot/config.d.ts +17 -0
- package/dist/src/helpbot/index.d.ts +29 -0
- package/dist/src/helpbot/index.vue.d.ts +2 -0
- package/dist/src/helpbot/metadata.d.ts +2 -0
- package/dist/src/helpbot/pages/Knowledge.vue.d.ts +4 -0
- package/dist/src/helpbot/pages/Overview.vue.d.ts +2 -0
- package/dist/src/helpbot/pages/Test.vue.d.ts +4 -0
- package/dist/src/helpbot/permissions.d.ts +5 -0
- package/dist/src/helpbot/routes.d.ts +5 -0
- package/dist/src/helpbot/types.d.ts +42 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/server-handlers.d.ts +2 -0
- package/dist/src/table/chat-components/TableCalculationWidget.vue.d.ts +15 -0
- package/dist/src/table/chat-components/TableDataWidget.vue.d.ts +15 -0
- package/dist/src/table/chat-components/TableOverviewCard.vue.d.ts +61 -0
- package/dist/src/table/chat-components/TableResultView.vue.d.ts +48 -0
- package/dist/src/table/chat-components/TableRowActionCard.vue.d.ts +15 -0
- package/dist/src/table/components/filters/DialogTableFilterOptionItem.vue.d.ts +16 -0
- package/dist/src/table/components/filters/NumberComparisonFilter.vue.d.ts +12 -0
- package/dist/src/table/components/filters/NumberComparisonFilterSettings.vue.d.ts +12 -0
- package/dist/src/table/components/filters/NumberRangeFilter.vue.d.ts +12 -0
- package/dist/src/table/components/filters/NumberRangeFilterSettings.vue.d.ts +12 -0
- package/dist/src/table/components/filters/filter-utils.d.ts +14 -0
- package/dist/src/table/composables/useTableAgentTools.d.ts +33 -0
- package/dist/src/table/index.d.ts +339 -0
- package/dist/src/table/tool-sandbox.d.ts +13 -0
- package/dist/src/table/tool-utils.d.ts +11 -0
- package/dist/src/table/tools.d.ts +333 -1
- package/dist/src/table/types.d.ts +79 -0
- package/dist/tool-sandbox-DdkMLkDK.mjs +182 -0
- package/dist/tool-sandbox-DoF1PsyS.js +115 -0
- package/dist/types-9N-DKxNj.mjs +250 -0
- package/dist/types-BMraG1kN.js +1 -0
- package/dist/types-Cem0vPo_.js +1 -0
- package/dist/types-ibC3-e1N.mjs +2630 -0
- package/dist/useHelpbotApi-BIIungOh.js +1 -0
- package/dist/useHelpbotApi-HC9JSiSn.mjs +229 -0
- package/dist/useTableAgentTools-D5mzGf3A.js +1 -0
- package/dist/useTableAgentTools-DcN3kYRL.mjs +131 -0
- package/dist/vee-validate-zod-BQElPD0m.js +1 -0
- package/dist/vee-validate-zod-CN16K2x9.mjs +1780 -0
- package/package.json +17 -17
- package/src/formBuilder/tools.ts +1 -0
- package/src/table/tools.ts +304 -1
- package/dist/Detail-BCUUNWvp.js +0 -1
- package/dist/Detail-DGg_xXZc.js +0 -1
- package/dist/Detail-Sc2qZdxj.js +0 -1
- package/dist/NewReservationDialog.vue_vue_type_script_setup_true_lang-BdZbcubW.js +0 -1
- package/dist/OrderDetailDialog.vue_vue_type_script_setup_true_lang-BYSeUW_V.js +0 -925
- package/dist/OrderDetailDialog.vue_vue_type_script_setup_true_lang-DwAUYR8p.mjs +0 -5021
- package/dist/TableView-Ctnep-Qu.mjs +0 -6247
- package/dist/TableView-DJqyJL9L.js +0 -4
- package/dist/agenda-B54F8cEr.js +0 -1
- package/dist/orders-BMosTk6r.js +0 -1
- package/dist/reports-BQT_GZzU.js +0 -158
- package/dist/reports-DksZCTOw.mjs +0 -837
- package/dist/vee-validate-zod-SjDT8X35.mjs +0 -4404
- package/dist/vee-validate-zod-VCmLnrtQ.js +0 -1
package/dist/server-handlers.mjs
CHANGED
|
@@ -1,400 +1,401 @@
|
|
|
1
|
-
import { createRouter as
|
|
1
|
+
import { createRouter as se, defineEventHandler as A, createError as y, getRouterParam as N, getQuery as R, readBody as Q, setResponseStatus as Ze } from "h3";
|
|
2
2
|
import "vue";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
import { s as B, o as ae, b as re } from "./types-ibC3-e1N.mjs";
|
|
4
|
+
function Gt(e) {
|
|
5
|
+
const { initFirebase: t, normalizeTimestamps: s } = e, i = se();
|
|
6
|
+
async function r(a) {
|
|
7
|
+
const { supabase: n, instanceId: c } = a.context.module, { data: o, error: u } = await n.from("project_modules").select("config").eq("id", c).single();
|
|
8
|
+
if (u || !o?.config)
|
|
9
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load module config." });
|
|
10
|
+
const d = o.config, l = d.project, f = d.productCollection || "products";
|
|
11
|
+
if (!l)
|
|
12
|
+
throw y({ statusCode: 400, statusMessage: "Products module has no Firebase integration configured." });
|
|
13
|
+
return { firebase: await t(a, l), collection: f };
|
|
13
14
|
}
|
|
14
|
-
return
|
|
15
|
+
return i.get("/products", A(async (a) => {
|
|
15
16
|
try {
|
|
16
|
-
const { firebase:
|
|
17
|
-
return (await
|
|
18
|
-
id:
|
|
19
|
-
...
|
|
17
|
+
const { firebase: n, collection: c } = await r(a);
|
|
18
|
+
return (await n.firestore().collection(c).get()).docs.map((u) => s({
|
|
19
|
+
id: u.id,
|
|
20
|
+
...u.data()
|
|
20
21
|
}));
|
|
21
|
-
} catch (
|
|
22
|
-
throw console.error("Products handler — list error:",
|
|
23
|
-
statusCode:
|
|
24
|
-
statusMessage:
|
|
22
|
+
} catch (n) {
|
|
23
|
+
throw console.error("Products handler — list error:", n), y({
|
|
24
|
+
statusCode: n.statusCode || 500,
|
|
25
|
+
statusMessage: n.statusMessage || "Failed to list products"
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
|
-
})),
|
|
28
|
-
const
|
|
29
|
-
if (!
|
|
30
|
-
throw
|
|
28
|
+
})), i.get("/products/:productId", A(async (a) => {
|
|
29
|
+
const n = N(a, "productId");
|
|
30
|
+
if (!n)
|
|
31
|
+
throw y({ statusCode: 400, statusMessage: "Product ID is required." });
|
|
31
32
|
try {
|
|
32
|
-
const { firebase:
|
|
33
|
-
if (!
|
|
34
|
-
throw
|
|
35
|
-
return
|
|
36
|
-
id:
|
|
37
|
-
...
|
|
33
|
+
const { firebase: c, collection: o } = await r(a), u = await c.firestore().collection(o).doc(n).get();
|
|
34
|
+
if (!u.exists)
|
|
35
|
+
throw y({ statusCode: 404, statusMessage: "Product not found." });
|
|
36
|
+
return s({
|
|
37
|
+
id: u.id,
|
|
38
|
+
...u.data()
|
|
38
39
|
});
|
|
39
|
-
} catch (
|
|
40
|
-
throw console.error("Products handler — get error:",
|
|
41
|
-
statusCode:
|
|
42
|
-
statusMessage:
|
|
40
|
+
} catch (c) {
|
|
41
|
+
throw console.error("Products handler — get error:", c), y({
|
|
42
|
+
statusCode: c.statusCode || 500,
|
|
43
|
+
statusMessage: c.statusMessage || "Failed to get product"
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
|
-
})),
|
|
46
|
-
const
|
|
46
|
+
})), i.get("/empty-stock", A(async (a) => {
|
|
47
|
+
const c = R(a).category;
|
|
47
48
|
try {
|
|
48
|
-
const { firebase:
|
|
49
|
-
let
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
id:
|
|
53
|
-
...
|
|
49
|
+
const { firebase: o, collection: u } = await r(a);
|
|
50
|
+
let d = o.firestore().collection(u).where("stock", "==", 0);
|
|
51
|
+
c && (d = d.where("collections", "array-contains", c));
|
|
52
|
+
const f = (await d.get()).docs.map((g) => s({
|
|
53
|
+
id: g.id,
|
|
54
|
+
...g.data()
|
|
54
55
|
}));
|
|
55
56
|
return {
|
|
56
|
-
count:
|
|
57
|
-
products:
|
|
58
|
-
id:
|
|
59
|
-
title:
|
|
60
|
-
slug:
|
|
61
|
-
stock:
|
|
62
|
-
price:
|
|
63
|
-
currency:
|
|
64
|
-
status:
|
|
57
|
+
count: f.length,
|
|
58
|
+
products: f.map((g) => ({
|
|
59
|
+
id: g.id,
|
|
60
|
+
title: g.title,
|
|
61
|
+
slug: g.slug,
|
|
62
|
+
stock: g.stock,
|
|
63
|
+
price: g.price,
|
|
64
|
+
currency: g.currency,
|
|
65
|
+
status: g.status
|
|
65
66
|
}))
|
|
66
67
|
};
|
|
67
|
-
} catch (
|
|
68
|
-
throw console.error("Products handler — empty-stock error:",
|
|
69
|
-
statusCode:
|
|
70
|
-
statusMessage:
|
|
68
|
+
} catch (o) {
|
|
69
|
+
throw console.error("Products handler — empty-stock error:", o), y({
|
|
70
|
+
statusCode: o.statusCode || 500,
|
|
71
|
+
statusMessage: o.statusMessage || "Failed to query empty stock"
|
|
71
72
|
});
|
|
72
73
|
}
|
|
73
|
-
})),
|
|
74
|
-
}
|
|
75
|
-
function
|
|
76
|
-
const { initFirebase:
|
|
77
|
-
async function
|
|
78
|
-
const { supabase:
|
|
79
|
-
if (
|
|
80
|
-
throw
|
|
81
|
-
const
|
|
82
|
-
if (!
|
|
83
|
-
throw
|
|
84
|
-
return { firebase: await
|
|
74
|
+
})), i.handler;
|
|
75
|
+
}
|
|
76
|
+
function Qt(e) {
|
|
77
|
+
const { initFirebase: t, normalizeTimestamps: s, deleteField: i } = e, r = se(), a = "privateSettings", n = "refundFlow", c = "metadataSchema";
|
|
78
|
+
async function o(m) {
|
|
79
|
+
const { supabase: h, instanceId: p } = m.context.module, { data: w, error: C } = await h.from("project_modules").select("config").eq("id", p).single();
|
|
80
|
+
if (C || !w?.config)
|
|
81
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load module config." });
|
|
82
|
+
const b = w.config, D = b.project, F = b.reservationsCollection || "bookings_orders", M = b.agendaCollection || "agendas";
|
|
83
|
+
if (!D)
|
|
84
|
+
throw y({ statusCode: 400, statusMessage: "Appointments module has no Firebase integration configured." });
|
|
85
|
+
return { firebase: await t(m, D), reservationsCollection: F, agendaCollection: M };
|
|
85
86
|
}
|
|
86
|
-
function
|
|
87
|
-
return (
|
|
88
|
-
orderId:
|
|
89
|
-
reservationId:
|
|
90
|
-
customerInfo:
|
|
91
|
-
date:
|
|
92
|
-
startTime:
|
|
93
|
-
endTime:
|
|
94
|
-
spots:
|
|
95
|
-
status:
|
|
96
|
-
reservationStatus:
|
|
97
|
-
resourceId:
|
|
98
|
-
reservationPrice:
|
|
99
|
-
reservationBasePrice:
|
|
100
|
-
reservationAddOnsPrice:
|
|
101
|
-
pricingOption:
|
|
102
|
-
amountDue:
|
|
103
|
-
amountPaid:
|
|
104
|
-
createdAt:
|
|
105
|
-
metadata:
|
|
106
|
-
reservationMetadata:
|
|
87
|
+
function u(m) {
|
|
88
|
+
return (m.reservations || []).map((p) => ({
|
|
89
|
+
orderId: m.id,
|
|
90
|
+
reservationId: p.id,
|
|
91
|
+
customerInfo: m.customerInfo,
|
|
92
|
+
date: p.date,
|
|
93
|
+
startTime: p.timeslot?.startTime,
|
|
94
|
+
endTime: p.timeslot?.endTime,
|
|
95
|
+
spots: p.spots,
|
|
96
|
+
status: m.status,
|
|
97
|
+
reservationStatus: p.status,
|
|
98
|
+
resourceId: p.resourceId,
|
|
99
|
+
reservationPrice: p.totalPrice,
|
|
100
|
+
reservationBasePrice: p.basePrice,
|
|
101
|
+
reservationAddOnsPrice: p.addOnsPrice,
|
|
102
|
+
pricingOption: p.pricingOption,
|
|
103
|
+
amountDue: m.amountDue,
|
|
104
|
+
amountPaid: m.amountPaid,
|
|
105
|
+
createdAt: m.createdAt,
|
|
106
|
+
metadata: m.metadata || {},
|
|
107
|
+
reservationMetadata: p.metadata || {}
|
|
107
108
|
}));
|
|
108
109
|
}
|
|
109
|
-
function
|
|
110
|
+
function d(m) {
|
|
110
111
|
return {
|
|
111
|
-
showAmount:
|
|
112
|
-
fields: Array.isArray(
|
|
112
|
+
showAmount: m?.showAmount !== !1,
|
|
113
|
+
fields: Array.isArray(m?.fields) ? m.fields : []
|
|
113
114
|
};
|
|
114
115
|
}
|
|
115
|
-
function
|
|
116
|
-
return
|
|
117
|
-
refundFlow:
|
|
118
|
-
refundFlowEnabled:
|
|
119
|
-
refundDialog:
|
|
116
|
+
function l() {
|
|
117
|
+
return i ? {
|
|
118
|
+
refundFlow: i(),
|
|
119
|
+
refundFlowEnabled: i(),
|
|
120
|
+
refundDialog: i()
|
|
120
121
|
} : {
|
|
121
122
|
refundFlow: null,
|
|
122
123
|
refundFlowEnabled: null,
|
|
123
124
|
refundDialog: null
|
|
124
125
|
};
|
|
125
126
|
}
|
|
126
|
-
function
|
|
127
|
-
return Array.isArray(
|
|
127
|
+
function f(m) {
|
|
128
|
+
return Array.isArray(m) ? m : [];
|
|
128
129
|
}
|
|
129
|
-
function
|
|
130
|
-
return
|
|
131
|
-
metadataSchema:
|
|
130
|
+
function g() {
|
|
131
|
+
return i ? {
|
|
132
|
+
metadataSchema: i()
|
|
132
133
|
} : {
|
|
133
134
|
metadataSchema: null
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
|
-
return
|
|
137
|
+
return r.get("/agendas", A(async (m) => {
|
|
137
138
|
try {
|
|
138
|
-
const { firebase:
|
|
139
|
-
const
|
|
139
|
+
const { firebase: h, agendaCollection: p } = await o(m), C = (await h.firestore().collection(p).get()).docs.map((b) => {
|
|
140
|
+
const D = b.data();
|
|
140
141
|
return {
|
|
141
|
-
id:
|
|
142
|
-
serviceName:
|
|
143
|
-
type:
|
|
142
|
+
id: b.id,
|
|
143
|
+
serviceName: D.serviceName || "",
|
|
144
|
+
type: D.type || "regular"
|
|
144
145
|
};
|
|
145
146
|
});
|
|
146
147
|
return {
|
|
147
|
-
count:
|
|
148
|
-
agendas:
|
|
148
|
+
count: C.length,
|
|
149
|
+
agendas: C
|
|
149
150
|
};
|
|
150
|
-
} catch (
|
|
151
|
-
throw console.error("Appointments handler — list agendas error:",
|
|
152
|
-
statusCode:
|
|
153
|
-
statusMessage:
|
|
151
|
+
} catch (h) {
|
|
152
|
+
throw console.error("Appointments handler — list agendas error:", h), y({
|
|
153
|
+
statusCode: h.statusCode || 500,
|
|
154
|
+
statusMessage: h.statusMessage || "Failed to list agendas"
|
|
154
155
|
});
|
|
155
156
|
}
|
|
156
|
-
})),
|
|
157
|
-
const
|
|
158
|
-
if (!
|
|
159
|
-
throw
|
|
160
|
-
if (!
|
|
161
|
-
throw
|
|
157
|
+
})), r.get("/agendas/:agendaId/opening-hours", A(async (m) => {
|
|
158
|
+
const h = N(m, "agendaId"), w = R(m).date;
|
|
159
|
+
if (!h)
|
|
160
|
+
throw y({ statusCode: 400, statusMessage: "Agenda ID is required." });
|
|
161
|
+
if (!w)
|
|
162
|
+
throw y({ statusCode: 400, statusMessage: "Date is required (YYYY-MM-DD)." });
|
|
162
163
|
try {
|
|
163
|
-
const { firebase:
|
|
164
|
-
if (!
|
|
165
|
-
throw
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
return
|
|
164
|
+
const { firebase: C, agendaCollection: b } = await o(m), D = await C.firestore().collection(b).doc(h).get();
|
|
165
|
+
if (!D.exists)
|
|
166
|
+
throw y({ statusCode: 404, statusMessage: "Agenda not found." });
|
|
167
|
+
const F = D.data(), M = (F.resources || []).filter(($) => $.isActive), S = F.exceptions || [], j = (/* @__PURE__ */ new Date(w + "T00:00:00")).getDay(), ce = M.map(($) => {
|
|
168
|
+
const Ae = $.openingHours?.[j] || [], de = S.find((V) => {
|
|
169
|
+
const Je = w >= V.startDate && w <= V.endDate, Xe = !V.resourceIds || V.resourceIds.length === 0 || V.resourceIds.includes($.id);
|
|
170
|
+
return Je && Xe;
|
|
170
171
|
});
|
|
171
|
-
return
|
|
172
|
-
resourceId:
|
|
173
|
-
resourceName:
|
|
174
|
-
date:
|
|
175
|
-
dayOfWeek:
|
|
176
|
-
isClosed:
|
|
177
|
-
hours:
|
|
178
|
-
start:
|
|
179
|
-
end:
|
|
172
|
+
return de ? {
|
|
173
|
+
resourceId: $.id,
|
|
174
|
+
resourceName: $.name,
|
|
175
|
+
date: w,
|
|
176
|
+
dayOfWeek: j,
|
|
177
|
+
isClosed: de.isClosed,
|
|
178
|
+
hours: de.isClosed ? [] : (de.timeslots || []).map((V) => ({
|
|
179
|
+
start: V.startTime,
|
|
180
|
+
end: V.endTime
|
|
180
181
|
})),
|
|
181
182
|
isException: !0
|
|
182
183
|
} : {
|
|
183
|
-
resourceId:
|
|
184
|
-
resourceName:
|
|
185
|
-
date:
|
|
186
|
-
dayOfWeek:
|
|
187
|
-
isClosed:
|
|
188
|
-
hours:
|
|
184
|
+
resourceId: $.id,
|
|
185
|
+
resourceName: $.name,
|
|
186
|
+
date: w,
|
|
187
|
+
dayOfWeek: j,
|
|
188
|
+
isClosed: Ae.length === 0,
|
|
189
|
+
hours: Ae,
|
|
189
190
|
isException: !1
|
|
190
191
|
};
|
|
191
192
|
});
|
|
192
193
|
return {
|
|
193
|
-
agendaId:
|
|
194
|
-
serviceName:
|
|
195
|
-
date:
|
|
196
|
-
resources:
|
|
194
|
+
agendaId: h,
|
|
195
|
+
serviceName: F.serviceName || "",
|
|
196
|
+
date: w,
|
|
197
|
+
resources: ce
|
|
197
198
|
};
|
|
198
|
-
} catch (
|
|
199
|
-
throw console.error("Appointments handler — opening hours error:",
|
|
200
|
-
statusCode:
|
|
201
|
-
statusMessage:
|
|
199
|
+
} catch (C) {
|
|
200
|
+
throw console.error("Appointments handler — opening hours error:", C), y({
|
|
201
|
+
statusCode: C.statusCode || 500,
|
|
202
|
+
statusMessage: C.statusMessage || "Failed to get opening hours"
|
|
202
203
|
});
|
|
203
204
|
}
|
|
204
|
-
})),
|
|
205
|
-
const
|
|
206
|
-
if (!
|
|
207
|
-
throw
|
|
205
|
+
})), r.get("/agendas/:agendaId/refund-flow", A(async (m) => {
|
|
206
|
+
const h = N(m, "agendaId");
|
|
207
|
+
if (!h)
|
|
208
|
+
throw y({ statusCode: 400, statusMessage: "Agenda ID is required." });
|
|
208
209
|
try {
|
|
209
|
-
const { firebase:
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
const { firebase: p, agendaCollection: w } = await o(m), b = p.firestore().collection(w).doc(h), D = b.collection(a).doc(n), [F, M] = await Promise.all([
|
|
211
|
+
b.get(),
|
|
212
|
+
D.get()
|
|
212
213
|
]);
|
|
213
|
-
if (!
|
|
214
|
-
throw
|
|
215
|
-
const
|
|
214
|
+
if (!F.exists)
|
|
215
|
+
throw y({ statusCode: 404, statusMessage: "Agenda not found." });
|
|
216
|
+
const S = M.exists ? M.data() || {} : {};
|
|
216
217
|
return {
|
|
217
|
-
refundFlow:
|
|
218
|
-
refundFlowEnabled:
|
|
219
|
-
refundDialog:
|
|
218
|
+
refundFlow: S.flow || {},
|
|
219
|
+
refundFlowEnabled: S.enabled !== !1,
|
|
220
|
+
refundDialog: d(S.dialog)
|
|
220
221
|
};
|
|
221
|
-
} catch (
|
|
222
|
-
throw console.error("Appointments handler — refund flow load error:",
|
|
223
|
-
statusCode:
|
|
224
|
-
statusMessage:
|
|
222
|
+
} catch (p) {
|
|
223
|
+
throw console.error("Appointments handler — refund flow load error:", p), y({
|
|
224
|
+
statusCode: p.statusCode || 500,
|
|
225
|
+
statusMessage: p.statusMessage || "Failed to load refund flow"
|
|
225
226
|
});
|
|
226
227
|
}
|
|
227
|
-
})),
|
|
228
|
-
const
|
|
229
|
-
if (!
|
|
230
|
-
throw
|
|
228
|
+
})), r.patch("/agendas/:agendaId/refund-flow", A(async (m) => {
|
|
229
|
+
const h = N(m, "agendaId");
|
|
230
|
+
if (!h)
|
|
231
|
+
throw y({ statusCode: 400, statusMessage: "Agenda ID is required." });
|
|
231
232
|
try {
|
|
232
|
-
const
|
|
233
|
-
if (!(await
|
|
234
|
-
throw
|
|
235
|
-
const k =
|
|
236
|
-
return k.set(
|
|
237
|
-
flow:
|
|
238
|
-
enabled:
|
|
239
|
-
dialog:
|
|
233
|
+
const p = await Q(m), { firebase: w, agendaCollection: C } = await o(m), b = w.firestore(), D = b.collection(C).doc(h), F = D.collection(a).doc(n), M = d(p?.refundDialog);
|
|
234
|
+
if (!(await D.get()).exists)
|
|
235
|
+
throw y({ statusCode: 404, statusMessage: "Agenda not found." });
|
|
236
|
+
const k = b.batch();
|
|
237
|
+
return k.set(F, {
|
|
238
|
+
flow: p?.refundFlow || {},
|
|
239
|
+
enabled: p?.refundFlowEnabled !== !1,
|
|
240
|
+
dialog: M,
|
|
240
241
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
241
|
-
}, { merge: !0 }), k.set(
|
|
242
|
-
} catch (
|
|
243
|
-
throw console.error("Appointments handler — refund flow save error:",
|
|
244
|
-
statusCode:
|
|
245
|
-
statusMessage:
|
|
242
|
+
}, { merge: !0 }), k.set(D, l(), { merge: !0 }), await k.commit(), { success: !0 };
|
|
243
|
+
} catch (p) {
|
|
244
|
+
throw console.error("Appointments handler — refund flow save error:", p), y({
|
|
245
|
+
statusCode: p.statusCode || 500,
|
|
246
|
+
statusMessage: p.statusMessage || "Failed to save refund flow"
|
|
246
247
|
});
|
|
247
248
|
}
|
|
248
|
-
})),
|
|
249
|
-
const
|
|
250
|
-
if (!
|
|
251
|
-
throw
|
|
249
|
+
})), r.get("/agendas/:agendaId/metadata-schema", A(async (m) => {
|
|
250
|
+
const h = N(m, "agendaId");
|
|
251
|
+
if (!h)
|
|
252
|
+
throw y({ statusCode: 400, statusMessage: "Agenda ID is required." });
|
|
252
253
|
try {
|
|
253
|
-
const { firebase:
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
const { firebase: p, agendaCollection: w } = await o(m), b = p.firestore().collection(w).doc(h), D = b.collection(a).doc(c), [F, M] = await Promise.all([
|
|
255
|
+
b.get(),
|
|
256
|
+
D.get()
|
|
256
257
|
]);
|
|
257
|
-
if (!
|
|
258
|
-
throw
|
|
259
|
-
const
|
|
258
|
+
if (!F.exists)
|
|
259
|
+
throw y({ statusCode: 404, statusMessage: "Agenda not found." });
|
|
260
|
+
const S = M.exists ? M.data() || {} : {};
|
|
260
261
|
return {
|
|
261
|
-
metadataSchema:
|
|
262
|
+
metadataSchema: f(S.schema)
|
|
262
263
|
};
|
|
263
|
-
} catch (
|
|
264
|
-
throw console.error("Appointments handler — metadata schema load error:",
|
|
265
|
-
statusCode:
|
|
266
|
-
statusMessage:
|
|
264
|
+
} catch (p) {
|
|
265
|
+
throw console.error("Appointments handler — metadata schema load error:", p), y({
|
|
266
|
+
statusCode: p.statusCode || 500,
|
|
267
|
+
statusMessage: p.statusMessage || "Failed to load metadata schema"
|
|
267
268
|
});
|
|
268
269
|
}
|
|
269
|
-
})),
|
|
270
|
-
const
|
|
271
|
-
if (!
|
|
272
|
-
throw
|
|
270
|
+
})), r.patch("/agendas/:agendaId/metadata-schema", A(async (m) => {
|
|
271
|
+
const h = N(m, "agendaId");
|
|
272
|
+
if (!h)
|
|
273
|
+
throw y({ statusCode: 400, statusMessage: "Agenda ID is required." });
|
|
273
274
|
try {
|
|
274
|
-
const
|
|
275
|
-
if (!(await
|
|
276
|
-
throw
|
|
277
|
-
const
|
|
278
|
-
return
|
|
279
|
-
schema:
|
|
275
|
+
const p = await Q(m), { firebase: w, agendaCollection: C } = await o(m), b = w.firestore(), D = b.collection(C).doc(h), F = D.collection(a).doc(c);
|
|
276
|
+
if (!(await D.get()).exists)
|
|
277
|
+
throw y({ statusCode: 404, statusMessage: "Agenda not found." });
|
|
278
|
+
const S = b.batch();
|
|
279
|
+
return S.set(F, {
|
|
280
|
+
schema: f(p?.metadataSchema),
|
|
280
281
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
281
|
-
}, { merge: !0 }),
|
|
282
|
-
} catch (
|
|
283
|
-
throw console.error("Appointments handler — metadata schema save error:",
|
|
284
|
-
statusCode:
|
|
285
|
-
statusMessage:
|
|
282
|
+
}, { merge: !0 }), S.set(D, g(), { merge: !0 }), await S.commit(), { success: !0 };
|
|
283
|
+
} catch (p) {
|
|
284
|
+
throw console.error("Appointments handler — metadata schema save error:", p), y({
|
|
285
|
+
statusCode: p.statusCode || 500,
|
|
286
|
+
statusMessage: p.statusMessage || "Failed to save metadata schema"
|
|
286
287
|
});
|
|
287
288
|
}
|
|
288
|
-
})),
|
|
289
|
-
const
|
|
289
|
+
})), r.get("/appointments", A(async (m) => {
|
|
290
|
+
const h = R(m), p = Math.min(Number(h.quantity) || 20, 100), w = h.status || "confirmed";
|
|
290
291
|
try {
|
|
291
|
-
const { firebase:
|
|
292
|
-
(
|
|
292
|
+
const { firebase: C, reservationsCollection: b } = await o(m), F = (await C.firestore().collection(b).orderBy("createdAt", "desc").where("status", "==", w).limit(p).get()).docs.flatMap(
|
|
293
|
+
(M) => u(s({ id: M.id, ...M.data() }))
|
|
293
294
|
);
|
|
294
295
|
return {
|
|
295
|
-
count:
|
|
296
|
-
appointments:
|
|
296
|
+
count: F.length,
|
|
297
|
+
appointments: F
|
|
297
298
|
};
|
|
298
|
-
} catch (
|
|
299
|
-
throw console.error("Appointments handler — list error:",
|
|
300
|
-
statusCode:
|
|
301
|
-
statusMessage:
|
|
299
|
+
} catch (C) {
|
|
300
|
+
throw console.error("Appointments handler — list error:", C), y({
|
|
301
|
+
statusCode: C.statusCode || 500,
|
|
302
|
+
statusMessage: C.statusMessage || "Failed to list appointments"
|
|
302
303
|
});
|
|
303
304
|
}
|
|
304
|
-
})),
|
|
305
|
-
const
|
|
305
|
+
})), r.get("/appointments/find", A(async (m) => {
|
|
306
|
+
const h = R(m), p = (h.name || "").toLowerCase().trim(), w = (h.email || "").toLowerCase().trim(), C = h.date;
|
|
306
307
|
try {
|
|
307
|
-
const { firebase:
|
|
308
|
-
let
|
|
309
|
-
|
|
310
|
-
let
|
|
311
|
-
(k) =>
|
|
308
|
+
const { firebase: b, reservationsCollection: D } = await o(m);
|
|
309
|
+
let F = b.firestore().collection(D);
|
|
310
|
+
w && (F = F.where("customerInfo.email", "==", w));
|
|
311
|
+
let S = (await F.orderBy("createdAt", "desc").limit(200).get()).docs.flatMap(
|
|
312
|
+
(k) => u(s({ id: k.id, ...k.data() }))
|
|
312
313
|
);
|
|
313
|
-
return
|
|
314
|
-
const
|
|
315
|
-
return
|
|
314
|
+
return C && (S = S.filter((k) => k.date === C)), p && (S = S.filter((k) => {
|
|
315
|
+
const j = (k.customerInfo?.firstName || "").toLowerCase(), ce = (k.customerInfo?.lastName || "").toLowerCase();
|
|
316
|
+
return j.includes(p) || ce.includes(p) || `${j} ${ce}`.includes(p);
|
|
316
317
|
})), {
|
|
317
|
-
count:
|
|
318
|
-
appointments:
|
|
318
|
+
count: S.length,
|
|
319
|
+
appointments: S
|
|
319
320
|
};
|
|
320
|
-
} catch (
|
|
321
|
-
throw console.error("Appointments handler — find error:",
|
|
322
|
-
statusCode:
|
|
323
|
-
statusMessage:
|
|
321
|
+
} catch (b) {
|
|
322
|
+
throw console.error("Appointments handler — find error:", b), y({
|
|
323
|
+
statusCode: b.statusCode || 500,
|
|
324
|
+
statusMessage: b.statusMessage || "Failed to find appointments"
|
|
324
325
|
});
|
|
325
326
|
}
|
|
326
|
-
})),
|
|
327
|
-
const
|
|
328
|
-
if (!
|
|
329
|
-
throw
|
|
327
|
+
})), r.get("/appointments/:appointmentId", A(async (m) => {
|
|
328
|
+
const h = N(m, "appointmentId");
|
|
329
|
+
if (!h)
|
|
330
|
+
throw y({ statusCode: 400, statusMessage: "Appointment ID is required." });
|
|
330
331
|
try {
|
|
331
|
-
const { firebase:
|
|
332
|
-
if (!
|
|
333
|
-
throw
|
|
334
|
-
const
|
|
332
|
+
const { firebase: p, reservationsCollection: w } = await o(m), C = await p.firestore().collection(w).doc(h).get();
|
|
333
|
+
if (!C.exists)
|
|
334
|
+
throw y({ statusCode: 404, statusMessage: "Appointment not found." });
|
|
335
|
+
const b = s({ id: C.id, ...C.data() });
|
|
335
336
|
return {
|
|
336
|
-
...
|
|
337
|
-
reservations:
|
|
337
|
+
...b,
|
|
338
|
+
reservations: u(b)
|
|
338
339
|
};
|
|
339
|
-
} catch (
|
|
340
|
-
throw console.error("Appointments handler — get error:",
|
|
341
|
-
statusCode:
|
|
342
|
-
statusMessage:
|
|
340
|
+
} catch (p) {
|
|
341
|
+
throw console.error("Appointments handler — get error:", p), y({
|
|
342
|
+
statusCode: p.statusCode || 500,
|
|
343
|
+
statusMessage: p.statusMessage || "Failed to get appointment"
|
|
343
344
|
});
|
|
344
345
|
}
|
|
345
|
-
})),
|
|
346
|
+
})), r.handler;
|
|
346
347
|
}
|
|
347
|
-
const
|
|
348
|
+
const De = "https://analyticsdata.googleapis.com/v1beta", We = [
|
|
348
349
|
"https://www.googleapis.com/auth/analytics.readonly",
|
|
349
350
|
"https://www.googleapis.com/auth/webmasters.readonly"
|
|
350
|
-
],
|
|
351
|
-
function
|
|
352
|
-
const { decrypt:
|
|
353
|
-
async function
|
|
354
|
-
const { supabase:
|
|
355
|
-
if (
|
|
356
|
-
throw
|
|
357
|
-
const
|
|
358
|
-
if (!
|
|
359
|
-
throw
|
|
360
|
-
if (!
|
|
361
|
-
throw
|
|
362
|
-
const { data:
|
|
363
|
-
let
|
|
364
|
-
if (
|
|
365
|
-
const { data: k, error:
|
|
366
|
-
if (
|
|
367
|
-
throw
|
|
368
|
-
|
|
351
|
+
], Fe = "https://searchconsole.googleapis.com/webmasters/v3";
|
|
352
|
+
function Yt(e) {
|
|
353
|
+
const { decrypt: t, getGoogleAccessToken: s } = e, i = se();
|
|
354
|
+
async function r(o) {
|
|
355
|
+
const { supabase: u, instanceId: d } = o.context.module, { data: l, error: f } = await u.from("project_modules").select("config").eq("id", d).single();
|
|
356
|
+
if (f || !l?.config)
|
|
357
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load module config." });
|
|
358
|
+
const g = l.config, m = g.propertyId, h = g.serviceAccount, p = g.siteUrl || "";
|
|
359
|
+
if (!h)
|
|
360
|
+
throw y({ statusCode: 400, statusMessage: "No Google Service Account configured for this module." });
|
|
361
|
+
if (!m)
|
|
362
|
+
throw y({ statusCode: 400, statusMessage: "No GA4 Property ID configured for this module." });
|
|
363
|
+
const { data: w, error: C } = await u.from("integrations").select("config").eq("id", h).single();
|
|
364
|
+
let b = w?.config;
|
|
365
|
+
if (C || !b) {
|
|
366
|
+
const { data: k, error: j } = await u.from("agency_integrations").select("config").eq("id", h).single();
|
|
367
|
+
if (j || !k?.config)
|
|
368
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load Google Service Account credentials." });
|
|
369
|
+
b = k.config;
|
|
369
370
|
}
|
|
370
|
-
const
|
|
371
|
-
return { accessToken: await
|
|
372
|
-
{ clientEmail:
|
|
373
|
-
|
|
374
|
-
), propertyId:
|
|
371
|
+
const D = t(b.clientEmail), F = t(b.privateKey), M = t(b.projectId);
|
|
372
|
+
return { accessToken: await s(
|
|
373
|
+
{ clientEmail: D, privateKey: F, projectId: M },
|
|
374
|
+
We
|
|
375
|
+
), propertyId: m, siteUrl: p };
|
|
375
376
|
}
|
|
376
|
-
async function
|
|
377
|
-
const
|
|
377
|
+
async function a(o, u, d) {
|
|
378
|
+
const l = await fetch(`${De}/properties/${u}:runReport`, {
|
|
378
379
|
method: "POST",
|
|
379
380
|
headers: {
|
|
380
|
-
Authorization: `Bearer ${
|
|
381
|
+
Authorization: `Bearer ${o}`,
|
|
381
382
|
"Content-Type": "application/json"
|
|
382
383
|
},
|
|
383
|
-
body: JSON.stringify(
|
|
384
|
+
body: JSON.stringify(d)
|
|
384
385
|
});
|
|
385
|
-
if (!
|
|
386
|
-
const
|
|
387
|
-
throw console.error("GA4 runReport failed:",
|
|
388
|
-
statusCode:
|
|
389
|
-
statusMessage:
|
|
386
|
+
if (!l.ok) {
|
|
387
|
+
const f = await l.json().catch(() => ({}));
|
|
388
|
+
throw console.error("GA4 runReport failed:", f), y({
|
|
389
|
+
statusCode: l.status,
|
|
390
|
+
statusMessage: f?.error?.message || "GA4 API request failed"
|
|
390
391
|
});
|
|
391
392
|
}
|
|
392
|
-
return
|
|
393
|
+
return l.json();
|
|
393
394
|
}
|
|
394
|
-
|
|
395
|
+
i.get(
|
|
395
396
|
"/report",
|
|
396
|
-
|
|
397
|
-
const { accessToken:
|
|
397
|
+
A(async (o) => {
|
|
398
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = f.match(/^(\d+)daysAgo$/), h = m ? parseInt(m[1], 10) : 30, p = `${h * 2}daysAgo`, w = `${h + 1}daysAgo`, C = [
|
|
398
399
|
{ name: "sessions" },
|
|
399
400
|
{ name: "totalUsers" },
|
|
400
401
|
{ name: "screenPageViews" },
|
|
@@ -405,38 +406,38 @@ function W(A) {
|
|
|
405
406
|
{ name: "sessionsPerUser" },
|
|
406
407
|
{ name: "screenPageViewsPerSession" }
|
|
407
408
|
];
|
|
408
|
-
let
|
|
409
|
+
let b, D = !0;
|
|
409
410
|
try {
|
|
410
|
-
|
|
411
|
+
b = await a(u, d, {
|
|
411
412
|
dateRanges: [
|
|
412
|
-
{ startDate:
|
|
413
|
-
{ startDate:
|
|
413
|
+
{ startDate: f, endDate: g, name: "current" },
|
|
414
|
+
{ startDate: p, endDate: w, name: "previous" }
|
|
414
415
|
],
|
|
415
416
|
dimensions: [{ name: "date" }],
|
|
416
|
-
metrics:
|
|
417
|
+
metrics: C,
|
|
417
418
|
metricAggregations: ["TOTAL"],
|
|
418
419
|
orderBys: [{ dimension: { dimensionName: "date" } }]
|
|
419
420
|
});
|
|
420
421
|
} catch {
|
|
421
|
-
|
|
422
|
-
dateRanges: [{ startDate:
|
|
422
|
+
D = !1, b = await a(u, d, {
|
|
423
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
423
424
|
dimensions: [{ name: "date" }],
|
|
424
|
-
metrics:
|
|
425
|
+
metrics: C,
|
|
425
426
|
metricAggregations: ["TOTAL"],
|
|
426
427
|
orderBys: [{ dimension: { dimensionName: "date" } }]
|
|
427
428
|
});
|
|
428
429
|
}
|
|
429
|
-
return
|
|
430
|
+
return et(b, D);
|
|
430
431
|
})
|
|
431
|
-
),
|
|
432
|
+
), i.get(
|
|
432
433
|
"/realtime",
|
|
433
|
-
|
|
434
|
-
const { accessToken:
|
|
435
|
-
`${
|
|
434
|
+
A(async (o) => {
|
|
435
|
+
const { accessToken: u, propertyId: d } = await r(o), l = await fetch(
|
|
436
|
+
`${De}/properties/${d}:runRealtimeReport`,
|
|
436
437
|
{
|
|
437
438
|
method: "POST",
|
|
438
439
|
headers: {
|
|
439
|
-
Authorization: `Bearer ${
|
|
440
|
+
Authorization: `Bearer ${u}`,
|
|
440
441
|
"Content-Type": "application/json"
|
|
441
442
|
},
|
|
442
443
|
body: JSON.stringify({
|
|
@@ -447,24 +448,24 @@ function W(A) {
|
|
|
447
448
|
})
|
|
448
449
|
}
|
|
449
450
|
);
|
|
450
|
-
if (!
|
|
451
|
-
const
|
|
452
|
-
throw
|
|
451
|
+
if (!l.ok) {
|
|
452
|
+
const h = await l.json().catch(() => ({}));
|
|
453
|
+
throw y({ statusCode: l.status, statusMessage: h?.error?.message || "Realtime API failed" });
|
|
453
454
|
}
|
|
454
|
-
const
|
|
455
|
-
(
|
|
455
|
+
const f = await l.json(), g = (f?.rows || []).reduce(
|
|
456
|
+
(h, p) => h + parseInt(p.metricValues?.[0]?.value || "0", 10),
|
|
456
457
|
0
|
|
457
|
-
),
|
|
458
|
-
page:
|
|
459
|
-
activeUsers: parseInt(
|
|
458
|
+
), m = (f?.rows || []).map((h) => ({
|
|
459
|
+
page: h.dimensionValues?.[0]?.value || "(not set)",
|
|
460
|
+
activeUsers: parseInt(h.metricValues?.[0]?.value || "0", 10)
|
|
460
461
|
}));
|
|
461
|
-
return { activeUsers:
|
|
462
|
+
return { activeUsers: g, activePages: m };
|
|
462
463
|
})
|
|
463
|
-
),
|
|
464
|
+
), i.get(
|
|
464
465
|
"/top-pages",
|
|
465
|
-
|
|
466
|
-
const { accessToken:
|
|
467
|
-
dateRanges: [{ startDate:
|
|
466
|
+
A(async (o) => {
|
|
467
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = parseInt(l.limit || "10", 10), h = await a(u, d, {
|
|
468
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
468
469
|
dimensions: [{ name: "pagePath" }],
|
|
469
470
|
metrics: [
|
|
470
471
|
{ name: "screenPageViews" },
|
|
@@ -472,25 +473,25 @@ function W(A) {
|
|
|
472
473
|
{ name: "averageSessionDuration" }
|
|
473
474
|
],
|
|
474
475
|
orderBys: [{ metric: { metricName: "screenPageViews" }, desc: !0 }],
|
|
475
|
-
limit:
|
|
476
|
+
limit: m * 2
|
|
476
477
|
// fetch extra to account for duplicates before aggregation
|
|
477
|
-
}),
|
|
478
|
-
for (const
|
|
479
|
-
const
|
|
480
|
-
if (
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
478
|
+
}), p = P(h, "pagePath"), w = /* @__PURE__ */ new Map();
|
|
479
|
+
for (const b of p.rows) {
|
|
480
|
+
const D = b.pagePath.toLowerCase().replace(/\/+$/, ""), F = w.get(D);
|
|
481
|
+
if (F) {
|
|
482
|
+
F.screenPageViews += b.screenPageViews || 0, F.totalUsers += b.totalUsers || 0;
|
|
483
|
+
const M = F.screenPageViews;
|
|
484
|
+
M > 0 && (F.averageSessionDuration = (F.averageSessionDuration * (M - (b.screenPageViews || 0)) + (b.averageSessionDuration || 0) * (b.screenPageViews || 0)) / M);
|
|
484
485
|
} else
|
|
485
|
-
|
|
486
|
+
w.set(D, { ...b });
|
|
486
487
|
}
|
|
487
|
-
return { rows: [...
|
|
488
|
+
return { rows: [...w.values()].sort((b, D) => (D.screenPageViews || 0) - (b.screenPageViews || 0)).slice(0, m), rowCount: p.rowCount };
|
|
488
489
|
})
|
|
489
|
-
),
|
|
490
|
+
), i.get(
|
|
490
491
|
"/top-sources",
|
|
491
|
-
|
|
492
|
-
const { accessToken:
|
|
493
|
-
dateRanges: [{ startDate:
|
|
492
|
+
A(async (o) => {
|
|
493
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
494
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
494
495
|
dimensions: [{ name: "sessionSource" }],
|
|
495
496
|
metrics: [
|
|
496
497
|
{ name: "sessions" },
|
|
@@ -499,13 +500,13 @@ function W(A) {
|
|
|
499
500
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
500
501
|
limit: 10
|
|
501
502
|
});
|
|
502
|
-
return
|
|
503
|
+
return P(m, "sessionSource");
|
|
503
504
|
})
|
|
504
|
-
),
|
|
505
|
+
), i.get(
|
|
505
506
|
"/devices",
|
|
506
|
-
|
|
507
|
-
const { accessToken:
|
|
508
|
-
dateRanges: [{ startDate:
|
|
507
|
+
A(async (o) => {
|
|
508
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
509
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
509
510
|
dimensions: [{ name: "deviceCategory" }],
|
|
510
511
|
metrics: [
|
|
511
512
|
{ name: "sessions" },
|
|
@@ -513,13 +514,13 @@ function W(A) {
|
|
|
513
514
|
],
|
|
514
515
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }]
|
|
515
516
|
});
|
|
516
|
-
return
|
|
517
|
+
return P(m, "deviceCategory");
|
|
517
518
|
})
|
|
518
|
-
),
|
|
519
|
+
), i.get(
|
|
519
520
|
"/countries",
|
|
520
|
-
|
|
521
|
-
const { accessToken:
|
|
522
|
-
dateRanges: [{ startDate:
|
|
521
|
+
A(async (o) => {
|
|
522
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
523
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
523
524
|
dimensions: [{ name: "country" }],
|
|
524
525
|
metrics: [
|
|
525
526
|
{ name: "sessions" },
|
|
@@ -528,13 +529,13 @@ function W(A) {
|
|
|
528
529
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
529
530
|
limit: 10
|
|
530
531
|
});
|
|
531
|
-
return
|
|
532
|
+
return P(m, "country");
|
|
532
533
|
})
|
|
533
|
-
),
|
|
534
|
+
), i.get(
|
|
534
535
|
"/acquisition/channels",
|
|
535
|
-
|
|
536
|
-
const { accessToken:
|
|
537
|
-
dateRanges: [{ startDate:
|
|
536
|
+
A(async (o) => {
|
|
537
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
538
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
538
539
|
dimensions: [{ name: "sessionDefaultChannelGroup" }],
|
|
539
540
|
metrics: [
|
|
540
541
|
{ name: "sessions" },
|
|
@@ -548,13 +549,13 @@ function W(A) {
|
|
|
548
549
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
549
550
|
limit: 15
|
|
550
551
|
});
|
|
551
|
-
return
|
|
552
|
+
return P(m, "sessionDefaultChannelGroup");
|
|
552
553
|
})
|
|
553
|
-
),
|
|
554
|
+
), i.get(
|
|
554
555
|
"/acquisition/source-medium",
|
|
555
|
-
|
|
556
|
-
const { accessToken:
|
|
557
|
-
dateRanges: [{ startDate:
|
|
556
|
+
A(async (o) => {
|
|
557
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
558
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
558
559
|
dimensions: [{ name: "sessionSourceMedium" }],
|
|
559
560
|
metrics: [
|
|
560
561
|
{ name: "sessions" },
|
|
@@ -567,13 +568,13 @@ function W(A) {
|
|
|
567
568
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
568
569
|
limit: 20
|
|
569
570
|
});
|
|
570
|
-
return
|
|
571
|
+
return P(m, "sessionSourceMedium");
|
|
571
572
|
})
|
|
572
|
-
),
|
|
573
|
+
), i.get(
|
|
573
574
|
"/acquisition/referrals",
|
|
574
|
-
|
|
575
|
-
const { accessToken:
|
|
576
|
-
dateRanges: [{ startDate:
|
|
575
|
+
A(async (o) => {
|
|
576
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
577
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
577
578
|
dimensions: [{ name: "sessionSource" }],
|
|
578
579
|
dimensionFilter: {
|
|
579
580
|
filter: {
|
|
@@ -590,13 +591,13 @@ function W(A) {
|
|
|
590
591
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
591
592
|
limit: 20
|
|
592
593
|
});
|
|
593
|
-
return
|
|
594
|
+
return P(m, "sessionSource");
|
|
594
595
|
})
|
|
595
|
-
),
|
|
596
|
+
), i.get(
|
|
596
597
|
"/acquisition/campaigns",
|
|
597
|
-
|
|
598
|
-
const { accessToken:
|
|
599
|
-
dateRanges: [{ startDate:
|
|
598
|
+
A(async (o) => {
|
|
599
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
600
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
600
601
|
dimensions: [{ name: "sessionCampaignName" }],
|
|
601
602
|
dimensionFilter: {
|
|
602
603
|
notExpression: {
|
|
@@ -615,13 +616,13 @@ function W(A) {
|
|
|
615
616
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
616
617
|
limit: 20
|
|
617
618
|
});
|
|
618
|
-
return
|
|
619
|
+
return P(m, "sessionCampaignName");
|
|
619
620
|
})
|
|
620
|
-
),
|
|
621
|
+
), i.get(
|
|
621
622
|
"/content/all-pages",
|
|
622
|
-
|
|
623
|
-
const { accessToken:
|
|
624
|
-
dateRanges: [{ startDate:
|
|
623
|
+
A(async (o) => {
|
|
624
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = parseInt(l.limit || "50", 10), h = await a(u, d, {
|
|
625
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
625
626
|
dimensions: [{ name: "pagePath" }],
|
|
626
627
|
metrics: [
|
|
627
628
|
{ name: "screenPageViews" },
|
|
@@ -633,15 +634,15 @@ function W(A) {
|
|
|
633
634
|
{ name: "userEngagementDuration" }
|
|
634
635
|
],
|
|
635
636
|
orderBys: [{ metric: { metricName: "screenPageViews" }, desc: !0 }],
|
|
636
|
-
limit:
|
|
637
|
+
limit: m
|
|
637
638
|
});
|
|
638
|
-
return
|
|
639
|
+
return P(h, "pagePath");
|
|
639
640
|
})
|
|
640
|
-
),
|
|
641
|
+
), i.get(
|
|
641
642
|
"/content/landing-pages",
|
|
642
|
-
|
|
643
|
-
const { accessToken:
|
|
644
|
-
dateRanges: [{ startDate:
|
|
643
|
+
A(async (o) => {
|
|
644
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
645
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
645
646
|
dimensions: [{ name: "landingPagePlusQueryString" }],
|
|
646
647
|
metrics: [
|
|
647
648
|
{ name: "sessions" },
|
|
@@ -654,13 +655,13 @@ function W(A) {
|
|
|
654
655
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
655
656
|
limit: 30
|
|
656
657
|
});
|
|
657
|
-
return
|
|
658
|
+
return P(m, "landingPagePlusQueryString");
|
|
658
659
|
})
|
|
659
|
-
),
|
|
660
|
+
), i.get(
|
|
660
661
|
"/content/exit-pages",
|
|
661
|
-
|
|
662
|
-
const { accessToken:
|
|
663
|
-
dateRanges: [{ startDate:
|
|
662
|
+
A(async (o) => {
|
|
663
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
664
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
664
665
|
dimensions: [{ name: "pagePath" }],
|
|
665
666
|
metrics: [
|
|
666
667
|
{ name: "sessions" },
|
|
@@ -670,60 +671,60 @@ function W(A) {
|
|
|
670
671
|
],
|
|
671
672
|
orderBys: [{ metric: { metricName: "screenPageViews" }, desc: !0 }],
|
|
672
673
|
limit: 20
|
|
673
|
-
}),
|
|
674
|
-
return
|
|
675
|
-
...
|
|
676
|
-
exitRate:
|
|
677
|
-
})),
|
|
674
|
+
}), h = P(m, "pagePath");
|
|
675
|
+
return h.rows = h.rows.map((p) => ({
|
|
676
|
+
...p,
|
|
677
|
+
exitRate: p.bounceRate || 0
|
|
678
|
+
})), h;
|
|
678
679
|
})
|
|
679
|
-
),
|
|
680
|
+
), i.get(
|
|
680
681
|
"/content/search-terms",
|
|
681
|
-
|
|
682
|
-
const { accessToken:
|
|
683
|
-
if (
|
|
682
|
+
A(async (o) => {
|
|
683
|
+
const { accessToken: u, propertyId: d, siteUrl: l } = await r(o), f = R(o), g = f.startDate || "30daysAgo", m = f.endDate || "today";
|
|
684
|
+
if (l)
|
|
684
685
|
try {
|
|
685
|
-
const
|
|
686
|
-
|
|
686
|
+
const w = `${Fe}/sites/${encodeURIComponent(l)}/searchAnalytics/query`, C = await fetch(
|
|
687
|
+
w,
|
|
687
688
|
{
|
|
688
689
|
method: "POST",
|
|
689
690
|
headers: {
|
|
690
|
-
Authorization: `Bearer ${
|
|
691
|
+
Authorization: `Bearer ${u}`,
|
|
691
692
|
"Content-Type": "application/json"
|
|
692
693
|
},
|
|
693
694
|
body: JSON.stringify({
|
|
694
|
-
startDate:
|
|
695
|
-
endDate:
|
|
695
|
+
startDate: n(g),
|
|
696
|
+
endDate: n(m),
|
|
696
697
|
dimensions: ["query"],
|
|
697
698
|
rowLimit: 30
|
|
698
699
|
})
|
|
699
700
|
}
|
|
700
701
|
);
|
|
701
|
-
if (
|
|
702
|
-
const
|
|
703
|
-
query:
|
|
704
|
-
clicks:
|
|
705
|
-
impressions:
|
|
706
|
-
ctr:
|
|
707
|
-
position:
|
|
702
|
+
if (C.ok) {
|
|
703
|
+
const D = ((await C.json()).rows || []).map((F) => ({
|
|
704
|
+
query: F.keys[0],
|
|
705
|
+
clicks: F.clicks,
|
|
706
|
+
impressions: F.impressions,
|
|
707
|
+
ctr: F.ctr,
|
|
708
|
+
position: F.position
|
|
708
709
|
}));
|
|
709
|
-
if (
|
|
710
|
+
if (D.length > 0)
|
|
710
711
|
return {
|
|
711
|
-
rows:
|
|
712
|
-
rowCount:
|
|
712
|
+
rows: D,
|
|
713
|
+
rowCount: D.length,
|
|
713
714
|
source: "search_console"
|
|
714
715
|
};
|
|
715
716
|
} else {
|
|
716
|
-
const
|
|
717
|
-
console.error(`[GA Module] Search Console API error (${
|
|
717
|
+
const b = await C.text().catch(() => "");
|
|
718
|
+
console.error(`[GA Module] Search Console API error (${C.status}):`, b);
|
|
718
719
|
}
|
|
719
|
-
} catch (
|
|
720
|
-
console.error("[GA Module] Search Console request failed:",
|
|
720
|
+
} catch (w) {
|
|
721
|
+
console.error("[GA Module] Search Console request failed:", w?.message || w);
|
|
721
722
|
}
|
|
722
723
|
else
|
|
723
724
|
console.log("[GA Module] No siteUrl configured, skipping Search Console");
|
|
724
725
|
try {
|
|
725
|
-
const
|
|
726
|
-
dateRanges: [{ startDate:
|
|
726
|
+
const w = await a(u, d, {
|
|
727
|
+
dateRanges: [{ startDate: g, endDate: m }],
|
|
727
728
|
dimensions: [{ name: "sessionGoogleAdsQuery" }],
|
|
728
729
|
metrics: [
|
|
729
730
|
{ name: "sessions" },
|
|
@@ -754,13 +755,13 @@ function W(A) {
|
|
|
754
755
|
},
|
|
755
756
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
756
757
|
limit: 30
|
|
757
|
-
}),
|
|
758
|
-
if (
|
|
759
|
-
return { ...
|
|
758
|
+
}), C = P(w, "sessionGoogleAdsQuery");
|
|
759
|
+
if (C.rows.length > 0)
|
|
760
|
+
return { ...C, source: "google_ads" };
|
|
760
761
|
} catch {
|
|
761
762
|
}
|
|
762
|
-
const
|
|
763
|
-
dateRanges: [{ startDate:
|
|
763
|
+
const h = await a(u, d, {
|
|
764
|
+
dateRanges: [{ startDate: g, endDate: m }],
|
|
764
765
|
dimensions: [{ name: "landingPagePlusQueryString" }],
|
|
765
766
|
metrics: [
|
|
766
767
|
{ name: "sessions" },
|
|
@@ -793,13 +794,13 @@ function W(A) {
|
|
|
793
794
|
orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
|
|
794
795
|
limit: 30
|
|
795
796
|
});
|
|
796
|
-
return { ...
|
|
797
|
+
return { ...P(h, "landingPagePlusQueryString"), source: "organic_landing_pages" };
|
|
797
798
|
})
|
|
798
|
-
),
|
|
799
|
+
), i.get(
|
|
799
800
|
"/audience/overview",
|
|
800
|
-
|
|
801
|
-
const { accessToken:
|
|
802
|
-
dateRanges: [{ startDate:
|
|
801
|
+
A(async (o) => {
|
|
802
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
803
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
803
804
|
dimensions: [{ name: "newVsReturning" }],
|
|
804
805
|
metrics: [
|
|
805
806
|
{ name: "totalUsers" },
|
|
@@ -810,14 +811,14 @@ function W(A) {
|
|
|
810
811
|
],
|
|
811
812
|
metricAggregations: ["TOTAL"]
|
|
812
813
|
});
|
|
813
|
-
return
|
|
814
|
+
return P(m, "newVsReturning");
|
|
814
815
|
})
|
|
815
|
-
),
|
|
816
|
+
), i.get(
|
|
816
817
|
"/audience/technology",
|
|
817
|
-
|
|
818
|
-
const { accessToken:
|
|
819
|
-
dateRanges: [{ startDate:
|
|
820
|
-
dimensions: [{ name:
|
|
818
|
+
A(async (o) => {
|
|
819
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = l.dimension || "browser", p = ["browser", "operatingSystem", "screenResolution"].includes(m) ? m : "browser", w = await a(u, d, {
|
|
820
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
821
|
+
dimensions: [{ name: p }],
|
|
821
822
|
metrics: [
|
|
822
823
|
{ name: "totalUsers" },
|
|
823
824
|
{ name: "sessions" },
|
|
@@ -826,13 +827,13 @@ function W(A) {
|
|
|
826
827
|
orderBys: [{ metric: { metricName: "totalUsers" }, desc: !0 }],
|
|
827
828
|
limit: 10
|
|
828
829
|
});
|
|
829
|
-
return
|
|
830
|
+
return P(w, p);
|
|
830
831
|
})
|
|
831
|
-
),
|
|
832
|
+
), i.get(
|
|
832
833
|
"/audience/languages",
|
|
833
|
-
|
|
834
|
-
const { accessToken:
|
|
835
|
-
dateRanges: [{ startDate:
|
|
834
|
+
A(async (o) => {
|
|
835
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
836
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
836
837
|
dimensions: [{ name: "language" }],
|
|
837
838
|
metrics: [
|
|
838
839
|
{ name: "totalUsers" },
|
|
@@ -841,13 +842,13 @@ function W(A) {
|
|
|
841
842
|
orderBys: [{ metric: { metricName: "totalUsers" }, desc: !0 }],
|
|
842
843
|
limit: 15
|
|
843
844
|
});
|
|
844
|
-
return
|
|
845
|
+
return P(m, "language");
|
|
845
846
|
})
|
|
846
|
-
),
|
|
847
|
+
), i.get(
|
|
847
848
|
"/audience/hours",
|
|
848
|
-
|
|
849
|
-
const { accessToken:
|
|
850
|
-
dateRanges: [{ startDate:
|
|
849
|
+
A(async (o) => {
|
|
850
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
851
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
851
852
|
dimensions: [{ name: "dayOfWeekName" }, { name: "hour" }],
|
|
852
853
|
metrics: [{ name: "sessions" }],
|
|
853
854
|
orderBys: [
|
|
@@ -857,13 +858,13 @@ function W(A) {
|
|
|
857
858
|
limit: 168
|
|
858
859
|
// 7 days × 24 hours
|
|
859
860
|
});
|
|
860
|
-
return
|
|
861
|
+
return Ie(m, ["dayOfWeekName", "hour"]);
|
|
861
862
|
})
|
|
862
|
-
),
|
|
863
|
+
), i.get(
|
|
863
864
|
"/audience/cities",
|
|
864
|
-
|
|
865
|
-
const { accessToken:
|
|
866
|
-
dateRanges: [{ startDate:
|
|
865
|
+
A(async (o) => {
|
|
866
|
+
const { accessToken: u, propertyId: d } = await r(o), l = R(o), f = l.startDate || "30daysAgo", g = l.endDate || "today", m = await a(u, d, {
|
|
867
|
+
dateRanges: [{ startDate: f, endDate: g }],
|
|
867
868
|
dimensions: [{ name: "city" }, { name: "country" }],
|
|
868
869
|
metrics: [
|
|
869
870
|
{ name: "totalUsers" },
|
|
@@ -880,236 +881,1394 @@ function W(A) {
|
|
|
880
881
|
orderBys: [{ metric: { metricName: "totalUsers" }, desc: !0 }],
|
|
881
882
|
limit: 20
|
|
882
883
|
});
|
|
883
|
-
return
|
|
884
|
+
return Ie(m, ["city", "country"]);
|
|
884
885
|
})
|
|
885
886
|
);
|
|
886
|
-
function
|
|
887
|
-
const
|
|
888
|
-
if (
|
|
889
|
-
const
|
|
890
|
-
return
|
|
887
|
+
function n(o) {
|
|
888
|
+
const u = o.match(/^(\d+)daysAgo$/);
|
|
889
|
+
if (u) {
|
|
890
|
+
const d = /* @__PURE__ */ new Date();
|
|
891
|
+
return d.setDate(d.getDate() - parseInt(u[1], 10)), d.toISOString().slice(0, 10);
|
|
891
892
|
}
|
|
892
|
-
if (
|
|
893
|
-
if (
|
|
894
|
-
const
|
|
895
|
-
return
|
|
893
|
+
if (o === "today") return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
894
|
+
if (o === "yesterday") {
|
|
895
|
+
const d = /* @__PURE__ */ new Date();
|
|
896
|
+
return d.setDate(d.getDate() - 1), d.toISOString().slice(0, 10);
|
|
896
897
|
}
|
|
897
|
-
return
|
|
898
|
+
return o;
|
|
898
899
|
}
|
|
899
|
-
async function
|
|
900
|
-
const
|
|
900
|
+
async function c(o, u, d) {
|
|
901
|
+
const l = `${Fe}/sites/${encodeURIComponent(u)}/searchAnalytics/query`, f = await fetch(l, {
|
|
901
902
|
method: "POST",
|
|
902
903
|
headers: {
|
|
903
|
-
Authorization: `Bearer ${
|
|
904
|
+
Authorization: `Bearer ${o}`,
|
|
904
905
|
"Content-Type": "application/json"
|
|
905
906
|
},
|
|
906
|
-
body: JSON.stringify(
|
|
907
|
+
body: JSON.stringify(d)
|
|
907
908
|
});
|
|
908
|
-
if (!
|
|
909
|
-
const
|
|
910
|
-
throw
|
|
911
|
-
statusCode:
|
|
912
|
-
statusMessage:
|
|
909
|
+
if (!f.ok) {
|
|
910
|
+
const g = await f.json().catch(() => ({}));
|
|
911
|
+
throw y({
|
|
912
|
+
statusCode: f.status,
|
|
913
|
+
statusMessage: g?.error?.message || "Search Console API request failed"
|
|
913
914
|
});
|
|
914
915
|
}
|
|
915
|
-
return
|
|
916
|
+
return f.json();
|
|
916
917
|
}
|
|
917
|
-
return
|
|
918
|
+
return i.get(
|
|
918
919
|
"/seo/keywords",
|
|
919
|
-
|
|
920
|
-
const { accessToken:
|
|
921
|
-
if (!
|
|
922
|
-
throw
|
|
923
|
-
const
|
|
924
|
-
startDate:
|
|
925
|
-
endDate:
|
|
920
|
+
A(async (o) => {
|
|
921
|
+
const { accessToken: u, siteUrl: d } = await r(o);
|
|
922
|
+
if (!d)
|
|
923
|
+
throw y({ statusCode: 400, statusMessage: "Search Console Site URL is not configured. Add it in module settings." });
|
|
924
|
+
const l = R(o), f = n(l.startDate || "30daysAgo"), g = n(l.endDate || "today"), m = Math.min(parseInt(l.limit) || 50, 100), h = await c(u, d, {
|
|
925
|
+
startDate: f,
|
|
926
|
+
endDate: g,
|
|
926
927
|
dimensions: ["query"],
|
|
927
|
-
rowLimit:
|
|
928
|
+
rowLimit: m
|
|
928
929
|
});
|
|
929
930
|
return {
|
|
930
|
-
rows: (
|
|
931
|
-
query:
|
|
932
|
-
clicks:
|
|
933
|
-
impressions:
|
|
934
|
-
ctr:
|
|
935
|
-
position:
|
|
931
|
+
rows: (h.rows || []).map((p) => ({
|
|
932
|
+
query: p.keys[0],
|
|
933
|
+
clicks: p.clicks,
|
|
934
|
+
impressions: p.impressions,
|
|
935
|
+
ctr: p.ctr,
|
|
936
|
+
position: p.position
|
|
936
937
|
})),
|
|
937
|
-
rowCount:
|
|
938
|
+
rowCount: h.rows?.length || 0
|
|
938
939
|
};
|
|
939
940
|
})
|
|
940
|
-
),
|
|
941
|
+
), i.get(
|
|
941
942
|
"/seo/pages",
|
|
942
|
-
|
|
943
|
-
const { accessToken:
|
|
944
|
-
if (!
|
|
945
|
-
throw
|
|
946
|
-
const
|
|
947
|
-
startDate:
|
|
948
|
-
endDate:
|
|
943
|
+
A(async (o) => {
|
|
944
|
+
const { accessToken: u, siteUrl: d } = await r(o);
|
|
945
|
+
if (!d)
|
|
946
|
+
throw y({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
|
|
947
|
+
const l = R(o), f = n(l.startDate || "30daysAgo"), g = n(l.endDate || "today"), m = Math.min(parseInt(l.limit) || 50, 100), h = await c(u, d, {
|
|
948
|
+
startDate: f,
|
|
949
|
+
endDate: g,
|
|
949
950
|
dimensions: ["page"],
|
|
950
|
-
rowLimit:
|
|
951
|
+
rowLimit: m
|
|
951
952
|
});
|
|
952
953
|
return {
|
|
953
|
-
rows: (
|
|
954
|
-
page:
|
|
955
|
-
clicks:
|
|
956
|
-
impressions:
|
|
957
|
-
ctr:
|
|
958
|
-
position:
|
|
954
|
+
rows: (h.rows || []).map((p) => ({
|
|
955
|
+
page: p.keys[0],
|
|
956
|
+
clicks: p.clicks,
|
|
957
|
+
impressions: p.impressions,
|
|
958
|
+
ctr: p.ctr,
|
|
959
|
+
position: p.position
|
|
959
960
|
})),
|
|
960
|
-
rowCount:
|
|
961
|
+
rowCount: h.rows?.length || 0
|
|
961
962
|
};
|
|
962
963
|
})
|
|
963
|
-
),
|
|
964
|
+
), i.get(
|
|
964
965
|
"/seo/trends",
|
|
965
|
-
|
|
966
|
-
const { accessToken:
|
|
967
|
-
if (!
|
|
968
|
-
throw
|
|
969
|
-
const
|
|
970
|
-
startDate:
|
|
971
|
-
endDate:
|
|
966
|
+
A(async (o) => {
|
|
967
|
+
const { accessToken: u, siteUrl: d } = await r(o);
|
|
968
|
+
if (!d)
|
|
969
|
+
throw y({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
|
|
970
|
+
const l = R(o), f = n(l.startDate || "30daysAgo"), g = n(l.endDate || "today"), h = ((await c(u, d, {
|
|
971
|
+
startDate: f,
|
|
972
|
+
endDate: g,
|
|
972
973
|
dimensions: ["date"],
|
|
973
974
|
rowLimit: 500
|
|
974
|
-
})).rows || []).map((
|
|
975
|
-
date:
|
|
976
|
-
clicks:
|
|
977
|
-
impressions:
|
|
978
|
-
ctr:
|
|
979
|
-
position:
|
|
980
|
-
})).sort((
|
|
981
|
-
(
|
|
982
|
-
clicks:
|
|
983
|
-
impressions:
|
|
975
|
+
})).rows || []).map((w) => ({
|
|
976
|
+
date: w.keys[0],
|
|
977
|
+
clicks: w.clicks,
|
|
978
|
+
impressions: w.impressions,
|
|
979
|
+
ctr: w.ctr,
|
|
980
|
+
position: w.position
|
|
981
|
+
})).sort((w, C) => w.date.localeCompare(C.date)), p = h.reduce(
|
|
982
|
+
(w, C) => ({
|
|
983
|
+
clicks: w.clicks + C.clicks,
|
|
984
|
+
impressions: w.impressions + C.impressions
|
|
984
985
|
}),
|
|
985
986
|
{ clicks: 0, impressions: 0 }
|
|
986
987
|
);
|
|
987
|
-
return
|
|
988
|
+
return p.ctr = p.impressions > 0 ? p.clicks / p.impressions : 0, p.avgPosition = h.length > 0 ? h.reduce((w, C) => w + C.position, 0) / h.length : 0, { rows: h, totals: p, rowCount: h.length };
|
|
988
989
|
})
|
|
989
|
-
),
|
|
990
|
+
), i.get(
|
|
990
991
|
"/seo/query-pages",
|
|
991
|
-
|
|
992
|
-
const { accessToken:
|
|
993
|
-
if (!
|
|
994
|
-
throw
|
|
995
|
-
const
|
|
996
|
-
startDate:
|
|
997
|
-
endDate:
|
|
992
|
+
A(async (o) => {
|
|
993
|
+
const { accessToken: u, siteUrl: d } = await r(o);
|
|
994
|
+
if (!d)
|
|
995
|
+
throw y({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
|
|
996
|
+
const l = R(o), f = n(l.startDate || "30daysAgo"), g = n(l.endDate || "today"), m = await c(u, d, {
|
|
997
|
+
startDate: f,
|
|
998
|
+
endDate: g,
|
|
998
999
|
dimensions: ["query", "page"],
|
|
999
1000
|
rowLimit: 100
|
|
1000
1001
|
});
|
|
1001
1002
|
return {
|
|
1002
|
-
rows: (
|
|
1003
|
-
query:
|
|
1004
|
-
page:
|
|
1005
|
-
clicks:
|
|
1006
|
-
impressions:
|
|
1007
|
-
ctr:
|
|
1008
|
-
position:
|
|
1003
|
+
rows: (m.rows || []).map((h) => ({
|
|
1004
|
+
query: h.keys[0],
|
|
1005
|
+
page: h.keys[1],
|
|
1006
|
+
clicks: h.clicks,
|
|
1007
|
+
impressions: h.impressions,
|
|
1008
|
+
ctr: h.ctr,
|
|
1009
|
+
position: h.position
|
|
1009
1010
|
})),
|
|
1010
|
-
rowCount:
|
|
1011
|
+
rowCount: m.rows?.length || 0
|
|
1011
1012
|
};
|
|
1012
1013
|
})
|
|
1013
|
-
),
|
|
1014
|
-
}
|
|
1015
|
-
function
|
|
1016
|
-
const
|
|
1017
|
-
(
|
|
1018
|
-
const
|
|
1019
|
-
if (!
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
}),
|
|
1014
|
+
), i.handler;
|
|
1015
|
+
}
|
|
1016
|
+
function et(e, t = !0) {
|
|
1017
|
+
const s = (e.metricHeaders || []).map((f) => f.name), r = (e.dimensionHeaders || []).map((f) => f.name).indexOf("date"), a = [];
|
|
1018
|
+
(e.rows || []).forEach((f) => {
|
|
1019
|
+
const g = r >= 0 ? f.dimensionValues[r]?.value : f.dimensionValues[0]?.value;
|
|
1020
|
+
if (!g || g.length < 8) return;
|
|
1021
|
+
const h = { date: `${g.slice(0, 4)}-${g.slice(4, 6)}-${g.slice(6, 8)}` };
|
|
1022
|
+
s.forEach((p, w) => {
|
|
1023
|
+
h[p] = parseFloat(f.metricValues[w]?.value || "0");
|
|
1024
|
+
}), a.push(h);
|
|
1024
1025
|
});
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
}) :
|
|
1029
|
-
|
|
1026
|
+
const n = {}, c = {};
|
|
1027
|
+
e.totals && e.totals.length >= 2 ? s.forEach((f, g) => {
|
|
1028
|
+
n[f] = parseFloat(e.totals[0]?.metricValues?.[g]?.value || "0"), c[f] = parseFloat(e.totals[1]?.metricValues?.[g]?.value || "0");
|
|
1029
|
+
}) : e.totals && e.totals.length === 1 && s.forEach((f, g) => {
|
|
1030
|
+
n[f] = parseFloat(e.totals[0]?.metricValues?.[g]?.value || "0");
|
|
1030
1031
|
});
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1032
|
+
const o = {};
|
|
1033
|
+
s.forEach((f) => {
|
|
1034
|
+
const g = n[f] || 0, m = c[f];
|
|
1035
|
+
m !== void 0 && m !== 0 ? o[f] = (g - m) / m * 100 : o[f] = null;
|
|
1035
1036
|
});
|
|
1036
|
-
const
|
|
1037
|
-
for (const
|
|
1038
|
-
|
|
1039
|
-
const
|
|
1037
|
+
const u = /* @__PURE__ */ new Map();
|
|
1038
|
+
for (const f of a)
|
|
1039
|
+
u.has(f.date) || u.set(f.date, f);
|
|
1040
|
+
const d = Array.from(u.values()).sort((f, g) => f.date.localeCompare(g.date)), l = t && e.totals && e.totals.length >= 2 ? d.slice(-Math.ceil(d.length / 2)) : d;
|
|
1040
1041
|
return {
|
|
1041
|
-
rows:
|
|
1042
|
-
totals:
|
|
1043
|
-
previousTotals:
|
|
1044
|
-
changes:
|
|
1045
|
-
rowCount:
|
|
1042
|
+
rows: l,
|
|
1043
|
+
totals: n,
|
|
1044
|
+
previousTotals: c,
|
|
1045
|
+
changes: o,
|
|
1046
|
+
rowCount: l.length
|
|
1046
1047
|
};
|
|
1047
1048
|
}
|
|
1048
|
-
function
|
|
1049
|
-
const
|
|
1050
|
-
const
|
|
1051
|
-
[
|
|
1049
|
+
function P(e, t) {
|
|
1050
|
+
const s = (e.metricHeaders || []).map((r) => r.name), i = (e.rows || []).map((r) => {
|
|
1051
|
+
const a = {
|
|
1052
|
+
[t]: r.dimensionValues[0].value
|
|
1052
1053
|
};
|
|
1053
|
-
return
|
|
1054
|
-
|
|
1055
|
-
}),
|
|
1054
|
+
return s.forEach((n, c) => {
|
|
1055
|
+
a[n] = parseFloat(r.metricValues[c].value);
|
|
1056
|
+
}), a;
|
|
1056
1057
|
});
|
|
1057
|
-
return { rows:
|
|
1058
|
-
}
|
|
1059
|
-
function
|
|
1060
|
-
const
|
|
1061
|
-
const
|
|
1062
|
-
return
|
|
1063
|
-
|
|
1064
|
-
}),
|
|
1065
|
-
|
|
1066
|
-
}),
|
|
1058
|
+
return { rows: i, rowCount: e.rowCount || i.length };
|
|
1059
|
+
}
|
|
1060
|
+
function Ie(e, t) {
|
|
1061
|
+
const s = (e.metricHeaders || []).map((r) => r.name), i = (e.rows || []).map((r) => {
|
|
1062
|
+
const a = {};
|
|
1063
|
+
return t.forEach((n, c) => {
|
|
1064
|
+
a[n] = r.dimensionValues[c]?.value || "";
|
|
1065
|
+
}), s.forEach((n, c) => {
|
|
1066
|
+
a[n] = parseFloat(r.metricValues[c].value);
|
|
1067
|
+
}), a;
|
|
1067
1068
|
});
|
|
1068
|
-
return { rows:
|
|
1069
|
-
}
|
|
1070
|
-
function
|
|
1071
|
-
const { decrypt:
|
|
1072
|
-
async function
|
|
1073
|
-
const { supabase:
|
|
1074
|
-
if (
|
|
1075
|
-
throw
|
|
1076
|
-
const
|
|
1077
|
-
if (!
|
|
1078
|
-
throw
|
|
1079
|
-
const { data:
|
|
1080
|
-
let
|
|
1081
|
-
if (
|
|
1082
|
-
const { data:
|
|
1083
|
-
if (
|
|
1084
|
-
throw
|
|
1085
|
-
|
|
1069
|
+
return { rows: i, rowCount: e.rowCount || i.length };
|
|
1070
|
+
}
|
|
1071
|
+
function Kt(e) {
|
|
1072
|
+
const { decrypt: t } = e, s = se();
|
|
1073
|
+
async function i(r) {
|
|
1074
|
+
const { supabase: a, instanceId: n } = r.context.module, { data: c, error: o } = await a.from("project_modules").select("config").eq("id", n).single();
|
|
1075
|
+
if (o || !c?.config)
|
|
1076
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load module config." });
|
|
1077
|
+
const u = c.config, d = u.githubIntegration, l = u.githubRepo, f = u.githubOwner;
|
|
1078
|
+
if (!d || !l || !f)
|
|
1079
|
+
throw y({ statusCode: 400, statusMessage: "No GitHub integration configured for this module." });
|
|
1080
|
+
const { data: g, error: m } = await a.from("integrations").select("config").eq("id", d).single();
|
|
1081
|
+
let h = g?.config;
|
|
1082
|
+
if (m || !h) {
|
|
1083
|
+
const { data: w, error: C } = await a.from("agency_integrations").select("config").eq("id", d).single();
|
|
1084
|
+
if (C || !w?.config)
|
|
1085
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load Github credentials." });
|
|
1086
|
+
h = w.config;
|
|
1086
1087
|
}
|
|
1087
|
-
return { token:
|
|
1088
|
+
return { token: t(h.token), repo: l, owner: f };
|
|
1088
1089
|
}
|
|
1089
|
-
return
|
|
1090
|
+
return s.post(
|
|
1090
1091
|
"/github/deploy",
|
|
1091
|
-
|
|
1092
|
+
A(async (r) => {
|
|
1092
1093
|
try {
|
|
1093
|
-
const { token:
|
|
1094
|
-
await fetch(`https://api.github.com/repos/${
|
|
1094
|
+
const { token: a, repo: n, owner: c } = await i(r);
|
|
1095
|
+
await fetch(`https://api.github.com/repos/${c}/${n}/dispatches`, {
|
|
1095
1096
|
method: "POST",
|
|
1096
1097
|
headers: {
|
|
1097
|
-
Authorization: `Bearer ${
|
|
1098
|
+
Authorization: `Bearer ${a}`,
|
|
1098
1099
|
Accept: "application/vnd.github+json"
|
|
1099
1100
|
},
|
|
1100
1101
|
body: JSON.stringify({
|
|
1101
1102
|
event_type: "deploy-site"
|
|
1102
1103
|
})
|
|
1103
1104
|
});
|
|
1104
|
-
} catch (
|
|
1105
|
-
throw console.error("Error triggering GitHub deployment:",
|
|
1105
|
+
} catch (a) {
|
|
1106
|
+
throw console.error("Error triggering GitHub deployment:", a), y({ statusCode: 500, statusMessage: "Failed to trigger deployment." });
|
|
1106
1107
|
}
|
|
1107
1108
|
})
|
|
1108
|
-
),
|
|
1109
|
+
), s.handler;
|
|
1110
|
+
}
|
|
1111
|
+
const Oe = "firebaseAuthManager", v = {
|
|
1112
|
+
viewUsers: "view-auth-users",
|
|
1113
|
+
viewPii: "view-auth-user-pii",
|
|
1114
|
+
createUsers: "create-auth-users",
|
|
1115
|
+
editUsers: "edit-auth-users",
|
|
1116
|
+
manageStatus: "manage-auth-status",
|
|
1117
|
+
manageSessions: "manage-auth-sessions",
|
|
1118
|
+
manageMfa: "manage-auth-mfa",
|
|
1119
|
+
manageRoles: "manage-auth-roles",
|
|
1120
|
+
manageProfiles: "manage-auth-profiles",
|
|
1121
|
+
deleteUsers: "delete-auth-users"
|
|
1122
|
+
}, Be = B().trim().email().max(254), he = B().min(1).max(128), tt = he.refine((e) => e === e.trim(), {
|
|
1123
|
+
message: "Custom UID may not begin or end with whitespace."
|
|
1124
|
+
}), je = B().trim().regex(/^\+[1-9]\d{1,14}$/, "Phone number must use E.164 format."), st = B().email().max(254), at = B().regex(/^\+[1-9]\d{1,14}$/, "Phone number must use E.164 format."), ne = B().trim().min(3).max(500), $e = B().trim().min(1).max(128), ot = ae({
|
|
1125
|
+
email: Be,
|
|
1126
|
+
password: B().min(8).max(128),
|
|
1127
|
+
uid: tt.optional(),
|
|
1128
|
+
displayName: $e.optional(),
|
|
1129
|
+
phoneNumber: je.optional(),
|
|
1130
|
+
emailVerified: re().optional(),
|
|
1131
|
+
disabled: re().optional(),
|
|
1132
|
+
createProfile: re().optional().default(!1)
|
|
1133
|
+
}).strict(), rt = ae({
|
|
1134
|
+
reason: ne,
|
|
1135
|
+
displayName: $e.nullable().optional(),
|
|
1136
|
+
email: Be.optional(),
|
|
1137
|
+
phoneNumber: je.nullable().optional(),
|
|
1138
|
+
emailVerified: re().optional()
|
|
1139
|
+
}).strict().refine((e) => Y(e, "displayName") || Y(e, "email") || Y(e, "phoneNumber") || Y(e, "emailVerified"), {
|
|
1140
|
+
message: "At least one editable user field is required."
|
|
1141
|
+
}), nt = ae({
|
|
1142
|
+
disabled: re(),
|
|
1143
|
+
reason: ne
|
|
1144
|
+
}).strict(), fe = ae({
|
|
1145
|
+
reason: ne
|
|
1146
|
+
}).strict(), it = ae({
|
|
1147
|
+
role: B().trim().min(1).max(64).nullable(),
|
|
1148
|
+
reason: ne
|
|
1149
|
+
}).strict(), ct = ae({
|
|
1150
|
+
reason: ne,
|
|
1151
|
+
// Deliberately do not trim this value: destructive confirmation must be an
|
|
1152
|
+
// exact byte-for-byte match with the UID shown to the operator.
|
|
1153
|
+
confirmation: B().min(1).max(128)
|
|
1154
|
+
}).strict(), dt = /* @__PURE__ */ new Set([
|
|
1155
|
+
"acr",
|
|
1156
|
+
"amr",
|
|
1157
|
+
"at_hash",
|
|
1158
|
+
"aud",
|
|
1159
|
+
"auth_time",
|
|
1160
|
+
"azp",
|
|
1161
|
+
"cnf",
|
|
1162
|
+
"c_hash",
|
|
1163
|
+
"email",
|
|
1164
|
+
"email_verified",
|
|
1165
|
+
"exp",
|
|
1166
|
+
"firebase",
|
|
1167
|
+
"iat",
|
|
1168
|
+
"iss",
|
|
1169
|
+
"jti",
|
|
1170
|
+
"name",
|
|
1171
|
+
"nbf",
|
|
1172
|
+
"nonce",
|
|
1173
|
+
"phone_number",
|
|
1174
|
+
"picture",
|
|
1175
|
+
"sub",
|
|
1176
|
+
"user_id"
|
|
1177
|
+
]), ut = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1178
|
+
function Y(e, t) {
|
|
1179
|
+
return Object.prototype.hasOwnProperty.call(e, t);
|
|
1180
|
+
}
|
|
1181
|
+
function K(e) {
|
|
1182
|
+
throw y({ statusCode: 400, statusMessage: e });
|
|
1183
|
+
}
|
|
1184
|
+
function Re(e, t) {
|
|
1185
|
+
return typeof e == "boolean" ? e : e === "true" ? !0 : e === "false" ? !1 : t;
|
|
1186
|
+
}
|
|
1187
|
+
function Ve(e) {
|
|
1188
|
+
const t = typeof e == "number" ? e : Number(e);
|
|
1189
|
+
return Number.isFinite(t) ? Math.min(100, Math.max(10, Math.trunc(t))) : 50;
|
|
1190
|
+
}
|
|
1191
|
+
function lt(e) {
|
|
1192
|
+
const t = String(e || "users").trim();
|
|
1193
|
+
return t.length === 0 || t.length > 256 || t.includes("/") || t === "." || t === ".." || /^__.*__$/.test(t) ? K("The configured Firestore profile collection is invalid.") : t;
|
|
1194
|
+
}
|
|
1195
|
+
function W(e, t, s) {
|
|
1196
|
+
const i = String(e ?? t).trim() || t;
|
|
1197
|
+
return i.length > 128 || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(i) || ut.has(i) ? K(`The configured ${s} field is invalid.`) : i;
|
|
1198
|
+
}
|
|
1199
|
+
function mt(e) {
|
|
1200
|
+
const t = Array.isArray(e) ? e : String(e || "").split(","), s = [];
|
|
1201
|
+
for (const i of t) {
|
|
1202
|
+
if (typeof i != "string")
|
|
1203
|
+
return K("Configured role options must be strings.");
|
|
1204
|
+
const r = i.trim();
|
|
1205
|
+
if (r) {
|
|
1206
|
+
if (r.length > 64)
|
|
1207
|
+
return K("Configured role options may not exceed 64 characters.");
|
|
1208
|
+
s.includes(r) || s.push(r);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
return s.length > 50 ? K("No more than 50 role options may be configured.") : s;
|
|
1212
|
+
}
|
|
1213
|
+
function ft(e) {
|
|
1214
|
+
const t = typeof e.project == "string" ? e.project.trim() : "";
|
|
1215
|
+
t || K("Firebase Auth Manager has no Firebase integration configured.");
|
|
1216
|
+
const s = Re(e.manageRoleClaim, !1), i = W(e.roleClaimKey, "role", "role claim");
|
|
1217
|
+
s && dt.has(i) && K("The configured role claim key is reserved by Firebase.");
|
|
1218
|
+
const r = {
|
|
1219
|
+
profileUidField: W(e.profileUidField, "uid", "profile UID"),
|
|
1220
|
+
profileNameField: W(e.profileNameField, "displayName", "profile name"),
|
|
1221
|
+
profileEmailField: W(e.profileEmailField, "email", "profile email"),
|
|
1222
|
+
profileRoleField: W(e.profileRoleField, "role", "profile role"),
|
|
1223
|
+
profileStatusField: W(e.profileStatusField, "status", "profile status")
|
|
1224
|
+
};
|
|
1225
|
+
return new Set(Object.values(r)).size !== Object.keys(r).length && K("Each configured Firestore profile field must use a distinct field name."), {
|
|
1226
|
+
project: t,
|
|
1227
|
+
pageSize: Ve(e.pageSize),
|
|
1228
|
+
linkFirestoreProfiles: Re(e.linkFirestoreProfiles, !0),
|
|
1229
|
+
profileCollection: lt(e.profileCollection),
|
|
1230
|
+
...r,
|
|
1231
|
+
manageRoleClaim: s,
|
|
1232
|
+
roleClaimKey: i,
|
|
1233
|
+
roleOptions: mt(e.roleOptions)
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
function Me(e) {
|
|
1237
|
+
const t = Array.isArray(e) ? e[0] : e;
|
|
1238
|
+
return typeof t != "string" ? void 0 : t.trim() || void 0;
|
|
1239
|
+
}
|
|
1240
|
+
function Se(e) {
|
|
1241
|
+
const t = Array.isArray(e) ? e[0] : e;
|
|
1242
|
+
if (!(typeof t != "string" || t.length === 0))
|
|
1243
|
+
return t;
|
|
1244
|
+
}
|
|
1245
|
+
function gt(e, t = 50) {
|
|
1246
|
+
const s = Me(e.limit);
|
|
1247
|
+
let i = Ve(t);
|
|
1248
|
+
if (s !== void 0) {
|
|
1249
|
+
if (!/^\d+$/.test(s))
|
|
1250
|
+
throw y({ statusCode: 422, statusMessage: "Limit must be a whole number." });
|
|
1251
|
+
if (i = Number(s), i < 1 || i > 100)
|
|
1252
|
+
throw y({ statusCode: 422, statusMessage: "Limit must be between 1 and 100." });
|
|
1253
|
+
}
|
|
1254
|
+
const r = Se(e.pageToken);
|
|
1255
|
+
if (r && r.length > 4096)
|
|
1256
|
+
throw y({ statusCode: 422, statusMessage: "Page token is too long." });
|
|
1257
|
+
const a = Se(e.search), n = Me(e.searchBy);
|
|
1258
|
+
if (n && !a)
|
|
1259
|
+
throw y({ statusCode: 422, statusMessage: "Search mode requires a search value." });
|
|
1260
|
+
if (!a) return { limit: i, pageToken: r };
|
|
1261
|
+
if (r)
|
|
1262
|
+
throw y({ statusCode: 422, statusMessage: "Exact search cannot be combined with a page token." });
|
|
1263
|
+
const c = n || "uid";
|
|
1264
|
+
if (!["uid", "email", "phone"].includes(c))
|
|
1265
|
+
throw y({ statusCode: 422, statusMessage: "Search mode must be uid, email, or phone." });
|
|
1266
|
+
if (c === "phone") {
|
|
1267
|
+
const u = at.safeParse(a);
|
|
1268
|
+
if (!u.success)
|
|
1269
|
+
throw y({ statusCode: 422, statusMessage: "Phone search must use E.164 format." });
|
|
1270
|
+
return { limit: 1, search: { kind: "phone", value: u.data } };
|
|
1271
|
+
}
|
|
1272
|
+
if (c === "email") {
|
|
1273
|
+
const u = st.safeParse(a);
|
|
1274
|
+
if (!u.success)
|
|
1275
|
+
throw y({ statusCode: 422, statusMessage: "Email search is invalid." });
|
|
1276
|
+
return { limit: 1, search: { kind: "email", value: u.data } };
|
|
1277
|
+
}
|
|
1278
|
+
const o = he.safeParse(a);
|
|
1279
|
+
if (!o.success)
|
|
1280
|
+
throw y({ statusCode: 422, statusMessage: "UID search is invalid." });
|
|
1281
|
+
return { limit: 1, search: { kind: "uid", value: o.data } };
|
|
1282
|
+
}
|
|
1283
|
+
function pt(e) {
|
|
1284
|
+
const t = new TextEncoder().encode(e);
|
|
1285
|
+
let s = "";
|
|
1286
|
+
for (const i of t) s += String.fromCharCode(i);
|
|
1287
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
1288
|
+
}
|
|
1289
|
+
function ht(e) {
|
|
1290
|
+
if (!/^[A-Za-z0-9_-]{1,1024}$/.test(e))
|
|
1291
|
+
throw y({ statusCode: 400, statusMessage: "Firebase UID token is invalid." });
|
|
1292
|
+
const t = e.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(e.length / 4) * 4, "=");
|
|
1293
|
+
let s;
|
|
1294
|
+
try {
|
|
1295
|
+
const i = atob(t), r = Uint8Array.from(i, (a) => a.charCodeAt(0));
|
|
1296
|
+
s = new TextDecoder("utf-8", { fatal: !0 }).decode(r);
|
|
1297
|
+
} catch {
|
|
1298
|
+
throw y({ statusCode: 400, statusMessage: "Firebase UID token is invalid." });
|
|
1299
|
+
}
|
|
1300
|
+
if (pt(s) !== e || !he.safeParse(s).success)
|
|
1301
|
+
throw y({ statusCode: 400, statusMessage: "Firebase UID token is invalid." });
|
|
1302
|
+
return s;
|
|
1303
|
+
}
|
|
1304
|
+
function ye(e, t) {
|
|
1305
|
+
return e.isAdmin === !0 || Array.isArray(e.permissions) && e.permissions.includes(t);
|
|
1306
|
+
}
|
|
1307
|
+
function yt(e, t) {
|
|
1308
|
+
return t.kind === "uid" || ye(e, v.viewPii);
|
|
1309
|
+
}
|
|
1310
|
+
function wt(e, t) {
|
|
1311
|
+
let s = e.length ^ t.length;
|
|
1312
|
+
const i = Math.max(e.length, t.length);
|
|
1313
|
+
for (let r = 0; r < i; r += 1)
|
|
1314
|
+
s |= (e.charCodeAt(r) || 0) ^ (t.charCodeAt(r) || 0);
|
|
1315
|
+
return s === 0;
|
|
1316
|
+
}
|
|
1317
|
+
function T(e, t) {
|
|
1318
|
+
if (!ye(e, t))
|
|
1319
|
+
throw y({ statusCode: 403, statusMessage: "You do not have permission to perform this action." });
|
|
1320
|
+
}
|
|
1321
|
+
function ze(e) {
|
|
1322
|
+
if (!e) return null;
|
|
1323
|
+
const t = e.lastIndexOf("@");
|
|
1324
|
+
if (t <= 0) return "••••";
|
|
1325
|
+
const s = e.slice(0, t), i = e.slice(t + 1), r = i.lastIndexOf("."), a = r > 0 ? i.slice(0, r) : i, n = r > 0 ? i.slice(r) : "";
|
|
1326
|
+
return `${s.slice(0, 1)}•••@${a.slice(0, 1)}•••${n}`;
|
|
1327
|
+
}
|
|
1328
|
+
function bt(e) {
|
|
1329
|
+
return e ? `+••••••${e.replace(/\D/g, "").slice(-2)}` : null;
|
|
1330
|
+
}
|
|
1331
|
+
function Ct(e) {
|
|
1332
|
+
return e.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, "[redacted-email]").replace(/\+[1-9][\d\s().-]{5,}\d/g, "[redacted-phone]").replace(
|
|
1333
|
+
/\b(password|passcode|token|secret|api[-_\s]?key|authorization)\b\s*[:=]?\s*\S+/gi,
|
|
1334
|
+
"$1 [redacted]"
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
function Le(e) {
|
|
1338
|
+
return e ? `${e.slice(0, 1)}•••` : null;
|
|
1339
|
+
}
|
|
1340
|
+
function U(e) {
|
|
1341
|
+
return typeof e == "string" ? e : null;
|
|
1342
|
+
}
|
|
1343
|
+
function At(e) {
|
|
1344
|
+
const t = [];
|
|
1345
|
+
if (Array.isArray(e?.providerData))
|
|
1346
|
+
for (const s of e.providerData) {
|
|
1347
|
+
const i = U(s?.providerId);
|
|
1348
|
+
i && t.push(i);
|
|
1349
|
+
}
|
|
1350
|
+
return [...new Set(t)].sort();
|
|
1351
|
+
}
|
|
1352
|
+
function He(e) {
|
|
1353
|
+
return Array.isArray(e?.multiFactor?.enrolledFactors) ? e.multiFactor.enrolledFactors.length : 0;
|
|
1354
|
+
}
|
|
1355
|
+
function we(e, t) {
|
|
1356
|
+
return t.manageRoleClaim ? U(e?.customClaims?.[t.roleClaimKey]) : null;
|
|
1357
|
+
}
|
|
1358
|
+
function Dt() {
|
|
1359
|
+
return {
|
|
1360
|
+
enabled: !1,
|
|
1361
|
+
exists: !1,
|
|
1362
|
+
name: null,
|
|
1363
|
+
email: null,
|
|
1364
|
+
role: null,
|
|
1365
|
+
status: null
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function Ft(e, t, s) {
|
|
1369
|
+
const i = e ? U(e[s.profileNameField]) : null, r = e ? U(e[s.profileEmailField]) : null;
|
|
1370
|
+
return {
|
|
1371
|
+
enabled: !0,
|
|
1372
|
+
exists: !!e,
|
|
1373
|
+
name: t ? i : Le(i),
|
|
1374
|
+
email: t ? r : ze(r),
|
|
1375
|
+
role: t && e ? U(e[s.profileRoleField]) : null,
|
|
1376
|
+
status: t && e ? U(e[s.profileStatusField]) : null
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
function le(e, t) {
|
|
1380
|
+
const s = U(e?.email), i = U(e?.displayName), r = U(e?.phoneNumber);
|
|
1381
|
+
let a;
|
|
1382
|
+
return t.config.linkFirestoreProfiles ? t.profileAvailable === !1 ? a = null : a = Ft(t.profile || null, t.canViewPii, t.config) : a = Dt(), {
|
|
1383
|
+
uid: String(e?.uid || ""),
|
|
1384
|
+
displayName: t.canViewPii ? i : Le(i),
|
|
1385
|
+
email: t.canViewPii ? s : ze(s),
|
|
1386
|
+
phoneNumber: t.canViewPii ? r : bt(r),
|
|
1387
|
+
photoURL: t.canViewPii ? U(e?.photoURL) : null,
|
|
1388
|
+
emailVerified: e?.emailVerified === !0,
|
|
1389
|
+
disabled: e?.disabled === !0,
|
|
1390
|
+
providers: At(e),
|
|
1391
|
+
createdAt: U(e?.metadata?.creationTime),
|
|
1392
|
+
lastSignInAt: U(e?.metadata?.lastSignInTime),
|
|
1393
|
+
lastRefreshAt: U(e?.metadata?.lastRefreshTime),
|
|
1394
|
+
mfaFactorCount: He(e),
|
|
1395
|
+
role: we(e, t.config),
|
|
1396
|
+
profile: a
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
function Pe(e, t) {
|
|
1400
|
+
if (!e) return null;
|
|
1401
|
+
const s = le(e, {
|
|
1402
|
+
canViewPii: !1,
|
|
1403
|
+
config: t,
|
|
1404
|
+
profileAvailable: !1
|
|
1405
|
+
});
|
|
1406
|
+
return {
|
|
1407
|
+
uid: s.uid,
|
|
1408
|
+
displayName: s.displayName,
|
|
1409
|
+
email: s.email,
|
|
1410
|
+
phoneNumber: s.phoneNumber,
|
|
1411
|
+
emailVerified: s.emailVerified,
|
|
1412
|
+
disabled: s.disabled,
|
|
1413
|
+
providers: s.providers,
|
|
1414
|
+
mfaFactorCount: s.mfaFactorCount,
|
|
1415
|
+
role: s.role
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
function It(e, t, s) {
|
|
1419
|
+
const i = { ...e || {} };
|
|
1420
|
+
return s === null ? delete i[t] : i[t] = s, i;
|
|
1421
|
+
}
|
|
1422
|
+
function ke(e, t) {
|
|
1423
|
+
const s = {
|
|
1424
|
+
[t.profileUidField]: String(e.uid),
|
|
1425
|
+
[t.profileStatusField]: e.disabled === !0 ? "disabled" : "active"
|
|
1426
|
+
};
|
|
1427
|
+
typeof e.displayName == "string" && (s[t.profileNameField] = e.displayName), typeof e.email == "string" && (s[t.profileEmailField] = e.email);
|
|
1428
|
+
const i = we(e, t);
|
|
1429
|
+
return i !== null && (s[t.profileRoleField] = i), s;
|
|
1430
|
+
}
|
|
1431
|
+
function ie(e) {
|
|
1432
|
+
if (!e || typeof e != "object") return;
|
|
1433
|
+
const t = e;
|
|
1434
|
+
if (typeof t.code == "string") return t.code;
|
|
1435
|
+
if (typeof t.errorInfo?.code == "string") return t.errorInfo.code;
|
|
1436
|
+
}
|
|
1437
|
+
function Rt(e, t = "Firebase Authentication request failed.") {
|
|
1438
|
+
const s = ie(e);
|
|
1439
|
+
return s === "auth/user-not-found" ? { statusCode: 404, statusMessage: "Firebase user not found." } : s && [
|
|
1440
|
+
"auth/email-already-exists",
|
|
1441
|
+
"auth/phone-number-already-exists",
|
|
1442
|
+
"auth/uid-already-exists"
|
|
1443
|
+
].includes(s) ? { statusCode: 409, statusMessage: "A Firebase user already uses that identifier." } : s && (s.startsWith("auth/invalid-") || s === "auth/weak-password" || s === "auth/claims-too-large" || s === "auth/reserved-claims") ? { statusCode: 422, statusMessage: "Firebase rejected the supplied user data." } : s && [
|
|
1444
|
+
"auth/too-many-requests",
|
|
1445
|
+
"auth/quota-exceeded"
|
|
1446
|
+
].includes(s) ? { statusCode: 429, statusMessage: "Firebase temporarily rate-limited this request." } : s && [
|
|
1447
|
+
"auth/insufficient-permission",
|
|
1448
|
+
"auth/project-not-found",
|
|
1449
|
+
"auth/configuration-not-found"
|
|
1450
|
+
].includes(s) ? { statusCode: 424, statusMessage: "The Firebase integration is not authorized for this operation." } : { statusCode: 502, statusMessage: t };
|
|
1451
|
+
}
|
|
1452
|
+
function _(e) {
|
|
1453
|
+
const t = e?.context?.module;
|
|
1454
|
+
if (!t || !t.supabase || typeof t.instanceId != "string" || typeof t.projectId != "string" || typeof t.userId != "string")
|
|
1455
|
+
throw y({ statusCode: 500, statusMessage: "Firebase Auth Manager request context is unavailable." });
|
|
1456
|
+
return {
|
|
1457
|
+
...t,
|
|
1458
|
+
isAdmin: t.isAdmin === !0,
|
|
1459
|
+
permissions: Array.isArray(t.permissions) ? t.permissions : []
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
async function z(e, t) {
|
|
1463
|
+
const s = t.safeParse(await Q(e));
|
|
1464
|
+
if (!s.success)
|
|
1465
|
+
throw y({
|
|
1466
|
+
statusCode: 422,
|
|
1467
|
+
statusMessage: s.error.issues[0]?.message || "Request body is invalid."
|
|
1468
|
+
});
|
|
1469
|
+
return s.data;
|
|
1470
|
+
}
|
|
1471
|
+
function L(e) {
|
|
1472
|
+
const t = N(e, "uid");
|
|
1473
|
+
if (typeof t != "string")
|
|
1474
|
+
throw y({ statusCode: 400, statusMessage: "A valid Firebase UID is required." });
|
|
1475
|
+
return ht(t);
|
|
1476
|
+
}
|
|
1477
|
+
function q(e, t) {
|
|
1478
|
+
if (typeof e?.statusCode == "number" && typeof e?.statusMessage == "string")
|
|
1479
|
+
throw e;
|
|
1480
|
+
const s = Rt(e, t), i = ie(e);
|
|
1481
|
+
throw console.error("Firebase Auth Manager request failed", i ? { code: i } : { code: "unknown" }), y(s);
|
|
1482
|
+
}
|
|
1483
|
+
function Mt(e) {
|
|
1484
|
+
return ie(e) === "auth/user-not-found";
|
|
1485
|
+
}
|
|
1486
|
+
function pe(e) {
|
|
1487
|
+
return ye(e, v.viewPii);
|
|
1488
|
+
}
|
|
1489
|
+
function St(e, t) {
|
|
1490
|
+
const s = [];
|
|
1491
|
+
for (let i = 0; i < e.length; i += t)
|
|
1492
|
+
s.push(e.slice(i, i + t));
|
|
1493
|
+
return s;
|
|
1494
|
+
}
|
|
1495
|
+
function me(e) {
|
|
1496
|
+
return e.length > 0 && e !== "." && e !== ".." && !e.includes("/") && !/^__.*__$/.test(e);
|
|
1497
|
+
}
|
|
1498
|
+
async function Pt(e, t, s) {
|
|
1499
|
+
const i = /* @__PURE__ */ new Map();
|
|
1500
|
+
if (!s.linkFirestoreProfiles || t.length === 0) return i;
|
|
1501
|
+
const r = [...new Set(t.map((d) => String(d.uid)).filter(Boolean))], a = e.collection(s.profileCollection), n = r.filter(me), c = n.map((d) => a.doc(d)), o = c.length === 0 ? [] : typeof e.getAll == "function" ? await e.getAll(...c) : await Promise.all(c.map((d) => d.get()));
|
|
1502
|
+
for (let d = 0; d < o.length; d += 1) {
|
|
1503
|
+
const l = o[d], f = n[d];
|
|
1504
|
+
f && l?.exists && i.set(f, l.data());
|
|
1505
|
+
}
|
|
1506
|
+
const u = r.filter((d) => !i.has(d));
|
|
1507
|
+
for (const d of St(u, 30)) {
|
|
1508
|
+
const l = await a.where(s.profileUidField, "in", d).get();
|
|
1509
|
+
for (const f of l.docs || []) {
|
|
1510
|
+
const g = f.data(), m = g[s.profileUidField];
|
|
1511
|
+
typeof m == "string" && d.includes(m) && !i.has(m) && i.set(m, g);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return i;
|
|
1515
|
+
}
|
|
1516
|
+
async function ve(e, t, s) {
|
|
1517
|
+
const i = e.collection(s.profileCollection);
|
|
1518
|
+
if (me(t)) {
|
|
1519
|
+
const n = await i.doc(t).get();
|
|
1520
|
+
if (n.exists)
|
|
1521
|
+
return { id: String(n.id), data: n.data() };
|
|
1522
|
+
}
|
|
1523
|
+
const a = (await i.where(s.profileUidField, "==", t).limit(1).get()).docs?.[0];
|
|
1524
|
+
return a ? { id: String(a.id), data: a.data() } : null;
|
|
1525
|
+
}
|
|
1526
|
+
async function O(e, t) {
|
|
1527
|
+
const s = _(e), { data: i, error: r } = await s.supabase.from("project_modules").select("type, config").eq("id", s.instanceId).eq("project_id", s.projectId).maybeSingle();
|
|
1528
|
+
if (r)
|
|
1529
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load Firebase Auth Manager configuration." });
|
|
1530
|
+
if (!i || i.type !== Oe || !i.config)
|
|
1531
|
+
throw y({ statusCode: 404, statusMessage: "Firebase Auth Manager module instance not found." });
|
|
1532
|
+
const a = ft(i.config), { data: n, error: c } = await s.supabase.from("integrations").select("id, project_id, type").eq("id", a.project).eq("project_id", s.projectId).eq("type", "FIREBASE").maybeSingle();
|
|
1533
|
+
if (c)
|
|
1534
|
+
throw y({ statusCode: 500, statusMessage: "Failed to verify the Firebase integration." });
|
|
1535
|
+
if (!n)
|
|
1536
|
+
throw y({ statusCode: 424, statusMessage: "The configured Firebase integration is unavailable for this project." });
|
|
1537
|
+
const o = await t.initFirebase(e, a.project);
|
|
1538
|
+
return {
|
|
1539
|
+
module: s,
|
|
1540
|
+
config: a,
|
|
1541
|
+
integrationId: a.project,
|
|
1542
|
+
firebase: o,
|
|
1543
|
+
auth: o.auth(),
|
|
1544
|
+
firestore: o.firestore()
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
async function H(e, t, s) {
|
|
1548
|
+
const i = {
|
|
1549
|
+
project_id: t.module.projectId,
|
|
1550
|
+
module_id: t.module.instanceId,
|
|
1551
|
+
user_id: t.module.userId,
|
|
1552
|
+
message: `Firebase Auth: ${s.action}`,
|
|
1553
|
+
level: "INFO",
|
|
1554
|
+
params: {
|
|
1555
|
+
source: Oe,
|
|
1556
|
+
action: s.action,
|
|
1557
|
+
targetUid: s.targetUid,
|
|
1558
|
+
integrationId: t.integrationId,
|
|
1559
|
+
reason: Ct(s.reason),
|
|
1560
|
+
before: Pe(s.before, t.config),
|
|
1561
|
+
after: Pe(s.after, t.config),
|
|
1562
|
+
...s.details || {}
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
try {
|
|
1566
|
+
const { error: r } = await t.module.supabase.from("project_logs").insert(i);
|
|
1567
|
+
return r ? (console.error("Firebase Auth Manager audit insert failed", { code: r.code || "unknown" }), !1) : !0;
|
|
1568
|
+
} catch (r) {
|
|
1569
|
+
const a = r && typeof r == "object" && "code" in r ? String(r.code) : "unknown";
|
|
1570
|
+
return console.error("Firebase Auth Manager audit insert failed", { code: a }), !1;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
async function kt(e, t) {
|
|
1574
|
+
try {
|
|
1575
|
+
return t.kind === "email" ? [await e.getUserByEmail(t.value)] : t.kind === "phone" ? [await e.getUserByPhoneNumber(t.value)] : [await e.getUser(t.value)];
|
|
1576
|
+
} catch (s) {
|
|
1577
|
+
if (Mt(s)) return [];
|
|
1578
|
+
throw s;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
async function Ue(e, t, s) {
|
|
1582
|
+
try {
|
|
1583
|
+
return await e.getUser(t);
|
|
1584
|
+
} catch (i) {
|
|
1585
|
+
return console.error("Firebase Auth Manager post-mutation refresh failed", {
|
|
1586
|
+
code: ie(i) || "unknown"
|
|
1587
|
+
}), s;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
async function J(e, t) {
|
|
1591
|
+
let s = /* @__PURE__ */ new Map(), i = !0;
|
|
1592
|
+
if (t.config.linkFirestoreProfiles)
|
|
1593
|
+
try {
|
|
1594
|
+
s = await Pt(t.firestore, e, t.config);
|
|
1595
|
+
} catch (r) {
|
|
1596
|
+
i = !1;
|
|
1597
|
+
const a = ie(r);
|
|
1598
|
+
console.error("Firebase Auth Manager profile join failed", { code: a || "unknown" });
|
|
1599
|
+
}
|
|
1600
|
+
return e.map((r) => le(r, {
|
|
1601
|
+
canViewPii: pe(t.module),
|
|
1602
|
+
config: t.config,
|
|
1603
|
+
profile: s.get(String(r.uid)) || null,
|
|
1604
|
+
profileAvailable: i
|
|
1605
|
+
}));
|
|
1606
|
+
}
|
|
1607
|
+
function Jt(e) {
|
|
1608
|
+
const t = se();
|
|
1609
|
+
return t.get("/users", A(async (s) => {
|
|
1610
|
+
const i = _(s);
|
|
1611
|
+
T(i, v.viewUsers);
|
|
1612
|
+
try {
|
|
1613
|
+
const r = await O(s, e), a = gt(R(s), r.config.pageSize);
|
|
1614
|
+
if (a.search && !yt(r.module, a.search))
|
|
1615
|
+
throw y({
|
|
1616
|
+
statusCode: 403,
|
|
1617
|
+
statusMessage: "Viewing personal data is required to search by email or phone number."
|
|
1618
|
+
});
|
|
1619
|
+
let n, c = null;
|
|
1620
|
+
if (a.search)
|
|
1621
|
+
n = await kt(r.auth, a.search);
|
|
1622
|
+
else {
|
|
1623
|
+
const o = await r.auth.listUsers(a.limit, a.pageToken);
|
|
1624
|
+
n = Array.isArray(o.users) ? o.users : [], c = typeof o.pageToken == "string" ? o.pageToken : null;
|
|
1625
|
+
}
|
|
1626
|
+
return {
|
|
1627
|
+
users: await J(n, r),
|
|
1628
|
+
nextPageToken: c
|
|
1629
|
+
};
|
|
1630
|
+
} catch (r) {
|
|
1631
|
+
q(r, "Failed to list Firebase users.");
|
|
1632
|
+
}
|
|
1633
|
+
})), t.get("/users/:uid", A(async (s) => {
|
|
1634
|
+
const i = _(s);
|
|
1635
|
+
T(i, v.viewUsers);
|
|
1636
|
+
const r = L(s);
|
|
1637
|
+
try {
|
|
1638
|
+
const a = await O(s, e), n = await a.auth.getUser(r), [c] = await J([n], a);
|
|
1639
|
+
return { user: c };
|
|
1640
|
+
} catch (a) {
|
|
1641
|
+
q(a, "Failed to load the Firebase user.");
|
|
1642
|
+
}
|
|
1643
|
+
})), t.post("/users", A(async (s) => {
|
|
1644
|
+
const i = _(s);
|
|
1645
|
+
T(i, v.createUsers);
|
|
1646
|
+
const r = await z(s, ot);
|
|
1647
|
+
r.createProfile && T(i, v.manageProfiles);
|
|
1648
|
+
try {
|
|
1649
|
+
const a = await O(s, e);
|
|
1650
|
+
if (r.createProfile && !a.config.linkFirestoreProfiles)
|
|
1651
|
+
throw y({ statusCode: 409, statusMessage: "Firestore profile linking is disabled for this module." });
|
|
1652
|
+
if (r.uid && a.config.linkFirestoreProfiles && await ve(a.firestore, r.uid, a.config))
|
|
1653
|
+
throw y({
|
|
1654
|
+
statusCode: 409,
|
|
1655
|
+
statusMessage: "A retained Firestore profile already uses this UID. Resolve that profile before reusing the Authentication UID."
|
|
1656
|
+
});
|
|
1657
|
+
const n = await a.auth.createUser({
|
|
1658
|
+
email: r.email,
|
|
1659
|
+
password: r.password,
|
|
1660
|
+
uid: r.uid,
|
|
1661
|
+
displayName: r.displayName,
|
|
1662
|
+
phoneNumber: r.phoneNumber,
|
|
1663
|
+
emailVerified: r.emailVerified,
|
|
1664
|
+
disabled: r.disabled
|
|
1665
|
+
});
|
|
1666
|
+
let c = null, o = !1, u = null;
|
|
1667
|
+
if (r.createProfile) {
|
|
1668
|
+
c = ke(n, a.config);
|
|
1669
|
+
try {
|
|
1670
|
+
const l = a.firestore.collection(a.config.profileCollection);
|
|
1671
|
+
await (me(n.uid) ? l.doc(n.uid) : l.doc()).create(c), o = !0;
|
|
1672
|
+
} catch {
|
|
1673
|
+
c = null, u = "The Authentication user was created, but its Firestore profile could not be created.";
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const d = await H(s, a, {
|
|
1677
|
+
action: "user.create",
|
|
1678
|
+
targetUid: n.uid,
|
|
1679
|
+
reason: "Created through Firebase Auth Manager.",
|
|
1680
|
+
before: null,
|
|
1681
|
+
after: n,
|
|
1682
|
+
details: {
|
|
1683
|
+
profileRequested: r.createProfile === !0,
|
|
1684
|
+
profileCreated: o
|
|
1685
|
+
}
|
|
1686
|
+
});
|
|
1687
|
+
return Ze(s, 201), {
|
|
1688
|
+
user: le(n, {
|
|
1689
|
+
canViewPii: pe(a.module),
|
|
1690
|
+
config: a.config,
|
|
1691
|
+
profile: c
|
|
1692
|
+
}),
|
|
1693
|
+
profileCreated: o,
|
|
1694
|
+
profileError: u,
|
|
1695
|
+
auditRecorded: d
|
|
1696
|
+
};
|
|
1697
|
+
} catch (a) {
|
|
1698
|
+
q(a, "Failed to create the Firebase user.");
|
|
1699
|
+
}
|
|
1700
|
+
})), t.patch("/users/:uid", A(async (s) => {
|
|
1701
|
+
const i = _(s);
|
|
1702
|
+
T(i, v.editUsers);
|
|
1703
|
+
const r = L(s), a = await z(s, rt);
|
|
1704
|
+
try {
|
|
1705
|
+
const n = await O(s, e), c = await n.auth.getUser(r), o = {};
|
|
1706
|
+
Y(a, "displayName") && (o.displayName = a.displayName), Y(a, "email") && (o.email = a.email), Y(a, "phoneNumber") && (o.phoneNumber = a.phoneNumber), Y(a, "emailVerified") && (o.emailVerified = a.emailVerified);
|
|
1707
|
+
const u = await n.auth.updateUser(r, o), d = await H(s, n, {
|
|
1708
|
+
action: "user.update",
|
|
1709
|
+
targetUid: r,
|
|
1710
|
+
reason: a.reason,
|
|
1711
|
+
before: c,
|
|
1712
|
+
after: u,
|
|
1713
|
+
details: { changedFieldCount: Object.keys(o).length }
|
|
1714
|
+
}), [l] = await J([u], n);
|
|
1715
|
+
return { user: l, auditRecorded: d };
|
|
1716
|
+
} catch (n) {
|
|
1717
|
+
q(n, "Failed to update the Firebase user.");
|
|
1718
|
+
}
|
|
1719
|
+
})), t.post("/users/:uid/status", A(async (s) => {
|
|
1720
|
+
const i = _(s);
|
|
1721
|
+
T(i, v.manageStatus);
|
|
1722
|
+
const r = L(s), a = await z(s, nt);
|
|
1723
|
+
try {
|
|
1724
|
+
const n = await O(s, e), c = await n.auth.getUser(r), o = c.disabled !== a.disabled, u = o ? await n.auth.updateUser(r, { disabled: a.disabled }) : c, d = await H(s, n, {
|
|
1725
|
+
action: a.disabled ? "user.disable" : "user.enable",
|
|
1726
|
+
targetUid: r,
|
|
1727
|
+
reason: a.reason,
|
|
1728
|
+
before: c,
|
|
1729
|
+
after: u,
|
|
1730
|
+
details: { changed: o }
|
|
1731
|
+
}), [l] = await J([u], n);
|
|
1732
|
+
return { user: l, changed: o, auditRecorded: d };
|
|
1733
|
+
} catch (n) {
|
|
1734
|
+
q(n, "Failed to change the Firebase user status.");
|
|
1735
|
+
}
|
|
1736
|
+
})), t.post("/users/:uid/revoke-sessions", A(async (s) => {
|
|
1737
|
+
const i = _(s);
|
|
1738
|
+
T(i, v.manageSessions);
|
|
1739
|
+
const r = L(s), a = await z(s, fe);
|
|
1740
|
+
try {
|
|
1741
|
+
const n = await O(s, e), c = await n.auth.getUser(r);
|
|
1742
|
+
await n.auth.revokeRefreshTokens(r);
|
|
1743
|
+
const o = await H(s, n, {
|
|
1744
|
+
action: "user.revoke_sessions",
|
|
1745
|
+
targetUid: r,
|
|
1746
|
+
reason: a.reason,
|
|
1747
|
+
before: c,
|
|
1748
|
+
after: c,
|
|
1749
|
+
details: { sessionsRevoked: !0 }
|
|
1750
|
+
}), u = await Ue(n.auth, r, c), [d] = await J([u], n);
|
|
1751
|
+
return { user: d, sessionsRevoked: !0, auditRecorded: o };
|
|
1752
|
+
} catch (n) {
|
|
1753
|
+
q(n, "Failed to revoke Firebase user sessions.");
|
|
1754
|
+
}
|
|
1755
|
+
})), t.post("/users/:uid/reset-mfa", A(async (s) => {
|
|
1756
|
+
const i = _(s);
|
|
1757
|
+
T(i, v.manageMfa);
|
|
1758
|
+
const r = L(s), a = await z(s, fe);
|
|
1759
|
+
try {
|
|
1760
|
+
const n = await O(s, e), c = await n.auth.getUser(r), o = He(c) > 0, u = o ? await n.auth.updateUser(r, { multiFactor: { enrolledFactors: [] } }) : c, d = await H(s, n, {
|
|
1761
|
+
action: "user.reset_mfa",
|
|
1762
|
+
targetUid: r,
|
|
1763
|
+
reason: a.reason,
|
|
1764
|
+
before: c,
|
|
1765
|
+
after: u,
|
|
1766
|
+
details: { changed: o }
|
|
1767
|
+
}), [l] = await J([u], n);
|
|
1768
|
+
return { user: l, changed: o, auditRecorded: d };
|
|
1769
|
+
} catch (n) {
|
|
1770
|
+
q(n, "Failed to reset Firebase multi-factor authentication.");
|
|
1771
|
+
}
|
|
1772
|
+
})), t.post("/users/:uid/role", A(async (s) => {
|
|
1773
|
+
const i = _(s);
|
|
1774
|
+
T(i, v.manageRoles);
|
|
1775
|
+
const r = L(s), a = await z(s, it);
|
|
1776
|
+
try {
|
|
1777
|
+
const n = await O(s, e);
|
|
1778
|
+
if (!n.config.manageRoleClaim)
|
|
1779
|
+
throw y({ statusCode: 409, statusMessage: "Role claim management is disabled for this module." });
|
|
1780
|
+
if (a.role !== null && !n.config.roleOptions.includes(a.role))
|
|
1781
|
+
throw y({ statusCode: 422, statusMessage: "Role must be one of the configured values." });
|
|
1782
|
+
const c = await n.auth.getUser(r), u = we(c, n.config) !== a.role;
|
|
1783
|
+
let d = c;
|
|
1784
|
+
if (u) {
|
|
1785
|
+
const g = It(c.customClaims, n.config.roleClaimKey, a.role);
|
|
1786
|
+
await n.auth.setCustomUserClaims(r, g), d = { ...c, customClaims: g };
|
|
1787
|
+
}
|
|
1788
|
+
const l = await H(s, n, {
|
|
1789
|
+
action: "user.set_role",
|
|
1790
|
+
targetUid: r,
|
|
1791
|
+
reason: a.reason,
|
|
1792
|
+
before: c,
|
|
1793
|
+
after: d,
|
|
1794
|
+
details: { changed: u, role: a.role }
|
|
1795
|
+
});
|
|
1796
|
+
u && (d = await Ue(n.auth, r, d));
|
|
1797
|
+
const [f] = await J([d], n);
|
|
1798
|
+
return { user: f, changed: u, auditRecorded: l };
|
|
1799
|
+
} catch (n) {
|
|
1800
|
+
q(n, "Failed to update the Firebase role claim.");
|
|
1801
|
+
}
|
|
1802
|
+
})), t.post("/users/:uid/profile", A(async (s) => {
|
|
1803
|
+
const i = _(s);
|
|
1804
|
+
T(i, v.manageProfiles);
|
|
1805
|
+
const r = L(s), a = await z(s, fe);
|
|
1806
|
+
try {
|
|
1807
|
+
const n = await O(s, e);
|
|
1808
|
+
if (!n.config.linkFirestoreProfiles)
|
|
1809
|
+
throw y({ statusCode: 409, statusMessage: "Firestore profile linking is disabled for this module." });
|
|
1810
|
+
const c = await n.auth.getUser(r), o = await ve(n.firestore, r, n.config);
|
|
1811
|
+
let u = o?.data || null, d = !1;
|
|
1812
|
+
if (!o) {
|
|
1813
|
+
u = ke(c, n.config);
|
|
1814
|
+
const f = n.firestore.collection(n.config.profileCollection);
|
|
1815
|
+
await (me(r) ? f.doc(r) : f.doc()).create(u), d = !0;
|
|
1816
|
+
}
|
|
1817
|
+
const l = await H(s, n, {
|
|
1818
|
+
action: "user.create_profile",
|
|
1819
|
+
targetUid: r,
|
|
1820
|
+
reason: a.reason,
|
|
1821
|
+
before: c,
|
|
1822
|
+
after: c,
|
|
1823
|
+
details: { changed: d }
|
|
1824
|
+
});
|
|
1825
|
+
return {
|
|
1826
|
+
user: le(c, {
|
|
1827
|
+
canViewPii: pe(n.module),
|
|
1828
|
+
config: n.config,
|
|
1829
|
+
profile: u
|
|
1830
|
+
}),
|
|
1831
|
+
profileCreated: d,
|
|
1832
|
+
auditRecorded: l
|
|
1833
|
+
};
|
|
1834
|
+
} catch (n) {
|
|
1835
|
+
q(n, "Failed to create the Firestore profile.");
|
|
1836
|
+
}
|
|
1837
|
+
})), t.delete("/users/:uid", A(async (s) => {
|
|
1838
|
+
const i = _(s);
|
|
1839
|
+
T(i, v.deleteUsers);
|
|
1840
|
+
const r = L(s), a = await z(s, ct);
|
|
1841
|
+
if (!wt(r, a.confirmation))
|
|
1842
|
+
throw y({ statusCode: 422, statusMessage: "Deletion confirmation must exactly match the Firebase UID." });
|
|
1843
|
+
try {
|
|
1844
|
+
const n = await O(s, e), c = await n.auth.getUser(r);
|
|
1845
|
+
await n.auth.deleteUser(r);
|
|
1846
|
+
const o = await H(s, n, {
|
|
1847
|
+
action: "user.delete",
|
|
1848
|
+
targetUid: r,
|
|
1849
|
+
reason: a.reason,
|
|
1850
|
+
before: c,
|
|
1851
|
+
after: null,
|
|
1852
|
+
details: { firestoreProfilePreserved: !0 }
|
|
1853
|
+
});
|
|
1854
|
+
return {
|
|
1855
|
+
uid: r,
|
|
1856
|
+
deleted: !0,
|
|
1857
|
+
firestoreProfilePreserved: !0,
|
|
1858
|
+
auditRecorded: o
|
|
1859
|
+
};
|
|
1860
|
+
} catch (n) {
|
|
1861
|
+
q(n, "Failed to delete the Firebase user.");
|
|
1862
|
+
}
|
|
1863
|
+
})), t.handler;
|
|
1864
|
+
}
|
|
1865
|
+
const be = 1, Ne = 700 * 1024, xe = 20, vt = 5e4, Ut = 120, Nt = 80, Ee = 2e3, Te = 120, xt = "studio_forms", Ge = /* @__PURE__ */ new Set(["draft", "published", "archived"]), Et = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]), x = {
|
|
1866
|
+
view: "view-forms",
|
|
1867
|
+
create: "create-form",
|
|
1868
|
+
edit: "edit-form",
|
|
1869
|
+
publish: "publish-form",
|
|
1870
|
+
archive: "archive-form"
|
|
1871
|
+
};
|
|
1872
|
+
function I(e) {
|
|
1873
|
+
throw y({ statusCode: 400, statusMessage: e });
|
|
1874
|
+
}
|
|
1875
|
+
function G(e) {
|
|
1876
|
+
throw y({ statusCode: 409, statusMessage: e });
|
|
1877
|
+
}
|
|
1878
|
+
function Ce(e, t) {
|
|
1879
|
+
(!e || typeof e != "object" || Array.isArray(e)) && I(`${t} must be an object.`);
|
|
1880
|
+
const s = Object.getPrototypeOf(e);
|
|
1881
|
+
return s !== Object.prototype && s !== null && I(`${t} must be a plain object.`), e;
|
|
1882
|
+
}
|
|
1883
|
+
function Qe(e, t, s) {
|
|
1884
|
+
typeof e != "string" && I(`${t} is required.`);
|
|
1885
|
+
const i = e.trim();
|
|
1886
|
+
return i || I(`${t} is required.`), i.length > s && I(`${t} cannot exceed ${s} characters.`), i;
|
|
1887
|
+
}
|
|
1888
|
+
function Tt(e) {
|
|
1889
|
+
if (e == null) return "";
|
|
1890
|
+
typeof e != "string" && I("Form description must be a string.");
|
|
1891
|
+
const t = e.trim();
|
|
1892
|
+
return t.length > Ee && I(`Form description cannot exceed ${Ee} characters.`), t;
|
|
1893
|
+
}
|
|
1894
|
+
function _t(e) {
|
|
1895
|
+
const t = Qe(e, "Form key", Nt).toLowerCase();
|
|
1896
|
+
return /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(t) || I("Form key must start with a letter and contain only lowercase letters, numbers, hyphens, or underscores."), t;
|
|
1897
|
+
}
|
|
1898
|
+
function qt(e) {
|
|
1899
|
+
if (e == null || e === "")
|
|
1900
|
+
return xt;
|
|
1901
|
+
typeof e != "string" && I("The configured forms collection must be a string.");
|
|
1902
|
+
const t = e.trim();
|
|
1903
|
+
(!t || t.length > 500) && I("The configured forms collection path is invalid."), (t.startsWith("/") || t.endsWith("/") || t.includes("//")) && I("The configured forms collection path is invalid.");
|
|
1904
|
+
const s = t.split("/");
|
|
1905
|
+
s.length % 2 === 0 && I("The configured forms collection path must point to a collection.");
|
|
1906
|
+
for (const i of s)
|
|
1907
|
+
(!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(i) || i === "." || i === ".." || /^__.*__$/.test(i)) && I("The configured forms collection path contains an invalid segment.");
|
|
1908
|
+
return t;
|
|
1909
|
+
}
|
|
1910
|
+
function Ot(e) {
|
|
1911
|
+
(!e || typeof e != "object") && I("Form definition must be an object or array.");
|
|
1912
|
+
const t = /* @__PURE__ */ new Set();
|
|
1913
|
+
let s = 0;
|
|
1914
|
+
const i = (n, c, o) => {
|
|
1915
|
+
if (s += 1, s > vt && I("Form definition is too complex."), c > xe && I(`Form definition cannot be nested deeper than ${xe} levels.`), n === null || typeof n == "string" || typeof n == "boolean") return;
|
|
1916
|
+
if (typeof n == "number") {
|
|
1917
|
+
Number.isFinite(n) || I("Form definition contains an invalid number.");
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1920
|
+
if (typeof n != "object" && I("Form definition contains a value that cannot be stored."), t.has(n) && I("Form definition cannot contain circular references."), t.add(n), Array.isArray(n)) {
|
|
1921
|
+
o && I("Form definition cannot contain an array directly inside another array.");
|
|
1922
|
+
for (const d of n) i(d, c + 1, !0);
|
|
1923
|
+
t.delete(n);
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
const u = Object.getPrototypeOf(n);
|
|
1927
|
+
u !== Object.prototype && u !== null && I("Form definition can contain only plain JSON objects.");
|
|
1928
|
+
for (const [d, l] of Object.entries(n))
|
|
1929
|
+
Et.has(d) && I(`Form definition contains a forbidden object key: ${d}.`), new TextEncoder().encode(d).byteLength > 1500 && I("Form definition contains an object key that is too long."), i(l, c + 1, !1);
|
|
1930
|
+
t.delete(n);
|
|
1931
|
+
};
|
|
1932
|
+
i(e, 1, !1);
|
|
1933
|
+
let r;
|
|
1934
|
+
try {
|
|
1935
|
+
r = JSON.stringify(e);
|
|
1936
|
+
} catch {
|
|
1937
|
+
I("Form definition must be valid JSON.");
|
|
1938
|
+
}
|
|
1939
|
+
new TextEncoder().encode(r).byteLength > Ne && I(`Form definition cannot exceed ${Ne} bytes.`);
|
|
1940
|
+
}
|
|
1941
|
+
function Ye(e) {
|
|
1942
|
+
const t = Ce(e, "Request body"), s = t.definition;
|
|
1943
|
+
return Ot(s), {
|
|
1944
|
+
name: Qe(t.name, "Form name", Ut),
|
|
1945
|
+
key: _t(t.key),
|
|
1946
|
+
description: Tt(t.description),
|
|
1947
|
+
definition: s
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
function Bt(e) {
|
|
1951
|
+
return Ye(e);
|
|
1952
|
+
}
|
|
1953
|
+
function Ke(e) {
|
|
1954
|
+
return (!Number.isInteger(e) || e < 1) && I("expectedRevision must be a positive integer."), e;
|
|
1955
|
+
}
|
|
1956
|
+
function jt(e) {
|
|
1957
|
+
const t = Ce(e, "Request body");
|
|
1958
|
+
return {
|
|
1959
|
+
...Ye(t),
|
|
1960
|
+
expectedRevision: Ke(t.expectedRevision)
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
function ge(e) {
|
|
1964
|
+
const t = Ce(e, "Request body");
|
|
1965
|
+
return { expectedRevision: Ke(t.expectedRevision) };
|
|
1966
|
+
}
|
|
1967
|
+
function $t(e) {
|
|
1968
|
+
return {
|
|
1969
|
+
id: e.revisionId,
|
|
1970
|
+
formId: e.formId,
|
|
1971
|
+
key: e.form.key,
|
|
1972
|
+
name: e.form.name,
|
|
1973
|
+
description: e.form.description || "",
|
|
1974
|
+
definition: e.form.draftDefinition,
|
|
1975
|
+
schemaVersion: e.form.schemaVersion || be,
|
|
1976
|
+
revision: e.revision,
|
|
1977
|
+
draftRevision: e.form.draftRevision,
|
|
1978
|
+
previousRevisionId: e.form.publishedRevisionId || null,
|
|
1979
|
+
publishedAt: e.publishedAt,
|
|
1980
|
+
publishedBy: e.publishedBy
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
function ee(e) {
|
|
1984
|
+
typeof e != "string" && I("Form ID is required.");
|
|
1985
|
+
const t = e.trim();
|
|
1986
|
+
return (!t || t === "." || t === ".." || t.includes("/") || /[\u0000-\u001f\u007f]/.test(t) || new TextEncoder().encode(t).byteLength > 1500) && I("Form ID is invalid."), t;
|
|
1987
|
+
}
|
|
1988
|
+
function oe(e) {
|
|
1989
|
+
const s = e?.context?.module?.userId || e?.context?.user?.id || null;
|
|
1990
|
+
return typeof s == "string" && s.trim() ? s : null;
|
|
1991
|
+
}
|
|
1992
|
+
function E(e, t) {
|
|
1993
|
+
const s = e?.context?.module;
|
|
1994
|
+
if (!s)
|
|
1995
|
+
throw y({ statusCode: 500, statusMessage: "Module context is unavailable." });
|
|
1996
|
+
if (!s.userId)
|
|
1997
|
+
throw y({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
1998
|
+
if (s.isAdmin === !0) return;
|
|
1999
|
+
if (!(Array.isArray(s.permissions) ? s.permissions : []).includes(t))
|
|
2000
|
+
throw y({ statusCode: 403, statusMessage: `Missing module permission: ${t}.` });
|
|
2001
|
+
}
|
|
2002
|
+
function te(e) {
|
|
2003
|
+
if (!e || typeof e != "object" || Array.isArray(e))
|
|
2004
|
+
throw y({ statusCode: 500, statusMessage: "Stored form data is invalid." });
|
|
2005
|
+
const t = e;
|
|
2006
|
+
if (typeof t.name != "string" || typeof t.key != "string" || !Ge.has(String(t.status)) || !Number.isInteger(t.revision) || !Number.isInteger(t.draftRevision) || !t.draftDefinition || typeof t.draftDefinition != "object")
|
|
2007
|
+
throw y({ statusCode: 500, statusMessage: "Stored form data is invalid." });
|
|
2008
|
+
return t;
|
|
2009
|
+
}
|
|
2010
|
+
function ue(e, t) {
|
|
2011
|
+
e.revision !== t && G(`Form has changed. Expected revision ${t}, current revision is ${e.revision}.`);
|
|
2012
|
+
}
|
|
2013
|
+
function Vt(e) {
|
|
2014
|
+
(Array.isArray(e) || !Array.isArray(e.layout) || e.layout.length === 0) && I("Add at least one form element before publishing.");
|
|
2015
|
+
}
|
|
2016
|
+
function X(e, t, s, i = !0) {
|
|
2017
|
+
const { draftDefinition: r, ...a } = t, n = i ? { id: e, ...a, draftDefinition: r } : { id: e, ...a }, c = s ? s(n) : n, o = Number.isInteger(c.publishedRevision) ? c.publishedRevision : null, u = Number.isInteger(c.publishedDraftRevision) ? c.publishedDraftRevision : null;
|
|
2018
|
+
return {
|
|
2019
|
+
id: c.id,
|
|
2020
|
+
name: c.name,
|
|
2021
|
+
key: c.key,
|
|
2022
|
+
description: c.description || "",
|
|
2023
|
+
...i ? { definition: r } : {},
|
|
2024
|
+
status: c.status,
|
|
2025
|
+
revision: c.revision,
|
|
2026
|
+
draftRevision: c.draftRevision,
|
|
2027
|
+
publishedRevision: o,
|
|
2028
|
+
publishedRevisionId: c.publishedRevisionId || null,
|
|
2029
|
+
hasUnpublishedChanges: !c.publishedRevisionId || c.draftRevision !== u,
|
|
2030
|
+
schemaVersion: c.schemaVersion || be,
|
|
2031
|
+
createdAt: c.createdAt || null,
|
|
2032
|
+
createdBy: c.createdBy || null,
|
|
2033
|
+
updatedAt: c.updatedAt || null,
|
|
2034
|
+
updatedBy: c.updatedBy || null,
|
|
2035
|
+
publishedAt: c.publishedAt || null,
|
|
2036
|
+
publishedBy: c.publishedBy || null,
|
|
2037
|
+
archivedAt: c.archivedAt || null,
|
|
2038
|
+
archivedBy: c.archivedBy || null
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
function _e(e, t) {
|
|
2042
|
+
if (!(e == null || e === ""))
|
|
2043
|
+
return (Array.isArray(e) || typeof e != "string") && I(`${t} must be a single string.`), e.trim();
|
|
2044
|
+
}
|
|
2045
|
+
function qe(e) {
|
|
2046
|
+
if (typeof e == "string") {
|
|
2047
|
+
const t = Date.parse(e);
|
|
2048
|
+
return Number.isNaN(t) ? 0 : t;
|
|
2049
|
+
}
|
|
2050
|
+
return e instanceof Date ? e.getTime() : e && typeof e.toMillis == "function" ? e.toMillis() : 0;
|
|
2051
|
+
}
|
|
2052
|
+
function Z(e, t, s) {
|
|
2053
|
+
throw Number.isInteger(t?.statusCode) && t.statusCode >= 400 && t.statusCode < 600 ? t : (console.error(`Form Builder handler — ${e} error:`, t), y({ statusCode: 500, statusMessage: s }));
|
|
2054
|
+
}
|
|
2055
|
+
function Xt(e) {
|
|
2056
|
+
const { initFirebase: t, normalizeTimestamps: s } = e, i = se();
|
|
2057
|
+
async function r(a) {
|
|
2058
|
+
const n = a?.context?.module, { supabase: c, instanceId: o, projectId: u, moduleType: d } = n || {};
|
|
2059
|
+
if (!c || !o)
|
|
2060
|
+
throw y({ statusCode: 500, statusMessage: "Module context is unavailable." });
|
|
2061
|
+
const { data: l, error: f } = await c.from("project_modules").select("config, project_id, type").eq("id", o).single();
|
|
2062
|
+
if (f || !l?.config)
|
|
2063
|
+
throw y({ statusCode: 500, statusMessage: "Failed to load module config." });
|
|
2064
|
+
if (u && l.project_id !== u)
|
|
2065
|
+
throw y({ statusCode: 403, statusMessage: "Module project context does not match." });
|
|
2066
|
+
if (d && l.type !== d)
|
|
2067
|
+
throw y({ statusCode: 409, statusMessage: "Module type context does not match." });
|
|
2068
|
+
const g = l.config, m = g.databaseIntegration;
|
|
2069
|
+
if (typeof m != "string" || !m.trim())
|
|
2070
|
+
throw y({
|
|
2071
|
+
statusCode: 400,
|
|
2072
|
+
statusMessage: "Form Builder has no Firebase integration configured."
|
|
2073
|
+
});
|
|
2074
|
+
if (m.length > 200 || !/^[A-Za-z0-9_-]+$/.test(m))
|
|
2075
|
+
throw y({ statusCode: 500, statusMessage: "Configured Firebase integration ID is invalid." });
|
|
2076
|
+
const h = u || l.project_id, { data: p, error: w } = await c.from("integrations").select("id").eq("id", m).eq("project_id", h).eq("type", "FIREBASE").single();
|
|
2077
|
+
if (w || !p)
|
|
2078
|
+
throw y({
|
|
2079
|
+
statusCode: 400,
|
|
2080
|
+
statusMessage: "Configured Firebase integration is unavailable for this project."
|
|
2081
|
+
});
|
|
2082
|
+
const C = qt(g.formsCollection), b = ee(o), F = (await t(a, m)).firestore();
|
|
2083
|
+
return {
|
|
2084
|
+
firestore: F,
|
|
2085
|
+
// Access is granted per module instance in Studio, so storage must be
|
|
2086
|
+
// isolated at the same boundary even when multiple instances retain the
|
|
2087
|
+
// default root collection.
|
|
2088
|
+
forms: F.collection(C).doc(b).collection("forms")
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
return i.get("/forms", A(async (a) => {
|
|
2092
|
+
E(a, x.view);
|
|
2093
|
+
try {
|
|
2094
|
+
const n = R(a), c = _e(n.status, "status"), o = _e(n.search, "search");
|
|
2095
|
+
c && !Ge.has(c) && I("status must be draft, published, or archived."), o && o.length > Te && I(`search cannot exceed ${Te} characters.`);
|
|
2096
|
+
const { forms: u } = await r(a), d = c ? await u.where("status", "==", c).get() : await u.get(), l = o?.toLocaleLowerCase();
|
|
2097
|
+
return d.docs.map((f) => X(
|
|
2098
|
+
f.id,
|
|
2099
|
+
te(f.data()),
|
|
2100
|
+
s,
|
|
2101
|
+
!1
|
|
2102
|
+
)).filter((f) => l ? [f.name, f.key, f.description].some((g) => String(g || "").toLocaleLowerCase().includes(l)) : !0).sort((f, g) => qe(g.updatedAt) - qe(f.updatedAt));
|
|
2103
|
+
} catch (n) {
|
|
2104
|
+
Z("list", n, "Failed to list forms.");
|
|
2105
|
+
}
|
|
2106
|
+
})), i.get("/forms/:formId", A(async (a) => {
|
|
2107
|
+
E(a, x.view);
|
|
2108
|
+
const n = ee(N(a, "formId"));
|
|
2109
|
+
try {
|
|
2110
|
+
const { forms: c } = await r(a), o = await c.doc(n).get();
|
|
2111
|
+
if (!o.exists)
|
|
2112
|
+
throw y({ statusCode: 404, statusMessage: "Form not found." });
|
|
2113
|
+
return X(o.id, te(o.data()), s);
|
|
2114
|
+
} catch (c) {
|
|
2115
|
+
Z("get", c, "Failed to load form.");
|
|
2116
|
+
}
|
|
2117
|
+
})), i.post("/forms", A(async (a) => {
|
|
2118
|
+
E(a, x.view), E(a, x.create);
|
|
2119
|
+
try {
|
|
2120
|
+
const n = Bt(await Q(a)), c = oe(a), o = (/* @__PURE__ */ new Date()).toISOString(), { firestore: u, forms: d } = await r(a), l = d.doc(n.key), f = {
|
|
2121
|
+
name: n.name,
|
|
2122
|
+
key: n.key,
|
|
2123
|
+
description: n.description,
|
|
2124
|
+
status: "draft",
|
|
2125
|
+
draftDefinition: n.definition,
|
|
2126
|
+
schemaVersion: be,
|
|
2127
|
+
revision: 1,
|
|
2128
|
+
draftRevision: 1,
|
|
2129
|
+
publishedRevision: null,
|
|
2130
|
+
publishedRevisionId: null,
|
|
2131
|
+
publishedDraftRevision: null,
|
|
2132
|
+
createdAt: o,
|
|
2133
|
+
createdBy: c,
|
|
2134
|
+
updatedAt: o,
|
|
2135
|
+
updatedBy: c,
|
|
2136
|
+
draftUpdatedAt: o,
|
|
2137
|
+
draftUpdatedBy: c,
|
|
2138
|
+
publishedAt: null,
|
|
2139
|
+
publishedBy: null,
|
|
2140
|
+
archivedAt: null,
|
|
2141
|
+
archivedBy: null,
|
|
2142
|
+
statusBeforeArchive: null
|
|
2143
|
+
};
|
|
2144
|
+
return await u.runTransaction(async (g) => {
|
|
2145
|
+
(await g.get(l)).exists && G(`A form with key "${n.key}" already exists.`), g.create(l, f);
|
|
2146
|
+
}), X(l.id, f, s);
|
|
2147
|
+
} catch (n) {
|
|
2148
|
+
Z("create", n, "Failed to create form.");
|
|
2149
|
+
}
|
|
2150
|
+
})), i.patch("/forms/:formId/draft", A(async (a) => {
|
|
2151
|
+
E(a, x.view), E(a, x.edit);
|
|
2152
|
+
const n = ee(N(a, "formId"));
|
|
2153
|
+
try {
|
|
2154
|
+
const c = jt(await Q(a)), o = oe(a), u = (/* @__PURE__ */ new Date()).toISOString(), { firestore: d, forms: l } = await r(a), f = l.doc(n);
|
|
2155
|
+
return await d.runTransaction(async (g) => {
|
|
2156
|
+
const m = await g.get(f);
|
|
2157
|
+
if (!m.exists)
|
|
2158
|
+
throw y({ statusCode: 404, statusMessage: "Form not found." });
|
|
2159
|
+
const h = te(m.data());
|
|
2160
|
+
ue(h, c.expectedRevision), h.status === "archived" && G("Restore this form before editing its draft."), h.key !== c.key && G("A form key cannot be changed after the form is created.");
|
|
2161
|
+
const p = {
|
|
2162
|
+
name: c.name,
|
|
2163
|
+
key: c.key,
|
|
2164
|
+
description: c.description,
|
|
2165
|
+
draftDefinition: c.definition,
|
|
2166
|
+
revision: h.revision + 1,
|
|
2167
|
+
draftRevision: h.draftRevision + 1,
|
|
2168
|
+
updatedAt: u,
|
|
2169
|
+
updatedBy: o,
|
|
2170
|
+
draftUpdatedAt: u,
|
|
2171
|
+
draftUpdatedBy: o
|
|
2172
|
+
};
|
|
2173
|
+
return g.update(f, p), X(n, { ...h, ...p }, s);
|
|
2174
|
+
});
|
|
2175
|
+
} catch (c) {
|
|
2176
|
+
Z("update draft", c, "Failed to update form draft.");
|
|
2177
|
+
}
|
|
2178
|
+
})), i.post("/forms/:formId/publish", A(async (a) => {
|
|
2179
|
+
E(a, x.view), E(a, x.publish);
|
|
2180
|
+
const n = ee(N(a, "formId"));
|
|
2181
|
+
try {
|
|
2182
|
+
const { expectedRevision: c } = ge(await Q(a)), o = oe(a), u = (/* @__PURE__ */ new Date()).toISOString(), { firestore: d, forms: l } = await r(a), f = l.doc(n), g = f.collection("revisions").doc();
|
|
2183
|
+
return await d.runTransaction(async (m) => {
|
|
2184
|
+
const h = await m.get(f);
|
|
2185
|
+
if (!h.exists)
|
|
2186
|
+
throw y({ statusCode: 404, statusMessage: "Form not found." });
|
|
2187
|
+
const p = te(h.data());
|
|
2188
|
+
ue(p, c), p.status === "archived" && G("Restore this form before publishing it."), Vt(p.draftDefinition), p.publishedRevisionId && p.publishedDraftRevision === p.draftRevision && G("This form has no unpublished changes.");
|
|
2189
|
+
const w = p.revision + 1, C = $t({
|
|
2190
|
+
formId: n,
|
|
2191
|
+
form: p,
|
|
2192
|
+
revision: w,
|
|
2193
|
+
revisionId: g.id,
|
|
2194
|
+
publishedAt: u,
|
|
2195
|
+
publishedBy: o
|
|
2196
|
+
}), b = {
|
|
2197
|
+
status: "published",
|
|
2198
|
+
revision: w,
|
|
2199
|
+
publishedRevision: w,
|
|
2200
|
+
publishedRevisionId: g.id,
|
|
2201
|
+
publishedDraftRevision: p.draftRevision,
|
|
2202
|
+
publishedAt: u,
|
|
2203
|
+
publishedBy: o,
|
|
2204
|
+
updatedAt: u,
|
|
2205
|
+
updatedBy: o,
|
|
2206
|
+
archivedAt: null,
|
|
2207
|
+
archivedBy: null,
|
|
2208
|
+
statusBeforeArchive: null
|
|
2209
|
+
};
|
|
2210
|
+
return m.create(g, C), m.update(f, b), X(n, { ...p, ...b }, s);
|
|
2211
|
+
});
|
|
2212
|
+
} catch (c) {
|
|
2213
|
+
Z("publish", c, "Failed to publish form.");
|
|
2214
|
+
}
|
|
2215
|
+
})), i.post("/forms/:formId/archive", A(async (a) => {
|
|
2216
|
+
E(a, x.view), E(a, x.archive);
|
|
2217
|
+
const n = ee(N(a, "formId"));
|
|
2218
|
+
try {
|
|
2219
|
+
const { expectedRevision: c } = ge(await Q(a)), o = oe(a), u = (/* @__PURE__ */ new Date()).toISOString(), { firestore: d, forms: l } = await r(a), f = l.doc(n);
|
|
2220
|
+
return await d.runTransaction(async (g) => {
|
|
2221
|
+
const m = await g.get(f);
|
|
2222
|
+
if (!m.exists)
|
|
2223
|
+
throw y({ statusCode: 404, statusMessage: "Form not found." });
|
|
2224
|
+
const h = te(m.data());
|
|
2225
|
+
ue(h, c), h.status === "archived" && G("Form is already archived.");
|
|
2226
|
+
const p = {
|
|
2227
|
+
status: "archived",
|
|
2228
|
+
statusBeforeArchive: h.status,
|
|
2229
|
+
revision: h.revision + 1,
|
|
2230
|
+
archivedAt: u,
|
|
2231
|
+
archivedBy: o,
|
|
2232
|
+
updatedAt: u,
|
|
2233
|
+
updatedBy: o
|
|
2234
|
+
};
|
|
2235
|
+
return g.update(f, p), X(n, { ...h, ...p }, s);
|
|
2236
|
+
});
|
|
2237
|
+
} catch (c) {
|
|
2238
|
+
Z("archive", c, "Failed to archive form.");
|
|
2239
|
+
}
|
|
2240
|
+
})), i.post("/forms/:formId/restore", A(async (a) => {
|
|
2241
|
+
E(a, x.view), E(a, x.archive);
|
|
2242
|
+
const n = ee(N(a, "formId"));
|
|
2243
|
+
try {
|
|
2244
|
+
const { expectedRevision: c } = ge(await Q(a)), o = oe(a), u = (/* @__PURE__ */ new Date()).toISOString(), { firestore: d, forms: l } = await r(a), f = l.doc(n);
|
|
2245
|
+
return await d.runTransaction(async (g) => {
|
|
2246
|
+
const m = await g.get(f);
|
|
2247
|
+
if (!m.exists)
|
|
2248
|
+
throw y({ statusCode: 404, statusMessage: "Form not found." });
|
|
2249
|
+
const h = te(m.data());
|
|
2250
|
+
ue(h, c), h.status !== "archived" && G("Only archived forms can be restored.");
|
|
2251
|
+
const w = {
|
|
2252
|
+
status: h.publishedRevisionId ? "published" : "draft",
|
|
2253
|
+
statusBeforeArchive: null,
|
|
2254
|
+
revision: h.revision + 1,
|
|
2255
|
+
archivedAt: null,
|
|
2256
|
+
archivedBy: null,
|
|
2257
|
+
updatedAt: u,
|
|
2258
|
+
updatedBy: o
|
|
2259
|
+
};
|
|
2260
|
+
return g.update(f, w), X(n, { ...h, ...w }, s);
|
|
2261
|
+
});
|
|
2262
|
+
} catch (c) {
|
|
2263
|
+
Z("restore", c, "Failed to restore form.");
|
|
2264
|
+
}
|
|
2265
|
+
})), i.handler;
|
|
1109
2266
|
}
|
|
1110
2267
|
export {
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
2268
|
+
Qt as appointments,
|
|
2269
|
+
Kt as contentManager,
|
|
2270
|
+
Jt as firebaseAuthManager,
|
|
2271
|
+
Xt as formBuilder,
|
|
2272
|
+
Yt as googleAnalytics,
|
|
2273
|
+
Gt as products
|
|
1115
2274
|
};
|