Merge pull request 'MTM Improve app by leveraging more Laravel code' (#111) from develop into main
Reviewed-on: #111
This commit is contained in:
commit
db2c7b19ed
36 changed files with 1331 additions and 1431 deletions
|
|
@ -6,9 +6,9 @@ namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Models\Media;
|
use App\Models\Media;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Image\ImageException;
|
||||||
|
use Illuminate\Support\Facades\Image;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Intervention\Image\Exceptions\DecoderException;
|
|
||||||
use Intervention\Image\ImageManager;
|
|
||||||
|
|
||||||
class ReprocessMediaImages extends Command
|
class ReprocessMediaImages extends Command
|
||||||
{
|
{
|
||||||
|
|
@ -16,7 +16,7 @@ class ReprocessMediaImages extends Command
|
||||||
|
|
||||||
protected $description = 'Regenerate medium and small image variants using correct aspect-ratio scaling';
|
protected $description = 'Regenerate medium and small image variants using correct aspect-ratio scaling';
|
||||||
|
|
||||||
public function handle(ImageManager $manager): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$media = Media::where('type', 'image')
|
$media = Media::where('type', 'image')
|
||||||
->whereNotNull('image_widths')
|
->whereNotNull('image_widths')
|
||||||
|
|
@ -44,10 +44,10 @@ class ReprocessMediaImages extends Command
|
||||||
|
|
||||||
$this->info("Processing: {$path}");
|
$this->info("Processing: {$path}");
|
||||||
|
|
||||||
|
$image = Image::fromStorage($path, 'public');
|
||||||
try {
|
try {
|
||||||
$file = Storage::disk('public')->get($path);
|
$image->width();
|
||||||
$image = $manager->read($file);
|
} catch (ImageException) {
|
||||||
} catch (DecoderException) {
|
|
||||||
$this->warn(' Could not decode image, skipping.');
|
$this->warn(' Could not decode image, skipping.');
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -57,11 +57,8 @@ class ReprocessMediaImages extends Command
|
||||||
$extension = array_pop($filenameParts);
|
$extension = array_pop($filenameParts);
|
||||||
$basename = trim(implode('.', $filenameParts), '.');
|
$basename = trim(implode('.', $filenameParts), '.');
|
||||||
|
|
||||||
$medium = $image->scale(width: 1000);
|
Storage::disk('public')->put($basename.'-medium.'.$extension, $image->scale(width: 1000)->toBytes());
|
||||||
Storage::disk('public')->put($basename.'-medium.'.$extension, (string) $medium->encode());
|
Storage::disk('public')->put($basename.'-small.'.$extension, $image->scale(width: 500)->toBytes());
|
||||||
|
|
||||||
$small = $image->scale(width: 500);
|
|
||||||
Storage::disk('public')->put($basename.'-small.'.$extension, (string) $small->encode());
|
|
||||||
|
|
||||||
$this->info(' Done.');
|
$this->info(' Done.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Contact;
|
use App\Models\Contact;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\BadResponseException;
|
|
||||||
use Illuminate\Filesystem\Filesystem;
|
use Illuminate\Filesystem\Filesystem;
|
||||||
|
use Illuminate\Http\Client\RequestException;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class ContactsController extends Controller
|
class ContactsController extends Controller
|
||||||
|
|
@ -113,14 +113,13 @@ class ContactsController extends Controller
|
||||||
$avatar = null;
|
$avatar = null;
|
||||||
$contact = Contact::findOrFail($contactId);
|
$contact = Contact::findOrFail($contactId);
|
||||||
if ($contact->homepage !== null && mb_strlen($contact->homepage) !== 0) {
|
if ($contact->homepage !== null && mb_strlen($contact->homepage) !== 0) {
|
||||||
$client = resolve(Client::class);
|
|
||||||
try {
|
try {
|
||||||
$response = $client->get($contact->homepage);
|
$response = Http::throw()->get($contact->homepage);
|
||||||
} catch (BadResponseException $e) {
|
} catch (RequestException $e) {
|
||||||
return redirect('/admin/contacts/'.$contactId.'/edit')
|
return redirect('/admin/contacts/'.$contactId.'/edit')
|
||||||
->with('error', 'Bad resposne from contact’s homepage');
|
->with('error', 'Bad resposne from contact’s homepage');
|
||||||
}
|
}
|
||||||
$mf2 = \Mf2\parse((string) $response->getBody(), $contact->homepage);
|
$mf2 = \Mf2\parse($response->body(), $contact->homepage);
|
||||||
foreach ($mf2['items'] as $microformat) {
|
foreach ($mf2['items'] as $microformat) {
|
||||||
if (Arr::get($microformat, 'type.0') === 'h-card') {
|
if (Arr::get($microformat, 'type.0') === 'h-card') {
|
||||||
$avatarURL = Arr::get($microformat, 'properties.photo.0.value');
|
$avatarURL = Arr::get($microformat, 'properties.photo.0.value');
|
||||||
|
|
@ -129,8 +128,8 @@ class ContactsController extends Controller
|
||||||
}
|
}
|
||||||
if ($avatarURL !== null) {
|
if ($avatarURL !== null) {
|
||||||
try {
|
try {
|
||||||
$avatar = $client->get($avatarURL);
|
$avatar = Http::throw()->get($avatarURL);
|
||||||
} catch (BadResponseException $e) {
|
} catch (RequestException $e) {
|
||||||
return redirect('/admin/contacts/'.$contactId.'/edit')
|
return redirect('/admin/contacts/'.$contactId.'/edit')
|
||||||
->with('error', 'Unable to download avatar');
|
->with('error', 'Unable to download avatar');
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +140,7 @@ class ContactsController extends Controller
|
||||||
if ($filesystem->isDirectory($directory) === false) {
|
if ($filesystem->isDirectory($directory) === false) {
|
||||||
$filesystem->makeDirectory($directory);
|
$filesystem->makeDirectory($directory);
|
||||||
}
|
}
|
||||||
$filesystem->put($directory.'/image', $avatar->getBody());
|
$filesystem->put($directory.'/image', $avatar->body());
|
||||||
|
|
||||||
return view('admin.contacts.getavatarsuccess', [
|
return view('admin.contacts.getavatarsuccess', [
|
||||||
'homepage' => parse_url($contact->homepage, PHP_URL_HOST),
|
'homepage' => parse_url($contact->homepage, PHP_URL_HOST),
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Services\TokenService;
|
use App\Services\TokenService;
|
||||||
use Exception;
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Psr7\Uri;
|
use GuzzleHttp\Psr7\Uri;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Random\RandomException;
|
use Random\RandomException;
|
||||||
|
|
@ -199,15 +198,13 @@ class IndieAuthController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise we need to check the redirect_uri is in the client_id's redirect_uris
|
// Otherwise we need to check the redirect_uri is in the client_id's redirect_uris
|
||||||
$guzzle = resolve(Client::class);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$clientInfo = $guzzle->get($clientId);
|
$clientInfo = Http::throw()->get($clientId);
|
||||||
} catch (Exception) {
|
} catch (\Throwable) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$clientInfoParsed = \Mf2\parse($clientInfo->getBody()->getContents(), $clientId);
|
$clientInfoParsed = \Mf2\parse($clientInfo->body(), $clientId);
|
||||||
|
|
||||||
$redirectUris = $clientInfoParsed['rels']['redirect_uri'] ?? [];
|
$redirectUris = $clientInfoParsed['rels']['redirect_uri'] ?? [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Image\ImageException;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Image;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Intervention\Image\ImageManager;
|
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
class MicropubMediaController extends Controller
|
class MicropubMediaController extends Controller
|
||||||
|
|
@ -111,12 +112,9 @@ class MicropubMediaController extends Controller
|
||||||
|
|
||||||
$filename = Storage::disk('local')->putFile('media', $file);
|
$filename = Storage::disk('local')->putFile('media', $file);
|
||||||
|
|
||||||
/** @var ImageManager $manager */
|
|
||||||
$manager = resolve(ImageManager::class);
|
|
||||||
try {
|
try {
|
||||||
$image = $manager->read($request->file('file'));
|
$width = Image::fromUpload($request->file('file'))->width();
|
||||||
$width = $image->width();
|
} catch (ImageException) {
|
||||||
} catch (Exception) {
|
|
||||||
// not an image
|
// not an image
|
||||||
$width = null;
|
$width = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\FileSystem\FileSystem;
|
use Illuminate\FileSystem\FileSystem;
|
||||||
|
use Illuminate\Http\Client\RequestException;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class DownloadWebMention implements ShouldQueue
|
class DownloadWebMention implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
@ -29,15 +29,15 @@ class DownloadWebMention implements ShouldQueue
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
*
|
||||||
* @throws GuzzleException
|
* @throws RequestException
|
||||||
* @throws FileNotFoundException
|
* @throws FileNotFoundException
|
||||||
*/
|
*/
|
||||||
public function handle(Client $guzzle): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$response = $guzzle->request('GET', $this->source);
|
// 4XX and 5XX responses should throw so Laravel can catch and
|
||||||
// 4XX and 5XX responses should get Guzzle to throw an exception,
|
// retry these automatically.
|
||||||
// Laravel should catch and retry these automatically.
|
$response = Http::throw()->get($this->source);
|
||||||
if ($response->getStatusCode() === 200) {
|
if ($response->status() === 200) {
|
||||||
$filesystem = new FileSystem;
|
$filesystem = new FileSystem;
|
||||||
$filename = storage_path('HTML').'/'.$this->createFilenameFromURL($this->source);
|
$filename = storage_path('HTML').'/'.$this->createFilenameFromURL($this->source);
|
||||||
// backup file first
|
// backup file first
|
||||||
|
|
@ -56,7 +56,7 @@ class DownloadWebMention implements ShouldQueue
|
||||||
// save new HTML
|
// save new HTML
|
||||||
$filesystem->put(
|
$filesystem->put(
|
||||||
$filename,
|
$filename,
|
||||||
(string) $response->getBody()
|
$response->body()
|
||||||
);
|
);
|
||||||
// remove backup if the same
|
// remove backup if the same
|
||||||
if ($filesystem->exists($filenameBackup)) {
|
if ($filesystem->exists($filenameBackup)) {
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,13 @@ declare(strict_types=1);
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Like;
|
use App\Models\Like;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||||
|
|
||||||
|
|
@ -32,13 +31,11 @@ class ProcessLike implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
|
||||||
* @throws GuzzleException
|
|
||||||
*/
|
*/
|
||||||
public function handle(Client $client, Authorship $authorship): int
|
public function handle(Authorship $authorship): int
|
||||||
{
|
{
|
||||||
$response = $client->request('GET', $this->like->url);
|
$response = Http::throw()->get($this->like->url);
|
||||||
$mf2 = \Mf2\parse((string) $response->getBody(), $this->like->url);
|
$mf2 = \Mf2\parse($response->body(), $this->like->url);
|
||||||
if (Arr::has($mf2, 'items.0.properties.content')) {
|
if (Arr::has($mf2, 'items.0.properties.content')) {
|
||||||
$this->like->content = $mf2['items'][0]['properties']['content'][0]['html'];
|
$this->like->content = $mf2['items'][0]['properties']['content'][0]['html'];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ namespace App\Jobs;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Image\ImageException;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Image;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Intervention\Image\Exceptions\DecoderException;
|
|
||||||
use Intervention\Image\ImageManager;
|
|
||||||
|
|
||||||
class ProcessMedia implements ShouldQueue
|
class ProcessMedia implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
@ -30,15 +30,16 @@ class ProcessMedia implements ShouldQueue
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*/
|
*/
|
||||||
public function handle(ImageManager $manager): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
// Load file
|
// Load file
|
||||||
$file = Storage::disk('local')->get($this->filename);
|
$file = Storage::disk('local')->get($this->filename);
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
|
$image = Image::fromStorage($this->filename, 'local');
|
||||||
try {
|
try {
|
||||||
$image = $manager->read($file);
|
$width = $image->width();
|
||||||
} catch (DecoderException) {
|
} catch (ImageException) {
|
||||||
// not an image; delete file and end job
|
// not an image; delete file and end job
|
||||||
Storage::disk('local')->delete($this->filename);
|
Storage::disk('local')->delete($this->filename);
|
||||||
|
|
||||||
|
|
@ -49,18 +50,15 @@ class ProcessMedia implements ShouldQueue
|
||||||
Storage::disk('public')->put($this->filename, $file);
|
Storage::disk('public')->put($this->filename, $file);
|
||||||
|
|
||||||
// Create smaller versions if necessary
|
// Create smaller versions if necessary
|
||||||
if ($image->width() > 1000) {
|
if ($width > 1000) {
|
||||||
$filenameParts = explode('.', $this->filename);
|
$filenameParts = explode('.', $this->filename);
|
||||||
$extension = array_pop($filenameParts);
|
$extension = array_pop($filenameParts);
|
||||||
// the following achieves this data flow
|
// the following achieves this data flow
|
||||||
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
|
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
|
||||||
$basename = trim(implode('.', $filenameParts), '.');
|
$basename = trim(implode('.', $filenameParts), '.');
|
||||||
|
|
||||||
$medium = $image->scale(width: 1000);
|
Storage::disk('public')->put($basename.'-medium.'.$extension, $image->scale(width: 1000)->toBytes());
|
||||||
Storage::disk('public')->put($basename.'-medium.'.$extension, (string) $medium->encode());
|
Storage::disk('public')->put($basename.'-small.'.$extension, $image->scale(width: 500)->toBytes());
|
||||||
|
|
||||||
$small = $image->scale(width: 500);
|
|
||||||
Storage::disk('public')->put($basename.'-small.'.$extension, (string) $small->encode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can delete the locally saved image
|
// Now we can delete the locally saved image
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,12 @@ namespace App\Jobs;
|
||||||
use App\Exceptions\RemoteContentNotFoundException;
|
use App\Exceptions\RemoteContentNotFoundException;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use App\Models\WebMention;
|
use App\Models\WebMention;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use GuzzleHttp\Exception\RequestException;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Jonnybarnes\WebmentionsParser\Exceptions\InvalidMentionException;
|
use Jonnybarnes\WebmentionsParser\Exceptions\InvalidMentionException;
|
||||||
use Jonnybarnes\WebmentionsParser\Parser;
|
use Jonnybarnes\WebmentionsParser\Parser;
|
||||||
use Mf2;
|
use Mf2;
|
||||||
|
|
@ -36,18 +35,20 @@ class ProcessWebMention implements ShouldQueue
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
*
|
||||||
* @throws RemoteContentNotFoundException
|
* @throws RemoteContentNotFoundException
|
||||||
* @throws GuzzleException
|
|
||||||
* @throws InvalidMentionException
|
* @throws InvalidMentionException
|
||||||
*/
|
*/
|
||||||
public function handle(Parser $parser, Client $guzzle): void
|
public function handle(Parser $parser): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$response = $guzzle->request('GET', $this->source);
|
$response = Http::get($this->source);
|
||||||
} catch (RequestException $e) {
|
} catch (ConnectionException) {
|
||||||
throw new RemoteContentNotFoundException;
|
throw new RemoteContentNotFoundException;
|
||||||
}
|
}
|
||||||
$this->saveRemoteContent((string) $response->getBody(), $this->source);
|
if ($response->failed()) {
|
||||||
$microformats = Mf2\parse((string) $response->getBody(), $this->source);
|
throw new RemoteContentNotFoundException;
|
||||||
|
}
|
||||||
|
$this->saveRemoteContent($response->body(), $this->source);
|
||||||
|
$microformats = Mf2\parse($response->body(), $this->source);
|
||||||
$webmentions = WebMention::where('source', $this->source)->get();
|
$webmentions = WebMention::where('source', $this->source)->get();
|
||||||
foreach ($webmentions as $webmention) {
|
foreach ($webmentions as $webmention) {
|
||||||
// check webmention still references target
|
// check webmention still references target
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\RequestException;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
|
use Illuminate\Http\Client\RequestException;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||||
|
|
||||||
|
|
@ -55,12 +56,10 @@ class SaveProfileImage implements ShouldQueue
|
||||||
&& parse_url($photo, PHP_URL_HOST) !== 'pbs.twimg.com'
|
&& parse_url($photo, PHP_URL_HOST) !== 'pbs.twimg.com'
|
||||||
&& parse_url($photo, PHP_URL_HOST) !== 'twitter.com'
|
&& parse_url($photo, PHP_URL_HOST) !== 'twitter.com'
|
||||||
) {
|
) {
|
||||||
$client = resolve(Client::class);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$response = $client->get($photo);
|
$response = Http::throw()->get($photo);
|
||||||
$image = $response->getBody();
|
$image = $response->body();
|
||||||
} catch (RequestException) {
|
} catch (ConnectionException|RequestException) {
|
||||||
// we are opening and reading the default image so that
|
// we are opening and reading the default image so that
|
||||||
$default = public_path().'/assets/profile-images/default-image';
|
$default = public_path().'/assets/profile-images/default-image';
|
||||||
$handle = fopen($default, 'rb');
|
$handle = fopen($default, 'rb');
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,15 @@ declare(strict_types=1);
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Bookmark;
|
use App\Models\Bookmark;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Http\Client\PendingRequest;
|
||||||
|
use Illuminate\Http\Client\Response;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use JsonException;
|
|
||||||
|
|
||||||
class SaveScreenshot implements ShouldQueue
|
class SaveScreenshot implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
@ -27,77 +28,68 @@ class SaveScreenshot implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
|
||||||
*
|
|
||||||
* @throws JsonException
|
|
||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
// A normal Guzzle client
|
$cloudConvert = Http::baseUrl('https://api.cloudconvert.com/v2')
|
||||||
$client = resolve(Client::class);
|
->withToken(config('services.cloudconvert.token'))
|
||||||
// A Guzzle client with a custom Middleware to retry the CloudConvert API requests
|
->throw();
|
||||||
$retryClient = resolve('RetryGuzzle');
|
|
||||||
|
|
||||||
// First request that CloudConvert takes a screenshot of the URL
|
// First request that CloudConvert takes a screenshot of the URL
|
||||||
$takeScreenshotJobResponse = $client->request('POST', 'https://api.cloudconvert.com/v2/capture-website', [
|
$takeScreenshotJobResponse = $cloudConvert->post('/capture-website', [
|
||||||
'headers' => [
|
'url' => $this->bookmark->url,
|
||||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
'output_format' => 'png',
|
||||||
],
|
'screen_width' => 1440,
|
||||||
'json' => [
|
'screen_height' => 900,
|
||||||
'url' => $this->bookmark->url,
|
'wait_until' => 'networkidle0',
|
||||||
'output_format' => 'png',
|
'wait_time' => 100,
|
||||||
'screen_width' => 1440,
|
|
||||||
'screen_height' => 900,
|
|
||||||
'wait_until' => 'networkidle0',
|
|
||||||
'wait_time' => 100,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$taskId = json_decode($takeScreenshotJobResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
|
$taskId = $takeScreenshotJobResponse->json('data.id');
|
||||||
|
|
||||||
// Now wait till the status job is finished
|
// Now wait till the status job is finished
|
||||||
$screenshotJobStatusResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$taskId, [
|
$screenshotJobStatusResponse = $this->pollUntilFinished($cloudConvert, $taskId);
|
||||||
'headers' => [
|
|
||||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
|
||||||
],
|
|
||||||
'query' => [
|
|
||||||
'include' => 'payload',
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$finishedCaptureId = json_decode($screenshotJobStatusResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
|
$finishedCaptureId = $screenshotJobStatusResponse->json('data.id');
|
||||||
|
|
||||||
// Now we can create a new job to request thst the screenshot is exported to a temporary URL we can download the screenshot from
|
// Now we can create a new job to request thst the screenshot is exported to a temporary URL we can download the screenshot from
|
||||||
$exportImageJob = $client->request('POST', 'https://api.cloudconvert.com/v2/export/url', [
|
$exportImageJob = $cloudConvert->post('/export/url', [
|
||||||
'headers' => [
|
'input' => $finishedCaptureId,
|
||||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
'archive_multiple_files' => false,
|
||||||
],
|
|
||||||
'json' => [
|
|
||||||
'input' => $finishedCaptureId,
|
|
||||||
'archive_multiple_files' => false,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$exportImageJobId = json_decode($exportImageJob->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
|
$exportImageJobId = $exportImageJob->json('data.id');
|
||||||
|
|
||||||
// Again, wait till the status of this export job is finished
|
// Again, wait till the status of this export job is finished
|
||||||
$finalImageUrlResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$exportImageJobId, [
|
$finalImageUrlResponse = $this->pollUntilFinished($cloudConvert, $exportImageJobId);
|
||||||
'headers' => [
|
|
||||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
|
||||||
],
|
|
||||||
'query' => [
|
|
||||||
'include' => 'payload',
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Now we can download the screenshot and save it to the storage
|
// Now we can download the screenshot and save it to the storage
|
||||||
$finalImageUrl = json_decode($finalImageUrlResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->result->files[0]->url;
|
$finalImageUrl = $finalImageUrlResponse->json('data.result.files.0.url');
|
||||||
|
|
||||||
$finalImageUrlContent = $client->request('GET', $finalImageUrl);
|
$finalImageUrlContent = Http::throw()->get($finalImageUrl);
|
||||||
|
|
||||||
Storage::disk('public')->put('/assets/img/bookmarks/'.$taskId.'.png', $finalImageUrlContent->getBody()->getContents());
|
Storage::disk('public')->put('/assets/img/bookmarks/'.$taskId.'.png', $finalImageUrlContent->body());
|
||||||
|
|
||||||
$this->bookmark->screenshot = $taskId;
|
$this->bookmark->screenshot = $taskId;
|
||||||
$this->bookmark->save();
|
$this->bookmark->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll a CloudConvert task until it reports a "finished" status.
|
||||||
|
*/
|
||||||
|
private function pollUntilFinished(PendingRequest $client, string $taskId): Response
|
||||||
|
{
|
||||||
|
$attempts = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$response = $client->get('/tasks/'.$taskId, ['include' => 'payload']);
|
||||||
|
$finished = $response->json('data.status') === 'finished';
|
||||||
|
if (! $finished) {
|
||||||
|
$attempts++;
|
||||||
|
usleep(1_000_000); // 1 second, matches CloudConvert's own polling guidance
|
||||||
|
}
|
||||||
|
} while (! $finished && $attempts < 5);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ declare(strict_types=1);
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use GuzzleHttp\Psr7\Header;
|
use GuzzleHttp\Psr7\Header;
|
||||||
use GuzzleHttp\Psr7\UriResolver;
|
use GuzzleHttp\Psr7\UriResolver;
|
||||||
use GuzzleHttp\Psr7\Utils;
|
use GuzzleHttp\Psr7\Utils;
|
||||||
|
|
@ -14,6 +12,7 @@ use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Mf2\Parser;
|
use Mf2\Parser;
|
||||||
|
|
||||||
|
|
@ -32,8 +31,6 @@ class SendWebMentions implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
|
||||||
* @throws GuzzleException
|
|
||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
|
@ -43,12 +40,9 @@ class SendWebMentions implements ShouldQueue
|
||||||
foreach ($urls as $url) {
|
foreach ($urls as $url) {
|
||||||
$endpoint = $this->discoverWebmentionEndpoint($url);
|
$endpoint = $this->discoverWebmentionEndpoint($url);
|
||||||
if ($endpoint !== null) {
|
if ($endpoint !== null) {
|
||||||
$guzzle = resolve(Client::class);
|
Http::asForm()->post($endpoint, [
|
||||||
$guzzle->post($endpoint, [
|
'source' => $this->note->uri,
|
||||||
'form_params' => [
|
'target' => $url,
|
||||||
'source' => $this->note->uri,
|
|
||||||
'target' => $url,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,8 +50,6 @@ class SendWebMentions implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover if a URL has a webmention endpoint.
|
* Discover if a URL has a webmention endpoint.
|
||||||
*
|
|
||||||
* @throws GuzzleException
|
|
||||||
*/
|
*/
|
||||||
public function discoverWebmentionEndpoint(string $url): ?string
|
public function discoverWebmentionEndpoint(string $url): ?string
|
||||||
{
|
{
|
||||||
|
|
@ -71,10 +63,9 @@ class SendWebMentions implements ShouldQueue
|
||||||
|
|
||||||
$endpoint = null;
|
$endpoint = null;
|
||||||
|
|
||||||
$guzzle = resolve(Client::class);
|
$response = Http::get($url);
|
||||||
$response = $guzzle->get($url);
|
|
||||||
// check HTTP Headers for webmention endpoint
|
// check HTTP Headers for webmention endpoint
|
||||||
$links = Header::parse($response->getHeader('Link'));
|
$links = Header::parse($response->header('Link'));
|
||||||
foreach ($links as $link) {
|
foreach ($links as $link) {
|
||||||
if (array_key_exists('rel', $link) && mb_stristr($link['rel'], 'webmention')) {
|
if (array_key_exists('rel', $link) && mb_stristr($link['rel'], 'webmention')) {
|
||||||
return $this->resolveUri(trim($link[0], '<>'), $url);
|
return $this->resolveUri(trim($link[0], '<>'), $url);
|
||||||
|
|
@ -82,7 +73,7 @@ class SendWebMentions implements ShouldQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
// failed to find a header so parse HTML
|
// failed to find a header so parse HTML
|
||||||
$html = (string) $response->getBody();
|
$html = $response->body();
|
||||||
|
|
||||||
if ($html === '') {
|
if ($html === '') {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class SyndicateNoteToBluesky implements ShouldQueue
|
class SyndicateNoteToBluesky implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
@ -31,29 +30,18 @@ class SyndicateNoteToBluesky implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
|
||||||
* @throws GuzzleException
|
|
||||||
*/
|
*/
|
||||||
public function handle(Client $guzzle): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$response = $guzzle->request(
|
// no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
|
||||||
'POST',
|
$response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
|
||||||
'https://brid.gy/publish/webmention',
|
'source' => $this->note->uri,
|
||||||
[
|
'target' => 'https://brid.gy/publish/bluesky',
|
||||||
'headers' => [
|
]);
|
||||||
'Accept' => 'application/json',
|
|
||||||
],
|
|
||||||
'form_params' => [
|
|
||||||
'source' => $this->note->uri,
|
|
||||||
'target' => 'https://brid.gy/publish/bluesky',
|
|
||||||
],
|
|
||||||
'http_errors' => false,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$body = json_decode((string) $response->getBody(), true);
|
$body = $response->json();
|
||||||
|
|
||||||
if ($response->getStatusCode() === 201) {
|
if ($response->status() === 201) {
|
||||||
$this->note->bluesky_url = $body['url'];
|
$this->note->bluesky_url = $body['url'];
|
||||||
$this->note->save();
|
$this->note->save();
|
||||||
|
|
||||||
|
|
@ -61,7 +49,7 @@ class SyndicateNoteToBluesky implements ShouldQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException(
|
throw new \RuntimeException(
|
||||||
'Bridgy publish to Bluesky failed: '.($body['error'] ?? (string) $response->getBody())
|
'Bridgy publish to Bluesky failed: '.($body['error'] ?? $response->body())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class SyndicateNoteToMastodon implements ShouldQueue
|
class SyndicateNoteToMastodon implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
@ -31,29 +30,18 @@ class SyndicateNoteToMastodon implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
|
||||||
* @throws GuzzleException
|
|
||||||
*/
|
*/
|
||||||
public function handle(Client $guzzle): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$response = $guzzle->request(
|
// no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
|
||||||
'POST',
|
$response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
|
||||||
'https://brid.gy/publish/webmention',
|
'source' => $this->note->uri,
|
||||||
[
|
'target' => 'https://brid.gy/publish/mastodon',
|
||||||
'headers' => [
|
]);
|
||||||
'Accept' => 'application/json',
|
|
||||||
],
|
|
||||||
'form_params' => [
|
|
||||||
'source' => $this->note->uri,
|
|
||||||
'target' => 'https://brid.gy/publish/mastodon',
|
|
||||||
],
|
|
||||||
'http_errors' => false,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$body = json_decode((string) $response->getBody(), true);
|
$body = $response->json();
|
||||||
|
|
||||||
if ($response->getStatusCode() === 201) {
|
if ($response->status() === 201) {
|
||||||
$this->note->mastodon_url = $body['url'];
|
$this->note->mastodon_url = $body['url'];
|
||||||
$this->note->save();
|
$this->note->save();
|
||||||
|
|
||||||
|
|
@ -61,7 +49,7 @@ class SyndicateNoteToMastodon implements ShouldQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException(
|
throw new \RuntimeException(
|
||||||
'Bridgy publish to Mastodon failed: '.($body['error'] ?? (string) $response->getBody())
|
'Bridgy publish to Mastodon failed: '.($body['error'] ?? $response->body())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ namespace App\Models;
|
||||||
use App\CommonMark\Generators\MentionGenerator;
|
use App\CommonMark\Generators\MentionGenerator;
|
||||||
use App\CommonMark\Renderers\MentionRenderer;
|
use App\CommonMark\Renderers\MentionRenderer;
|
||||||
use App\Observers\NoteObserver;
|
use App\Observers\NoteObserver;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||||
|
|
@ -20,6 +19,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Jonnybarnes\IndieWeb\Numbers;
|
use Jonnybarnes\IndieWeb\Numbers;
|
||||||
use Laravel\Scout\Searchable;
|
use Laravel\Scout\Searchable;
|
||||||
use League\CommonMark\Environment\Environment;
|
use League\CommonMark\Environment\Environment;
|
||||||
|
|
@ -363,18 +363,15 @@ class Note extends Model
|
||||||
$latLng = $latitude.','.$longitude;
|
$latLng = $latitude.','.$longitude;
|
||||||
|
|
||||||
return Cache::get($latLng, function () use ($latLng, $latitude, $longitude) {
|
return Cache::get($latLng, function () use ($latLng, $latitude, $longitude) {
|
||||||
$guzzle = resolve(Client::class);
|
$response = Http::withHeaders(['User-Agent' => 'jonnybarnes.uk, email jonny@jonnybarnes.uk'])
|
||||||
$response = $guzzle->request('GET', 'https://nominatim.openstreetmap.org/reverse', [
|
->get('https://nominatim.openstreetmap.org/reverse', [
|
||||||
'query' => [
|
|
||||||
'format' => 'json',
|
'format' => 'json',
|
||||||
'lat' => $latitude,
|
'lat' => $latitude,
|
||||||
'lon' => $longitude,
|
'lon' => $longitude,
|
||||||
'zoom' => 18,
|
'zoom' => 18,
|
||||||
'addressdetails' => 1,
|
'addressdetails' => 1,
|
||||||
],
|
]);
|
||||||
'headers' => ['User-Agent' => 'jonnybarnes.uk via Guzzle, email jonny@jonnybarnes.uk'],
|
$json = $response->object();
|
||||||
]);
|
|
||||||
$json = json_decode((string) $response->getBody());
|
|
||||||
if (isset($json->address->suburb)) {
|
if (isset($json->address->suburb)) {
|
||||||
$locality = $json->address->suburb;
|
$locality = $json->address->suburb;
|
||||||
if (isset($json->address->city)) {
|
if (isset($json->address->city)) {
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,11 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\ConnectException;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Middleware;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Intervention\Image\ImageManager;
|
|
||||||
use Lcobucci\JWT\Configuration;
|
use Lcobucci\JWT\Configuration;
|
||||||
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
||||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||||
|
|
@ -33,11 +29,6 @@ class AppServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
// configure Intervention/Image
|
|
||||||
$this->app->bind('Intervention\Image\ImageManager', function () {
|
|
||||||
return ImageManager::withDriver(config('image.driver'));
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paginate a standard Laravel Collection.
|
* Paginate a standard Laravel Collection.
|
||||||
*
|
*
|
||||||
|
|
@ -82,39 +73,10 @@ class AppServiceProvider extends ServiceProvider
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure Guzzle
|
|
||||||
$this->app->bind('RetryGuzzle', function () {
|
|
||||||
$handlerStack = HandlerStack::create();
|
|
||||||
$handlerStack->push(Middleware::retry(
|
|
||||||
function ($retries, $request, $response, $exception) {
|
|
||||||
// Limit the number of retries to 5
|
|
||||||
if ($retries >= 5) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry connection exceptions
|
|
||||||
if ($exception instanceof ConnectException) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry on server errors
|
|
||||||
if ($response && $response->getStatusCode() >= 500) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally for CloudConvert, retry if status is not final
|
|
||||||
return json_decode($response, false, 512, JSON_THROW_ON_ERROR)->data->status !== 'finished';
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
// Retry after 1 second
|
|
||||||
return 1000;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
return new Client(['handler' => $handlerStack]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Turn on Eloquent strict mode when developing
|
// Turn on Eloquent strict mode when developing
|
||||||
Model::shouldBeStrict(! $this->app->isProduction());
|
Model::shouldBeStrict(! $this->app->isProduction());
|
||||||
|
|
||||||
|
// Force HTTPS URL generation in production
|
||||||
|
URL::forceHttps($this->app->isProduction());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,8 @@ use App\Exceptions\InternetArchiveException;
|
||||||
use App\Jobs\ProcessBookmark;
|
use App\Jobs\ProcessBookmark;
|
||||||
use App\Models\Bookmark;
|
use App\Models\Bookmark;
|
||||||
use App\Models\Tag;
|
use App\Models\Tag;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\ClientException;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class BookmarkService
|
class BookmarkService
|
||||||
|
|
@ -55,21 +53,19 @@ class BookmarkService
|
||||||
* Given a URL, attempt to save it to the Internet Archive.
|
* Given a URL, attempt to save it to the Internet Archive.
|
||||||
*
|
*
|
||||||
* @throws InternetArchiveException
|
* @throws InternetArchiveException
|
||||||
* @throws GuzzleException
|
|
||||||
*/
|
*/
|
||||||
public function getArchiveLink(string $url): string
|
public function getArchiveLink(string $url): string
|
||||||
{
|
{
|
||||||
$client = resolve(Client::class);
|
$response = Http::get('https://web.archive.org/save/'.$url);
|
||||||
try {
|
|
||||||
$response = $client->request('GET', 'https://web.archive.org/save/'.$url);
|
if ($response->clientError()) {
|
||||||
} catch (ClientException $e) {
|
|
||||||
// throw an exception to be caught
|
// throw an exception to be caught
|
||||||
throw new InternetArchiveException;
|
throw new InternetArchiveException;
|
||||||
}
|
}
|
||||||
if ($response->hasHeader('Content-Location')) {
|
|
||||||
if (Str::startsWith(Arr::get($response->getHeader('Content-Location'), 0), '/web')) {
|
$contentLocation = $response->header('Content-Location');
|
||||||
return $response->getHeader('Content-Location')[0];
|
if ($contentLocation !== '' && Str::startsWith($contentLocation, '/web')) {
|
||||||
}
|
return $contentLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// throw an exception to be caught
|
// throw an exception to be caught
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,8 @@
|
||||||
"ext-pgsql": "*",
|
"ext-pgsql": "*",
|
||||||
"ext-sodium": "*",
|
"ext-sodium": "*",
|
||||||
"cviebrock/eloquent-sluggable": "^13.0",
|
"cviebrock/eloquent-sluggable": "^13.0",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
|
||||||
"indieauth/client": "^1.1",
|
"indieauth/client": "^1.1",
|
||||||
"intervention/image": "^3",
|
"intervention/image": "^4.0",
|
||||||
"jonnybarnes/indieweb": "~0.2",
|
"jonnybarnes/indieweb": "~0.2",
|
||||||
"jonnybarnes/webmentions-parser": "~0.5",
|
"jonnybarnes/webmentions-parser": "~0.5",
|
||||||
"laravel/framework": "^13.0",
|
"laravel/framework": "^13.0",
|
||||||
|
|
@ -27,7 +26,7 @@
|
||||||
"league/commonmark": "^2.0",
|
"league/commonmark": "^2.0",
|
||||||
"league/flysystem-aws-s3-v3": "^3.0",
|
"league/flysystem-aws-s3-v3": "^3.0",
|
||||||
"mf2/mf2": "~0.3",
|
"mf2/mf2": "~0.3",
|
||||||
"spatie/laravel-flare": "^2.2",
|
"spatie/laravel-flare": "^3.0",
|
||||||
"symfony/html-sanitizer": "^8.0",
|
"symfony/html-sanitizer": "^8.0",
|
||||||
"tempest/highlight": "^2.27",
|
"tempest/highlight": "^2.27",
|
||||||
"web-auth/webauthn-lib": "^5.0"
|
"web-auth/webauthn-lib": "^5.0"
|
||||||
|
|
@ -42,8 +41,8 @@
|
||||||
"laravel/sail": "^1.41",
|
"laravel/sail": "^1.41",
|
||||||
"mockery/mockery": "^1.6",
|
"mockery/mockery": "^1.6",
|
||||||
"nunomaduro/collision": "^8.6",
|
"nunomaduro/collision": "^8.6",
|
||||||
"phpunit/php-code-coverage": "^12.0",
|
"phpunit/php-code-coverage": "^14.0",
|
||||||
"phpunit/phpunit": "^12.5.12",
|
"phpunit/phpunit": "^13.0",
|
||||||
"spatie/laravel-ray": "^1.12",
|
"spatie/laravel-ray": "^1.12",
|
||||||
"spatie/x-ray": "^1.2"
|
"spatie/x-ray": "^1.2"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
1276
composer.lock
generated
1276
composer.lock
generated
File diff suppressed because it is too large
Load diff
151
config/flare.php
151
config/flare.php
|
|
@ -1,8 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Spatie\FlareClient\Api;
|
|
||||||
use Spatie\FlareClient\Sampling\RateSampler;
|
use Spatie\FlareClient\Sampling\RateSampler;
|
||||||
use Spatie\LaravelFlare\AttributesProviders\LaravelUserAttributesProvider;
|
|
||||||
use Spatie\LaravelFlare\FlareConfig;
|
use Spatie\LaravelFlare\FlareConfig;
|
||||||
use Spatie\LaravelFlare\Senders\LaravelHttpSender;
|
use Spatie\LaravelFlare\Senders\LaravelHttpSender;
|
||||||
|
|
||||||
|
|
@ -21,17 +19,6 @@ return [
|
||||||
|
|
||||||
'key' => env('FLARE_KEY'),
|
'key' => env('FLARE_KEY'),
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Flare Base URL
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Which server should be used to send the reports/traces to.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'base_url' => env('FLARE_BASE_URL', Api::BASE_URL),
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Collects
|
| Collects
|
||||||
|
|
@ -47,22 +34,6 @@ return [
|
||||||
extra: []
|
extra: []
|
||||||
),
|
),
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Attribute providers
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| When sending an error report or trace to Flare attributes can be added to
|
|
||||||
| the report or trace for common entries. An example of such an entry is
|
|
||||||
| the currently authenticated user. In an attribute provider you can
|
|
||||||
| specify which attributes should be sent.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'attribute_providers' => [
|
|
||||||
'user' => LaravelUserAttributesProvider::class,
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Censor data
|
| Censor data
|
||||||
|
|
@ -88,19 +59,49 @@ return [
|
||||||
'X-XSRF-TOKEN',
|
'X-XSRF-TOKEN',
|
||||||
],
|
],
|
||||||
'client_ips' => false,
|
'client_ips' => false,
|
||||||
|
'cookies' => false,
|
||||||
|
'session' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Reporting log statements
|
| Sender
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| If this setting is `false` log statements won't be sent as events to Flare,
|
| The sender is responsible for sending the error reports and traces to
|
||||||
| no matter which error level you specified in the Flare log channel.
|
| Flare. By default, Laravel Flare sends them over HTTP. To use the local
|
||||||
|
| Flare daemon, switch the sender class to
|
||||||
|
| `Spatie\FlareClient\Senders\DaemonSender::class` and set `daemon_url`.
|
||||||
|
| The daemon sender defaults to localhost on port 8787 and uses its own
|
||||||
|
| default timeouts and fallback sender config unless you override them.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'send_logs_as_events' => true,
|
'sender' => [
|
||||||
|
'class' => LaravelHttpSender::class,
|
||||||
|
'config' => [
|
||||||
|
'timeout' => 10,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
// Daemon sender example
|
||||||
|
// 'sender' => [
|
||||||
|
// 'class' => \Spatie\FlareClient\Senders\DaemonSender::class,
|
||||||
|
// 'config' => [
|
||||||
|
// 'daemon_url' => env('FLARE_DAEMON_URL', 'http://127.0.0.1:8787'),
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Report
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Flare reports errors and exceptions happening within your application.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'report' => env('FLARE_REPORT', true),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
@ -109,10 +110,27 @@ return [
|
||||||
| When reporting errors, you can specify which error levels should be
|
| When reporting errors, you can specify which error levels should be
|
||||||
| reported. By default, all error levels are reported by setting
|
| reported. By default, all error levels are reported by setting
|
||||||
| this value to `null`.
|
| this value to `null`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'report_error_levels' => null,
|
'report_error_levels' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Override grouping
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Flare will try to group errors and exceptions as best as possible, that
|
||||||
|
| being said, sometimes you might want to override the grouping. You can
|
||||||
|
| do this by adding exception classes to this array which should always
|
||||||
|
| be grouped by exception class, exception message or exception class
|
||||||
|
| and message.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'overridden_groupings' => [
|
||||||
|
// Illuminate\Http\Client\ConnectionException::class => Spatie\FlareClient\Enums\OverriddenGrouping::ExceptionMessageAndClass,
|
||||||
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Share button
|
| Share button
|
||||||
|
|
@ -126,40 +144,6 @@ return [
|
||||||
|
|
||||||
'enable_share_button' => true,
|
'enable_share_button' => true,
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Override grouping
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Flare will try to group errors and exceptions as best as possible, that
|
|
||||||
| being said, sometimes you might want to override the grouping. You can
|
|
||||||
| do this by adding exception classes to this array which should always
|
|
||||||
| be grouped by exception class, exception message or exception class
|
|
||||||
| and message.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'overridden_groupings' => [
|
|
||||||
// Illuminate\Http\Client\ConnectionException::class => Spatie\FlareClient\Enums\OverriddenGrouping::ExceptionMessageAndClass,
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Sender
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The sender is responsible for sending the error reports and traces to
|
|
||||||
| Flare it can be configured if needed.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'sender' => [
|
|
||||||
'class' => LaravelHttpSender::class,
|
|
||||||
'config' => [
|
|
||||||
'timeout' => 10,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Trace
|
| Trace
|
||||||
|
|
@ -170,7 +154,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'trace' => env('FLARE_TRACE', false),
|
'trace' => env('FLARE_TRACE', true),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
@ -183,26 +167,35 @@ return [
|
||||||
| which means that 10% of the traces will be recorded.
|
| which means that 10% of the traces will be recorded.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'sampler' => [
|
'sampler' => [
|
||||||
'class' => RateSampler::class,
|
'class' => RateSampler::class,
|
||||||
'config' => [
|
'config' => [
|
||||||
'rate' => 0.1,
|
'rate' => env('FLARE_SAMPLER_RATE', 0.1),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Trace limits
|
| Log
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Limits for the tracing data. These limits are used to prevent
|
| Logging show you an overview of log entries within your application.
|
||||||
| the tracing data from growing too large.
|
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
'trace_limits' => [
|
|
||||||
'max_spans' => 512,
|
'log' => env('FLARE_LOG', false),
|
||||||
'max_attributes_per_span' => 128,
|
|
||||||
'max_span_events_per_span' => 128,
|
/*
|
||||||
'max_attributes_per_span_event' => 128,
|
|--------------------------------------------------------------------------
|
||||||
],
|
| Minimal log level
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You can specify the minimal (Monolog) log level that should be sent to Flare.
|
||||||
|
| Log levels lower than the specified level will be ignored.
|
||||||
|
| If null all log levels will be sent to Flare.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'minimal_log_level' => null,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Intervention\Image\Drivers\Gd\Driver;
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Image Driver
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Intervention Image supports "GD Library" and "Imagick" to process images
|
|
||||||
| internally. You may choose one of them according to your PHP
|
|
||||||
| configuration. By default PHP's "GD Library" implementation is used.
|
|
||||||
|
|
|
||||||
| Supported: "gd", "imagick"
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'driver' => Driver::class,
|
|
||||||
|
|
||||||
];
|
|
||||||
314
package-lock.json
generated
314
package-lock.json
generated
|
|
@ -121,9 +121,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@csstools/css-calc": {
|
"node_modules/@csstools/css-calc": {
|
||||||
"version": "3.2.1",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz",
|
||||||
"integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==",
|
"integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -168,9 +168,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||||
"version": "1.1.6",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz",
|
||||||
"integrity": "sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==",
|
"integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -237,9 +237,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@csstools/selector-resolve-nested": {
|
"node_modules/@csstools/selector-resolve-nested": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.1.tgz",
|
||||||
"integrity": "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==",
|
"integrity": "sha512-j3vdQu0XwLME5qOTWxm8cnmvsf423R2YL6DbKklCHZwkDm7UdKNu6RPlw4REIJhSlKBICY3B70/7QZdicLqZgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -725,9 +725,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
|
||||||
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
|
"integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -782,9 +782,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-helpers": {
|
"node_modules/@eslint/config-helpers": {
|
||||||
"version": "0.6.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz",
|
||||||
"integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
|
"integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -1019,9 +1019,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.62.0",
|
"version": "8.65.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
|
||||||
"integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==",
|
"integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -1135,16 +1135,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "5.0.7",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^4.0.2"
|
"balanced-match": "^4.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "18 || 20 || >=22"
|
"node": "20 || >=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
|
|
@ -1418,9 +1418,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "10.6.0",
|
"version": "10.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz",
|
||||||
"integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
|
"integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
|
@ -1430,7 +1430,7 @@
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.2",
|
"@eslint-community/regexpp": "^4.12.2",
|
||||||
"@eslint/config-array": "^0.23.5",
|
"@eslint/config-array": "^0.23.5",
|
||||||
"@eslint/config-helpers": "^0.6.0",
|
"@eslint/config-helpers": "^0.7.0",
|
||||||
"@eslint/core": "^1.2.1",
|
"@eslint/core": "^1.2.1",
|
||||||
"@eslint/plugin-kit": "^0.7.2",
|
"@eslint/plugin-kit": "^0.7.2",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
|
|
@ -1454,7 +1454,7 @@
|
||||||
"imurmurhash": "^0.1.4",
|
"imurmurhash": "^0.1.4",
|
||||||
"is-glob": "^4.0.0",
|
"is-glob": "^4.0.0",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"minimatch": "^10.2.4",
|
"minimatch": "^10.2.5",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"optionator": "^0.9.3"
|
"optionator": "^0.9.3"
|
||||||
},
|
},
|
||||||
|
|
@ -1655,9 +1655,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-uri": {
|
"node_modules/fast-uri": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
|
||||||
"integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
|
"integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -1749,9 +1749,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.4.2",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz",
|
||||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
"integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
|
@ -1836,9 +1836,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globby": {
|
"node_modules/globby": {
|
||||||
"version": "16.2.0",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/globby/-/globby-16.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/globby/-/globby-16.2.2.tgz",
|
||||||
"integrity": "sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==",
|
"integrity": "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -1857,9 +1857,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globby/node_modules/ignore": {
|
"node_modules/globby/node_modules/ignore": {
|
||||||
"version": "7.0.5",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
|
||||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
"integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -2137,9 +2137,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
|
||||||
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -2153,23 +2153,23 @@
|
||||||
"url": "https://opencollective.com/parcel"
|
"url": "https://opencollective.com/parcel"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"lightningcss-android-arm64": "1.32.0",
|
"lightningcss-android-arm64": "1.33.0",
|
||||||
"lightningcss-darwin-arm64": "1.32.0",
|
"lightningcss-darwin-arm64": "1.33.0",
|
||||||
"lightningcss-darwin-x64": "1.32.0",
|
"lightningcss-darwin-x64": "1.33.0",
|
||||||
"lightningcss-freebsd-x64": "1.32.0",
|
"lightningcss-freebsd-x64": "1.33.0",
|
||||||
"lightningcss-linux-arm-gnueabihf": "1.32.0",
|
"lightningcss-linux-arm-gnueabihf": "1.33.0",
|
||||||
"lightningcss-linux-arm64-gnu": "1.32.0",
|
"lightningcss-linux-arm64-gnu": "1.33.0",
|
||||||
"lightningcss-linux-arm64-musl": "1.32.0",
|
"lightningcss-linux-arm64-musl": "1.33.0",
|
||||||
"lightningcss-linux-x64-gnu": "1.32.0",
|
"lightningcss-linux-x64-gnu": "1.33.0",
|
||||||
"lightningcss-linux-x64-musl": "1.32.0",
|
"lightningcss-linux-x64-musl": "1.33.0",
|
||||||
"lightningcss-win32-arm64-msvc": "1.32.0",
|
"lightningcss-win32-arm64-msvc": "1.33.0",
|
||||||
"lightningcss-win32-x64-msvc": "1.32.0"
|
"lightningcss-win32-x64-msvc": "1.33.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-android-arm64": {
|
"node_modules/lightningcss-android-arm64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
|
||||||
"integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
|
"integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2188,9 +2188,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli": {
|
"node_modules/lightningcss-cli": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli/-/lightningcss-cli-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli/-/lightningcss-cli-1.33.0.tgz",
|
||||||
"integrity": "sha512-IFb/ChmSEbeWU3xeRybR6WFlJXCvfDS84//PUzLrRACgvoWrwRJBmcPS9azSo7LMh5QqEuyKanPBByhKM5z01Q==",
|
"integrity": "sha512-/tBcBZlBFFxy1iYKDC/HSH/NEjv7Frq6dmDVKRbhGJQI/gFJIJ1/4ZXclxJkd0eEjBjDc5MY0YugHcvJVVGJVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
|
|
@ -2208,23 +2208,23 @@
|
||||||
"url": "https://opencollective.com/parcel"
|
"url": "https://opencollective.com/parcel"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"lightningcss-cli-android-arm64": "1.32.0",
|
"lightningcss-cli-android-arm64": "1.33.0",
|
||||||
"lightningcss-cli-darwin-arm64": "1.32.0",
|
"lightningcss-cli-darwin-arm64": "1.33.0",
|
||||||
"lightningcss-cli-darwin-x64": "1.32.0",
|
"lightningcss-cli-darwin-x64": "1.33.0",
|
||||||
"lightningcss-cli-freebsd-x64": "1.32.0",
|
"lightningcss-cli-freebsd-x64": "1.33.0",
|
||||||
"lightningcss-cli-linux-arm-gnueabihf": "1.32.0",
|
"lightningcss-cli-linux-arm-gnueabihf": "1.33.0",
|
||||||
"lightningcss-cli-linux-arm64-gnu": "1.32.0",
|
"lightningcss-cli-linux-arm64-gnu": "1.33.0",
|
||||||
"lightningcss-cli-linux-arm64-musl": "1.32.0",
|
"lightningcss-cli-linux-arm64-musl": "1.33.0",
|
||||||
"lightningcss-cli-linux-x64-gnu": "1.32.0",
|
"lightningcss-cli-linux-x64-gnu": "1.33.0",
|
||||||
"lightningcss-cli-linux-x64-musl": "1.32.0",
|
"lightningcss-cli-linux-x64-musl": "1.33.0",
|
||||||
"lightningcss-cli-win32-arm64-msvc": "1.32.0",
|
"lightningcss-cli-win32-arm64-msvc": "1.33.0",
|
||||||
"lightningcss-cli-win32-x64-msvc": "1.32.0"
|
"lightningcss-cli-win32-x64-msvc": "1.33.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-android-arm64": {
|
"node_modules/lightningcss-cli-android-arm64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-android-arm64/-/lightningcss-cli-android-arm64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-android-arm64/-/lightningcss-cli-android-arm64-1.33.0.tgz",
|
||||||
"integrity": "sha512-4O3QY+VdgpBZLIq4crcKOEPVAXX0p7zDoykuTVstRtyolg9XU8CntdtbxcMPSC4SkzAuM/W4KsnPAzmv6Jb5LA==",
|
"integrity": "sha512-c0Xd7Gxaw3mNOyrb9ET1JH3JjuB69GY/6pHO4vgwEbIoHCXAt6DBg6kxr5t/oZL9LRxdnZCXF+o0Evz6kuR/xA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2243,9 +2243,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-darwin-arm64": {
|
"node_modules/lightningcss-cli-darwin-arm64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-arm64/-/lightningcss-cli-darwin-arm64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-arm64/-/lightningcss-cli-darwin-arm64-1.33.0.tgz",
|
||||||
"integrity": "sha512-Xx+zeD7bDKJZwbd1N63TJfIUHEtYspf+tqObdnQEJEvZAwmGfA4iEGrkCRT8R57tDRBDSXg3XHMDDvo/cq7gBQ==",
|
"integrity": "sha512-sso5hSFPis7ldw2FcBopsZviSVAXWRGX8ybUwMhQHssTlERenQJ88WihZs08tCdSnQULo2kunnrGhh4VjRw8Fg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2264,9 +2264,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-darwin-x64": {
|
"node_modules/lightningcss-cli-darwin-x64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-x64/-/lightningcss-cli-darwin-x64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-x64/-/lightningcss-cli-darwin-x64-1.33.0.tgz",
|
||||||
"integrity": "sha512-fYWANZ8RJDpI0tBcPQ7oBOYihfXmgDBHR4lZ6d4z7rcRLlZAOeI00mTO0IXAKfSm/UgnatjM4aBuNlLh8L9noA==",
|
"integrity": "sha512-/aDYpMv2QKpJoSIwvwpIs4tIX9SCU894eospSm55FR5MxDZkJDy4fEX6elsXkHCBDqbAknb4qd2h/oDMLTC4Ew==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2285,9 +2285,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-freebsd-x64": {
|
"node_modules/lightningcss-cli-freebsd-x64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-freebsd-x64/-/lightningcss-cli-freebsd-x64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-freebsd-x64/-/lightningcss-cli-freebsd-x64-1.33.0.tgz",
|
||||||
"integrity": "sha512-TJm7z1Ghvo9FKAza2KvfkFgH/9rcV1xAhCYQZlLrV0CiuTZ17uzLobBWb9oelGWUG+wTnEs6XEl4h/ve61YySg==",
|
"integrity": "sha512-kK9u4IEAvt3+1m7lkloyfZNfXykXq2vCH7lgZ5aEqa8VuBZYqvcAIQ4Qt1593QJxbko9HEKUP0erdSxw7k7eFw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2306,9 +2306,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-linux-arm-gnueabihf": {
|
"node_modules/lightningcss-cli-linux-arm-gnueabihf": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm-gnueabihf/-/lightningcss-cli-linux-arm-gnueabihf-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm-gnueabihf/-/lightningcss-cli-linux-arm-gnueabihf-1.33.0.tgz",
|
||||||
"integrity": "sha512-/jS9L5p3eexs5QJvARiDGxibz1umKJmmtff86fWXl3r7RbEUFrMAD4q1WhAR7DurRFgc1YWld6r0mX1YHEYttA==",
|
"integrity": "sha512-4bHmUlYCb8WKdPKx2yhk8y/kD034JVuUatDMWav0uU/hEIYJrxPimWBJJ4w2lgpcWchVnk/Kuihs4vaLjO/ozw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
|
@ -2327,9 +2327,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-linux-arm64-gnu": {
|
"node_modules/lightningcss-cli-linux-arm64-gnu": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-gnu/-/lightningcss-cli-linux-arm64-gnu-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-gnu/-/lightningcss-cli-linux-arm64-gnu-1.33.0.tgz",
|
||||||
"integrity": "sha512-PSdjwcRtSrJpsaqnY3ebnCfDOJ5ePi8s/0ItL3CX1b1Eu0iy44xo7MjgES1ZOQ2ntOthPRqaGsbAiVBLbgFLYQ==",
|
"integrity": "sha512-DkYmkBix3icAZKZsxGsjBAj59CIV0zflZtsT66lCPd2z3OqBLJlhKRrw4TlAW7mIkYeOEYWKVz/G00ZGXNse4A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2351,9 +2351,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-linux-arm64-musl": {
|
"node_modules/lightningcss-cli-linux-arm64-musl": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-musl/-/lightningcss-cli-linux-arm64-musl-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-musl/-/lightningcss-cli-linux-arm64-musl-1.33.0.tgz",
|
||||||
"integrity": "sha512-t6DdpXFtEdonZHzRgH8cmqhC2o4tl0KrD33cDBBniJz5TmWS20MJxb4YEr4WqBLksqW8GE399HJo+g8/YVuKcA==",
|
"integrity": "sha512-euNOhzo1ysRl719HfFzRe9r9usWO2Z/Nb8RnmqXxb/kvzOgAv2BcflpoTu6tWhSrbHX6YS/WP6mMFrmII6T7Hw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2375,9 +2375,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-linux-x64-gnu": {
|
"node_modules/lightningcss-cli-linux-x64-gnu": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-gnu/-/lightningcss-cli-linux-x64-gnu-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-gnu/-/lightningcss-cli-linux-x64-gnu-1.33.0.tgz",
|
||||||
"integrity": "sha512-QMQllbHYkbkQ4N+v8OGExQlGHBc3YZIcKlVkYucQQj66thkFQsRjmv8p5q3iCB0inNsCoSZ8lBspugkU1iPlPg==",
|
"integrity": "sha512-9Q4DAglm17bLhA+HZPB+vxjrHXdhR/8U44zyc8MQNA5Rw+tisvcoR/lv4iuBUFn2qreYVRWQq0wP0rgdhTSxyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2399,9 +2399,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-linux-x64-musl": {
|
"node_modules/lightningcss-cli-linux-x64-musl": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-musl/-/lightningcss-cli-linux-x64-musl-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-musl/-/lightningcss-cli-linux-x64-musl-1.33.0.tgz",
|
||||||
"integrity": "sha512-dMSWdk4kMAi5f+J1xetxRCDQOvPix2whT0UdjuwP8r/5Xcdl2SQU/c80MQzu1S82kVDEANs1vHVEHp/+26LUnA==",
|
"integrity": "sha512-+EobHdqxQ21SUMF8OU0qcizNIrLpPDaPTKQCaGbra9gL3NrW3ELG+exZl5y+WzJsEjLZ0hhjbCFjHUL/03H2JA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2423,9 +2423,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-win32-arm64-msvc": {
|
"node_modules/lightningcss-cli-win32-arm64-msvc": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-arm64-msvc/-/lightningcss-cli-win32-arm64-msvc-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-arm64-msvc/-/lightningcss-cli-win32-arm64-msvc-1.33.0.tgz",
|
||||||
"integrity": "sha512-MJo22OqSp9FLV10nTRDJQhP6zkEBFqBFQe9mnjfCs+G1Ft+QimIPmC+gBqZHFXveOBOsjFLzU72q0FPvRWFmZQ==",
|
"integrity": "sha512-iwQ+8rHQy3ewC4c43Ik3UZ8TCC/rjShP3r/8TOPw4fT8wQmW7dS3BNNHPK+LWz9qNrObATJ4MZEEZVRneckOBw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2444,9 +2444,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-cli-win32-x64-msvc": {
|
"node_modules/lightningcss-cli-win32-x64-msvc": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-x64-msvc/-/lightningcss-cli-win32-x64-msvc-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-x64-msvc/-/lightningcss-cli-win32-x64-msvc-1.33.0.tgz",
|
||||||
"integrity": "sha512-nOBHLPeePXZpGR4Ptp+gkOIkr9pxlq2dFmO954ol5zBi/iOKxuD1hUTIQ7aG+ldKIx4eH9jwoTfZ6owUkm1paA==",
|
"integrity": "sha512-sgtNPw2gxnY8OzCCMTKCkHSIiBYb/wfXLpLMmDIJFzcX2OETY+VOnQd7OBHx+hVSlrMU7iWL6oJ5rc+nQwteVA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2465,9 +2465,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-darwin-arm64": {
|
"node_modules/lightningcss-darwin-arm64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
|
||||||
"integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
|
"integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2486,9 +2486,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-darwin-x64": {
|
"node_modules/lightningcss-darwin-x64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
|
||||||
"integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
|
"integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2507,9 +2507,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-freebsd-x64": {
|
"node_modules/lightningcss-freebsd-x64": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
|
||||||
"integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
|
"integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2528,9 +2528,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
|
||||||
"integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
|
"integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
|
@ -2549,9 +2549,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
|
||||||
"integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
|
"integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2573,9 +2573,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-linux-arm64-musl": {
|
"node_modules/lightningcss-linux-arm64-musl": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
|
||||||
"integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
|
"integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2597,9 +2597,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-linux-x64-gnu": {
|
"node_modules/lightningcss-linux-x64-gnu": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
|
||||||
"integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
|
"integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2621,9 +2621,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-linux-x64-musl": {
|
"node_modules/lightningcss-linux-x64-musl": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
|
||||||
"integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
|
"integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2645,9 +2645,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-win32-arm64-msvc": {
|
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
|
||||||
"integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
|
"integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -2666,9 +2666,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-win32-x64-msvc": {
|
"node_modules/lightningcss-win32-x64-msvc": {
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
|
||||||
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
|
"integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2808,9 +2808,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.15",
|
"version": "3.3.16",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
||||||
"integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
|
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2953,9 +2953,9 @@
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
||||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -2966,9 +2966,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.16",
|
"version": "8.5.23",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz",
|
||||||
"integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
|
"integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2986,7 +2986,7 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.12",
|
"nanoid": "^3.3.16",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
|
|
@ -3236,9 +3236,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "8.2.1",
|
"version": "8.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
|
||||||
"integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
|
"integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -3269,9 +3269,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint": {
|
"node_modules/stylelint": {
|
||||||
"version": "17.14.0",
|
"version": "17.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.14.1.tgz",
|
||||||
"integrity": "sha512-8xkHPpdqYryeIsOgfsYTmr6cIeC4nLYWk5S8BPxpodq8mIuepggkMljsHewWfuAjj/+qpRKou2QerhjMH3iasg==",
|
"integrity": "sha512-xVQwyiuxALUBNB2fBe0tmNemg9KqLtdj3T64mioFDar79B2cU8LIyz+3KL6LdiHs9NkeNfwxpKSaIVOY8f112g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -3287,7 +3287,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@csstools/css-calc": "^3.2.1",
|
"@csstools/css-calc": "^3.2.1",
|
||||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.5",
|
"@csstools/css-syntax-patches-for-csstree": "^1.1.6",
|
||||||
"@csstools/css-tokenizer": "^4.0.0",
|
"@csstools/css-tokenizer": "^4.0.0",
|
||||||
"@csstools/media-query-list-parser": "^5.0.0",
|
"@csstools/media-query-list-parser": "^5.0.0",
|
||||||
"@csstools/selector-resolve-nested": "^4.0.0",
|
"@csstools/selector-resolve-nested": "^4.0.0",
|
||||||
|
|
@ -3299,9 +3299,9 @@
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"fastest-levenshtein": "^1.0.16",
|
"fastest-levenshtein": "^1.0.16",
|
||||||
"file-entry-cache": "^11.1.3",
|
"file-entry-cache": "^11.1.5",
|
||||||
"global-modules": "^2.0.0",
|
"global-modules": "^2.0.0",
|
||||||
"globby": "^16.2.0",
|
"globby": "^16.2.1",
|
||||||
"globjoin": "^0.1.4",
|
"globjoin": "^0.1.4",
|
||||||
"html-tags": "^5.1.0",
|
"html-tags": "^5.1.0",
|
||||||
"ignore": "^7.0.5",
|
"ignore": "^7.0.5",
|
||||||
|
|
@ -3311,12 +3311,12 @@
|
||||||
"micromatch": "^4.0.8",
|
"micromatch": "^4.0.8",
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"postcss": "^8.5.15",
|
"postcss": "^8.5.16",
|
||||||
"postcss-safe-parser": "^7.0.1",
|
"postcss-safe-parser": "^7.0.1",
|
||||||
"postcss-selector-parser": "^7.1.4",
|
"postcss-selector-parser": "^7.1.4",
|
||||||
"postcss-value-parser": "^4.2.0",
|
"postcss-value-parser": "^4.2.0",
|
||||||
"string-width": "^8.2.1",
|
"string-width": "^8.2.1",
|
||||||
"supports-hyperlinks": "^4.4.0",
|
"supports-hyperlinks": "^4.5.0",
|
||||||
"svg-tags": "^1.0.0",
|
"svg-tags": "^1.0.0",
|
||||||
"table": "^6.9.0",
|
"table": "^6.9.0",
|
||||||
"write-file-atomic": "^7.0.1"
|
"write-file-atomic": "^7.0.1"
|
||||||
|
|
@ -3400,9 +3400,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint/node_modules/ignore": {
|
"node_modules/stylelint/node_modules/ignore": {
|
||||||
"version": "7.0.5",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
|
||||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
"integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -6,12 +6,9 @@ namespace Tests\Feature\Admin;
|
||||||
|
|
||||||
use App\Models\Contact;
|
use App\Models\Contact;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -141,14 +138,12 @@ class ContactsTest extends TestCase
|
||||||
<img class="u-photo" alt="" src="http://tantek.com/tantek.png">
|
<img class="u-photo" alt="" src="http://tantek.com/tantek.png">
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$file = fopen(__DIR__.'/../../aaron.png', 'rb');
|
$file = file_get_contents(__DIR__.'/../../aaron.png');
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'text/html'], $html),
|
'*' => Http::sequence()
|
||||||
new Response(200, ['Content-Type' => 'image/png'], $file),
|
->push($html, 200, ['Content-Type' => 'text/html'])
|
||||||
|
->push($file, 200, ['Content-Type' => 'image/png']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$user = User::factory()->make();
|
$user = User::factory()->make();
|
||||||
$contact = Contact::factory()->create([
|
$contact = Contact::factory()->create([
|
||||||
'homepage' => 'https://tantek.com',
|
'homepage' => 'https://tantek.com',
|
||||||
|
|
@ -165,12 +160,9 @@ class ContactsTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function getting_remote_avatar_fails_gracefully_with_remote_not_found(): void
|
public function getting_remote_avatar_fails_gracefully_with_remote_not_found(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(404),
|
'*' => Http::response('', 404),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$user = User::factory()->make();
|
$user = User::factory()->make();
|
||||||
$contact = Contact::factory()->create();
|
$contact = Contact::factory()->create();
|
||||||
|
|
||||||
|
|
@ -187,13 +179,11 @@ class ContactsTest extends TestCase
|
||||||
<img class="u-photo" src="http://tantek.com/tantek.png">
|
<img class="u-photo" src="http://tantek.com/tantek.png">
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'text/html'], $html),
|
'*' => Http::sequence()
|
||||||
new Response(404),
|
->push($html, 200, ['Content-Type' => 'text/html'])
|
||||||
|
->push('', 404),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$user = User::factory()->make();
|
$user = User::factory()->make();
|
||||||
$contact = Contact::factory()->create();
|
$contact = Contact::factory()->create();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,11 @@ declare(strict_types=1);
|
||||||
namespace Tests\Feature;
|
namespace Tests\Feature;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use GuzzleHttp\Psr7\Uri;
|
use GuzzleHttp\Psr7\Uri;
|
||||||
use GuzzleHttp\Psr7\UriResolver;
|
use GuzzleHttp\Psr7\UriResolver;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -269,12 +266,9 @@ class IndieAuthTest extends TestCase
|
||||||
</html>
|
</html>
|
||||||
HTML;
|
HTML;
|
||||||
|
|
||||||
$mockHandler = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $appPageHtml),
|
'*' => Http::response($appPageHtml, 200),
|
||||||
]);
|
]);
|
||||||
$handlerStack = HandlerStack::create($mockHandler);
|
|
||||||
$mockGuzzleClient = new Client(['handler' => $handlerStack]);
|
|
||||||
$this->app->instance(Client::class, $mockGuzzleClient);
|
|
||||||
|
|
||||||
$user = User::factory()->make();
|
$user = User::factory()->make();
|
||||||
$url = url()->query('/auth', [
|
$url = url()->query('/auth', [
|
||||||
|
|
@ -313,12 +307,9 @@ class IndieAuthTest extends TestCase
|
||||||
</html>
|
</html>
|
||||||
HTML;
|
HTML;
|
||||||
|
|
||||||
$mockHandler = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $appPageHtml),
|
'*' => Http::response($appPageHtml, 200),
|
||||||
]);
|
]);
|
||||||
$handlerStack = HandlerStack::create($mockHandler);
|
|
||||||
$mockGuzzleClient = new Client(['handler' => $handlerStack]);
|
|
||||||
$this->app->instance(Client::class, $mockGuzzleClient);
|
|
||||||
|
|
||||||
$user = User::factory()->make();
|
$user = User::factory()->make();
|
||||||
$url = url()->query('/auth', [
|
$url = url()->query('/auth', [
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,8 @@ namespace Tests\Feature;
|
||||||
|
|
||||||
use App\Jobs\ProcessLike;
|
use App\Jobs\ProcessLike;
|
||||||
use App\Models\Like;
|
use App\Models\Like;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Queue;
|
use Illuminate\Support\Facades\Queue;
|
||||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
|
@ -98,18 +95,12 @@ class LikesTest extends TestCase
|
||||||
</html>
|
</html>
|
||||||
END;
|
END;
|
||||||
|
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $content),
|
'*' => Http::response($content, 200),
|
||||||
new Response(200, [], $content),
|
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->bind(Client::class, function () use ($client) {
|
|
||||||
return $client;
|
|
||||||
});
|
|
||||||
$authorship = new Authorship;
|
$authorship = new Authorship;
|
||||||
|
|
||||||
$job->handle($client, $authorship);
|
$job->handle($authorship);
|
||||||
|
|
||||||
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
||||||
}
|
}
|
||||||
|
|
@ -141,18 +132,12 @@ class LikesTest extends TestCase
|
||||||
</html>
|
</html>
|
||||||
END;
|
END;
|
||||||
|
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $content),
|
'*' => Http::response($content, 200),
|
||||||
new Response(200, [], $content),
|
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->bind(Client::class, function () use ($client) {
|
|
||||||
return $client;
|
|
||||||
});
|
|
||||||
$authorship = new Authorship;
|
$authorship = new Authorship;
|
||||||
|
|
||||||
$job->handle($client, $authorship);
|
$job->handle($authorship);
|
||||||
|
|
||||||
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
||||||
}
|
}
|
||||||
|
|
@ -177,18 +162,12 @@ class LikesTest extends TestCase
|
||||||
</html>
|
</html>
|
||||||
END;
|
END;
|
||||||
|
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $content),
|
'*' => Http::response($content, 200),
|
||||||
new Response(200, [], $content),
|
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->bind(Client::class, function () use ($client) {
|
|
||||||
return $client;
|
|
||||||
});
|
|
||||||
$authorship = new Authorship;
|
$authorship = new Authorship;
|
||||||
|
|
||||||
$job->handle($client, $authorship);
|
$job->handle($authorship);
|
||||||
|
|
||||||
$this->assertNull(Like::find($id)->author_name);
|
$this->assertNull(Like::find($id)->author_name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,19 @@
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
abstract class TestCase extends BaseTestCase
|
abstract class TestCase extends BaseTestCase
|
||||||
{
|
{
|
||||||
use CreatesApplication;
|
use CreatesApplication;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
Http::preventStrayRequests();
|
||||||
|
}
|
||||||
|
|
||||||
public function removeDirIfEmpty(string $dir): void
|
public function removeDirIfEmpty(string $dir): void
|
||||||
{
|
{
|
||||||
// scandir() will always return `.` and `..` so even an “empty”
|
// scandir() will always return `.` and `..` so even an “empty”
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@ namespace Tests\Unit;
|
||||||
|
|
||||||
use App\Exceptions\InternetArchiveException;
|
use App\Exceptions\InternetArchiveException;
|
||||||
use App\Services\BookmarkService;
|
use App\Services\BookmarkService;
|
||||||
use GuzzleHttp\Client;
|
use Illuminate\Support\Facades\Http;
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -29,12 +26,9 @@ class BookmarksTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function archive_link_method_calls_archive_service(): void
|
public function archive_link_method_calls_archive_service(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Location' => '/web/1234/example.org']),
|
'web.archive.org/*' => Http::response('', 200, ['Content-Location' => '/web/1234/example.org']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$url = (new BookmarkService)->getArchiveLink('https://example.org');
|
$url = (new BookmarkService)->getArchiveLink('https://example.org');
|
||||||
$this->assertEquals('/web/1234/example.org', $url);
|
$this->assertEquals('/web/1234/example.org', $url);
|
||||||
}
|
}
|
||||||
|
|
@ -44,12 +38,9 @@ class BookmarksTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->expectException(InternetArchiveException::class);
|
$this->expectException(InternetArchiveException::class);
|
||||||
|
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(403),
|
'web.archive.org/*' => Http::response('', 403),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
(new BookmarkService)->getArchiveLink('https://example.org');
|
(new BookmarkService)->getArchiveLink('https://example.org');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,12 +49,9 @@ class BookmarksTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->expectException(InternetArchiveException::class);
|
$this->expectException(InternetArchiveException::class);
|
||||||
|
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200),
|
'web.archive.org/*' => Http::response('', 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
(new BookmarkService)->getArchiveLink('https://example.org');
|
(new BookmarkService)->getArchiveLink('https://example.org');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,8 @@ declare(strict_types=1);
|
||||||
namespace Tests\Unit\Jobs;
|
namespace Tests\Unit\Jobs;
|
||||||
|
|
||||||
use App\Jobs\DownloadWebMention;
|
use App\Jobs\DownloadWebMention;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\FileSystem\FileSystem;
|
use Illuminate\FileSystem\FileSystem;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -35,19 +32,16 @@ class DownloadWebMentionJobTest extends TestCase
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
'example.org/*' => Http::response($html, 200, ['X-Foo' => 'Bar']),
|
||||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$job = new DownloadWebMention($source);
|
$job = new DownloadWebMention($source);
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertFileExists(storage_path('HTML/https'));
|
$this->assertFileExists(storage_path('HTML/https'));
|
||||||
|
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertFileDoesNotExist(storage_path('HTML/https/example.org/reply').'/1.'.date('Y-m-d').'.backup');
|
$this->assertFileDoesNotExist(storage_path('HTML/https/example.org/reply').'/1.'.date('Y-m-d').'.backup');
|
||||||
}
|
}
|
||||||
|
|
@ -70,19 +64,18 @@ class DownloadWebMentionJobTest extends TestCase
|
||||||
HTML;
|
HTML;
|
||||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||||
$html2 = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html2);
|
$html2 = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html2);
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
'example.org/*' => Http::sequence()
|
||||||
new Response(200, ['X-Foo' => 'Bar'], $html2),
|
->push($html, 200, ['X-Foo' => 'Bar'])
|
||||||
|
->push($html2, 200, ['X-Foo' => 'Bar']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$job = new DownloadWebMention($source);
|
$job = new DownloadWebMention($source);
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertFileExists(storage_path('HTML/https'));
|
$this->assertFileExists(storage_path('HTML/https'));
|
||||||
|
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertFileExists(storage_path('HTML/https/example.org/reply').'/1.'.date('Y-m-d').'.backup');
|
$this->assertFileExists(storage_path('HTML/https/example.org/reply').'/1.'.date('Y-m-d').'.backup');
|
||||||
}
|
}
|
||||||
|
|
@ -98,14 +91,12 @@ class DownloadWebMentionJobTest extends TestCase
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
'example.org/*' => Http::response($html, 200, ['X-Foo' => 'Bar']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$job = new DownloadWebMention($source);
|
$job = new DownloadWebMention($source);
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertFileExists(storage_path('HTML/https/example.org/reply-one/index.html'));
|
$this->assertFileExists(storage_path('HTML/https/example.org/reply-one/index.html'));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ namespace Tests\Unit\Jobs;
|
||||||
|
|
||||||
use App\Jobs\ProcessMedia;
|
use App\Jobs\ProcessMedia;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Intervention\Image\ImageManager;
|
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -15,10 +14,9 @@ class ProcessMediaJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function non_media_files_are_not_saved(): void
|
public function non_media_files_are_not_saved(): void
|
||||||
{
|
{
|
||||||
$manager = app()->make(ImageManager::class);
|
|
||||||
Storage::disk('local')->put('media/file.txt', 'This is not an image');
|
Storage::disk('local')->put('media/file.txt', 'This is not an image');
|
||||||
$job = new ProcessMedia('file.txt');
|
$job = new ProcessMedia('media/file.txt');
|
||||||
$job->handle($manager);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertFileDoesNotExist(storage_path('app/media/').'file.txt');
|
$this->assertFileDoesNotExist(storage_path('app/media/').'file.txt');
|
||||||
}
|
}
|
||||||
|
|
@ -26,10 +24,9 @@ class ProcessMediaJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function small_images_are_not_resized(): void
|
public function small_images_are_not_resized(): void
|
||||||
{
|
{
|
||||||
$manager = app()->make(ImageManager::class);
|
|
||||||
Storage::disk('local')->put('media/aaron.png', file_get_contents(__DIR__.'/../../aaron.png'));
|
Storage::disk('local')->put('media/aaron.png', file_get_contents(__DIR__.'/../../aaron.png'));
|
||||||
$job = new ProcessMedia('aaron.png');
|
$job = new ProcessMedia('media/aaron.png');
|
||||||
$job->handle($manager);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertFileDoesNotExist(storage_path('app/media/').'aaron.png');
|
$this->assertFileDoesNotExist(storage_path('app/media/').'aaron.png');
|
||||||
|
|
||||||
|
|
@ -41,10 +38,9 @@ class ProcessMediaJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function large_images_have_smaller_images_created(): void
|
public function large_images_have_smaller_images_created(): void
|
||||||
{
|
{
|
||||||
$manager = app()->make(ImageManager::class);
|
|
||||||
Storage::disk('local')->put('media/test-image.jpg', file_get_contents(__DIR__.'/../../test-image.jpg'));
|
Storage::disk('local')->put('media/test-image.jpg', file_get_contents(__DIR__.'/../../test-image.jpg'));
|
||||||
$job = new ProcessMedia('media/test-image.jpg');
|
$job = new ProcessMedia('media/test-image.jpg');
|
||||||
$job->handle($manager);
|
$job->handle();
|
||||||
|
|
||||||
// These need to look in public disk
|
// These need to look in public disk
|
||||||
Storage::disk('public')->assertExists('media/test-image.jpg');
|
Storage::disk('public')->assertExists('media/test-image.jpg');
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,9 @@ use App\Jobs\ProcessWebMention;
|
||||||
use App\Jobs\SaveProfileImage;
|
use App\Jobs\SaveProfileImage;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use App\Models\WebMention;
|
use App\Models\WebMention;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\FileSystem\FileSystem;
|
use Illuminate\FileSystem\FileSystem;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Queue;
|
use Illuminate\Support\Facades\Queue;
|
||||||
use Jonnybarnes\WebmentionsParser\Parser;
|
use Jonnybarnes\WebmentionsParser\Parser;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
|
@ -39,17 +36,15 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
$this->expectException(RemoteContentNotFoundException::class);
|
$this->expectException(RemoteContentNotFoundException::class);
|
||||||
|
|
||||||
$parser = new Parser;
|
$parser = new Parser;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(404),
|
'*' => Http::response('', 404),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$source = 'https://example.org/mention/1/';
|
$source = 'https://example.org/mention/1/';
|
||||||
|
|
||||||
$job = new ProcessWebMention($note, $source);
|
$job = new ProcessWebMention($note, $source);
|
||||||
$job->handle($parser, $client);
|
$job->handle($parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -65,17 +60,15 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$source = 'https://example.org/mention/1/';
|
$source = 'https://example.org/mention/1/';
|
||||||
|
|
||||||
$job = new ProcessWebMention($note, $source);
|
$job = new ProcessWebMention($note, $source);
|
||||||
$job->handle($parser, $client);
|
$job->handle($parser);
|
||||||
|
|
||||||
Queue::assertPushed(SaveProfileImage::class);
|
Queue::assertPushed(SaveProfileImage::class);
|
||||||
$this->assertDatabaseHas('webmentions', [
|
$this->assertDatabaseHas('webmentions', [
|
||||||
|
|
@ -103,14 +96,12 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
<div class="e-content">Updated reply</div>
|
<div class="e-content">Updated reply</div>
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$job = new ProcessWebMention($note, $source);
|
$job = new ProcessWebMention($note, $source);
|
||||||
$job->handle($parser, $client);
|
$job->handle($parser);
|
||||||
|
|
||||||
Queue::assertPushed(SaveProfileImage::class);
|
Queue::assertPushed(SaveProfileImage::class);
|
||||||
$this->assertDatabaseHas('webmentions', [
|
$this->assertDatabaseHas('webmentions', [
|
||||||
|
|
@ -132,11 +123,9 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
<div class="e-content">Replying to someone else</div>
|
<div class="e-content">Replying to someone else</div>
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$source = 'https://example.org/reply/1';
|
$source = 'https://example.org/reply/1';
|
||||||
|
|
@ -151,7 +140,7 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$job = new ProcessWebMention($note, $source);
|
$job = new ProcessWebMention($note, $source);
|
||||||
$job->handle($parser, $client);
|
$job->handle($parser);
|
||||||
|
|
||||||
$this->assertDatabaseMissing('webmentions', [
|
$this->assertDatabaseMissing('webmentions', [
|
||||||
'source' => $source,
|
'source' => $source,
|
||||||
|
|
@ -169,11 +158,9 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
<div class="e-content">I like someone else now</div>
|
<div class="e-content">I like someone else now</div>
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$source = 'https://example.org/reply/1';
|
$source = 'https://example.org/reply/1';
|
||||||
|
|
@ -188,7 +175,7 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$job = new ProcessWebMention($note, $source);
|
$job = new ProcessWebMention($note, $source);
|
||||||
$job->handle($parser, $client);
|
$job->handle($parser);
|
||||||
|
|
||||||
$this->assertDatabaseMissing('webmentions', [
|
$this->assertDatabaseMissing('webmentions', [
|
||||||
'source' => $source,
|
'source' => $source,
|
||||||
|
|
@ -208,18 +195,16 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
// Simulate a long brid.gy Bluesky source URL (well over 255 characters)
|
// Simulate a long brid.gy Bluesky source URL (well over 255 characters)
|
||||||
$source = 'https://brid.gy/comment/bluesky/did:plc:n3jhgiq2ykctnpgzlm6p6b25/at%253A%252F%252Fdid%253Aplc%253An3jhgiq2ykctnpgzlm6p6b25%252Fapp.bsky.feed.post%252F3mfekjuhykb2k/at%253A%252F%252Fdid%253Aplc%253Afsfuwigo5juzdwp23hyianzg%252Fapp.bsky.feed.post%252F3mfemw4rrvs2t';
|
$source = 'https://brid.gy/comment/bluesky/did:plc:n3jhgiq2ykctnpgzlm6p6b25/at%253A%252F%252Fdid%253Aplc%253An3jhgiq2ykctnpgzlm6p6b25%252Fapp.bsky.feed.post%252F3mfekjuhykb2k/at%253A%252F%252Fdid%253Aplc%253Afsfuwigo5juzdwp23hyianzg%252Fapp.bsky.feed.post%252F3mfemw4rrvs2t';
|
||||||
|
|
||||||
$job = new ProcessWebMention($note, $source);
|
$job = new ProcessWebMention($note, $source);
|
||||||
$job->handle($parser, $client);
|
$job->handle($parser);
|
||||||
|
|
||||||
$this->assertGreaterThan(255, strlen($source));
|
$this->assertGreaterThan(255, strlen($source));
|
||||||
$this->assertDatabaseHas('webmentions', [
|
$this->assertDatabaseHas('webmentions', [
|
||||||
|
|
@ -238,11 +223,9 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
<div class="e-content">Reposting someone else</div>
|
<div class="e-content">Reposting someone else</div>
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$source = 'https://example.org/reply/1';
|
$source = 'https://example.org/reply/1';
|
||||||
|
|
@ -257,7 +240,7 @@ class ProcessWebMentionJobTest extends TestCase
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$job = new ProcessWebMention($note, $source);
|
$job = new ProcessWebMention($note, $source);
|
||||||
$job->handle($parser, $client);
|
$job->handle($parser);
|
||||||
|
|
||||||
$this->assertDatabaseMissing('webmentions', [
|
$this->assertDatabaseMissing('webmentions', [
|
||||||
'source' => $source,
|
'source' => $source,
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Tests\Unit\Jobs;
|
namespace Tests\Unit\Jobs;
|
||||||
|
|
||||||
use App\Jobs\SaveProfileImage;
|
use App\Jobs\SaveProfileImage;
|
||||||
use GuzzleHttp\Client;
|
use Illuminate\Support\Facades\Http;
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
|
@ -58,12 +55,9 @@ class SaveProfileImageJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function remote_author_images_are_saved_locally(): void
|
public function remote_author_images_are_saved_locally(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
'*' => Http::response('fake jpeg image', 200, ['Content-Type' => 'image/jpeg']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$mf = ['items' => []];
|
$mf = ['items' => []];
|
||||||
$author = [
|
$author = [
|
||||||
'properties' => [
|
'properties' => [
|
||||||
|
|
@ -83,12 +77,9 @@ class SaveProfileImageJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function local_default_author_image_is_used_as_fallback(): void
|
public function local_default_author_image_is_used_as_fallback(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(404),
|
'*' => Http::response('', 404),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$mf = ['items' => []];
|
$mf = ['items' => []];
|
||||||
$author = [
|
$author = [
|
||||||
'properties' => [
|
'properties' => [
|
||||||
|
|
@ -111,12 +102,9 @@ class SaveProfileImageJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function we_get_url_from_photo_object_if_alt_text_is_provided(): void
|
public function we_get_url_from_photo_object_if_alt_text_is_provided(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
'*' => Http::response('fake jpeg image', 200, ['Content-Type' => 'image/jpeg']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$mf = ['items' => []];
|
$mf = ['items' => []];
|
||||||
$author = [
|
$author = [
|
||||||
'properties' => [
|
'properties' => [
|
||||||
|
|
@ -139,12 +127,9 @@ class SaveProfileImageJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function use_first_url_if_multiple_homepages_are_provided(): void
|
public function use_first_url_if_multiple_homepages_are_provided(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
'*' => Http::response('fake jpeg image', 200, ['Content-Type' => 'image/jpeg']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
$mf = ['items' => []];
|
$mf = ['items' => []];
|
||||||
$author = [
|
$author = [
|
||||||
'properties' => [
|
'properties' => [
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,8 @@ namespace Tests\Unit\Jobs;
|
||||||
|
|
||||||
use App\Jobs\SaveScreenshot;
|
use App\Jobs\SaveScreenshot;
|
||||||
use App\Models\Bookmark;
|
use App\Models\Bookmark;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\ConnectException;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Middleware;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
@ -25,57 +20,34 @@ class SaveScreenshotJobTest extends TestCase
|
||||||
public function screenshot_is_saved_by_job(): void
|
public function screenshot_is_saved_by_job(): void
|
||||||
{
|
{
|
||||||
Storage::fake('public');
|
Storage::fake('public');
|
||||||
$guzzleMock = new MockHandler([
|
|
||||||
new Response(201, ['Content-Type' => 'application/json'], '{"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"finished","credits":null,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","result":null,"created_at":"2023-01-07T21:05:48+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
Http::fake([
|
||||||
new Response(201, ['Content-Type' => 'application/json'], '{"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"finished","credits":null,"code":null,"message":null,"percent":100,"operation":"export\/url","result":null,"created_at":"2023-01-07T21:10:02+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
'api.cloudconvert.com/v2/capture-website' => Http::response([
|
||||||
new Response(200, ['Content-Type' => 'image/png'], fopen(__DIR__.'/../../theverge.com.png', 'rb')),
|
'data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'finished'],
|
||||||
|
], 201),
|
||||||
|
'api.cloudconvert.com/v2/tasks/68d52633-e170-465e-b13e-746c97d01ffb*' => Http::response([
|
||||||
|
'data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'finished'],
|
||||||
|
], 200),
|
||||||
|
'api.cloudconvert.com/v2/export/url' => Http::response([
|
||||||
|
'data' => ['id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb', 'status' => 'finished'],
|
||||||
|
], 201),
|
||||||
|
'api.cloudconvert.com/v2/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb*' => Http::response([
|
||||||
|
'data' => [
|
||||||
|
'id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb',
|
||||||
|
'status' => 'finished',
|
||||||
|
'result' => [
|
||||||
|
'files' => [[
|
||||||
|
'url' => 'https://storage.cloudconvert.com/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb/theverge.com.png',
|
||||||
|
]],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
], 200),
|
||||||
|
'storage.cloudconvert.com/*' => Http::response(
|
||||||
|
file_get_contents(__DIR__.'/../../theverge.com.png'),
|
||||||
|
200,
|
||||||
|
['Content-Type' => 'image/png']
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
$guzzleHandler = HandlerStack::create($guzzleMock);
|
|
||||||
$guzzleClient = new Client(['handler' => $guzzleHandler]);
|
|
||||||
$this->app->instance(Client::class, $guzzleClient);
|
|
||||||
$retryMock = new MockHandler([
|
|
||||||
new Response(200, ['Content-Type' => 'application/json'], '{"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"finished","credits":1,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","payload":{"url":"https:\/\/theverge.com","output_format":"png","screen_width":1440,"screen_height":900,"wait_until":"networkidle0","wait_time":"100"},"result":{"files":[{"filename":"theverge.com.png","size":811819}]},"created_at":"2023-01-07T21:05:48+00:00","started_at":"2023-01-07T21:05:48+00:00","ended_at":"2023-01-07T21:05:55+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
|
||||||
new Response(200, ['Content-Type' => 'application/json'], '{"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"finished","credits":0,"code":null,"message":null,"percent":100,"operation":"export\/url","payload":{"input":"68d52633-e170-465e-b13e-746c97d01ffb","archive_multiple_files":false},"result":{"files":[{"filename":"theverge.com.png","size":811819,"url":"https:\/\/storage.cloudconvert.com\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb\/theverge.com.png?AWSAccessKeyId=cloudconvert-production&Expires=1673212203&Signature=xyz&response-content-disposition=attachment%3B%20filename%3D%22theverge.com.png%22&response-content-type=image%2Fpng"}]},"created_at":"2023-01-07T21:10:02+00:00","started_at":"2023-01-07T21:10:03+00:00","ended_at":"2023-01-07T21:10:03+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
|
||||||
]);
|
|
||||||
$retryHandler = HandlerStack::create($retryMock);
|
|
||||||
$retryHandler->push(Middleware::retry(
|
|
||||||
function ($retries, $request, $response, $exception) {
|
|
||||||
// Limit the number of retries to 5
|
|
||||||
if ($retries >= 5) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry connection exceptions
|
|
||||||
if ($exception instanceof ConnectException) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry on server errors
|
|
||||||
if ($response && $response->getStatusCode() >= 500) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$responseBody = '';
|
|
||||||
|
|
||||||
if (is_string($response)) {
|
|
||||||
$responseBody = $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($response instanceof Response) {
|
|
||||||
$responseBody = $response->getBody()->getContents();
|
|
||||||
$response->getBody()->rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally for CloudConvert, retry if status is not final
|
|
||||||
return json_decode($responseBody, false, 512, JSON_THROW_ON_ERROR)?->data?->status !== 'finished';
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
// Retry after 1 second
|
|
||||||
return 1000;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
$retryClient = new Client(['handler' => $retryHandler]);
|
|
||||||
$this->app->instance('RetryGuzzle', $retryClient);
|
|
||||||
|
|
||||||
$bookmark = Bookmark::factory()->create();
|
$bookmark = Bookmark::factory()->create();
|
||||||
$job = new SaveScreenshot($bookmark);
|
$job = new SaveScreenshot($bookmark);
|
||||||
|
|
@ -84,68 +56,45 @@ class SaveScreenshotJobTest extends TestCase
|
||||||
|
|
||||||
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
||||||
Storage::disk('public')->assertExists('/assets/img/bookmarks/'.$bookmark->screenshot.'.png');
|
Storage::disk('public')->assertExists('/assets/img/bookmarks/'.$bookmark->screenshot.'.png');
|
||||||
|
|
||||||
|
// capture-website, 1x poll (finished immediately), export/url, 1x poll (finished immediately), download
|
||||||
|
Http::assertSentCount(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function screenshot_job_handles_unfinished_tasks(): void
|
public function screenshot_job_handles_unfinished_tasks(): void
|
||||||
{
|
{
|
||||||
Storage::fake('public');
|
Storage::fake('public');
|
||||||
$guzzleMock = new MockHandler([
|
|
||||||
new Response(201, ['Content-Type' => 'application/json'], '{"id":1,"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"waiting","credits":null,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","result":null,"created_at":"2023-01-07T21:05:48+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
Http::fake([
|
||||||
new Response(201, ['Content-Type' => 'application/json'], '{"id":2,"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"waiting","credits":null,"code":null,"message":null,"percent":100,"operation":"export\/url","result":null,"created_at":"2023-01-07T21:10:02+00:00","started_at":null,"ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":null,"storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
'api.cloudconvert.com/v2/capture-website' => Http::response([
|
||||||
new Response(200, ['Content-Type' => 'image/png'], fopen(__DIR__.'/../../theverge.com.png', 'rb')),
|
'data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'waiting'],
|
||||||
|
], 201),
|
||||||
|
'api.cloudconvert.com/v2/tasks/68d52633-e170-465e-b13e-746c97d01ffb*' => Http::sequence()
|
||||||
|
->push(['data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'waiting']], 200)
|
||||||
|
->push(['data' => ['id' => '68d52633-e170-465e-b13e-746c97d01ffb', 'status' => 'finished']], 200),
|
||||||
|
'api.cloudconvert.com/v2/export/url' => Http::response([
|
||||||
|
'data' => ['id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb', 'status' => 'waiting'],
|
||||||
|
], 201),
|
||||||
|
'api.cloudconvert.com/v2/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb*' => Http::sequence()
|
||||||
|
->push(['data' => ['id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb', 'status' => 'waiting']], 200)
|
||||||
|
->push([
|
||||||
|
'data' => [
|
||||||
|
'id' => '27f33137-cc03-4468-aba4-1e1aa8c096fb',
|
||||||
|
'status' => 'finished',
|
||||||
|
'result' => [
|
||||||
|
'files' => [[
|
||||||
|
'url' => 'https://storage.cloudconvert.com/tasks/27f33137-cc03-4468-aba4-1e1aa8c096fb/theverge.com.png',
|
||||||
|
]],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
], 200),
|
||||||
|
'storage.cloudconvert.com/*' => Http::response(
|
||||||
|
file_get_contents(__DIR__.'/../../theverge.com.png'),
|
||||||
|
200,
|
||||||
|
['Content-Type' => 'image/png']
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
$guzzleHandler = HandlerStack::create($guzzleMock);
|
|
||||||
$guzzleClient = new Client(['handler' => $guzzleHandler]);
|
|
||||||
$this->app->instance(Client::class, $guzzleClient);
|
|
||||||
$container = [];
|
|
||||||
$history = Middleware::history($container);
|
|
||||||
$retryMock = new MockHandler([
|
|
||||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":3,"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"waiting","credits":1,"code":null,"message":null,"percent":50,"operation":"capture-website","engine":"chrome","engine_version":"107","payload":{"url":"https:\/\/theverge.com","output_format":"png","screen_width":1440,"screen_height":900,"wait_until":"networkidle0","wait_time":"100"},"result":{"files":[{"filename":"theverge.com.png","size":811819}]},"created_at":"2023-01-07T21:05:48+00:00","started_at":"2023-01-07T21:05:48+00:00","ended_at":"2023-01-07T21:05:55+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
|
||||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":4,"data":{"id":"68d52633-e170-465e-b13e-746c97d01ffb","job_id":null,"status":"finished","credits":1,"code":null,"message":null,"percent":100,"operation":"capture-website","engine":"chrome","engine_version":"107","payload":{"url":"https:\/\/theverge.com","output_format":"png","screen_width":1440,"screen_height":900,"wait_until":"networkidle0","wait_time":"100"},"result":{"files":[{"filename":"theverge.com.png","size":811819}]},"created_at":"2023-01-07T21:05:48+00:00","started_at":"2023-01-07T21:05:48+00:00","ended_at":"2023-01-07T21:05:55+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":[],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/68d52633-e170-465e-b13e-746c97d01ffb"}}}'),
|
|
||||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":5,"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"waiting","credits":0,"code":null,"message":null,"percent":50,"operation":"export\/url","payload":{"input":"68d52633-e170-465e-b13e-746c97d01ffb","archive_multiple_files":false},"created_at":"2023-01-07T21:10:02+00:00","started_at":"2023-01-07T21:10:03+00:00","ended_at":null,"retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
|
||||||
new Response(200, ['Content-Type' => 'application/json'], '{"id":6,"data":{"id":"27f33137-cc03-4468-aba4-1e1aa8c096fb","job_id":null,"status":"finished","credits":0,"code":null,"message":null,"percent":100,"operation":"export\/url","payload":{"input":"68d52633-e170-465e-b13e-746c97d01ffb","archive_multiple_files":false},"result":{"files":[{"filename":"theverge.com.png","size":811819,"url":"https:\/\/storage.cloudconvert.com\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb\/theverge.com.png?AWSAccessKeyId=cloudconvert-production&Expires=1673212203&Signature=xyz&response-content-disposition=attachment%3B%20filename%3D%22theverge.com.png%22&response-content-type=image%2Fpng"}]},"created_at":"2023-01-07T21:10:02+00:00","started_at":"2023-01-07T21:10:03+00:00","ended_at":"2023-01-07T21:10:03+00:00","retry_of_task_id":null,"copy_of_task_id":null,"user_id":61485254,"priority":-10,"host_name":"virgie","storage":"ceph-fra","depends_on_task_ids":["68d52633-e170-465e-b13e-746c97d01ffb"],"links":{"self":"https:\/\/api.cloudconvert.com\/v2\/tasks\/27f33137-cc03-4468-aba4-1e1aa8c096fb"}}}'),
|
|
||||||
]);
|
|
||||||
$retryHandler = HandlerStack::create($retryMock);
|
|
||||||
$retryHandler->push($history);
|
|
||||||
$retryHandler->push(Middleware::retry(
|
|
||||||
function ($retries, $request, $response, $exception) {
|
|
||||||
// Limit the number of retries to 5
|
|
||||||
if ($retries >= 5) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry connection exceptions
|
|
||||||
if ($exception instanceof ConnectException) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry on server errors
|
|
||||||
if ($response && $response->getStatusCode() >= 500) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$responseBody = '';
|
|
||||||
|
|
||||||
if (is_string($response)) {
|
|
||||||
$responseBody = $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($response instanceof Response) {
|
|
||||||
$responseBody = $response->getBody()->getContents();
|
|
||||||
$response->getBody()->rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally for CloudConvert, retry if status is not final
|
|
||||||
return json_decode($responseBody, false, 512, JSON_THROW_ON_ERROR)?->data?->status !== 'finished';
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
// Retry after 1 second
|
|
||||||
return 1000;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
$retryClient = new Client(['handler' => $retryHandler]);
|
|
||||||
$this->app->instance('RetryGuzzle', $retryClient);
|
|
||||||
|
|
||||||
$bookmark = Bookmark::factory()->create();
|
$bookmark = Bookmark::factory()->create();
|
||||||
$job = new SaveScreenshot($bookmark);
|
$job = new SaveScreenshot($bookmark);
|
||||||
|
|
@ -154,9 +103,10 @@ class SaveScreenshotJobTest extends TestCase
|
||||||
|
|
||||||
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
||||||
Storage::disk('public')->assertExists('/assets/img/bookmarks/'.$bookmark->screenshot.'.png');
|
Storage::disk('public')->assertExists('/assets/img/bookmarks/'.$bookmark->screenshot.'.png');
|
||||||
// Also assert we made the correct number of requests
|
|
||||||
$this->assertCount(2, $container);
|
// capture-website, 2x poll (waiting then finished), export/url, 2x poll (waiting then finished), download
|
||||||
// However with retries there should be more than 4 responses for the 2 requests
|
Http::assertSentCount(7);
|
||||||
$this->assertEquals(0, $retryMock->count());
|
// Also assert every queued response in each sequence was consumed, no more no less
|
||||||
|
Http::assertSequencesAreEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@ namespace Tests\Unit\Jobs;
|
||||||
|
|
||||||
use App\Jobs\SendWebMentions;
|
use App\Jobs\SendWebMentions;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use GuzzleHttp\Client;
|
use Illuminate\Support\Facades\Http;
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -28,12 +25,9 @@ class SendWebMentionJobTest extends TestCase
|
||||||
public function discover_webmention_endpoint_from_header_links(): void
|
public function discover_webmention_endpoint_from_header_links(): void
|
||||||
{
|
{
|
||||||
$url = 'https://example.org/webmention';
|
$url = 'https://example.org/webmention';
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Link' => '<'.$url.'>; rel="webmention"']),
|
'*' => Http::response('', 200, ['Link' => '<'.$url.'>; rel="webmention"']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$job = new SendWebMentions(new Note);
|
$job = new SendWebMentions(new Note);
|
||||||
$this->assertEquals($url, $job->discoverWebmentionEndpoint('https://example.org'));
|
$this->assertEquals($url, $job->discoverWebmentionEndpoint('https://example.org'));
|
||||||
|
|
@ -43,12 +37,9 @@ class SendWebMentionJobTest extends TestCase
|
||||||
public function discover_webmention_endpoint_from_html_link_tags(): void
|
public function discover_webmention_endpoint_from_html_link_tags(): void
|
||||||
{
|
{
|
||||||
$html = '<link rel="webmention" href="https://example.org/webmention">';
|
$html = '<link rel="webmention" href="https://example.org/webmention">';
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$job = new SendWebMentions(new Note);
|
$job = new SendWebMentions(new Note);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
|
|
@ -61,12 +52,9 @@ class SendWebMentionJobTest extends TestCase
|
||||||
public function discover_webmention_endpoint_from_legacy_html_markup(): void
|
public function discover_webmention_endpoint_from_legacy_html_markup(): void
|
||||||
{
|
{
|
||||||
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$job = new SendWebMentions(new Note);
|
$job = new SendWebMentions(new Note);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
|
|
@ -95,13 +83,10 @@ class SendWebMentionJobTest extends TestCase
|
||||||
public function we_send_a_webmention_for_a_note(): void
|
public function we_send_a_webmention_for_a_note(): void
|
||||||
{
|
{
|
||||||
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, [], $html),
|
'example.org/webmention' => Http::response('', 202),
|
||||||
new Response(202),
|
'*' => Http::response($html, 200),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$note = new Note;
|
$note = new Note;
|
||||||
$note->note = 'Hi [Aaron](https://aaronparecki.com)';
|
$note->note = 'Hi [Aaron](https://aaronparecki.com)';
|
||||||
|
|
@ -114,13 +99,10 @@ class SendWebMentionJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function links_in_notes_can_not_support_webmentions(): void
|
public function links_in_notes_can_not_support_webmentions(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
// URLs with commas currently break the parse function I’m using
|
// URLs with commas currently break the parse function I’m using
|
||||||
new Response(200, ['Link' => '<https://example.org/foo,bar>; rel="preconnect"']),
|
'*' => Http::response('', 200, ['Link' => '<https://example.org/foo,bar>; rel="preconnect"']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$job = new SendWebMentions(new Note);
|
$job = new SendWebMentions(new Note);
|
||||||
$this->assertNull($job->discoverWebmentionEndpoint('https://example.org'));
|
$this->assertNull($job->discoverWebmentionEndpoint('https://example.org'));
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,9 @@ namespace Tests\Unit\Jobs;
|
||||||
use App\Jobs\SyndicateNoteToBluesky;
|
use App\Jobs\SyndicateNoteToBluesky;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use Faker\Factory;
|
use Faker\Factory;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Middleware;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Http\Client\Request;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -24,19 +21,17 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
||||||
$faker = Factory::create();
|
$faker = Factory::create();
|
||||||
$randomNumber = $faker->randomNumber();
|
$randomNumber = $faker->randomNumber();
|
||||||
$blueskyUrl = 'https://bsky.app/profile/jonnybarnes.uk/'.$randomNumber;
|
$blueskyUrl = 'https://bsky.app/profile/jonnybarnes.uk/'.$randomNumber;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
'brid.gy/*' => Http::response([
|
||||||
'url' => $blueskyUrl,
|
'url' => $blueskyUrl,
|
||||||
'id' => (string) $randomNumber,
|
'id' => (string) $randomNumber,
|
||||||
'type' => ['h-entry'],
|
'type' => ['h-entry'],
|
||||||
])),
|
], 201),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$job = new SyndicateNoteToBluesky($note);
|
$job = new SyndicateNoteToBluesky($note);
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertDatabaseHas('notes', [
|
$this->assertDatabaseHas('notes', [
|
||||||
'bluesky_url' => $blueskyUrl,
|
'bluesky_url' => $blueskyUrl,
|
||||||
|
|
@ -46,39 +41,31 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function we_post_the_correct_source_and_target(): void
|
public function we_post_the_correct_source_and_target(): void
|
||||||
{
|
{
|
||||||
$container = [];
|
Http::fake([
|
||||||
$history = Middleware::history($container);
|
'brid.gy/*' => Http::response([
|
||||||
$mock = new MockHandler([
|
|
||||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
|
||||||
'url' => 'https://bsky.app/profile/jonnybarnes.uk/1',
|
'url' => 'https://bsky.app/profile/jonnybarnes.uk/1',
|
||||||
])),
|
], 201),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$handler->push($history);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create(['note' => 'This is a **test**']);
|
$note = Note::factory()->create(['note' => 'This is a **test**']);
|
||||||
$job = new SyndicateNoteToBluesky($note);
|
$job = new SyndicateNoteToBluesky($note);
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$request = $container[0]['request'];
|
Http::assertSent(function (Request $request) use ($note) {
|
||||||
$body = [];
|
return $request->url() === 'https://brid.gy/publish/webmention'
|
||||||
parse_str((string) $request->getBody(), $body);
|
&& $request['source'] === $note->uri
|
||||||
|
&& $request['target'] === 'https://brid.gy/publish/bluesky';
|
||||||
$this->assertSame('https://brid.gy/publish/webmention', (string) $request->getUri());
|
});
|
||||||
$this->assertSame($note->uri, $body['source']);
|
|
||||||
$this->assertSame('https://brid.gy/publish/bluesky', $body['target']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function a_bridgy_failure_throws_and_does_not_set_bluesky_url(): void
|
public function a_bridgy_failure_throws_and_does_not_set_bluesky_url(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(400, ['Content-Type' => 'application/json'], json_encode([
|
'brid.gy/*' => Http::response([
|
||||||
'error' => 'Could not find target link',
|
'error' => 'Could not find target link',
|
||||||
])),
|
], 400),
|
||||||
]);
|
]);
|
||||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$job = new SyndicateNoteToBluesky($note);
|
$job = new SyndicateNoteToBluesky($note);
|
||||||
|
|
@ -86,7 +73,7 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
} finally {
|
} finally {
|
||||||
$this->assertDatabaseHas('notes', [
|
$this->assertDatabaseHas('notes', [
|
||||||
'id' => $note->id,
|
'id' => $note->id,
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,9 @@ namespace Tests\Unit\Jobs;
|
||||||
use App\Jobs\SyndicateNoteToMastodon;
|
use App\Jobs\SyndicateNoteToMastodon;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use Faker\Factory;
|
use Faker\Factory;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Middleware;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Http\Client\Request;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -24,19 +21,17 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
||||||
$faker = Factory::create();
|
$faker = Factory::create();
|
||||||
$randomNumber = $faker->randomNumber();
|
$randomNumber = $faker->randomNumber();
|
||||||
$mastodonUrl = 'https://mastodon.example/@jonny/'.$randomNumber;
|
$mastodonUrl = 'https://mastodon.example/@jonny/'.$randomNumber;
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
'brid.gy/*' => Http::response([
|
||||||
'url' => $mastodonUrl,
|
'url' => $mastodonUrl,
|
||||||
'id' => (string) $randomNumber,
|
'id' => (string) $randomNumber,
|
||||||
'type' => ['h-entry'],
|
'type' => ['h-entry'],
|
||||||
])),
|
], 201),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$job = new SyndicateNoteToMastodon($note);
|
$job = new SyndicateNoteToMastodon($note);
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$this->assertDatabaseHas('notes', [
|
$this->assertDatabaseHas('notes', [
|
||||||
'mastodon_url' => $mastodonUrl,
|
'mastodon_url' => $mastodonUrl,
|
||||||
|
|
@ -46,39 +41,31 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function we_post_the_correct_source_and_target(): void
|
public function we_post_the_correct_source_and_target(): void
|
||||||
{
|
{
|
||||||
$container = [];
|
Http::fake([
|
||||||
$history = Middleware::history($container);
|
'brid.gy/*' => Http::response([
|
||||||
$mock = new MockHandler([
|
|
||||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
|
||||||
'url' => 'https://mastodon.example/@jonny/1',
|
'url' => 'https://mastodon.example/@jonny/1',
|
||||||
])),
|
], 201),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$handler->push($history);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create(['note' => 'This is a **test**']);
|
$note = Note::factory()->create(['note' => 'This is a **test**']);
|
||||||
$job = new SyndicateNoteToMastodon($note);
|
$job = new SyndicateNoteToMastodon($note);
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
|
|
||||||
$request = $container[0]['request'];
|
Http::assertSent(function (Request $request) use ($note) {
|
||||||
$body = [];
|
return $request->url() === 'https://brid.gy/publish/webmention'
|
||||||
parse_str((string) $request->getBody(), $body);
|
&& $request['source'] === $note->uri
|
||||||
|
&& $request['target'] === 'https://brid.gy/publish/mastodon';
|
||||||
$this->assertSame('https://brid.gy/publish/webmention', (string) $request->getUri());
|
});
|
||||||
$this->assertSame($note->uri, $body['source']);
|
|
||||||
$this->assertSame('https://brid.gy/publish/mastodon', $body['target']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function a_bridgy_failure_throws_and_does_not_set_mastodon_url(): void
|
public function a_bridgy_failure_throws_and_does_not_set_mastodon_url(): void
|
||||||
{
|
{
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(400, ['Content-Type' => 'application/json'], json_encode([
|
'brid.gy/*' => Http::response([
|
||||||
'error' => 'Could not find target link',
|
'error' => 'Could not find target link',
|
||||||
])),
|
], 400),
|
||||||
]);
|
]);
|
||||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$job = new SyndicateNoteToMastodon($note);
|
$job = new SyndicateNoteToMastodon($note);
|
||||||
|
|
@ -86,7 +73,7 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$job->handle($client);
|
$job->handle();
|
||||||
} finally {
|
} finally {
|
||||||
$this->assertDatabaseHas('notes', [
|
$this->assertDatabaseHas('notes', [
|
||||||
'id' => $note->id,
|
'id' => $note->id,
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,10 @@ use App\Models\Media;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use App\Models\Place;
|
use App\Models\Place;
|
||||||
use App\Models\Tag;
|
use App\Models\Tag;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use Illuminate\Filesystem\Filesystem;
|
use Illuminate\Filesystem\Filesystem;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
@ -182,13 +179,9 @@ class NotesTest extends TestCase
|
||||||
{"place_id":"198791063","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"relation","osm_id":"5208404","lat":"51.50084125","lon":"-0.142990166340849","display_name":"Buckingham Palace, Ambassador's Court, St. James's, Victoria, Westminster, London, Greater London, England, SW1E 6LA, United Kingdom","address":{"attraction":"Buckingham Palace","road":"Ambassador's Court","neighbourhood":"St. James's","suburb":"Victoria","city":"London","state_district":"Greater London","state":"England","postcode":"SW1E 6LA","country":"UK","country_code":"gb"},"boundingbox":["51.4997342","51.5019473","-0.143984","-0.1413002"]}
|
{"place_id":"198791063","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"relation","osm_id":"5208404","lat":"51.50084125","lon":"-0.142990166340849","display_name":"Buckingham Palace, Ambassador's Court, St. James's, Victoria, Westminster, London, Greater London, England, SW1E 6LA, United Kingdom","address":{"attraction":"Buckingham Palace","road":"Ambassador's Court","neighbourhood":"St. James's","suburb":"Victoria","city":"London","state_district":"Greater London","state":"England","postcode":"SW1E 6LA","country":"UK","country_code":"gb"},"boundingbox":["51.4997342","51.5019473","-0.143984","-0.1413002"]}
|
||||||
JSON;
|
JSON;
|
||||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$note = new Note;
|
$note = new Note;
|
||||||
$address = $note->reverseGeoCode(51.50084, -0.14264);
|
$address = $note->reverseGeoCode(51.50084, -0.14264);
|
||||||
|
|
@ -207,13 +200,9 @@ class NotesTest extends TestCase
|
||||||
{"place_id":"96518506","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"94107885","lat":"51.0225764535969","lon":"0.906664040464189","display_name":"Melon Lane, Newchurch, Shepway, Kent, South East, England, TN29 0AS, United Kingdom","address":{"road":"Melon Lane","suburb":"Newchurch","city":"Shepway","county":"Kent","state_district":"South East","state":"England","postcode":"TN29 0AS","country":"UK","country_code":"gb"},"boundingbox":["51.0140377","51.0371494","0.8873312","0.9109506"]}
|
{"place_id":"96518506","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"94107885","lat":"51.0225764535969","lon":"0.906664040464189","display_name":"Melon Lane, Newchurch, Shepway, Kent, South East, England, TN29 0AS, United Kingdom","address":{"road":"Melon Lane","suburb":"Newchurch","city":"Shepway","county":"Kent","state_district":"South East","state":"England","postcode":"TN29 0AS","country":"UK","country_code":"gb"},"boundingbox":["51.0140377","51.0371494","0.8873312","0.9109506"]}
|
||||||
JSON;
|
JSON;
|
||||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$note = new Note;
|
$note = new Note;
|
||||||
$address = $note->reverseGeoCode(51.02, 0.91);
|
$address = $note->reverseGeoCode(51.02, 0.91);
|
||||||
|
|
@ -234,13 +223,9 @@ class NotesTest extends TestCase
|
||||||
{"place_id":"198561071","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"relation","osm_id":"1839026","lat":"53.46600455","lon":"-2.23300880782987","display_name":"University of Manchester - Main Campus, Brunswick Street, Curry Mile, Ardwick, Manchester, Greater Manchester, North West England, England, M13 9NR, United Kingdom","address":{"university":"University of Manchester - Main Campus","city":"Manchester","county":"Greater Manchester","state_district":"North West England","state":"England","postcode":"M13 9NR","country":"UK","country_code":"gb"},"boundingbox":["53.4598667","53.4716848","-2.2390346","-2.2262754"]}
|
{"place_id":"198561071","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"relation","osm_id":"1839026","lat":"53.46600455","lon":"-2.23300880782987","display_name":"University of Manchester - Main Campus, Brunswick Street, Curry Mile, Ardwick, Manchester, Greater Manchester, North West England, England, M13 9NR, United Kingdom","address":{"university":"University of Manchester - Main Campus","city":"Manchester","county":"Greater Manchester","state_district":"North West England","state":"England","postcode":"M13 9NR","country":"UK","country_code":"gb"},"boundingbox":["53.4598667","53.4716848","-2.2390346","-2.2262754"]}
|
||||||
JSON;
|
JSON;
|
||||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$note = new Note;
|
$note = new Note;
|
||||||
$address = $note->reverseGeoCode(53.466277988406, -2.2304474827445);
|
$address = $note->reverseGeoCode(53.466277988406, -2.2304474827445);
|
||||||
|
|
@ -261,13 +246,9 @@ class NotesTest extends TestCase
|
||||||
{"place_id":"98085404","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"103703318","lat":"51.0997470194065","lon":"0.609897771085209","display_name":"Biddenden, Ashford, Kent, South East, England, TN27 8ET, United Kingdom","address":{"county":"Kent","state_district":"South East","state":"England","postcode":"TN27 8ET","country":"UK","country_code":"gb"},"boundingbox":["51.0986632","51.104459","0.5954434","0.6167775"]}
|
{"place_id":"98085404","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"103703318","lat":"51.0997470194065","lon":"0.609897771085209","display_name":"Biddenden, Ashford, Kent, South East, England, TN27 8ET, United Kingdom","address":{"county":"Kent","state_district":"South East","state":"England","postcode":"TN27 8ET","country":"UK","country_code":"gb"},"boundingbox":["51.0986632","51.104459","0.5954434","0.6167775"]}
|
||||||
JSON;
|
JSON;
|
||||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$note = new Note;
|
$note = new Note;
|
||||||
$address = $note->reverseGeoCode(51.1, 0.61);
|
$address = $note->reverseGeoCode(51.1, 0.61);
|
||||||
|
|
@ -285,13 +266,9 @@ class NotesTest extends TestCase
|
||||||
{"place_id":"120553244","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"191508282","lat":"54.3004150140189","lon":"-9.39993720828084","display_name":"R314, Doonfeeny Lower, Ballycastle ED, Ballina, County Mayo, Connacht, Ireland","address":{"country":"Ireland","country_code":"ie"},"boundingbox":["54.2964027","54.3045856","-9.4337961","-9.3960403"]}
|
{"place_id":"120553244","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/osm.org\/copyright","osm_type":"way","osm_id":"191508282","lat":"54.3004150140189","lon":"-9.39993720828084","display_name":"R314, Doonfeeny Lower, Ballycastle ED, Ballina, County Mayo, Connacht, Ireland","address":{"country":"Ireland","country_code":"ie"},"boundingbox":["54.2964027","54.3045856","-9.4337961","-9.3960403"]}
|
||||||
JSON;
|
JSON;
|
||||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||||
$mock = new MockHandler([
|
Http::fake([
|
||||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
'nominatim.openstreetmap.org/*' => Http::response($json, 200, ['Content-Type' => 'application/json']),
|
||||||
]);
|
]);
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
$this->app->instance(Client::class, $client);
|
|
||||||
|
|
||||||
$note = new Note;
|
$note = new Note;
|
||||||
$address = $note->reverseGeoCode(54.3, 9.4);
|
$address = $note->reverseGeoCode(54.3, 9.4);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue