sharee.bike/copri4/main/src/Mod/APIshareeio.pm

243 lines
8.2 KiB
Perl
Executable file

package Mod::APIshareeio;
#
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) Rainer Gümpelein, TeilRad GmbH
#
#use lib qw(/var/www/copri-bike/shareeapp-sx/src);
#
use warnings;
use strict;
use Exporter;
our @ISA = qw (Exporter);
use POSIX;
use CGI;
use Apache2::Const -compile => qw(OK );
use JSON;
use Scalar::Util qw(looks_like_number);
use Config::General;
use Lib::Config;
use Mod::DBtank;
use Mod::Basework;
use Mod::Shareework;
use Mod::APIfunc;
use Data::Dumper;
sub handler {
my ($r) = @_;
my $q = new CGI;
my $netloc = $q->url(-base=>1);
$q->import_names('R');
my $json = JSON->new->allow_nonref;
my $cf = new Config;
my $dbt = new DBtank;
my $bw = new Basework;
my $tk = new Shareework;
my %varenv = $cf->envonline();
my $oprefix = $dbt->{operator}->{$varenv{dbname}}->{oprefix};
my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime;
my @keywords = $q->param;
my $user_agent = $q->user_agent();
my $aowner = 168;
my $debug=1;
my $api_file = "/var/www/copri4/shareeconf/apikeys.cfg";
my $aconf = Config::General->new($api_file);
my %apikeyconf = $aconf->getall;
my %headers = map { $_ => $q->http($_) } $q->http();
$bw->log("APIshareeio request:\n--> user-agent '$user_agent' ",$q,"");
$bw->log("headers:",\%headers,"");
#for my $header ( keys %headers ) {
# print "$header: $headers{$header}\n";
#}
print $q->header(-type => "application/json", -charset => "utf-8", -'Access-Control-Allow-Origin' => "*");
open(FILE,">>$varenv{logdir}/APIshareeio.log") if($debug);
print FILE "\n*** $now_dt user-agent: '$user_agent' to syshost: $varenv{syshost}\n" if($debug);
print FILE "<=== DUMP query\n " . Dumper($q) . "\n" if($debug);
print FILE "<=== DUMP postdata:\n " . Dumper($q->param('POSTDATA')) . "\n" if($debug);
my $response = {
event => "",
response_state => "Event doesn't match",
};
my $http_sharee_api_key = $headers{HTTP_SHAREE_API_KEY} || $R::HTTP_SHAREE_API_KEY || "";
if(!$apikeyconf{shareeio}->{sharee_api_key} || !$http_sharee_api_key || $apikeyconf{shareeio}->{sharee_api_key} ne $http_sharee_api_key){
$response->{response_state} = "Failure: access denied, api-key doesn't match";
$bw->log("Failure: access denied, api-key doesn't match",$q,"");
my $jrout = $json->pretty->encode({shareeio => $response});
print $jrout;
return Apache2::Const::OK;
exit 0;
}
foreach(@keywords){
if(length($_) > 40 || length($q->param($_)) > 400){
$response->{response_state} = "Failure 9000: amount of characters in $_ exceeds";
$bw->log("Failure 9000: amount of characters in $_ exceeds",$q,"");
my $jrout = $json->pretty->encode({shareeio => $response});
print $jrout;
return Apache2::Const::OK;
exit 0;
}
}
#sig booking_update
my $response_out = {};
$response_out = sig_booking_update($q,\%varenv,$response,$aowner);# sig json post
#sig json api
sub sig_booking_update {
my $q = shift;
my $varenv = shift;
my $response = shift || {};
my $aowner = shift || "";
$q->import_names('R');
my $dbt = new DBtank;
my $apif = new APIfunc;
my $dbh = "";
my $debug=1;
my $jrout = "seems to be not valid";
#sig using POST JSON
my $POSTDATA = $q->param('POSTDATA');
#REST methode only used for easy testing
if($R::request && $R::request eq "booking_update"){
my $rentalId = $q->escapeHTML($R::rentalId) || "";
my %POSTDATA_hash = (
"event"=>"RENTAL_END",
"data"=>{
"bikeId"=>"test16b5-0522-43da-ab66-477744a731a3",
"lockStatus"=>"locked",
"rentalId"=>"$rentalId",
"reservationState"=>"",
"startTime"=>"2022-06-06T15:00:18.045Z",
"endTime"=>"2022-06-06T15:01:43.118Z",
"distance"=>1000
}
);
$POSTDATA = encode_json(\%POSTDATA_hash);
print FILE "<=== DUMP POSTDATA_hash:\n " . Dumper(\%POSTDATA_hash) . "\n" if($debug);
}
eval {
my $response_in = {};
$response_in = decode_json($POSTDATA) if($POSTDATA);
$response->{event} = "$response_in->{event}";
if($response_in->{event}){
my $bikeId = $q->escapeHTML($response_in->{data}->{bikeId}) || "";#on push, bikeId is bike_id
my $rentalId = $q->escapeHTML($response_in->{data}->{rentalId}) || "";
my $rows = 0;
my $booking_values = {};
print FILE "event: $response_in->{event}\n" if($debug);
if($response_in->{event} eq "BIKE_STATUS" || $response_in->{event} eq "BATTERY_CAPACITY"){
$response->{response_state} = "OK: $response_in->{event} methode not implemented, because we will get it just-in-time on requesting sig GET bikes";
}
if($response_in->{event} eq "RENTAL_START"){
$response->{response_state} = "OK: methode not implemented, because rental will be started by App and success returned by sig-connector";
}
elsif($response_in->{event} eq "RENTAL_END" || $response_in->{event} eq "RESERVATION_END" || ($response_in->{event} eq "SMARTLOCK" && $response_in->{data}->{lockStatus} eq "unlocked")){
if($rentalId){
my $ctpos = {};
my $booking_pos = {
table => "contenttranspos",
fetch => "one",
txt11 => "$rentalId",
int10 => "IN::('2','3')",
#ca_id => "$authraw->{c_id}",#sig doesn't know uid
};
#only reserved alias requested rentals can be canceled
$booking_pos->{int10} = 2 if($response_in->{event} eq "RESERVATION_END");
$ctpos = $dbt->fetch_tablerecord($dbh,$booking_pos);
my $distance = 0;
$distance = $response_in->{data}->{distance} / 1000 if(looks_like_number($response_in->{data}->{distance}) && $response_in->{data}->{distance} > 0);
$distance = sprintf('%.2f',$distance);
if(ref($ctpos) eq "HASH" && $ctpos->{txt22} && $ctpos->{txt11}){
my $sig_book = {
bikeId => $ctpos->{txt22},
rentalId => $ctpos->{txt11},
reservationId => "",
distance => $distance,
};
my $authraw = { c_id => "" };
$authraw->{c_id} = $ctpos->{ca_id} if($ctpos->{ca_id});
$q->param(-name=>'bike',-value=>"$ctpos->{ct_name}") if($ctpos->{ct_name});
$q->param(-name=>'state',-value=>"canceled") if($response_in->{event} eq "RESERVATION_END");
if($response_in->{event} eq "SMARTLOCK" && $response_in->{data}->{lockStatus} eq "unlocked"){
$q->param(-name=>'lock_state',-value=>"unlocked");
}
if($response_in->{event} eq "RENTAL_END"){
$q->param(-name=>'state',-value=>"available");
$q->param(-name=>'lock_state',-value=>"locked");
#addition state in context to get state=returned for miniquery
$sig_book->{station_lock_state} = "3";#set int28 to 3
}
($rows, $booking_values) = $apif->booking_update($q,$varenv,$authraw,$aowner,$sig_book) if($authraw->{c_id});
#response is for sig json after rental-end
$booking_values->{bikeId} = $sig_book->{bikeId};
$booking_values->{rentalId} = $sig_book->{rentalId};
delete $booking_values->{geo_distance};
delete $booking_values->{co2saving};
delete $booking_values->{bike};
delete $booking_values->{response_text};
delete $booking_values->{user_miniquery};
$response->{response_state} = "OK: event matched, but something seems to goes wrong on booking_update" if(!$booking_values->{response_state});
$response = {%$response, %$booking_values};
}else{
$response->{response_state} = "Failure: $response_in->{event} : there is no reservation OR rental with rentalID=$rentalId";
}
}else{
$response->{response_state} = "Failure: no rentalId defined";
}
}#end RENTAL_END
}#end event
};#end eval
if ($@){
print FILE "failure! can not decode POST json, POSTDATA:\n" . Dumper($q->param('POSTDATA')) . "\n" if($debug);
#warn $@;
print FILE "warn:" . $@ . "\n" if($debug);
$response->{response_state} = "Failure: can not decode POST json";
}
return $response;
}#end sig json booking_update
#end RESTful ------------------------------------------------------------
#FINAL JSON response OUTPUT ----------------------------------------------------------
my $jrout = $json->pretty->encode({shareeio => $response_out});
print $jrout;
print FILE "APIshareeio jrout:\n" . Dumper($jrout) . "\n" if($debug);
$bw->log("APIshareeio response by $user_agent mapped aowner:$aowner",$jrout,"");
#end JSON ----------------------------------------------------------------------------
close(FILE) if($debug);
return Apache2::Const::OK;
}#end handler
1;