mirror of
https://github.com/henrywhitaker3/Speedtest-Tracker.git
synced 2025-12-21 13:23:04 +01:00
Added optional authentication
This commit is contained in:
59
app/Console/Commands/AuthenticationCommand.php
Normal file
59
app/Console/Commands/AuthenticationCommand.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class AuthenticationCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'speedtest:auth {--enable} {--disable}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Toggle authentication for the app';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$opts = $this->options();
|
||||||
|
|
||||||
|
if($opts['enable'] === true && $opts['disable'] === true) {
|
||||||
|
$this->warn('Please specify only ONE of --enable and --disable');
|
||||||
|
} else if($opts['enable'] === false && $opts['disable'] === false) {
|
||||||
|
$this->warn('You need to specify either --enable OR --disable');
|
||||||
|
} else {
|
||||||
|
if($opts['enable'] === true) {
|
||||||
|
$this->info('Enabling authentication');
|
||||||
|
SettingsHelper::set('auth', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($opts['disable'] === true) {
|
||||||
|
$this->info('Disabling authentication');
|
||||||
|
SettingsHelper::set('auth', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
app/Console/Commands/ClearOldSessionsCommand.php
Normal file
48
app/Console/Commands/ClearOldSessionsCommand.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Auth\LoginSession;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
class ClearOldSessionsCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'speedtest:clear-sessions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Clear expired sessions from database';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$now = Carbon::now()->timestamp;
|
||||||
|
$sessions = LoginSession::where('expires', '<=', $now)
|
||||||
|
->delete();
|
||||||
|
$this->info('Invalidated expired sessions');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ class Kernel extends ConsoleKernel
|
|||||||
{
|
{
|
||||||
$schedule->job(new SpeedtestJob(true, config('integrations')))->cron(SettingsHelper::get('schedule')['value']);
|
$schedule->job(new SpeedtestJob(true, config('integrations')))->cron(SettingsHelper::get('schedule')['value']);
|
||||||
$schedule->command('speedtest:overview')->cron('0 ' . SettingsHelper::get('speedtest_overview_time')->value . ' * * *');
|
$schedule->command('speedtest:overview')->cron('0 ' . SettingsHelper::get('speedtest_overview_time')->value . ' * * *');
|
||||||
|
$schedule->command('speedtest:clear-sessions')->everyMinute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ class SettingsHelper {
|
|||||||
'slack_webhook' => SettingsHelper::settingIsEditable('slack_webhook'),
|
'slack_webhook' => SettingsHelper::settingIsEditable('slack_webhook'),
|
||||||
'telegram_bot_token' => SettingsHelper::settingIsEditable('telegram_bot_token'),
|
'telegram_bot_token' => SettingsHelper::settingIsEditable('telegram_bot_token'),
|
||||||
'telegram_chat_id' => SettingsHelper::settingIsEditable('telegram_chat_id'),
|
'telegram_chat_id' => SettingsHelper::settingIsEditable('telegram_chat_id'),
|
||||||
]
|
],
|
||||||
|
'auth' => (bool)SettingsHelper::get('auth')->value
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ class SpeedtestHelper {
|
|||||||
$range = [
|
$range = [
|
||||||
Carbon::today()
|
Carbon::today()
|
||||||
];
|
];
|
||||||
for($i = 0; $i < $days; $i++) {
|
for($i = 0; $i < ($days - 1); $i++) {
|
||||||
$prev = end($range);
|
$prev = end($range);
|
||||||
$new = $prev->copy()->subDays(1);
|
$new = $prev->copy()->subDays(1);
|
||||||
array_push($range, $new);
|
array_push($range, $new);
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ use App\Helpers\EmailVerificationHelper;
|
|||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\LoginSession;
|
use App\LoginSession;
|
||||||
|
use App\Rules\CurrentPasswordMatches;
|
||||||
use App\User;
|
use App\User;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use Hash;
|
||||||
use Illuminate\Support\Facades\Request as RequestFacade;
|
use Illuminate\Support\Facades\Request as RequestFacade;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Log;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
@@ -164,6 +167,10 @@ class AuthController extends Controller
|
|||||||
[ 'expires', '>', time() ]
|
[ 'expires', '>', time() ]
|
||||||
])->get();
|
])->get();
|
||||||
|
|
||||||
|
$sessions = $sessions->map(function ($item) {
|
||||||
|
return collect($item)->forget(['token']);
|
||||||
|
});
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get auth sessions',
|
'method' => 'get auth sessions',
|
||||||
'response' => $sessions
|
'response' => $sessions
|
||||||
@@ -211,4 +218,36 @@ class AuthController extends Controller
|
|||||||
'success' => true,
|
'success' => true,
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function changePassword(Request $request)
|
||||||
|
{
|
||||||
|
$rules = [
|
||||||
|
'currentPassword' => [ 'string', 'required', new CurrentPasswordMatches() ],
|
||||||
|
'newPassword' => [ 'required', 'string', 'confirmed', 'min:8' ],
|
||||||
|
'logoutDevices' => [ 'required', 'bool' ]
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules);
|
||||||
|
if($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'reset password',
|
||||||
|
'success' => false,
|
||||||
|
'error' => $validator->errors()
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
$user->password = $request->newPassword;
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
if($request->logoutDevices == true) {
|
||||||
|
AuthLoginSession::where('user_id', $user->id)->update([ 'active' => false ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'reset password',
|
||||||
|
'success' => true
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Helpers\BackupHelper;
|
use App\Helpers\BackupHelper;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@@ -12,6 +13,13 @@ use Illuminate\Http\JsonResponse;
|
|||||||
class BackupController extends Controller
|
class BackupController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get backup of speedtests
|
* Get backup of speedtests
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ use Ramsey\Uuid\Exception\InvalidUuidStringException;
|
|||||||
|
|
||||||
class IntegrationsController extends Controller
|
class IntegrationsController extends Controller
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the healthchecks config
|
* Test the healthchecks config
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ use Illuminate\Support\Collection;
|
|||||||
|
|
||||||
class SettingsController extends Controller
|
class SettingsController extends Controller
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api')
|
||||||
|
->except([ 'config' ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all settings
|
* Return all settings
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
use App\Jobs\SpeedtestJob;
|
use App\Jobs\SpeedtestJob;
|
||||||
use App\Speedtest;
|
use App\Speedtest;
|
||||||
@@ -15,6 +16,13 @@ use Illuminate\Http\JsonResponse;
|
|||||||
|
|
||||||
class SpeedtestController extends Controller
|
class SpeedtestController extends Controller
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api')
|
||||||
|
->only([ 'run', 'delete', 'deleteAll' ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns paginated list of speedtests
|
* Returns paginated list of speedtests
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Updater;
|
use Updater;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -9,6 +10,12 @@ use Illuminate\Http\JsonResponse;
|
|||||||
|
|
||||||
class UpdateController extends Controller
|
class UpdateController extends Controller
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for new update
|
* Check for new update
|
||||||
|
|||||||
41
app/Rules/CurrentPasswordMatches.php
Normal file
41
app/Rules/CurrentPasswordMatches.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Rules;
|
||||||
|
|
||||||
|
use Hash;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
class CurrentPasswordMatches implements Rule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new rule instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the validation rule passes.
|
||||||
|
*
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
return Hash::check($value, auth()->user()->password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation error message.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return 'The current password doesn\'t match.';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddAuthenticationSettings extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if(!SettingsHelper::get('auth')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'auth',
|
||||||
|
'value' => false,
|
||||||
|
'description' => 'Enable authentication.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'auth',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -6156,6 +6156,11 @@
|
|||||||
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==",
|
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"js-cookie": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="
|
||||||
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||||
"chart.js": "^2.9.3",
|
"chart.js": "^2.9.3",
|
||||||
"csv-file-validator": "^1.8.0",
|
"csv-file-validator": "^1.8.0",
|
||||||
|
"js-cookie": "^2.2.1",
|
||||||
"react-bootstrap": "^1.3.0",
|
"react-bootstrap": "^1.3.0",
|
||||||
"react-chartjs-2": "^2.10.0",
|
"react-chartjs-2": "^2.10.0",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
|
|||||||
10907
public/css/app.css
vendored
10907
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
137935
public/js/app.js
vendored
137935
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
86
resources/js/components/Authentication/Authentication.js
vendored
Normal file
86
resources/js/components/Authentication/Authentication.js
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Container, Row, Col, Collapse, Button, Modal } from 'react-bootstrap';
|
||||||
|
import SessionsTable from './SessionsTable';
|
||||||
|
import ResetPassword from './ResetPassword';
|
||||||
|
|
||||||
|
export default class Authentication extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
showCollapse: false,
|
||||||
|
showModal: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCollapse = () => {
|
||||||
|
if(this.state.show) {
|
||||||
|
this.setState({
|
||||||
|
showCollapse: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
showCollapse: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleModal = () => {
|
||||||
|
if(this.state.show) {
|
||||||
|
this.setState({
|
||||||
|
showModal: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
showModal: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var showCollapse = this.state.showCollapse;
|
||||||
|
var showModal = this.state.showModal;
|
||||||
|
|
||||||
|
if( (window.config.auth == true && window.authenticated == true)) {
|
||||||
|
return (
|
||||||
|
<Container className="mb-4">
|
||||||
|
<Row>
|
||||||
|
<Col sm={{ span: 12 }} className="mb-3 text-center">
|
||||||
|
<div className="mouse" aria-controls="testsTable" onClick={this.toggleCollapse} aria-expanded={showCollapse}>
|
||||||
|
<h4 className="d-inline mr-2">Authentication</h4>
|
||||||
|
{(showCollapse) ?
|
||||||
|
<span className="ti-angle-up"></span>
|
||||||
|
:
|
||||||
|
<span className="ti-angle-down"></span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Collapse in={showCollapse}>
|
||||||
|
<div>
|
||||||
|
<Row>
|
||||||
|
<Col sm={{ span: 12 }} className="text-center">
|
||||||
|
<ResetPassword />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col sm={{ span: 12 }} className="text-center">
|
||||||
|
<SessionsTable />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<></>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById('Authentication')) {
|
||||||
|
ReactDOM.render(<Authentication />, document.getElementById('Authentication'));
|
||||||
|
}
|
||||||
108
resources/js/components/Authentication/ResetPassword.js
vendored
Normal file
108
resources/js/components/Authentication/ResetPassword.js
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Container, Row, Col, Collapse, Button, Modal, Form } from 'react-bootstrap';
|
||||||
|
import SessionsTable from './SessionsTable';
|
||||||
|
import Axios from 'axios';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
export default class ResetPassword extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
showModal: false,
|
||||||
|
currentPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
newPasswordConfirmation: '',
|
||||||
|
logoutDevices: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleModal = () => {
|
||||||
|
if(this.state.showModal) {
|
||||||
|
this.setState({
|
||||||
|
showModal: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
showModal: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTextField = (e) => {
|
||||||
|
this.setState({
|
||||||
|
[e.target.id]: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCheckbox = (e) => {
|
||||||
|
this.setState({
|
||||||
|
[e.target.id]: e.target.checked
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changePassword = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
currentPassword: this.state.currentPassword,
|
||||||
|
newPassword: this.state.newPassword,
|
||||||
|
newPassword_confirmation: this.state.newPasswordConfirmation,
|
||||||
|
logoutDevices: this.state.logoutDevices
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = 'api/auth/change-password?token=' + window.token;
|
||||||
|
Axios.post(url, data)
|
||||||
|
.then((resp) => {
|
||||||
|
toast.success('Password updated');
|
||||||
|
this.toggleModal();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if(err.response) {
|
||||||
|
for(var key in err.response.data.error) {
|
||||||
|
toast.error(err.response.data.error[key][0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var showModal = this.state.showModal;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button variant="primary" onClick={this.toggleModal} className="mb-3">Change password</Button>
|
||||||
|
<Modal show={showModal} onHide={this.toggleModal}>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>Change password</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<Form onSubmit={this.changePassword}>
|
||||||
|
<Form.Group controlId="currentPassword">
|
||||||
|
<Form.Label>Current password</Form.Label>
|
||||||
|
<Form.Control type="password" onInput={this.updateTextField} required />
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group controlId="newPassword">
|
||||||
|
<Form.Label>New Password</Form.Label>
|
||||||
|
<Form.Control type="password" onInput={this.updateTextField} required />
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group controlId="newPasswordConfirmation">
|
||||||
|
<Form.Label>Confirm New Password</Form.Label>
|
||||||
|
<Form.Control type="password" onInput={this.updateTextField} required />
|
||||||
|
</Form.Group>
|
||||||
|
<Button variant="primary" type="submit" className="d-inline-block">Change password</Button>
|
||||||
|
<Form.Group controlId="logoutDevices" className="d-inline-block ml-2">
|
||||||
|
<Form.Check type="checkbox" label="Log everywhere out" onInput={this.updateCheckbox} />
|
||||||
|
</Form.Group>
|
||||||
|
</Form>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById('ResetPassword')) {
|
||||||
|
ReactDOM.render(<ResetPassword />, document.getElementById('ResetPassword'));
|
||||||
|
}
|
||||||
67
resources/js/components/Authentication/SessionsTable.js
vendored
Normal file
67
resources/js/components/Authentication/SessionsTable.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Container, Row, Col, Table } from 'react-bootstrap';
|
||||||
|
import Axios from 'axios';
|
||||||
|
|
||||||
|
export default class SessionsTable extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
sessions: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getSessions();
|
||||||
|
}
|
||||||
|
|
||||||
|
getSessions = () => {
|
||||||
|
var url = 'api/auth/sessions?token=' + window.token;
|
||||||
|
|
||||||
|
Axios.get(url)
|
||||||
|
.then((resp) => {
|
||||||
|
this.setState({
|
||||||
|
sessions: resp.data.response
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var sessions = this.state.sessions;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className="mb-4">
|
||||||
|
<Row>
|
||||||
|
<Col sm={{ span: 12 }} className="mb-3 text-center">
|
||||||
|
<h5>Login Sessions</h5>
|
||||||
|
<Table responsive>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>IP</th>
|
||||||
|
<th>Expires</th>
|
||||||
|
<th>Created at</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{sessions.map((e,i) => {
|
||||||
|
return(
|
||||||
|
<tr key={i}>
|
||||||
|
<td>{e.ip}</td>
|
||||||
|
<td>{new Date(e.expires * 1000).toLocaleDateString() + ' ' + new Date(e.expires * 1000).toLocaleTimeString()}</td>
|
||||||
|
<td>{e.created_at}</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById('SessionsTable')) {
|
||||||
|
ReactDOM.render(<SessionsTable />, document.getElementById('SessionsTable'));
|
||||||
|
}
|
||||||
2
resources/js/components/Data/Backup.js
vendored
2
resources/js/components/Data/Backup.js
vendored
@@ -6,7 +6,7 @@ import Axios from 'axios';
|
|||||||
|
|
||||||
export default class Backup extends Component {
|
export default class Backup extends Component {
|
||||||
backup = (format) => {
|
backup = (format) => {
|
||||||
var url = 'api/backup?format=' + format;
|
var url = 'api/backup?format=' + format + '&token=' + window.token;
|
||||||
|
|
||||||
toast.info('Your backup has started downloading...');
|
toast.info('Your backup has started downloading...');
|
||||||
|
|
||||||
|
|||||||
6
resources/js/components/Data/Changelog.js
vendored
6
resources/js/components/Data/Changelog.js
vendored
@@ -16,11 +16,13 @@ export default class Changelog extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
this.getChangelog();
|
if( (window.config.auth == true && window.authenticated == true) || window.config.auth == false) {
|
||||||
|
this.getChangelog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getChangelog = () => {
|
getChangelog = () => {
|
||||||
Axios.get('api/update/changelog')
|
Axios.get('api/update/changelog?token=' + window.token)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
changelog: resp.data.data,
|
changelog: resp.data.data,
|
||||||
|
|||||||
36
resources/js/components/Data/DataRow.js
vendored
36
resources/js/components/Data/DataRow.js
vendored
@@ -8,21 +8,27 @@ import Restore from './Restore';
|
|||||||
|
|
||||||
export default class DataRow extends Component {
|
export default class DataRow extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
if( (window.config.auth == true && window.authenticated == true) || window.config.auth == false) {
|
||||||
<Container className="mb-4">
|
return (
|
||||||
<Row>
|
<Container className="mb-4">
|
||||||
<Col sm={{ span: 12 }} className="text-center">
|
<Row>
|
||||||
<p>Use these buttons to backup/restore your data</p>
|
<Col sm={{ span: 12 }} className="text-center">
|
||||||
</Col>
|
<p>Use these buttons to backup/restore your data</p>
|
||||||
</Row>
|
</Col>
|
||||||
<Row>
|
</Row>
|
||||||
<Col sm={{ span: 12 }} className="text-center">
|
<Row>
|
||||||
<Backup />
|
<Col sm={{ span: 12 }} className="text-center">
|
||||||
<Restore />
|
<Backup />
|
||||||
</Col>
|
<Restore />
|
||||||
</Row>
|
</Col>
|
||||||
</Container>
|
</Row>
|
||||||
);
|
</Container>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
resources/js/components/Data/Restore.js
vendored
2
resources/js/components/Data/Restore.js
vendored
@@ -148,7 +148,7 @@ export default class Restore extends Component {
|
|||||||
|
|
||||||
uploadFile = () => {
|
uploadFile = () => {
|
||||||
var data = { data: this.state.data, format: this.state.format };
|
var data = { data: this.state.data, format: this.state.format };
|
||||||
var url = 'api/restore';
|
var url = 'api/restore?token=' + window.token;
|
||||||
|
|
||||||
Axios.post(url, data)
|
Axios.post(url, data)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default class LatestResults extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newScan = () => {
|
newScan = () => {
|
||||||
var url = 'api/speedtest/run';
|
var url = 'api/speedtest/run?token=' + window.token;
|
||||||
|
|
||||||
Axios.get(url)
|
Axios.get(url)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
@@ -78,25 +78,47 @@ export default class LatestResults extends Component {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
} else if(data === false) {
|
} else if(data === false) {
|
||||||
return (
|
if( (window.config.auth == true && window.authenticated == true) || window.config.auth == false) {
|
||||||
<Container fluid>
|
return (
|
||||||
<Row>
|
<Container fluid>
|
||||||
<Col sm={{ span: 12 }} className="text-center">
|
<Row>
|
||||||
<div>
|
<Col sm={{ span: 12 }} className="text-center">
|
||||||
<Button variant="primary" onClick={this.newScan}>Start your first test!</Button>
|
<div>
|
||||||
</div>
|
<Button variant="primary" onClick={this.newScan}>Start your first test!</Button>
|
||||||
</Col>
|
</div>
|
||||||
</Row>
|
</Col>
|
||||||
</Container>
|
</Row>
|
||||||
);
|
</Container>
|
||||||
|
);
|
||||||
|
} else if(window.config.auth == true && window.authenticated == false) {
|
||||||
|
return (
|
||||||
|
<Container fluid>
|
||||||
|
<Row>
|
||||||
|
<Col sm={{ span: 12 }} className="text-center">
|
||||||
|
<div>
|
||||||
|
<p>Please login to run the first test</p>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Container fluid>
|
<Container fluid>
|
||||||
<Row>
|
<Row>
|
||||||
<Col sm={{ span: 12 }} className="text-center mb-2">
|
<Col sm={{ span: 12 }} className="text-center mb-2">
|
||||||
<div>
|
<div>
|
||||||
<Button className="d-inline-block mx-3 mb-2" variant="primary" onClick={this.newScan}>Test again</Button>
|
{(window.config.auth == true && window.authenticated == true) || window.config.auth == false ?
|
||||||
<p className="text-muted mb-0 d-inline-block">Last test performed at: {new Date(data.data.created_at).toLocaleString()}</p>
|
<div>
|
||||||
|
<Button className="d-inline-block mx-3 mb-2" variant="primary" onClick={this.newScan}>Test again</Button>
|
||||||
|
<p className="text-muted mb-0 d-inline-block">Last test performed at: {new Date(data.data.created_at).toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<p className="text-muted mb-0 d-inline-block">Last test performed at: {new Date(data.data.created_at).toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
6
resources/js/components/Home/HomePage.js
vendored
6
resources/js/components/Home/HomePage.js
vendored
@@ -6,6 +6,8 @@ import Footer from './Footer';
|
|||||||
import DataRow from '../Data/DataRow';
|
import DataRow from '../Data/DataRow';
|
||||||
import TestsTable from '../Graphics/TestsTable';
|
import TestsTable from '../Graphics/TestsTable';
|
||||||
import Settings from '../Settings/Settings';
|
import Settings from '../Settings/Settings';
|
||||||
|
import Login from '../Login';
|
||||||
|
import Authentication from '../Authentication/Authentication';
|
||||||
|
|
||||||
export default class HomePage extends Component {
|
export default class HomePage extends Component {
|
||||||
|
|
||||||
@@ -13,10 +15,14 @@ export default class HomePage extends Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="my-4">
|
<div className="my-4">
|
||||||
|
{(window.config.auth == true && window.authenticated == false) &&
|
||||||
|
<Login />
|
||||||
|
}
|
||||||
<LatestResults />
|
<LatestResults />
|
||||||
<HistoryGraph />
|
<HistoryGraph />
|
||||||
<TestsTable />
|
<TestsTable />
|
||||||
<Settings />
|
<Settings />
|
||||||
|
<Authentication />
|
||||||
<DataRow />
|
<DataRow />
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
77
resources/js/components/Login.js
vendored
77
resources/js/components/Login.js
vendored
@@ -1,10 +1,11 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Container, Row, Form, Toast } from 'react-bootstrap';
|
import { Container, Row, Form, Toast, Modal } from 'react-bootstrap';
|
||||||
import { Col } from 'react-bootstrap';
|
import { Col } from 'react-bootstrap';
|
||||||
import { Button } from 'react-bootstrap';
|
import { Button } from 'react-bootstrap';
|
||||||
import Axios from 'axios';
|
import Axios from 'axios';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
|
||||||
export default class Login extends Component {
|
export default class Login extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -12,59 +13,75 @@ export default class Login extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loginEmailInput: '',
|
loginEmailInput: '',
|
||||||
loginPasswordInput: '',
|
loginPasswordInput: ''
|
||||||
toast: null,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateTextField = (e) => {
|
||||||
|
this.setState({
|
||||||
|
[e.target.id]: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
login = (e) => {
|
login = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
email: this.state.loginEmailInput,
|
email: this.state.loginEmailInput,
|
||||||
password: this.state.loginPasswordInput
|
password: this.state.loginPasswordInput
|
||||||
};
|
}
|
||||||
var url = 'api/auth/login';
|
|
||||||
|
|
||||||
|
var url = 'api/auth/login';
|
||||||
Axios.post(url, data)
|
Axios.post(url, data)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
var token = resp.data;
|
var token = resp.data.access_token;
|
||||||
this.props.setToken(token);
|
var expires = (resp.data.expires_in / 60) / 24;
|
||||||
|
Cookies.set('auth', token, { expires: expires })
|
||||||
|
window.location.reload(true);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
|
||||||
toast.error('Something went wrong. Please try again.')
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTextField = (e) => {
|
toggleShow = () => {
|
||||||
this.setState({
|
if(this.state.show) {
|
||||||
[e.target.id]: e.target.value
|
this.setState({
|
||||||
});
|
show: false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
show: true
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var error = this.state.error;
|
var show = this.state.show;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Row className="fullscreen align-items-center">
|
<Row>
|
||||||
<Col
|
<Col
|
||||||
lg={{ span: 4, offset: 4 }}
|
|
||||||
md={{ span: 6, offset: 3 }}
|
|
||||||
sm={{ span: 10, offset: 1 }}
|
|
||||||
xs={{ span: 12 }}
|
xs={{ span: 12 }}
|
||||||
|
className="pb-2 text-center"
|
||||||
>
|
>
|
||||||
<Form onSubmit={this.login}>
|
<Button variant="primary" onClick={this.toggleShow}>Login</Button>
|
||||||
<Form.Group controlId="loginEmailInput">
|
<Modal show={show} onHide={this.toggleShow}>
|
||||||
<Form.Label>Email address</Form.Label>
|
<Modal.Header closeButton>
|
||||||
<Form.Control type="email" placeholder="admin@admin.com" onInput={this.updateTextField} required />
|
<Modal.Title>Login</Modal.Title>
|
||||||
</Form.Group>
|
</Modal.Header>
|
||||||
<Form.Group controlId="loginPasswordInput">
|
<Modal.Body>
|
||||||
<Form.Label>Password</Form.Label>
|
<Form onSubmit={this.login}>
|
||||||
<Form.Control type="password" onInput={this.updateTextField} required />
|
<Form.Group controlId="loginEmailInput">
|
||||||
</Form.Group>
|
<Form.Label>Email address</Form.Label>
|
||||||
<Button variant="primary" type="submit">Login</Button>
|
<Form.Control type="email" placeholder="admin@admin.com" onInput={this.updateTextField} required />
|
||||||
</Form>
|
</Form.Group>
|
||||||
|
<Form.Group controlId="loginPasswordInput">
|
||||||
|
<Form.Label>Password</Form.Label>
|
||||||
|
<Form.Control type="password" onInput={this.updateTextField} required />
|
||||||
|
</Form.Group>
|
||||||
|
<Button variant="primary" type="submit">Login</Button>
|
||||||
|
</Form>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
72
resources/js/components/Settings/Settings.js
vendored
72
resources/js/components/Settings/Settings.js
vendored
@@ -19,7 +19,9 @@ export default class Settings extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
this.getData();
|
if( (window.config.auth == true && window.authenticated == true) || window.config.auth == false) {
|
||||||
|
this.getData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleShow = () => {
|
toggleShow = () => {
|
||||||
@@ -35,7 +37,7 @@ export default class Settings extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getData = () => {
|
getData = () => {
|
||||||
var url = 'api/settings/';
|
var url = 'api/settings/?token=' + window.token;
|
||||||
|
|
||||||
Axios.get(url)
|
Axios.get(url)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
@@ -53,6 +55,7 @@ export default class Settings extends Component {
|
|||||||
|
|
||||||
buildSettingsCards = () => {
|
buildSettingsCards = () => {
|
||||||
var e = this.state.data;
|
var e = this.state.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
@@ -215,39 +218,44 @@ export default class Settings extends Component {
|
|||||||
if(!loading) {
|
if(!loading) {
|
||||||
var cards = this.buildSettingsCards();
|
var cards = this.buildSettingsCards();
|
||||||
}
|
}
|
||||||
|
if( (window.config.auth == true && window.authenticated == true) || window.config.auth == false) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Container className="my-4">
|
<Container className="my-4">
|
||||||
<Row>
|
<Row>
|
||||||
<Col sm={{ span: 12 }} className="mb-3 text-center">
|
<Col sm={{ span: 12 }} className="mb-3 text-center">
|
||||||
<div className="mouse" onClick={this.toggleShow}>
|
<div className="mouse" onClick={this.toggleShow}>
|
||||||
<h4 className="mb-0 mr-2 d-inline">Settings</h4>
|
<h4 className="mb-0 mr-2 d-inline">Settings</h4>
|
||||||
{(show) ?
|
{(show) ?
|
||||||
<span className="ti-angle-up"></span>
|
<span className="ti-angle-up"></span>
|
||||||
:
|
|
||||||
<span className="ti-angle-down"></span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Collapse in={show}>
|
|
||||||
<div>
|
|
||||||
<Row>
|
|
||||||
<Col sm={{ span: 12 }}>
|
|
||||||
{loading ?
|
|
||||||
<Loader small />
|
|
||||||
:
|
:
|
||||||
cards
|
<span className="ti-angle-down"></span>
|
||||||
}
|
}
|
||||||
</Col>
|
</div>
|
||||||
</Row>
|
</Col>
|
||||||
</div>
|
</Row>
|
||||||
</Collapse>
|
<Collapse in={show}>
|
||||||
</Container>
|
<div>
|
||||||
|
<Row>
|
||||||
|
<Col sm={{ span: 12 }}>
|
||||||
|
{loading ?
|
||||||
|
<Loader small />
|
||||||
|
:
|
||||||
|
cards
|
||||||
|
}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
</Container>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return(
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
40
resources/js/index.js
vendored
40
resources/js/index.js
vendored
@@ -4,10 +4,10 @@ import { BrowserRouter, Switch, Route, Redirect, useHistory } from 'react-router
|
|||||||
import Axios from 'axios';
|
import Axios from 'axios';
|
||||||
import ErrorPage from './components/ErrorPage';
|
import ErrorPage from './components/ErrorPage';
|
||||||
import Loader from './components/Loader';
|
import Loader from './components/Loader';
|
||||||
import Login from './components/Login';
|
|
||||||
import { ToastContainer } from 'react-toastify';
|
import { ToastContainer } from 'react-toastify';
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
import HomePage from './components/Home/HomePage';
|
import HomePage from './components/Home/HomePage';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
|
||||||
export default class Index extends Component {
|
export default class Index extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -15,7 +15,7 @@ export default class Index extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
redirect: false
|
redirect: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,10 +29,38 @@ export default class Index extends Component {
|
|||||||
Axios.get(url)
|
Axios.get(url)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
window.config = resp.data;
|
window.config = resp.data;
|
||||||
this.setState({
|
if(window.config.auth === true) {
|
||||||
loading: false,
|
var authCookie = Cookies.get('auth');
|
||||||
redirect: true,
|
if(authCookie == undefined) {
|
||||||
});
|
window.authenticated = false;
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
redirect: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var url = 'api/auth/me?token=' + authCookie;
|
||||||
|
Axios.get(url)
|
||||||
|
.then((resp) => {
|
||||||
|
window.authenticated = true;
|
||||||
|
window.token = authCookie;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
Cookies.remove('auth');
|
||||||
|
window.authenticated = false;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
redirect: true,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
redirect: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
use App\Http\Controllers\SpeedtestController;
|
use App\Http\Controllers\SpeedtestController;
|
||||||
|
use App\Speedtest;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@@ -83,3 +84,29 @@ Route::group([
|
|||||||
Route::post('/bulk', 'SettingsController@bulkStore')
|
Route::post('/bulk', 'SettingsController@bulkStore')
|
||||||
->name('settings.bulk.update');
|
->name('settings.bulk.update');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::group(
|
||||||
|
[
|
||||||
|
'middleware' => 'api',
|
||||||
|
'prefix' => 'auth'
|
||||||
|
],
|
||||||
|
function ($router) {
|
||||||
|
Route::post('register', 'AuthController@register')->name('auth.register');
|
||||||
|
Route::post('login', 'AuthController@login')->middleware('throttle:60,1')->name('auth.login');
|
||||||
|
Route::get('logout', 'AuthController@logout')->name('auth.logout');
|
||||||
|
Route::get('refresh', 'AuthController@refresh')->middleware('throttle:60,1')->name('auth.refresh');
|
||||||
|
Route::get('me', 'AuthController@me')->middleware('session_active')->name('auth.me');
|
||||||
|
Route::post('change-password', 'AuthController@changePassword')->middleware('session_active')->name('auth.change_password');
|
||||||
|
|
||||||
|
Route::group(
|
||||||
|
[
|
||||||
|
'middleware' => ['api', 'session_active'],
|
||||||
|
'prefix' => 'sessions'
|
||||||
|
],
|
||||||
|
function($router) {
|
||||||
|
Route::get('/', 'AuthController@getSessions')->name('auth.sessions.all');
|
||||||
|
Route::delete('/{id}', 'AuthController@deleteSession')->name('auth.sessions.delete');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user