revert v2

This commit is contained in:
Xargana 2025-04-17 17:11:04 +03:00
parent 5a474c5592
commit f2cc49124f
18 changed files with 5666 additions and 0 deletions

View file

@ -0,0 +1,351 @@
const express = require('express');
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const router = express.Router();
// Get API key from environment variables
const API_KEY = process.env.EXCHANGE_RATE_API_KEY;
if (!API_KEY) {
console.error('WARNING: EXCHANGE_RATE_API_KEY environment variable is not set!');
console.error('Exchange rate functionality will not work correctly.');
}
const BASE_URL = 'https://v6.exchangerate-api.com/v6';
// We'll use USD as our single base currency
const BASE_CURRENCY = 'USD';
// Path to the cache file
const CACHE_FILE_PATH = path.join(__dirname, 'exchange-rates-cache.json');
// In-memory storage for cached exchange rates
let exchangeRatesCache = {
USD: {
lastUpdated: null,
rates: {},
nextUpdateTime: null
}
};
// one day in milliseconds
const UPDATE_INTERVAL = 1 * 24 * 60 * 60 * 1000;
// Load cached exchange rates from file
function loadCachedRates() {
try {
if (fs.existsSync(CACHE_FILE_PATH)) {
const data = fs.readFileSync(CACHE_FILE_PATH, 'utf8');
const parsedData = JSON.parse(data);
// Convert string dates back to Date objects
if (parsedData.USD) {
if (parsedData.USD.lastUpdated) {
parsedData.USD.lastUpdated = new Date(parsedData.USD.lastUpdated);
}
if (parsedData.USD.nextUpdateTime) {
parsedData.USD.nextUpdateTime = new Date(parsedData.USD.nextUpdateTime);
}
}
exchangeRatesCache = parsedData;
console.log('Loaded exchange rates from cache file');
} else {
console.log('No cache file found, will create one when rates are fetched');
}
} catch (error) {
console.error('Error loading cached exchange rates:', error.message);
// Continue with default cache if file can't be loaded
}
}
// Save exchange rates to cache file
function saveCachedRates() {
try {
fs.writeFileSync(CACHE_FILE_PATH, JSON.stringify(exchangeRatesCache, null, 2));
console.log('Exchange rates saved to cache file');
} catch (error) {
console.error('Error saving exchange rates to cache file:', error.message);
}
}
// Function to fetch and update exchange rates using USD as base
async function updateExchangeRates() {
if (!API_KEY) {
console.error('Cannot update exchange rates: API key is not set');
return false;
}
try {
console.log(`Fetching latest exchange rates using ${BASE_CURRENCY} as base...`);
const response = await axios.get(`${BASE_URL}/${API_KEY}/latest/${BASE_CURRENCY}`);
if (response.data && response.data.result === 'success') {
exchangeRatesCache.USD = {
lastUpdated: new Date(),
rates: response.data.conversion_rates,
nextUpdateTime: new Date(Date.now() + UPDATE_INTERVAL)
};
// Save to file after updating
saveCachedRates();
console.log('Exchange rates updated successfully');
return true;
}
return false;
} catch (error) {
console.error('Failed to update exchange rates:', error.message);
return false;
}
}
// Check if rates need updating and update if necessary
async function ensureRatesUpdated() {
if (!exchangeRatesCache.USD.lastUpdated ||
Date.now() > exchangeRatesCache.USD.nextUpdateTime.getTime()) {
return await updateExchangeRates();
}
console.log(`Using cached rates, next update: ${exchangeRatesCache.USD.nextUpdateTime}`);
return true;
}
// Calculate conversion rate between any two currencies using USD as base
function calculateRate(from, to) {
const rates = exchangeRatesCache.USD.rates;
// If either currency is USD, we can use the rate directly
if (from === 'USD') return rates[to];
if (to === 'USD') return 1 / rates[from];
// Otherwise, calculate cross rate: from -> USD -> to
return rates[to] / rates[from];
}
// Load cached rates when the module is loaded
loadCachedRates();
// Initialize rates if needed
ensureRatesUpdated();
// Root endpoint
router.get('/', (req, res) => {
const availableCurrencies = exchangeRatesCache.USD.rates ?
Object.keys(exchangeRatesCache.USD.rates) : [];
res.json({
message: 'Exchange Rate API is running',
baseCurrency: BASE_CURRENCY,
availableCurrencies,
lastUpdated: exchangeRatesCache.USD.lastUpdated,
nextUpdate: exchangeRatesCache.USD.nextUpdateTime,
updateInterval: '1 day',
endpoints: {
latest: '/latest',
convert: '/convert/:from/:to/:amount',
currencies: '/currencies'
}
});
});
// Get all cached exchange rates
router.get('/latest', async (req, res) => {
await ensureRatesUpdated();
if (!exchangeRatesCache.USD.rates) {
return res.status(503).json({ error: 'Exchange rate data not yet available' });
}
res.json({
result: 'success',
base: BASE_CURRENCY,
lastUpdated: exchangeRatesCache.USD.lastUpdated,
nextUpdate: exchangeRatesCache.USD.nextUpdateTime,
rates: exchangeRatesCache.USD.rates
});
});
// Get rates for a specific currency as base
router.get('/latest/:currency', async (req, res) => {
const { currency } = req.params;
const currencyCode = currency.toUpperCase();
await ensureRatesUpdated();
if (!exchangeRatesCache.USD.rates) {
return res.status(503).json({ error: 'Exchange rate data not yet available' });
}
// Check if the currency is supported
if (!exchangeRatesCache.USD.rates[currencyCode] && currencyCode !== 'USD') {
return res.status(400).json({ error: `Currency '${currencyCode}' not supported` });
}
// Calculate rates with the requested currency as base
const rates = {};
const usdRates = exchangeRatesCache.USD.rates;
// If the requested base is USD, return rates directly
if (currencyCode === 'USD') {
res.json({
result: 'success',
base: currencyCode,
lastUpdated: exchangeRatesCache.USD.lastUpdated,
nextUpdate: exchangeRatesCache.USD.nextUpdateTime,
rates: usdRates
});
return;
}
// Otherwise, calculate rates for all currencies with the requested currency as base
const baseRate = usdRates[currencyCode]; // Rate of 1 USD in the requested currency
// Add USD rate
rates['USD'] = 1 / baseRate;
// Add rates for all other currencies
for (const toCurrency in usdRates) {
if (toCurrency !== currencyCode) {
// Convert through USD: from -> USD -> to
rates[toCurrency] = usdRates[toCurrency] / baseRate;
}
}
// Add rate for the base currency itself
rates[currencyCode] = 1;
res.json({
result: 'success',
base: currencyCode,
lastUpdated: exchangeRatesCache.USD.lastUpdated,
nextUpdate: exchangeRatesCache.USD.nextUpdateTime,
rates: rates
});
});
// Get list of available currencies
router.get('/currencies', async (req, res) => {
await ensureRatesUpdated();
const availableCurrencies = exchangeRatesCache.USD.rates ?
Object.keys(exchangeRatesCache.USD.rates) : [];
res.json({
result: 'success',
baseCurrency: BASE_CURRENCY,
availableCurrencies,
lastUpdated: exchangeRatesCache.USD.lastUpdated,
nextUpdate: exchangeRatesCache.USD.nextUpdateTime
});
});
// Convert between currencies using cached rates
router.get('/convert/:from/:to/:amount', async (req, res) => {
const { from, to, amount } = req.params;
const fromCurrency = from.toUpperCase();
const toCurrency = to.toUpperCase();
await ensureRatesUpdated();
if (!exchangeRatesCache.USD.rates) {
return res.status(503).json({ error: 'Exchange rate data not yet available' });
}
// Check if currencies are supported
if (fromCurrency !== 'USD' && !exchangeRatesCache.USD.rates[fromCurrency]) {
return res.status(400).json({ error: `Currency '${fromCurrency}' not supported` });
}
if (toCurrency !== 'USD' && !exchangeRatesCache.USD.rates[toCurrency]) {
return res.status(400).json({ error: `Currency '${toCurrency}' not supported` });
}
try {
const numericAmount = parseFloat(amount);
if (isNaN(numericAmount)) {
return res.status(400).json({ error: 'Invalid amount' });
}
// Calculate conversion rate
const rate = calculateRate(fromCurrency, toCurrency);
const convertedAmount = numericAmount * rate;
res.json({
result: 'success',
from: fromCurrency,
to: toCurrency,
amount: numericAmount,
rate,
convertedAmount: parseFloat(convertedAmount.toFixed(4)),
lastUpdated: exchangeRatesCache.USD.lastUpdated,
nextUpdate: exchangeRatesCache.USD.nextUpdateTime
});
} catch (error) {
console.error('Conversion error:', error);
res.status(500).json({ error: 'Failed to convert currency' });
}
});
// Direct pair conversion (fallback to API if needed)
router.get('/pair/:from/:to/:amount', async (req, res) => {
const { from, to, amount } = req.params;
const fromCurrency = from.toUpperCase();
const toCurrency = to.toUpperCase();
// First try to use our cached rates
await ensureRatesUpdated();
if (exchangeRatesCache.USD.rates &&
(fromCurrency === 'USD' || exchangeRatesCache.USD.rates[fromCurrency]) &&
(toCurrency === 'USD' || exchangeRatesCache.USD.rates[toCurrency])) {
try {
const numericAmount = parseFloat(amount);
if (isNaN(numericAmount)) {
return res.status(400).json({ error: 'Invalid amount' });
}
// Calculate conversion rate
const rate = calculateRate(fromCurrency, toCurrency);
const convertedAmount = numericAmount * rate;
res.json({
result: 'success',
from: fromCurrency,
to: toCurrency,
amount: numericAmount,
rate,
convertedAmount: parseFloat(convertedAmount.toFixed(4)),
lastUpdated: exchangeRatesCache.USD.lastUpdated,
source: 'cache'
});
return;
} catch (error) {
console.error('Error using cached rates:', error);
// Fall through to API call
}
}
// If we can't use cached rates, call the API directly
if (!API_KEY) {
return res.status(503).json({ error: 'Exchange rate API key is not configured' });
}
try {
const response = await axios.get(`${BASE_URL}/${API_KEY}/pair/${fromCurrency}/${toCurrency}/${amount}`);
// Update our cache with the latest USD rates if it's time
ensureRatesUpdated();
res.json({
...response.data,
source: 'api'
});
} catch (error) {
res.status(500).json({ error: 'Failed to convert currency' });
}
});
module.exports = router;

