Merge pull request #16 from cowingtonpost1/cow

restructure project and fix issue #12
This commit is contained in:
rottenwheel 2025-03-16 21:44:42 +00:00 committed by GitHub
commit eadc8d3026
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 534 additions and 734 deletions

11
.gitignore vendored
View file

@ -6,13 +6,14 @@
/node_modules/ /node_modules/
# Coingecko export data # Coingecko export data
/coingecko.json /storage/coingecko.json
/coingecko-original.json /storage/coingecko-original.json
/coingecko-currencies.json /storage/coingecko-currencies.json
/storage/cache/
# Secret files # Secret files
/secrets.php /secrets.php
# Compiled files # Compiled files
/js/ /public/js/
/css/ /public/css/

View file

@ -98,7 +98,7 @@ For an example, see [kuno.anne.media](https://kuno.anne.media/donate/onml/).
npm run build npm run build
``` ```
4. Point your web server to the repository directory. 4. Point your web server to the public directory inside this repository.
## Configuration ## Configuration

View file

@ -1,11 +1,13 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
class FileCache { class FileCache
{
private $cacheDir; private $cacheDir;
private $defaultExpiration; private $defaultExpiration;
public function __construct($cacheDir = 'cache', $defaultExpiration = 60) { public function __construct($cacheDir = '../storage/cache', $defaultExpiration = 60)
{
$this->cacheDir = $cacheDir; $this->cacheDir = $cacheDir;
$this->defaultExpiration = $defaultExpiration; $this->defaultExpiration = $defaultExpiration;
// Create the cache directory if it doesn't exist // Create the cache directory if it doesn't exist
@ -57,4 +59,4 @@ class FileCache {
} }
return $data; return $data;
} }
} }

