Compare commits

...

12 Commits

Author SHA1 Message Date
nabeelio
db34d0e3e7 Add test for METAR string 2021-03-15 09:14:39 -04:00
Nabeel S
fffbab7201 Merge branch 'dev' into patch-2 2021-03-15 08:30:29 -04:00
exciler
b9e7a2efc9 Add unit conversion handling for block_fuel like for fuel_used (#1076)
* Add unit conversion handling for block_fuel like for fuel_used, add further checks in PIREPTest->testUnitFields

* Remove accidentially added "use"

Co-authored-by: Andreas Palm <ap@ewsp.de>
2021-03-15 08:30:14 -04:00
B.Fatih KOZ
76a2a16fa6 Merge branch 'dev' into patch-2 2021-03-14 03:54:53 +03:00
Nabeel Shahzad
73f88fce0c Fix permissions with modules 2021-03-13 18:31:09 -05:00
B.Fatih KOZ
cfbd1c901a Merge branch 'dev' into patch-2 2021-03-10 04:53:25 +03:00
Nabeel S
e70ee5aa6f Add web cron ability #821 (#1073)
* Use database for kvp storage

* Read kvpstore

* Add web cron ability through API #821

* Style fixes

* Fix text
2021-03-09 11:36:56 -05:00
B.Fatih KOZ
617813bdbf Fix Metar Decoding / Wind check for Wind Chill
PR aims to fix the bug #1071 by checking both the wind speed and it's value for starting Wind Chill calculation.
2021-03-09 16:06:39 +03:00
nabeelio
f1c54bcc7c Add Postmark package #1067 2021-03-08 11:35:11 -05:00
nabeelio
d94d754961 Block user if they're not active #1066 2021-03-08 11:24:09 -05:00
nabeelio
9abfbd6c8c Remove unneeded import #1066 2021-03-08 11:21:55 -05:00
nabeelio
97fc1dd43d Don't block API logins #1066 2021-03-08 11:19:31 -05:00
21 changed files with 350 additions and 8 deletions

View File

@@ -0,0 +1,19 @@
<?php
use App\Contracts\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Add a hub to the subfleet is
*/
class AddKvpTable extends Migration
{
public function up()
{
Schema::create('kvp', function (Blueprint $table) {
$table->string('key')->index();
$table->string('value');
});
}
}

View File

@@ -361,3 +361,9 @@
options: ''
type: 'text'
description: 'Discord public channel ID for broadcasat notifications'
- key: 'cron.random_id'
name: 'Cron Randomized ID'
group: 'cron'
value: ''
type: 'hidden'
description: ''

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Exceptions;
class CronInvalid extends AbstractHttpException
{
public const MESSAGE = 'Cron ID is disabled or invalid';
public function __construct()
{
parent::__construct(400, static::MESSAGE);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'cron-invalid';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [];
}
}

View File

@@ -6,6 +6,7 @@ use App\Contracts\Controller;
use App\Repositories\KvpRepository;
use App\Services\CronService;
use App\Services\VersionService;
use App\Support\Utils;
use Codedge\Updater\UpdaterManager;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
@@ -34,7 +35,12 @@ class MaintenanceController extends Controller
public function index()
{
// Get the cron URL
$cron_id = setting('cron.random_id');
$cron_url = empty($cron_id) ? 'Not enabled' : url(route('api.maintenance.cron', $cron_id));
return view('admin.maintenance.index', [
'cron_url' => $cron_url,
'cron_path' => $this->cronSvc->getCronExecString(),
'cron_problem_exists' => $this->cronSvc->cronProblemExists(),
'new_version' => $this->kvpRepo->get('new_version_available', false),
@@ -117,4 +123,33 @@ class MaintenanceController extends Controller
return redirect('/update/downloader');
}
/**
* Enable the cron, or if it's enabled, change the ID that is used
*
* @param Request $request
*/
public function cron_enable(Request $request)
{
$id = Utils::generateNewId(24);
setting_save('cron.random_id', $id);
Flash::success('Web cron refreshed!');
return redirect(route('admin.maintenance.index'));
}
/**
* Disable the web cron
*
* @param Request $request
*
* @return mixed
*/
public function cron_disable(Request $request)
{
setting_save('cron.random_id', '');
Flash::success('Web cron disabled!');
return redirect(route('admin.maintenance.index'));
}
}

View File

@@ -62,7 +62,7 @@ class SettingsController extends Controller
*/
public function index()
{
$settings = Setting::orderBy('order', 'asc')->get();
$settings = Setting::where('type', '!=', 'hidden')->orderBy('order')->get();
$settings = $settings->groupBy('group');
return view('admin.settings.index', [

View File

@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api;
use App\Contracts\Controller;
use App\Exceptions\AssetNotFound;
use App\Exceptions\Unauthorized;
use App\Http\Resources\Flight as FlightResource;
use App\Http\Resources\Navdata as NavdataResource;
use App\Models\SimBrief;

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers\Api;
use App\Contracts\Controller;
use App\Exceptions\CronInvalid;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
class MaintenanceController extends Controller
{
/**
* Run the cron job from the web
*
* @param Request $request
* @param string $id The ID passed in for the cron
*
* @return mixed
*/
public function cron(Request $request, string $id)
{
$cron_id = setting('cron.random_id');
if (empty($cron_id) || $id !== $cron_id) {
throw new CronInvalid();
}
$output = '';
Artisan::call('schedule:run');
$output .= trim(Artisan::output());
return response([
'content' => $output,
]);
}
}

View File

@@ -39,7 +39,7 @@ class ApiAuth implements Middleware
return $this->unauthorized('User not found with key "'.$api_key.'"');
}
if ($user->state !== UserState::ACTIVE) {
if ($user->state !== UserState::ACTIVE && $user->state !== UserState::ON_LEAVE) {
return $this->unauthorized('User is not ACTIVE, please contact an administrator');
}

View File

@@ -38,6 +38,13 @@ class Pirep extends Resource
$distance = new Distance($res['distance'], config('phpvms.internal_units.distance'));
$res['distance'] = $distance->getResponseUnits();
if (!array_key_exists('block_fuel', $res)) {
$res['block_fuel'] = 0;
}
$block_fuel = new Fuel($res['block_fuel'], config('phpvms.internal_units.fuel'));
$res['block_fuel'] = $block_fuel->getResponseUnits();
if (!array_key_exists('fuel_used', $res)) {
$res['fuel_used'] = 0;
}

23
app/Models/Kvp.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use App\Contracts\Model;
/**
* @property string key
* @property string value
*/
class Kvp extends Model
{
public $table = 'kvp';
public $timestamps = false;
public $incrementing = false;
protected $keyType = 'string';
public $fillable = [
'key',
'value',
];
}

View File

@@ -326,6 +326,22 @@ class Pirep extends Model
return $field_values;
}
/**
* Set the amount of block fuel
*
* @param $value
*/
public function setBlockFuelAttribute($value): void
{
if ($value instanceof Fuel) {
$this->attributes['block_fuel'] = $value->toUnit(
config('phpvms.internal_units.fuel')
);
} else {
$this->attributes['block_fuel'] = $value;
}
}
/**
* Set the amount of fuel used
*

View File

@@ -10,6 +10,7 @@ use Illuminate\Support\Collection;
* @property int $user_id The user that generated this
* @property string $flight_id Optional, if attached to a flight, removed if attached to PIREP
* @property string $pirep_id Optional, if attached to a PIREP, removed if attached to flight
* @property string $aircraft_id The aircraft this is for
* @property string $acars_xml
* @property string $ofp_xml
* @property string $ofp_html

View File

@@ -392,6 +392,12 @@ class RouteServiceProvider extends ServiceProvider
Route::match(['post'], 'maintenance/forcecheck', 'MaintenanceController@forcecheck')
->name('maintenance.forcecheck')->middleware('ability:admin,maintenance');
Route::match(['post'], 'maintenance/cron_enable', 'MaintenanceController@cron_enable')
->name('maintenance.cron_enable')->middleware('ability:admin,maintenance');
Route::match(['post'], 'maintenance/cron_disable', 'MaintenanceController@cron_disable')
->name('maintenance.cron_disable')->middleware('ability:admin,maintenance');
// subfleet
Route::get('subfleets/export', 'SubfleetController@export')
->name('subfleets.export')->middleware('ability:admin,fleet');
@@ -459,7 +465,7 @@ class RouteServiceProvider extends ServiceProvider
Route::group([
'as' => 'modules.',
'prefix' => 'modules',
'middleware' => ['ability:admin, modules'],
'middleware' => ['ability:admin,modules'],
], function () {
//Modules Index
@@ -508,6 +514,8 @@ class RouteServiceProvider extends ServiceProvider
Route::get('pireps/{pirep_id}', 'PirepController@get');
Route::get('pireps/{pirep_id}/acars/geojson', 'AcarsController@acars_geojson');
Route::get('cron/{id}', 'MaintenanceController@cron')->name('maintenance.cron');
Route::get('news', 'NewsController@index');
Route::get('status', 'StatusController@status');
Route::get('version', 'StatusController@status');

View File

@@ -1772,7 +1772,7 @@ class Metar implements \ArrayAccess
*/
private function calculate_wind_chill($temperature_f): void
{
if ($temperature_f < 51 && $this->result['wind_speed'] !== 0) {
if ($temperature_f < 51 && $this->result['wind_speed'] && $this->result['wind_speed'] !== 0) {
$windspeed = $this->result['wind_speed']->toUnit('mph');
if ($windspeed > 3) {
$chill_f = 35.74 + 0.6215 * $temperature_f - 35.75 * ($windspeed ** 0.16);

View File

@@ -61,7 +61,8 @@
"madnest/madzipper": "^1.1.0",
"elcobvg/laravel-opcache": "^0.4.1",
"laravel/legacy-factories": "^1.1",
"fakerphp/faker": "^1.13"
"fakerphp/faker": "^1.13",
"wildbit/swiftmailer-postmark": "^3.3"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.5",

46
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9c810647ebea1e88f066d7c0679d9b4c",
"content-hash": "4180601e7a77313a007604954b01f071",
"packages": [
{
"name": "akaunting/money",
@@ -9348,6 +9348,50 @@
},
"time": "2020-12-14T12:45:59+00:00"
},
{
"name": "wildbit/swiftmailer-postmark",
"version": "3.3.0",
"source": {
"type": "git",
"url": "https://github.com/wildbit/swiftmailer-postmark.git",
"reference": "44ccab7834de8b220d292647ecb2cb683f9962ee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wildbit/swiftmailer-postmark/zipball/44ccab7834de8b220d292647ecb2cb683f9962ee",
"reference": "44ccab7834de8b220d292647ecb2cb683f9962ee",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0|^7.0",
"swiftmailer/swiftmailer": "^6.0.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Postmark\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Postmark",
"email": "support@postmarkapp.com"
}
],
"description": "A Swiftmailer Transport for Postmark.",
"support": {
"issues": "https://github.com/wildbit/swiftmailer-postmark/issues",
"source": "https://github.com/wildbit/swiftmailer-postmark/tree/3.3.0"
},
"time": "2020-09-10T10:54:20+00:00"
},
{
"name": "willdurand/geocoder",
"version": "4.4.0",

View File

@@ -6,7 +6,8 @@
</h6>
<div class="row" style="padding-top: 5px">
<div class="col-sm-12">
<p>A cron must be created that runs every minute calling artisan. Example:</p>
<p>A cron must be created that runs every minute calling artisan. An example is below.
<strong><a href="{{ docs_link('cron') }}" target="_blank">See the docs</a></strong></p>
<label style="width: 100%">
<input type="text" value="{{ $cron_path }}" class="form-control" style="width: 100%"/>
</label>
@@ -20,6 +21,45 @@
@endif
</div>
</div>
<hr>
<div class="row" style="padding-top: 5px">
<div class="col-sm-12">
<h5>Web Cron</h5>
</div>
<div class="col-sm-6">
<p>
If you don't have cron access on your server, you can use a web-cron service to
access this URL every minute. Keep it disabled if you're not using it. It's a
unique ID that can be reset/changed if needed for security.
</p>
</div>
<div class="col-sm-6 pull-right">
<table class="table-condensed">
<tr class="text-right">
<td style="padding-right: 10px;" class="text-right">
{{ Form::open(['url' => route('admin.maintenance.cron_enable'),
'method' => 'post']) }}
{{ Form::button('Enable/Change ID', ['type' => 'submit', 'class' => 'btn btn-success']) }}
{{ Form::close() }}
</td>
<td class="text-right">
{{ Form::open(['url' => route('admin.maintenance.cron_disable'),
'method' => 'post']) }}
{{ Form::button('Disable', ['type' => 'submit', 'class' => 'btn btn-warning']) }}
{{ Form::close() }}
</td>
</tr>
</table>
</div>
<div class="col-sm-12">
<label style="width: 100%">
<input type="text" value="{{ $cron_url }}" class="form-control" style="width: 100%"/>
</label>
</div>
</div>
</div>
</div>
</div>

View File

@@ -12,6 +12,7 @@ use App\Models\News;
use App\Models\Subfleet;
use App\Models\User;
use App\Services\FareService;
use App\Support\Utils;
use Exception;
use function random_int;
@@ -312,4 +313,21 @@ class ApiTest extends TestCase
$this->assertNotNull($user);
$this->assertTrue(strpos($user['avatar'], 'gravatar') !== -1);
}
/**
* Test that the web cron runs
*/
public function testWebCron()
{
$this->updateSetting('cron.random_id', '');
$this->get('/api/cron/sdf')->assertStatus(400);
$id = Utils::generateNewId(24);
$this->updateSetting('cron.random_id', $id);
$this->get('/api/cron/sdf')->assertStatus(400);
$res = $this->get('/api/cron/'.$id);
$res->assertStatus(200);
}
}

View File

@@ -151,6 +151,19 @@ class MetarTest extends TestCase
$this->assertEquals('Few at 1500 feet; few at 25000 feet', $metar['clouds_report_ft']);
}
/**
* https://github.com/nabeelio/phpvms/issues/1071
*/
public function testMetarWindSpeedChill()
{
$metar = 'EKYT 091020Z /////KT CAVOK 02/M03 Q1019';
$metar = Metar::parse($metar);
$this->assertEquals('VFR', $metar['category']);
$this->assertNull($metar['wind_speed']);
$this->assertEquals(6.21, $metar['visibility']['mi']);
}
/**
* Visibility in KM not parsed
*

View File

@@ -21,6 +21,7 @@ use App\Services\BidService;
use App\Services\FlightService;
use App\Services\PirepService;
use App\Services\UserService;
use App\Support\Units\Fuel;
use Carbon\Carbon;
use Illuminate\Support\Facades\Notification;
@@ -164,6 +165,8 @@ class PIREPTest extends TestCase
$body = $response->json('data');
// Check that it has the fuel units
$this->assertHasKeys($body['block_fuel'], ['lbs', 'kg']);
$this->assertEquals($pirep->block_fuel, $body['block_fuel']['lbs']);
$this->assertHasKeys($body['fuel_used'], ['lbs', 'kg']);
$this->assertEquals($pirep->fuel_used, $body['fuel_used']['lbs']);
@@ -174,6 +177,24 @@ class PIREPTest extends TestCase
// Check the planned_distance field
$this->assertHasKeys($body['planned_distance'], ['km', 'nmi', 'mi']);
$this->assertEquals($pirep->planned_distance, $body['planned_distance']['nmi']);
//Check conversion on save
$val = random_int(1000, 9999999);
$pirep->block_fuel = $val;
$pirep->fuel_used = $val;
// no conversion with plain numbers
$this->assertEquals($pirep->block_fuel, $val);
$this->assertEquals($pirep->fuel_used, $val);
// no conversion with lbs
$pirep->block_fuel = new Fuel($val, 'lbs');
$this->assertEquals($pirep->block_fuel, $val);
$pirep->fuel_used = new Fuel($val, 'lbs');
$this->assertEquals($pirep->fuel_used, $val);
// conversion of kg to lbs
$pirep->block_fuel = new Fuel($val, 'kg');
$this->assertEquals($pirep->block_fuel, (new Fuel($val, 'kg'))->toUnit('lbs'));
$pirep->fuel_used = new Fuel($val, 'kg');
$this->assertEquals($pirep->fuel_used, (new Fuel($val, 'kg'))->toUnit('lbs'));
}
public function testGetUserPireps()

View File

@@ -2,6 +2,7 @@
namespace Tests;
use App\Repositories\KvpRepository;
use App\Support\ICAO;
use App\Support\Units\Time;
use App\Support\Utils;
@@ -15,6 +16,24 @@ class UtilsTest extends TestCase
$this->assertNotNull($carbon);
}
/**
* Simple test for KVP
*/
public function testKvp()
{
/** @var KvpRepository $kvpRepo */
$kvpRepo = app(KvpRepository::class);
$kvpRepo->save('testkey', 'some value');
$this->assertEquals('some value', $kvpRepo->get('testkey'));
// test that default value is working
$this->assertEquals('default value', $kvpRepo->get('unknownkey', 'default value'));
// try saving an integer
$kvpRepo->save('intval', 1);
$this->assertEquals(1, $kvpRepo->get('intval'));
}
/**
* @throws \Exception
*/