Files
com_sportsmanager/src/structure/components/com_sportsmanager/api.php
T
2025-08-04 00:00:42 +02:00

315 lines
9.0 KiB
PHP

<?php
/*
* Sports Manager API Extension
*/
use JetBrains\PhpStorm\NoReturn;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Factory;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Registry\Registry;
defined("_JEXEC") or die();
require_once JPATH_SITE . '/components/com_sportsmanager/database/init.php';
Factory::getContainer()->set(Registry::class, function () {
return new Registry();
});
$secret = Factory::getContainer()->get(Registry::class)->get("secret");
#[NoReturn] function abortWithError($error): void
{
if (isJson()) {
header("content-type: application/json");
die(json_encode(["error" => $error]));
} else {
die($error);
}
}
function isJson(): bool
{
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
return $jInput->get('format') === 'json';
}
function notifyChange($data): void
{
try {
$db = getDatabase();
$query = "SELECT wert from #__sportsmanager_einstellungen WHERE name='api_push_key'";
$push_key = loadResult($db, $query);
$push_server = !empty($push_key) && isset(_payload($push_key)->aud) ? _payload($push_key)->aud : '';
if ($push_server != '' && $push_key != '') {
$url = $push_server . (str_ends_with($push_server, '/') ? '' : '/') . 'v1/notifications/send';
$key = 'key=' . $push_key;
$ch = curl_init($url);
curl_setopt_array($ch, array(
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => TRUE,
CURLOPT_HTTPHEADER => array(
'Authorization: ' . $key,
'Content-Type: application/json',
),
CURLOPT_TIMEOUT => 2,
CURLOPT_POSTFIELDS => json_encode($data),
));
$resp = curl_exec($ch);
if (!$resp) {
error_log("failed to send notification");
}
}
} catch (Exception $ex) {
error_log($ex);
}
}
function begegnungChanged($begegnung, $begegnung_vorher, $modus, $heim_team, $gast_team, $spiele): void
{
notifyChange(['payload' => [
'begegnung' => $begegnung,
'begegnung_vorher' => $begegnung_vorher,
'$modus' => $modus,
'heim_team' => $heim_team,
'gast_team' => $gast_team,
'spiele' => $spiele,
], 'type' => 'FIXTURE_RESULT_CHANGED']);
}
function begegnungTischChanged($begegnung, $heim_team, $gast_team): void
{
notifyChange(['payload' => [
'begegnung' => $begegnung,
'heim_team' => $heim_team,
'gast_team' => $gast_team,
], 'type' => 'TABLE_CHANGED']);
}
function begegnungVerlegenNotify($begegnung, $users, $vorschlagendes_team_id, $heim_team, $gast_team): void
{
notifyChange([
'payload' => [
'begegnung' => $begegnung,
'users' => $users,
'vorschlagendes_team_id' => $vorschlagendes_team_id,
'heim_team' => $heim_team,
'gast_team' => $gast_team,
],
'type' => 'FIXTURE_DATE_CHANGED'
]);
}
/*
* erstellen von request tokens. diese sind 16 stunden gültig.
* @reponse body
* { data: { token: "reqest_token", access_for_team: ["team_id_1", "team_id_2"]}, expires: 1520013747000}
*/
#[NoReturn] function userToken(): void
{
global $secret;
if (!isJson()) {
abortWithError("JSON Request only");
}
if (isExternalDatabase()) {
abortWithError("Local Database only");
}
$container = Factory::getContainer();
$jInput = $container->get(SiteApplication::class)->input->json;
$access_key = $jInput->getString('access_key');
$user_id = _payload($access_key)->sub;
$user = $container->get(UserFactoryInterface::class)->loadUserById($user_id);
if (!jwt_validate($access_key, $secret . $user->password)) {
abortWithError('Access Key is invalid');
}
try {
$expires = new DateTime();
$expires->modify('+16 hours');
$db = getDatabase();
$query = "SELECT berechtigt_team_id from #__sportsmanager_berechtigt_fuer_team where berechtigt_user_id = $user_id";
$team_id = loadObjectList($db, $query);
JSON_sportsmanager::JSON([
'token' => jwt_token([
'sub' => $user_id,
'exp' => $expires->getTimestamp(),
], $secret),
'access_for_teams' => array_map(function ($item) {
return $item->berechtigt_team_id;
}, $team_id),
'expires' => $expires->getTimestamp() * 1000, //
]);
} catch (Exception $ex) {
error_log($ex);
}
}
/*
*
* @response body
* { data: { token: "api_acccess_token" }}
*/
#[NoReturn] function userAuth(): void
{
global $secret;
if (!isJson()) {
die();
}
if (isExternalDatabase()) {
abortWithError("Local Database only");
}
$container = Factory::getContainer();
$jInput = $container->get(SiteApplication::class)->input->json;
$username = $jInput->getString('username');
$password = $jInput->getString('password');
$db = getDatabase();
$query = $db->getQuery(true);
$query->select('id')->from('#__users')->where('username = "' . $username . '"')->setLimit(1);
$user_id = loadResult($db, $query);
$user = $container->get(UserFactoryInterface::class)->loadUserById($user_id);
//TODO: pw verification modernising: use php native methods, however this also needs new pw hashing. maybe force a pw reset on all accounts
if (password_verify($password, $user->password)) {
JSON_sportsmanager::JSON([
'token' => jwt_token([
'sub' => $user_id,
'iat' => (new DateTime())->getTimestamp(),
], $secret . $user->password)
]);
}
abortWithError('Wrong credentials');
}
function getUserID(): int
{
global $secret;
$container = Factory::getContainer();
$input = $container->get(SiteApplication::class)->input;
$token = $input->server->getString('HTTP_SECRET', NULL);
return $token != NULL && jwt_validate($token, $secret) && isset(_payload($token)->sub)
? (int)_payload($token)->sub
: 0;
}
function getColorOfImage($image)
{
if ($image != NULL) {
if (str_contains($image, '.png')) {
$img = imagecreatefrompng($image);
} else {
$img = imagecreatefromjpeg($image);
}
$width = imagesx($img);
$height = imagesx($img);
$colorMap = [];
$colors = [];
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$color = imagecolorsforindex($img, imagecolorat($img, $x, $y));
if ($color['alpha'] < 20) {
$c = colorKey($color);
$hex = hex($color);
if ($hex != NULL) {
if (!isset($colors[$c])) {
$colors[$c] = 0;
$colorMap[$c] = $hex;
}
$colors[$c] += 1;
}
}
}
}
arsort($colors);
$result = array_keys($colors);
return sizeof($result) > 1 && $result[0] === '0-0-0' ? $colorMap[$result[1]] : $colorMap[$result[0]];
}
return NULL;
}
function colorKey($rgb): string
{
$r = (int)($rgb['red'] / 100);
$g = (int)($rgb['green'] / 100);
$b = (int)($rgb['blue'] / 100);
return $r . '-' . $g . '-' . $b;
}
function hex($rgb): ?string
{
$r = $rgb['red'];
$g = $rgb['green'];
$b = $rgb['blue'];
if (($r + $g + $b) / 3 > 125) {
return NULL;
}
$r = dechex($r);
if (strlen($r) < 2) {
$r = '0' . $r;
}
$g = dechex($g);
if (strlen($g) < 2) {
$g = '0' . $g;
}
$b = dechex($b);
if (strlen($b) < 2) {
$b = '0' . $b;
}
return '#' . $r . $g . $b;
}
/*
* sign string with secret
*/
function _sign($data, $secret): string
{
return base64_encode(hash_hmac('sha256', $data, $secret));
}
/*
* get payload from jwt token
*/
function _payload($token)
{
$jwt = explode('.', $token);
return json_decode(base64_decode($jwt[0]));
}
/*
* headless signed jwt token
*/
function jwt_token($payload, $secret): string
{
$data = base64_encode(json_encode($payload));
return $data . '.' . _sign($data, $secret);
}
/*
* validate token
*/
function jwt_validate($token, $secret): bool
{
$jwt = explode('.', $token);
if (sizeof($jwt) == 2 && $jwt[1] == _sign($jwt[0], $secret)) {
if (isset(_payload($token)->exp)) {
return _payload($token)->exp > (new DateTime())->getTimestamp();
}
return true;
}
return sizeof($jwt) == 2 && $jwt[1] == _sign($jwt[0], $secret);
}