1160
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,22 +6,24 @@ date_default_timezone_set('Europe/Berlin');
// Define currencies that should *not* be included in the list // Define currencies that should *not* be included in the list
$excludedCurrencies = ['bits', 'sats']; $excludedCurrencies = ['bits', 'sats'];
require_once __DIR__ . '/cache.php'; require_once '../lib/cache.php';
// Fetch JSON data from a file and decode it // Fetch JSON data from a file and decode it
function fetchJson($filename) { function fetchJson($filename)
{
return json_decode(file_get_contents($filename), true); return json_decode(file_get_contents($filename), true);
} }
function fetchCache(string $key, string $url, FileCache $cache) function fetchCache(string $key, string $url, FileCache $cache)
{ {
return $cache->getOrSet($key, function() use ($url) { return $cache->getOrSet($key, function () use ($url) {
return makeApiRequest($url); return makeApiRequest($url);
}, 60); }, 60);
} }
// Make an API request and return the JSON response // Make an API request and return the JSON response
function makeApiRequest($url) { function makeApiRequest($url)
{
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$json = curl_exec($ch); $json = curl_exec($ch);
@ -36,15 +38,19 @@ function makeApiRequest($url) {
} }
// Get CoinGecko key URL parameter // Get CoinGecko key URL parameter
function getCoinGeckoApiUrl($path, $params = []) { function getCoinGeckoApiUrl($path, $params = [])
$secrets = require_once 'secrets.php'; {
$key = $secrets['coingecko_api_key']; $secrets = require '../secrets.php';
$demo = $secrets['coingecko_key_is_demo']; $demo = $secrets['coingecko_key_is_demo'];
$paramName = $demo ? 'x_cg_demo_api_key' : 'x_cg_pro_api_key'; if ($secrets['use_api_key'] === true) {
$key = $secrets['coingecko_api_key'];
$paramName = $demo ? 'x_cg_demo_api_key' : 'x_cg_pro_api_key';
$params[$paramName] = $key;
}
$baseUrl = $demo ? "https://api.coingecko.com/api/v3/" : "https://pro-api.coingecko.com/api/v3/"; $baseUrl = $demo ? "https://api.coingecko.com/api/v3/" : "https://pro-api.coingecko.com/api/v3/";
$params[$paramName] = $key;
$url = $baseUrl . $path; $url = $baseUrl . $path;
if (!empty($params)) { if (!empty($params)) {
@ -58,8 +64,9 @@ $currentTime = time();
// Fetch list of available currencies from CoinGecko API // Fetch list of available currencies from CoinGecko API
// Available currencies are cached for 24 hours // Available currencies are cached for 24 hours
function fetchAvailableCurrencies() { function fetchAvailableCurrencies()
$cacheFile = 'coingecko-currencies.json'; {
$cacheFile = dirname(__DIR__) . '/storage/coingecko-currencies.json';
$cacheTime = 86400; $cacheTime = 86400;
// Return cached data if it exists and is less than 24 hours old // Return cached data if it exists and is less than 24 hours old
@ -75,21 +82,23 @@ function fetchAvailableCurrencies() {
return $data; return $data;
} }
return null; return $data;
} }
// Fetch currency data from CoinGecko API // Fetch currency data from CoinGecko API
function fetchCurrencyData($currencies) { function fetchCurrencyData($currencies)
$cache = new FileCache(__DIR__ . '/cache'); {
$cache = new FileCache(dirname(__DIR__) . '/storage/cache');
$apiUrl = getCoinGeckoApiUrl('simple/price', ['ids' => 'monero', 'vs_currencies' => implode(',', array_map('strtolower', $currencies))]); $apiUrl = getCoinGeckoApiUrl('simple/price', ['ids' => 'monero', 'vs_currencies' => implode(',', array_map('strtolower', $currencies))]);
return fetchCache('currency_data', $apiUrl, $cache); return fetchCache('currency_data', $apiUrl, $cache);
} }
$currencyFile = 'coingecko.json'; $currencyFile = dirname(__DIR__) . '/storage/coingecko.json';
$originalFile = 'coingecko-original.json'; $originalFile = dirname(__DIR__) . '/storage/coingecko-original.json';
// Function to process currency data // Function to process currency data
function processCurrencyData($availableCurrencies, $previousData, $currentTime, $excludedCurrencies) { function processCurrencyData($availableCurrencies, $previousData, $currentTime, $excludedCurrencies)
{
// Remove excluded currencies // Remove excluded currencies
$availableCurrencies = array_diff($availableCurrencies, $excludedCurrencies); $availableCurrencies = array_diff($availableCurrencies, $excludedCurrencies);
$currencies = array_map('strtoupper', $availableCurrencies); $currencies = array_map('strtoupper', $availableCurrencies);
@ -116,7 +125,7 @@ function processCurrencyData($availableCurrencies, $previousData, $currentTime,
return null; return null;
} }
$previousData = fetchJson($currencyFile); $previousData = file_exists($currencyFile) ? fetchJson($currencyFile) : ["time" => 0];
$output = $previousData; $output = $previousData;
// Check if five seconds have passed since the last update // Check if five seconds have passed since the last update
@ -135,4 +144,4 @@ if (($currentTime - $previousData['time']) >= 5) {
// Output the data // Output the data
header('Content-Type: application/json'); header('Content-Type: application/json');
echo json_encode($output, JSON_PRETTY_PRINT); echo json_encode($output, JSON_PRETTY_PRINT);

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

Before

Width:  |  Height:  |  Size: 952 B

After

Width:  |  Height:  |  Size: 952 B

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -4,23 +4,24 @@ header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false); header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache"); header("Pragma: no-cache");
$COINGECKO_JSON_PATH = dirname(__DIR__) . '/storage/coingecko.json';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://"; $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
$currentUrl = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $currentUrl = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$parentUrl = dirname($currentUrl); $parentUrl = dirname($currentUrl);
// Get currency data from JSON // Get currency data from JSON
if (!file_exists('coingecko.json')) { if (!file_exists($COINGECKO_JSON_PATH)) {
// Special case: First run. `php coingecko.php`;
exec('php coingecko.php');
sleep(1); sleep(1);
} }
$api_cg = json_decode(file_get_contents('coingecko.json'), true); $api_cg = json_decode(file_get_contents($COINGECKO_JSON_PATH), true);
// Configuration file // Configuration file
$config = []; $config = [];
if (file_exists('config.php')) { if (file_exists(dirname(__DIR__) . '/config.php')) {
$config = require_once 'config.php'; $config = require_once '../config.php';
} }
$display_servers_guru = isset($config['servers_guru']) && $config['servers_guru'] === true; $display_servers_guru = isset($config['servers_guru']) && $config['servers_guru'] === true;
@ -71,8 +72,8 @@ $language_code = explode('-', $lang)[0];
$language_files = ["en", $language_code, $lang]; $language_files = ["en", $language_code, $lang];
foreach ($language_files as $language_file) { foreach ($language_files as $language_file) {
if (file_exists('lang/' . $language_file . '.php')) { if (file_exists('../lang/' . $language_file . '.php')) {
require_once 'lang/' . $language_file . '.php'; require_once '../lang/' . $language_file . '.php';
} }
} }
@ -106,5 +107,4 @@ foreach (array_reverse($preferred_currencies) as $currency) {
} }
// Output the HTML // Output the HTML
require_once 'templates/index.php'; require_once '../templates/index.php';
?>

View file

@ -1,6 +1,6 @@
<?php <?php
return [ return [
'coingecko_api_key' => 'CG-xxxx', 'coingecko_api_key' => 'CG-xxxx',
'coingecko_key_is_demo' => true, 'coingecko_key_is_demo' => true,
]; 'use_api_key' => false,
];

0
storage/.gitkeep Normal file
View file

View file

@ -1,7 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="<?php echo $lang_meta; ?>" <?php if ($rtl) { <html lang="<?php echo $lang_meta ?>" <?= $rtl ? 'dir="rtl"' : '' ?>>
echo 'dir="rtl"';
} ?>>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@ -33,7 +31,7 @@
<link rel="apple-touch-icon-precomposed" sizes="32x32" href="img/favicon-32x32.png" /> <link rel="apple-touch-icon-precomposed" sizes="32x32" href="img/favicon-32x32.png" />
<link rel="apple-touch-icon-precomposed" sizes="16x16" href="img/favicon-16x16.png" /> <link rel="apple-touch-icon-precomposed" sizes="16x16" href="img/favicon-16x16.png" />
<link rel="apple-touch-startup-image" href="img/favicon-196x196.png" /> <link rel="apple-touch-startup-image" href="img/favicon-196x196.png" />
<link rel="icon" type="image/png" href="img/favicon-196x196.png" sizes="196x196" /> <link rel="icon" type="image/png" href="img/favicon-196x196.png" sizes="196x196" />
<link rel="icon" type="image/png" href="img/favicon-152x152.png" sizes="152x152" /> <link rel="icon" type="image/png" href="img/favicon-152x152.png" sizes="152x152" />
<link rel="icon" type="image/png" href="img/favicon-144x144.png" sizes="144x144" /> <link rel="icon" type="image/png" href="img/favicon-144x144.png" sizes="144x144" />

View file

@ -10,7 +10,7 @@ module.exports = {
entry: './src/js/main.js', entry: './src/js/main.js',
output: { output: {
filename: 'main.js', filename: 'main.js',
path: path.resolve(__dirname, 'js'), path: path.resolve(__dirname, 'public/js'),
}, },
module: { module: {
rules: [ rules: [
@ -29,11 +29,11 @@ module.exports = {
}), }),
new PurgeCSSPlugin({ new PurgeCSSPlugin({
paths: glob.sync([ paths: glob.sync([
path.join(__dirname, 'index.php'), path.join(__dirname, 'public/index.php'),
path.join(__dirname, 'src/js/*.js'), path.join(__dirname, 'src/js/*.js'),
path.join(__dirname, 'templates/*.php'), path.join(__dirname, 'templates/*.php'),
]), ]),
safelist: ['tooltip', 'fade', 'show', 'bs-tooltip-top', 'tooltip-inner', 'tooltip-arrow', 'btn-equals', 'btn-arrow', 'alert', 'alert-warning', 'donation-qr', 'donation-qr-toggle', 'donation-qr-container'] safelist: ['tooltip', 'fade', 'show', 'bs-tooltip-top', 'tooltip-inner', 'tooltip-arrow', 'btn-equals', 'btn-arrow', 'alert', 'alert-warning', 'donation-qr', 'donation-qr-toggle', 'donation-qr-container']
}) })
] ]
}; };