1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-01-18 13:56:31 +01:00

Move the import form to fetch via JS to prevent request timeouts (#120)

This commit is contained in:
Kovah 2020-05-05 23:24:17 +02:00
parent 907790da49
commit d2a61c868e
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
8 changed files with 123 additions and 27 deletions

View File

@ -3,15 +3,17 @@
namespace App\Http\Controllers\App;
use App\Helper\HtmlMeta;
use App\Helper\LinkAce;
use App\Helper\LinkIconMapper;
use App\Http\Controllers\Controller;
use App\Http\Requests\DoImportRequest;
use App\Models\Link;
use App\Models\Tag;
use Carbon\Carbon;
use Exception;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
/**
@ -21,12 +23,7 @@ use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
*/
class ImportController extends Controller
{
/**
* Show the application dashboard.
*
* @return Response
*/
public function getImport()
public function getImport(): View
{
return view('actions.import.import');
}
@ -35,7 +32,7 @@ class ImportController extends Controller
* Permanently delete entries for a model from the trash
*
* @param DoImportRequest $request
* @return Response
* @return JsonResponse
* @throws FileNotFoundException
*/
public function doImport(DoImportRequest $request)
@ -44,11 +41,24 @@ class ImportController extends Controller
$parser = new NetscapeBookmarkParser();
$links = $parser->parseString($data);
try {
$links = $parser->parseString($data);
} catch (Exception $e) {
Log::error($e->getMessage());
return response()->json([
'success' => false,
'message' => trans('import.import_error'),
]);
}
if (empty($links)) {
flash(trans('import.import_empty'), 'warning');
return redirect()->back();
// This will never be reached at the moment because the bookmark parser is not capable of handling
// empty bookmarks exports. See https://github.com/shaarli/netscape-bookmark-parser/issues/50
return response()->json([
'success' => false,
'message' => trans('import.import_empty'),
]);
}
$userId = auth()->id();
@ -93,11 +103,12 @@ class ImportController extends Controller
$imported++;
}
flash(trans('import.import_successfully', [
'imported' => $imported,
'skipped' => $skipped,
]), 'success');
return redirect()->back();
return response()->json([
'success' => true,
'message' => trans('import.import_successfully', [
'imported' => $imported,
'skipped' => $skipped,
]),
]);
}
}

View File

