cloudcommerce 0.3.0 → 0.4.1
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/CHANGELOG.md +40 -0
- package/package.json +8 -8
- package/packages/api/package.json +1 -1
- package/packages/apps/correios/package.json +2 -2
- package/packages/apps/custom-payment/package.json +1 -1
- package/packages/apps/custom-shipping/package.json +1 -1
- package/packages/apps/datafrete/package.json +3 -3
- package/packages/apps/discounts/package.json +1 -1
- package/packages/apps/emails/package.json +1 -1
- package/packages/apps/fb-conversions/package.json +2 -2
- package/packages/apps/frenet/package.json +3 -3
- package/packages/apps/galaxpay/package.json +3 -3
- package/packages/apps/google-analytics/package.json +3 -3
- package/packages/apps/infinitepay/lib/ip-create-transaction.js +1 -1
- package/packages/apps/infinitepay/lib/ip-create-transaction.js.map +1 -1
- package/packages/apps/infinitepay/package.json +3 -3
- package/packages/apps/infinitepay/src/ip-create-transaction.ts +1 -1
- package/packages/apps/jadlog/package.json +2 -2
- package/packages/apps/loyalty-points/package.json +1 -1
- package/packages/apps/melhor-envio/package.json +3 -3
- package/packages/apps/mercadopago/package.json +3 -3
- package/packages/apps/pagarme/package.json +3 -3
- package/packages/apps/paghiper/package.json +3 -3
- package/packages/apps/pix/package.json +3 -3
- package/packages/apps/tiny-erp/package.json +3 -3
- package/packages/apps/webhooks/CHANGELOG.md +1 -0
- package/packages/apps/webhooks/README.md +1 -0
- package/packages/apps/webhooks/lib/app-webhooks.js +9 -0
- package/packages/apps/webhooks/lib/app-webhooks.js.map +1 -0
- package/packages/apps/webhooks/lib/events-to-webhooks-app.js +127 -0
- package/packages/apps/webhooks/lib/events-to-webhooks-app.js.map +1 -0
- package/packages/apps/webhooks/lib/index.js +2 -0
- package/packages/apps/webhooks/lib/index.js.map +1 -0
- package/packages/apps/webhooks/package.json +32 -0
- package/packages/apps/webhooks/src/app-webhooks.ts +14 -0
- package/packages/apps/webhooks/src/events-to-webhooks-app.ts +178 -0
- package/packages/apps/webhooks/src/index.ts +1 -0
- package/packages/apps/webhooks/tsconfig.json +3 -0
- package/packages/cli/lib/setup-gcloud.js +120 -20
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/cli.ts +1 -1
- package/packages/cli/src/setup-gcloud.ts +146 -21
- package/packages/config/package.json +1 -1
- package/packages/emails/package.json +2 -2
- package/packages/emails/tests/assets/order.json +1 -1
- package/packages/events/lib/firebase.js +2 -0
- package/packages/events/lib/firebase.js.map +1 -1
- package/packages/events/package.json +3 -2
- package/packages/events/src/firebase.ts +2 -0
- package/packages/firebase/lib/config.d.ts +4 -0
- package/packages/firebase/lib/config.js +10 -1
- package/packages/firebase/lib/config.js.map +1 -1
- package/packages/firebase/lib/handlers/check-store-events.js +13 -4
- package/packages/firebase/lib/handlers/check-store-events.js.map +1 -1
- package/packages/firebase/package.json +2 -2
- package/packages/firebase/src/config.ts +10 -7
- package/packages/firebase/src/handlers/check-store-events.ts +14 -4
- package/packages/i18n/package.json +1 -1
- package/packages/modules/lib/firebase/checkout.js +4 -4
- package/packages/modules/lib/firebase/checkout.js.map +1 -1
- package/packages/modules/package.json +3 -3
- package/packages/modules/schemas/@checkout.cjs +0 -5
- package/packages/modules/schemas/create_transaction.cjs +6 -1
- package/packages/modules/src/firebase/checkout.ts +4 -4
- package/packages/passport/package.json +2 -2
- package/packages/ssr/package.json +7 -7
- package/packages/storefront/dist/client/_astro/PitchBar.004b6ea4.js +1 -0
- package/packages/storefront/dist/client/_astro/Prices.f311909a.js +1 -0
- package/packages/storefront/dist/client/_astro/Prices.vue_vue_type_script_setup_true_lang.ef47de70.js +1 -0
- package/packages/storefront/dist/client/_astro/ProductCard.34de5097.js +1 -0
- package/packages/storefront/dist/client/_astro/ShopHeader.cbfee289.js +1 -0
- package/packages/storefront/dist/client/_astro/{_...slug_.32968ccf.css → _...slug_.bcc33d9d.css} +1 -1
- package/packages/storefront/dist/client/_astro/{client.5a46cc02.js → client.56d86c9b.js} +1 -1
- package/packages/storefront/dist/client/_astro/index.0c833781.css +1 -0
- package/packages/storefront/dist/client/_astro/index.2d12be6c.js +1 -0
- package/packages/storefront/dist/client/_astro/{modules-info.d9373e21.js → modules-info.0debb0b0.js} +1 -1
- package/packages/storefront/dist/client/_astro/runtime-core.esm-bundler.a0432a8e.js +1 -0
- package/packages/storefront/dist/client/_astro/{runtime-dom.esm-bundler.00313542.js → runtime-dom.esm-bundler.0e5774ce.js} +1 -1
- package/packages/storefront/dist/client/fallback/index.html +10 -10
- package/packages/storefront/dist/client/sw.js +1 -1
- package/packages/storefront/dist/server/chunks/{astro.89bd9221.mjs → astro.0f5b754a.mjs} +871 -826
- package/packages/storefront/dist/server/chunks/pages/{all.23de4e5c.mjs → all.671e6bc1.mjs} +367 -466
- package/packages/storefront/dist/server/chunks/{prerender.f40361a3.mjs → prerender.fd8cdc24.mjs} +0 -0
- package/packages/storefront/dist/server/entry.mjs +20 -12
- package/packages/storefront/package.json +11 -11
- package/packages/storefront/src/lib/components/Drawer.vue +48 -45
- package/packages/storefront/src/lib/components/ProductCard.vue +1 -1
- package/packages/storefront/src/lib/components/ShopHeader.vue +42 -13
- package/packages/storefront/src/lib/components/ShopSidenav.vue +26 -0
- package/packages/storefront/src/lib/composables/use-pitch-bar.ts +27 -0
- package/packages/storefront/src/lib/composables/use-sticky-header.ts +111 -0
- package/packages/storefront/src/lib/layouts/BaseBody.astro +3 -1
- package/packages/storefront/src/lib/layouts/PagesHeader.astro +16 -25
- package/packages/types/index.ts +1 -0
- package/packages/types/modules/@checkout:params.d.ts +1 -5
- package/packages/types/modules/apply_discount:params.d.ts +1 -1
- package/packages/types/modules/apply_discount:response.d.ts +1 -1
- package/packages/types/modules/calculate_shipping:params.d.ts +1 -1
- package/packages/types/modules/calculate_shipping:response.d.ts +1 -1
- package/packages/types/modules/create_transaction:params.d.ts +6 -2
- package/packages/types/modules/create_transaction:response.d.ts +1 -1
- package/packages/types/modules/list_payments:params.d.ts +1 -1
- package/packages/types/modules/list_payments:response.d.ts +1 -1
- package/packages/types/package.json +1 -1
- package/packages/storefront/dist/client/_astro/PitchBar.209c6645.js +0 -1
- package/packages/storefront/dist/client/_astro/Prices.6fbcb5ac.js +0 -1
- package/packages/storefront/dist/client/_astro/Prices.vue_vue_type_script_setup_true_lang.44f23680.js +0 -1
- package/packages/storefront/dist/client/_astro/ProductCard.ee5eee91.js +0 -1
- package/packages/storefront/dist/client/_astro/ShopHeader.b801c785.js +0 -1
- package/packages/storefront/dist/client/_astro/index.844a4059.js +0 -1
- package/packages/storefront/dist/client/_astro/index.90df622b.css +0 -1
- package/packages/storefront/dist/client/_astro/runtime-core.esm-bundler.f04cee62.js +0 -1
- package/packages/storefront/dist/client/_astro/use-component-variant.58788b6e.js +0 -1
- package/packages/storefront/src/lib/components/PitchBar.vue +0 -66
- package/packages/storefront/src/lib/components/Prices.vue +0 -176
- package/packages/storefront/src/lib/components/StickyHeader.vue +0 -84
- package/packages/storefront/src/lib/composables/use-component-variant.ts +0 -17
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import logger from 'firebase-functions/logger';
|
|
2
|
+
import api from '@cloudcommerce/api';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
|
|
5
|
+
const updateManualQueue = async (manualQueue, appId) => {
|
|
6
|
+
if (manualQueue) {
|
|
7
|
+
try {
|
|
8
|
+
await api.patch(`applications/${appId}`, { data: { manual_queue: manualQueue } });
|
|
9
|
+
manualQueue = null;
|
|
10
|
+
} catch (err) {
|
|
11
|
+
logger.error(err);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const sendWebhook = async (options, doc, urls, manualQueue, appId, customer, docId, isCart) => {
|
|
16
|
+
const url = options && options.webhook_uri;
|
|
17
|
+
if (url && !urls.includes(url) && (!isCart || options.send_carts)) {
|
|
18
|
+
urls.push(url);
|
|
19
|
+
logger.log(`Event ${isCart ? 'cart' : 'order'} => ${url}`);
|
|
20
|
+
if (options.skip_pending === true
|
|
21
|
+
&& (!doc.financial_status || doc.financial_status.current === 'pending')) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
logger.log(`> Sending ${isCart ? 'cart' : 'order'} notification`);
|
|
25
|
+
const token = options.webhook_token;
|
|
26
|
+
let headers;
|
|
27
|
+
if (token) {
|
|
28
|
+
headers = {
|
|
29
|
+
Authorization: `Bearer ${token}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const body = {
|
|
33
|
+
[isCart ? 'cart' : 'order']: doc,
|
|
34
|
+
customer,
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
const { status } = await axios.post(url, body, { headers });
|
|
38
|
+
if (status) {
|
|
39
|
+
await updateManualQueue(manualQueue, appId);
|
|
40
|
+
// logger.log(`> ${status}`);
|
|
41
|
+
return status;
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error.response && error.config) {
|
|
45
|
+
const err = { name: `#${docId} POST to ${error.config.url} failed` };
|
|
46
|
+
const { status, data } = error.response;
|
|
47
|
+
err.response = {
|
|
48
|
+
status,
|
|
49
|
+
data: JSON.stringify(data),
|
|
50
|
+
};
|
|
51
|
+
err.data = JSON.stringify(error.config.data);
|
|
52
|
+
logger.error(err);
|
|
53
|
+
} else {
|
|
54
|
+
logger.error(error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
};
|
|
60
|
+
const handleApiEvent = async ({
|
|
61
|
+
evName, apiEvent, apiDoc, app,
|
|
62
|
+
}) => {
|
|
63
|
+
const appData = { ...app.data, ...app.hidden_data };
|
|
64
|
+
const resourceId = apiEvent.resource_id;
|
|
65
|
+
const key = `${evName}_${resourceId}`;
|
|
66
|
+
if (Array.isArray(appData.ignore_events)
|
|
67
|
+
&& appData.ignore_events.includes(evName)) {
|
|
68
|
+
logger.info('>> ', key, ' - Ignored event');
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
if (appData.webhooks) {
|
|
73
|
+
let docId;
|
|
74
|
+
let manualQueue = null;
|
|
75
|
+
let isCart;
|
|
76
|
+
if (evName === 'applications-dataSet') {
|
|
77
|
+
const body = appData;
|
|
78
|
+
if (body && Array.isArray(body.manual_queue) && body.manual_queue.length) {
|
|
79
|
+
manualQueue = body.manual_queue;
|
|
80
|
+
const nextId = manualQueue[0];
|
|
81
|
+
if (typeof nextId === 'string' && /[a-f0-9]{24}/.test(nextId)) {
|
|
82
|
+
docId = nextId.trim();
|
|
83
|
+
}
|
|
84
|
+
manualQueue.shift();
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
isCart = evName === 'carts-delayed';
|
|
88
|
+
docId = apiDoc._id;
|
|
89
|
+
}
|
|
90
|
+
if (docId) {
|
|
91
|
+
let customer;
|
|
92
|
+
let doc;
|
|
93
|
+
if (isCart) {
|
|
94
|
+
doc = apiDoc;
|
|
95
|
+
if (doc.completed || doc.available === false) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const customerId = doc.customers && apiDoc.customers[0];
|
|
99
|
+
if (customerId) {
|
|
100
|
+
customer = (await api.get(`customers/${customerId}`)).data;
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
doc = (await api.get(`orders/${docId}`)).data;
|
|
104
|
+
}
|
|
105
|
+
if (doc) {
|
|
106
|
+
const urls = [];
|
|
107
|
+
const { webhooks } = appData;
|
|
108
|
+
if (Array.isArray(webhooks)) {
|
|
109
|
+
webhooks.forEach(async (webhook) => {
|
|
110
|
+
await sendWebhook(webhook, doc, urls, manualQueue, app._id, customer, docId, isCart);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
await sendWebhook(appData, doc, urls, manualQueue, app._id, customer, docId, isCart);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
await updateManualQueue(manualQueue, app._id);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
} catch (err) {
|
|
121
|
+
logger.error(err);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default handleApiEvent;
|
|
127
|
+
// # sourceMappingURL=events-to-webhooks-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-to-webhooks-app.js","sourceRoot":"","sources":["../src/events-to-webhooks-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cloudcommerce/app-webhooks",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.4.1",
|
|
5
|
+
"description": "E-Com Plus Cloud Commerce app for general order webhooks",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/ecomplus/cloud-commerce.git",
|
|
10
|
+
"directory": "packages/apps/webhooks"
|
|
11
|
+
},
|
|
12
|
+
"author": "E-Com Club Softwares para E-commerce <ti@e-com.club>",
|
|
13
|
+
"license": "Apache 2.0 with Commons Clause",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/ecomplus/cloud-commerce/issues"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/ecomplus/cloud-commerce/tree/main/packages/apps/webhooks#readme",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "sh ../../../scripts/build-lib.sh"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@cloudcommerce/api": "workspace:*",
|
|
23
|
+
"@cloudcommerce/firebase": "workspace:*",
|
|
24
|
+
"axios": "^1.2.6",
|
|
25
|
+
"firebase-admin": "^11.5.0",
|
|
26
|
+
"firebase-functions": "^4.2.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@cloudcommerce/types": "workspace:*",
|
|
30
|
+
"@firebase/app-types": "^0.9.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
import '@cloudcommerce/firebase/lib/init';
|
|
3
|
+
import {
|
|
4
|
+
createAppEventsFunction,
|
|
5
|
+
ApiEventHandler,
|
|
6
|
+
} from '@cloudcommerce/firebase/lib/helpers/pubsub';
|
|
7
|
+
import handleApiEvent from './events-to-webhooks-app';
|
|
8
|
+
|
|
9
|
+
export const webhooksapp = {
|
|
10
|
+
onStoreEvent: createAppEventsFunction(
|
|
11
|
+
'webhooksApp',
|
|
12
|
+
handleApiEvent as ApiEventHandler,
|
|
13
|
+
),
|
|
14
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { ApiEventHandler } from '@cloudcommerce/firebase/lib/helpers/pubsub';
|
|
2
|
+
import type { Carts, Customers, Orders } from '@cloudcommerce/api/types';
|
|
3
|
+
import logger from 'firebase-functions/logger';
|
|
4
|
+
import api from '@cloudcommerce/api';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
|
|
7
|
+
const updateManualQueue = async (
|
|
8
|
+
manualQueue: any[] | null,
|
|
9
|
+
appId: string,
|
|
10
|
+
) => {
|
|
11
|
+
if (manualQueue) {
|
|
12
|
+
try {
|
|
13
|
+
await api.patch(`applications/${appId}`, { data: { manual_queue: manualQueue } });
|
|
14
|
+
manualQueue = null;
|
|
15
|
+
} catch (err) {
|
|
16
|
+
logger.error(err);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const sendWebhook = async (
|
|
22
|
+
options: { [x: string]: any },
|
|
23
|
+
doc: Record<string, any>,
|
|
24
|
+
urls: any[],
|
|
25
|
+
manualQueue: any[] | null,
|
|
26
|
+
appId: string,
|
|
27
|
+
customer?: Customers,
|
|
28
|
+
docId?: string,
|
|
29
|
+
isCart?: boolean,
|
|
30
|
+
) => {
|
|
31
|
+
const url = options && options.webhook_uri;
|
|
32
|
+
if (url && !urls.includes(url) && (!isCart || options.send_carts)) {
|
|
33
|
+
urls.push(url);
|
|
34
|
+
logger.log(`Event ${isCart ? 'cart' : 'order'} => ${url}`);
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
options.skip_pending === true
|
|
38
|
+
&& (!doc.financial_status || doc.financial_status.current === 'pending')
|
|
39
|
+
) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
logger.log(`> Sending ${isCart ? 'cart' : 'order'} notification`);
|
|
44
|
+
|
|
45
|
+
const token = options.webhook_token;
|
|
46
|
+
let headers: { Authorization: string; } | undefined;
|
|
47
|
+
|
|
48
|
+
if (token) {
|
|
49
|
+
headers = {
|
|
50
|
+
Authorization: `Bearer ${token}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const body = {
|
|
54
|
+
[isCart ? 'cart' : 'order']: doc,
|
|
55
|
+
customer,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const { status } = await axios.post(url, body, { headers });
|
|
60
|
+
if (status) {
|
|
61
|
+
await updateManualQueue(manualQueue, appId);
|
|
62
|
+
// logger.log(`> ${status}`);
|
|
63
|
+
return status;
|
|
64
|
+
}
|
|
65
|
+
} catch (error: any) {
|
|
66
|
+
if (error.response && error.config) {
|
|
67
|
+
const err: { [x: string]: any } = { name: `#${docId} POST to ${error.config.url} failed` };
|
|
68
|
+
const { status, data } = error.response;
|
|
69
|
+
err.response = {
|
|
70
|
+
status,
|
|
71
|
+
data: JSON.stringify(data),
|
|
72
|
+
};
|
|
73
|
+
err.data = JSON.stringify(error.config.data);
|
|
74
|
+
logger.error(err);
|
|
75
|
+
} else {
|
|
76
|
+
logger.error(error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const handleApiEvent: ApiEventHandler = async ({
|
|
84
|
+
evName,
|
|
85
|
+
apiEvent,
|
|
86
|
+
apiDoc,
|
|
87
|
+
app,
|
|
88
|
+
}) => {
|
|
89
|
+
const appData = { ...app.data, ...app.hidden_data };
|
|
90
|
+
const resourceId = apiEvent.resource_id;
|
|
91
|
+
const key = `${evName}_${resourceId}`;
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
Array.isArray(appData.ignore_events)
|
|
95
|
+
&& appData.ignore_events.includes(evName)
|
|
96
|
+
) {
|
|
97
|
+
logger.info('>> ', key, ' - Ignored event');
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
if (appData.webhooks) {
|
|
103
|
+
let docId: string | undefined;
|
|
104
|
+
let manualQueue: any[] | null = null;
|
|
105
|
+
let isCart: boolean | undefined;
|
|
106
|
+
|
|
107
|
+
if (evName === 'applications-dataSet') {
|
|
108
|
+
const body = appData;
|
|
109
|
+
if (body && Array.isArray(body.manual_queue) && body.manual_queue.length) {
|
|
110
|
+
manualQueue = body.manual_queue;
|
|
111
|
+
const nextId = manualQueue[0];
|
|
112
|
+
if (typeof nextId === 'string' && /[a-f0-9]{24}/.test(nextId)) {
|
|
113
|
+
docId = nextId.trim();
|
|
114
|
+
}
|
|
115
|
+
manualQueue.shift();
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
isCart = evName === 'carts-delayed';
|
|
119
|
+
docId = apiDoc._id;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (docId) {
|
|
123
|
+
let customer: Customers | undefined;
|
|
124
|
+
let doc: Carts | Orders;
|
|
125
|
+
if (isCart) {
|
|
126
|
+
doc = apiDoc as Carts;
|
|
127
|
+
if (doc.completed || doc.available === false) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const customerId = doc.customers && apiDoc.customers[0];
|
|
131
|
+
if (customerId) {
|
|
132
|
+
customer = (await api.get(`customers/${customerId}`)).data;
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
doc = (await api.get(`orders/${docId}`)).data;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (doc) {
|
|
139
|
+
const urls: string[] = [];
|
|
140
|
+
const { webhooks } = appData;
|
|
141
|
+
if (Array.isArray(webhooks)) {
|
|
142
|
+
webhooks.forEach(async (webhook) => {
|
|
143
|
+
await sendWebhook(
|
|
144
|
+
webhook,
|
|
145
|
+
doc,
|
|
146
|
+
urls,
|
|
147
|
+
manualQueue,
|
|
148
|
+
app._id,
|
|
149
|
+
customer,
|
|
150
|
+
docId,
|
|
151
|
+
isCart,
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
await sendWebhook(
|
|
156
|
+
appData,
|
|
157
|
+
doc,
|
|
158
|
+
urls,
|
|
159
|
+
manualQueue,
|
|
160
|
+
app._id,
|
|
161
|
+
customer,
|
|
162
|
+
docId,
|
|
163
|
+
isCart,
|
|
164
|
+
);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
await updateManualQueue(manualQueue, app._id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
} catch (err) {
|
|
173
|
+
logger.error(err);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export default handleApiEvent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './app-webhooks';
|
|
@@ -1,25 +1,112 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
$, fs, question, echo,
|
|
4
|
+
} from 'zx';
|
|
3
5
|
|
|
6
|
+
let gcpAccessToken;
|
|
4
7
|
const serviceAccountId = 'cloud-commerce-gh-actions';
|
|
5
8
|
const getAccountEmail = (projectId) => {
|
|
6
9
|
return `${serviceAccountId}@${projectId}.iam.gserviceaccount.com`;
|
|
7
10
|
};
|
|
11
|
+
const requestApi = async (projectId, options) => {
|
|
12
|
+
const body = options?.body;
|
|
13
|
+
let url = options?.baseURL
|
|
14
|
+
|| `https://iam.googleapis.com/v1/projects/${projectId}/serviceAccounts`;
|
|
15
|
+
url += options?.url || '';
|
|
16
|
+
const data = await (await fetch(url, {
|
|
17
|
+
method: options?.method || 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
Authorization: `Bearer ${gcpAccessToken}`,
|
|
20
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
21
|
+
},
|
|
22
|
+
body,
|
|
23
|
+
})).json();
|
|
24
|
+
const { error } = data;
|
|
25
|
+
if (error) {
|
|
26
|
+
let msgErr = 'Unexpected error in request';
|
|
27
|
+
msgErr = error.message ? `Code: ${error.code} - ${error.message}` : msgErr;
|
|
28
|
+
const err = new Error(msgErr);
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
return data;
|
|
32
|
+
};
|
|
33
|
+
const getGcpAccessToken = async () => {
|
|
34
|
+
await echo`-- Get the Google Cloud account credentials:
|
|
35
|
+
1. Access https://shell.cloud.google.com/?fromcloudshell=true&show=terminal
|
|
36
|
+
2. Execute \`gcloud auth application-default print-access-token\` in Cloud Shell
|
|
37
|
+
`;
|
|
38
|
+
return question('Google Cloud access token: ');
|
|
39
|
+
};
|
|
8
40
|
const checkServiceAccountExists = async (projectId) => {
|
|
9
41
|
let hasServiceAccount;
|
|
10
42
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
43
|
+
if (!gcpAccessToken) {
|
|
44
|
+
const { stderr } = await $`gcloud iam service-accounts describe ${getAccountEmail(projectId)}`;
|
|
45
|
+
hasServiceAccount = !/not_?found/i.test(stderr);
|
|
46
|
+
} else {
|
|
47
|
+
// https://cloud.google.com/iam/docs/creating-managing-service-accounts?hl=pt-br#listing
|
|
48
|
+
const { accounts: listAccounts } = await requestApi(projectId);
|
|
49
|
+
const accountFound = listAccounts
|
|
50
|
+
&& listAccounts.find(({ email }) => email === getAccountEmail(projectId));
|
|
51
|
+
hasServiceAccount = Boolean(accountFound);
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
14
54
|
return null;
|
|
15
55
|
}
|
|
16
56
|
return hasServiceAccount;
|
|
17
57
|
};
|
|
18
58
|
const siginGcloudAndSetIAM = async (projectId, pwd) => {
|
|
19
|
-
|
|
20
|
-
|
|
59
|
+
let hasGcloud;
|
|
60
|
+
try {
|
|
61
|
+
hasGcloud = Boolean(await $`command -v gcloud`);
|
|
62
|
+
} catch {
|
|
63
|
+
hasGcloud = false;
|
|
64
|
+
}
|
|
65
|
+
if (hasGcloud) {
|
|
66
|
+
if (/no credential/i.test((await $`gcloud auth list`).stderr)) {
|
|
67
|
+
await $`gcloud auth login`;
|
|
68
|
+
}
|
|
69
|
+
await $`gcloud config set project ${projectId}`;
|
|
70
|
+
} else {
|
|
71
|
+
gcpAccessToken = await getGcpAccessToken();
|
|
72
|
+
}
|
|
73
|
+
const serviceAccount = await checkServiceAccountExists(projectId);
|
|
74
|
+
if (!serviceAccount) {
|
|
75
|
+
const description = 'A service account with permission to deploy Cloud Commerce'
|
|
76
|
+
+ ' from the GitHub repository to Firebase';
|
|
77
|
+
const displayName = 'Cloud Commerce GH Actions';
|
|
78
|
+
if (hasGcloud) {
|
|
79
|
+
await $`gcloud iam service-accounts create ${serviceAccountId} \
|
|
80
|
+
--description=${description} --display-name=${displayName}`;
|
|
81
|
+
} else if (gcpAccessToken) {
|
|
82
|
+
const body = JSON.stringify({
|
|
83
|
+
accountId: serviceAccountId,
|
|
84
|
+
serviceAccount: {
|
|
85
|
+
description,
|
|
86
|
+
displayName,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
await requestApi(projectId, { method: 'POST', body });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
await fs.ensureDir(path.join(pwd, '.cloudcommerce'));
|
|
93
|
+
const pathPolicyIAM = path.join(pwd, '.cloudcommerce', 'policyIAM.json');
|
|
94
|
+
let policyIAM = {};
|
|
95
|
+
const version = 3; // most recent
|
|
96
|
+
const baseURL = `https://cloudresourcemanager.googleapis.com/v1/projects/${projectId}`;
|
|
97
|
+
if (hasGcloud) {
|
|
98
|
+
await $`gcloud projects get-iam-policy ${projectId} --format json > ${pathPolicyIAM}`;
|
|
99
|
+
policyIAM = fs.readJSONSync(pathPolicyIAM);
|
|
100
|
+
} else if (gcpAccessToken) {
|
|
101
|
+
// https://cloud.google.com/iam/docs/granting-changing-revoking-access?hl=pt-br#view-access
|
|
102
|
+
// POST https://cloudresourcemanager.googleapis.com/API_VERSION/RESOURCE_TYPE/RESOURCE_ID:getIamPolicy
|
|
103
|
+
policyIAM = await requestApi(projectId, {
|
|
104
|
+
baseURL,
|
|
105
|
+
url: ':getIamPolicy',
|
|
106
|
+
method: 'POST',
|
|
107
|
+
body: JSON.stringify({ options: { requestedPolicyVersion: version } }),
|
|
108
|
+
});
|
|
21
109
|
}
|
|
22
|
-
await $`gcloud config set project ${projectId}`;
|
|
23
110
|
const roles = [
|
|
24
111
|
'roles/firebase.admin',
|
|
25
112
|
'roles/appengine.appAdmin',
|
|
@@ -32,17 +119,10 @@ const siginGcloudAndSetIAM = async (projectId, pwd) => {
|
|
|
32
119
|
'roles/serviceusage.apiKeysViewer',
|
|
33
120
|
'roles/serviceusage.serviceUsageAdmin',
|
|
34
121
|
];
|
|
35
|
-
|
|
36
|
-
if (!
|
|
37
|
-
|
|
38
|
-
--description="A service account with permission to deploy Cloud Commerce from the GitHub repository to Firebase" \
|
|
39
|
-
--display-name="Cloud Commerce GH Actions"`;
|
|
122
|
+
let { bindings } = policyIAM;
|
|
123
|
+
if (!bindings) {
|
|
124
|
+
bindings = [];
|
|
40
125
|
}
|
|
41
|
-
await fs.ensureDir(path.join(pwd, '.cloudcommerce'));
|
|
42
|
-
const pathPolicyIAM = path.join(pwd, '.cloudcommerce', 'policyIAM.json');
|
|
43
|
-
await $`gcloud projects get-iam-policy ${projectId} --format json > ${pathPolicyIAM}`;
|
|
44
|
-
const policyIAM = fs.readJSONSync(pathPolicyIAM);
|
|
45
|
-
const { bindings } = policyIAM;
|
|
46
126
|
let mustUpdatePolicy = false;
|
|
47
127
|
roles.forEach((role) => {
|
|
48
128
|
const roleFound = bindings.find((binding) => binding.role === role);
|
|
@@ -73,16 +153,36 @@ const siginGcloudAndSetIAM = async (projectId, pwd) => {
|
|
|
73
153
|
}
|
|
74
154
|
});
|
|
75
155
|
if (mustUpdatePolicy) {
|
|
76
|
-
|
|
77
|
-
|
|
156
|
+
if (hasGcloud) {
|
|
157
|
+
fs.writeJSONSync(pathPolicyIAM, policyIAM);
|
|
158
|
+
return $`gcloud projects set-iam-policy ${projectId} ${pathPolicyIAM}`;
|
|
159
|
+
}
|
|
160
|
+
if (gcpAccessToken) {
|
|
161
|
+
Object.assign(policyIAM, { version, bindings });
|
|
162
|
+
// POST https://cloudresourcemanager.googleapis.com/API_VERSION/RESOURCE_TYPE/RESOURCE_ID:setIamPolicy
|
|
163
|
+
return requestApi(projectId, {
|
|
164
|
+
baseURL,
|
|
165
|
+
url: ':setIamPolicy',
|
|
166
|
+
method: 'POST',
|
|
167
|
+
body: JSON.stringify({ policy: policyIAM }),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
78
170
|
}
|
|
79
171
|
return null;
|
|
80
172
|
};
|
|
81
173
|
const createServiceAccountKey = async (projectId, pwd) => {
|
|
82
174
|
try {
|
|
83
175
|
const pathFileKey = path.join(pwd, '.cloudcommerce', 'serviceAccountKey.json');
|
|
84
|
-
|
|
176
|
+
if (!gcpAccessToken) {
|
|
177
|
+
await $`gcloud iam service-accounts keys create ${pathFileKey} \
|
|
85
178
|
--iam-account=${getAccountEmail(projectId)}`;
|
|
179
|
+
} else {
|
|
180
|
+
const { privateKeyData } = await requestApi(projectId, {
|
|
181
|
+
url: `/${getAccountEmail(projectId)}/keys`,
|
|
182
|
+
method: 'POST',
|
|
183
|
+
});
|
|
184
|
+
await $`echo '${privateKeyData}' | base64 --decode > ${pathFileKey}`;
|
|
185
|
+
}
|
|
86
186
|
return JSON.stringify(fs.readJSONSync(pathFileKey));
|
|
87
187
|
} catch (e) {
|
|
88
188
|
return null;
|
package/packages/cli/src/cli.ts
CHANGED