genesis
This commit is contained in:
368
x.js
Normal file
368
x.js
Normal file
@@ -0,0 +1,368 @@
|
||||
// server.js
|
||||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const axios = require('axios');
|
||||
const app = express();
|
||||
const { exec, spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Create logs directory if it doesn't exist
|
||||
const logsDir = path.join(__dirname, 'logs');
|
||||
if (!fs.existsSync(logsDir)) {
|
||||
fs.mkdirSync(logsDir);
|
||||
}
|
||||
|
||||
// Logger function
|
||||
function logMessage(message, type = 'info') {
|
||||
const timestamp = new Date().toISOString();
|
||||
const logEntry = `[${timestamp}] [${type.toUpperCase()}] ${message}`;
|
||||
|
||||
console[type === 'error' ? 'error' : 'log'](logEntry);
|
||||
|
||||
// Also log to file
|
||||
const logFile = path.join(logsDir, `webhook_${new Date().toISOString().split('T')[0]}.log`);
|
||||
fs.appendFileSync(logFile, logEntry + '\n');
|
||||
}
|
||||
|
||||
// Telegram bot function
|
||||
async function sendTelegramMessage(message) {
|
||||
const botToken = process.env.TELEGRAM_BOT_TOKEN;
|
||||
const chatId = process.env.TELEGRAM_CHAT_ID;
|
||||
|
||||
if (!botToken || !chatId) {
|
||||
logMessage('Telegram bot token or chat ID not configured', 'warn');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
|
||||
const response = await axios.post(url, {
|
||||
chat_id: chatId,
|
||||
text: message,
|
||||
parse_mode: 'MarkdownV2',
|
||||
disable_web_page_preview: false
|
||||
});
|
||||
|
||||
logMessage('Telegram message sent successfully');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
logMessage(`Failed to send Telegram message: ${error.message}`, 'error');
|
||||
if (error.response) {
|
||||
logMessage(`Telegram API error: ${JSON.stringify(error.response.data)}`, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format commit message for Telegram
|
||||
function formatCommitMessage(payload) {
|
||||
const repo = payload.repository?.name || 'Unknown Repository';
|
||||
const commits = payload.commits || [];
|
||||
const pusher = payload.pusher?.username || payload.pusher?.login || 'Unknown';
|
||||
|
||||
if (!commits || commits.length === 0) {
|
||||
return `🔄 *${repo}* - Push event received but no commit details available`;
|
||||
}
|
||||
|
||||
// Aggregate all files across commits
|
||||
const allAdded = new Set();
|
||||
const allModified = new Set();
|
||||
const allRemoved = new Set();
|
||||
|
||||
commits.forEach(commit => {
|
||||
(commit.added || []).forEach(file => allAdded.add(file));
|
||||
(commit.modified || []).forEach(file => allModified.add(file));
|
||||
(commit.removed || []).forEach(file => allRemoved.add(file));
|
||||
});
|
||||
|
||||
// Format commits list
|
||||
let commitsText = '';
|
||||
// MarkdownV2 full escaper for arbitrary text (not code)
|
||||
const escapeMdV2 = (s) => {
|
||||
if (s == null) return '';
|
||||
return String(s)
|
||||
// Order matters: escape backslash first
|
||||
.replace(/\\/g, '\\\\')
|
||||
// Telegram MarkdownV2 reserved characters (in text contexts)
|
||||
.replace(/([_*[\]()~`>#+\-=|{}.!])/g, '\\$1');
|
||||
};
|
||||
// Escaper for inline code content inside backticks: only escape backticks and backslash
|
||||
const escapeInlineCode = (s) => {
|
||||
if (s == null) return '';
|
||||
return String(s)
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/`/g, '\\`');
|
||||
};
|
||||
|
||||
commits.forEach((commit) => {
|
||||
const commitMessage = commit.message?.trim() || 'No commit message';
|
||||
const escapedMessage = escapeMdV2(commitMessage);
|
||||
|
||||
const shortHash = commit.id?.substring(0, 7) || 'unknown';
|
||||
const author = commit.author?.name || 'Unknown';
|
||||
|
||||
// backticked hash must use code-escaper rules
|
||||
const hashInline = escapeInlineCode(shortHash);
|
||||
|
||||
commitsText += `\n🔧 \`${hashInline}\` - ${escapedMessage}`;
|
||||
if (commits.length > 1) {
|
||||
commitsText += ` (${escapeMdV2(author)})`;
|
||||
}
|
||||
});
|
||||
|
||||
// Format aggregated file lists
|
||||
let filesList = '';
|
||||
|
||||
if (allAdded.size > 0) {
|
||||
filesList += `\n➕ *Added (${allAdded.size}):*\n`;
|
||||
Array.from(allAdded).slice(0, 10).forEach(file => {
|
||||
filesList += ` • \`${escapeInlineCode(file)}\`\n`;
|
||||
});
|
||||
if (allAdded.size > 10) {
|
||||
filesList += ` • ... and ${allAdded.size - 10} more\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (allModified.size > 0) {
|
||||
filesList += `\n📝 *Modified (${allModified.size}):*\n`;
|
||||
Array.from(allModified).slice(0, 10).forEach(file => {
|
||||
filesList += ` • \`${escapeInlineCode(file)}\`\n`;
|
||||
});
|
||||
if (allModified.size > 10) {
|
||||
filesList += ` • ... and ${allModified.size - 10} more\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (allRemoved.size > 0) {
|
||||
filesList += `\n❌ *Removed (${allRemoved.size}):*\n`;
|
||||
Array.from(allRemoved).slice(0, 10).forEach(file => {
|
||||
filesList += ` • \`${escapeInlineCode(file)}\`\n`;
|
||||
});
|
||||
if (allRemoved.size > 10) {
|
||||
filesList += ` • ... and ${allRemoved.size - 10} more\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!filesList) {
|
||||
filesList = '\n📂 No files changed';
|
||||
}
|
||||
|
||||
const totalCommits = commits.length;
|
||||
const commitWord = totalCommits === 1 ? 'commit' : 'commits';
|
||||
|
||||
// Escape heading line content and link text/url appropriately
|
||||
const repoEsc = escapeMdV2(repo);
|
||||
const heading = `🚀 *${repoEsc}* - ${totalCommits} ${commitWord} pushed`;
|
||||
|
||||
let compare = '';
|
||||
if (payload.compare_url) {
|
||||
const linkText = escapeMdV2('Compare Changes');
|
||||
const linkUrl = escapeMdV2(payload.compare_url);
|
||||
compare = `[${linkText}](${linkUrl})`;
|
||||
}
|
||||
|
||||
return `${heading}
|
||||
${commitsText}
|
||||
${filesList}
|
||||
${compare}`;
|
||||
}
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.post('/releasehook_kjfhdkf987987', (req, res) => {
|
||||
try {
|
||||
const payload = req.body;
|
||||
logMessage(`Webhook received for repository: ${payload?.repository?.name || 'unknown'}`);
|
||||
|
||||
// Log the complete payload for analysis
|
||||
logMessage(`Complete payload received: ${JSON.stringify(payload, null, 2)}`);
|
||||
|
||||
// Send Telegram notification
|
||||
const telegramMessage = formatCommitMessage(payload);
|
||||
sendTelegramMessage(telegramMessage).catch(error => {
|
||||
logMessage(`Error sending Telegram message: ${error.message}`, 'error');
|
||||
});
|
||||
|
||||
// Set a flag to track if we've sent a response
|
||||
let responseSent = false;
|
||||
|
||||
if(payload && payload.repository && payload.repository.name == 'reactShop'){
|
||||
// cd /home/seb/src/reactShop ; git pull ; npm run build
|
||||
const repoPath = '/home/seb/src/growheads_de';
|
||||
const command = `cd ${repoPath} && pkill -f "node.*prerender\\.cjs" || true && git pull && npm i . && npm run build:prerender`;
|
||||
logMessage(`Executing command for reactShop: ${command}`);
|
||||
|
||||
// Use spawn to stream output line by line
|
||||
// @note Try with explicit options to prevent early termination
|
||||
const child = spawn('bash', ['-c', command], {
|
||||
detached: false,
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
logMessage(`Started process for reactShop with PID: ${child.pid}`);
|
||||
|
||||
// Stream stdout line by line
|
||||
child.stdout.on('data', (data) => {
|
||||
const lines = data.toString().split('\n');
|
||||
lines.forEach(line => {
|
||||
if (line.trim()) {
|
||||
logMessage(`[reactShop stdout] ${line.trim()}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Stream stderr line by line
|
||||
child.stderr.on('data', (data) => {
|
||||
const lines = data.toString().split('\n');
|
||||
lines.forEach(line => {
|
||||
if (line.trim()) {
|
||||
logMessage(`[reactShop stderr] ${line.trim()}`, 'warn');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle process completion
|
||||
child.on('close', (code, signal) => {
|
||||
if (signal) {
|
||||
logMessage(`Command for reactShop was terminated by signal: ${signal}`, 'error');
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
return res.status(500).json({
|
||||
error: 'Command execution failed',
|
||||
details: `Process terminated by signal: ${signal}`
|
||||
});
|
||||
}
|
||||
} else if (code !== 0) {
|
||||
logMessage(`Command for reactShop exited with code ${code}`, 'error');
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
return res.status(500).json({
|
||||
error: 'Command execution failed',
|
||||
details: `Process exited with code ${code}`
|
||||
});
|
||||
}
|
||||
} else {
|
||||
logMessage(`Command for reactShop completed successfully (exit code: ${code})`);
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
res.status(200).json({ success: true, repository: 'reactShop' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle process errors
|
||||
child.on('error', (error) => {
|
||||
logMessage(`Error executing command for reactShop: ${error.message}`, 'error');
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
return res.status(500).json({
|
||||
error: 'Command execution failed',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle process exit (fired before close)
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
logMessage(`Process for reactShop exited due to signal: ${signal}`, 'warn');
|
||||
} else {
|
||||
logMessage(`Process for reactShop exited with code: ${code}`, code === 0 ? 'info' : 'warn');
|
||||
}
|
||||
});
|
||||
|
||||
return; // Exit early to avoid double response
|
||||
}
|
||||
|
||||
if(payload && payload.repository && payload.repository.name == 'shopApi'){
|
||||
// cd /home/seb/src/shopApi ; git pull
|
||||
const repoPath = '/home/seb/src/shopApi';
|
||||
const command = `cd ${repoPath} && git pull`;
|
||||
logMessage(`Executing command for shopApi: ${command}`);
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
logMessage(`Error executing command for shopApi: ${error.message}`, 'error');
|
||||
if (stderr) {
|
||||
logMessage(`Command stderr: ${stderr}`, 'error');
|
||||
}
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
return res.status(500).json({
|
||||
error: 'Command execution failed',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
logMessage(`Command output for shopApi: ${stdout.trim()}`);
|
||||
}
|
||||
|
||||
if (stderr && !error) {
|
||||
logMessage(`Command stderr (non-fatal) for shopApi: ${stderr}`, 'warn');
|
||||
}
|
||||
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
res.status(200).json({ success: true, repository: 'shopApi' });
|
||||
}
|
||||
});
|
||||
return; // Exit early to avoid double response
|
||||
}
|
||||
|
||||
if(payload && payload.repository && payload.repository.name == 'quickdhl'){
|
||||
// cd /home/seb/src/shopApi ; git pull
|
||||
const repoPath = '/home/seb/src/quickdhl';
|
||||
const command = `cd ${repoPath} && git pull`;
|
||||
logMessage(`Executing command for quickdhl: ${command}`);
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
logMessage(`Error executing command for quickdhl: ${error.message}`, 'error');
|
||||
if (stderr) {
|
||||
logMessage(`Command stderr: ${stderr}`, 'error');
|
||||
}
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
return res.status(500).json({
|
||||
error: 'Command execution failed',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
logMessage(`Command output for quickdhl: ${stdout.trim()}`);
|
||||
}
|
||||
|
||||
if (stderr && !error) {
|
||||
logMessage(`Command stderr (non-fatal) for quickdhl: ${stderr}`, 'warn');
|
||||
}
|
||||
|
||||
if (!responseSent) {
|
||||
responseSent = true;
|
||||
res.status(200).json({ success: true, repository: 'shopApi' });
|
||||
}
|
||||
});
|
||||
return; // Exit early to avoid double response
|
||||
}
|
||||
|
||||
// If we get here, no repository matched
|
||||
logMessage(`No handler found for repository: ${payload?.repository?.name || 'unknown'}`, 'warn');
|
||||
res.status(200).json({ success: true, message: 'No action taken' });
|
||||
|
||||
} catch (err) {
|
||||
logMessage(`Unhandled exception in webhook handler: ${err.message}`, 'error');
|
||||
logMessage(err.stack, 'error');
|
||||
res.status(500).json({ error: 'Internal server error', message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Add a simple health check endpoint
|
||||
app.get('/health', (req, res) => {
|
||||
res.status(200).json({ status: 'ok' });
|
||||
});
|
||||
|
||||
app.listen(9304, () => logMessage('Webhook server listening on port 9304'));
|
||||
Reference in New Issue
Block a user