Skip to content

feat: serverless.yml支持函数url开启cors配置 #304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ prettier.config.js
release.config.js
commitlint.config.js
.editorconfig
src
*.ts
tsconfig.json
babel.config.js
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@semantic-release/npm": "^7.0.4",
"@semantic-release/release-notes-generator": "^9.0.1",
"@types/axios": "^0.14.0",
"@types/lodash": "^4.17.17",
"@types/react-grid-layout": "^1.1.2",
"@types/uuid": "^8.3.1",
"@typescript-eslint/eslint-plugin": "^4.14.0",
Expand Down Expand Up @@ -94,6 +95,7 @@
"camelcase": "^6.2.0",
"cos-nodejs-sdk-v5": "^2.9.20",
"dayjs": "^1.10.4",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"tencent-cloud-sdk": "^1.0.5",
"type-fest": "^0.20.2",
Expand Down
1 change: 1 addition & 0 deletions src/modules/scf/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const ACTIONS = [
'GetFunctionEventInvokeConfig',
'UpdateFunctionEventInvokeConfig',
'CreateTrigger',
'UpdateTrigger',
'DeleteTrigger',
'PublishVersion',
'ListVersionByFunction',
Expand Down
2 changes: 1 addition & 1 deletion src/modules/triggers/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@ export const TRIGGER_STATUS_MAP = {
0: 'CLOSE',
};

export const CAN_UPDATE_TRIGGER = ['apigw', 'cls', 'mps', 'clb'];
export const CAN_UPDATE_TRIGGER = ['apigw', 'cls', 'mps', 'clb','http','ckafka'];
42 changes: 32 additions & 10 deletions src/modules/triggers/ckafka.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { CapiCredentials, RegionType } from './../interface';
import { TriggerInputs, CkafkaTriggerInputsParams, CreateTriggerReq } from './interface';
import { TriggerInputs, CkafkaTriggerInputsParams, CreateTriggerReq,TriggerAction } from './interface';
import Scf from '../scf';
import { TRIGGER_STATUS_MAP } from './base';
import { TRIGGER_STATUS_MAP } from './base';
import { TriggerManager } from './manager';
import { getScfTriggerByName } from './utils';

export default class CkafkaTrigger {
credentials: CapiCredentials;
Expand All @@ -29,20 +30,22 @@ export default class CkafkaTrigger {
return `${triggerInputs.Type}-${triggerInputs.TriggerName}-${desc}-${Enable}-${triggerInputs.Qualifier}`;
}

formatInputs({ inputs }: { inputs: TriggerInputs<CkafkaTriggerInputsParams> }) {
formatInputs({ inputs,action = 'CreateTrigger'}: { inputs: TriggerInputs<CkafkaTriggerInputsParams>,action?: TriggerAction }) {
const { parameters } = inputs;
const triggerName = parameters?.name || `${parameters?.instanceId}-${parameters?.topic}`;
const triggerInputs: CreateTriggerReq = {
Action: 'CreateTrigger',
Action: action,
FunctionName: inputs.functionName,
Namespace: inputs.namespace,
Type: 'ckafka',
Qualifier: parameters?.qualifier ?? '$DEFAULT',
TriggerName: `${parameters?.name}-${parameters?.topic}`,
TriggerName: triggerName,
TriggerDesc: JSON.stringify({
maxMsgNum: parameters?.maxMsgNum ?? 100,
offset: parameters?.offset ?? 'latest',
retry: parameters?.retry ?? 10000,
timeOut: parameters?.timeout ?? 60,
consumerGroupName: parameters?.consumerGroupName ?? '',
}),
Enable: parameters?.enable ? 'OPEN' : 'CLOSE',
};
Expand All @@ -57,16 +60,35 @@ export default class CkafkaTrigger {
async create({
scf,
inputs,
region
}: {
scf: Scf | TriggerManager;
region: RegionType;
inputs: TriggerInputs<CkafkaTriggerInputsParams>;
}) {
const { triggerInputs } = this.formatInputs({ inputs });
console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`);
const { TriggerInfo } = await scf.request(triggerInputs as any);
TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier;
return TriggerInfo;
// 查询当前触发器是否已存在
const existTrigger = await getScfTriggerByName({ scf, region, inputs });
// 更新触发器
if (existTrigger) {
const { triggerInputs } = this.formatInputs({ inputs, action: 'UpdateTrigger' });
console.log(`${triggerInputs.Type} trigger ${triggerInputs.TriggerName} is exist`)
console.log(`Updating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`);
try {
// 更新触发器
await scf.request(triggerInputs as any);
// 更新成功后,查询最新的触发器信息
const trigger = await getScfTriggerByName({ scf, region, inputs });
return trigger;
} catch (error) {
return {}
}
} else { // 创建触发器
const { triggerInputs } = this.formatInputs({ inputs });
console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`);
const { TriggerInfo } = await scf.request(triggerInputs as any);
TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier;
return TriggerInfo;
}
}
async delete({ scf, inputs }: { scf: Scf; inputs: TriggerInputs }) {
console.log(`Removing ${inputs.type} trigger ${inputs.triggerName}`);
Expand Down
57 changes: 39 additions & 18 deletions src/modules/triggers/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import Scf from '../scf';
import { TriggerManager } from './manager';
import { CapiCredentials, RegionType } from './../interface';
import BaseTrigger from './base';
import { HttpTriggerInputsParams, TriggerInputs, CreateTriggerReq } from './interface';
import { HttpTriggerInputsParams, TriggerInputs, CreateTriggerReq,TriggerAction } from './interface';
import { caseForObject } from '../../utils';
import { getScfTriggerByName } from './utils';

export default class HttpTrigger extends BaseTrigger<HttpTriggerInputsParams> {
credentials: CapiCredentials;
Expand All @@ -15,31 +17,33 @@ export default class HttpTrigger extends BaseTrigger<HttpTriggerInputsParams> {
}

getKey(triggerInputs: CreateTriggerReq) {
const triggerDesc = JSON.parse(triggerInputs.TriggerDesc!);
const tempDest = JSON.stringify({
authType: triggerDesc?.AuthType,
enableIntranet: triggerDesc?.NetConfig?.EnableIntranet,
enableExtranet: triggerDesc?.NetConfig?.EnableExtranet,
});
return `http-${tempDest}-${triggerInputs.Qualifier}`;
return `http-${triggerInputs?.TriggerName}`;
}

formatInputs({ inputs }: { region: RegionType; inputs: TriggerInputs<HttpTriggerInputsParams> }) {
formatInputs({ inputs,action = 'CreateTrigger' }: { region: RegionType; inputs: TriggerInputs<HttpTriggerInputsParams> ,action?: TriggerAction}) {
const { parameters } = inputs;
const triggerName = parameters?.name || 'url-trigger';
const { origins,headers,methods,exposeHeaders } = parameters?.corsConfig || {}
const triggerInputs: CreateTriggerReq = {
Action: 'CreateTrigger',
Action: action,
FunctionName: inputs.functionName,
Namespace: inputs.namespace,

Type: 'http',
Qualifier: parameters?.qualifier || '$DEFAULT',
TriggerName: parameters?.name || 'url-trigger',
TriggerName: triggerName,
TriggerDesc: JSON.stringify({
AuthType: parameters?.authType || 'NONE',
NetConfig: {
EnableIntranet: parameters?.netConfig?.enableIntranet ?? false,
EnableExtranet: parameters?.netConfig?.enableExtranet ?? false,
},
CorsConfig: parameters?.corsConfig ? caseForObject({
...parameters?.corsConfig,
origins: typeof origins === 'string' ? origins?.split(',') : origins,
methods: typeof methods === 'string' ? methods?.split(',') : methods,
headers: typeof headers === 'string' ? headers?.split(',') : headers,
exposeHeaders: typeof exposeHeaders === 'string' ? exposeHeaders?.split(',') : exposeHeaders,
},'upper') : undefined
}),
Enable: 'OPEN',
};
Expand All @@ -61,12 +65,29 @@ export default class HttpTrigger extends BaseTrigger<HttpTriggerInputsParams> {
region: RegionType;
inputs: TriggerInputs<HttpTriggerInputsParams>;
}) {
const { triggerInputs } = this.formatInputs({ region, inputs });
console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`);
const { TriggerInfo } = await scf.request(triggerInputs);
TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier;

return TriggerInfo;
// 查询当前触发器是否已存在
const existTrigger = await getScfTriggerByName({ scf, region, inputs });
// 更新触发器
if (existTrigger) {
const { triggerInputs } = this.formatInputs({ region, inputs, action: 'UpdateTrigger' });
console.log(`${triggerInputs.Type} trigger ${triggerInputs.TriggerName} is exist`)
console.log(`Updating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`);
try {
// 更新触发器
await scf.request(triggerInputs);
// 更新成功后,查询最新的触发器信息
const trigger = await getScfTriggerByName({ scf, region, inputs });
return trigger;
} catch (error) {
return {}
}
} else { // 创建触发器
const { triggerInputs } = this.formatInputs({ region, inputs });
console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`);
const { TriggerInfo } = await scf.request(triggerInputs);
TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier;
return TriggerInfo;
}
}

async delete({
Expand Down
17 changes: 15 additions & 2 deletions src/modules/triggers/interface/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ApigwDeployInputs, ApiEndpoint } from '../../apigw/interface';
import { TagInput } from '../../interface';

export type TriggerAction = 'CreateTrigger' | 'UpdateTrigger'
export interface ApigwTriggerRemoveScfTriggerInputs {
serviceId: string;
apiId: string;
Expand Down Expand Up @@ -42,7 +43,7 @@ export interface ApigwTriggerInputsParams extends ApigwDeployInputs {

export type TriggerType = 'scf' | 'timer' | string;
export interface CreateTriggerReq {
Action?: 'CreateTrigger';
Action?: TriggerAction;
ResourceId?: string;
FunctionName?: string;
Namespace?: string;
Expand All @@ -57,11 +58,13 @@ export interface CreateTriggerReq {
export interface CkafkaTriggerInputsParams extends TriggerInputsParams {
qualifier?: string;
name?: string;
topic?: string;
instanceId?: string; //ckafka实例ID
topic?: string; //ckafka主题名称
maxMsgNum?: number;
offset?: number;
retry?: number;
timeout?: number;
consumerGroupName?: string;
enable?: boolean;
}

Expand Down Expand Up @@ -100,6 +103,15 @@ export interface HttpTriggerInputsParams {
enableIntranet?: boolean;
enableExtranet?: boolean;
};
corsConfig: {
enable: boolean
origins: Array<string> | string
methods: Array<string> | string
headers: Array<string> | string
exposeHeaders: Array<string> | string
credentials: boolean
maxAge: number
}
}

export interface MpsTriggerInputsParams {
Expand All @@ -120,6 +132,7 @@ export interface TimerTriggerInputsParams {

export interface TriggerInputs<P extends TriggerInputsParams = TriggerInputsParams> {
functionName: string;
Type?: string; // 兼容scf组件触发器类型字段
type?: string;
triggerDesc?: string;
triggerName?: string;
Expand Down
16 changes: 11 additions & 5 deletions src/modules/triggers/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,17 @@ export class TriggerManager {
// 1. 删除老的无法更新的触发器
for (let i = 0, len = deleteList.length; i < len; i++) {
const trigger = deleteList[i];
await this.removeTrigger({
name,
namespace,
trigger,
});
// 若类型触发器不支持编辑,需要先删除,后重新创建;
if (!CAN_UPDATE_TRIGGER.includes(trigger?.Type)) {
await this.removeTrigger({
name,
namespace,
trigger,
});
} else {
// 若触发器类型支持编辑,直接跳过删除
continue;
}
}

// 2. 创建新的触发器
Expand Down
41 changes: 41 additions & 0 deletions src/modules/triggers/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { RegionType } from "../../interface";
import Scf from "../../scf";
import { CkafkaTriggerInputsParams, HttpTriggerInputsParams, TriggerDetail, TriggerInputs } from "../interface";
import { TriggerManager } from "../manager";

// 获取函数下指定类型以及指定触发器名称的触发器
export async function getScfTriggerByName({
scf,
inputs
}: {
scf: Scf | TriggerManager;
region: RegionType;
inputs: TriggerInputs<HttpTriggerInputsParams | CkafkaTriggerInputsParams>;
}): Promise<TriggerDetail> {
const filters = [
{
Name: 'Type',
Values: [inputs?.type || inputs?.Type]
}
]
if (inputs?.parameters?.name) {
filters.push({
Name: 'TriggerName',
Values: [inputs?.parameters?.name]
})
}
if (inputs?.parameters?.qualifier) {
filters.push({
Name: 'Qualifier',
Values: [inputs?.parameters?.qualifier?.toString()]
})
}
const response = await scf.request({
Action: 'ListTriggers',
FunctionName: inputs?.functionName,
Namespace: inputs?.namespace,
Limit: 1000,
Filters: filters
});
return response?.Triggers?.[0];
}
23 changes: 23 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import camelCase from 'camelcase';
import { PascalCase } from 'type-fest';
import { CamelCasedProps, PascalCasedProps } from '../modules/interface';
import crypto from 'crypto';
import _ from 'lodash';


// TODO: 将一些库换成 lodash

Expand Down Expand Up @@ -314,3 +316,24 @@ export const getYunTiApiUrl = (): string => {
const url = `${apiUrl}?api_key=${apiKey}&api_ts=${timeStamp}&api_sign=${apiSign}`;
return url;
};



/**
* 首字母转换大小写
* @param {*} obj
* @param {*} type
* @returns
*/
export function caseForObject(obj: object,type : 'upper' | 'lower') {
if (!_.isPlainObject(obj)) return obj;
return _.transform(obj, (result: { [key: string]: any }, value, key) => {
let newKey:string = '';
if (type === 'upper') {
newKey = _.upperFirst(key)
} else {
newKey = _.lowerFirst(key);
}
result[newKey] = _.isPlainObject(value) ? caseForObject(value,type) : value;
}, {});
}
Loading