commit
335acb130e
114 changed files with 4696 additions and 2415 deletions
|
@ -8,6 +8,9 @@ indent_style = space
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{js,css}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ SESSION_SECURE_COOKIE=true
|
||||||
|
|
||||||
LOG_SLACK_WEBHOOK_URL=
|
LOG_SLACK_WEBHOOK_URL=
|
||||||
FLARE_KEY=
|
FLARE_KEY=
|
||||||
|
IGNITION_OPEN_AI_KEY=
|
||||||
|
|
||||||
FONT_LINK=
|
FONT_LINK=
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
parserOptions:
|
parserOptions:
|
||||||
sourceType: 'module'
|
sourceType: 'module'
|
||||||
|
ecmaVersion: 'latest'
|
||||||
extends: 'eslint:recommended'
|
extends: 'eslint:recommended'
|
||||||
env:
|
env:
|
||||||
browser: true
|
browser: true
|
||||||
|
@ -9,7 +10,7 @@ ignorePatterns:
|
||||||
rules:
|
rules:
|
||||||
indent:
|
indent:
|
||||||
- error
|
- error
|
||||||
- 4
|
- 2
|
||||||
linebreak-style:
|
linebreak-style:
|
||||||
- error
|
- error
|
||||||
- unix
|
- unix
|
||||||
|
@ -24,3 +25,14 @@ rules:
|
||||||
- allow:
|
- allow:
|
||||||
- warn
|
- warn
|
||||||
- error
|
- error
|
||||||
|
no-await-in-loop:
|
||||||
|
- error
|
||||||
|
no-promise-executor-return:
|
||||||
|
- error
|
||||||
|
require-atomic-updates:
|
||||||
|
- error
|
||||||
|
max-nested-callbacks:
|
||||||
|
- error
|
||||||
|
- 3
|
||||||
|
prefer-promise-reject-errors:
|
||||||
|
- error
|
||||||
|
|
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
|
@ -2,6 +2,8 @@ name: Deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
|
|
2
.github/workflows/phpunit.yml
vendored
2
.github/workflows/phpunit.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
||||||
- name: Setup PHP with pecl extensions
|
- name: Setup PHP with pecl extensions
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: '8.1'
|
php-version: '8.2'
|
||||||
extensions: phpredis,imagick
|
extensions: phpredis,imagick
|
||||||
|
|
||||||
- name: Copy .env
|
- name: Copy .env
|
||||||
|
|
2
.github/workflows/pint.yml
vendored
2
.github/workflows/pint.yml
vendored
|
@ -16,7 +16,7 @@ jobs:
|
||||||
- name: Setup PHP with pecl extensions
|
- name: Setup PHP with pecl extensions
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: '8.1'
|
php-version: '8.2'
|
||||||
|
|
||||||
- name: Get Composer Cache Directory
|
- name: Get Composer Cache Directory
|
||||||
id: composer-cache
|
id: composer-cache
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
{
|
{
|
||||||
"extends": ["stylelint-config-standard"],
|
"extends": ["stylelint-config-standard"]
|
||||||
"rules": {
|
|
||||||
"indentation": 4,
|
|
||||||
"import-notation": "string"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
*/
|
*/
|
||||||
class MigratePlaceDataFromPostgis extends Command
|
class MigratePlaceDataFromPostgis extends Command
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,9 @@ use Illuminate\Console\Command;
|
||||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||||
use Illuminate\FileSystem\FileSystem;
|
use Illuminate\FileSystem\FileSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ParseCachedWebMentions extends Command
|
class ParseCachedWebMentions extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,9 @@ use App\Jobs\DownloadWebMention;
|
||||||
use App\Models\WebMention;
|
use App\Models\WebMention;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ReDownloadWebMentions extends Command
|
class ReDownloadWebMentions extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,23 +7,12 @@ use Throwable;
|
||||||
|
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* The list of the inputs that are never flashed to the session on validation exceptions.
|
|
||||||
*
|
|
||||||
* @var array<int, string>
|
|
||||||
*/
|
|
||||||
protected $dontFlash = [
|
|
||||||
'current_password',
|
|
||||||
'password',
|
|
||||||
'password_confirmation',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the exception handling callbacks for the application.
|
* Register the exception handling callbacks for the application.
|
||||||
*/
|
*/
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $_e) {
|
||||||
//
|
//
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions;
|
|
||||||
|
|
||||||
class TwitterContentException extends \Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -9,6 +9,9 @@ use App\Models\Article;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ArticlesController extends Controller
|
class ArticlesController extends Controller
|
||||||
{
|
{
|
||||||
public function index(): View
|
public function index(): View
|
||||||
|
|
|
@ -10,6 +10,9 @@ use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class BioController extends Controller
|
class BioController extends Controller
|
||||||
{
|
{
|
||||||
public function show(): View
|
public function show(): View
|
||||||
|
|
|
@ -7,9 +7,11 @@ namespace App\Http\Controllers\Admin;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\MicropubClient;
|
use App\Models\MicropubClient;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ClientsController extends Controller
|
class ClientsController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,10 +9,12 @@ use App\Models\Contact;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Filesystem\Filesystem;
|
use Illuminate\Filesystem\Filesystem;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ContactsController extends Controller
|
class ContactsController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,6 +7,9 @@ namespace App\Http\Controllers\Admin;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,6 +10,9 @@ use App\Models\Like;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class LikesController extends Controller
|
class LikesController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,9 @@ use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class NotesController extends Controller
|
class NotesController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
257
app/Http/Controllers/Admin/PasskeysController.php
Normal file
257
app/Http/Controllers/Admin/PasskeysController.php
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Passkey;
|
||||||
|
use App\Models\User;
|
||||||
|
use Cose\Algorithm\Manager;
|
||||||
|
use Cose\Algorithm\Signature\ECDSA\ES256;
|
||||||
|
use Cose\Algorithm\Signature\EdDSA\Ed25519;
|
||||||
|
use Cose\Algorithm\Signature\RSA\RS256;
|
||||||
|
use Cose\Algorithms;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use ParagonIE\ConstantTime\Base64UrlSafe;
|
||||||
|
use Throwable;
|
||||||
|
use Webauthn\AttestationStatement\AttestationObjectLoader;
|
||||||
|
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
|
||||||
|
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
|
||||||
|
use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler;
|
||||||
|
use Webauthn\AuthenticatorAssertionResponse;
|
||||||
|
use Webauthn\AuthenticatorAssertionResponseValidator;
|
||||||
|
use Webauthn\AuthenticatorAttestationResponse;
|
||||||
|
use Webauthn\AuthenticatorAttestationResponseValidator;
|
||||||
|
use Webauthn\AuthenticatorSelectionCriteria;
|
||||||
|
use Webauthn\Exception\WebauthnException;
|
||||||
|
use Webauthn\PublicKeyCredentialCreationOptions;
|
||||||
|
use Webauthn\PublicKeyCredentialLoader;
|
||||||
|
use Webauthn\PublicKeyCredentialParameters;
|
||||||
|
use Webauthn\PublicKeyCredentialRequestOptions;
|
||||||
|
use Webauthn\PublicKeyCredentialRpEntity;
|
||||||
|
use Webauthn\PublicKeyCredentialSource;
|
||||||
|
use Webauthn\PublicKeyCredentialUserEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
|
class PasskeysController extends Controller
|
||||||
|
{
|
||||||
|
public function index(): View
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$passkeys = $user->passkey;
|
||||||
|
|
||||||
|
return view('admin.passkeys.index', compact('passkeys'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreateOptions(): JsonResponse
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
// RP Entity i.e. the application
|
||||||
|
$rpEntity = PublicKeyCredentialRpEntity::create(
|
||||||
|
config('app.name'),
|
||||||
|
config('url.longurl'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// User Entity
|
||||||
|
$userEntity = PublicKeyCredentialUserEntity::create(
|
||||||
|
$user->name,
|
||||||
|
(string) $user->id,
|
||||||
|
$user->name,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Challenge
|
||||||
|
$challenge = random_bytes(16);
|
||||||
|
|
||||||
|
// List of supported public key parameters
|
||||||
|
$pubKeyCredParams = collect([
|
||||||
|
Algorithms::COSE_ALGORITHM_EDDSA,
|
||||||
|
Algorithms::COSE_ALGORITHM_ES256,
|
||||||
|
Algorithms::COSE_ALGORITHM_RS256,
|
||||||
|
])->map(
|
||||||
|
fn ($algorithm) => PublicKeyCredentialParameters::create('public-key', $algorithm)
|
||||||
|
)->toArray();
|
||||||
|
|
||||||
|
$authenticatorSelectionCriteria = AuthenticatorSelectionCriteria::create(
|
||||||
|
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED,
|
||||||
|
residentKey: AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_REQUIRED,
|
||||||
|
requireResidentKey: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = PublicKeyCredentialCreationOptions::create(
|
||||||
|
$rpEntity,
|
||||||
|
$userEntity,
|
||||||
|
$challenge,
|
||||||
|
$pubKeyCredParams,
|
||||||
|
authenticatorSelection: $authenticatorSelectionCriteria,
|
||||||
|
attestation: PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = json_encode($options, JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
session(['create_options' => $options]);
|
||||||
|
|
||||||
|
return JsonResponse::fromJsonString($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
$publicKeyCredentialCreationOptionsData = session('create_options');
|
||||||
|
if (empty($publicKeyCredentialCreationOptionsData)) {
|
||||||
|
throw new WebAuthnException('No public key credential request options found');
|
||||||
|
}
|
||||||
|
$publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromString($publicKeyCredentialCreationOptionsData);
|
||||||
|
|
||||||
|
// Unset session data to mitigate replay attacks
|
||||||
|
session()->forget('create_options');
|
||||||
|
|
||||||
|
$attestationSupportManager = AttestationStatementSupportManager::create();
|
||||||
|
$attestationSupportManager->add(NoneAttestationStatementSupport::create());
|
||||||
|
$attestationObjectLoader = AttestationObjectLoader::create($attestationSupportManager);
|
||||||
|
$publicKeyCredentialLoader = PublicKeyCredentialLoader::create($attestationObjectLoader);
|
||||||
|
|
||||||
|
$publicKeyCredential = $publicKeyCredentialLoader->load(json_encode($request->all(), JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
if (! $publicKeyCredential->response instanceof AuthenticatorAttestationResponse) {
|
||||||
|
throw new WebAuthnException('Invalid response type');
|
||||||
|
}
|
||||||
|
|
||||||
|
$attestationStatementSupportManager = AttestationStatementSupportManager::create();
|
||||||
|
$attestationStatementSupportManager->add(NoneAttestationStatementSupport::create());
|
||||||
|
|
||||||
|
$authenticatorAttestationResponseValidator = AuthenticatorAttestationResponseValidator::create(
|
||||||
|
attestationStatementSupportManager: $attestationStatementSupportManager,
|
||||||
|
publicKeyCredentialSourceRepository: null,
|
||||||
|
tokenBindingHandler: null,
|
||||||
|
extensionOutputCheckerHandler: ExtensionOutputCheckerHandler::create(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$securedRelyingPartyId = [];
|
||||||
|
if (App::environment('local', 'development')) {
|
||||||
|
$securedRelyingPartyId = [config('url.longurl')];
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check(
|
||||||
|
authenticatorAttestationResponse: $publicKeyCredential->response,
|
||||||
|
publicKeyCredentialCreationOptions: $publicKeyCredentialCreationOptions,
|
||||||
|
request: config('url.longurl'),
|
||||||
|
securedRelyingPartyId: $securedRelyingPartyId,
|
||||||
|
);
|
||||||
|
|
||||||
|
$user->passkey()->create([
|
||||||
|
'passkey_id' => Base64UrlSafe::encodeUnpadded($publicKeyCredentialSource->publicKeyCredentialId),
|
||||||
|
'passkey' => json_encode($publicKeyCredentialSource, JSON_THROW_ON_ERROR),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Passkey created successfully',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestOptions(): JsonResponse
|
||||||
|
{
|
||||||
|
$publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create(
|
||||||
|
challenge: random_bytes(16),
|
||||||
|
userVerification: PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED
|
||||||
|
);
|
||||||
|
|
||||||
|
$publicKeyCredentialRequestOptions = json_encode($publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
session(['request_options' => $publicKeyCredentialRequestOptions]);
|
||||||
|
|
||||||
|
return JsonResponse::fromJsonString($publicKeyCredentialRequestOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$requestOptions = session('request_options');
|
||||||
|
session()->forget('request_options');
|
||||||
|
|
||||||
|
if (empty($requestOptions)) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'No request options found',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::createFromString($requestOptions);
|
||||||
|
|
||||||
|
$attestationSupportManager = AttestationStatementSupportManager::create();
|
||||||
|
$attestationSupportManager->add(NoneAttestationStatementSupport::create());
|
||||||
|
$attestationObjectLoader = AttestationObjectLoader::create($attestationSupportManager);
|
||||||
|
$publicKeyCredentialLoader = PublicKeyCredentialLoader::create($attestationObjectLoader);
|
||||||
|
|
||||||
|
$publicKeyCredential = $publicKeyCredentialLoader->load(json_encode($request->all(), JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
if (! $publicKeyCredential->response instanceof AuthenticatorAssertionResponse) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Invalid response type',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$passkey = Passkey::firstWhere('passkey_id', $publicKeyCredential->id);
|
||||||
|
if (! $passkey) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Passkey not found',
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$credential = PublicKeyCredentialSource::createFromArray(json_decode($passkey->passkey, true, 512, JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
$algorithmManager = Manager::create();
|
||||||
|
$algorithmManager->add(new Ed25519());
|
||||||
|
$algorithmManager->add(new ES256());
|
||||||
|
$algorithmManager->add(new RS256());
|
||||||
|
|
||||||
|
$authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator(
|
||||||
|
publicKeyCredentialSourceRepository: null,
|
||||||
|
tokenBindingHandler: null,
|
||||||
|
extensionOutputCheckerHandler: ExtensionOutputCheckerHandler::create(),
|
||||||
|
algorithmManager: $algorithmManager,
|
||||||
|
);
|
||||||
|
|
||||||
|
$securedRelyingPartyId = [];
|
||||||
|
if (App::environment('local', 'development')) {
|
||||||
|
$securedRelyingPartyId = [config('url.longurl')];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$authenticatorAssertionResponseValidator->check(
|
||||||
|
credentialId: $credential,
|
||||||
|
authenticatorAssertionResponse: $publicKeyCredential->response,
|
||||||
|
publicKeyCredentialRequestOptions: $publicKeyCredentialRequestOptions,
|
||||||
|
request: config('url.longurl'),
|
||||||
|
userHandle: null,
|
||||||
|
securedRelyingPartyId: $securedRelyingPartyId,
|
||||||
|
);
|
||||||
|
} catch (Throwable) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Passkey could not be verified',
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = User::find($passkey->user_id);
|
||||||
|
Auth::login($user);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Passkey verified successfully',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,9 @@ use App\Services\PlaceService;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class PlacesController extends Controller
|
class PlacesController extends Controller
|
||||||
{
|
{
|
||||||
protected PlaceService $placeService;
|
protected PlaceService $placeService;
|
||||||
|
|
|
@ -10,6 +10,9 @@ use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class SyndicationTargetsController extends Controller
|
class SyndicationTargetsController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,6 +10,9 @@ use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Jonnybarnes\IndieWeb\Numbers;
|
use Jonnybarnes\IndieWeb\Numbers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ArticlesController extends Controller
|
class ArticlesController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,6 +9,9 @@ use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +34,7 @@ class AuthController extends Controller
|
||||||
$credentials = $request->only('name', 'password');
|
$credentials = $request->only('name', 'password');
|
||||||
|
|
||||||
if (Auth::attempt($credentials, true)) {
|
if (Auth::attempt($credentials, true)) {
|
||||||
return redirect()->intended('/');
|
return redirect()->intended('/admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
|
|
|
@ -7,6 +7,9 @@ namespace App\Http\Controllers;
|
||||||
use App\Models\Bookmark;
|
use App\Models\Bookmark;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class BookmarksController extends Controller
|
class BookmarksController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,9 @@ use App\Models\Contact;
|
||||||
use Illuminate\Filesystem\Filesystem;
|
use Illuminate\Filesystem\Filesystem;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ContactsController extends Controller
|
class ContactsController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,6 +9,9 @@ use App\Models\Note;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class FeedsController extends Controller
|
class FeedsController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,16 +7,18 @@ use App\Models\Bio;
|
||||||
use App\Models\Bookmark;
|
use App\Models\Bookmark;
|
||||||
use App\Models\Like;
|
use App\Models\Like;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class FrontPageController extends Controller
|
class FrontPageController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Show all the recent activity.
|
* Show all the recent activity.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): Response|View
|
public function index(): Response|View
|
||||||
{
|
{
|
||||||
$notes = Note::latest()->with(['media', 'client', 'place'])->get();
|
$notes = Note::latest()->with(['media', 'client', 'place'])->get();
|
||||||
$articles = Article::latest()->get();
|
$articles = Article::latest()->get();
|
||||||
|
|
|
@ -7,6 +7,9 @@ namespace App\Http\Controllers;
|
||||||
use App\Models\Like;
|
use App\Models\Like;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class LikesController extends Controller
|
class LikesController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,6 +19,9 @@ use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
|
||||||
use Monolog\Handler\StreamHandler;
|
use Monolog\Handler\StreamHandler;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class MicropubController extends Controller
|
class MicropubController extends Controller
|
||||||
{
|
{
|
||||||
protected TokenService $tokenService;
|
protected TokenService $tokenService;
|
||||||
|
|
|
@ -24,6 +24,9 @@ use Lcobucci\JWT\Token\InvalidTokenStructure;
|
||||||
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
|
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class MicropubMediaController extends Controller
|
class MicropubMediaController extends Controller
|
||||||
{
|
{
|
||||||
protected TokenService $tokenService;
|
protected TokenService $tokenService;
|
||||||
|
|
|
@ -8,19 +8,21 @@ use App\Models\Note;
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Jonnybarnes\IndieWeb\Numbers;
|
use Jonnybarnes\IndieWeb\Numbers;
|
||||||
|
|
||||||
// Need to sort out Twitter and webmentions!
|
/**
|
||||||
|
* @todo Need to sort out Twitter and webmentions!
|
||||||
|
*
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class NotesController extends Controller
|
class NotesController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Show all the notes. This is also the homepage.
|
* Show all the notes. This is also the homepage.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): View|Response
|
public function index(): View|Response
|
||||||
{
|
{
|
||||||
$notes = Note::latest()
|
$notes = Note::latest()
|
||||||
->with('place', 'media', 'client')
|
->with('place', 'media', 'client')
|
||||||
|
|
|
@ -7,6 +7,9 @@ namespace App\Http\Controllers;
|
||||||
use App\Models\Place;
|
use App\Models\Place;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class PlacesController extends Controller
|
class PlacesController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,6 +6,9 @@ use App\Models\Note;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
{
|
{
|
||||||
public function search(Request $request): View
|
public function search(Request $request): View
|
||||||
|
|
|
@ -6,6 +6,9 @@ namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class ShortURLsController extends Controller
|
class ShortURLsController extends Controller
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -12,6 +12,9 @@ use Illuminate\Http\Request;
|
||||||
use IndieAuth\Client;
|
use IndieAuth\Client;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class TokenEndpointController extends Controller
|
class TokenEndpointController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,6 +12,9 @@ use Illuminate\Http\Response;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Jonnybarnes\IndieWeb\Numbers;
|
use Jonnybarnes\IndieWeb\Numbers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class WebMentionsController extends Controller
|
class WebMentionsController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,8 @@ class CSPHeader
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,8 @@ class CorsHeaders
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,8 @@ class LinkHeadersMiddleware
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,8 @@ class LocalhostSessionMiddleware
|
||||||
* Whilst we are developing locally, automatically log in as
|
* Whilst we are developing locally, automatically log in as
|
||||||
* `['me' => config('app.url')]` as I can’t manually log in as
|
* `['me' => config('app.url')]` as I can’t manually log in as
|
||||||
* a .localhost domain.
|
* a .localhost domain.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,8 @@ class MyAuthMiddleware
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Check the user is logged in.
|
* Check the user is logged in.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,8 @@ class ValidateSignature extends Middleware
|
||||||
* The names of the query string parameters that should be ignored.
|
* The names of the query string parameters that should be ignored.
|
||||||
*
|
*
|
||||||
* @var array<int, string>
|
* @var array<int, string>
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedProperty
|
||||||
*/
|
*/
|
||||||
protected $except = [
|
protected $except = [
|
||||||
// 'fbclid',
|
// 'fbclid',
|
||||||
|
|
|
@ -12,6 +12,8 @@ class VerifyMicropubToken
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
|
|
@ -71,7 +71,6 @@ class SendWebMentions implements ShouldQueue
|
||||||
|
|
||||||
$endpoint = null;
|
$endpoint = null;
|
||||||
|
|
||||||
/** @var Client $guzzle */
|
|
||||||
$guzzle = resolve(Client::class);
|
$guzzle = resolve(Client::class);
|
||||||
$response = $guzzle->get($url);
|
$response = $guzzle->get($url);
|
||||||
//check HTTP Headers for webmention endpoint
|
//check HTTP Headers for webmention endpoint
|
||||||
|
|
25
app/Models/Passkey.php
Normal file
25
app/Models/Passkey.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class Passkey extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/** @inerhitDoc */
|
||||||
|
protected $fillable = [
|
||||||
|
'passkey_id',
|
||||||
|
'passkey',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
|
@ -24,4 +25,9 @@ class User extends Authenticatable
|
||||||
'password',
|
'password',
|
||||||
'remember_token',
|
'remember_token',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function passkey(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Passkey::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,15 @@ use App\Models\Tag;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Do we need psalm-suppress for these observer methods?
|
||||||
|
*/
|
||||||
class NoteObserver
|
class NoteObserver
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Listen to the Note created event.
|
* Listen to the Note created event.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function created(Note $note): void
|
public function created(Note $note): void
|
||||||
{
|
{
|
||||||
|
@ -35,6 +40,8 @@ class NoteObserver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to the Note updated event.
|
* Listen to the Note updated event.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function updated(Note $note): void
|
public function updated(Note $note): void
|
||||||
{
|
{
|
||||||
|
@ -59,6 +66,8 @@ class NoteObserver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to the Note deleting event.
|
* Listen to the Note deleting event.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function deleting(Note $note): void
|
public function deleting(Note $note): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,6 @@ namespace App\Providers;
|
||||||
use Illuminate\Auth\Events\Registered;
|
use Illuminate\Auth\Events\Registered;
|
||||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
use Illuminate\Support\Facades\Event;
|
|
||||||
|
|
||||||
class EventServiceProvider extends ServiceProvider
|
class EventServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,9 @@ use Illuminate\Support\Facades\Gate;
|
||||||
use Laravel\Horizon\Horizon;
|
use Laravel\Horizon\Horizon;
|
||||||
use Laravel\Horizon\HorizonApplicationServiceProvider;
|
use Laravel\Horizon\HorizonApplicationServiceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class HorizonServiceProvider extends HorizonApplicationServiceProvider
|
class HorizonServiceProvider extends HorizonApplicationServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,7 +8,7 @@ use App\Models\Article;
|
||||||
|
|
||||||
class ArticleService extends Service
|
class ArticleService extends Service
|
||||||
{
|
{
|
||||||
public function create(array $request, ?string $client = null): Article
|
public function create(array $request, string $client = null): Article
|
||||||
{
|
{
|
||||||
return Article::create([
|
return Article::create([
|
||||||
'title' => $this->getDataByKey($request, 'name'),
|
'title' => $this->getDataByKey($request, 'name'),
|
||||||
|
|
|
@ -18,7 +18,7 @@ class BookmarkService extends Service
|
||||||
/**
|
/**
|
||||||
* Create a new Bookmark.
|
* Create a new Bookmark.
|
||||||
*/
|
*/
|
||||||
public function create(array $request, ?string $client = null): Bookmark
|
public function create(array $request, string $client = null): Bookmark
|
||||||
{
|
{
|
||||||
if (Arr::get($request, 'properties.bookmark-of.0')) {
|
if (Arr::get($request, 'properties.bookmark-of.0')) {
|
||||||
//micropub request
|
//micropub request
|
||||||
|
|
|
@ -13,7 +13,7 @@ class LikeService extends Service
|
||||||
/**
|
/**
|
||||||
* Create a new Like.
|
* Create a new Like.
|
||||||
*/
|
*/
|
||||||
public function create(array $request, ?string $client = null): Like
|
public function create(array $request, string $client = null): Like
|
||||||
{
|
{
|
||||||
if (Arr::get($request, 'properties.like-of.0')) {
|
if (Arr::get($request, 'properties.like-of.0')) {
|
||||||
//micropub request
|
//micropub request
|
||||||
|
|
|
@ -15,7 +15,7 @@ class HEntryService
|
||||||
/**
|
/**
|
||||||
* Create the relevant model from some h-entry data.
|
* Create the relevant model from some h-entry data.
|
||||||
*/
|
*/
|
||||||
public function process(array $request, ?string $client = null): ?string
|
public function process(array $request, string $client = null): ?string
|
||||||
{
|
{
|
||||||
if (Arr::get($request, 'properties.like-of') || Arr::get($request, 'like-of')) {
|
if (Arr::get($request, 'properties.like-of') || Arr::get($request, 'like-of')) {
|
||||||
return resolve(LikeService::class)->create($request)->longurl;
|
return resolve(LikeService::class)->create($request)->longurl;
|
||||||
|
|
|
@ -18,7 +18,7 @@ class NoteService extends Service
|
||||||
/**
|
/**
|
||||||
* Create a new note.
|
* Create a new note.
|
||||||
*/
|
*/
|
||||||
public function create(array $request, ?string $client = null): Note
|
public function create(array $request, string $client = null): Note
|
||||||
{
|
{
|
||||||
$note = Note::create(
|
$note = Note::create(
|
||||||
[
|
[
|
||||||
|
|
|
@ -9,7 +9,7 @@ use Illuminate\Support\Arr;
|
||||||
|
|
||||||
abstract class Service
|
abstract class Service
|
||||||
{
|
{
|
||||||
abstract public function create(array $request, ?string $client = null): Model;
|
abstract public function create(array $request, string $client = null): Model;
|
||||||
|
|
||||||
protected function getDataByKey(array $request, string $key): ?string
|
protected function getDataByKey(array $request, string $key): ?string
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
"keywords": ["laravel", "framework", "indieweb"],
|
"keywords": ["laravel", "framework", "indieweb"],
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.1",
|
"php": "^8.2",
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-intl": "*",
|
"ext-intl": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
|
"ext-pgsql": "*",
|
||||||
"cviebrock/eloquent-sluggable": "^10.0",
|
"cviebrock/eloquent-sluggable": "^10.0",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
"guzzlehttp/guzzle": "^7.2",
|
||||||
"indieauth/client": "^1.1",
|
"indieauth/client": "^1.1",
|
||||||
|
@ -27,7 +28,8 @@
|
||||||
"mf2/mf2": "~0.3",
|
"mf2/mf2": "~0.3",
|
||||||
"spatie/commonmark-highlighter": "^3.0",
|
"spatie/commonmark-highlighter": "^3.0",
|
||||||
"spatie/laravel-ignition": "^2.1",
|
"spatie/laravel-ignition": "^2.1",
|
||||||
"symfony/html-sanitizer": "^6.1"
|
"symfony/html-sanitizer": "^6.1",
|
||||||
|
"web-auth/webauthn-lib": "^4.7"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-debugbar": "^3.0",
|
"barryvdh/laravel-debugbar": "^3.0",
|
||||||
|
@ -39,8 +41,10 @@
|
||||||
"laravel/sail": "^1.18",
|
"laravel/sail": "^1.18",
|
||||||
"mockery/mockery": "^1.4.4",
|
"mockery/mockery": "^1.4.4",
|
||||||
"nunomaduro/collision": "^7.0",
|
"nunomaduro/collision": "^7.0",
|
||||||
|
"openai-php/client": "^0.7.1",
|
||||||
"phpunit/php-code-coverage": "^10.0",
|
"phpunit/php-code-coverage": "^10.0",
|
||||||
"phpunit/phpunit": "^10.1",
|
"phpunit/phpunit": "^10.1",
|
||||||
|
"psalm/plugin-laravel": "^2.8",
|
||||||
"spatie/laravel-ray": "^1.12",
|
"spatie/laravel-ray": "^1.12",
|
||||||
"vimeo/psalm": "^5.0"
|
"vimeo/psalm": "^5.0"
|
||||||
},
|
},
|
||||||
|
|
2889
composer.lock
generated
2889
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Article>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Article>
|
||||||
*/
|
*/
|
||||||
class ArticleFactory extends Factory
|
class ArticleFactory extends Factory
|
||||||
|
|
|
@ -5,6 +5,8 @@ namespace Database\Factories;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Bio>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Bio>
|
||||||
*/
|
*/
|
||||||
class BioFactory extends Factory
|
class BioFactory extends Factory
|
||||||
|
|
|
@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Bookmark>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Bookmark>
|
||||||
*/
|
*/
|
||||||
class BookmarkFactory extends Factory
|
class BookmarkFactory extends Factory
|
||||||
|
|
|
@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Contact>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Contact>
|
||||||
*/
|
*/
|
||||||
class ContactFactory extends Factory
|
class ContactFactory extends Factory
|
||||||
|
|
|
@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Like>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Like>
|
||||||
*/
|
*/
|
||||||
class LikeFactory extends Factory
|
class LikeFactory extends Factory
|
||||||
|
|
|
@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Media>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Media>
|
||||||
*/
|
*/
|
||||||
class MediaFactory extends Factory
|
class MediaFactory extends Factory
|
||||||
|
|
|
@ -6,6 +6,8 @@ use App\Models\MicropubClient;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\MicropubClient>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\MicropubClient>
|
||||||
*/
|
*/
|
||||||
class MicropubClientFactory extends Factory
|
class MicropubClientFactory extends Factory
|
||||||
|
|
|
@ -8,6 +8,8 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Note>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Note>
|
||||||
*/
|
*/
|
||||||
class NoteFactory extends Factory
|
class NoteFactory extends Factory
|
||||||
|
|
34
database/factories/PasskeyFactory.php
Normal file
34
database/factories/PasskeyFactory.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Passkey;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Passkey>
|
||||||
|
*/
|
||||||
|
class PasskeyFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name of the factory's corresponding model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $model = Passkey::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'user_id' => $this->faker->numberBetween(1, 1000),
|
||||||
|
'passkey_id' => $this->faker->uuid,
|
||||||
|
'passkey' => $this->faker->sha256,
|
||||||
|
'transports' => ['internal'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ use App\Models\Place;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Place>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Place>
|
||||||
*/
|
*/
|
||||||
class PlaceFactory extends Factory
|
class PlaceFactory extends Factory
|
||||||
|
|
|
@ -5,6 +5,8 @@ namespace Database\Factories;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\SyndicationTarget>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\SyndicationTarget>
|
||||||
*/
|
*/
|
||||||
class SyndicationTargetFactory extends Factory
|
class SyndicationTargetFactory extends Factory
|
||||||
|
|
|
@ -6,6 +6,8 @@ use App\Models\Tag;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Tag>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Tag>
|
||||||
*/
|
*/
|
||||||
class TagFactory extends Factory
|
class TagFactory extends Factory
|
||||||
|
|
|
@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||||
*/
|
*/
|
||||||
class UserFactory extends Factory
|
class UserFactory extends Factory
|
||||||
|
|
|
@ -6,6 +6,8 @@ use App\Models\WebMention;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\WebMention>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\WebMention>
|
||||||
*/
|
*/
|
||||||
class WebMentionFactory extends Factory
|
class WebMentionFactory extends Factory
|
||||||
|
|
32
database/migrations/2023_08_27_113904_create_passkeys.php
Normal file
32
database/migrations/2023_08_27_113904_create_passkeys.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('passkeys', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('user_id');
|
||||||
|
$table->string('passkey_id')->unique();
|
||||||
|
$table->text('passkey');
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->foreign('user_id')->references('id')->on('users');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('passkeys');
|
||||||
|
}
|
||||||
|
};
|
|
@ -11,6 +11,8 @@ class ArticlesTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the articles table.
|
* Seed the articles table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,9 @@ namespace Database\Seeders;
|
||||||
use App\Models\Bio;
|
use App\Models\Bio;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class BioSeeder extends Seeder
|
class BioSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,6 +10,8 @@ class BookmarksTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the bookmarks table.
|
* Seed the bookmarks table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,8 @@ class ClientsTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the clients table.
|
* Seed the clients table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,8 @@ class ContactsTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the contacts table.
|
* Seed the contacts table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,6 +4,9 @@ namespace Database\Seeders;
|
||||||
|
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress UnusedClass
|
||||||
|
*/
|
||||||
class DatabaseSeeder extends Seeder
|
class DatabaseSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,6 +12,8 @@ class LikesTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the likes table.
|
* Seed the likes table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,8 @@ class NotesTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the notes table.
|
* Seed the notes table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,8 @@ class PlacesTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the places table.
|
* Seed the places table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,8 @@ class UsersTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the users table.
|
* Seed the users table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,8 @@ class WebMentionsTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Seed the webmentions table.
|
* Seed the webmentions table.
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
|
2872
package-lock.json
generated
2872
package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
package.json
22
package.json
|
@ -5,29 +5,29 @@
|
||||||
"repository": "https://github.com/jonnybarnes/jonnybarnes.uk",
|
"repository": "https://github.com/jonnybarnes/jonnybarnes.uk",
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.22.5",
|
"@babel/core": "^7.23.2",
|
||||||
"@babel/preset-env": "^7.22.5",
|
"@babel/preset-env": "^7.23.2",
|
||||||
"@csstools/postcss-oklab-function": "^2.2.3",
|
"@csstools/postcss-oklab-function": "^3.0.7",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.16",
|
||||||
"babel-loader": "^9.1.2",
|
"babel-loader": "^9.1.3",
|
||||||
"browserlist": "^1.0.1",
|
"browserlist": "^1.0.1",
|
||||||
"compression-webpack-plugin": "^10.0.0",
|
"compression-webpack-plugin": "^10.0.0",
|
||||||
"css-loader": "^6.8.1",
|
"css-loader": "^6.8.1",
|
||||||
"cssnano": "^6.0.1",
|
"cssnano": "^6.0.1",
|
||||||
"eslint": "^8.42.0",
|
"eslint": "^8.52.0",
|
||||||
"eslint-webpack-plugin": "^4.0.1",
|
"eslint-webpack-plugin": "^4.0.1",
|
||||||
"mini-css-extract-plugin": "^2.7.6",
|
"mini-css-extract-plugin": "^2.7.6",
|
||||||
"postcss": "^8.4.24",
|
"postcss": "^8.4.31",
|
||||||
"postcss-combine-duplicated-selectors": "^10.0.2",
|
"postcss-combine-duplicated-selectors": "^10.0.2",
|
||||||
"postcss-combine-media-query": "^1.0.1",
|
"postcss-combine-media-query": "^1.0.1",
|
||||||
"postcss-import": "^15.1.0",
|
"postcss-import": "^15.1.0",
|
||||||
"postcss-loader": "^7.3.3",
|
"postcss-loader": "^7.3.3",
|
||||||
"postcss-nesting": "^11.3.0",
|
"postcss-nesting": "^12.0.1",
|
||||||
"style-loader": "^3.3.3",
|
"style-loader": "^3.3.3",
|
||||||
"stylelint": "^15.7.0",
|
"stylelint": "^15.11.0",
|
||||||
"stylelint-config-standard": "^33.0.0",
|
"stylelint-config-standard": "^34.0.0",
|
||||||
"stylelint-webpack-plugin": "^4.1.1",
|
"stylelint-webpack-plugin": "^4.1.1",
|
||||||
"webpack": "^5.87.0",
|
"webpack": "^5.89.0",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
15
psalm.xml
15
psalm.xml
|
@ -1,23 +1,18 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<psalm
|
<psalm
|
||||||
errorLevel="7"
|
errorLevel="7"
|
||||||
resolveFromConfigFile="true"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns="https://getpsalm.org/schema/config"
|
xmlns="https://getpsalm.org/schema/config"
|
||||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||||
|
findUnusedBaselineEntry="true"
|
||||||
|
findUnusedCode="true"
|
||||||
>
|
>
|
||||||
<projectFiles>
|
<projectFiles>
|
||||||
<directory name="app"/>
|
<directory name="app"/>
|
||||||
|
<directory name="database/factories"/>
|
||||||
|
<directory name="database/seeders"/>
|
||||||
<ignoreFiles>
|
<ignoreFiles>
|
||||||
<directory name="vendor"/>
|
<directory name="vendor"/>
|
||||||
</ignoreFiles>
|
</ignoreFiles>
|
||||||
</projectFiles>
|
</projectFiles>
|
||||||
|
<plugins><pluginClass class="Psalm\LaravelPlugin\Plugin"/></plugins></psalm>
|
||||||
<issueHandlers>
|
|
||||||
<InvalidStaticInvocation>
|
|
||||||
<errorLevel type="suppress">
|
|
||||||
<file name="app/Providers/RouteServiceProvider.php" />
|
|
||||||
</errorLevel>
|
|
||||||
</InvalidStaticInvocation>
|
|
||||||
</issueHandlers>
|
|
||||||
</psalm>
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
:root{--font-family-headings:"Archer SSm A","Archer SSm B",serif;--font-family-body:"Verlag A","Verlag B",sans-serif;--font-family-monospace:"Operator Mono SSm A","Operator Mono SSm B",monospace;--font-size-sm:0.75rem;--font-size-base:1rem;--font-size-md:1.25rem;--font-size-lg:1.5rem;--font-size-xl:1.75rem;--font-size-xxl:2rem;--font-size-xxxl:2.25rem;--color-primary:#334700;--color-secondary:#e3ffb7;--color-link:#00649e;--color-link-visited:#bc7aff;--color-primary-shadow:rgba(16,25,0,.4)}@supports (color:color(display-p3 0 0 0)){:root{--color-primary:color(display-p3 0.21567 0.27838 0.03615);--color-secondary:color(display-p3 0.91016 0.99842 0.74082);--color-link:color(display-p3 0.01045 0.38351 0.63618);--color-link-visited:color(display-p3 0.70467 0.47549 0.99958);--color-primary-shadow:color(display-p3 0.06762 0.09646 0.00441/0.4)}}@supports (color:oklch(0% 0 0)){:root{--color-primary:oklch(36.8% 0.1 125.505);--color-secondary:oklch(96.3% 0.1 125.505);--color-link:oklch(48.09% 0.146 241.41);--color-link-visited:oklch(70.44% 0.21 304.41);--color-primary-shadow:oklch(19.56% 0.054 125.505/40%)}}body{background-color:var(--color-secondary);color:var(--color-primary);font-family:var(--font-family-body);font-size:var(--font-size-md)}code{font-family:var(--font-family-monospace)}h1,h2,h3,h4,h5,h6{font-family:var(--font-family-headings)}.grid{display:grid;grid-template-columns:5vw 1fr 5vw;grid-template-rows:-webkit-min-content 1fr -webkit-min-content;grid-template-rows:min-content 1fr min-content;row-gap:1rem}#site-header{grid-column:2/3;grid-row:1/2}main{grid-row:2/3}footer,main{grid-column:2/3}footer{grid-row:3/4}footer .iwc-logo{max-width:85vw}a{color:var(--color-link)}a:visited{color:var(--color-link-visited)}#site-header a:visited{color:var(--color-link)}.hljs{border-radius:.5rem}.p-bridgy-twitter-content{display:none}.h-card .hovercard{-webkit-box-orient:vertical;-webkit-box-direction:normal;background-color:var(--color-secondary);border-radius:1rem;-webkit-box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);display:none;-ms-flex-direction:column;flex-direction:column;gap:.5rem;opacity:0;padding:1rem;position:absolute;-webkit-transition:opacity .5s ease-in-out;transition:opacity .5s ease-in-out;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;z-index:100}.h-card .hovercard .u-photo{max-width:6rem}.h-card .hovercard .social-icon{height:1rem;width:1rem}.h-card:hover .hovercard{display:-webkit-box;display:-ms-flexbox;display:flex;opacity:1}.h-entry{-webkit-border-start:1px solid var(--color-primary);-webkit-padding-start:.5rem;border-inline-start:1px solid var(--color-primary);padding-inline-start:.5rem}.h-entry .reply-to{font-style:italic}.h-entry .post-info a{text-decoration:none}.h-entry .note-metadata{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;gap:1rem}.h-entry .note-metadata .syndication-links{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.h-entry .note-metadata .syndication-links a{text-decoration:none}.h-entry .note-metadata .syndication-links a svg{height:1rem;width:1rem}
|
:root{--font-family-headings:"Archer SSm A","Archer SSm B",serif;--font-family-body:"Verlag A","Verlag B",sans-serif;--font-family-monospace:"Operator Mono SSm A","Operator Mono SSm B",monospace;--font-size-sm:0.75rem;--font-size-base:1rem;--font-size-md:1.25rem;--font-size-lg:1.5rem;--font-size-xl:1.75rem;--font-size-xxl:2rem;--font-size-xxxl:2.25rem;--color-primary:#334700;--color-secondary:#e3ffb7;--color-link:#00649e;--color-link-visited:#bc7aff;--color-primary-shadow:rgba(16,25,0,.4)}@supports (color:color(display-p3 0 0 0)){:root{--color-primary:color(display-p3 0.21567 0.27838 0.03615);--color-secondary:color(display-p3 0.91016 0.99842 0.74082);--color-link:color(display-p3 0.01045 0.38351 0.63618);--color-link-visited:color(display-p3 0.70467 0.47549 0.99958);--color-primary-shadow:color(display-p3 0.06762 0.09646 0.00441/0.4)}}@supports (color:oklch(0% 0 0)){:root{--color-primary:oklch(36.8% 0.1 125.505deg);--color-secondary:oklch(96.3% 0.1 125.505deg);--color-link:oklch(48.09% 0.146 241.41deg);--color-link-visited:oklch(70.44% 0.21 304.41deg);--color-primary-shadow:oklch(19.56% 0.054 125.505deg/40%)}}body{background-color:var(--color-secondary);color:var(--color-primary);font-family:var(--font-family-body);font-size:var(--font-size-md)}code{font-family:var(--font-family-monospace)}h1,h2,h3,h4,h5,h6{font-family:var(--font-family-headings)}.grid{display:grid;grid-template-columns:5vw 1fr 5vw;grid-template-rows:-webkit-min-content 1fr -webkit-min-content;grid-template-rows:min-content 1fr min-content;row-gap:1rem}#site-header{grid-column:2/3;grid-row:1/2}main{grid-row:2/3}footer,main{grid-column:2/3}footer{grid-row:3/4}footer .iwc-logo{max-width:85vw}footer .footer-actions{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;gap:1rem}a{color:var(--color-link)}a:visited{color:var(--color-link-visited)}#site-header a:visited,a.auth:visited{color:var(--color-link)}.hljs{border-radius:.5rem}.h-card .hovercard{-webkit-box-orient:vertical;-webkit-box-direction:normal;background-color:var(--color-secondary);border-radius:1rem;-webkit-box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);display:none;-ms-flex-direction:column;flex-direction:column;gap:.5rem;opacity:0;padding:1rem;position:absolute;-webkit-transition:opacity .5s ease-in-out;transition:opacity .5s ease-in-out;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;z-index:100}.h-card .hovercard .u-photo{max-width:6rem}.h-card .hovercard .social-icon{height:1rem;width:1rem}.h-card:hover .hovercard{display:-webkit-box;display:-ms-flexbox;display:flex;opacity:1}.h-entry{-webkit-border-start:1px solid var(--color-primary);-webkit-padding-start:.5rem;border-inline-start:1px solid var(--color-primary);padding-inline-start:.5rem}.h-entry .reply-to{font-style:italic}.h-entry .post-info a{text-decoration:none}.h-entry .note-metadata{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;gap:1rem}.h-entry .note-metadata .syndication-links{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.h-entry .note-metadata .syndication-links a{text-decoration:none}.h-entry .note-metadata .syndication-links a svg{height:1rem;width:1rem}
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
!function(){"use strict";let e=new class{constructor(){}async register(){const e=await this.getCreateOptions(),t={challenge:this.base64URLStringToBuffer(e.challenge),rp:{id:e.rp.id,name:e.rp.name},user:{id:(new TextEncoder).encode(window.atob(e.user.id)),name:e.user.name,displayName:e.user.displayName},pubKeyCredParams:e.pubKeyCredParams,excludeCredentials:[],authenticatorSelection:e.authenticatorSelection,timeout:6e4},a=await navigator.credentials.create({publicKey:t});if(!a)throw new Error("Error generating a passkey");const n={id:a.id?a.id:null,type:a.type?a.type:null,rawId:a.rawId?this.bufferToBase64URLString(a.rawId):null,response:{attestationObject:a.response.attestationObject?this.bufferToBase64URLString(a.response.attestationObject):null,clientDataJSON:a.response.clientDataJSON?this.bufferToBase64URLString(a.response.clientDataJSON):null}};if(!(await window.fetch("/admin/passkeys/register",{method:"POST",body:JSON.stringify(n),cache:"no-cache",headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Error saving the passkey");window.location.reload()}async getCreateOptions(){const e=await fetch("/admin/passkeys/register",{method:"GET"});return await e.json()}async login(){const e=await this.getLoginData(),t=await navigator.credentials.get({publicKey:{challenge:this.base64URLStringToBuffer(e.challenge),userVerification:e.userVerification,timeout:6e4}});if(!t)throw new Error("Authentication failed");const a={id:t.id?t.id:"",type:t.type?t.type:"",rawId:t.rawId?this.bufferToBase64URLString(t.rawId):"",response:{authenticatorData:t.response.authenticatorData?this.bufferToBase64URLString(t.response.authenticatorData):"",clientDataJSON:t.response.clientDataJSON?this.bufferToBase64URLString(t.response.clientDataJSON):"",signature:t.response.signature?this.bufferToBase64URLString(t.response.signature):"",userHandle:t.response.userHandle?this.bufferToBase64URLString(t.response.userHandle):""}};if(!(await window.fetch("/login/passkey",{method:"POST",body:JSON.stringify(a),headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Login failed");window.location.assign("/admin")}async getLoginData(){const e=await fetch("/login/passkey",{method:"GET"});return await e.json()}base64URLStringToBuffer(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),a=(4-t.length%4)%4,n=t.padEnd(t.length+a,"="),r=window.atob(n),i=new ArrayBuffer(r.length),s=new Uint8Array(i);for(let e=0;e<r.length;e++)s[e]=r.charCodeAt(e);return i}bufferToBase64URLString(e){const t=new Uint8Array(e);let a="";for(const e of t)a+=String.fromCharCode(e);return btoa(a).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}};document.querySelectorAll(".add-passkey").forEach((t=>{t.addEventListener("click",(()=>{e.register()}))})),document.querySelectorAll(".login-passkey").forEach((t=>{t.addEventListener("click",(()=>{e.login()}))}))}();
|
Binary file not shown.
12
public/vendor/horizon/app-dark.css
vendored
12
public/vendor/horizon/app-dark.css
vendored
File diff suppressed because one or more lines are too long
12
public/vendor/horizon/app.css
vendored
12
public/vendor/horizon/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/horizon/app.js
vendored
2
public/vendor/horizon/app.js
vendored
File diff suppressed because one or more lines are too long
9
public/vendor/horizon/mix-manifest.json
vendored
9
public/vendor/horizon/mix-manifest.json
vendored
|
@ -1,5 +1,8 @@
|
||||||
{
|
{
|
||||||
"/app.js": "/app.js?id=4f93645700a6c5485654",
|
"/app.js": "/app.js?id=ff1533ec4a7afad65c5bd7bcc2cc7d7b",
|
||||||
"/app.css": "/app.css?id=99048465add47d086ac7",
|
"/app-dark.css": "/app-dark.css?id=15c72df05e2b1147fa3e4b0670cfb435",
|
||||||
"/app-dark.css": "/app-dark.css?id=f42b555818ed19478827"
|
"/app.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
|
||||||
|
"/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
|
||||||
|
"/img/horizon.svg": "/img/horizon.svg?id=904d5b5185fefb09035384e15bfca765",
|
||||||
|
"/img/sprite.svg": "/img/sprite.svg?id=afc4952b74895bdef3ab4ebe9adb746f"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
@import "variables.css";
|
@import url('variables.css');
|
||||||
@import "fonts.css";
|
@import url('fonts.css');
|
||||||
@import "layout.css";
|
@import url('layout.css');
|
||||||
@import "colours.css";
|
@import url('colours.css');
|
||||||
@import "code.css";
|
@import url('code.css');
|
||||||
@import "content.css";
|
@import url('content.css');
|
||||||
|
|
|
@ -9,6 +9,10 @@ a {
|
||||||
&:visited {
|
&:visited {
|
||||||
color: var(--color-link-visited);
|
color: var(--color-link-visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.auth:visited {
|
||||||
|
color: var(--color-link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#site-header {
|
#site-header {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import "posse.css";
|
@import url('h-card.css');
|
||||||
@import "h-card.css";
|
|
||||||
|
|
||||||
.h-entry {
|
.h-entry {
|
||||||
border-inline-start: 1px solid var(--color-primary);
|
border-inline-start: 1px solid var(--color-primary);
|
||||||
|
|
|
@ -22,4 +22,10 @@ footer {
|
||||||
& .iwc-logo {
|
& .iwc-logo {
|
||||||
max-width: 85vw;
|
max-width: 85vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .footer-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
.p-bridgy-twitter-content {
|
|
||||||
display: none;
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue