@geek-fun/serverlessinsight 0.0.3 → 0.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geek-fun/serverlessinsight",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Full life cycle cross providers serverless application management for your fast-growing business.",
5
5
  "homepage": "https://serverlessinsight.geekfun.club",
6
6
  "main": "dist/src/index.js",
@@ -49,32 +49,34 @@
49
49
  "function"
50
50
  ],
51
51
  "dependencies": {
52
- "@alicloud/openapi-client": "^0.4.11",
53
- "@alicloud/ros-cdk-apigateway": "^1.2.0",
54
- "@alicloud/ros-cdk-core": "^1.2.0",
55
- "@alicloud/ros-cdk-fc": "^1.2.0",
56
- "@alicloud/ros-cdk-ram": "^1.2.0",
57
- "@alicloud/ros20190910": "^3.4.3",
52
+ "@alicloud/openapi-client": "^0.4.12",
53
+ "@alicloud/ros-cdk-apigateway": "^1.4.0",
54
+ "@alicloud/ros-cdk-core": "^1.4.0",
55
+ "@alicloud/ros-cdk-fc3": "^1.4.0",
56
+ "@alicloud/ros-cdk-ram": "^1.4.0",
57
+ "@alicloud/ros20190910": "^3.5.0",
58
58
  "ajv": "^8.17.1",
59
- "chalk": "^4.1.2",
60
- "commander": "^11.1.0",
59
+ "chalk": "^5.3.0",
60
+ "commander": "^12.1.0",
61
61
  "i18n": "^0.15.1",
62
- "pino": "^8.17.2",
62
+ "pino": "^9.4.0",
63
+ "pino-pretty": "^11.2.2",
63
64
  "yaml": "^2.5.1"
64
65
  },
65
66
  "devDependencies": {
66
- "@types/i18n": "^0.13.10",
67
- "@types/jest": "^29.5.11",
68
- "@types/node": "^20.11.5",
69
- "@typescript-eslint/eslint-plugin": "^6.19.0",
70
- "@typescript-eslint/parser": "^6.19.0",
71
- "eslint": "^8.56.0",
67
+ "@types/i18n": "^0.13.12",
68
+ "@types/jest": "^29.5.13",
69
+ "@types/node": "^22.7.4",
70
+ "@typescript-eslint/eslint-plugin": "^8.8.0",
71
+ "@typescript-eslint/parser": "^8.8.0",
72
+ "eslint": "^8.57.1",
72
73
  "eslint-config-prettier": "^9.1.0",
73
- "eslint-plugin-prettier": "^5.1.3",
74
+ "eslint-plugin-prettier": "^5.2.1",
75
+ "globals": "^15.9.0",
74
76
  "husky": "^9.1.6",
75
77
  "jest": "^29.7.0",
76
- "prettier": "^3.2.4",
77
- "ts-jest": "^29.1.1",
78
+ "prettier": "^3.3.3",
79
+ "ts-jest": "^29.2.5",
78
80
  "ts-node": "^10.9.2",
79
81
  "typescript": "^5.6.2"
80
82
  }
@@ -3,16 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deploy = void 0;
4
4
  const stack_1 = require("../stack");
5
5
  const common_1 = require("../common");
6
- const iac_1 = require("../iac");
7
- const actionContext_1 = require("../common/actionContext");
8
6
  const deploy = async (stackName, options) => {
9
- const context = (0, actionContext_1.constructActionContext)({ location: options.location });
10
- common_1.printer.info(`Deploying stack context: ${JSON.stringify(context)}...`);
11
- common_1.printer.info('Validating yaml...');
12
- const iac = (0, iac_1.parseYaml)(context.iacLocation);
13
- common_1.printer.success('Yaml is valid! 🎉');
14
- common_1.printer.info('Deploying stack...');
7
+ const context = (0, common_1.constructActionContext)({ ...options, stackName });
8
+ common_1.logger.info('Validating yaml...');
9
+ const iac = (0, stack_1.parseYaml)(context.iacLocation);
10
+ common_1.logger.info('Yaml is valid! 🎉');
11
+ common_1.logger.info('Deploying stack...');
15
12
  await (0, stack_1.deployStack)(stackName, iac, context);
16
- common_1.printer.success('Stack deployed! 🎉');
13
+ common_1.logger.info('Stack deployed! 🎉');
17
14
  };
18
15
  exports.deploy = deploy;
@@ -31,8 +31,13 @@ program
31
31
  .command('deploy <stackName>')
32
32
  .description('deploy serverless Iac yaml')
33
33
  .option('-f, --file <path>', 'specify the yaml file')
34
+ .option('-p, --parameter <key=value>', 'override parameters', (value, previous) => {
35
+ const [key, val] = value.split('=');
36
+ previous[key] = val;
37
+ return previous;
38
+ }, {})
34
39
  .action(async (stackName, options) => {
35
40
  common_1.logger.debug('log command info');
36
- await (0, deploy_1.deploy)(stackName, { location: options.file });
41
+ await (0, deploy_1.deploy)(stackName, { location: options.file, parameters: options.parameter });
37
42
  });
