Skip to content

Commit

Permalink
add system hook
Browse files Browse the repository at this point in the history
  • Loading branch information
goodideal committed Sep 5, 2023
1 parent 87be2bb commit 1187064
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 75 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ todo:

- [X] 使用mustache模板
- [X] 增加note通知
- [X] 增加system hook通知,参考 [gitlab system hook](https://docs.gitlab.com/ee/administration/system_hooks.html)
- [ ] 增加消息模板配置文件
- [ ] 支持飞书机器人
- [ ] 支持按天统计数据
Expand Down
62 changes: 51 additions & 11 deletions app/controller/home.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,71 @@
'use strict';

const Controller = require('egg').Controller;
const { X_GITLAB_EVENT } = require('../imports/const');
class HomeController extends Controller {
async index() {
const { ctx } = this;
const { ctx, config } = this;
const { path = '' } = ctx.params;
const webhookUrl =
process.env['WEBHOOK_URL' + (path ? '_' + path.toUpperCase() : '')];

this.logger.debug('webhookUrl', webhookUrl);

this.logger.info('request body: ', ctx.request.body);
const message = await ctx.service.webhook.translateMsg(ctx.request.body);
this.logger.info('====> request headers: ', ctx.request.headers);
this.logger.info('====> request body: ', ctx.request.body);

if (!message) {
this.logger.info('====> message is empty, suppressed.');
ctx.body = { msg: 'message is empty or not supported, suppressed.' };
// platform check
const platform = process.env['PLATFORM'] || config.platform;
this.logger.debug('platform: ', platform);
if (config.supportPlatforms.indexOf(platform) === -1) {
const errMsg = `====> platform "${platform}" is not supported, only support: ${config.supportPlatforms.join(
', '
)}`;
this.logger.error(errMsg);
ctx.body = {
error: errMsg,
};
return;
}

// webhookUrl check
const webhookUrl =
process.env['WEBHOOK_URL' + (path ? '_' + path.toUpperCase() : '')];

this.logger.debug('webhookUrl: ', webhookUrl);

// if webhookUrl not match, exit
if (!webhookUrl) {
this.logger.error('webhook url error, webhookUrl: ' + webhookUrl);
this.logger.error('====> webhook url error, webhookUrl: ' + webhookUrl);
ctx.body = {
error: 'webhook url error, webhookUrl: ' + webhookUrl,
};
return;
}
let gitlabEvent = X_GITLAB_EVENT.push;

// check x-gitlab-event
if (ctx.request.headers['x-gitlab-event']) {
gitlabEvent = ctx.request.headers['x-gitlab-event'];
if (Object.values(X_GITLAB_EVENT).indexOf(gitlabEvent) === -1) {
const errMsg = `====> x-gitlab-event "${gitlabEvent}" is not supported}`;
this.logger.error(errMsg);
ctx.body = {
error: errMsg,
};
return;
}
}

const message = await ctx.service.webhook.translateMsg(
ctx.request.body,
platform,
gitlabEvent
);

if (!message) {
this.logger.info('====> message is empty, suppressed.');
ctx.body = {
msg: '====> message is empty or not supported, suppressed.',
};
return;
}

const result = await ctx.curl(webhookUrl, {
method: 'POST',
Expand Down
41 changes: 41 additions & 0 deletions app/imports/const.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const OBJECT_KIND = {
push: 'push',
tag_push: 'tag_push',
issue: 'issue',
note: 'note',
merge_request: 'merge_request',
wiki_page: 'wiki_page',
pipeline: 'pipeline',
build: 'build', // todo
};

const EVENT_TYPE = {
group_create: 'group_create',
group_destroy: 'group_destroy',
group_rename: 'group_rename',
key_create: 'key_create',
key_destroy: 'key_destroy',
project_create: 'project_create',
project_destroy: 'project_destroy',
project_rename: 'project_rename',
project_transfer: 'project_transfer',
project_update: 'project_update',
repository_update: 'repository_update',
user_add_to_group: 'user_add_to_group',
user_add_to_team: 'user_add_to_team',
user_create: 'user_create',
user_destroy: 'user_destroy',
user_failed_login: 'user_failed_login',
user_remove_from_group: 'user_remove_from_group',
user_remove_from_team: 'user_remove_from_team',
user_rename: 'user_rename',
user_update_for_group: 'user_update_for_group',
user_update_for_team: 'user_update_for_team',
};

const X_GITLAB_EVENT = {
push: 'Push Hook',
system: 'System Hook',
};

module.exports = { OBJECT_KIND, EVENT_TYPE, X_GITLAB_EVENT };
114 changes: 67 additions & 47 deletions app/service/webhook.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,62 @@ const Service = require('egg').Service;
const moment = require('moment');
const Mustache = require('mustache');

const { OBJECT_KIND, X_GITLAB_EVENT, EVENT_TYPE } = require('../imports/const');

// set default lang
moment.locale('zh-cn');

// override default escape, remove \n and collapse whitespace
Mustache.escape = text =>
text.toString().replace('\n', ' ').replace(/\s+/g, ' ');

const OBJECT_KIND = {
push: 'push',
tag_push: 'tag_push',
issue: 'issue',
note: 'note',
merge_request: 'merge_request',
wiki_page: 'wiki_page',
pipeline: 'pipeline',
build: 'build', // todo
};

const REDIS_KEY = {
pipeline: id => `gitlab.pipeline.${id}`,
};

const REDIS_VAL = {
pipeline: ({ pipelineId, stages, status, duration, builds }) => {
// all customized variables start with GB_
class WebhookService extends Service {
async translateMsg(data = {}, platform, gitlabEvent) {
this.platform = platform;

const { object_kind } = data;

const content = [];
switch (gitlabEvent) {
case X_GITLAB_EVENT.push:
this.pushHookHandler(content, data);
break;
case X_GITLAB_EVENT.system:
// system hook to push hook if object_kind exists
OBJECT_KIND[object_kind]
? this.pushHookHandler(content, data)
: this.systemHookHandler(content, data);
break;
default:
// controller make sure not to here
break;
}

return {
type: 'pipeline',
id: pipelineId,
duration: duration,
durationMin: Math.round(duration / 60 - 0.5),
durationSec: duration % 60,
status: status,
stages: stages,
builds: builds,
msgtype: 'markdown',
markdown: { content: content.join(' \n ') },
};
},
};
}

async pushHookHandler(content, data) {
const { object_kind } = data;

// all customized variables start with GB_
class WebhookService extends Service {
async translateMsg(data) {
const { object_kind } = data || {};
if (!OBJECT_KIND[object_kind]) {
return {};
}

let res = true;
const content = [];
switch (object_kind) {
case OBJECT_KIND.push:
res = await this.assemblePushMsg(content, data);
break;

case OBJECT_KIND.pipeline:
res = await this.assemblePipelineMsg(content, data);
break;

case OBJECT_KIND.merge_request:
res = await this.assembleMergeMsg(content, data);
break;

case OBJECT_KIND.tag_push:
res = await this.assembleTagPushMsq(content, data);
break;
Expand All @@ -80,12 +76,36 @@ class WebhookService extends Service {
res = false;
break;
}
if (!res) return false;
return res;
}

return {
msgtype: 'markdown',
markdown: { content: content.join(' \n ') },
};
async systemHookHandler(content, data) {
const template = this.getTemplateByPlatform(this.platform);

const { event_name } = data;
if (!EVENT_TYPE[event_name]) {
const errMsg = `====> event_name "${event_name}" is not supported, suppressed}`;
this.logger.error(errMsg);
return false;
}

this.logger.debug('template: ', template.push);
this.logger.debug('content: ', content);
if (template[event_name]) {
// match template by event_name first
content.push(Mustache.render(template[event_name], data));
} else {
const event_arr = event_name.split('_');
const event_action = event_arr[0] + '_action';
if (!template[event_action]) {
const errMsg = `====> event_action "${event_action}" is not supported, suppressed}`;
this.logger.error(errMsg);
return false;
}
content.push(Mustache.render(template[event_action], data));
}

return content;
}

async assemblePushMsg(content, data) {
Expand All @@ -103,7 +123,7 @@ class WebhookService extends Service {
GB_op = '将代码推至';
}

const template = this.getTemplateByPlatform('qywx');
const template = this.getTemplateByPlatform(this.platform);

this.logger.debug('template: ', template.push);
this.logger.debug('content: ', content);
Expand Down Expand Up @@ -180,7 +200,7 @@ class WebhookService extends Service {
// gitlab 11.3 未支持source参数
GB_sourceString = `${name}`;
}
const template = this.getTemplateByPlatform('qywx');
const template = this.getTemplateByPlatform(this.platform);
const pipeline = Mustache.render(template.pipeline, {
...data,
GB_pipelineId,
Expand Down Expand Up @@ -222,7 +242,7 @@ class WebhookService extends Service {
default:
}

const template = this.getTemplateByPlatform('qywx');
const template = this.getTemplateByPlatform(this.platform);
const merge_request = Mustache.render(template.merge_request, {
...data,
GB_stateAction,
Expand All @@ -247,7 +267,7 @@ class WebhookService extends Service {
GB_op = '删除';
}

const template = this.getTemplateByPlatform('qywx');
const template = this.getTemplateByPlatform(this.platform);
const tag_push = Mustache.render(template.tag_push, {
...data,
GB_tag,
Expand All @@ -262,7 +282,7 @@ class WebhookService extends Service {
const { object_attributes = {} } = data;
const { state } = object_attributes;

const template = this.getTemplateByPlatform('qywx');
const template = this.getTemplateByPlatform(this.platform);
const issue = Mustache.render(template.issue, {
...data,
GB_state: this.formatStatus(state),
Expand All @@ -274,7 +294,7 @@ class WebhookService extends Service {
const { object_attributes = {} } = data;
const { action } = object_attributes;

const template = this.getTemplateByPlatform('qywx');
const template = this.getTemplateByPlatform(this.platform);
const issue = Mustache.render(template.wiki, {
...data,
GB_action: this.formatAction(action),
Expand All @@ -286,7 +306,7 @@ class WebhookService extends Service {
const { object_attributes = {} } = data;
const { action } = object_attributes;

const template = this.getTemplateByPlatform('qywx');
const template = this.getTemplateByPlatform(this.platform);
const note = Mustache.render(template.note, {
...data,
GB_action: this.formatAction(action),
Expand Down
Loading

0 comments on commit 1187064

Please sign in to comment.