Verified Commit 936c5a4c authored by Armando Lüscher's avatar Armando Lüscher

Merge develop into autolanguage

parents 40856c29 4aff6813
#2+
# 2x+
## Podmins
* Can no longer access db/pull.php to test their pod, they can however get to a debug screen from the edit pod area
* Language is detected based on your homepage, edit your homepage to non-en if that is what you use
# DB
## DB
* Add development and release dates to masterversions table https://github.com/diasporg/Poduptime/issues/143
* Store full country name, store days monitored each pod
* Store detectedlanguage
* Migration needed see db/version.md
* DB migrations see db/version.md
## Cleanup
* Use the git API for release versions, check development releases on pods https://github.com/diasporg/Poduptime/issues/143
* Forbid access to files that should be cli only https://github.com/diasporg/Poduptime/issues/152
* Move from bower to yarn for packages
## End Users
* Show version and update in full view cleaner https://github.com/diasporg/Poduptime/issues/143
* Edit will send to email on file and be less delay, runner of site does not really have anyway to verify email address
* Filter and search on the columns of data
* Paginate the results so they fit per page
* Show time as human readable everywhere
# 2.2.0
......
......@@ -4,15 +4,14 @@ Poduptime is software to get live stats and data on listed Diaspora Pods.
Dependencies:
```
php7.2 php7.2-curl php7.2-pgsql php-geoip php7.2-cli php7.2-common php7.2-json php7.2-readline php7.2-mbstring php7.2-xml php-cgi git curl postgresql postgresql-contrib wget dnsutils bind9 npm nodejs nodejs-legacy composer
php7.2 php7.2-curl php7.2-pgsql php-geoip php7.2-cli php7.2-common php7.2-json php7.2-readline php7.2-mbstring php7.2-xml php-cgi git curl postgresql postgresql-contrib wget dnsutils bind9 npm nodejs nodejs-legacy composer yarn
```
To Install:
```
git clone https://github.com/diasporg/Poduptime.git
cd Poduptime
sudo npm install -g bower
bower install
yarn install
composer install
cp config.php.example config.php
```
......@@ -38,6 +37,7 @@ touch add.log in location you configured in config.php
run `db/pull.sh` manually or with cron to update your data
run `db/pull.sh debug` to debug output
run `db/pull.sh sqldebug` to debug sql
run `db/pull.sh develop` to run without email alerts to end users
run `db/pull.sh Check_System_Deleted` to re-check system deleted pods as needed
......
......@@ -19,12 +19,6 @@
"tests"
],
"dependencies": {
"bootstrap": "v4.0.0-alpha.6",
"jquery-ui": "^1.12.1",
"tablesorter": "jquery.tablesorter#^2.28.3",
"leaflet": "^1.0.2",
"leaflet.markercluster": "^1.0.0",
"facebox": "*",
"chart.js": "^2.4.0"
"facebox": "*"
}
}
{
"config": {
"platform": {
"php": "7.2"
}
},
"require": {
"noplanman/xec": "0.1.0",
"php" : "^7.2",
"gabordemooij/redbean": "^5.0",
"jaybizzle/crawler-detect" :"1.*",
"nesbot/carbon": "^1.31",
"commerceguys/enum": "^1.0",
"noplanman/xec": "0.1.0",
"jaybizzle/crawler-detect" :"1.*",
"twbs/bootstrap": "^4.1.0",
"patrickschur/language-detection": "^3.3"
},
"autoload": {
......
This diff is collapsed.
body {
padding-top: 54px;
}
.sub-header {
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.sidebar {
background-color: #f5f5f5;
background-color: #fff;
border-right: 1px solid #eee;
bottom: 0;
display: block;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
padding: 10px;
position: fixed;
top: 54px;
z-index: 1000;
top: 56px;
}
.sidebar .nav {
margin-bottom: 20px;
......@@ -26,7 +21,6 @@ body {
.main {
padding: 20px;
}
.placeholders {
margin-bottom: 30px;
text-align: center;
......@@ -42,59 +36,153 @@ body {
display: inline-block;
}
.rating {
font-size:9px;
font-size:9px;
}
.tfont {
font-size:12px;
font-size:12px;
}
.smlogo {
width: 16px;
height: 16px;
background: url('/images/smlogo.png') 0 0;
display: inline-block;
margin: 0 2px;
width: 16px;
height: 16px;
background: url('/images/smlogo.png') 0 0;
display: inline-block;
margin: 0 2px;
}
.smlogo-twitter {
background-position: 0 -776px;
background-position: 0 -776px;
}
.smlogo-facebook {
background-position: 0 -824px;
background-position: 0 -824px;
}
.smlogo-tumblr {
background-position: 0 -792px;
background-position: 0 -792px;
}
.smlogo-wordpress {
background-position: 0 -656px;
background-position: 0 -656px;
}
.smlogo-xmpp {
background: none;
background: none;
}
.smlogo-xmpp img {
vertical-align: unset;
vertical-align: unset;
}
#map {
height: 580px;
}
#map { height: 580px; }
.mycluster {
width: 35px;
height: 35px;
background-color: blue;
text-align: left;
font-size: 17px;
background: url('/bower_components/leaflet/dist/images/marker-icon-2x.png') repeat-y right bottom;
background-size: 25px 37px;
width: 35px;
height: 35px;
background-color: blue;
text-align: left;
font-size: 17px;
background: url('/bower_components/leaflet/dist/images/marker-icon-2x.png') repeat-y right bottom;
background-size: 25px 37px;
}
.icon {
color: red;
color: red;
}
table {
empty-cells: show;
empty-cells: show;
}
#facebox {
z-index: 1000 !important;
z-index: 1000 !important;
}
#facebox .content {
width: 800px !important;
width: 800px !important;
}
.tablesorter-pager .btn-group-sm .btn {
font-size: 1.2em; /* make pager arrows more visible */
}
.tablesorter-filter-row td {
background: #eee;
line-height: normal;
text-align: center; /* center the input */
-webkit-transition: line-height 0.1s ease;
-moz-transition: line-height 0.1s ease;
-o-transition: line-height 0.1s ease;
transition: line-height 0.1s ease;
}
.tablesorter-filter-row .disabled {
opacity: 0.5;
filter: alpha(opacity=50);
cursor: not-allowed;
}
.tablesorter-filter-row.hideme td {
/*** *********************************************** ***/
/*** change this padding to modify the thickness ***/
/*** of the closed filter row (height = padding x 2) ***/
padding: 1px;
/*** *********************************************** ***/
margin: 0;
line-height: 0;
cursor: pointer;
}
.tablesorter-filter-row.hideme * {
height: 1px;
min-height: 0;
border: 0;
padding: 0;
margin: 0;
/* don't use visibility: hidden because it disables tabbing */
opacity: 0;
filter: alpha(opacity=0);
}
/* filters */
.tablesorter-filter {
width: 98%;
height: inherit;
margin: 1px;
padding: 1px;
background-color: #fff;
border: 1px solid #bbb;
color: #333;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: height 0.1s ease;
-moz-transition: height 0.1s ease;
-o-transition: height 0.1s ease;
transition: height 0.1s ease;
}
.pagination {
display: inline-block;
color: black;
float: left;
padding: 8px 16px;
margin: 0 4px;
text-decoration: none;
transition: background-color .3s;
border: 1px solid #ddd;
cursor: pointer;
}
.pagination.disabled {
color: #f0f0f0;
}
.pagedisplay {
display: inline-block;
color: black;
float: left;
padding: 8px 16px !important;
margin: 0 4px;
text-decoration: none;
transition: background-color .3s;
border: 1px solid #ddd;
cursor: pointer;
}
.tablesorter-header {
background-size: 10px 15px !important;
background-size: 10px 15px !important;
}
.modal-xlg {
max-width: 92%;
}
......@@ -64,7 +64,7 @@ foreach ($pods as $pod) {
}
echo <<<EOF
<form action="edit.php" method="get">
<form action="/?edit" method="get">
<input type="hidden" name="domain" value="{$_domain}">
<input type="hidden" name="token" value="{$uuid}">
<label>Email <input type="text" size="20" name="email"></label><br>
......@@ -82,8 +82,14 @@ EOF;
}
}
if ($infos = json_decode(file_get_contents('https://' . $_domain . '/.well-known/nodeinfo'), true)) {
$link = max($infos['links'])['href'];
} else {
$link= 'https://' . $_domain . '/.well-known/nodeinfo';
}
$chss = curl_init();
curl_setopt($chss, CURLOPT_URL, 'https://' . $_domain . '/nodeinfo/1.0');
curl_setopt($chss, CURLOPT_URL, $link);
curl_setopt($chss, CURLOPT_POST, 0);
curl_setopt($chss, CURLOPT_HEADER, 0);
curl_setopt($chss, CURLOPT_CONNECTTIMEOUT, 5);
......@@ -118,19 +124,17 @@ if (stristr($outputssl, 'openRegistrations')) {
$message_lines = [
'https://' . $_SERVER['HTTP_HOST'],
'Pod: https://' . $_SERVER['HTTP_HOST'] . '/db/pull.php?debug=1&domain=' . $_domain,
'',
'Your pod will not show up right away, as it needs to pass a few checks first.',
'Your pod ' . $_domain . ' will not show up right away, as it needs to pass a few checks first.',
'Give it a few hours!',
];
@mail($to, $subject, implode("\r\n", $message_lines), implode("\r\n", $headers));
}
echo 'Data successfully inserted! Your pod will be reviewed and live on the list in a few hours!';
echo 'Data successfully inserted! Your pod will be checked and live on the list in a few hours!';
} else {
$log->lwrite('Could not validate your pod, check your setup! ' . $_domain);
echo 'Could not validate your pod, check your setup!<br>Take a look at <a href="https://' . $_domain . '/nodeinfo/1.0">your /nodeinfo</a>';
echo 'Could not validate your pod, check your setup!<br>Take a look at <a href="' . $link . '">your /nodeinfo</a>';
}
$log->lclose();
<?php
if (PHP_SAPI !== 'cli') {
header('HTTP/1.0 403 Forbidden');
exit;
}
require_once __DIR__ . '/../config.php';
$keep = (60 * 60 * 6) * 1;
......
......@@ -120,4 +120,25 @@ if ('save' === $_action) {
<input type="hidden" name="token" value="<?php echo $_token; ?>">
<input type="submit" name="action" value="unpause">
</form>
<button type="button" class="openBtn" value="<?php echo $_domain; ?>">Do a debug test pull of your pod</button>
<!-- Modal -->
<div class="modal fade" id="podpull" role="dialog">
<div class="modal-dialog modal-xlg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Pod debug data</h4>
</div>
<div class="modal-body">
Loading.....
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<?php
......@@ -38,16 +38,16 @@ if ($_email) {
$subject = 'Temporary edit key for ' . $_SERVER['HTTP_HOST'];
$headers[] = 'Bcc: ' . $adminemail;
$expire = time() + 2700;
$output = 'Link sent to your email';
$output = 'Link sent to your email.';
} elseif (!$pod['email']) {
die('domain is registered but no email associated, to add an email use the add a pod feature');
die('Domain is registered but no email associated, to add an email use the add a pod feature.');
} else {
$to = $adminemail;
$subject = 'FORWARD REQUEST: Temporary edit key for ' . $_SERVER['HTTP_HOST'];
$message_lines[] = 'User trying to edit pod without email address.';
$to = $pod['email'];
$subject = 'Temporary edit key for ' . $_SERVER['HTTP_HOST'];
$message_lines[] = 'Looks like you did not enter your email address, be sure to update it if you forgot the one we have for you.';
$message_lines[] = 'Email found: ' . $pod['email'];
$expire = time() + 9700;
$output = 'Link sent to administrator to review and verify, if approved they will forward the edit key to you.';
$expire = time() + 2700;
$output = 'Link sent to email we have for this pod on file.';
}
try {
......
ALTER TABLE masterversions ADD devlastcommit timestamp;
ALTER TABLE masterversions ADD releasedate timestamp;
ALTER TABLE pods ADD daysmonitored int, ADD countryname text;
ALTER TABLE pods ADD detectedlanguage text;
<?php
if (PHP_SAPI !== 'cli') {
header('HTTP/1.0 403 Forbidden');
exit;
}
use RedBeanPHP\R;
require_once __DIR__ . '/../vendor/autoload.php';
......
<?php
if (php_sapi_name() == "cli") {
$json = json_decode(file_get_contents('https://the-federation.info/pods.json'));
if ($json) {
foreach ($json->pods ?? [] as $poddata) {
echo exec("php-cgi add.php domain={$poddata->host}") . "\r\n";
if (PHP_SAPI !== 'cli') {
header('HTTP/1.0 403 Forbidden');
exit;
}
use RedBeanPHP\R;
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../config.php';
define('PODUPTIME', microtime(true));
// Set up global DB connection.
R::setup("pgsql:host={$pghost};dbname={$pgdb}", $pguser, $pgpass, true);
R::testConnection() || die('Error in DB connection');
R::usePartialBeans(true);
try {
$sql = '
SELECT domain, status
FROM pods
';
$pods = R::getAll($sql);
} catch (\RedBeanPHP\RedException $e) {
die('Error in SQL query: ' . $e->getMessage());
}
//get all existing pod domains
$existingpods = array_column($pods, 'domain');
$foundpods = [];
//pulling all nodes for now
if ($pods = json_decode(file_get_contents('https://the-federation.info/graphql?query=%7Bnodes%7Bhost%20platform%7Bname%7Dprotocols%7Bname%7D%7D%7D&raw'), true)) {
foreach ($pods['data']['nodes'] ?? [] as $poddata) {
$protocols = array_column($poddata['protocols'] ?? [], 'name');
//limiting to diaspora compatible for now
if (in_array('diaspora', $protocols, true)) {
$foundpods[] = $poddata['host'];
}
}
} else {
header('HTTP/1.0 403 Forbidden');
}
if ($pods = json_decode(file_get_contents('https://diasp.org/pods.json'), true)) {
foreach ($pods ?? [] as $poddata) {
$foundpods[] = $poddata['host'];
}
}
$results = array_diff($foundpods, $existingpods);
foreach ($results as $result) {
echo exec("php-cgi add.php domain={$result}") . "\r\n";
}
<?php
//* Copyright (c) 2017, David Morley. This file is licensed under the Affero General Public License version 3 or later. See the COPYRIGHT file. */
if (PHP_SAPI !== 'cli') {
header('HTTP/1.0 403 Forbidden');
exit;
}
use RedBeanPHP\R;
require_once __DIR__ . '/../vendor/autoload.php';
......@@ -14,32 +19,70 @@ R::testConnection() || die('Error in DB connection');
R::usePartialBeans(true);
$softwares = [
'diaspora' => ['url' => 'https://raw.githubusercontent.com/diaspora/diaspora/master/config/defaults.yml', 'regex' => '/number:.*"(.*)"/'],
'friendica' => ['url' => 'https://raw.githubusercontent.com/friendica/friendica/master/boot.php', 'regex' => '/define.*\'FRIENDICA_VERSION\'.*\'(.*)\'/'],
'redmatrix' => ['url' => 'https://raw.githubusercontent.com/redmatrix/hubzilla/master/boot.php', 'regex' => '/define.*\'STD_VERSION\'.*\'(.*)\'/'],
'socialhome' => ['url' => 'https://raw.githubusercontent.com/jaywink/socialhome/master/socialhome/__init__.py', 'regex' => '/__version__ =.*"(.*)"/'],
'social-relay' => ['url' => 'https://raw.githubusercontent.com/jaywink/social-relay/master/social_relay/config.py', 'regex' => '/VERSION.*"(.*)"/'],
'ganggo' => ['url' => 'https://raw.githubusercontent.com/ganggo/ganggo/master/package.json', 'regex' => '/"version": "(.*)"/'],
'diaspora' => ['repo' => 'diaspora/diaspora', 'gitsite' => 'api.github.com', 'gittype' => 'github', 'devbranch' => 'develop'],
'friendica' => ['repo' => 'friendica/friendica', 'gitsite' => 'api.github.com', 'gittype' => 'github', 'devbranch' => 'develop'],
'hubzilla' => ['repo' => 'hubzilla%2fcore', 'gitsite' => 'framagit.org', 'gittype' => 'gitlab', 'devbranch' => 'dev'],
'pleroma' => ['repo' => 'pleroma%2fpleroma', 'gitsite' => 'git.pleroma.social', 'gittype' => 'gitlab', 'devbranch' => 'develop'],
'socialhome' => ['repo' => 'jaywink/socialhome', 'gitsite' => 'api.github.com', 'gittype' => 'github', 'devbranch' => ''],
'social-relay' => ['repo' => 'jaywink/social-relay', 'gitsite' => 'api.github.com', 'gittype' => 'github', 'devbranch' => ''],
'ganggo' => ['repo' => 'ganggo/ganggo', 'gitsite' => 'api.github.com', 'gittype' => 'github', 'devbranch' => ''],
];
$opts = [
'http' => ['method' => 'GET', 'header' => ['User-Agent: Poduptime']]
];
foreach ($softwares as $software => $details) {
$mv = curl_init();
curl_setopt($mv, CURLOPT_URL, $details['url']);
curl_setopt($mv, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($mv, CURLOPT_RETURNTRANSFER, 1);
$outputmv = curl_exec($mv);
curl_close($mv);
if ($masterversion = preg_match($details['regex'], $outputmv, $version) ? $version[1] : '') {
try {
$m = R::dispense('masterversions');
$m['software'] = $software;
$m['version'] = $masterversion;
R::store($m);
} catch (\RedBeanPHP\RedException $e) {
die('Error in SQL query: ' . $e->getMessage());
if ($details['gittype'] == 'github') {
$context = stream_context_create($opts);
$releasejson = json_decode(file_get_contents('https://' . $details["gitsite"] . '/repos/' . $details["repo"] . '/releases/latest', false, $context));
if ($details["devbranch"]) {
$commitjson = json_decode(file_get_contents('https://' . $details["gitsite"] . '/repos/' . $details["repo"] . '/commits/' . $details["devbranch"], false, $context));
} else {
$commitjson = '';
}
if ($masterversion = $releasejson->tag_name ? str_replace('v', '', $releasejson->tag_name) : '') {
try {
$m = R::dispense('masterversions');
$m['software'] = $software;
$m['version'] = $masterversion;
if ($releasedate = $releasejson ? $releasejson->published_at : '') {
$m['releasedate'] = $releasedate;
}
if ($devlastcommit = $commitjson ? $commitjson->commit->author->date : '') {
$m['devlastcommit'] = $devlastcommit;
}
R::store($m);
} catch (\RedBeanPHP\RedException $e) {
die('Error in SQL query: ' . $e->getMessage());
}
}
} elseif ($details['gittype'] == 'gitlab') {
$context = stream_context_create($opts);
$releasejson = json_decode(file_get_contents('https://' . $details["gitsite"] . '/api/v4/projects/' . $details["repo"] . '/repository/tags', false, $context));
if ($details["devbranch"]) {
$commitjson = json_decode(file_get_contents('https://' . $details["gitsite"] . '/api/v4/projects/' . $details["repo"] . '/repository/commits/' . $details["devbranch"], false, $context));
} else {
$commitjson = '';
}
if ($masterversion = $releasejson[0]->name ? str_replace('v', '', $releasejson[0]->name) : '') {
try {
$m = R::dispense('masterversions');
$m['software'] = $software;
$m['version'] = $masterversion;
if ($releasedate = $releasejson[0] ? $releasejson[0]->commit->created_at : '') {
$m['releasedate'] = $releasedate;
}
if ($devlastcommit = $commitjson ? $commitjson->created_at : '') {
$m['devlastcommit'] = $devlastcommit;
}
R::store($m);
} catch (\RedBeanPHP\RedException $e) {
die('Error in SQL query: ' . $e->getMessage());
}
}
}
}
printf('%s:%s ', $software, $masterversion ?: 'n/a');
printf('%s:%s:%s ', $software, $masterversion, $devlastcommit ?: 'n/a');
}
<?php
//* Copyright (c) 2011, David Morley. This file is licensed under the Affero General Public License version 3 or later. See the COPYRIGHT file. */
if ($_SERVER['SERVER_ADDR'] !== $_SERVER['REMOTE_ADDR']) {
header('HTTP/1.0 403 Forbidden');
exit;
}
use RedBeanPHP\R;
use LanguageDetection\Language;
$debug = isset($_GET['debug']) || (isset($argv) && in_array('debug', $argv, true));
$newline = PHP_SAPI === 'cli' ? "\n" : '<br>';
$debug = isset($_GET['debug']) || (isset($argv) && in_array('debug', $argv, true));
$sqldebug = isset($_GET['sqldebug']) || (isset($argv) && in_array('sqldebug', $argv, true));
$write = !(isset($_GET['nowrite']) || (isset($argv) && in_array('nowrite', $argv, true)));
$newline = PHP_SAPI === 'cli' ? "\n\n" : '<br><br>';
$_domain = $_GET['domain'] ?? null;
......@@ -19,6 +26,7 @@ define('PODUPTIME', microtime(true));
// Set up global DB connection.
R::setup("pgsql:host={$pghost};dbname={$pgdb}", $pguser, $pgpass, true);
$sqldebug && R::fancyDebug(true);
R::testConnection() || die('Error in DB connection');
R::usePartialBeans(true);
......@@ -94,8 +102,14 @@ foreach ($pods as $pod) {
_debug('Detected Language', $detectedlanguage);
}
if ($infos = json_decode(file_get_contents('https://' . $domain . '/.well-known/nodeinfo'), true)) {
$link = max($infos['links'])['href'];
} else {
$link = 'https://' . $domain . '/nodeinfo/1.0';
}
$chss = curl_init();
curl_setopt($chss, CURLOPT_URL, 'https://' . $domain . '/nodeinfo/1.0');
curl_setopt($chss, CURLOPT_URL, $link);
curl_setopt($chss, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($chss, CURLOPT_TIMEOUT, 30);
curl_setopt($chss, CURLOPT_RETURNTRANSFER, 1);
......@@ -120,8 +134,8 @@ foreach ($pods as $pod) {
$jsonssl = json_decode($outputssl);
$xdver = $jsonssl->software->version ?? 0;
$dverr = explode('-', trim($xdver));
$shortversion = $dverr[0];
preg_match_all('((?:\d(.|-)?)+(\.|-)\d+\.*)', $xdver, $dverr);
$shortversion = $dverr[0][0];
$signup = ($jsonssl->openRegistrations ?? false) === true;
$softwarename = $jsonssl->software->name ?? 'unknown';
$name = $jsonssl->metadata->nodeName ?? $softwarename;
......@@ -142,7 +156,7 @@ foreach ($pods as $pod) {
$service_twitter = in_array('twitter', $jsonssl->services->outbound, true);
$service_tumblr = in_array('tumblr', $jsonssl->services->outbound, true);
$service_wordpress = in_array('wordpress', $jsonssl->services->outbound, true);
}
}
}
if ($jsonssl !== null) {
......@@ -156,11 +170,15 @@ foreach ($pods as $pod) {
$c['local_posts'] = $local_posts;
$c['comment_counts'] = $comment_counts;
$c['shortversion'] = $shortversion;
R::store($c);
if ($write) {
R::store($c);
} else {
echo $c;
}
} catch (\RedBeanPHP\RedException $e) {
die('Error in SQL query: ' . $e->getMessage());
}
$status = PodStatus::Up;
}
......@@ -173,7 +191,11 @@ foreach ($pods as $pod) {
$c['online'] = false;
$c['error'] = $outputsslerror;
$c['latency'] = $latency;
R::store($c);
if ($write) {
R::store($c);
} else {
echo $c;
}
} catch (\RedBeanPHP\RedException $e) {
die('Error in SQL query: ' . $e->getMessage());
}
......@@ -209,17 +231,19 @@ foreach ($pods as $pod) {
$location = geoip_record_by_name($ip);
_debug('Location', $location, true);
$country = !empty($location['country_code']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['country_code']) : null;
$city = !empty($location['city']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['city']) : null;
$state = !empty($location['region']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['region']) : null;
$lat = !empty($location['latitude']) ? $location['latitude'] : 0;
$long = !empty($location['longitude']) ? $location['longitude'] : 0;
$countryname = !empty($location['country_name']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['country_name']) : null;
$country = !empty($location['country_code']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['country_code']) : null;
$city = !empty($location['city']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['city']) : null;
$state = !empty($location['region']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['region']) : null;
$lat = !empty($location['latitude']) ? $location['latitude'] : 0;
$long = !empty($location['longitude']) ? $location['longitude'] : 0;
echo $newline;
$statslastdate = date('Y-m-d H:i:s');
$diff = (new DateTime())->diff(new DateTime($dateadded));
$months = $diff->m + ($diff->y * 12);
$days = $diff->days;
try {
$checks = R::getRow('
......@@ -239,19 +263,47 @@ foreach ($pods as $pod) {
_debug('Uptime', $uptime);
try {