38
43
  program.parse();
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validate = void 0;
4
- const iac_1 = require("../iac");
5
4
  const common_1 = require("../common");
6
- const actionContext_1 = require("../common/actionContext");
5
+ const stack_1 = require("../stack");
7
6
  const validate = (location) => {
8
- const context = (0, actionContext_1.constructActionContext)({ location });
9
- (0, iac_1.parseYaml)(context.iacLocation);
10
- common_1.printer.success('Yaml is valid! 🎉');
7
+ const context = (0, common_1.constructActionContext)({ location });
8
+ (0, stack_1.parseYaml)(context.iacLocation);
9
+ common_1.logger.info('Yaml is valid! 🎉');
10
+ common_1.logger.debug('Yaml is valid! debug🎉');
11
11
  };
12
12
  exports.validate = validate;
@@ -7,6 +7,8 @@ exports.constructActionContext = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const constructActionContext = (config) => {
9
9
  return {
10
+ stage: config?.stage ?? 'default',
11
+ stackName: config?.stackName ?? '',
10
12
  region: config?.region ?? process.env.ROS_REGION_ID ?? process.env.ALIYUN_REGION ?? 'cn-hangzhou',
11
13
  accessKeyId: config?.accessKeyId ?? process.env.ALIYUN_ACCESS_KEY_ID,
12
14
  accessKeySecret: config?.accessKeySecret ?? process.env.ALIYUN_ACCESS_KEY_SECRET,
@@ -17,6 +19,7 @@ const constructActionContext = (config) => {
17
19
  ? node_path_1.default.resolve(projectRoot, config?.location)
18
20
  : node_path_1.default.resolve(projectRoot, 'serverless-insight.yml');
19
21
  })(),
22
+ parameters: Object.entries(config?.parameters ?? {}).map(([key, value]) => ({ key, value })),
20
23
  };
21
24
  };
22
25
  exports.constructActionContext = constructActionContext;
