Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 975277ebdb | |||
|
f9f2744fad |
|||
| db2c7b19ed | |||
|
31c49ac3fc |
|||
|
287520ad7b |
|||
|
2eab5749ea |
|||
|
e08763c526 |
|||
| a9263cffc4 | |||
|
c420e19f08 |
|||
| b6ee8f6521 | |||
|
f7e13976e0 |
56 changed files with 1633 additions and 1661 deletions
|
|
@ -78,6 +78,8 @@ SESSION_SAME_SITE=strict
|
|||
|
||||
LOG_SLACK_WEBHOOK_URL=
|
||||
|
||||
BRRR_WEBHOOK_URL=
|
||||
|
||||
FLARE_KEY=
|
||||
|
||||
IGNITION_OPEN_AI_KEY=
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ namespace App\Console\Commands;
|
|||
|
||||
use App\Models\Media;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Image\ImageException;
|
||||
use Illuminate\Support\Facades\Image;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\ImageManager;
|
||||
|
||||
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';
|
||||
|
||||
public function handle(ImageManager $manager): void
|
||||
public function handle(): void
|
||||
{
|
||||
$media = Media::where('type', 'image')
|
||||
->whereNotNull('image_widths')
|
||||
|
|
@ -44,10 +44,10 @@ class ReprocessMediaImages extends Command
|
|||
|
||||
$this->info("Processing: {$path}");
|
||||
|
||||
$image = Image::fromStorage($path, 'public');
|
||||
try {
|
||||
$file = Storage::disk('public')->get($path);
|
||||
$image = $manager->read($file);
|
||||
} catch (DecoderException) {
|
||||
$image->width();
|
||||
} catch (ImageException) {
|
||||
$this->warn(' Could not decode image, skipping.');
|
||||
|
||||
continue;
|
||||
|
|
@ -57,11 +57,8 @@ class ReprocessMediaImages extends Command
|
|||
$extension = array_pop($filenameParts);
|
||||
$basename = trim(implode('.', $filenameParts), '.');
|
||||
|
||||
$medium = $image->scale(width: 1000);
|
||||
Storage::disk('public')->put($basename.'-medium.'.$extension, (string) $medium->encode());
|
||||
|
||||
$small = $image->scale(width: 500);
|
||||
Storage::disk('public')->put($basename.'-small.'.$extension, (string) $small->encode());
|
||||
Storage::disk('public')->put($basename.'-medium.'.$extension, $image->scale(width: 1000)->toBytes());
|
||||
Storage::disk('public')->put($basename.'-small.'.$extension, $image->scale(width: 500)->toBytes());
|
||||
|
||||
$this->info(' Done.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ namespace App\Http\Controllers\Admin;
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Contact;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\BadResponseException;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ContactsController extends Controller
|
||||
|
|
@ -113,14 +113,13 @@ class ContactsController extends Controller
|
|||
$avatar = null;
|
||||
$contact = Contact::findOrFail($contactId);
|
||||
if ($contact->homepage !== null && mb_strlen($contact->homepage) !== 0) {
|
||||
$client = resolve(Client::class);
|
||||
try {
|
||||
$response = $client->get($contact->homepage);
|
||||
} catch (BadResponseException $e) {
|
||||
$response = Http::throw()->get($contact->homepage);
|
||||
} catch (RequestException $e) {
|
||||
return redirect('/admin/contacts/'.$contactId.'/edit')
|
||||
->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) {
|
||||
if (Arr::get($microformat, 'type.0') === 'h-card') {
|
||||
$avatarURL = Arr::get($microformat, 'properties.photo.0.value');
|
||||
|
|
@ -129,8 +128,8 @@ class ContactsController extends Controller
|
|||
}
|
||||
if ($avatarURL !== null) {
|
||||
try {
|
||||
$avatar = $client->get($avatarURL);
|
||||
} catch (BadResponseException $e) {
|
||||
$avatar = Http::throw()->get($avatarURL);
|
||||
} catch (RequestException $e) {
|
||||
return redirect('/admin/contacts/'.$contactId.'/edit')
|
||||
->with('error', 'Unable to download avatar');
|
||||
}
|
||||
|
|
@ -141,7 +140,7 @@ class ContactsController extends Controller
|
|||
if ($filesystem->isDirectory($directory) === false) {
|
||||
$filesystem->makeDirectory($directory);
|
||||
}
|
||||
$filesystem->put($directory.'/image', $avatar->getBody());
|
||||
$filesystem->put($directory.'/image', $avatar->body());
|
||||
|
||||
return view('admin.contacts.getavatarsuccess', [
|
||||
'homepage' => parse_url($contact->homepage, PHP_URL_HOST),
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\TokenService;
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\View\View;
|
||||
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
|
||||
$guzzle = resolve(Client::class);
|
||||
|
||||
try {
|
||||
$clientInfo = $guzzle->get($clientId);
|
||||
} catch (Exception) {
|
||||
$clientInfo = Http::throw()->get($clientId);
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$clientInfoParsed = \Mf2\parse($clientInfo->getBody()->getContents(), $clientId);
|
||||
$clientInfoParsed = \Mf2\parse($clientInfo->body(), $clientId);
|
||||
|
||||
$redirectUris = $clientInfoParsed['rels']['redirect_uri'] ?? [];
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ use Illuminate\Http\JsonResponse;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Image\ImageException;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Image;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
class MicropubMediaController extends Controller
|
||||
|
|
@ -111,12 +112,9 @@ class MicropubMediaController extends Controller
|
|||
|
||||
$filename = Storage::disk('local')->putFile('media', $file);
|
||||
|
||||
/** @var ImageManager $manager */
|
||||
$manager = resolve(ImageManager::class);
|
||||
try {
|
||||
$image = $manager->read($request->file('file'));
|
||||
$width = $image->width();
|
||||
} catch (Exception) {
|
||||
$width = Image::fromUpload($request->file('file'))->width();
|
||||
} catch (ImageException) {
|
||||
// not an image
|
||||
$width = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\FileSystem\FileSystem;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class DownloadWebMention implements ShouldQueue
|
||||
{
|
||||
|
|
@ -29,15 +29,15 @@ class DownloadWebMention implements ShouldQueue
|
|||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws RequestException
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public function handle(Client $guzzle): void
|
||||
public function handle(): void
|
||||
{
|
||||
$response = $guzzle->request('GET', $this->source);
|
||||
// 4XX and 5XX responses should get Guzzle to throw an exception,
|
||||
// Laravel should catch and retry these automatically.
|
||||
if ($response->getStatusCode() === 200) {
|
||||
// 4XX and 5XX responses should throw so Laravel can catch and
|
||||
// retry these automatically.
|
||||
$response = Http::throw()->get($this->source);
|
||||
if ($response->status() === 200) {
|
||||
$filesystem = new FileSystem;
|
||||
$filename = storage_path('HTML').'/'.$this->createFilenameFromURL($this->source);
|
||||
// backup file first
|
||||
|
|
@ -56,7 +56,7 @@ class DownloadWebMention implements ShouldQueue
|
|||
// save new HTML
|
||||
$filesystem->put(
|
||||
$filename,
|
||||
(string) $response->getBody()
|
||||
$response->body()
|
||||
);
|
||||
// remove backup if the same
|
||||
if ($filesystem->exists($filenameBackup)) {
|
||||
|
|
|
|||
56
app/Jobs/NotifyBrrrOfWebMention.php
Normal file
56
app/Jobs/NotifyBrrrOfWebMention.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\WebMention;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class NotifyBrrrOfWebMention implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
protected WebMention $webMention
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$webhookUrl = config('services.brrr.webhook_url');
|
||||
|
||||
if (blank($webhookUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Http::post($webhookUrl, [
|
||||
'title' => $this->title(),
|
||||
'message' => "From {$this->webMention->source}",
|
||||
'open_url' => $this->webMention->target,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a notification title based on the webmention type.
|
||||
*/
|
||||
private function title(): string
|
||||
{
|
||||
return match ($this->webMention->type) {
|
||||
'in-reply-to' => 'New reply',
|
||||
'like-of' => 'New like',
|
||||
'repost-of' => 'New repost',
|
||||
default => 'New webmention',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -5,14 +5,13 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Like;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||
|
||||
|
|
@ -32,13 +31,11 @@ class ProcessLike implements ShouldQueue
|
|||
|
||||
/**
|
||||
* 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);
|
||||
$mf2 = \Mf2\parse((string) $response->getBody(), $this->like->url);
|
||||
$response = Http::throw()->get($this->like->url);
|
||||
$mf2 = \Mf2\parse($response->body(), $this->like->url);
|
||||
if (Arr::has($mf2, 'items.0.properties.content')) {
|
||||
$this->like->content = $mf2['items'][0]['properties']['content'][0]['html'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ namespace App\Jobs;
|
|||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Image\ImageException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Image;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\ImageManager;
|
||||
|
||||
class ProcessMedia implements ShouldQueue
|
||||
{
|
||||
|
|
@ -30,15 +30,16 @@ class ProcessMedia implements ShouldQueue
|
|||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(ImageManager $manager): void
|
||||
public function handle(): void
|
||||
{
|
||||
// Load file
|
||||
$file = Storage::disk('local')->get($this->filename);
|
||||
|
||||
// Open file
|
||||
$image = Image::fromStorage($this->filename, 'local');
|
||||
try {
|
||||
$image = $manager->read($file);
|
||||
} catch (DecoderException) {
|
||||
$width = $image->width();
|
||||
} catch (ImageException) {
|
||||
// not an image; delete file and end job
|
||||
Storage::disk('local')->delete($this->filename);
|
||||
|
||||
|
|
@ -49,18 +50,15 @@ class ProcessMedia implements ShouldQueue
|
|||
Storage::disk('public')->put($this->filename, $file);
|
||||
|
||||
// Create smaller versions if necessary
|
||||
if ($image->width() > 1000) {
|
||||
if ($width > 1000) {
|
||||
$filenameParts = explode('.', $this->filename);
|
||||
$extension = array_pop($filenameParts);
|
||||
// the following achieves this data flow
|
||||
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
|
||||
$basename = trim(implode('.', $filenameParts), '.');
|
||||
|
||||
$medium = $image->scale(width: 1000);
|
||||
Storage::disk('public')->put($basename.'-medium.'.$extension, (string) $medium->encode());
|
||||
|
||||
$small = $image->scale(width: 500);
|
||||
Storage::disk('public')->put($basename.'-small.'.$extension, (string) $small->encode());
|
||||
Storage::disk('public')->put($basename.'-medium.'.$extension, $image->scale(width: 1000)->toBytes());
|
||||
Storage::disk('public')->put($basename.'-small.'.$extension, $image->scale(width: 500)->toBytes());
|
||||
}
|
||||
|
||||
// Now we can delete the locally saved image
|
||||
|
|
|
|||
|
|
@ -7,13 +7,12 @@ namespace App\Jobs;
|
|||
use App\Exceptions\RemoteContentNotFoundException;
|
||||
use App\Models\Note;
|
||||
use App\Models\WebMention;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\InvalidMentionException;
|
||||
use Jonnybarnes\WebmentionsParser\Parser;
|
||||
use Mf2;
|
||||
|
|
@ -36,18 +35,20 @@ class ProcessWebMention implements ShouldQueue
|
|||
* Execute the job.
|
||||
*
|
||||
* @throws RemoteContentNotFoundException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidMentionException
|
||||
*/
|
||||
public function handle(Parser $parser, Client $guzzle): void
|
||||
public function handle(Parser $parser): void
|
||||
{
|
||||
try {
|
||||
$response = $guzzle->request('GET', $this->source);
|
||||
} catch (RequestException $e) {
|
||||
$response = Http::get($this->source);
|
||||
} catch (ConnectionException) {
|
||||
throw new RemoteContentNotFoundException;
|
||||
}
|
||||
$this->saveRemoteContent((string) $response->getBody(), $this->source);
|
||||
$microformats = Mf2\parse((string) $response->getBody(), $this->source);
|
||||
if ($response->failed()) {
|
||||
throw new RemoteContentNotFoundException;
|
||||
}
|
||||
$this->saveRemoteContent($response->body(), $this->source);
|
||||
$microformats = Mf2\parse($response->body(), $this->source);
|
||||
$webmentions = WebMention::where('source', $this->source)->get();
|
||||
foreach ($webmentions as $webmention) {
|
||||
// check webmention still references target
|
||||
|
|
@ -95,6 +96,7 @@ class ProcessWebMention implements ShouldQueue
|
|||
$webmention->type = $type;
|
||||
$webmention->mf2 = json_encode($microformats);
|
||||
$webmention->save();
|
||||
dispatch(new NotifyBrrrOfWebMention($webmention));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
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) !== 'twitter.com'
|
||||
) {
|
||||
$client = resolve(Client::class);
|
||||
|
||||
try {
|
||||
$response = $client->get($photo);
|
||||
$image = $response->getBody();
|
||||
} catch (RequestException) {
|
||||
$response = Http::throw()->get($photo);
|
||||
$image = $response->body();
|
||||
} catch (ConnectionException|RequestException) {
|
||||
// we are opening and reading the default image so that
|
||||
$default = public_path().'/assets/profile-images/default-image';
|
||||
$handle = fopen($default, 'rb');
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Bookmark;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\Client\PendingRequest;
|
||||
use Illuminate\Http\Client\Response;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use JsonException;
|
||||
|
||||
class SaveScreenshot implements ShouldQueue
|
||||
{
|
||||
|
|
@ -27,77 +28,68 @@ class SaveScreenshot implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
// A normal Guzzle client
|
||||
$client = resolve(Client::class);
|
||||
// A Guzzle client with a custom Middleware to retry the CloudConvert API requests
|
||||
$retryClient = resolve('RetryGuzzle');
|
||||
$cloudConvert = Http::baseUrl('https://api.cloudconvert.com/v2')
|
||||
->withToken(config('services.cloudconvert.token'))
|
||||
->throw();
|
||||
|
||||
// First request that CloudConvert takes a screenshot of the URL
|
||||
$takeScreenshotJobResponse = $client->request('POST', 'https://api.cloudconvert.com/v2/capture-website', [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'json' => [
|
||||
'url' => $this->bookmark->url,
|
||||
'output_format' => 'png',
|
||||
'screen_width' => 1440,
|
||||
'screen_height' => 900,
|
||||
'wait_until' => 'networkidle0',
|
||||
'wait_time' => 100,
|
||||
],
|
||||
$takeScreenshotJobResponse = $cloudConvert->post('/capture-website', [
|
||||
'url' => $this->bookmark->url,
|
||||
'output_format' => 'png',
|
||||
'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
|
||||
$screenshotJobStatusResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$taskId, [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'query' => [
|
||||
'include' => 'payload',
|
||||
],
|
||||
]);
|
||||
$screenshotJobStatusResponse = $this->pollUntilFinished($cloudConvert, $taskId);
|
||||
|
||||
$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
|
||||
$exportImageJob = $client->request('POST', 'https://api.cloudconvert.com/v2/export/url', [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'json' => [
|
||||
'input' => $finishedCaptureId,
|
||||
'archive_multiple_files' => false,
|
||||
],
|
||||
$exportImageJob = $cloudConvert->post('/export/url', [
|
||||
'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
|
||||
$finalImageUrlResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$exportImageJobId, [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
|
||||
],
|
||||
'query' => [
|
||||
'include' => 'payload',
|
||||
],
|
||||
]);
|
||||
$finalImageUrlResponse = $this->pollUntilFinished($cloudConvert, $exportImageJobId);
|
||||
|
||||
// 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->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;
|
||||
|
||||
use App\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Psr7\Header;
|
||||
use GuzzleHttp\Psr7\UriResolver;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
|
|
@ -14,6 +12,7 @@ use Illuminate\Bus\Queueable;
|
|||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use Mf2\Parser;
|
||||
|
||||
|
|
@ -32,8 +31,6 @@ class SendWebMentions implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
|
|
@ -43,12 +40,9 @@ class SendWebMentions implements ShouldQueue
|
|||
foreach ($urls as $url) {
|
||||
$endpoint = $this->discoverWebmentionEndpoint($url);
|
||||
if ($endpoint !== null) {
|
||||
$guzzle = resolve(Client::class);
|
||||
$guzzle->post($endpoint, [
|
||||
'form_params' => [
|
||||
'source' => $this->note->uri,
|
||||
'target' => $url,
|
||||
],
|
||||
Http::asForm()->post($endpoint, [
|
||||
'source' => $this->note->uri,
|
||||
'target' => $url,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -56,8 +50,6 @@ class SendWebMentions implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Discover if a URL has a webmention endpoint.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function discoverWebmentionEndpoint(string $url): ?string
|
||||
{
|
||||
|
|
@ -71,10 +63,9 @@ class SendWebMentions implements ShouldQueue
|
|||
|
||||
$endpoint = null;
|
||||
|
||||
$guzzle = resolve(Client::class);
|
||||
$response = $guzzle->get($url);
|
||||
$response = Http::get($url);
|
||||
// check HTTP Headers for webmention endpoint
|
||||
$links = Header::parse($response->getHeader('Link'));
|
||||
$links = Header::parse($response->header('Link'));
|
||||
foreach ($links as $link) {
|
||||
if (array_key_exists('rel', $link) && mb_stristr($link['rel'], 'webmention')) {
|
||||
return $this->resolveUri(trim($link[0], '<>'), $url);
|
||||
|
|
@ -82,7 +73,7 @@ class SendWebMentions implements ShouldQueue
|
|||
}
|
||||
|
||||
// failed to find a header so parse HTML
|
||||
$html = (string) $response->getBody();
|
||||
$html = $response->body();
|
||||
|
||||
if ($html === '') {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ declare(strict_types=1);
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class SyndicateNoteToBluesky implements ShouldQueue
|
||||
{
|
||||
|
|
@ -31,29 +30,18 @@ class SyndicateNoteToBluesky implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(Client $guzzle): void
|
||||
public function handle(): void
|
||||
{
|
||||
$response = $guzzle->request(
|
||||
'POST',
|
||||
'https://brid.gy/publish/webmention',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'form_params' => [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/bluesky',
|
||||
],
|
||||
'http_errors' => false,
|
||||
]
|
||||
);
|
||||
// no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
|
||||
$response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/bluesky',
|
||||
]);
|
||||
|
||||
$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->save();
|
||||
|
||||
|
|
@ -61,7 +49,7 @@ class SyndicateNoteToBluesky implements ShouldQueue
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
use App\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class SyndicateNoteToMastodon implements ShouldQueue
|
||||
{
|
||||
|
|
@ -31,29 +30,18 @@ class SyndicateNoteToMastodon implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(Client $guzzle): void
|
||||
public function handle(): void
|
||||
{
|
||||
$response = $guzzle->request(
|
||||
'POST',
|
||||
'https://brid.gy/publish/webmention',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
'form_params' => [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/mastodon',
|
||||
],
|
||||
'http_errors' => false,
|
||||
]
|
||||
);
|
||||
// no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
|
||||
$response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
|
||||
'source' => $this->note->uri,
|
||||
'target' => 'https://brid.gy/publish/mastodon',
|
||||
]);
|
||||
|
||||
$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->save();
|
||||
|
||||
|
|
@ -61,7 +49,7 @@ class SyndicateNoteToMastodon implements ShouldQueue
|
|||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
'Bridgy publish to Mastodon failed: '.($body['error'] ?? (string) $response->getBody())
|
||||
'Bridgy publish to Mastodon failed: '.($body['error'] ?? $response->body())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,8 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use League\CommonMark\Environment\Environment;
|
||||
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
|
||||
use League\CommonMark\MarkdownConverter;
|
||||
use Spatie\CommonMarkHighlighter\FencedCodeRenderer;
|
||||
use Spatie\CommonMarkHighlighter\IndentedCodeRenderer;
|
||||
use Tempest\Highlight\CommonMark\HighlightExtension;
|
||||
|
||||
#[Table('articles')]
|
||||
#[Fillable(['url', 'title', 'main', 'published'])]
|
||||
|
|
@ -53,8 +50,7 @@ class Article extends Model
|
|||
get: function () {
|
||||
$environment = new Environment;
|
||||
$environment->addExtension(new CommonMarkCoreExtension);
|
||||
$environment->addRenderer(FencedCode::class, new FencedCodeRenderer);
|
||||
$environment->addRenderer(IndentedCode::class, new IndentedCodeRenderer);
|
||||
$environment->addExtension(new HighlightExtension);
|
||||
$markdownConverter = new MarkdownConverter($environment);
|
||||
|
||||
return $markdownConverter->convert($this->main)->getContent();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ namespace App\Models;
|
|||
use App\CommonMark\Generators\MentionGenerator;
|
||||
use App\CommonMark\Renderers\MentionRenderer;
|
||||
use App\Observers\NoteObserver;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||
|
|
@ -20,19 +19,17 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\IndieWeb\Numbers;
|
||||
use Laravel\Scout\Searchable;
|
||||
use League\CommonMark\Environment\Environment;
|
||||
use League\CommonMark\Extension\Autolink\AutolinkExtension;
|
||||
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
|
||||
use League\CommonMark\Extension\Mention\Mention;
|
||||
use League\CommonMark\Extension\Mention\MentionExtension;
|
||||
use League\CommonMark\MarkdownConverter;
|
||||
use Normalizer;
|
||||
use Spatie\CommonMarkHighlighter\FencedCodeRenderer;
|
||||
use Spatie\CommonMarkHighlighter\IndentedCodeRenderer;
|
||||
use Tempest\Highlight\CommonMark\HighlightExtension;
|
||||
|
||||
#[Table('notes')]
|
||||
#[Fillable(['note', 'in_reply_to', 'client_id'])]
|
||||
|
|
@ -354,9 +351,8 @@ class Note extends Model
|
|||
$environment->addExtension(new CommonMarkCoreExtension);
|
||||
$environment->addExtension(new AutolinkExtension);
|
||||
$environment->addExtension(new MentionExtension);
|
||||
$environment->addExtension(new HighlightExtension);
|
||||
$environment->addRenderer(Mention::class, new MentionRenderer);
|
||||
$environment->addRenderer(FencedCode::class, new FencedCodeRenderer);
|
||||
$environment->addRenderer(IndentedCode::class, new IndentedCodeRenderer);
|
||||
$markdownConverter = new MarkdownConverter($environment);
|
||||
|
||||
return $markdownConverter->convert($note)->getContent();
|
||||
|
|
@ -367,18 +363,15 @@ class Note extends Model
|
|||
$latLng = $latitude.','.$longitude;
|
||||
|
||||
return Cache::get($latLng, function () use ($latLng, $latitude, $longitude) {
|
||||
$guzzle = resolve(Client::class);
|
||||
$response = $guzzle->request('GET', 'https://nominatim.openstreetmap.org/reverse', [
|
||||
'query' => [
|
||||
$response = Http::withHeaders(['User-Agent' => 'jonnybarnes.uk, email jonny@jonnybarnes.uk'])
|
||||
->get('https://nominatim.openstreetmap.org/reverse', [
|
||||
'format' => 'json',
|
||||
'lat' => $latitude,
|
||||
'lon' => $longitude,
|
||||
'zoom' => 18,
|
||||
'addressdetails' => 1,
|
||||
],
|
||||
'headers' => ['User-Agent' => 'jonnybarnes.uk via Guzzle, email jonny@jonnybarnes.uk'],
|
||||
]);
|
||||
$json = json_decode((string) $response->getBody());
|
||||
]);
|
||||
$json = $response->object();
|
||||
if (isset($json->address->suburb)) {
|
||||
$locality = $json->address->suburb;
|
||||
if (isset($json->address->city)) {
|
||||
|
|
|
|||
|
|
@ -2,15 +2,11 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Lcobucci\JWT\Configuration;
|
||||
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||
|
|
@ -33,11 +29,6 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
|
@ -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
|
||||
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\Models\Bookmark;
|
||||
use App\Models\Tag;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BookmarkService
|
||||
|
|
@ -55,21 +53,19 @@ class BookmarkService
|
|||
* Given a URL, attempt to save it to the Internet Archive.
|
||||
*
|
||||
* @throws InternetArchiveException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function getArchiveLink(string $url): string
|
||||
{
|
||||
$client = resolve(Client::class);
|
||||
try {
|
||||
$response = $client->request('GET', 'https://web.archive.org/save/'.$url);
|
||||
} catch (ClientException $e) {
|
||||
$response = Http::get('https://web.archive.org/save/'.$url);
|
||||
|
||||
if ($response->clientError()) {
|
||||
// throw an exception to be caught
|
||||
throw new InternetArchiveException;
|
||||
}
|
||||
if ($response->hasHeader('Content-Location')) {
|
||||
if (Str::startsWith(Arr::get($response->getHeader('Content-Location'), 0), '/web')) {
|
||||
return $response->getHeader('Content-Location')[0];
|
||||
}
|
||||
|
||||
$contentLocation = $response->header('Content-Location');
|
||||
if ($contentLocation !== '' && Str::startsWith($contentLocation, '/web')) {
|
||||
return $contentLocation;
|
||||
}
|
||||
|
||||
// throw an exception to be caught
|
||||
|
|
|
|||
|
|
@ -10,13 +10,12 @@
|
|||
"ext-dom": "*",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-pgsql": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-pgsql": "*",
|
||||
"ext-sodium": "*",
|
||||
"cviebrock/eloquent-sluggable": "^13.0",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"indieauth/client": "^1.1",
|
||||
"intervention/image": "^3",
|
||||
"intervention/image": "^4.0",
|
||||
"jonnybarnes/indieweb": "~0.2",
|
||||
"jonnybarnes/webmentions-parser": "~0.5",
|
||||
"laravel/framework": "^13.0",
|
||||
|
|
@ -27,9 +26,9 @@
|
|||
"league/commonmark": "^2.0",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"mf2/mf2": "~0.3",
|
||||
"spatie/commonmark-highlighter": "^3.0",
|
||||
"spatie/laravel-flare": "^2.2",
|
||||
"spatie/laravel-flare": "^3.0",
|
||||
"symfony/html-sanitizer": "^8.0",
|
||||
"tempest/highlight": "^2.27",
|
||||
"web-auth/webauthn-lib": "^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
@ -42,8 +41,8 @@
|
|||
"laravel/sail": "^1.41",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"phpunit/php-code-coverage": "^12.0",
|
||||
"phpunit/phpunit": "^12.5.12",
|
||||
"phpunit/php-code-coverage": "^14.0",
|
||||
"phpunit/phpunit": "^13.0",
|
||||
"spatie/laravel-ray": "^1.12",
|
||||
"spatie/x-ray": "^1.2"
|
||||
},
|
||||
|
|
|
|||
1470
composer.lock
generated
1470
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
|
||||
|
||||
use Spatie\FlareClient\Api;
|
||||
use Spatie\FlareClient\Sampling\RateSampler;
|
||||
use Spatie\LaravelFlare\AttributesProviders\LaravelUserAttributesProvider;
|
||||
use Spatie\LaravelFlare\FlareConfig;
|
||||
use Spatie\LaravelFlare\Senders\LaravelHttpSender;
|
||||
|
||||
|
|
@ -21,17 +19,6 @@ return [
|
|||
|
||||
'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
|
||||
|
|
@ -47,22 +34,6 @@ return [
|
|||
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
|
||||
|
|
@ -88,19 +59,49 @@ return [
|
|||
'X-XSRF-TOKEN',
|
||||
],
|
||||
'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,
|
||||
| no matter which error level you specified in the Flare log channel.
|
||||
| The sender is responsible for sending the error reports and traces to
|
||||
| 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
|
||||
| reported. By default, all error levels are reported by setting
|
||||
| this value to `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
|
||||
|
|
@ -126,40 +144,6 @@ return [
|
|||
|
||||
'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
|
||||
|
|
@ -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.
|
||||
|
|
||||
*/
|
||||
|
||||
'sampler' => [
|
||||
'class' => RateSampler::class,
|
||||
'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
|
||||
| the tracing data from growing too large.
|
||||
| Logging show you an overview of log entries within your application.
|
||||
|
|
||||
*/
|
||||
'trace_limits' => [
|
||||
'max_spans' => 512,
|
||||
'max_attributes_per_span' => 128,
|
||||
'max_span_events_per_span' => 128,
|
||||
'max_attributes_per_span_event' => 128,
|
||||
],
|
||||
|
||||
'log' => env('FLARE_LOG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 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,
|
||||
|
||||
];
|
||||
|
|
@ -39,4 +39,8 @@ return [
|
|||
'token' => env('CLOUDCONVERT_API_TOKEN'),
|
||||
],
|
||||
|
||||
'brrr' => [
|
||||
'webhook_url' => env('BRRR_WEBHOOK_URL'),
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
314
package-lock.json
generated
314
package-lock.json
generated
|
|
@ -121,9 +121,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@csstools/css-calc": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz",
|
||||
"integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz",
|
||||
"integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -168,9 +168,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.6.tgz",
|
||||
"integrity": "sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz",
|
||||
"integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -237,9 +237,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@csstools/selector-resolve-nested": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz",
|
||||
"integrity": "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.1.tgz",
|
||||
"integrity": "sha512-j3vdQu0XwLME5qOTWxm8cnmvsf423R2YL6DbKklCHZwkDm7UdKNu6RPlw4REIJhSlKBICY3B70/7QZdicLqZgg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -725,9 +725,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
||||
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
|
||||
"integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -782,9 +782,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
|
||||
"integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz",
|
||||
"integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
|
@ -1019,9 +1019,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz",
|
||||
"integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
|
||||
"integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -1135,16 +1135,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
|
|
@ -1418,9 +1418,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
|
||||
"integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
|
||||
"version": "10.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz",
|
||||
"integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
|
@ -1430,7 +1430,7 @@
|
|||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@eslint/config-array": "^0.23.5",
|
||||
"@eslint/config-helpers": "^0.6.0",
|
||||
"@eslint/config-helpers": "^0.7.0",
|
||||
"@eslint/core": "^1.2.1",
|
||||
"@eslint/plugin-kit": "^0.7.2",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
|
|
@ -1454,7 +1454,7 @@
|
|||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"minimatch": "^10.2.4",
|
||||
"minimatch": "^10.2.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.3"
|
||||
},
|
||||
|
|
@ -1655,9 +1655,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
|
||||
"integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
|
||||
"integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1749,9 +1749,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz",
|
||||
"integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
|
|
@ -1836,9 +1836,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/globby": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-16.2.0.tgz",
|
||||
"integrity": "sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==",
|
||||
"version": "16.2.2",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-16.2.2.tgz",
|
||||
"integrity": "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -1857,9 +1857,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/globby/node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
|
||||
"integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -2137,9 +2137,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
||||
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
|
||||
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
|
|
@ -2153,23 +2153,23 @@
|
|||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"lightningcss-android-arm64": "1.32.0",
|
||||
"lightningcss-darwin-arm64": "1.32.0",
|
||||
"lightningcss-darwin-x64": "1.32.0",
|
||||
"lightningcss-freebsd-x64": "1.32.0",
|
||||
"lightningcss-linux-arm-gnueabihf": "1.32.0",
|
||||
"lightningcss-linux-arm64-gnu": "1.32.0",
|
||||
"lightningcss-linux-arm64-musl": "1.32.0",
|
||||
"lightningcss-linux-x64-gnu": "1.32.0",
|
||||
"lightningcss-linux-x64-musl": "1.32.0",
|
||||
"lightningcss-win32-arm64-msvc": "1.32.0",
|
||||
"lightningcss-win32-x64-msvc": "1.32.0"
|
||||
"lightningcss-android-arm64": "1.33.0",
|
||||
"lightningcss-darwin-arm64": "1.33.0",
|
||||
"lightningcss-darwin-x64": "1.33.0",
|
||||
"lightningcss-freebsd-x64": "1.33.0",
|
||||
"lightningcss-linux-arm-gnueabihf": "1.33.0",
|
||||
"lightningcss-linux-arm64-gnu": "1.33.0",
|
||||
"lightningcss-linux-arm64-musl": "1.33.0",
|
||||
"lightningcss-linux-x64-gnu": "1.33.0",
|
||||
"lightningcss-linux-x64-musl": "1.33.0",
|
||||
"lightningcss-win32-arm64-msvc": "1.33.0",
|
||||
"lightningcss-win32-x64-msvc": "1.33.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-android-arm64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
|
||||
"integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
|
||||
"integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2188,9 +2188,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli/-/lightningcss-cli-1.32.0.tgz",
|
||||
"integrity": "sha512-IFb/ChmSEbeWU3xeRybR6WFlJXCvfDS84//PUzLrRACgvoWrwRJBmcPS9azSo7LMh5QqEuyKanPBByhKM5z01Q==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli/-/lightningcss-cli-1.33.0.tgz",
|
||||
"integrity": "sha512-/tBcBZlBFFxy1iYKDC/HSH/NEjv7Frq6dmDVKRbhGJQI/gFJIJ1/4ZXclxJkd0eEjBjDc5MY0YugHcvJVVGJVg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MPL-2.0",
|
||||
|
|
@ -2208,23 +2208,23 @@
|
|||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"lightningcss-cli-android-arm64": "1.32.0",
|
||||
"lightningcss-cli-darwin-arm64": "1.32.0",
|
||||
"lightningcss-cli-darwin-x64": "1.32.0",
|
||||
"lightningcss-cli-freebsd-x64": "1.32.0",
|
||||
"lightningcss-cli-linux-arm-gnueabihf": "1.32.0",
|
||||
"lightningcss-cli-linux-arm64-gnu": "1.32.0",
|
||||
"lightningcss-cli-linux-arm64-musl": "1.32.0",
|
||||
"lightningcss-cli-linux-x64-gnu": "1.32.0",
|
||||
"lightningcss-cli-linux-x64-musl": "1.32.0",
|
||||
"lightningcss-cli-win32-arm64-msvc": "1.32.0",
|
||||
"lightningcss-cli-win32-x64-msvc": "1.32.0"
|
||||
"lightningcss-cli-android-arm64": "1.33.0",
|
||||
"lightningcss-cli-darwin-arm64": "1.33.0",
|
||||
"lightningcss-cli-darwin-x64": "1.33.0",
|
||||
"lightningcss-cli-freebsd-x64": "1.33.0",
|
||||
"lightningcss-cli-linux-arm-gnueabihf": "1.33.0",
|
||||
"lightningcss-cli-linux-arm64-gnu": "1.33.0",
|
||||
"lightningcss-cli-linux-arm64-musl": "1.33.0",
|
||||
"lightningcss-cli-linux-x64-gnu": "1.33.0",
|
||||
"lightningcss-cli-linux-x64-musl": "1.33.0",
|
||||
"lightningcss-cli-win32-arm64-msvc": "1.33.0",
|
||||
"lightningcss-cli-win32-x64-msvc": "1.33.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-android-arm64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-android-arm64/-/lightningcss-cli-android-arm64-1.32.0.tgz",
|
||||
"integrity": "sha512-4O3QY+VdgpBZLIq4crcKOEPVAXX0p7zDoykuTVstRtyolg9XU8CntdtbxcMPSC4SkzAuM/W4KsnPAzmv6Jb5LA==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-android-arm64/-/lightningcss-cli-android-arm64-1.33.0.tgz",
|
||||
"integrity": "sha512-c0Xd7Gxaw3mNOyrb9ET1JH3JjuB69GY/6pHO4vgwEbIoHCXAt6DBg6kxr5t/oZL9LRxdnZCXF+o0Evz6kuR/xA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2243,9 +2243,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-darwin-arm64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-arm64/-/lightningcss-cli-darwin-arm64-1.32.0.tgz",
|
||||
"integrity": "sha512-Xx+zeD7bDKJZwbd1N63TJfIUHEtYspf+tqObdnQEJEvZAwmGfA4iEGrkCRT8R57tDRBDSXg3XHMDDvo/cq7gBQ==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-arm64/-/lightningcss-cli-darwin-arm64-1.33.0.tgz",
|
||||
"integrity": "sha512-sso5hSFPis7ldw2FcBopsZviSVAXWRGX8ybUwMhQHssTlERenQJ88WihZs08tCdSnQULo2kunnrGhh4VjRw8Fg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2264,9 +2264,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-darwin-x64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-x64/-/lightningcss-cli-darwin-x64-1.32.0.tgz",
|
||||
"integrity": "sha512-fYWANZ8RJDpI0tBcPQ7oBOYihfXmgDBHR4lZ6d4z7rcRLlZAOeI00mTO0IXAKfSm/UgnatjM4aBuNlLh8L9noA==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-darwin-x64/-/lightningcss-cli-darwin-x64-1.33.0.tgz",
|
||||
"integrity": "sha512-/aDYpMv2QKpJoSIwvwpIs4tIX9SCU894eospSm55FR5MxDZkJDy4fEX6elsXkHCBDqbAknb4qd2h/oDMLTC4Ew==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2285,9 +2285,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-freebsd-x64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-freebsd-x64/-/lightningcss-cli-freebsd-x64-1.32.0.tgz",
|
||||
"integrity": "sha512-TJm7z1Ghvo9FKAza2KvfkFgH/9rcV1xAhCYQZlLrV0CiuTZ17uzLobBWb9oelGWUG+wTnEs6XEl4h/ve61YySg==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-freebsd-x64/-/lightningcss-cli-freebsd-x64-1.33.0.tgz",
|
||||
"integrity": "sha512-kK9u4IEAvt3+1m7lkloyfZNfXykXq2vCH7lgZ5aEqa8VuBZYqvcAIQ4Qt1593QJxbko9HEKUP0erdSxw7k7eFw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2306,9 +2306,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-linux-arm-gnueabihf": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm-gnueabihf/-/lightningcss-cli-linux-arm-gnueabihf-1.32.0.tgz",
|
||||
"integrity": "sha512-/jS9L5p3eexs5QJvARiDGxibz1umKJmmtff86fWXl3r7RbEUFrMAD4q1WhAR7DurRFgc1YWld6r0mX1YHEYttA==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm-gnueabihf/-/lightningcss-cli-linux-arm-gnueabihf-1.33.0.tgz",
|
||||
"integrity": "sha512-4bHmUlYCb8WKdPKx2yhk8y/kD034JVuUatDMWav0uU/hEIYJrxPimWBJJ4w2lgpcWchVnk/Kuihs4vaLjO/ozw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -2327,9 +2327,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-linux-arm64-gnu": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-gnu/-/lightningcss-cli-linux-arm64-gnu-1.32.0.tgz",
|
||||
"integrity": "sha512-PSdjwcRtSrJpsaqnY3ebnCfDOJ5ePi8s/0ItL3CX1b1Eu0iy44xo7MjgES1ZOQ2ntOthPRqaGsbAiVBLbgFLYQ==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-gnu/-/lightningcss-cli-linux-arm64-gnu-1.33.0.tgz",
|
||||
"integrity": "sha512-DkYmkBix3icAZKZsxGsjBAj59CIV0zflZtsT66lCPd2z3OqBLJlhKRrw4TlAW7mIkYeOEYWKVz/G00ZGXNse4A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2351,9 +2351,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-linux-arm64-musl": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-musl/-/lightningcss-cli-linux-arm64-musl-1.32.0.tgz",
|
||||
"integrity": "sha512-t6DdpXFtEdonZHzRgH8cmqhC2o4tl0KrD33cDBBniJz5TmWS20MJxb4YEr4WqBLksqW8GE399HJo+g8/YVuKcA==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-arm64-musl/-/lightningcss-cli-linux-arm64-musl-1.33.0.tgz",
|
||||
"integrity": "sha512-euNOhzo1ysRl719HfFzRe9r9usWO2Z/Nb8RnmqXxb/kvzOgAv2BcflpoTu6tWhSrbHX6YS/WP6mMFrmII6T7Hw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2375,9 +2375,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-linux-x64-gnu": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-gnu/-/lightningcss-cli-linux-x64-gnu-1.32.0.tgz",
|
||||
"integrity": "sha512-QMQllbHYkbkQ4N+v8OGExQlGHBc3YZIcKlVkYucQQj66thkFQsRjmv8p5q3iCB0inNsCoSZ8lBspugkU1iPlPg==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-gnu/-/lightningcss-cli-linux-x64-gnu-1.33.0.tgz",
|
||||
"integrity": "sha512-9Q4DAglm17bLhA+HZPB+vxjrHXdhR/8U44zyc8MQNA5Rw+tisvcoR/lv4iuBUFn2qreYVRWQq0wP0rgdhTSxyQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2399,9 +2399,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-linux-x64-musl": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-musl/-/lightningcss-cli-linux-x64-musl-1.32.0.tgz",
|
||||
"integrity": "sha512-dMSWdk4kMAi5f+J1xetxRCDQOvPix2whT0UdjuwP8r/5Xcdl2SQU/c80MQzu1S82kVDEANs1vHVEHp/+26LUnA==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-linux-x64-musl/-/lightningcss-cli-linux-x64-musl-1.33.0.tgz",
|
||||
"integrity": "sha512-+EobHdqxQ21SUMF8OU0qcizNIrLpPDaPTKQCaGbra9gL3NrW3ELG+exZl5y+WzJsEjLZ0hhjbCFjHUL/03H2JA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2423,9 +2423,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-win32-arm64-msvc": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-arm64-msvc/-/lightningcss-cli-win32-arm64-msvc-1.32.0.tgz",
|
||||
"integrity": "sha512-MJo22OqSp9FLV10nTRDJQhP6zkEBFqBFQe9mnjfCs+G1Ft+QimIPmC+gBqZHFXveOBOsjFLzU72q0FPvRWFmZQ==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-arm64-msvc/-/lightningcss-cli-win32-arm64-msvc-1.33.0.tgz",
|
||||
"integrity": "sha512-iwQ+8rHQy3ewC4c43Ik3UZ8TCC/rjShP3r/8TOPw4fT8wQmW7dS3BNNHPK+LWz9qNrObATJ4MZEEZVRneckOBw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2444,9 +2444,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-cli-win32-x64-msvc": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-x64-msvc/-/lightningcss-cli-win32-x64-msvc-1.32.0.tgz",
|
||||
"integrity": "sha512-nOBHLPeePXZpGR4Ptp+gkOIkr9pxlq2dFmO954ol5zBi/iOKxuD1hUTIQ7aG+ldKIx4eH9jwoTfZ6owUkm1paA==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-cli-win32-x64-msvc/-/lightningcss-cli-win32-x64-msvc-1.33.0.tgz",
|
||||
"integrity": "sha512-sgtNPw2gxnY8OzCCMTKCkHSIiBYb/wfXLpLMmDIJFzcX2OETY+VOnQd7OBHx+hVSlrMU7iWL6oJ5rc+nQwteVA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2465,9 +2465,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-arm64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
|
||||
"integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
|
||||
"integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2486,9 +2486,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-x64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
|
||||
"integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
|
||||
"integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2507,9 +2507,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-freebsd-x64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
|
||||
"integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
|
||||
"integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2528,9 +2528,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
|
||||
"integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
|
||||
"integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -2549,9 +2549,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
|
||||
"integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
|
||||
"integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2573,9 +2573,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-musl": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
|
||||
"integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
|
||||
"integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2597,9 +2597,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-gnu": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
|
||||
"integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
|
||||
"integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2621,9 +2621,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-musl": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
|
||||
"integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
|
||||
"integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2645,9 +2645,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
|
||||
"integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
|
||||
"integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2666,9 +2666,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-x64-msvc": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
|
||||
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
|
||||
"integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2808,9 +2808,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.15",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
|
||||
"integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
|
||||
"version": "3.3.16",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2953,9 +2953,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
||||
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -2966,9 +2966,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.16",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
|
||||
"integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
|
||||
"version": "8.5.23",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz",
|
||||
"integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2986,7 +2986,7 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.12",
|
||||
"nanoid": "^3.3.16",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
|
|
@ -3236,9 +3236,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
|
||||
"integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
|
||||
"integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -3269,9 +3269,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/stylelint": {
|
||||
"version": "17.14.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.14.0.tgz",
|
||||
"integrity": "sha512-8xkHPpdqYryeIsOgfsYTmr6cIeC4nLYWk5S8BPxpodq8mIuepggkMljsHewWfuAjj/+qpRKou2QerhjMH3iasg==",
|
||||
"version": "17.14.1",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.14.1.tgz",
|
||||
"integrity": "sha512-xVQwyiuxALUBNB2fBe0tmNemg9KqLtdj3T64mioFDar79B2cU8LIyz+3KL6LdiHs9NkeNfwxpKSaIVOY8f112g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3287,7 +3287,7 @@
|
|||
"dependencies": {
|
||||
"@csstools/css-calc": "^3.2.1",
|
||||
"@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/media-query-list-parser": "^5.0.0",
|
||||
"@csstools/selector-resolve-nested": "^4.0.0",
|
||||
|
|
@ -3299,9 +3299,9 @@
|
|||
"debug": "^4.4.3",
|
||||
"fast-glob": "^3.3.3",
|
||||
"fastest-levenshtein": "^1.0.16",
|
||||
"file-entry-cache": "^11.1.3",
|
||||
"file-entry-cache": "^11.1.5",
|
||||
"global-modules": "^2.0.0",
|
||||
"globby": "^16.2.0",
|
||||
"globby": "^16.2.1",
|
||||
"globjoin": "^0.1.4",
|
||||
"html-tags": "^5.1.0",
|
||||
"ignore": "^7.0.5",
|
||||
|
|
@ -3311,12 +3311,12 @@
|
|||
"micromatch": "^4.0.8",
|
||||
"normalize-path": "^3.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss": "^8.5.15",
|
||||
"postcss": "^8.5.16",
|
||||
"postcss-safe-parser": "^7.0.1",
|
||||
"postcss-selector-parser": "^7.1.4",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"string-width": "^8.2.1",
|
||||
"supports-hyperlinks": "^4.4.0",
|
||||
"supports-hyperlinks": "^4.5.0",
|
||||
"svg-tags": "^1.0.0",
|
||||
"table": "^6.9.0",
|
||||
"write-file-atomic": "^7.0.1"
|
||||
|
|
@ -3400,9 +3400,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/stylelint/node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
|
||||
"integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
@layer reset{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin-block-end:0}ul[role=list],ol[role=list]{list-style:none}body{line-height:1.5}h1,h2,h3,h4,button,input,label{line-height:1.1}h1,h2,h3,h4{text-wrap:balance}a:not([class]){text-decoration-skip-ink:auto;color:currentColor}img,picture{max-width:100%;height:auto;display:block}input,button,textarea,select{font-family:inherit;font-size:inherit}textarea:not([rows]){min-height:10em}:target{scroll-margin-block:5ex}@media (prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}}}@layer base{:root{color-scheme:light dark;--primary-hue:307;--clr-text:light-dark(oklch(5% .1 var(--primary-hue)),oklch(100% .1 var(--primary-hue)));--clr-background:light-dark(oklch(100% .1 var(--primary-hue)),oklch(15% .15 var(--primary-hue)));--clr-border:light-dark(oklch(90% .2 var(--primary-hue)),oklch(25% .1 var(--primary-hue)));--clr-snow-fall:light-dark(oklch(25% .15 var(--primary-hue)),oklch(85% .2 var(--primary-hue)))}body{background-color:var(--clr-background);color:var(--clr-text)}:root{--border-radius:8px}html{font-size:125%}body{grid-template-rows:min-content 1fr min-content;grid-template-columns:1fr min(80ch,80vw) 1fr;padding-inline:4vw;display:grid;&>header{grid-column:1/-1}&>main{grid-column:2/3}&>footer{grid-column:1/-1;margin-block-start:2ex;& search form{flex-direction:row;align-items:center;gap:1ex;display:flex;& input{min-width:0}}}}.h-feed{flex-direction:column;gap:2ex;display:flex}}@layer components{body>header{grid-template-rows:auto auto;grid-template-columns:auto 1fr auto;align-items:baseline;gap:1rem;display:grid;& a{text-decoration:none}& h1{text-wrap:nowrap;margin:0}& nav{flex-direction:row;gap:.5rem;display:flex;@media screen and (width<=1100px){flex-wrap:wrap;grid-area:2/1/auto/span 3}}}.note{border:1px solid var(--clr-border);border-radius:var(--border-radius);flex-direction:column;padding:1ex 2ex;display:flex;& .e-content{&>p:first-child{margin-block-start:0}& .u-photo{width:100%;height:auto}}& .syndication-links{flex-direction:row;gap:1ex;display:flex;& svg{border-radius:4px;width:auto;height:1lh}}}.reply-to{border:1px solid var(--clr-border);border-top-left-radius:var(--border-radius);border-top-right-radius:var(--border-radius);border-bottom:none;margin-inline:2ex;padding-inline:1ex;font-size:85%}.pagination{flex-direction:row;justify-content:center;align-items:center;display:flex;& div{flex-direction:row;gap:1ex;display:flex}}#theme-selector{& button{appearance:none;background:0 0;border:none;&:hover{cursor:pointer}}& #theme-selector-dropdown{position-area:block-end span-inline-start;border:2px solid var(--clr-border);background-color:var(--clr-background);color:var(--clr-text);margin:0;position:absolute;& fieldset{border:0;flex-direction:row;gap:1ch;display:flex;& input{display:none}}}@media (prefers-reduced-motion:no-preference){& #theme-selector-dropdown{&:popover-open{opacity:1;transform:translateY(0)scale(1);@starting-style{opacity:0;transform:translateY(30px)scale(0)}}opacity:0;transition:transform, opacity, display allow-discrete, overlay allow-discrete;transform-origin:100% 0;transition-duration:.5s;transform:translateY(0)scale(0)}}}::view-transition-group(changing-theme){animation-duration:1.25s}::view-transition-new(changing-theme),::view-transition-old(changing-theme){mix-blend-mode:normal}::view-transition-new(changing-theme){animation-name:reveal}::view-transition-old(changing-theme){animation:none}@keyframes reveal{0%{clip-path:polygon(-30% 0,-30% 0,-15% 100%,-10% 115%)}to{clip-path:polygon(-30% 0,130% 0,115% 100%,-10% 115%)}}}
|
||||
@layer reset{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin-block-end:0}ul[role=list],ol[role=list]{list-style:none}body{line-height:1.5}h1,h2,h3,h4,button,input,label{line-height:1.1}h1,h2,h3,h4{text-wrap:balance}a:not([class]){text-decoration-skip-ink:auto;color:currentColor}img,picture{max-width:100%;height:auto;display:block}input,button,textarea,select{font-family:inherit;font-size:inherit}textarea:not([rows]){min-height:10em}:target{scroll-margin-block:5ex}@media (prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}}}@layer base{:root{color-scheme:light dark;--primary-hue:307;--clr-text:light-dark(oklch(5% .1 var(--primary-hue)),oklch(100% .1 var(--primary-hue)));--clr-background:light-dark(oklch(100% .1 var(--primary-hue)),oklch(15% .15 var(--primary-hue)));--clr-border:light-dark(oklch(90% .2 var(--primary-hue)),oklch(25% .1 var(--primary-hue)));--clr-snow-fall:light-dark(oklch(25% .15 var(--primary-hue)),oklch(85% .2 var(--primary-hue)))}body{background-color:var(--clr-background);color:var(--clr-text)}:root{--border-radius:8px}html{font-size:125%}body{grid-template-rows:min-content 1fr min-content;grid-template-columns:1fr min(80ch,80vw) 1fr;padding-inline:4vw;display:grid;&>header{grid-column:1/-1}&>main{grid-column:2/3}&>footer{grid-column:1/-1;margin-block-start:2ex;& search form{flex-direction:row;align-items:center;gap:1ex;display:flex;& input{min-width:0}}}}.h-feed{flex-direction:column;gap:2ex;display:flex}}@layer components{.h-entry{& pre{padding:1rem}}body>header{grid-template-rows:auto auto;grid-template-columns:auto 1fr auto;align-items:baseline;gap:1rem;display:grid;& a{text-decoration:none}& h1{text-wrap:nowrap;margin:0}& nav{flex-direction:row;gap:.5rem;display:flex;@media screen and (width<=1100px){flex-wrap:wrap;grid-area:2/1/auto/span 3}}}.note{border:1px solid var(--clr-border);border-radius:var(--border-radius);flex-direction:column;padding:1ex 2ex;display:flex;& .e-content{&>p:first-child{margin-block-start:0}& .u-photo{width:100%;height:auto}}& .syndication-links{flex-direction:row;gap:1ex;display:flex;& svg{border-radius:4px;width:auto;height:1lh}}}.reply-to{border:1px solid var(--clr-border);border-top-left-radius:var(--border-radius);border-top-right-radius:var(--border-radius);border-bottom:none;margin-inline:2ex;padding-inline:1ex;font-size:85%}.pagination{flex-direction:row;justify-content:center;align-items:center;display:flex;& div{flex-direction:row;gap:1ex;display:flex}}#theme-selector{& button{appearance:none;background:0 0;border:none;&:hover{cursor:pointer}}& #theme-selector-dropdown{position-area:block-end span-inline-start;border:2px solid var(--clr-border);background-color:var(--clr-background);color:var(--clr-text);margin:0;position:absolute;& fieldset{border:0;flex-direction:row;gap:1ch;display:flex;& input{display:none}}}@media (prefers-reduced-motion:no-preference){& #theme-selector-dropdown{&:popover-open{opacity:1;transform:translateY(0)scale(1);@starting-style{opacity:0;transform:translateY(30px)scale(0)}}opacity:0;transition:transform, opacity, display allow-discrete, overlay allow-discrete;transform-origin:100% 0;transition-duration:.5s;transform:translateY(0)scale(0)}}}::view-transition-group(changing-theme){animation-duration:1.25s}::view-transition-new(changing-theme),::view-transition-old(changing-theme){mix-blend-mode:normal}::view-transition-new(changing-theme){animation-name:reveal}::view-transition-old(changing-theme){animation:none}@keyframes reveal{0%{clip-path:polygon(-30% 0,-30% 0,-15% 100%,-10% 115%)}to{clip-path:polygon(-30% 0,130% 0,115% 100%,-10% 115%)}}}
|
||||
/*# sourceMappingURL=/assets/css/app.css.map */
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
87
public/assets/highlight/nord.css
Normal file
87
public/assets/highlight/nord.css
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
pre, code {
|
||||
color: #d8dee9;
|
||||
background-color: #2e3440;
|
||||
}
|
||||
|
||||
.hl-keyword {
|
||||
color: #81A1C1;
|
||||
}
|
||||
|
||||
.hl-property {
|
||||
color: #88C0D0;
|
||||
}
|
||||
|
||||
.hl-attribute {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hl-type {
|
||||
color: #8FBCBB;
|
||||
}
|
||||
|
||||
.hl-generic {
|
||||
color: #81A1C1;
|
||||
}
|
||||
|
||||
.hl-value {
|
||||
color: #A3BE8C;
|
||||
}
|
||||
|
||||
.hl-literal {
|
||||
color: #A3BE8C;
|
||||
}
|
||||
|
||||
.hl-number {
|
||||
color: #B48EAD;
|
||||
}
|
||||
|
||||
.hl-variable {
|
||||
color: #D8DEE9;
|
||||
}
|
||||
|
||||
.hl-comment {
|
||||
color: #616E88;
|
||||
}
|
||||
|
||||
.hl-blur {
|
||||
filter: blur(2px);
|
||||
}
|
||||
|
||||
.hl-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hl-em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hl-addition {
|
||||
display: inline-block;
|
||||
min-width: 100%;
|
||||
background-color: #00FF0022;
|
||||
}
|
||||
|
||||
.hl-deletion {
|
||||
display: inline-block;
|
||||
min-width: 100%;
|
||||
background-color: #FF000011;
|
||||
}
|
||||
|
||||
.hl-gutter {
|
||||
display: inline-block;
|
||||
font-size: 0.9em;
|
||||
color: #555;
|
||||
padding: 0 1ch;
|
||||
margin-right: 1ch;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.hl-gutter-addition {
|
||||
background-color: #34A853;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.hl-gutter-deletion {
|
||||
background-color: #EA4334;
|
||||
color: #fff;
|
||||
}
|
||||
BIN
public/assets/highlight/nord.css.br
Normal file
BIN
public/assets/highlight/nord.css.br
Normal file
Binary file not shown.
BIN
public/assets/highlight/nord.css.zst
Normal file
BIN
public/assets/highlight/nord.css.zst
Normal file
Binary file not shown.
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
|
||||
Zenburn style from voldmar.ru (c) Vladimir Epifanov <voldmar@voldmar.ru>
|
||||
based on dark.css by Ivan Sagalaev
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #3f3f3f;
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-tag {
|
||||
color: #e3ceab;
|
||||
}
|
||||
|
||||
.hljs-template-tag {
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
.hljs-number {
|
||||
color: #8cd0d3;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute {
|
||||
color: #efdcbc;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #efefaf;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
color: #8f8f8f;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-section,
|
||||
.hljs-type {
|
||||
color: #efef8f;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link {
|
||||
color: #dca3a3;
|
||||
}
|
||||
|
||||
.hljs-deletion,
|
||||
.hljs-string,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #cc9393;
|
||||
}
|
||||
|
||||
.hljs-addition,
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-meta {
|
||||
color: #7f9f7f;
|
||||
}
|
||||
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
BIN
public/assets/js/winter.js.br
Normal file
BIN
public/assets/js/winter.js.br
Normal file
Binary file not shown.
BIN
public/assets/js/winter.js.zst
Normal file
BIN
public/assets/js/winter.js.zst
Normal file
Binary file not shown.
|
|
@ -44,3 +44,12 @@
|
|||
gap: 2ex;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.h-entry {
|
||||
pre {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>@yield('title'){{ config('app.name') }}</title>
|
||||
<link rel="stylesheet" href="/assets/highlight/zenburn.css">
|
||||
<link rel="stylesheet" href="/assets/highlight/nord.css">
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
<link rel="alternate" type="application/rss+xml" title="Blog RSS Feed" href="{{ route('feed.blog.rss') }}">
|
||||
<link rel="alternate" type="application/atom+xml" title="Blog Atom Feed" href="{{ route('feed.blog.atom') }}">
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
<script type="module" src="/assets/js/app.js"></script>
|
||||
|
||||
<script type="module" src="/assets/js/is-land.min.js"></script>
|
||||
<script type="module" src="/assets/js/snow-fall.js"></script>
|
||||
<script type="module" src="/assets/js/winter.js"></script>
|
||||
<is-land on:media="(prefers-reduced-motion: no-preference)">
|
||||
<snow-fall
|
||||
count="150"
|
||||
|
|
|
|||
|
|
@ -56,5 +56,5 @@
|
|||
|
||||
@section('scripts')
|
||||
@parent
|
||||
<link rel="stylesheet" href="/assets/highlight/zenburn.css">
|
||||
<link rel="stylesheet" href="/assets/highlight/nord.css">
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -6,12 +6,9 @@ namespace Tests\Feature\Admin;
|
|||
|
||||
use App\Models\Contact;
|
||||
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\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -141,14 +138,12 @@ class ContactsTest extends TestCase
|
|||
<img class="u-photo" alt="" src="http://tantek.com/tantek.png">
|
||||
</div>
|
||||
HTML;
|
||||
$file = fopen(__DIR__.'/../../aaron.png', 'rb');
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'text/html'], $html),
|
||||
new Response(200, ['Content-Type' => 'image/png'], $file),
|
||||
$file = file_get_contents(__DIR__.'/../../aaron.png');
|
||||
Http::fake([
|
||||
'*' => Http::sequence()
|
||||
->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();
|
||||
$contact = Contact::factory()->create([
|
||||
'homepage' => 'https://tantek.com',
|
||||
|
|
@ -165,12 +160,9 @@ class ContactsTest extends TestCase
|
|||
#[Test]
|
||||
public function getting_remote_avatar_fails_gracefully_with_remote_not_found(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::response('', 404),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$user = User::factory()->make();
|
||||
$contact = Contact::factory()->create();
|
||||
|
||||
|
|
@ -187,13 +179,11 @@ class ContactsTest extends TestCase
|
|||
<img class="u-photo" src="http://tantek.com/tantek.png">
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'text/html'], $html),
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::sequence()
|
||||
->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();
|
||||
$contact = Contact::factory()->create();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ declare(strict_types=1);
|
|||
namespace Tests\Feature;
|
||||
|
||||
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\UriResolver;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -269,12 +266,9 @@ class IndieAuthTest extends TestCase
|
|||
</html>
|
||||
HTML;
|
||||
|
||||
$mockHandler = new MockHandler([
|
||||
new Response(200, [], $appPageHtml),
|
||||
Http::fake([
|
||||
'*' => Http::response($appPageHtml, 200),
|
||||
]);
|
||||
$handlerStack = HandlerStack::create($mockHandler);
|
||||
$mockGuzzleClient = new Client(['handler' => $handlerStack]);
|
||||
$this->app->instance(Client::class, $mockGuzzleClient);
|
||||
|
||||
$user = User::factory()->make();
|
||||
$url = url()->query('/auth', [
|
||||
|
|
@ -313,12 +307,9 @@ class IndieAuthTest extends TestCase
|
|||
</html>
|
||||
HTML;
|
||||
|
||||
$mockHandler = new MockHandler([
|
||||
new Response(200, [], $appPageHtml),
|
||||
Http::fake([
|
||||
'*' => Http::response($appPageHtml, 200),
|
||||
]);
|
||||
$handlerStack = HandlerStack::create($mockHandler);
|
||||
$mockGuzzleClient = new Client(['handler' => $handlerStack]);
|
||||
$this->app->instance(Client::class, $mockGuzzleClient);
|
||||
|
||||
$user = User::factory()->make();
|
||||
$url = url()->query('/auth', [
|
||||
|
|
|
|||
|
|
@ -6,11 +6,8 @@ namespace Tests\Feature;
|
|||
|
||||
use App\Jobs\ProcessLike;
|
||||
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\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
|
@ -98,18 +95,12 @@ class LikesTest extends TestCase
|
|||
</html>
|
||||
END;
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $content),
|
||||
new Response(200, [], $content),
|
||||
Http::fake([
|
||||
'*' => Http::response($content, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->bind(Client::class, function () use ($client) {
|
||||
return $client;
|
||||
});
|
||||
$authorship = new Authorship;
|
||||
|
||||
$job->handle($client, $authorship);
|
||||
$job->handle($authorship);
|
||||
|
||||
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
||||
}
|
||||
|
|
@ -141,18 +132,12 @@ class LikesTest extends TestCase
|
|||
</html>
|
||||
END;
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $content),
|
||||
new Response(200, [], $content),
|
||||
Http::fake([
|
||||
'*' => Http::response($content, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->bind(Client::class, function () use ($client) {
|
||||
return $client;
|
||||
});
|
||||
$authorship = new Authorship;
|
||||
|
||||
$job->handle($client, $authorship);
|
||||
$job->handle($authorship);
|
||||
|
||||
$this->assertEquals('Fred Bloggs', Like::find($id)->author_name);
|
||||
}
|
||||
|
|
@ -177,18 +162,12 @@ class LikesTest extends TestCase
|
|||
</html>
|
||||
END;
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $content),
|
||||
new Response(200, [], $content),
|
||||
Http::fake([
|
||||
'*' => Http::response($content, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->bind(Client::class, function () use ($client) {
|
||||
return $client;
|
||||
});
|
||||
$authorship = new Authorship;
|
||||
|
||||
$job->handle($client, $authorship);
|
||||
$job->handle($authorship);
|
||||
|
||||
$this->assertNull(Like::find($id)->author_name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,19 @@
|
|||
namespace Tests;
|
||||
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
use CreatesApplication;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Http::preventStrayRequests();
|
||||
}
|
||||
|
||||
public function removeDirIfEmpty(string $dir): void
|
||||
{
|
||||
// scandir() will always return `.` and `..` so even an “empty”
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ namespace Tests\Unit;
|
|||
|
||||
use App\Exceptions\InternetArchiveException;
|
||||
use App\Services\BookmarkService;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -29,12 +26,9 @@ class BookmarksTest extends TestCase
|
|||
#[Test]
|
||||
public function archive_link_method_calls_archive_service(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Location' => '/web/1234/example.org']),
|
||||
Http::fake([
|
||||
'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');
|
||||
$this->assertEquals('/web/1234/example.org', $url);
|
||||
}
|
||||
|
|
@ -44,12 +38,9 @@ class BookmarksTest extends TestCase
|
|||
{
|
||||
$this->expectException(InternetArchiveException::class);
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(403),
|
||||
Http::fake([
|
||||
'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');
|
||||
}
|
||||
|
||||
|
|
@ -58,12 +49,9 @@ class BookmarksTest extends TestCase
|
|||
{
|
||||
$this->expectException(InternetArchiveException::class);
|
||||
|
||||
$mock = new MockHandler([
|
||||
new Response(200),
|
||||
Http::fake([
|
||||
'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');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Jobs;
|
||||
|
||||
use App\Jobs\DownloadWebMention;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\FileSystem\FileSystem;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -35,19 +32,16 @@ class DownloadWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
Http::fake([
|
||||
'example.org/*' => Http::response($html, 200, ['X-Foo' => 'Bar']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$job = new DownloadWebMention($source);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$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');
|
||||
}
|
||||
|
|
@ -70,19 +64,18 @@ class DownloadWebMentionJobTest extends TestCase
|
|||
HTML;
|
||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||
$html2 = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html2);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html2),
|
||||
Http::fake([
|
||||
'example.org/*' => Http::sequence()
|
||||
->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->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$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');
|
||||
}
|
||||
|
|
@ -98,14 +91,12 @@ class DownloadWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href=""', 'href="'.config('app.url').'/notes/A"', $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['X-Foo' => 'Bar'], $html),
|
||||
Http::fake([
|
||||
'example.org/*' => Http::response($html, 200, ['X-Foo' => 'Bar']),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$job = new DownloadWebMention($source);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileExists(storage_path('HTML/https/example.org/reply-one/index.html'));
|
||||
}
|
||||
|
|
|
|||
71
tests/Unit/Jobs/NotifyBrrrOfWebMentionJobTest.php
Normal file
71
tests/Unit/Jobs/NotifyBrrrOfWebMentionJobTest.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Jobs;
|
||||
|
||||
use App\Jobs\NotifyBrrrOfWebMention;
|
||||
use App\Models\WebMention;
|
||||
use Illuminate\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
class NotifyBrrrOfWebMentionJobTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function it_posts_a_reply_notification_to_the_brrr_webhook(): void
|
||||
{
|
||||
config(['services.brrr.webhook_url' => 'https://api.brrr.now/v1/br_usr_test']);
|
||||
Http::fake();
|
||||
|
||||
$webMention = WebMention::factory()->make([
|
||||
'source' => 'https://example.org/reply/1',
|
||||
'target' => 'https://jonnybarnes.uk/notes/1',
|
||||
'type' => 'in-reply-to',
|
||||
]);
|
||||
|
||||
$job = new NotifyBrrrOfWebMention($webMention);
|
||||
$job->handle();
|
||||
|
||||
Http::assertSent(function (Request $request) {
|
||||
return $request->url() === 'https://api.brrr.now/v1/br_usr_test'
|
||||
&& $request['title'] === 'New reply'
|
||||
&& $request['message'] === 'From https://example.org/reply/1'
|
||||
&& $request['open_url'] === 'https://jonnybarnes.uk/notes/1';
|
||||
});
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_titles_notifications_by_webmention_type(): void
|
||||
{
|
||||
config(['services.brrr.webhook_url' => 'https://api.brrr.now/v1/br_usr_test']);
|
||||
Http::fake();
|
||||
|
||||
foreach ([
|
||||
'in-reply-to' => 'New reply',
|
||||
'like-of' => 'New like',
|
||||
'repost-of' => 'New repost',
|
||||
'something-else' => 'New webmention',
|
||||
] as $type => $expectedTitle) {
|
||||
$webMention = WebMention::factory()->make(['type' => $type]);
|
||||
|
||||
(new NotifyBrrrOfWebMention($webMention))->handle();
|
||||
|
||||
Http::assertSent(fn (Request $request) => $request['title'] === $expectedTitle);
|
||||
}
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_does_nothing_when_no_webhook_url_is_configured(): void
|
||||
{
|
||||
config(['services.brrr.webhook_url' => null]);
|
||||
Http::fake();
|
||||
|
||||
$webMention = WebMention::factory()->make();
|
||||
|
||||
(new NotifyBrrrOfWebMention($webMention))->handle();
|
||||
|
||||
Http::assertNothingSent();
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ namespace Tests\Unit\Jobs;
|
|||
|
||||
use App\Jobs\ProcessMedia;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\ImageManager;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -15,10 +14,9 @@ class ProcessMediaJobTest extends TestCase
|
|||
#[Test]
|
||||
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');
|
||||
$job = new ProcessMedia('file.txt');
|
||||
$job->handle($manager);
|
||||
$job = new ProcessMedia('media/file.txt');
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileDoesNotExist(storage_path('app/media/').'file.txt');
|
||||
}
|
||||
|
|
@ -26,10 +24,9 @@ class ProcessMediaJobTest extends TestCase
|
|||
#[Test]
|
||||
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'));
|
||||
$job = new ProcessMedia('aaron.png');
|
||||
$job->handle($manager);
|
||||
$job = new ProcessMedia('media/aaron.png');
|
||||
$job->handle();
|
||||
|
||||
$this->assertFileDoesNotExist(storage_path('app/media/').'aaron.png');
|
||||
|
||||
|
|
@ -41,10 +38,9 @@ class ProcessMediaJobTest extends TestCase
|
|||
#[Test]
|
||||
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'));
|
||||
$job = new ProcessMedia('media/test-image.jpg');
|
||||
$job->handle($manager);
|
||||
$job->handle();
|
||||
|
||||
// These need to look in public disk
|
||||
Storage::disk('public')->assertExists('media/test-image.jpg');
|
||||
|
|
|
|||
|
|
@ -5,16 +5,14 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Jobs;
|
||||
|
||||
use App\Exceptions\RemoteContentNotFoundException;
|
||||
use App\Jobs\NotifyBrrrOfWebMention;
|
||||
use App\Jobs\ProcessWebMention;
|
||||
use App\Jobs\SaveProfileImage;
|
||||
use App\Models\Note;
|
||||
use App\Models\WebMention;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\FileSystem\FileSystem;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Jonnybarnes\WebmentionsParser\Parser;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
|
@ -39,17 +37,15 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
$this->expectException(RemoteContentNotFoundException::class);
|
||||
|
||||
$parser = new Parser;
|
||||
$mock = new MockHandler([
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::response('', 404),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/mention/1/';
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
|
@ -65,19 +61,18 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/mention/1/';
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
Queue::assertPushed(SaveProfileImage::class);
|
||||
Queue::assertPushed(NotifyBrrrOfWebMention::class);
|
||||
$this->assertDatabaseHas('webmentions', [
|
||||
'source' => $source,
|
||||
'type' => 'like-of',
|
||||
|
|
@ -103,16 +98,15 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">Updated reply</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
Queue::assertPushed(SaveProfileImage::class);
|
||||
Queue::assertNotPushed(NotifyBrrrOfWebMention::class);
|
||||
$this->assertDatabaseHas('webmentions', [
|
||||
'source' => $source,
|
||||
'type' => 'in-reply-to',
|
||||
|
|
@ -132,11 +126,9 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">Replying to someone else</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/reply/1';
|
||||
|
|
@ -151,7 +143,7 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
$this->assertDatabaseMissing('webmentions', [
|
||||
'source' => $source,
|
||||
|
|
@ -169,11 +161,9 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">I like someone else now</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/reply/1';
|
||||
|
|
@ -188,7 +178,7 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
$this->assertDatabaseMissing('webmentions', [
|
||||
'source' => $source,
|
||||
|
|
@ -208,19 +198,18 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
</div>
|
||||
HTML;
|
||||
$html = str_replace('href="', 'href="'.config('app.url'), $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
// 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';
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
Queue::assertPushed(NotifyBrrrOfWebMention::class);
|
||||
$this->assertGreaterThan(255, strlen($source));
|
||||
$this->assertDatabaseHas('webmentions', [
|
||||
'source' => $source,
|
||||
|
|
@ -238,11 +227,9 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
<div class="e-content">Reposting someone else</div>
|
||||
</div>
|
||||
HTML;
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://example.org/reply/1';
|
||||
|
|
@ -257,7 +244,7 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
]);
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
$job->handle($parser);
|
||||
|
||||
$this->assertDatabaseMissing('webmentions', [
|
||||
'source' => $source,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Jobs;
|
||||
|
||||
use App\Jobs\SaveProfileImage;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
|
@ -58,12 +55,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function remote_author_images_are_saved_locally(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
||||
Http::fake([
|
||||
'*' => 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' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
@ -83,12 +77,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function local_default_author_image_is_used_as_fallback(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(404),
|
||||
Http::fake([
|
||||
'*' => Http::response('', 404),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
$mf = ['items' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
@ -111,12 +102,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function we_get_url_from_photo_object_if_alt_text_is_provided(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
||||
Http::fake([
|
||||
'*' => 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' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
@ -139,12 +127,9 @@ class SaveProfileImageJobTest extends TestCase
|
|||
#[Test]
|
||||
public function use_first_url_if_multiple_homepages_are_provided(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'image/jpeg'], 'fake jpeg image'),
|
||||
Http::fake([
|
||||
'*' => 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' => []];
|
||||
$author = [
|
||||
'properties' => [
|
||||
|
|
|
|||
|
|
@ -6,13 +6,8 @@ namespace Tests\Unit\Jobs;
|
|||
|
||||
use App\Jobs\SaveScreenshot;
|
||||
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\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
|
@ -25,57 +20,34 @@ class SaveScreenshotJobTest extends TestCase
|
|||
public function screenshot_is_saved_by_job(): void
|
||||
{
|
||||
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"}}}'),
|
||||
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"}}}'),
|
||||
new Response(200, ['Content-Type' => 'image/png'], fopen(__DIR__.'/../../theverge.com.png', 'rb')),
|
||||
|
||||
Http::fake([
|
||||
'api.cloudconvert.com/v2/capture-website' => Http::response([
|
||||
'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();
|
||||
$job = new SaveScreenshot($bookmark);
|
||||
|
|
@ -84,68 +56,45 @@ class SaveScreenshotJobTest extends TestCase
|
|||
|
||||
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
||||
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]
|
||||
public function screenshot_job_handles_unfinished_tasks(): void
|
||||
{
|
||||
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"}}}'),
|
||||
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"}}}'),
|
||||
new Response(200, ['Content-Type' => 'image/png'], fopen(__DIR__.'/../../theverge.com.png', 'rb')),
|
||||
|
||||
Http::fake([
|
||||
'api.cloudconvert.com/v2/capture-website' => Http::response([
|
||||
'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();
|
||||
$job = new SaveScreenshot($bookmark);
|
||||
|
|
@ -154,9 +103,10 @@ class SaveScreenshotJobTest extends TestCase
|
|||
|
||||
$this->assertEquals('68d52633-e170-465e-b13e-746c97d01ffb', $bookmark->screenshot);
|
||||
Storage::disk('public')->assertExists('/assets/img/bookmarks/'.$bookmark->screenshot.'.png');
|
||||
// Also assert we made the correct number of requests
|
||||
$this->assertCount(2, $container);
|
||||
// However with retries there should be more than 4 responses for the 2 requests
|
||||
$this->assertEquals(0, $retryMock->count());
|
||||
|
||||
// capture-website, 2x poll (waiting then finished), export/url, 2x poll (waiting then finished), download
|
||||
Http::assertSentCount(7);
|
||||
// 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\Models\Note;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -28,12 +25,9 @@ class SendWebMentionJobTest extends TestCase
|
|||
public function discover_webmention_endpoint_from_header_links(): void
|
||||
{
|
||||
$url = 'https://example.org/webmention';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Link' => '<'.$url.'>; rel="webmention"']),
|
||||
Http::fake([
|
||||
'*' => 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);
|
||||
$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
|
||||
{
|
||||
$html = '<link rel="webmention" href="https://example.org/webmention">';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$job = new SendWebMentions(new Note);
|
||||
$this->assertEquals(
|
||||
|
|
@ -61,12 +52,9 @@ class SendWebMentionJobTest extends TestCase
|
|||
public function discover_webmention_endpoint_from_legacy_html_markup(): void
|
||||
{
|
||||
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
Http::fake([
|
||||
'*' => Http::response($html, 200),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->app->instance(Client::class, $client);
|
||||
|
||||
$job = new SendWebMentions(new Note);
|
||||
$this->assertEquals(
|
||||
|
|
@ -95,13 +83,10 @@ class SendWebMentionJobTest extends TestCase
|
|||
public function we_send_a_webmention_for_a_note(): void
|
||||
{
|
||||
$html = '<link rel="http://webmention.org/" href="https://example.org/webmention">';
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
new Response(202),
|
||||
Http::fake([
|
||||
'example.org/webmention' => Http::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->note = 'Hi [Aaron](https://aaronparecki.com)';
|
||||
|
|
@ -114,13 +99,10 @@ class SendWebMentionJobTest extends TestCase
|
|||
#[Test]
|
||||
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
|
||||
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);
|
||||
$this->assertNull($job->discoverWebmentionEndpoint('https://example.org'));
|
||||
|
|
|
|||
|
|
@ -5,12 +5,9 @@ namespace Tests\Unit\Jobs;
|
|||
use App\Jobs\SyndicateNoteToBluesky;
|
||||
use App\Models\Note;
|
||||
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\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -24,19 +21,17 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
|||
$faker = Factory::create();
|
||||
$randomNumber = $faker->randomNumber();
|
||||
$blueskyUrl = 'https://bsky.app/profile/jonnybarnes.uk/'.$randomNumber;
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'url' => $blueskyUrl,
|
||||
'id' => (string) $randomNumber,
|
||||
'type' => ['h-entry'],
|
||||
])),
|
||||
], 201),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToBluesky($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'bluesky_url' => $blueskyUrl,
|
||||
|
|
@ -46,39 +41,31 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
|||
#[Test]
|
||||
public function we_post_the_correct_source_and_target(): void
|
||||
{
|
||||
$container = [];
|
||||
$history = Middleware::history($container);
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'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**']);
|
||||
$job = new SyndicateNoteToBluesky($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$request = $container[0]['request'];
|
||||
$body = [];
|
||||
parse_str((string) $request->getBody(), $body);
|
||||
|
||||
$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']);
|
||||
Http::assertSent(function (Request $request) use ($note) {
|
||||
return $request->url() === 'https://brid.gy/publish/webmention'
|
||||
&& $request['source'] === $note->uri
|
||||
&& $request['target'] === 'https://brid.gy/publish/bluesky';
|
||||
});
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_bridgy_failure_throws_and_does_not_set_bluesky_url(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(400, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'error' => 'Could not find target link',
|
||||
])),
|
||||
], 400),
|
||||
]);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToBluesky($note);
|
||||
|
|
@ -86,7 +73,7 @@ class SyndicateNoteToBlueskyJobTest extends TestCase
|
|||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
try {
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
} finally {
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'id' => $note->id,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,9 @@ namespace Tests\Unit\Jobs;
|
|||
use App\Jobs\SyndicateNoteToMastodon;
|
||||
use App\Models\Note;
|
||||
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\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -24,19 +21,17 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
|||
$faker = Factory::create();
|
||||
$randomNumber = $faker->randomNumber();
|
||||
$mastodonUrl = 'https://mastodon.example/@jonny/'.$randomNumber;
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'url' => $mastodonUrl,
|
||||
'id' => (string) $randomNumber,
|
||||
'type' => ['h-entry'],
|
||||
])),
|
||||
], 201),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToMastodon($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'mastodon_url' => $mastodonUrl,
|
||||
|
|
@ -46,39 +41,31 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
|||
#[Test]
|
||||
public function we_post_the_correct_source_and_target(): void
|
||||
{
|
||||
$container = [];
|
||||
$history = Middleware::history($container);
|
||||
$mock = new MockHandler([
|
||||
new Response(201, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'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**']);
|
||||
$job = new SyndicateNoteToMastodon($note);
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
|
||||
$request = $container[0]['request'];
|
||||
$body = [];
|
||||
parse_str((string) $request->getBody(), $body);
|
||||
|
||||
$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']);
|
||||
Http::assertSent(function (Request $request) use ($note) {
|
||||
return $request->url() === 'https://brid.gy/publish/webmention'
|
||||
&& $request['source'] === $note->uri
|
||||
&& $request['target'] === 'https://brid.gy/publish/mastodon';
|
||||
});
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_bridgy_failure_throws_and_does_not_set_mastodon_url(): void
|
||||
{
|
||||
$mock = new MockHandler([
|
||||
new Response(400, ['Content-Type' => 'application/json'], json_encode([
|
||||
Http::fake([
|
||||
'brid.gy/*' => Http::response([
|
||||
'error' => 'Could not find target link',
|
||||
])),
|
||||
], 400),
|
||||
]);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$job = new SyndicateNoteToMastodon($note);
|
||||
|
|
@ -86,7 +73,7 @@ class SyndicateNoteToMastodonJobTest extends TestCase
|
|||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
try {
|
||||
$job->handle($client);
|
||||
$job->handle();
|
||||
} finally {
|
||||
$this->assertDatabaseHas('notes', [
|
||||
'id' => $note->id,
|
||||
|
|
|
|||
|
|
@ -9,13 +9,10 @@ use App\Models\Media;
|
|||
use App\Models\Note;
|
||||
use App\Models\Place;
|
||||
use App\Models\Tag;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
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"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'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;
|
||||
$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"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'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;
|
||||
$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"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'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;
|
||||
$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"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'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;
|
||||
$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"]}
|
||||
JSON;
|
||||
// phpcs:enable Generic.Files.LineLength.TooLong
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], $json),
|
||||
Http::fake([
|
||||
'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;
|
||||
$address = $note->reverseGeoCode(54.3, 9.4);
|
||||
|
|
|
|||
Loading…
Reference in a new issue