@geek-fun/serverlessinsight 0.4.0 → 0.5.0

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 (160) hide show
  1. package/.gitattributes +1 -0
  2. package/README.md +108 -8
  3. package/README.zh-CN.md +52 -8
  4. package/dist/package.json +37 -35
  5. package/dist/src/commands/deploy.js +17 -7
  6. package/dist/src/commands/destroy.js +27 -4
  7. package/dist/src/commands/forceUnlock.js +61 -0
  8. package/dist/src/commands/index.js +86 -14
  9. package/dist/src/commands/local.js +10 -1
  10. package/dist/src/commands/plan.js +33 -0
  11. package/dist/src/commands/template.js +3 -1
  12. package/dist/src/commands/validate.js +2 -1
  13. package/dist/src/common/aliyunClient/apigwOperations.js +652 -0
  14. package/dist/src/common/aliyunClient/dnsOperations.js +90 -0
  15. package/dist/src/common/aliyunClient/ecsOperations.js +141 -0
  16. package/dist/src/common/aliyunClient/esOperations.js +219 -0
  17. package/dist/src/common/aliyunClient/fc3Operations.js +270 -0
  18. package/dist/src/common/aliyunClient/index.js +141 -0
  19. package/dist/src/common/aliyunClient/nasOperations.js +233 -0
  20. package/dist/src/common/aliyunClient/ossOperations.js +237 -0
  21. package/dist/src/common/aliyunClient/ramOperations.js +205 -0
  22. package/dist/src/common/aliyunClient/rdsOperations.js +206 -0
  23. package/dist/src/common/aliyunClient/slsOperations.js +218 -0
  24. package/dist/src/common/aliyunClient/tablestoreOperations.js +199 -0
  25. package/dist/src/common/aliyunClient/types.js +2 -0
  26. package/dist/src/common/constants.js +7 -1
  27. package/dist/src/common/context.js +32 -14
  28. package/dist/src/common/credentials.js +39 -0
  29. package/dist/src/common/dependencyGraph/graph.js +280 -0
  30. package/dist/src/common/dependencyGraph/index.js +18 -0
  31. package/dist/src/common/dependencyGraph/types.js +2 -0
  32. package/dist/src/common/fileUtils.js +16 -0
  33. package/dist/src/common/hashUtils.js +121 -0
  34. package/dist/src/common/iacHelper.js +25 -97
  35. package/dist/src/common/imsClient.js +4 -0
  36. package/dist/src/common/index.js +7 -2
  37. package/dist/src/common/lockManager.js +212 -0
  38. package/dist/src/common/logger.js +89 -6
  39. package/dist/src/common/providerEnum.js +2 -3
  40. package/dist/src/common/runtimeMapper.js +160 -0
  41. package/dist/src/common/scfClient.js +84 -0
  42. package/dist/src/common/stateManager.js +107 -0
  43. package/dist/src/common/tencentClient/cosOperations.js +287 -0
  44. package/dist/src/common/tencentClient/esOperations.js +156 -0
  45. package/dist/src/common/tencentClient/index.js +116 -0
  46. package/dist/src/common/tencentClient/scfOperations.js +141 -0
  47. package/dist/src/common/tencentClient/tdsqlcOperations.js +211 -0
  48. package/dist/src/common/tencentClient/types.js +17 -0
  49. package/dist/src/lang/en.js +254 -0
  50. package/dist/src/lang/index.js +28 -8
  51. package/dist/src/lang/zh-CN.js +229 -0
  52. package/dist/src/parser/bucketParser.js +25 -12
  53. package/dist/src/parser/databaseParser.js +14 -10
  54. package/dist/src/parser/functionParser.js +19 -6
  55. package/dist/src/parser/parseUtils.js +74 -0
  56. package/dist/src/parser/tableParser.js +19 -17
  57. package/dist/src/stack/aliyunStack/apigwExecutor.js +84 -0
  58. package/dist/src/stack/aliyunStack/apigwPlanner.js +118 -0
  59. package/dist/src/stack/aliyunStack/apigwResource.js +339 -0
  60. package/dist/src/stack/aliyunStack/apigwTypes.js +125 -0
  61. package/dist/src/stack/aliyunStack/databaseExecutor.js +112 -0
  62. package/dist/src/stack/aliyunStack/databasePlanner.js +128 -0
  63. package/dist/src/stack/aliyunStack/databaseResource.js +228 -0
  64. package/dist/src/stack/aliyunStack/deployer.js +133 -0
  65. package/dist/src/stack/aliyunStack/destroyer.js +114 -0
  66. package/dist/src/stack/aliyunStack/esServerlessTypes.js +141 -0
  67. package/dist/src/stack/aliyunStack/fc3Executor.js +91 -0
  68. package/dist/src/stack/aliyunStack/fc3Planner.js +77 -0
  69. package/dist/src/stack/aliyunStack/fc3Resource.js +511 -0
  70. package/dist/src/stack/aliyunStack/fc3Types.js +76 -0
  71. package/dist/src/stack/aliyunStack/index.js +40 -0
  72. package/dist/src/stack/aliyunStack/ossExecutor.js +91 -0
  73. package/dist/src/stack/aliyunStack/ossPlanner.js +76 -0
  74. package/dist/src/stack/aliyunStack/ossResource.js +196 -0
  75. package/dist/src/stack/aliyunStack/ossTypes.js +50 -0
  76. package/dist/src/stack/aliyunStack/planner.js +37 -0
  77. package/dist/src/stack/aliyunStack/rdsTypes.js +217 -0
  78. package/dist/src/stack/aliyunStack/tablestoreExecutor.js +92 -0
  79. package/dist/src/stack/aliyunStack/tablestorePlanner.js +94 -0
  80. package/dist/src/stack/aliyunStack/tablestoreResource.js +120 -0
  81. package/dist/src/stack/aliyunStack/tablestoreTypes.js +77 -0
  82. package/dist/src/stack/bucketTypes.js +17 -0
  83. package/dist/src/stack/deploy.js +24 -77
  84. package/dist/src/stack/localStack/bucket.js +11 -6
  85. package/dist/src/stack/localStack/event.js +10 -5
  86. package/dist/src/stack/localStack/function.js +13 -7
  87. package/dist/src/stack/localStack/functionRunner.js +1 -1
  88. package/dist/src/stack/localStack/localServer.js +7 -6
  89. package/dist/src/stack/scfStack/cosExecutor.js +91 -0
  90. package/dist/src/stack/scfStack/cosPlanner.js +76 -0
  91. package/dist/src/stack/scfStack/cosResource.js +126 -0
  92. package/dist/src/stack/scfStack/cosTypes.js +46 -0
  93. package/dist/src/stack/scfStack/deployer.js +91 -0
  94. package/dist/src/stack/scfStack/destroyer.js +88 -0
  95. package/dist/src/stack/scfStack/esServerlessExecutor.js +105 -0
  96. package/dist/src/stack/scfStack/esServerlessPlanner.js +86 -0
  97. package/dist/src/stack/scfStack/esServerlessResource.js +94 -0
  98. package/dist/src/stack/scfStack/esServerlessTypes.js +48 -0
  99. package/dist/src/stack/scfStack/index.js +35 -0
  100. package/dist/src/stack/scfStack/planner.js +91 -0
  101. package/dist/src/stack/scfStack/scfExecutor.js +91 -0
  102. package/dist/src/stack/scfStack/scfPlanner.js +78 -0
  103. package/dist/src/stack/scfStack/scfResource.js +216 -0
  104. package/dist/src/stack/scfStack/scfTypes.js +41 -0
  105. package/dist/src/stack/scfStack/tdsqlcExecutor.js +105 -0
  106. package/dist/src/stack/scfStack/tdsqlcPlanner.js +90 -0
  107. package/dist/src/stack/scfStack/tdsqlcResource.js +146 -0
  108. package/dist/src/stack/scfStack/tdsqlcTypes.js +59 -0
  109. package/dist/src/types/domains/lock.js +2 -0
  110. package/dist/src/types/domains/resolvable.js +2 -0
  111. package/dist/src/types/domains/state.js +19 -0
  112. package/dist/src/types/index.js +4 -0
  113. package/dist/src/validator/bucketSchema.js +4 -10
  114. package/dist/src/validator/databaseSchema.js +36 -36
  115. package/dist/src/validator/eventSchema.js +3 -2
  116. package/dist/src/validator/functionSchema.js +51 -46
  117. package/dist/src/validator/iacSchema.js +52 -3
  118. package/dist/src/validator/rootSchema.js +47 -1
  119. package/dist/src/validator/tableschema.js +9 -8
  120. package/dist/src/validator/templateRefSchema.js +23 -0
  121. package/dist/tsconfig.tsbuildinfo +1 -1
  122. package/package.json +37 -35
  123. package/samples/README_TENCENT_COS.md +486 -0
  124. package/samples/README_TENCENT_SCF.md +272 -0
  125. package/samples/aliyun-poc-api.yml +1 -1
  126. package/samples/aliyun-poc-bucket.yml +0 -1
  127. package/samples/aliyun-poc-domain.yml +0 -1
  128. package/samples/aliyun-poc-es.yml +14 -13
  129. package/samples/aliyun-poc-rds.yml +0 -2
  130. package/samples/aliyun-poc-table.yml +1 -3
  131. package/samples/tencent-poc-cos.yml +20 -0
  132. package/samples/tencent-poc-scf.yml +36 -0
  133. package/dist/src/commands/index.d.ts +0 -2
  134. package/dist/src/common/index.d.ts +0 -11
  135. package/dist/src/common/rosAssets.js +0 -178
  136. package/dist/src/common/rosClient.js +0 -198
  137. package/dist/src/index.d.ts +0 -1
  138. package/dist/src/lang/index.d.ts +0 -3
  139. package/dist/src/parser/index.d.ts +0 -3
  140. package/dist/src/stack/index.d.ts +0 -1
  141. package/dist/src/stack/localStack/index.d.ts +0 -5
  142. package/dist/src/stack/rfsStack/index.d.ts +0 -9
  143. package/dist/src/stack/rosStack/bootstrap.js +0 -187
  144. package/dist/src/stack/rosStack/bucket.js +0 -127
  145. package/dist/src/stack/rosStack/database.js +0 -313
  146. package/dist/src/stack/rosStack/event.js +0 -143
  147. package/dist/src/stack/rosStack/function.js +0 -259
  148. package/dist/src/stack/rosStack/index.d.ts +0 -7
  149. package/dist/src/stack/rosStack/index.js +0 -75
  150. package/dist/src/stack/rosStack/stage.js +0 -46
  151. package/dist/src/stack/rosStack/table.js +0 -95
  152. package/dist/src/stack/rosStack/tag.js +0 -11
  153. package/dist/src/stack/rosStack/vars.js +0 -49
  154. package/dist/src/types/index.d.ts +0 -55
  155. package/dist/src/types/localStack/index.d.ts +0 -81
  156. package/dist/src/validator/index.d.ts +0 -1
  157. package/layers/si-bootstrap-sdk/Dockerfile-aliyuncli +0 -12
  158. package/layers/si-bootstrap-sdk/README.md +0 -1
  159. package/layers/si-bootstrap-sdk/package-lock.json +0 -875
  160. package/layers/si-bootstrap-sdk/package.json +0 -33