@@ -0,0 +1,70 @@
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 (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.replaceVars = exports.resolveCode = void 0;
30
+ const node_path_1 = __importDefault(require("node:path"));
31
+ const node_fs_1 = __importDefault(require("node:fs"));
32
+ const ros = __importStar(require("@alicloud/ros-cdk-core"));
33
+ const resolveCode = (location) => {
34
+ const filePath = node_path_1.default.resolve(process.cwd(), location);
35
+ const fileContent = node_fs_1.default.readFileSync(filePath);
36
+ return fileContent.toString('base64');
37
+ };
38
+ exports.resolveCode = resolveCode;
39
+ const replaceVars = (value, stage) => {
40
+ if (typeof value === 'string') {
41
+ const matchVar = value.match(/^\$\{vars\.(\w+)}$/);
42
+ const containsVar = value.match(/\$\{vars\.(\w+)}/);
43
+ const matchMap = value.match(/^\$\{stages\.(\w+)}$/);
44
+ const containsMap = value.match(/\$\{stages\.(\w+)}/);
45
+ if (matchVar?.length) {
46
+ return ros.Fn.ref(matchVar[1]);
47
+ }
48
+ if (matchMap?.length) {
49
+ return ros.Fn.findInMap('stages', '', matchMap[1]);
50
+ }
51
+ if (containsMap?.length && containsVar?.length) {
52
+ return ros.Fn.sub(value.replace(/\$\{stages\.(\w+)}/g, '${$1}').replace(/\$\{vars\.(\w+)}/g, '${$1}'));
53
+ }
54
+ if (containsVar?.length) {
55
+ return ros.Fn.sub(value.replace(/\$\{vars\.(\w+)}/g, '${$1}'));
56
+ }
57
+ if (containsMap?.length) {
58
+ return ros.Fn.sub(value.replace(/\$\{stages\.(\w+)}/g, '${$1}'));
59
+ }
60
+ return value;
61
+ }
62
+ if (Array.isArray(value)) {
63
+ return value.map((item) => (0, exports.replaceVars)(item, stage));
64
+ }
65
+ if (typeof value === 'object' && value !== null) {
66
+ return Object.fromEntries(Object.entries(value).map(([key, val]) => [key, (0, exports.replaceVars)(val, stage)]));
67
+ }
68
+ return value;
69
+ };
70
+ exports.replaceVars = replaceVars;
@@ -1,6 +1,6 @@
1
- export * from './printer';
2
1
  export * from './provider';
3
2
  export * from './logger';
4
3
  export * from './getVersion';
5
4
  export * from './rosClient';
6
5
  export * from './actionContext';
6
+ export * from './iacHelper';
@@ -14,9 +14,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./printer"), exports);
18
17
  __exportStar(require("./provider"), exports);
19
18
  __exportStar(require("./logger"), exports);
20
19
  __exportStar(require("./getVersion"), exports);
21
20
  __exportStar(require("./rosClient"), exports);
22
21
  __exportStar(require("./actionContext"), exports);
22
+ __exportStar(require("./iacHelper"), exports);
@@ -7,5 +7,9 @@ exports.logger = void 0;
7
7
  const pino_1 = __importDefault(require("pino"));
8
8
  const logger = (0, pino_1.default)({
9
9
  name: 'ServerlessInsight',
10
+ level: ['ServerlessInsight', '*'].includes(process.env.DEBUG || '') ? 'debug' : 'info',
11
+ transport: {
12
+ target: 'pino-pretty',
13
+ },
10
14
  });
11
15
  exports.logger = logger;
@@ -30,7 +30,8 @@ exports.rosStackDeploy = void 0;
30
30
  const tea_util_1 = __importDefault(require("@alicloud/tea-util"));
31
31
  const ros20190910_1 = __importStar(require("@alicloud/ros20190910"));
32
32
  const openapi_client_1 = require("@alicloud/openapi-client");
33
- const printer_1 = require("./printer");
33
+ const logger_1 = require("./logger");
34
+ const lang_1 = require("../lang");
34
35
  const client = new ros20190910_1.default(new openapi_client_1.Config({
35
36
  accessKeyId: process.env.ALIYUN_ACCESS_KEY_ID,
36
37
  accessKeySecret: process.env.ALIYUN_ACCESS_KEY_SECRET,
@@ -44,31 +45,45 @@ const createStack = async (stackName, templateBody, context) => {
44
45
  }));
45
46
  const createStackRequest = new ros20190910_1.CreateStackRequest({
46
47
  regionId: context.region,
48
+ sync: true,
47
49
  stackName,
48
50
  templateBody: JSON.stringify(templateBody),
49
51
  parameters,
50
52
  tags: context.tags?.map((tag) => new ros20190910_1.CreateStackRequestTags(tag)),
51
53
  });
52
- console.log('createStackRequest:', createStackRequest);
53
54
  const response = await client.createStack(createStackRequest);
54
- console.log(`创建中,资源栈ID:${response.body?.stackId}`);
55
- return response.body?.stackId;
55
+ logger_1.logger.info(`创建中,资源栈ID:${response.body?.stackId}`);
56
+ // wait for stack create complete
57
+ return await getStackActionResult(response.body?.stackId || '', context.region);
56
58
  };
57
59
  const updateStack = async (stackId, templateBody, context) => {
58
- const parameters = context.parameters?.map((parameter) => new ros20190910_1.CreateStackRequestParameters({
60
+ const parameters = context.parameters?.map((parameter) => new ros20190910_1.UpdateStackRequestParameters({
59
61
  parameterKey: tea_util_1.default.assertAsString(parameter.key),
60
- parameterValue: tea_util_1.default.assertAsString(parameter.value),
62
+ parameterValue: tea_util_1.default.assertAsString(parameter.key),
61
63
  }));
62
- const createStackRequest = new ros20190910_1.UpdateStackRequest({
64
+ const updateStackRequest = new ros20190910_1.UpdateStackRequest({
63
65
  regionId: context.region,
64
66
  stackId,
65
67
  templateBody: JSON.stringify(templateBody),
66
68
  parameters,
67
69
  tags: context.tags?.map((tag) => new ros20190910_1.CreateStackRequestTags(tag)),
68
70
  });
69
- const response = await client.updateStack(createStackRequest);
70
- console.log(`更新中,资源栈ID:${response.body?.stackId}`);
71
- return response.body?.stackId;
71
+ try {
72
+ const response = await client.updateStack(updateStackRequest);
73
+ logger_1.logger.info(`更新中,资源栈ID:${response.body?.stackId}`);
74
+ // wait for stack update complete
75
+ return await getStackActionResult(response.body?.stackId || '', context.region);
76
+ }
77
+ catch (err) {
78
+ const { Message: message, statusCode } = err?.data || {};
79
+ if (statusCode === 400 && message.includes('Update the completely same stack')) {
80
+ logger_1.logger.warn(`${lang_1.lang.__('UPDATE_COMPLETELY_SAME_STACK')}`);
81
+ return null;
82
+ }
83
+ else {
84
+ throw err;
85
+ }
86
+ }
72
87
  };
73
88
  const getStackByName = async (stackName, region) => {
74
89
  const result = await client.listStacks(new ros20190910_1.ListStacksRequest({
@@ -84,21 +99,67 @@ const getStackByName = async (stackName, region) => {
84
99
  return null;
85
100
  }
86
101
  };
102
+ const getStackActionResult = async (stackId, region) => {
103
+ return new Promise((resolve, reject) => {
104
+ const startTime = Date.now();
105
+ const interval = setInterval(async () => {
106
+ try {
107
+ const result = await client.getStack(new ros20190910_1.GetStackRequest({ regionId: region, stackId }));
108
+ const status = result.body?.status ?? '';
109
+ logger_1.logger.info(`stack status: ${status}`);
110
+ if ([
111
+ 'CREATE_COMPLETE',
112
+ 'UPDATE_COMPLETE',
113
+ 'DELETE_COMPLETE',
114
+ 'CHECK_COMPLETE',
115
+ 'IMPORT_CREATE_COMPLETE',
116
+ 'IMPORT_UPDATE_COMPLETE',
117
+ ].includes(status)) {
118
+ clearInterval(interval);
119
+ resolve(result.body);
120
+ }
121
+ else if ([
122
+ 'CREATE_ROLLBACK_FAILED',
123
+ 'CREATE_ROLLBACK_COMPLETE',
124
+ 'ROLLBACK_FAILED',
125
+ 'ROLLBACK_COMPLETE',
126
+ 'IMPORT_CREATE_ROLLBACK_FAILED',
127
+ 'IMPORT_CREATE_ROLLBACK_COMPLETE',
128
+ 'IMPORT_UPDATE_ROLLBACK_FAILED',
129
+ 'IMPORT_UPDATE_ROLLBACK_COMPLETE',
130
+ ].includes(status)) {
131
+ clearInterval(interval);
132
+ reject(new Error(`Stack operation failed with status: ${status}`));
133
+ }
134
+ else if (Date.now() - startTime > 3600000) {
135
+ // 1 hour in milliseconds
136
+ clearInterval(interval);
137
+ reject(new Error('Stack operation did not finish within 1 hour'));
138
+ }
139
+ }
140
+ catch (error) {
141
+ clearInterval(interval);
142
+ reject(error);
143
+ }
144
+ }, 5000); // 5 seconds interval
145
+ });
146
+ };
87
147
  const rosStackDeploy = async (stackName, templateBody, context) => {
88
148
  const stackInfo = await getStackByName(stackName, context.region);
89
149
  if (stackInfo) {
90
150
  const { Status: stackStatus } = stackInfo;
91
151
  if (stackStatus?.indexOf('IN_PROGRESS') >= 0) {
92
- printer_1.printer.error(`fail to update stack, because stack status is ${stackStatus}`);
93
152
  throw new Error(`fail to update stack, because stack status is ${stackStatus}`);
94
153
  }
95
- printer_1.printer.info(`Update stack: ${stackName} deploying... `);
96
- return await updateStack(stackInfo.stackId, templateBody, context);
154
+ logger_1.logger.info(`Update stack: ${stackName} deploying... `);
155
+ const stack = await updateStack(stackInfo.stackId, templateBody, context);
156
+ logger_1.logger.info(`stackUpdate success! stackName:${stack?.stackName}, stackId:${stack?.stackId}`);
97
157
  }
98
158
  else {
99
159
  // create stack
100
- printer_1.printer.info(`Create stack: ${stackName} deploying... `);
101
- return await createStack(stackName, templateBody, context);
160
+ logger_1.logger.info(`Create stack: ${stackName} deploying... `);
161
+ const stack = await createStack(stackName, templateBody, context);
162
+ logger_1.logger.info(`createStack success! stackName:${stack?.stackName}, stackId:${stack?.stackId}`);
102
163
  }
103
164
  };
104
165
  exports.rosStackDeploy = rosStackDeploy;
@@ -4,6 +4,12 @@ exports.lang = void 0;
4
4
  const i18n_1 = require("i18n");
5
5
  const lang = new i18n_1.I18n({
6
6
  locales: ['en', 'cn'],
7
- register: [{ hello: 'hello' }, { hello: '你好' }],
7
+ register: [
8
+ {
9
+ hello: 'hello',
10
+ UPDATE_COMPLETELY_SAME_STACK: 'The stack is completely the same, update SKIPPED',
11
+ },
12
+ { hello: '你好', UPDATE_COMPLETELY_SAME_STACK: '栈完全相同,更新跳过' },
13
+ ],
8
14
  });
9
15
  exports.lang = lang;
@@ -22,133 +22,21 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.deployStack = exports.IacStack = void 0;
26
+ exports.deployStack = void 0;
30
27
  const ros = __importStar(require("@alicloud/ros-cdk-core"));
31
- const fc = __importStar(require("@alicloud/ros-cdk-fc"));
32
- const agw = __importStar(require("@alicloud/ros-cdk-apigateway"));
33
- const ram = __importStar(require("@alicloud/ros-cdk-ram"));
34
- const types_1 = require("../types");
35
28
  const common_1 = require("../common");
36
- const node_path_1 = __importDefault(require("node:path"));
37
- const fs = __importStar(require("node:fs"));
38
- const resolveCode = (location) => {
39
- const filePath = node_path_1.default.resolve(process.cwd(), location);
40
- const fileContent = fs.readFileSync(filePath);
41
- return fileContent.toString('base64');
42
- };
43
- class IacStack extends ros.Stack {
44
- constructor(scope, iac, context) {
45
- super(scope, iac.service);
46
- new ros.RosInfo(this, ros.RosInfo.description, `${iac.service} stack`);
47
- const service = new fc.RosService(this, `${iac.service}-service`, {
48
- serviceName: `${iac.service}-service`,
49
- }, true);
50
- iac.functions.forEach((fnc) => {
51
- const func = new fc.RosFunction(this, fnc.key, {
52
- functionName: fnc.name,
53
- serviceName: service.serviceName,
54
- handler: fnc.handler,
55
- runtime: fnc.runtime,
56
- memorySize: fnc.memory,
57
- timeout: fnc.timeout,
58
- environmentVariables: fnc.environment,
59
- code: {
60
- zipFile: resolveCode(fnc.code),
61
- },
62
- }, true);
63
- func.addDependsOn(service);
64
- });
65
- const apiGateway = iac.events.find((event) => event.type === types_1.EventTypes.API_GATEWAY);
66
- if (apiGateway) {
67
- const gatewayAccessRole = new ram.RosRole(this, `${iac.service}_role`, {
68
- roleName: `${iac.service}-gateway-access-role`,
69
- description: `${iac.service} role`,
70
- assumeRolePolicyDocument: {
71
- version: '1',
72
- statement: [
73
- {
74
- action: 'sts:AssumeRole',
75
- effect: 'Allow',
76
- principal: {
77
- service: ['apigateway.aliyuncs.com'],
78
- },
79
- },
80
- ],
81
- },
82
- policies: [
83
- {
84
- policyName: `${iac.service}-policy`,
85
- policyDocument: {
86
- version: '1',
87
- statement: [
88
- {
89
- action: ['fc:InvokeFunction'],
90
- effect: 'Allow',
91
- // @TODO implement at least permission granting
92
- resource: ['*'],
93
- },
94
- ],
95
- },
96
- },
97
- ],
98
- }, true);
99
- const apiGatewayGroup = new agw.RosGroup(this, `${iac.service}_apigroup`, {
100
- groupName: `${iac.service}_apigroup`,
101
- }, true);
102
- iac.events
103
- .filter((event) => event.type === types_1.EventTypes.API_GATEWAY)
104
- .forEach((event) => {
105
- event.triggers.forEach((trigger) => {
106
- const key = `${trigger.method}_${trigger.path}`.toLowerCase().replace(/\//g, '_');
107
- const api = new agw.RosApi(this, `${event.key}_api_${key}`, {
108
- apiName: `${event.name}_api_${key}`,
109
- groupId: apiGatewayGroup.attrGroupId,
110
- visibility: 'PRIVATE',
111
- requestConfig: {
112
- requestProtocol: 'HTTP',
113
- requestHttpMethod: trigger.method,
114
- requestPath: trigger.path,
115
- requestMode: 'PASSTHROUGH',
116
- },
117
- serviceConfig: {
118
- serviceProtocol: 'FunctionCompute',
119
- functionComputeConfig: {
120
- fcRegionId: context.region,
121
- serviceName: service.serviceName,
122
- functionName: trigger.backend,
123
- roleArn: gatewayAccessRole.attrArn,
124
- },
125
- },
126
- resultSample: 'ServerlessInsight resultSample',
127
- resultType: 'JSON',
128
- }, true);
129
- api.addDependsOn(apiGatewayGroup);
130
- });
131
- });
132
- }
133
- }
134
- }
135
- exports.IacStack = IacStack;
29
+ const iacStack_1 = require("./iacStack");
136
30
  const generateStackTemplate = (stackName, iac, context) => {
137
31
  const app = new ros.App();
138
- new IacStack(app, iac, context);
32
+ new iacStack_1.IacStack(app, iac, context);
139
33
  const assembly = app.synth();
140
34
  const stackArtifact = assembly.getStackByName(stackName);
141
- const parameters = Object.entries(stackArtifact.parameters).map(([key, value]) => ({
142
- key,
143
- value,
144
- }));
145
- return { template: stackArtifact.template, parameters };
35
+ return { template: stackArtifact.template };
146
36
  };
147
37
  const deployStack = async (stackName, iac, context) => {
148
- common_1.printer.info(`Deploying stack... ${JSON.stringify(iac)}`);
149
- const { template, parameters } = generateStackTemplate(stackName, iac, context);
150
- console.log('Generated ROS YAML:', JSON.stringify({ template, parameters }));
151
- await (0, common_1.rosStackDeploy)(stackName, template, { ...context, parameters });
152
- common_1.printer.info(`Stack deployed! 🎉`);
38
+ const { template } = generateStackTemplate(stackName, iac, context);
39
+ await (0, common_1.rosStackDeploy)(stackName, template, context);
40
+ common_1.logger.info(`Stack deployed! 🎉`);
153
41
  };
154
42
  exports.deployStack = deployStack;
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.validateYaml = void 0;
7
7
  const ajv_1 = __importDefault(require("ajv"));
8
+ const common_1 = require("../common");
8
9
  const ajv = new ajv_1.default({ allowUnionTypes: true, strict: false, allErrors: true });
9
10
  const schema = {
10
11
  type: 'object',
@@ -93,6 +94,21 @@ const schema = {
93
94
  required: ['method', 'path', 'backend'],
94
95
  },
95
96
  },
97
+ custom_domain: {
98
+ type: 'object',
99
+ properties: {
100
+ domain_name: { type: 'string' },
101
+ certificate_name: { type: 'string' },
102
+ certificate_body: { type: 'string' },
103
+ certificate_private_key: { type: 'string' },
104
+ },
105
+ required: [
106
+ 'domain_name',
107
+ 'certificate_name',
108
+ 'certificate_body',
109
+ 'certificate_private_key',
110
+ ],
111
+ },
96
112
  },
97
113
  required: ['name', 'type', 'triggers'],
98
114
  },
@@ -120,8 +136,10 @@ class IacSchemaErrors extends Error {
120
136
  const validateYaml = (iacJson) => {
121
137
  const validate = ajv.compile(schema);
122
138
  const valid = validate(iacJson);
123
- if (!valid)
139
+ if (!valid) {
140
+ common_1.logger.debug(`Invalid yaml: ${JSON.stringify(validate.errors)}`);
124
141
  throw new IacSchemaErrors(validate.errors);
142
+ }
125
143
  return true;
126
144
  };
127
145
  exports.validateYaml = validateYaml;
@@ -0,0 +1,146 @@
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 (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.IacStack = void 0;
27
+ const ros = __importStar(require("@alicloud/ros-cdk-core"));
28
+ const ros_cdk_core_1 = require("@alicloud/ros-cdk-core");
29
+ const types_1 = require("../types");
30
+ const fc = __importStar(require("@alicloud/ros-cdk-fc3"));
31
+ const ram = __importStar(require("@alicloud/ros-cdk-ram"));
32
+ const agw = __importStar(require("@alicloud/ros-cdk-apigateway"));
33
+ const common_1 = require("../common");
34
+ class IacStack extends ros.Stack {
35
+ constructor(scope, iac, context) {
36
+ super(scope, iac.service, {
37
+ stackName: context.stackName,
38
+ tags: iac.tags.reduce((acc, tag) => {
39
+ acc[tag.key] = (0, common_1.replaceVars)(tag.value, context.stage);
40
+ return acc;
41
+ }, {}),
42
+ });
43
+ // Define Parameters
44
+ Object.entries(iac.vars).map(([key, value]) => new ros.RosParameter(this, key, {
45
+ type: ros_cdk_core_1.RosParameterType.STRING,
46
+ defaultValue: value,
47
+ }));
48
+ // Define Mappings
49
+ new ros.RosMapping(this, 'stages', { mapping: (0, common_1.replaceVars)(iac.stages, context.stage) });
50
+ new ros.RosInfo(this, ros.RosInfo.description, (0, common_1.replaceVars)(`${iac.service} stack`, context.stage));
51
+ iac.functions.forEach((fnc) => {
52
+ new fc.RosFunction(this, fnc.key, {
53
+ functionName: (0, common_1.replaceVars)(fnc.name, context.stage),
54
+ handler: (0, common_1.replaceVars)(fnc.handler, context.stage),
55
+ runtime: (0, common_1.replaceVars)(fnc.runtime, context.stage),
56
+ memorySize: (0, common_1.replaceVars)(fnc.memory, context.stage),
57
+ timeout: (0, common_1.replaceVars)(fnc.timeout, context.stage),
58
+ environmentVariables: (0, common_1.replaceVars)(fnc.environment, context.stage),
59
+ code: {
60
+ zipFile: (0, common_1.resolveCode)(fnc.code),
61
+ },
62
+ }, true);
63
+ });
64
+ const apiGateway = iac.events?.filter((event) => event.type === types_1.EventTypes.API_GATEWAY);
65
+ if (apiGateway?.length) {
66
+ const gatewayAccessRole = new ram.RosRole(this, (0, common_1.replaceVars)(`${iac.service}_role`, context.stage), {
67
+ roleName: (0, common_1.replaceVars)(`${iac.service}-gateway-access-role`, context.stage),
68
+ description: (0, common_1.replaceVars)(`${iac.service} role`, context.stage),
69
+ assumeRolePolicyDocument: {
70
+ version: '1',
71
+ statement: [
72
+ {
73
+ action: 'sts:AssumeRole',
74
+ effect: 'Allow',
75
+ principal: {
76
+ service: ['apigateway.aliyuncs.com'],
77
+ },
78
+ },
79
+ ],
80
+ },
81
+ policies: [
82
+ {
83
+ policyName: (0, common_1.replaceVars)(`${iac.service}-policy`, context.stage),
84
+ policyDocument: {
85
+ version: '1',
86
+ statement: [
87
+ {
88
+ action: ['fc:InvokeFunction'],
89
+ effect: 'Allow',
90
+ // @TODO implement at least permission granting
91
+ resource: ['*'],
92
+ },
93
+ ],
94
+ },
95
+ },
96
+ ],
97
+ }, true);
98
+ const apiGatewayGroup = new agw.RosGroup(this, (0, common_1.replaceVars)(`${iac.service}_apigroup`, context.stage), {
99
+ groupName: (0, common_1.replaceVars)(`${iac.service}_apigroup`, context.stage),
100
+ tags: (0, common_1.replaceVars)(iac.tags, context.stage),
101
+ }, true);
102
+ // new agw.RosCustomDomain(
103
+ // this,
104
+ // 'customDomain',
105
+ // {
106
+ // domainName: 'example.com',
107
+ // certificateName: 'example.com',
108
+ // certificateBody: 'example.com',
109
+ // certificatePrivateKey: 'example.com',
110
+ // groupId: apiGatewayGroup.attrGroupId,
111
+ // },
112
+ // true,
113
+ // );
114
+ apiGateway.forEach((event) => {
115
+ event.triggers.forEach((trigger) => {
116
+ const key = `${trigger.method}_${trigger.path}`.toLowerCase().replace(/\//g, '_');
117
+ const api = new agw.RosApi(this, (0, common_1.replaceVars)(`${event.key}_api_${key}`, context.stage), {
118
+ apiName: (0, common_1.replaceVars)(`${event.name}_api_${key}`, context.stage),
119
+ groupId: apiGatewayGroup.attrGroupId,
120
+ visibility: 'PRIVATE',
121
+ requestConfig: {
122
+ requestProtocol: 'HTTP',
123
+ requestHttpMethod: (0, common_1.replaceVars)(trigger.method, context.stage),
124
+ requestPath: (0, common_1.replaceVars)(trigger.path, context.stage),
125
+ requestMode: 'PASSTHROUGH',
126
+ },
127
+ serviceConfig: {
128
+ serviceProtocol: 'FunctionCompute',
129
+ functionComputeConfig: {
130
+ fcRegionId: context.region,
131
+ functionName: trigger.backend,
132
+ roleArn: gatewayAccessRole.attrArn,
133
+ fcVersion: '3.0',
134
+ },
135
+ },
136
+ resultSample: 'ServerlessInsight resultSample',
137
+ resultType: 'JSON',
138
+ tags: (0, common_1.replaceVars)(iac.tags, context.stage),
139
+ }, true);
140
+ api.addDependsOn(apiGatewayGroup);
141
+ });
142
+ });
143
+ }
144
+ }
145
+ }
146
+ exports.IacStack = IacStack;
@@ -1 +1,3 @@
1
1
  export { deployStack } from './deploy';
2
+ export * from './parse';
3
+ export * from './iacSchema';
@@ -1,5 +1,21 @@
1
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.deployStack = void 0;
4
18
  var deploy_1 = require("./deploy");
5
19
  Object.defineProperty(exports, "deployStack", { enumerable: true, get: function () { return deploy_1.deployStack; } });
20
+ __exportStar(require("./parse"), exports);
21
+ __exportStar(require("./iacSchema"), exports);
@@ -5,7 +5,16 @@ const yaml_1 = require("yaml");
5
5
  const node_fs_1 = require("node:fs");
6
6
  const iacSchema_1 = require("./iacSchema");
7
7
  const mapToArr = (obj) => {
8
- return Object.entries(obj).map(([key, value]) => typeof value === 'string' ? { [key]: value } : { key, ...value });
8
+ if (!obj) {
9
+ return [];
10
+ }
11
+ return Object.entries(obj)?.map(([key, value]) => typeof value === 'string' ? { [key]: value } : { key, ...value });
12
+ };
13
+ const mapToKvArr = (obj) => {
14
+ if (!obj) {
15
+ return [];
16
+ }
17
+ return Object.entries(obj)?.map(([key, value]) => ({ key, value }));
9
18
  };
10
19
  const validateExistence = (path) => {
11
20
  if (!(0, node_fs_1.existsSync)(path)) {
@@ -21,7 +30,10 @@ const transformYaml = (iacJson) => {
21
30
  stages: iacJson.stages,
22
31
  functions: mapToArr(iacJson.functions),
23
32
  events: mapToArr(iacJson.events),
24
- tags: mapToArr(iacJson.tags),
33
+ tags: [
34
+ { key: 'iac-provider', value: 'ServerlessInsight' },
35
+ ...mapToKvArr(iacJson.tags),
36
+ ],
25
37
  };
26
38
  };
27
39
  const parseYaml = (yamlPath) => {
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/types.ts","../src/commands/deploy.ts","../src/commands/index.ts","../src/commands/validate.ts","../src/common/actionContext.ts","../src/common/getVersion.ts","../src/common/index.ts","../src/common/logger.ts","../src/common/printer.ts","../src/common/provider.ts","../src/common/rosClient.ts","../src/iac/iacSchema.ts","../src/iac/index.ts","../src/iac/parse.ts","../src/lang/index.ts","../src/stack/deploy.ts","../src/stack/index.ts"],"version":"5.6.2"}
1
+ {"root":["../src/index.ts","../src/types.ts","../src/commands/deploy.ts","../src/commands/index.ts","../src/commands/validate.ts","../src/common/actionContext.ts","../src/common/getVersion.ts","../src/common/iacHelper.ts","../src/common/index.ts","../src/common/logger.ts","../src/common/provider.ts","../src/common/rosClient.ts","../src/lang/index.ts","../src/stack/deploy.ts","../src/stack/iacSchema.ts","../src/stack/iacStack.ts","../src/stack/index.ts","../src/stack/parse.ts"],"version":"5.6.2"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geek-fun/serverlessinsight",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Full life cycle cross providers serverless application management for your fast-growing business.",
5
5
  "homepage": "https://serverlessinsight.geekfun.club",
6
6
  "main": "dist/src/index.js",
@@ -49,32 +49,34 @@
49
49
  "function"
50
50
  ],
51
51
  "dependencies": {
52
- "@alicloud/openapi-client": "^0.4.11",
53
- "@alicloud/ros-cdk-apigateway": "^1.2.0",
54
- "@alicloud/ros-cdk-core": "^1.2.0",
55
- "@alicloud/ros-cdk-fc": "^1.2.0",
56
- "@alicloud/ros-cdk-ram": "^1.2.0",
57
- "@alicloud/ros20190910": "^3.4.3",
52
+ "@alicloud/openapi-client": "^0.4.12",
53
+ "@alicloud/ros-cdk-apigateway": "^1.4.0",
54
+ "@alicloud/ros-cdk-core": "^1.4.0",
55
+ "@alicloud/ros-cdk-fc3": "^1.4.0",
56
+ "@alicloud/ros-cdk-ram": "^1.4.0",
57
+ "@alicloud/ros20190910": "^3.5.0",
58
58
  "ajv": "^8.17.1",
59
- "chalk": "^4.1.2",
60
- "commander": "^11.1.0",
59
+ "chalk": "^5.3.0",
60
+ "commander": "^12.1.0",
61
61
  "i18n": "^0.15.1",
62
- "pino": "^8.17.2",
62
+ "pino": "^9.4.0",
63
+ "pino-pretty": "^11.2.2",
63
64
  "yaml": "^2.5.1"
64
65
  },
65
66
  "devDependencies": {
66
- "@types/i18n": "^0.13.10",
67
- "@types/jest": "^29.5.11",
68
- "@types/node": "^20.11.5",
69
- "@typescript-eslint/eslint-plugin": "^6.19.0",
70
- "@typescript-eslint/parser": "^6.19.0",
71
- "eslint": "^8.56.0",
67
+ "@types/i18n": "^0.13.12",
68
+ "@types/jest": "^29.5.13",
69
+ "@types/node": "^22.7.4",
70
+ "@typescript-eslint/eslint-plugin": "^8.8.0",
71
+ "@typescript-eslint/parser": "^8.8.0",
72
+ "eslint": "^8.57.1",
72
73
  "eslint-config-prettier": "^9.1.0",
73
- "eslint-plugin-prettier": "^5.1.3",
74
+ "eslint-plugin-prettier": "^5.2.1",
75
+ "globals": "^15.9.0",
74
76
  "husky": "^9.1.6",
75
77
  "jest": "^29.7.0",
76
- "prettier": "^3.2.4",
77
- "ts-jest": "^29.1.1",
78
+ "prettier": "^3.3.3",
79
+ "ts-jest": "^29.2.5",
78
80
  "ts-node": "^10.9.2",
79
81
  "typescript": "^5.6.2"
80
82
  }
@@ -1,12 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.printer = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
8
- exports.printer = {
9
- success: (message) => console.log(`${chalk_1.default.blue('ServerlessInsight')}: ${chalk_1.default.green(message)}`),
10
- info: (message) => console.log(`${chalk_1.default.blue('ServerlessInsight')}: ${message}`),
11
- error: (message) => console.log(`${chalk_1.default.bgRed(chalk_1.default.black('ServerlessInsight'))}: ${chalk_1.default.red(message)}`),
12
- };
@@ -1,2 +0,0 @@
1
- export * from './parse';
2
- export * from './iacSchema';
@@ -1,18 +0,0 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./parse"), exports);
18
- __exportStar(require("./iacSchema"), exports);