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.
Files changed (117) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/package.json +8 -8
  3. package/packages/api/package.json +1 -1
  4. package/packages/apps/correios/package.json +2 -2
  5. package/packages/apps/custom-payment/package.json +1 -1
  6. package/packages/apps/custom-shipping/package.json +1 -1
  7. package/packages/apps/datafrete/package.json +3 -3
  8. package/packages/apps/discounts/package.json +1 -1
  9. package/packages/apps/emails/package.json +1 -1
  10. package/packages/apps/fb-conversions/package.json +2 -2
  11. package/packages/apps/frenet/package.json +3 -3
  12. package/packages/apps/galaxpay/package.json +3 -3
  13. package/packages/apps/google-analytics/package.json +3 -3
  14. package/packages/apps/infinitepay/lib/ip-create-transaction.js +1 -1
  15. package/packages/apps/infinitepay/lib/ip-create-transaction.js.map +1 -1
  16. package/packages/apps/infinitepay/package.json +3 -3
  17. package/packages/apps/infinitepay/src/ip-create-transaction.ts +1 -1
  18. package/packages/apps/jadlog/package.json +2 -2
  19. package/packages/apps/loyalty-points/package.json +1 -1
  20. package/packages/apps/melhor-envio/package.json +3 -3
  21. package/packages/apps/mercadopago/package.json +3 -3
  22. package/packages/apps/pagarme/package.json +3 -3
  23. package/packages/apps/paghiper/package.json +3 -3
  24. package/packages/apps/pix/package.json +3 -3
  25. package/packages/apps/tiny-erp/package.json +3 -3
  26. package/packages/apps/webhooks/CHANGELOG.md +1 -0
  27. package/packages/apps/webhooks/README.md +1 -0
  28. package/packages/apps/webhooks/lib/app-webhooks.js +9 -0
  29. package/packages/apps/webhooks/lib/app-webhooks.js.map +1 -0
  30. package/packages/apps/webhooks/lib/events-to-webhooks-app.js +127 -0
  31. package/packages/apps/webhooks/lib/events-to-webhooks-app.js.map +1 -0
  32. package/packages/apps/webhooks/lib/index.js +2 -0
  33. package/packages/apps/webhooks/lib/index.js.map +1 -0
  34. package/packages/apps/webhooks/package.json +32 -0
  35. package/packages/apps/webhooks/src/app-webhooks.ts +14 -0
  36. package/packages/apps/webhooks/src/events-to-webhooks-app.ts +178 -0
  37. package/packages/apps/webhooks/src/index.ts +1 -0
  38. package/packages/apps/webhooks/tsconfig.json +3 -0
  39. package/packages/cli/lib/setup-gcloud.js +120 -20
  40. package/packages/cli/package.json +1 -1
  41. package/packages/cli/src/cli.ts +1 -1
  42. package/packages/cli/src/setup-gcloud.ts +146 -21
  43. package/packages/config/package.json +1 -1
  44. package/packages/emails/package.json +2 -2
  45. package/packages/emails/tests/assets/order.json +1 -1
  46. package/packages/events/lib/firebase.js +2 -0
  47. package/packages/events/lib/firebase.js.map +1 -1
  48. package/packages/events/package.json +3 -2
  49. package/packages/events/src/firebase.ts +2 -0
  50. package/packages/firebase/lib/config.d.ts +4 -0
  51. package/packages/firebase/lib/config.js +10 -1
  52. package/packages/firebase/lib/config.js.map +1 -1
  53. package/packages/firebase/lib/handlers/check-store-events.js +13 -4
  54. package/packages/firebase/lib/handlers/check-store-events.js.map +1 -1
  55. package/packages/firebase/package.json +2 -2
  56. package/packages/firebase/src/config.ts +10 -7
  57. package/packages/firebase/src/handlers/check-store-events.ts +14 -4
  58. package/packages/i18n/package.json +1 -1
  59. package/packages/modules/lib/firebase/checkout.js +4 -4
  60. package/packages/modules/lib/firebase/checkout.js.map +1 -1
  61. package/packages/modules/package.json +3 -3
  62. package/packages/modules/schemas/@checkout.cjs +0 -5
  63. package/packages/modules/schemas/create_transaction.cjs +6 -1
  64. package/packages/modules/src/firebase/checkout.ts +4 -4
  65. package/packages/passport/package.json +2 -2
  66. package/packages/ssr/package.json +7 -7
  67. package/packages/storefront/dist/client/_astro/PitchBar.004b6ea4.js +1 -0
  68. package/packages/storefront/dist/client/_astro/Prices.f311909a.js +1 -0
  69. package/packages/storefront/dist/client/_astro/Prices.vue_vue_type_script_setup_true_lang.ef47de70.js +1 -0
  70. package/packages/storefront/dist/client/_astro/ProductCard.34de5097.js +1 -0
  71. package/packages/storefront/dist/client/_astro/ShopHeader.cbfee289.js +1 -0
  72. package/packages/storefront/dist/client/_astro/{_...slug_.32968ccf.css → _...slug_.bcc33d9d.css} +1 -1
  73. package/packages/storefront/dist/client/_astro/{client.5a46cc02.js → client.56d86c9b.js} +1 -1
  74. package/packages/storefront/dist/client/_astro/index.0c833781.css +1 -0
  75. package/packages/storefront/dist/client/_astro/index.2d12be6c.js +1 -0
  76. package/packages/storefront/dist/client/_astro/{modules-info.d9373e21.js → modules-info.0debb0b0.js} +1 -1
  77. package/packages/storefront/dist/client/_astro/runtime-core.esm-bundler.a0432a8e.js +1 -0
  78. package/packages/storefront/dist/client/_astro/{runtime-dom.esm-bundler.00313542.js → runtime-dom.esm-bundler.0e5774ce.js} +1 -1
  79. package/packages/storefront/dist/client/fallback/index.html +10 -10
  80. package/packages/storefront/dist/client/sw.js +1 -1
  81. package/packages/storefront/dist/server/chunks/{astro.89bd9221.mjs → astro.0f5b754a.mjs} +871 -826
  82. package/packages/storefront/dist/server/chunks/pages/{all.23de4e5c.mjs → all.671e6bc1.mjs} +367 -466
  83. package/packages/storefront/dist/server/chunks/{prerender.f40361a3.mjs → prerender.fd8cdc24.mjs} +0 -0
  84. package/packages/storefront/dist/server/entry.mjs +20 -12
  85. package/packages/storefront/package.json +11 -11
  86. package/packages/storefront/src/lib/components/Drawer.vue +48 -45
  87. package/packages/storefront/src/lib/components/ProductCard.vue +1 -1
  88. package/packages/storefront/src/lib/components/ShopHeader.vue +42 -13
  89. package/packages/storefront/src/lib/components/ShopSidenav.vue +26 -0
  90. package/packages/storefront/src/lib/composables/use-pitch-bar.ts +27 -0
  91. package/packages/storefront/src/lib/composables/use-sticky-header.ts +111 -0
  92. package/packages/storefront/src/lib/layouts/BaseBody.astro +3 -1
  93. package/packages/storefront/src/lib/layouts/PagesHeader.astro +16 -25
  94. package/packages/types/index.ts +1 -0
  95. package/packages/types/modules/@checkout:params.d.ts +1 -5
  96. package/packages/types/modules/apply_discount:params.d.ts +1 -1
  97. package/packages/types/modules/apply_discount:response.d.ts +1 -1
  98. package/packages/types/modules/calculate_shipping:params.d.ts +1 -1
  99. package/packages/types/modules/calculate_shipping:response.d.ts +1 -1
  100. package/packages/types/modules/create_transaction:params.d.ts +6 -2
  101. package/packages/types/modules/create_transaction:response.d.ts +1 -1
  102. package/packages/types/modules/list_payments:params.d.ts +1 -1
  103. package/packages/types/modules/list_payments:response.d.ts +1 -1
  104. package/packages/types/package.json +1 -1
  105. package/packages/storefront/dist/client/_astro/PitchBar.209c6645.js +0 -1
  106. package/packages/storefront/dist/client/_astro/Prices.6fbcb5ac.js +0 -1
  107. package/packages/storefront/dist/client/_astro/Prices.vue_vue_type_script_setup_true_lang.44f23680.js +0 -1
  108. package/packages/storefront/dist/client/_astro/ProductCard.ee5eee91.js +0 -1
  109. package/packages/storefront/dist/client/_astro/ShopHeader.b801c785.js +0 -1
  110. package/packages/storefront/dist/client/_astro/index.844a4059.js +0 -1
  111. package/packages/storefront/dist/client/_astro/index.90df622b.css +0 -1
  112. package/packages/storefront/dist/client/_astro/runtime-core.esm-bundler.f04cee62.js +0 -1
  113. package/packages/storefront/dist/client/_astro/use-component-variant.58788b6e.js +0 -1
  114. package/packages/storefront/src/lib/components/PitchBar.vue +0 -66
  115. package/packages/storefront/src/lib/components/Prices.vue +0 -176
  116. package/packages/storefront/src/lib/components/StickyHeader.vue +0 -84
  117. 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,2 @@