@ -10,6 +10,7 @@ import ShareToggleAll from './components/ShareToggleAll';
import GenerateApiToken from './components/GenerateApiToken';
import GenerateCronToken from './components/GenerateCronToken';
import UpdateCheck from './components/UpdateCheck';
import Import from './components/Import';
// Register view components
function registerViews () {
@ -23,6 +24,7 @@ function registerViews () {
register('.api-token', GenerateApiToken);
register('.cron-token', GenerateCronToken);
register('.update-check', UpdateCheck);
register('.import-form', Import);
}
if (document.readyState !== 'loading') {

View File

@ -0,0 +1,64 @@
export default class Import {
constructor ($el) {
this.$el = $el;
this.$file = $el.querySelector('#import-file');
this.$submit = $el.querySelector('.import-submit');
this.$submitProcessing = $el.querySelector('.import-submit-processing');
this.$submitDefault = $el.querySelector('.import-submit-default');
this.$alertNetworkError = $el.querySelector('.import-alert-networkerror');
this.$alertWarning = $el.querySelector('.import-alert-warning');
this.$alertSuccess = $el.querySelector('.import-alert-success');
this.$submit.addEventListener('click', this.onSubmit.bind(this));
}
onSubmit () {
this.toggleSubmitBtnState();
const formData = new FormData();
formData.append('import-file', this.$file.files[0]);
formData.append('_token', window.appData.user.token);
fetch(this.$el.dataset.action, {
method: 'POST',
credentials: 'same-origin',
headers: {'Accept': 'application/json'},
body: formData
}).then((response) => {
if (response.ok === false) {
console.log(response);
this.$alertNetworkError.classList.remove('d-none');
return response;
}
return response.json();
}).then((result) => {
this.toggleSubmitBtnState();
if (result.ok === false) {
return;
}
if (result.success) {
this.$alertSuccess.innerText = result.message;
this.$alertSuccess.classList.remove('d-none');
} else {
this.$alertWarning.innerText = result.message;
this.$alertWarning.classList.remove('d-none');
}
});
}
toggleSubmitBtnState (isProcessing) {
this.$submit.disabled = !isProcessing;
this.$submitProcessing.classList.toggle('d-none');
this.$submitDefault.classList.toggle('d-none');
}
}

View File

@ -4,6 +4,7 @@ import { faBan } from '@fortawesome/free-solid-svg-icons/faBan';
import { faBookmark } from '@fortawesome/free-solid-svg-icons/faBookmark';
import { faCaretDown } from '@fortawesome/free-solid-svg-icons/faCaretDown';
import { faCheck } from '@fortawesome/free-solid-svg-icons/faCheck';
import { faCog } from '@fortawesome/free-solid-svg-icons/faCog';
import { faEdit } from '@fortawesome/free-solid-svg-icons/faEdit';
import { faEnvelope } from '@fortawesome/free-solid-svg-icons/faEnvelope';
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons/faExternalLinkAlt';
@ -150,6 +151,7 @@ export function initFontAwesome () {
library.add(faBookmark);
library.add(faCaretDown);
library.add(faCheck);
library.add(faCog);
library.add(faEdit);
library.add(faEnvelope);
library.add(faExternalLinkAlt);

View File

@ -2,10 +2,13 @@
return [
'import' => 'Import',
'start_import' => 'Start Import',
'import_running' => 'Import running...',
'import_file' => 'File for Import',
'import_help' => 'You can import your existing browser bookmarks here. Usually, bookmarks are exported into an .html file by your browser. Select the file here and start the import.<br>Depending on the number of bookmarks this process may take some time.',
'import_networkerror' => 'Something went wrong while trying to import the bookmarks. Please check your browser console for details or consult the application logs.',
'import_error' => 'Something went wrong while trying to import the bookmarks. Please consult the application logs.',
'import_empty' => 'Could not import any bookmarks. Either the uploaded file is corrupt or empty.',
'import_successfully' => ':imported links imported successfully, :skipped skipped.',
];

View File

@ -8,8 +8,7 @@
</div>
<div class="card-body">
<form id="import-form" action="{{ route('do-import') }}" method="post" enctype="multipart/form-data">
@csrf
<form class="import-form" data-action="{{ route('do-import') }}" data-csrf="{{ csrf_token() }}">
<p>@lang('import.import_help')</p>
@ -26,9 +25,21 @@
@endif
</div>
<button type="submit" class="btn btn-primary import-submit">
<i class="fas fa-file-import mr-2"></i>
@lang('import.start_import')
<div class="import-alerts">
<div class="import-alert-networkerror alert alert-danger d-none">@lang('import.import_error')</div>
<div class="import-alert-warning alert alert-warning d-none"></div>
<div class="import-alert-success alert alert-success d-none"></div>
</div>
<button type="button" class="btn btn-primary import-submit">
<span class="import-submit-processing d-none">
<i class="fas fa-cog fa-spin mr-2"></i>
@lang('import.import_running')
</span>
<span class="import-submit-default">
<i class="fas fa-file-import mr-2"></i>
@lang('import.start_import')
</span>
</button>
</form>

View File

@ -2,8 +2,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ systemsettings('system_page_title') ?: config('app.name', 'LinkAce') }}</title>
@include('partials.favicon')

View File

@ -1,6 +1,6 @@
<?php
namespace Tests\Database;
namespace Tests\Controller\App;
use App\Models\Link;
use App\Models\User;
@ -39,9 +39,14 @@ class ImportControllerTest extends TestCase
$response = $this->post('import', [
'import-file' => $file,
], [
'Accept' => 'application/json',
]);
$response->assertStatus(302);
$response->assertStatus(200)
->assertJson([
'success' => true,
]);
$linkCount = Link::count();
$this->assertEquals(5, $linkCount);