Initial commit of LittleShop project (excluding large archives)
- BTCPay Server integration - TeleBot Telegram bot - Review system - Admin area - Docker deployment configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
285
Hostinger/mattermost_local_api.js
Normal file
285
Hostinger/mattermost_local_api.js
Normal file
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* ===============================================================================
|
||||
* MATTERMOST LOCAL API FOR BTCPAY SSH COMMANDS
|
||||
* ===============================================================================
|
||||
* Created: September 10, 2025
|
||||
* Purpose: Local web API that runs SSH commands to retrieve BTCPay onion addresses
|
||||
* Deploy: On your Mattermost server (not the VPS)
|
||||
* Usage: Mattermost slash commands → Local API → SSH to VPS → Return data
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const { exec } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3333;
|
||||
|
||||
// Configuration - ADJUST THESE PATHS FOR YOUR MATTERMOST SERVER
|
||||
const config = {
|
||||
vps_domain: 'thebankofdebbie.giize.com',
|
||||
vps_port: 2255,
|
||||
vps_user: 'sysadmin',
|
||||
ssh_key_path: '/mnt/c/Production/Source/LittleShop/Hostinger/vps_hardening_key',
|
||||
mattermost_token: '7grgg4r7sjf4dx9qxa7wuybmnh',
|
||||
allowed_users: ['bankofdebbie', 'admin', 'sysadmin']
|
||||
};
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
/**
|
||||
* Execute SSH command to VPS
|
||||
*/
|
||||
function executeSSHCommand(command) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const sshCmd = `ssh -i ${config.ssh_key_path} -p ${config.vps_port} -o StrictHostKeyChecking=no -o ConnectTimeout=15 ${config.vps_user}@${config.vps_domain} "${command}"`;
|
||||
|
||||
console.log(`Executing SSH command: ${command}`);
|
||||
|
||||
exec(sshCmd, { timeout: 30000 }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`SSH Error: ${error.message}`);
|
||||
reject(new Error(`SSH command failed: ${error.message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.warn(`SSH Warning: ${stderr}`);
|
||||
}
|
||||
|
||||
resolve(stdout.trim());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BTCPay onion address
|
||||
*/
|
||||
async function getBTCPayOnion() {
|
||||
try {
|
||||
const result = await executeSSHCommand('sudo cat /var/lib/docker/volumes/generated_tor_servicesdir/_data/BTCPayServer/hostname 2>/dev/null || echo "pending"');
|
||||
return result || 'pending';
|
||||
} catch (error) {
|
||||
return 'error: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Bitcoin P2P onion address
|
||||
*/
|
||||
async function getBitcoinOnion() {
|
||||
try {
|
||||
const result = await executeSSHCommand('sudo cat /var/lib/docker/volumes/generated_tor_servicesdir/_data/BTC-P2P/hostname 2>/dev/null || echo "pending"');
|
||||
return result || 'pending';
|
||||
} catch (error) {
|
||||
return 'error: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system status
|
||||
*/
|
||||
async function getSystemStatus() {
|
||||
try {
|
||||
const commands = [
|
||||
'docker ps --format "table {{.Names}}\\t{{.Status}}" | grep -E "(btcpay|bitcoin|tor)" | wc -l',
|
||||
'df -h / | grep -v Filesystem | awk "{print \\$3 \\" used / \\" \\$2 \\" total\\"}"',
|
||||
'docker logs btcpayserver_bitcoind 2>&1 | grep -i "prune configured" | tail -1 | grep -o "[0-9]* MiB" || echo "10000 MiB"'
|
||||
];
|
||||
|
||||
const [containers, disk, pruning] = await Promise.all(
|
||||
commands.map(cmd => executeSSHCommand(cmd).catch(err => 'error'))
|
||||
);
|
||||
|
||||
return {
|
||||
containers: containers + ' containers running',
|
||||
disk_usage: disk,
|
||||
bitcoin_pruning: pruning + ' max storage'
|
||||
};
|
||||
} catch (error) {
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Mattermost slash command endpoint
|
||||
*/
|
||||
app.post('/btcpay', async (req, res) => {
|
||||
try {
|
||||
console.log('Mattermost request:', JSON.stringify(req.body, null, 2));
|
||||
|
||||
const { token, user_name, text, command } = req.body;
|
||||
|
||||
// Validate token
|
||||
if (token !== config.mattermost_token) {
|
||||
return res.json({
|
||||
response_type: 'ephemeral',
|
||||
text: '❌ Unauthorized: Invalid token'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user is authorized
|
||||
if (!config.allowed_users.includes(user_name)) {
|
||||
return res.json({
|
||||
response_type: 'ephemeral',
|
||||
text: `❌ Access denied for user: ${user_name}. Contact admin for BTCPay access.`
|
||||
});
|
||||
}
|
||||
|
||||
const commandText = (text || '').toLowerCase().trim();
|
||||
const isOnionCommand = commandText.includes('onion') || commandText === '' || commandText.includes('addresses');
|
||||
const isStatusCommand = commandText.includes('status');
|
||||
const isHelpCommand = commandText.includes('help');
|
||||
|
||||
if (isHelpCommand) {
|
||||
return res.json({
|
||||
response_type: 'ephemeral',
|
||||
text: `## BTCPay Server Commands\n\n` +
|
||||
`**Available commands:**\n` +
|
||||
`• \`/btcpay\` or \`/btcpay onion\` - Get onion addresses\n` +
|
||||
`• \`/btcpay status\` - Get system status\n` +
|
||||
`• \`/btcpay help\` - Show this help\n\n` +
|
||||
`**VPS:** ${config.vps_domain}\n` +
|
||||
`**Method:** SSH-based secure retrieval\n` +
|
||||
`**User:** ${user_name} ✅`
|
||||
});
|
||||
}
|
||||
|
||||
if (isStatusCommand) {
|
||||
// Get full system status
|
||||
const [btcpayOnion, bitcoinOnion, systemStatus] = await Promise.all([
|
||||
getBTCPayOnion(),
|
||||
getBitcoinOnion(),
|
||||
getSystemStatus()
|
||||
]);
|
||||
|
||||
const response = {
|
||||
response_type: 'in_channel',
|
||||
text: `## 📊 BTCPay Server Status Report\n\n` +
|
||||
`**🌐 Domain:** https://${config.vps_domain}\n\n` +
|
||||
`**🧅 Tor Onion Services:**\n` +
|
||||
`• **BTCPay:** \`${btcpayOnion}\`\n` +
|
||||
`• **Bitcoin P2P:** \`${bitcoinOnion}\`\n\n` +
|
||||
`**📊 System Health:**\n` +
|
||||
`• **Containers:** ${systemStatus.containers || 'checking...'}\n` +
|
||||
`• **Storage:** ${systemStatus.disk_usage || 'checking...'}\n` +
|
||||
`• **Bitcoin:** ${systemStatus.bitcoin_pruning || 'Pruned mode'}\n\n` +
|
||||
`**🔒 Security:** Tor-only Bitcoin, Hardened Debian 13\n` +
|
||||
`**📅 Retrieved:** ${new Date().toLocaleString()}\n` +
|
||||
`**👤 Requested by:** ${user_name}`
|
||||
};
|
||||
|
||||
return res.json(response);
|
||||
}
|
||||
|
||||
if (isOnionCommand) {
|
||||
// Get onion addresses only
|
||||
const [btcpayOnion, bitcoinOnion] = await Promise.all([
|
||||
getBTCPayOnion(),
|
||||
getBitcoinOnion()
|
||||
]);
|
||||
|
||||
const response = {
|
||||
response_type: 'in_channel',
|
||||
text: `## 🧅 BTCPay Tor Onion Addresses\n\n` +
|
||||
`**🌐 Domain:** https://${config.vps_domain}\n\n` +
|
||||
`**🧅 Tor Hidden Services:**\n` +
|
||||
`• **BTCPay Server:** \`${btcpayOnion}\`\n` +
|
||||
`• **Bitcoin P2P:** \`${bitcoinOnion}\`\n\n` +
|
||||
`**🔐 Access Methods:**\n` +
|
||||
`• **Clearnet:** https://${config.vps_domain}\n` +
|
||||
`• **Tor Browser:** http://${btcpayOnion}\n\n` +
|
||||
`**⚡ API Endpoints:**\n` +
|
||||
`• **REST API:** https://${config.vps_domain}/api\n` +
|
||||
`• **Tor API:** http://${btcpayOnion}/api\n\n` +
|
||||
`**📅 Retrieved:** ${new Date().toLocaleString()}\n` +
|
||||
`**👤 Requested by:** ${user_name}`
|
||||
};
|
||||
|
||||
return res.json(response);
|
||||
}
|
||||
|
||||
// Default response
|
||||
return res.json({
|
||||
response_type: 'ephemeral',
|
||||
text: `❓ Unknown command: "${commandText}"\n\n` +
|
||||
`Use \`/btcpay help\` for available commands.\n\n` +
|
||||
`**Quick commands:**\n` +
|
||||
`• \`/btcpay\` - Get onion addresses\n` +
|
||||
`• \`/btcpay status\` - Get system status`
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('API Error:', error);
|
||||
return res.json({
|
||||
response_type: 'ephemeral',
|
||||
text: `❌ **Error retrieving BTCPay information:**\n\`\`\`\n${error.message}\n\`\`\`\n\nPlease check VPS connectivity.`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Health check endpoint
|
||||
*/
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'healthy',
|
||||
service: 'Mattermost BTCPay Local API',
|
||||
vps_target: config.vps_domain,
|
||||
method: 'SSH-based commands',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test endpoint
|
||||
*/
|
||||
app.get('/test', async (req, res) => {
|
||||
try {
|
||||
const [btcpayOnion, bitcoinOnion] = await Promise.all([
|
||||
getBTCPayOnion(),
|
||||
getBitcoinOnion()
|
||||
]);
|
||||
|
||||
res.json({
|
||||
vps_domain: config.vps_domain,
|
||||
btcpay_onion: btcpayOnion,
|
||||
bitcoin_onion: bitcoinOnion,
|
||||
method: 'SSH retrieval',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Start server
|
||||
*/
|
||||
app.listen(PORT, '127.0.0.1', () => {
|
||||
console.log(`🚀 Mattermost BTCPay Local API running on localhost:${PORT}`);
|
||||
console.log(`🎯 Target VPS: ${config.vps_domain}:${config.vps_port}`);
|
||||
console.log(`🔑 Method: SSH-based command execution`);
|
||||
console.log(`💡 Endpoints:`);
|
||||
console.log(` POST /btcpay - Mattermost slash command handler`);
|
||||
console.log(` GET /test - Test SSH connectivity`);
|
||||
console.log(` GET /health - Health check`);
|
||||
console.log(`\n🔧 Mattermost Slash Command Setup:`);
|
||||
console.log(` Command: /btcpay`);
|
||||
console.log(` URL: http://localhost:${PORT}/btcpay`);
|
||||
console.log(` Token: ${config.mattermost_token}`);
|
||||
console.log(` Method: POST`);
|
||||
console.log(`\n⚠️ IMPORTANT: Update ssh_key_path in config before running!`);
|
||||
console.log(` Current path: ${config.ssh_key_path}`);
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGTERM', () => {
|
||||
console.log('🛑 Shutting down local API server...');
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
Reference in New Issue
Block a user