1
+ export * from './app-webhooks.js';
2
+ // # sourceMappingURL=index.js.map
@@ -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';
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ }
@@ -1,25 +1,112 @@
1
1
  import path from 'node:path';
2
- import { $, fs } from 'zx';
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
- const { stderr } = await $`gcloud iam service-accounts describe ${getAccountEmail(projectId)}`;
12
- hasServiceAccount = !/not_?found/i.test(stderr);
13
- } catch (e) {
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
- if (/no credential/i.test((await $`gcloud auth list`).stderr)) {
20
- await $`gcloud auth login`;
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
- const serviceAccount = await checkServiceAccountExists(projectId);
36
- if (!serviceAccount) {
37
- await $`gcloud iam service-accounts create ${serviceAccountId} \
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
- fs.writeJSONSync(pathPolicyIAM, policyIAM);
77
- return $`gcloud projects set-iam-policy ${projectId} ${pathPolicyIAM}`;
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
- await $`gcloud iam service-accounts keys create ${pathFileKey} \
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;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/cli",
3
3
  "type": "module",
4
- "version": "0.3.0",
4
+ "version": "0.4.1",
5
5
  "description": "E-Com Plus Cloud Commerce CLI tools",
6
6
  "bin": {
7
7
  "cloudcommerce": "./bin/run.mjs"
@@ -124,7 +124,7 @@ ECOM_STORE_ID=${storeId}
124
124
  //
125
125
  }
126
126
  }
127
- let serviceAccountJSON : string | null = null;
127
+ let serviceAccountJSON: string | null = null;
128
128
  if (argv.gcloud !== false) {
129
129
  try {
130
130
  await siginGcloudAndSetIAM(projectId as string, pwd);