Added files
This commit is contained in:
7
.env
Normal file
7
.env
Normal file
@@ -0,0 +1,7 @@
|
||||
APIHOST=www.example.com
|
||||
APIURL=/track/api.php
|
||||
# The APIKEY from the things network
|
||||
APIKEY=xxx
|
||||
DBNAME=track
|
||||
DBUSER=track
|
||||
DBPASS=track
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.idea
|
||||
.env.*
|
||||
34
api.php
Normal file
34
api.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
const INCLUDER = true;
|
||||
require_once __DIR__ . '/lib.php';
|
||||
|
||||
$cli = strtolower(php_sapi_name()) === 'cli';
|
||||
|
||||
$headers = getallheaders();
|
||||
$raw = file_get_contents('php://input');
|
||||
|
||||
$apikey = Config::apiKey();
|
||||
|
||||
$allowed = $headers['X-Api-Key'] === $apikey || $_GET['X-Api-Key'] === $apikey || $_POST['X-Api-Key'] === $apikey;
|
||||
|
||||
if ($allowed) {
|
||||
try {
|
||||
if ($_GET['type'] == 'uplink' && preg_match("!thethings\.network$!i", $headers["X-Tts-Domain"])) {
|
||||
$process = new UplinkData($raw, $headers);
|
||||
$process->write();
|
||||
Logging::info('uplink', $headers, $raw);
|
||||
} else {
|
||||
db_logging($_GET['type'] ?? 'error', $raw, $headers);
|
||||
Logging::info($_GET['type'] || 'error', $headers);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log($e->getMessage() . ' - see also /tmp/track-error.log');
|
||||
Logging::info('error', $e->getMessage(), $headers, $_GET, $_POST, json_decode($raw));
|
||||
}
|
||||
} else {
|
||||
Logging::info('error', "Not allowed", $headers, $_GET, $_POST, json_decode($raw));
|
||||
}
|
||||
|
||||
header("Content-Type: application/json", true, 200);
|
||||
echo "true";
|
||||
exit(0);
|
||||
51
bootstrap.php
Normal file
51
bootstrap.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', false);
|
||||
|
||||
if ( ! file_exists(__DIR__ . '/.env.local') ){
|
||||
copy(__DIR__ . '/.env', __DIR__ . '/.env.local');
|
||||
}
|
||||
$_allowed = range('A', 'Z');
|
||||
foreach(file(__DIR__ . '/.env.local') as $line){
|
||||
$line = trim($line);
|
||||
if ( ! in_array($line[0], $_allowed) ) {
|
||||
continue;
|
||||
}
|
||||
putenv($line);
|
||||
}
|
||||
|
||||
class Config {
|
||||
private static PDO $_pdo;
|
||||
public static function dbName() {
|
||||
return getenv('DBNAME') ?: 'track';
|
||||
}
|
||||
public static function dbUser() {
|
||||
return getenv('DBUSER') ?: 'track';
|
||||
}
|
||||
public static function dbPass() {
|
||||
return getenv('DBPASS') ?: 'track';
|
||||
}
|
||||
public static function apiHost() {
|
||||
return getenv('APIHOST') ?: 'www.example.com';
|
||||
}
|
||||
public static function apiKey() {
|
||||
return getenv('APIKEY') ?: 'xxx';
|
||||
}
|
||||
public static function isVerbose() {
|
||||
return intval(getenv('VERBOSE')) ?: 0;
|
||||
}
|
||||
public static function dbConnection(): PDO {
|
||||
if(!self::$_pdo) {
|
||||
self::$_pdo = new PDO(
|
||||
sprintf("pgsql:dbname=%s;host=127.0.0.1", Config::dbName()),
|
||||
COnfig::dbUser(),
|
||||
COnfig::dbPass(),
|
||||
[
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
]
|
||||
);
|
||||
}
|
||||
return self::$_pdo;
|
||||
}
|
||||
}
|
||||
$pdo = Config::dbConnection();
|
||||
13
gpx.php
Normal file
13
gpx.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
const INCLUDER = true;
|
||||
require_once __DIR__ . '/lib.php';
|
||||
|
||||
if(!isset($_GET['json'])){
|
||||
header('Content-Type: application/xml');
|
||||
header('Access-Control-Allow-Origin: https://gpx.studio');
|
||||
echo UplinkData::makeGPX($_GET['device'] ?? '', $_GET['from'] ?? '', $_GET['to'] ?? '');
|
||||
}else{
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(UplinkData::makePositions($_GET['device'] ?? '', $_GET['from'] ?? '', $_GET['to'] ?? ''),128);
|
||||
}
|
||||
exit(0);
|
||||
6
index.php
Normal file
6
index.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
ini_set('display_errors', false);
|
||||
if(strtolower($_SERVER['REQUEST_METHOD']) == 'post'){
|
||||
error_log(json_encode($_POST));
|
||||
}
|
||||
exit(0);
|
||||
213
lib.php
Normal file
213
lib.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
if ( ! defined('INCLUDER') { exit(0); }
|
||||
|
||||
if(getenv('DBNAME') === false){
|
||||
require_once __DIR__ . '/bootstrap.php';
|
||||
}
|
||||
function db_logging(string $type, string $raw_data, array $headers)
|
||||
{
|
||||
$pdo = Config::dbConnection();
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS logging(
|
||||
id SERIAL PRIMARY KEY,
|
||||
request_type VARCHAR,
|
||||
inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
raw JSON,
|
||||
headers JSON,
|
||||
_get JSON,
|
||||
_post JSON
|
||||
)");
|
||||
$stmt = $pdo->prepare("INSERT INTO logging(raw, request_type, headers, _get, _post) VALUES(?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$raw_data, $_GET['type'] ?? 'NN', json_encode($headers), json_encode($_GET), json_encode($_POST)]);
|
||||
}
|
||||
|
||||
class UplinkData
|
||||
{
|
||||
|
||||
public $raw;
|
||||
|
||||
public $altitude;
|
||||
public $longitude;
|
||||
public $latitude;
|
||||
public $uplink_token;
|
||||
public $request_type = 'uplink';
|
||||
public $_get;
|
||||
public $_post;
|
||||
public $_headers;
|
||||
public $time;
|
||||
public $device_id;
|
||||
|
||||
public function __construct(string $raw, array $headers)
|
||||
{
|
||||
$this->raw = $raw;
|
||||
$data = json_decode($raw);
|
||||
|
||||
$this->device_id = $data->end_device_ids->device_id;
|
||||
$this->time = $data->received_at;
|
||||
$this->_headers = json_encode($headers);
|
||||
$this->_post = json_encode($_POST);
|
||||
$this->_get = json_encode($_GET);
|
||||
$this->uplink_token = $data->uplink_message->rx_metadata[0]->uplink_token;
|
||||
$this->latitude = $data->uplink_message->rx_metadata[0]->location->latitude;
|
||||
$this->longitude = $data->uplink_message->rx_metadata[0]->location->longitude;
|
||||
$this->altitude = $data->uplink_message->rx_metadata[0]->location->altitude;
|
||||
#$this->latitude = $data->uplink_message->decoded_payload->latitude;
|
||||
#$this->altitude = $data->uplink_message->decoded_payload->altitude;
|
||||
#$this->longitude = $data->uplink_message->decoded_payload->longitude;
|
||||
}
|
||||
|
||||
public function write()
|
||||
{
|
||||
$pdo = Config::dbConnection();
|
||||
$stmt = $pdo->prepare("INSERT INTO tracks
|
||||
(
|
||||
raw, device_id, _get, _post, _headers, latitude,
|
||||
longitude, altitude, time, request_type, uplink_token
|
||||
)
|
||||
VALUES(
|
||||
?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?
|
||||
)");
|
||||
$args = [
|
||||
$this->raw,
|
||||
$this->device_id,
|
||||
$this->_get,
|
||||
$this->_post,
|
||||
$this->_headers,
|
||||
$this->latitude,
|
||||
$this->longitude,
|
||||
$this->altitude,
|
||||
$this->time,
|
||||
$this->request_type,
|
||||
$this->uplink_token,
|
||||
];
|
||||
$stmt->execute($args);
|
||||
}
|
||||
|
||||
public static function get(string $device_id = '', string $from = '', string $to = ''): array
|
||||
{
|
||||
$pdo = Config::dbConnection();
|
||||
$from = $from ?: $_GET['from'] ?: date('Y-m-d H:i:s', time() - 3600 * 24);
|
||||
$to = $to ?: date('Y-m-d H:i:s');
|
||||
$sql = "SELECT * FROM tracks WHERE request_type = 'uplink' AND time BETWEEN ? AND ?";
|
||||
$args = [$from, $to];
|
||||
if ($device_id) {
|
||||
$sql .= " AND device_id = ?";
|
||||
$args[] = $device_id;
|
||||
}
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$res = $stmt->execute($args);
|
||||
$ret = [];
|
||||
if ($res) {
|
||||
$ret = [];
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_OBJ) as $_) {
|
||||
if(!$_->device_id){
|
||||
continue;
|
||||
}
|
||||
if ( ! isset($ret[$_->device_id])) {
|
||||
$ret[$_->device_id] = [];
|
||||
}
|
||||
$ret[$_->device_id][] = $_;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function makePositions(string $device_id = '',string $from = '', string $to='')
|
||||
{
|
||||
$data = self::get($device_id, $from, $to);
|
||||
$tracks = [];
|
||||
if ($data) {
|
||||
foreach ($data as $device_id => $resultset) {
|
||||
if(!isset($tracks[$device_id])){$tracks[$device_id] = [];}
|
||||
|
||||
foreach ($resultset as $res) {
|
||||
$raw = json_decode($res->raw);
|
||||
list($lat,$lon) = static::decodePayload($raw, $human);
|
||||
$tracks[$device_id][] = ['received_at' => $raw->received_at, 'lat' => $lat, 'lon' => $lon,];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$tracks, $human];
|
||||
|
||||
}
|
||||
|
||||
public static function makeGPX(string $device_id = '', string $from = '', string $to = ''): string
|
||||
{
|
||||
$root = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>'
|
||||
. '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="Holguin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
|
||||
. 'xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">'
|
||||
. '<metadata><name>Track - %s bis %s</name><desc>Track halt</desc><author><name>Holguin</name></author></metadata>'
|
||||
. '%s'
|
||||
. '</gpx>';
|
||||
$track = '<trk><name>Trackname %s</name><desc>Trackbeschreibung %s</desc>'
|
||||
. '<trkseg>'
|
||||
. '%s'
|
||||
. '</trkseg>'
|
||||
. '</trk>';
|
||||
$data = self::get($device_id, $from, $to);
|
||||
$tracks = [];
|
||||
if ($data) {
|
||||
foreach ($data as $device_id => $resultset) {
|
||||
|
||||
$inner = [];
|
||||
foreach ($resultset as $res) {
|
||||
$raw = json_decode($res->raw);
|
||||
list($lat,$lon) = static::decodePayload($raw);
|
||||
# $inner[] = sprintf('<trkpt lat="%s" lon="%s"><ele>%s</ele><time>%s</time></trkpt>', $res->latitude, $res->longitude, $res->altitude, $res->time);
|
||||
$inner[] = sprintf('<trkpt lat="%s" lon="%s"><ele>%s</ele><time>%s</time></trkpt>', $lat, $lon, 0, $raw->received_at);
|
||||
}
|
||||
$tracks[] = sprintf($track, $device_id, $device_id, join("\n", $inner));
|
||||
}
|
||||
|
||||
return sprintf($root,$from, $to, join("\n", $tracks));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
public static function decodePayload($raw, &$human=[])
|
||||
{
|
||||
$payload=$raw->uplink_message->frm_payload;
|
||||
$human = file_get_contents('https://1m2m.eu/services/GETPAYLOAD?Human=0&PL='.bin2hex(base64_decode($payload)));
|
||||
$human = json_decode($human);
|
||||
if(isset($human->Lat) && isset($human->Lon)){
|
||||
return [str_replace(',','.',$human->Lat),str_replace(',','.',$human->Lon)];
|
||||
}
|
||||
return [0,0];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class Logging
|
||||
{
|
||||
public static function info($type, ...$_)
|
||||
{
|
||||
global $verbose;
|
||||
if ($verbose || $type == 'error') {
|
||||
file_put_contents("/tmp/track-$type.log", json_encode($_) . "\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists('getallheaders')) {
|
||||
function getallheaders(): array
|
||||
{
|
||||
$headers = [
|
||||
'Content-type' => 'application/json',
|
||||
"X-Tts-Domain" => '',
|
||||
];
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if ($name != 'HTTP_MOD_REWRITE' && (substr($name, 0, 5) == 'HTTP_' || $name == 'CONTENT_LENGTH' || $name == 'CONTENT_TYPE')) {
|
||||
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', str_replace('HTTP_', '', $name)))));
|
||||
if ($name == 'Content-Type') {
|
||||
$name = 'Content-type';
|
||||
}
|
||||
$headers[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user