diff options
Diffstat (limited to 'lib/Travelynx/Controller/Api.pm')
-rwxr-xr-x | lib/Travelynx/Controller/Api.pm | 315 |
1 files changed, 188 insertions, 127 deletions
diff --git a/lib/Travelynx/Controller/Api.pm b/lib/Travelynx/Controller/Api.pm index 974b9ca..687243d 100755 --- a/lib/Travelynx/Controller/Api.pm +++ b/lib/Travelynx/Controller/Api.pm @@ -1,15 +1,17 @@ package Travelynx::Controller::Api; -# Copyright (C) 2020 Daniel Friesel +# Copyright (C) 2020-2023 Birte Kristina Friesel # # SPDX-License-Identifier: AGPL-3.0-or-later use Mojo::Base 'Mojolicious::Controller'; use DateTime; use List::Util; -use Travel::Status::DE::IRIS::Stations; +use Mojo::JSON qw(encode_json); use UUID::Tiny qw(:std); +# Internal Helpers + sub make_token { return create_uuid_as_string(UUID_V4); } @@ -28,10 +30,22 @@ sub sanitize { return 0; } +# Contollers + sub documentation { my ($self) = @_; - $self->render('api_documentation'); + if ( $self->is_user_authenticated ) { + my $uid = $self->current_user->{id}; + $self->render( + 'api_documentation', + uid => $uid, + api_token => $self->users->get_api_token( uid => $uid ), + ); + } + else { + $self->render('api_documentation'); + } } sub get_v1 { @@ -67,8 +81,11 @@ sub get_v1 { return; } - my $token = $self->get_api_token($uid); - if ( $api_token ne $token->{$api_action} ) { + my $token = $self->users->get_api_token( uid => $uid ); + if ( not $api_token + or not $token->{$api_action} + or $api_token ne $token->{$api_action} ) + { $self->render( json => { error => 'Invalid token', @@ -77,7 +94,7 @@ sub get_v1 { return; } if ( $api_action eq 'status' ) { - $self->render( json => $self->get_user_status_json_v1($uid) ); + $self->render( json => $self->get_user_status_json_v1( uid => $uid ) ); } else { $self->render( @@ -130,7 +147,7 @@ sub travel_v1 { return; } - my $token = $self->get_api_token($uid); + my $token = $self->users->get_api_token( uid => $uid ); if ( not $token->{'travel'} or $api_token ne $token->{'travel'} ) { $self->render( json => { @@ -150,7 +167,7 @@ sub travel_v1 { success => \0, deprecated => \0, error => 'Missing or invalid action', - status => $self->get_user_status_json_v1($uid) + status => $self->get_user_status_json_v1( uid => $uid ) }, ); return; @@ -160,12 +177,14 @@ sub travel_v1 { my $from_station = sanitize( q{}, $payload->{fromStation} ); my $to_station = sanitize( q{}, $payload->{toStation} ); my $train_id; + my $hafas = exists $payload->{train}{journeyID} ? 1 : 0; if ( not( $from_station - and ( ( $payload->{train}{type} and $payload->{train}{no} ) - or $payload->{train}{id} ) + and ( ( $payload->{train}{type} and $payload->{train}{no} ) + or $payload->{train}{id} + or $payload->{train}{journeyID} ) ) ) { @@ -174,131 +193,139 @@ sub travel_v1 { success => \0, deprecated => \0, error => 'Missing fromStation or train data', - status => $self->get_user_status_json_v1($uid) + status => $self->get_user_status_json_v1( uid => $uid ) }, ); return; } - if ( - @{ - [ - Travel::Status::DE::IRIS::Stations::get_station( - $from_station) - ] - } != 1 - ) - { + if ( not $hafas and not $self->stations->search($from_station) ) { $self->render( json => { success => \0, deprecated => \0, - error => 'fromStation is ambiguous', - status => $self->get_user_status_json_v1($uid) + error => 'Unknown fromStation', + status => $self->get_user_status_json_v1( uid => $uid ) }, ); return; } - if ( - $to_station - and @{ - [ - Travel::Status::DE::IRIS::Stations::get_station( - $to_station) - ] - } != 1 - ) + if ( $to_station + and not $hafas + and not $self->stations->search($to_station) ) { $self->render( json => { success => \0, deprecated => \0, - error => 'toStation is ambiguous', - status => $self->get_user_status_json_v1($uid) + error => 'Unknown toStation', + status => $self->get_user_status_json_v1( uid => $uid ) }, ); return; } - if ( exists $payload->{train}{id} ) { - $train_id = sanitize( 0, $payload->{train}{id} ); + my $train_p; + + if ( exists $payload->{train}{journeyID} ) { + $train_p = Mojo::Promise->resolve( + sanitize( q{}, $payload->{train}{journeyID} ) ); + } + elsif ( exists $payload->{train}{id} ) { + $train_p + = Mojo::Promise->resolve( sanitize( 0, $payload->{train}{id} ) ); } else { my $train_type = sanitize( q{}, $payload->{train}{type} ); my $train_no = sanitize( q{}, $payload->{train}{no} ); - my $status = $self->iris->get_departures( + + $train_p = $self->iris->get_departures_p( station => $from_station, lookbehind => 140, lookahead => 40 + )->then( + sub { + my ($status) = @_; + if ( $status->{errstr} ) { + return Mojo::Promise->reject( + 'Error requesting departures from fromStation: ' + . $status->{errstr} ); + } + my ($train) = List::Util::first { + $_->type eq $train_type and $_->train_no eq $train_no + } + @{ $status->{results} }; + if ( not defined $train ) { + return Mojo::Promise->reject( + 'Train not found at fromStation'); + } + return Mojo::Promise->resolve( $train->train_id ); + } ); - if ( $status->{errstr} ) { + } + + $self->render_later; + + $train_p->then( + sub { + my ($train_id) = @_; + return $self->checkin_p( + station => $from_station, + train_id => $train_id, + uid => $uid + ); + } + )->then( + sub { + my ($train) = @_; + if ( $payload->{comment} ) { + $self->in_transit->update_user_data( + uid => $uid, + user_data => + { comment => sanitize( q{}, $payload->{comment} ) } + ); + } + if ($to_station) { + + # the user may not have provided the correct to_station, so + # request related stations for checkout. + return $self->checkout_p( + station => $to_station, + force => 0, + uid => $uid, + with_related => 1, + ); + } + return Mojo::Promise->resolve; + } + )->then( + sub { + my ( undef, $error ) = @_; + if ($error) { + return Mojo::Promise->reject($error); + } $self->render( json => { - success => \0, - error => - 'Error requesting departures from fromStation: ' - . $status->{errstr}, - status => $self->get_user_status_json_v1($uid) + success => \1, + deprecated => \0, + status => $self->get_user_status_json_v1( uid => $uid ) } ); - return; } - my ($train) = List::Util::first { - $_->type eq $train_type and $_->train_no eq $train_no - } - @{ $status->{results} }; - if ( not defined $train ) { + )->catch( + sub { + my ($error) = @_; $self->render( json => { success => \0, deprecated => \0, - error => 'Train not found at fromStation', - status => $self->get_user_status_json_v1($uid) + error => 'Checkin/Checkout error: ' . $error, + status => $self->get_user_status_json_v1( uid => $uid ) } ); - return; } - $train_id = $train->train_id; - } - - my ( $train, $error ) = $self->checkin( - station => $from_station, - train_id => $train_id, - uid => $uid - ); - if ( $payload->{comment} and not $error ) { - $self->in_transit->update_user_data( - uid => $uid, - user_data => { comment => sanitize( q{}, $payload->{comment} ) } - ); - } - if ( $to_station and not $error ) { - ( $train, $error ) = $self->checkout( - station => $to_station, - force => 0, - uid => $uid - ); - } - if ($error) { - $self->render( - json => { - success => \0, - deprecated => \0, - error => 'Checkin/Checkout error: ' . $error, - status => $self->get_user_status_json_v1($uid) - } - ); - } - else { - $self->render( - json => { - success => \1, - deprecated => \0, - status => $self->get_user_status_json_v1($uid) - } - ); - } + )->wait; } elsif ( $payload->{action} eq 'checkout' ) { my $to_station = sanitize( q{}, $payload->{toStation} ); @@ -309,7 +336,7 @@ sub travel_v1 { success => \0, deprecated => \0, error => 'Missing toStation', - status => $self->get_user_status_json_v1($uid) + status => $self->get_user_status_json_v1( uid => $uid ) }, ); return; @@ -322,30 +349,43 @@ sub travel_v1 { ); } - my ( $train, $error ) = $self->checkout( - station => $to_station, - force => $payload->{force} ? 1 : 0, - uid => $uid - ); - if ($error) { - $self->render( - json => { - success => \0, - deprecated => \0, - error => 'Checkout error: ' . $error, - status => $self->get_user_status_json_v1($uid) - } - ); - } - else { - $self->render( - json => { - success => \1, - deprecated => \0, - status => $self->get_user_status_json_v1($uid) + $self->render_later; + + # the user may not have provided the correct to_station, so + # request related stations for checkout. + $self->checkout_p( + station => $to_station, + force => $payload->{force} ? 1 : 0, + uid => $uid, + with_related => 1, + )->then( + sub { + my ( $train, $error ) = @_; + if ($error) { + return Mojo::Promise->reject($error); } - ); - } + $self->render( + json => { + success => \1, + deprecated => \0, + status => $self->get_user_status_json_v1( uid => $uid ) + } + ); + return; + } + )->catch( + sub { + my ($err) = @_; + $self->render( + json => { + success => \0, + deprecated => \0, + error => 'Checkout error: ' . $err, + status => $self->get_user_status_json_v1( uid => $uid ) + } + ); + } + )->wait; } elsif ( $payload->{action} eq 'undo' ) { my $error = $self->undo( 'in_transit', $uid ); @@ -355,7 +395,7 @@ sub travel_v1 { success => \0, deprecated => \0, error => $error, - status => $self->get_user_status_json_v1($uid) + status => $self->get_user_status_json_v1( uid => $uid ) } ); } @@ -364,7 +404,7 @@ sub travel_v1 { json => { success => \1, deprecated => \0, - status => $self->get_user_status_json_v1($uid) + status => $self->get_user_status_json_v1( uid => $uid ) } ); } @@ -413,7 +453,7 @@ sub import_v1 { return; } - my $token = $self->get_api_token($uid); + my $token = $self->users->get_api_token( uid => $uid ); if ( not $token->{'import'} or $api_token ne $token->{'import'} ) { $self->render( json => { @@ -457,13 +497,13 @@ sub import_v1 { } %opt = ( - uid => $uid, - train_type => sanitize( q{}, $payload->{train}{type} ), - train_no => sanitize( q{}, $payload->{train}{no} ), - train_line => sanitize( q{}, $payload->{train}{line} ), - cancelled => $payload->{cancelled} ? 1 : 0, - dep_station => sanitize( q{}, $payload->{fromStation}{name} ), - arr_station => sanitize( q{}, $payload->{toStation}{name} ), + uid => $uid, + train_type => sanitize( q{}, $payload->{train}{type} ), + train_no => sanitize( q{}, $payload->{train}{no} ), + train_line => sanitize( q{}, $payload->{train}{line} ), + cancelled => $payload->{cancelled} ? 1 : 0, + dep_station => sanitize( q{}, $payload->{fromStation}{name} ), + arr_station => sanitize( q{}, $payload->{toStation}{name} ), sched_departure => sanitize( 0, $payload->{fromStation}{scheduledTime} ), rt_departure => sanitize( @@ -568,11 +608,15 @@ sub import_v1 { sub set_token { my ($self) = @_; if ( $self->validation->csrf_protect->has_error('csrf_token') ) { - $self->render( 'account', invalid => 'csrf' ); + $self->render( + 'bad_request', + csrf => 1, + status => 400 + ); return; } my $token = make_token(); - my $token_id = $self->app->token_type->{ $self->param('token') }; + my $token_id = $self->users->get_token_id( $self->param('token') ); if ( not $token_id ) { $self->redirect_to('account'); @@ -605,4 +649,21 @@ sub set_token { $self->redirect_to('account'); } +sub autocomplete { + my ($self) = @_; + + $self->res->headers->cache_control('max-age=86400, immutable'); + + my $output + = "document.addEventListener('DOMContentLoaded',function(){M.Autocomplete.init(document.querySelectorAll('.autocomplete'),{\n"; + $output .= 'minLength:3,limit:50,data:'; + $output .= encode_json( $self->stations->get_for_autocomplete ); + $output .= "\n});});\n"; + + $self->render( + format => 'js', + data => $output + ); +} + 1; |