View file

@ -0,0 +1,171 @@
{
"USD": {
"lastUpdated": "2025-04-07T19:30:40.700Z",
"rates": {
"USD": 1,
"AED": 3.6725,
"AFN": 71.3562,
"ALL": 90.1377,
"AMD": 391.1786,
"ANG": 1.79,
"AOA": 918.5748,
"ARS": 1075.88,
"AUD": 1.662,
"AWG": 1.79,
"AZN": 1.6999,
"BAM": 1.7805,
"BBD": 2,
"BDT": 121.3674,
"BGN": 1.7804,
"BHD": 0.376,
"BIF": 2960.3407,
"BMD": 1,
"BND": 1.3404,
"BOB": 6.8885,
"BRL": 5.7194,
"BSD": 1,
"BTN": 85.5708,
"BWP": 13.8733,
"BYN": 3.1354,
"BZD": 2,
"CAD": 1.423,
"CDF": 2899.0345,
"CHF": 0.8524,
"CLP": 960.6602,
"CNY": 7.2857,
"COP": 4203.747,
"CRC": 502.4294,
"CUP": 24,
"CVE": 100.3829,
"CZK": 22.9524,
"DJF": 177.721,
"DKK": 6.7905,
"DOP": 62.8304,
"DZD": 132.995,
"EGP": 50.8068,
"ERN": 15,
"ETB": 131.8684,
"EUR": 0.9102,
"FJD": 2.3154,
"FKP": 0.7757,
"FOK": 6.7935,
"GBP": 0.7751,
"GEL": 2.7559,
"GGP": 0.7757,
"GHS": 15.5067,
"GIP": 0.7757,
"GMD": 72.6441,
"GNF": 8570.968,
"GTQ": 7.6902,
"GYD": 209.8017,
"HKD": 7.7734,
"HNL": 25.5147,
"HRK": 6.8592,
"HTG": 130.7844,
"HUF": 369.6088,
"IDR": 16757.5574,
"ILS": 3.7448,
"IMP": 0.7757,
"INR": 85.5703,
"IQD": 1309.7144,
"IRR": 42008.9149,
"ISK": 131.125,
"JEP": 0.7757,
"JMD": 157.719,
"JOD": 0.709,
"JPY": 145.2473,
"KES": 129.2338,
"KGS": 86.8492,
"KHR": 3997.3556,
"KID": 1.6621,
"KMF": 447.8769,
"KRW": 1457.9608,
"KWD": 0.307,
"KYD": 0.8333,
"KZT": 510.3326,
"LAK": 21751.6772,
"LBP": 89500,
"LKR": 295.3817,
"LRD": 199.3433,
"LSL": 19.1915,
"LYD": 4.8358,
"MAD": 9.5166,
"MDL": 17.6443,
"MGA": 4654.3433,
"MKD": 55.9208,
"MMK": 2090.2388,
"MNT": 3479.6583,
"MOP": 8.0068,
"MRU": 39.8843,
"MUR": 44.5309,
"MVR": 15.4592,
"MWK": 1740.2553,
"MXN": 20.5515,
"MYR": 4.437,
"MZN": 63.6781,
"NAD": 19.1915,
"NGN": 1529.0612,
"NIO": 36.6793,
"NOK": 10.7699,
"NPR": 136.9132,
"NZD": 1.7956,
"OMR": 0.3845,
"PAB": 1,
"PEN": 3.6809,
"PGK": 4.0959,
"PHP": 57.3644,
"PKR": 280.7358,
"PLN": 3.8787,
"PYG": 8001.2022,
"QAR": 3.64,
"RON": 4.5185,
"RSD": 106.3911,
"RUB": 84.4536,
"RWF": 1422.8596,
"SAR": 3.75,
"SBD": 8.3385,
"SCR": 14.8196,
"SDG": 458.3047,
"SEK": 10.0072,
"SGD": 1.3405,
"SHP": 0.7757,
"SLE": 22.7181,
"SLL": 22718.051,
"SOS": 571.0444,
"SRD": 36.8241,
"SSP": 4519.748,
"STN": 22.3043,
"SYP": 12873.9497,
"SZL": 19.1915,
"THB": 34.3823,
"TJS": 10.9221,
"TMT": 3.4983,
"TND": 3.0571,
"TOP": 2.3833,
"TRY": 38.0295,
"TTD": 6.7342,
"TVD": 1.6621,
"TWD": 33.1309,
"TZS": 2647.2453,
"UAH": 41.1747,
"UGX": 3662.2001,
"UYU": 42.042,
"UZS": 12937.493,
"VES": 72.1856,
"VND": 25642.7185,
"VUV": 121.865,
"WST": 2.8015,
"XAF": 597.1692,
"XCD": 2.7,
"XCG": 1.79,
"XDR": 0.751,
"XOF": 597.1692,
"XPF": 108.6373,
"YER": 244.8828,
"ZAR": 19.2142,
"ZMW": 27.9801,
"ZWL": 6.7864
},
"nextUpdateTime": "2025-04-08T19:30:40.700Z"
}
}