@@ -0,0 +1,652 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createApigwOperations = void 0;
37
+ const cloudapi = __importStar(require("@alicloud/cloudapi20160714"));
38
+ const dnsOperations_1 = require("./dnsOperations");
39
+ const logger_1 = require("../logger");
40
+ const lang_1 = require("../../lang");
41
+ const stateManager_1 = require("../stateManager");
42
+ const removeUndefined = (obj) => {
43
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
44
+ };
45
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
46
+ const extractMainDomain = (domainName) => {
47
+ const parts = domainName.split('.');
48
+ if (parts.length <= 2) {
49
+ return domainName;
50
+ }
51
+ return parts.slice(-2).join('.');
52
+ };
53
+ const extractHostRecord = (fullDomain, mainDomain) => {
54
+ if (fullDomain === mainDomain) {
55
+ return '@'; // @ represents the root domain
56
+ }
57
+ const suffix = `.${mainDomain}`;
58
+ if (fullDomain.endsWith(suffix)) {
59
+ return fullDomain.slice(0, -suffix.length);
60
+ }
61
+ return fullDomain;
62
+ };
63
+ const createApigwOperations = (apigwClient, dnsClient, _context) => {
64
+ const dnsOps = (0, dnsOperations_1.createDnsOperations)(dnsClient);
65
+ const extractVerificationToken = (error, groupInfo) => {
66
+ try {
67
+ if (groupInfo?.subDomain) {
68
+ const match = groupInfo.subDomain.match(/^([a-f0-9]+)-[^.]+\.alicloudapi\.com$/);
69
+ if (match) {
70
+ return match[1];
71
+ }
72
+ }
73
+ const errorStr = String(error);
74
+ const cnameMatch = errorStr.match(/([a-f0-9]{32})-[^.]+\.alicloudapi\.com/);
75
+ if (cnameMatch) {
76
+ return cnameMatch[1];
77
+ }
78
+ }
79
+ catch {
80
+ // Ignore extraction errors
81
+ }
82
+ return null;
83
+ };
84
+ const pollDnsPropagation = async (mainDomain, verificationHost, verificationValue, shouldLoop = true) => {
85
+ const checkPropagation = async () => {
86
+ try {
87
+ const currentRecords = await dnsOps.describeDomainRecords(mainDomain, verificationHost);
88
+ return currentRecords.some((record) => record.rr === verificationHost &&
89
+ record.type === 'CNAME' &&
90
+ record.value === verificationValue &&
91
+ record.status === 'ENABLE');
92
+ }
93
+ catch (checkError) {
94
+ logger_1.logger.warn(lang_1.lang.__('APIGW_DNS_CHECK_FAILED', { attempt: '1', error: String(checkError) }));
95
+ return false;
96
+ }
97
+ };
98
+ if (!shouldLoop) {
99
+ logger_1.logger.info('Checking DNS record status...');
100
+ return await checkPropagation();
101
+ }
102
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_PROPAGATION_WAITING'));
103
+ const maxAttempts = 10;
104
+ const delayMs = 60000; // 1 minute
105
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
106
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_PROPAGATION_CHECK', {
107
+ attempt: String(attempt),
108
+ max: String(maxAttempts),
109
+ }));
110
+ await sleep(delayMs);
111
+ const propagated = await checkPropagation();
112
+ if (propagated) {
113
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_VERIFIED', { minutes: String(attempt) }));
114
+ return true;
115
+ }
116
+ }
117
+ logger_1.logger.warn(lang_1.lang.__('APIGW_DNS_PROPAGATION_TIMEOUT'));
118
+ return false;
119
+ };
120
+ const addDomainVerificationRecord = async (domainName, verificationToken, groupSubdomain, region, state, eventLogicalId) => {
121
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_ADDING_RECORD', { domain: domainName }));
122
+ const mainDomain = extractMainDomain(domainName);
123
+ const hostRecord = extractHostRecord(domainName, mainDomain);
124
+ // For CNAME verification: point the domain directly to the group subdomain
125
+ const verificationHost = domainName;
126
+ const verificationValue = groupSubdomain;
127
+ const dnsResourceId = `${eventLogicalId}.dns_verification`;
128
+ try {
129
+ // Check if DNS record is tracked in state
130
+ const existingDnsResource = (0, stateManager_1.getResource)(state, dnsResourceId);
131
+ const now = new Date();
132
+ const thirtyMinutesMs = 30 * 60 * 1000;
133
+ if (existingDnsResource) {
134
+ const createdAt = new Date(existingDnsResource.lastUpdated);
135
+ const ageMs = now.getTime() - createdAt.getTime();
136
+ if (ageMs < thirtyMinutesMs) {
137
+ // Created within 30 minutes - poll for propagation
138
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_RECORD_EXISTS_POLLING', {
139
+ minutes: String(Math.floor(ageMs / 60000)),
140
+ }));
141
+ await pollDnsPropagation(mainDomain, hostRecord, verificationValue, true);
142
+ return state;
143
+ }
144
+ else {
145
+ // Created more than 30 minutes ago - just check once
146
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_RECORD_EXISTS_CHECKING', {
147
+ minutes: String(Math.floor(ageMs / 60000)),
148
+ }));
149
+ const propagated = await pollDnsPropagation(mainDomain, hostRecord, verificationValue, false);
150
+ if (propagated) {
151
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_RECORD_ACTIVE'));
152
+ }
153
+ else {
154
+ logger_1.logger.warn(lang_1.lang.__('APIGW_DNS_RECORD_MAY_NOT_PROPAGATED'));
155
+ }
156
+ return state;
157
+ }
158
+ }
159
+ // Check if record already exists in DNS (but not tracked in state)
160
+ const existingRecords = await dnsOps.describeDomainRecords(mainDomain, hostRecord);
161
+ const recordExists = existingRecords.some((record) => record.rr === hostRecord && record.type === 'CNAME' && record.value === verificationValue);
162
+ if (!recordExists) {
163
+ // Add the verification record
164
+ const recordId = await dnsOps.addDomainRecord({
165
+ domainName: mainDomain,
166
+ rr: hostRecord,
167
+ type: 'CNAME',
168
+ value: verificationValue,
169
+ ttl: 600,
170
+ });
171
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_RECORD_ADDED', {
172
+ record: verificationHost,
173
+ type: 'CNAME',
174
+ value: verificationValue,
175
+ }));
176
+ // Track DNS record in state
177
+ const dnsResourceState = {
178
+ mode: 'managed',
179
+ region,
180
+ definition: {
181
+ domainName,
182
+ mainDomain,
183
+ verificationHost,
184
+ verificationValue,
185
+ verificationToken,
186
+ groupSubdomain,
187
+ },
188
+ instances: [
189
+ {
190
+ arn: `arn:acs:alidns:${region}:dns-record/${recordId}`,
191
+ id: recordId,
192
+ type: 'CNAME',
193
+ status: 'PENDING',
194
+ },
195
+ ],
196
+ lastUpdated: now.toISOString(),
197
+ };
198
+ state = (0, stateManager_1.setResource)(state, dnsResourceId, dnsResourceState);
199
+ }
200
+ else {
201
+ logger_1.logger.info(lang_1.lang.__('APIGW_DNS_RECORD_ALREADY_EXISTS', { domain: domainName }));
202
+ // Track existing record in state
203
+ const dnsResourceState = {
204
+ mode: 'managed',
205
+ region,
206
+ definition: {
207
+ domainName,
208
+ mainDomain,
209
+ verificationHost,
210
+ verificationValue,
211
+ verificationToken,
212
+ groupSubdomain,
213
+ },
214
+ instances: [
215
+ {
216
+ arn: `arn:acs:alidns:${region}:dns-record/existing`,
217
+ id: 'existing',
218
+ type: 'CNAME',
219
+ status: 'EXISTING',
220
+ },
221
+ ],
222
+ lastUpdated: now.toISOString(),
223
+ };
224
+ state = (0, stateManager_1.setResource)(state, dnsResourceId, dnsResourceState);
225
+ }
226
+ // Poll for DNS propagation
227
+ await pollDnsPropagation(mainDomain, hostRecord, verificationValue, true);
228
+ return state;
229
+ }
230
+ catch (error) {
231
+ logger_1.logger.error(lang_1.lang.__('APIGW_DNS_VERIFICATION_FAILED', { domain: domainName, error: String(error) }));
232
+ throw error;
233
+ }
234
+ };
235
+ const logVerificationInstructions = (domainName, verificationToken, groupSubdomain) => {
236
+ const separator = '='.repeat(80);
237
+ logger_1.logger.error(`\n${separator}\n${lang_1.lang.__('APIGW_VERIFICATION_HEADER')}\n${separator}\n${lang_1.lang.__('APIGW_VERIFICATION_DOMAIN', { domain: domainName })}\n\n${lang_1.lang.__('APIGW_VERIFICATION_INSTRUCTIONS')}\n\n${lang_1.lang.__('APIGW_VERIFICATION_RECORD_NAME', { name: domainName })}\n${lang_1.lang.__('APIGW_VERIFICATION_RECORD_TYPE', { type: 'CNAME' })}\n${lang_1.lang.__('APIGW_VERIFICATION_RECORD_VALUE', { value: groupSubdomain })}`);
238
+ logger_1.logger.error(`\n${separator}\n${lang_1.lang.__('APIGW_VERIFICATION_NEXT_STEPS')}\n${lang_1.lang.__('APIGW_VERIFICATION_STEP1')}\n${lang_1.lang.__('APIGW_VERIFICATION_STEP2')}\n${separator}\n`);
239
+ };
240
+ return {
241
+ /**
242
+ * Create an API Gateway group
243
+ */
244
+ createApiGroup: async (config) => {
245
+ const request = new cloudapi.CreateApiGroupRequest({
246
+ groupName: config.groupName,
247
+ description: config.description,
248
+ basePath: config.basePath,
249
+ instanceId: config.instanceId,
250
+ tag: config.tags?.map((t) => new cloudapi.CreateApiGroupRequestTag({ key: t.key, value: t.value })),
251
+ });
252
+ const response = await apigwClient.createApiGroup(request);
253
+ if (!response.body?.groupId) {
254
+ throw new Error('Failed to create API Gateway group: no groupId returned');
255
+ }
256
+ return response.body.groupId;
257
+ },
258
+ /**
259
+ * Get API Gateway group by group ID
260
+ */
261
+ getApiGroup: async (groupId) => {
262
+ try {
263
+ const request = new cloudapi.DescribeApiGroupRequest({
264
+ groupId,
265
+ });
266
+ const response = await apigwClient.describeApiGroup(request);
267
+ if (!response.body) {
268
+ return null;
269
+ }
270
+ return {
271
+ groupId: response.body.groupId,
272
+ groupName: response.body.groupName,
273
+ description: response.body.description,
274
+ basePath: response.body.basePath,
275
+ subDomain: response.body.subDomain,
276
+ instanceId: response.body.instanceId,
277
+ instanceType: response.body.instanceType,
278
+ regionId: response.body.regionId,
279
+ status: response.body.status,
280
+ createdTime: response.body.createdTime,
281
+ modifiedTime: response.body.modifiedTime,
282
+ billingStatus: response.body.billingStatus,
283
+ illegalStatus: response.body.illegalStatus,
284
+ trafficLimit: response.body.trafficLimit,
285
+ };
286
+ }
287
+ catch (error) {
288
+ if (error &&
289
+ typeof error === 'object' &&
290
+ 'code' in error &&
291
+ (error.code === 'NotFoundApiGroup' || error.code === 'InvalidGroupId.NotFound')) {
292
+ return null;
293
+ }
294
+ throw error;
295
+ }
296
+ },
297
+ /**
298
+ * Find API Gateway group by name
299
+ */
300
+ findApiGroupByName: async (groupName) => {
301
+ const request = new cloudapi.DescribeApiGroupsRequest({
302
+ groupName,
303
+ pageSize: 10,
304
+ pageNumber: 1,
305
+ });
306
+ const response = await apigwClient.describeApiGroups(request);
307
+ if (!response.body?.apiGroupAttributes?.apiGroupAttribute?.length) {
308
+ return null;
309
+ }
310
+ // Find exact match
311
+ const group = response.body.apiGroupAttributes.apiGroupAttribute.find((g) => g.groupName === groupName);
312
+ if (!group) {
313
+ return null;
314
+ }
315
+ return {
316
+ groupId: group.groupId,
317
+ groupName: group.groupName,
318
+ description: group.description,
319
+ basePath: group.basePath,
320
+ subDomain: group.subDomain,
321
+ instanceId: group.instanceId,
322
+ instanceType: group.instanceType,
323
+ regionId: group.regionId,
324
+ billingStatus: group.billingStatus,
325
+ illegalStatus: group.illegalStatus,
326
+ trafficLimit: group.trafficLimit,
327
+ };
328
+ },
329
+ /**
330
+ * Update API Gateway group
331
+ */
332
+ updateApiGroup: async (groupId, config) => {
333
+ const request = new cloudapi.ModifyApiGroupRequest({
334
+ groupId,
335
+ groupName: config.groupName,
336
+ description: config.description,
337
+ basePath: config.basePath,
338
+ });
339
+ await apigwClient.modifyApiGroup(request);
340
+ },
341
+ /**
342
+ * Delete API Gateway group
343
+ */
344
+ deleteApiGroup: async (groupId) => {
345
+ const request = new cloudapi.DeleteApiGroupRequest({
346
+ groupId,
347
+ });
348
+ await apigwClient.deleteApiGroup(request);
349
+ },
350
+ /**
351
+ * Create an API in API Gateway
352
+ */
353
+ createApi: async (config) => {
354
+ const requestConfig = JSON.stringify(removeUndefined({
355
+ RequestProtocol: config.requestConfig.requestProtocol,
356
+ RequestHttpMethod: config.requestConfig.requestHttpMethod,
357
+ RequestPath: config.requestConfig.requestPath,
358
+ RequestMode: config.requestConfig.requestMode,
359
+ BodyFormat: config.requestConfig.bodyFormat,
360
+ }));
361
+ const serviceConfigObj = {
362
+ ServiceProtocol: config.serviceConfig.serviceProtocol,
363
+ ServiceAddress: config.serviceConfig.serviceAddress,
364
+ ServiceHttpMethod: config.serviceConfig.serviceHttpMethod,
365
+ ServicePath: config.serviceConfig.servicePath,
366
+ ServiceTimeout: config.serviceConfig.serviceTimeout || 10000,
367
+ MockResult: config.serviceConfig.mockResult,
368
+ };
369
+ if (config.serviceConfig.functionComputeConfig) {
370
+ serviceConfigObj.FunctionComputeConfig = removeUndefined({
371
+ FcRegionId: config.serviceConfig.functionComputeConfig.fcRegionId,
372
+ FunctionName: config.serviceConfig.functionComputeConfig.functionName,
373
+ RoleArn: config.serviceConfig.functionComputeConfig.roleArn,
374
+ FcVersion: config.serviceConfig.functionComputeConfig.fcVersion || '3.0',
375
+ Method: config.serviceConfig.functionComputeConfig.method,
376
+ });
377
+ }
378
+ if (config.serviceConfig.vpcConfig) {
379
+ serviceConfigObj.VpcConfig = {
380
+ VpcId: config.serviceConfig.vpcConfig.vpcId,
381
+ InstanceId: config.serviceConfig.vpcConfig.instanceId,
382
+ Port: config.serviceConfig.vpcConfig.port,
383
+ };
384
+ }
385
+ const serviceConfig = JSON.stringify(removeUndefined(serviceConfigObj));
386
+ const request = new cloudapi.CreateApiRequest({
387
+ groupId: config.groupId,
388
+ apiName: config.apiName,
389
+ description: config.description,
390
+ visibility: config.visibility,
391
+ authType: config.authType,
392
+ requestConfig,
393
+ serviceConfig,
394
+ resultType: config.resultType,
395
+ resultSample: config.resultSample,
396
+ failResultSample: config.failResultSample,
397
+ tag: config.tags?.map((t) => new cloudapi.CreateApiRequestTag({ key: t.key, value: t.value })),
398
+ });
399
+ const response = await apigwClient.createApi(request);
400
+ if (!response.body?.apiId) {
401
+ throw new Error('Failed to create API: no apiId returned');
402
+ }
403
+ return response.body.apiId;
404
+ },
405
+ /**
406
+ * Get API by API ID
407
+ */
408
+ getApi: async (groupId, apiId) => {
409
+ try {
410
+ const request = new cloudapi.DescribeApiRequest({
411
+ groupId,
412
+ apiId,
413
+ });
414
+ const response = await apigwClient.describeApi(request);
415
+ if (!response.body) {
416
+ return null;
417
+ }
418
+ const body = response.body;
419
+ return {
420
+ apiId: body.apiId,
421
+ apiName: body.apiName,
422
+ groupId: body.groupId,
423
+ groupName: body.groupName,
424
+ description: body.description,
425
+ visibility: body.visibility,
426
+ authType: body.authType,
427
+ requestConfig: body.requestConfig
428
+ ? {
429
+ requestProtocol: body.requestConfig.requestProtocol,
430
+ requestHttpMethod: body.requestConfig.requestHttpMethod,
431
+ requestPath: body.requestConfig.requestPath,
432
+ requestMode: body.requestConfig.requestMode,
433
+ bodyFormat: body.requestConfig.bodyFormat,
434
+ }
435
+ : undefined,
436
+ serviceConfig: body.serviceConfig
437
+ ? {
438
+ serviceProtocol: body.serviceConfig.serviceProtocol,
439
+ serviceAddress: body.serviceConfig.serviceAddress,
440
+ serviceHttpMethod: body.serviceConfig.serviceHttpMethod,
441
+ servicePath: body.serviceConfig.servicePath,
442
+ serviceTimeout: body.serviceConfig.serviceTimeout,
443
+ functionComputeConfig: body.serviceConfig.functionComputeConfig
444
+ ? {
445
+ fcRegionId: body.serviceConfig.functionComputeConfig.fcRegionId,
446
+ functionName: body.serviceConfig.functionComputeConfig.functionName,
447
+ roleArn: body.serviceConfig.functionComputeConfig.roleArn,
448
+ fcVersion: body.serviceConfig.functionComputeConfig.fcVersion,
449
+ method: body.serviceConfig.functionComputeConfig.method,
450
+ }
451
+ : undefined,
452
+ mockResult: body.serviceConfig.mockResult,
453
+ }
454
+ : undefined,
455
+ resultType: body.resultType,
456
+ resultSample: body.resultSample,
457
+ createdTime: body.createdTime,
458
+ modifiedTime: body.modifiedTime,
459
+ deployedInfos: body.deployedInfos?.deployedInfo?.map((info) => ({
460
+ stageName: info.stageName,
461
+ deployedStatus: info.deployedStatus,
462
+ effectiveVersion: info.effectiveVersion,
463
+ })),
464
+ };
465
+ }
466
+ catch (error) {
467
+ if (error &&
468
+ typeof error === 'object' &&
469
+ 'code' in error &&
470
+ (error.code === 'NotFoundApi' || error.code === 'InvalidApiId.NotFound')) {
471
+ return null;
472
+ }
473
+ throw error;
474
+ }
475
+ },
476
+ /**
477
+ * Update API
478
+ */
479
+ updateApi: async (apiId, config) => {
480
+ const requestConfig = JSON.stringify(removeUndefined({
481
+ RequestProtocol: config.requestConfig.requestProtocol,
482
+ RequestHttpMethod: config.requestConfig.requestHttpMethod,
483
+ RequestPath: config.requestConfig.requestPath,
484
+ RequestMode: config.requestConfig.requestMode,
485
+ BodyFormat: config.requestConfig.bodyFormat,
486
+ }));
487
+ const serviceConfigObj = {
488
+ ServiceProtocol: config.serviceConfig.serviceProtocol,
489
+ ServiceAddress: config.serviceConfig.serviceAddress,
490
+ ServiceHttpMethod: config.serviceConfig.serviceHttpMethod,
491
+ ServicePath: config.serviceConfig.servicePath,
492
+ ServiceTimeout: config.serviceConfig.serviceTimeout || 10000,
493
+ MockResult: config.serviceConfig.mockResult,
494
+ };
495
+ if (config.serviceConfig.functionComputeConfig) {
496
+ serviceConfigObj.FunctionComputeConfig = removeUndefined({
497
+ FcRegionId: config.serviceConfig.functionComputeConfig.fcRegionId,
498
+ FunctionName: config.serviceConfig.functionComputeConfig.functionName,
499
+ RoleArn: config.serviceConfig.functionComputeConfig.roleArn,
500
+ FcVersion: config.serviceConfig.functionComputeConfig.fcVersion || '3.0',
501
+ Method: config.serviceConfig.functionComputeConfig.method,
502
+ });
503
+ }
504
+ const serviceConfig = JSON.stringify(removeUndefined(serviceConfigObj));
505
+ const request = new cloudapi.ModifyApiRequest({
506
+ groupId: config.groupId,
507
+ apiId,
508
+ apiName: config.apiName,
509
+ description: config.description,
510
+ visibility: config.visibility,
511
+ authType: config.authType,
512
+ requestConfig,
513
+ serviceConfig,
514
+ resultType: config.resultType,
515
+ resultSample: config.resultSample,
516
+ failResultSample: config.failResultSample,
517
+ });
518
+ await apigwClient.modifyApi(request);
519
+ },
520
+ /**
521
+ * Delete API
522
+ */
523
+ deleteApi: async (groupId, apiId) => {
524
+ const request = new cloudapi.DeleteApiRequest({
525
+ groupId,
526
+ apiId,
527
+ });
528
+ await apigwClient.deleteApi(request);
529
+ },
530
+ /**
531
+ * Deploy API to a stage
532
+ */
533
+ deployApi: async (config) => {
534
+ const request = new cloudapi.DeployApiRequest({
535
+ groupId: config.groupId,
536
+ apiId: config.apiId,
537
+ stageName: config.stageName,
538
+ description: config.description,
539
+ });
540
+ await apigwClient.deployApi(request);
541
+ },
542
+ /**
543
+ * Abolish (undeploy) API from a stage
544
+ */
545
+ abolishApi: async (groupId, apiId, stageName) => {
546
+ const request = new cloudapi.AbolishApiRequest({
547
+ groupId,
548
+ apiId,
549
+ stageName,
550
+ });
551
+ await apigwClient.abolishApi(request);
552
+ },
553
+ /**
554
+ * Bind custom domain to API group with automatic verification
555
+ */
556
+ bindCustomDomain: async (config, state, eventLogicalId) => {
557
+ logger_1.logger.info(lang_1.lang.__('APIGW_BINDING_DOMAIN', { domain: config.domainName }));
558
+ // Get the API group info to extract verification token from subDomain
559
+ const groupInfo = await apigwClient.describeApiGroup(new cloudapi.DescribeApiGroupRequest({ groupId: config.groupId }));
560
+ const region = groupInfo.body?.regionId || _context.region;
561
+ // Try to bind the domain first
562
+ try {
563
+ const request = new cloudapi.SetDomainRequest({
564
+ groupId: config.groupId,
565
+ domainName: config.domainName,
566
+ bindStageName: config.bindStageName || 'RELEASE',
567
+ customDomainType: config.customDomainType,
568
+ isHttpRedirectToHttps: config.isHttpRedirectToHttps,
569
+ });
570
+ await apigwClient.setDomain(request);
571
+ // Set certificate if provided
572
+ if (config.certificateName && config.certificateBody && config.certificatePrivateKey) {
573
+ const certRequest = new cloudapi.SetDomainCertificateRequest({
574
+ groupId: config.groupId,
575
+ domainName: config.domainName,
576
+ certificateName: config.certificateName,
577
+ certificateBody: config.certificateBody,
578
+ certificatePrivateKey: config.certificatePrivateKey,
579
+ });
580
+ await apigwClient.setDomainCertificate(certRequest);
581
+ }
582
+ logger_1.logger.info(lang_1.lang.__('APIGW_DOMAIN_BOUND_SUCCESS', { domain: config.domainName }));
583
+ return state;
584
+ }
585
+ catch (error) {
586
+ const err = error;
587
+ // Check if it's a domain ownership verification error
588
+ if (err.code === 'SingleDomainOwnershipCheckFail' || err.message?.includes('ownership')) {
589
+ logger_1.logger.error(lang_1.lang.__('APIGW_DOMAIN_OWNERSHIP_FAILED', { domain: config.domainName }));
590
+ const verificationToken = extractVerificationToken(error, groupInfo.body);
591
+ if (verificationToken && groupInfo.body?.subDomain) {
592
+ logger_1.logger.info(lang_1.lang.__('APIGW_ATTEMPTING_AUTO_VERIFICATION', { token: verificationToken }));
593
+ try {
594
+ // Try to add DNS verification record automatically and update state
595
+ state = await addDomainVerificationRecord(config.domainName, verificationToken, groupInfo.body.subDomain, region, state, eventLogicalId);
596
+ // Retry domain binding after DNS verification
597
+ logger_1.logger.info(lang_1.lang.__('APIGW_RETRYING_DOMAIN_BINDING'));
598
+ const retryRequest = new cloudapi.SetDomainRequest({
599
+ groupId: config.groupId,
600
+ domainName: config.domainName,
601
+ bindStageName: config.bindStageName || 'RELEASE',
602
+ customDomainType: config.customDomainType,
603
+ isHttpRedirectToHttps: config.isHttpRedirectToHttps,
604
+ });
605
+ await apigwClient.setDomain(retryRequest);
606
+ // Set certificate if provided
607
+ if (config.certificateName &&
608
+ config.certificateBody &&
609
+ config.certificatePrivateKey) {
610
+ const certRequest = new cloudapi.SetDomainCertificateRequest({
611
+ groupId: config.groupId,
612
+ domainName: config.domainName,
613
+ certificateName: config.certificateName,
614
+ certificateBody: config.certificateBody,
615
+ certificatePrivateKey: config.certificatePrivateKey,
616
+ });
617
+ await apigwClient.setDomainCertificate(certRequest);
618
+ }
619
+ logger_1.logger.info(lang_1.lang.__('APIGW_DOMAIN_BOUND_AFTER_VERIFICATION', { domain: config.domainName }));
620
+ return state;
621
+ }
622
+ catch (retryError) {
623
+ logger_1.logger.error(lang_1.lang.__('APIGW_AUTO_VERIFICATION_FAILED', { error: String(retryError) }));
624
+ // Fall through to show manual instructions
625
+ }
626
+ }
627
+ // Show manual verification instructions
628
+ if (verificationToken && groupInfo.body?.subDomain) {
629
+ logVerificationInstructions(config.domainName, verificationToken, groupInfo.body.subDomain);
630
+ }
631
+ else {
632
+ logger_1.logger.error(lang_1.lang.__('APIGW_NO_VERIFICATION_TOKEN'));
633
+ logger_1.logger.error(lang_1.lang.__('APIGW_MANUAL_VERIFICATION_REQUIRED'));
634
+ }
635
+ }
636
+ // Re-throw the error to be handled by caller
637
+ throw error;
638
+ }
639
+ },
640
+ /**
641
+ * Unbind custom domain from API group
642
+ */
643
+ unbindCustomDomain: async (groupId, domainName) => {
644
+ const request = new cloudapi.DeleteDomainRequest({
645
+ groupId,
646
+ domainName,
647
+ });
648
+ await apigwClient.deleteDomain(request);
649
+ },
650
+ };
651
+ };
652
+ exports.createApigwOperations = createApigwOperations;