48
api/server.js Normal file
View file

@ -0,0 +1,48 @@
const express = require("express");
const cors = require("cors");
const fs = require("fs");
const https = require("https");
const http = require("http");
const path = require("path");
// load environment variables from .env file
require('dotenv').config({ path: path.join(__dirname, '../.env') });
const status = require("./status/status");
const exchangeRate = require("./exchange-rate/exchange-rate");
const whois = require("./whois/whois");
const app = express();
const PORT = process.env.PORT || 2589;
const key = process.env.SSL_KEY_PATH || "/etc/letsencrypt/live/blahaj.tr/privkey.pem";
const cert = process.env.SSL_CERT_PATH || "/etc/letsencrypt/live/blahaj.tr/fullchain.pem";
app.use(cors());
app.use("/status", status);
app.use("/exchange-rate", exchangeRate);
app.use("/whois", whois);
// try to load certificates
try {
const sslOptions = {
key: fs.readFileSync(key),
cert: fs.readFileSync(cert),
};
https.createServer(sslOptions, app).listen(PORT, () => {
console.log(`API running at https://localhost:${PORT}`);
});
} catch (e) {
if (e.code === 'ENOENT') {
console.warn(`SSL certificate file(s) not found: ${e.path}`);
} else {
console.warn(`Error loading SSL certificates: ${e.message}`);
}
console.log("Starting server without SSL...");
// start http server as fallback
http.createServer(app).listen(PORT, () => {
console.log(`API running at http://localhost:${PORT}`);
});
}

136
api/status/status.js Normal file
View file

@ -0,0 +1,136 @@
const express = require("express");
const ping = require("ping");
const pm2 = require("pm2");
const router = express.Router();
const REMOTE_SERVERS = [
{ name: "blahaj.tr", host: "blahaj.tr" },
{ name: "xargana.com", host: "xargana.com" },
{ name: "home server", host: "31.223.36.208" }
];
const CHECK_INTERVAL = 5 * 1000;
let serversStatus = {};
REMOTE_SERVERS.forEach(server => {
serversStatus[server.name] = {
online: false,
lastChecked: null,
responseTime: null,
};
});
// Add PM2 services status object
let pm2ServicesStatus = {};
async function checkServers() {
try {
for (const server of REMOTE_SERVERS) {
try {
const res = await ping.promise.probe(server.host, {
timeout: 4, // Set a timeout of 4 seconds
});
serversStatus[server.name].online = res.alive;
serversStatus[server.name].responseTime = res.time;
} catch (error) {
console.error(`Error pinging ${server.host}:`, error);
serversStatus[server.name].online = false;
serversStatus[server.name].responseTime = null;
}
serversStatus[server.name].lastChecked = new Date().toISOString();
}
} catch (error) {
console.error("Error in checkServers function:", error);
}
}
async function checkPM2Services() {
return new Promise((resolve, reject) => {
pm2.connect(function(err) {
if (err) {
console.error('Error connecting to PM2:', err);
pm2.disconnect();
resolve();
return;
}
pm2.list((err, list) => {
if (err) {
console.error('Error getting PM2 process list:', err);
pm2.disconnect();
resolve();
return;
}
// Update PM2 services status
list.forEach(process => {
// Calculate uptime correctly - pm_uptime is a timestamp, not a duration
const uptimeMs = process.pm2_env.pm_uptime ?
Date.now() - process.pm2_env.pm_uptime :
null;
pm2ServicesStatus[process.name] = {
name: process.name,
id: process.pm_id,
status: process.pm2_env.status,
cpu: process.monit ? process.monit.cpu : null,
memory: process.monit ? process.monit.memory : null,
uptime: uptimeMs, // Store the uptime in milliseconds
restarts: process.pm2_env.restart_time,
lastChecked: new Date().toISOString()
};
});
pm2.disconnect();
resolve();
});
});
});
}
async function checkAll() {
try {
await checkServers();
await checkPM2Services();
} catch (error) {
console.error("Error in checkAll function:", error);
}
}
// Initial check with error handling
try {
checkAll();
} catch (error) {
console.error("Error during initial check:", error);
}
// Set interval with error handling
setInterval(() => {
try {
checkAll();
} catch (error) {
console.error("Error during scheduled check:", error);
}
}, CHECK_INTERVAL);
// Route with error handling
router.get("/", (req, res) => {
try {
res.json({
servers: serversStatus,
pm2Services: pm2ServicesStatus
});
} catch (error) {
console.error("Error sending status response:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Add a simple health check endpoint
router.get("/health", (req, res) => {
res.status(200).send("OK");
});
module.exports = router;

69
api/whois/whois.js Normal file
View file

@ -0,0 +1,69 @@
const express = require('express');
const whois = require('whois-json');
const router = express.Router();
// GET endpoint for WHOIS lookup
router.get('/:domain', async (req, res) => {
try {
const domain = req.params.domain;
if (!domain) {
return res.status(400).json({ error: 'Domain parameter is required' });
}
const result = await whois(domain);
// Format the response in a clean structure
const whoisData = {
domain: domain,
registrar: result.registrar || "Not available",
creationDate: result.creationDate ? new Date(result.creationDate).toISOString() : "Not available",
expirationDate: result.expirationDate ? new Date(result.expirationDate).toISOString() : "Not available",
nameServers: Array.isArray(result.nameServers) ? result.nameServers : (result.nameServers ? [result.nameServers] : ["Not available"]),
status: Array.isArray(result.status) ? result.status : (result.status ? [result.status] : ["Not available"]),
raw: result // Include the full raw data for advanced usage
};
res.json(whoisData);
} catch (error) {
console.error('WHOIS API Error:', error);
res.status(500).json({
error: 'Failed to fetch WHOIS information',
message: error.message
});
}
});
// POST endpoint for WHOIS lookup (alternative to GET with request body)
router.post('/', async (req, res) => {
try {
const domain = req.body.domain;
if (!domain) {
return res.status(400).json({ error: 'Domain parameter is required in request body' });
}
const result = await whois(domain);
// Format the response in a clean structure
const whoisData = {
domain: domain,
registrar: result.registrar || "Not available",
creationDate: result.creationDate ? new Date(result.creationDate).toISOString() : "Not available",
expirationDate: result.expirationDate ? new Date(result.expirationDate).toISOString() : "Not available",
nameServers: Array.isArray(result.nameServers) ? result.nameServers : (result.nameServers ? [result.nameServers] : ["Not available"]),
status: Array.isArray(result.status) ? result.status : (result.status ? [result.status] : ["Not available"]),
raw: result // Include the full raw data for advanced usage
};
res.json(whoisData);
} catch (error) {
console.error('WHOIS API Error:', error);
res.status(500).json({
error: 'Failed to fetch WHOIS information',
message: error.message
});
}
});
module.exports = router;