jonnybarnes.uk/app/Http/Controllers/MicropubController.php
Jonny Barnes 43beae2f82 Squashed commit of the following:
commit b87b6b2a96de870f1782b00cfe3f393cc79b7d3b
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Dec 18 14:05:11 2017 +0000

    Even more tests for this micropub refactor

commit 2d967f33c3abeea8fc89f91e1764e970681dc58f
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Sat Dec 16 22:19:53 2017 +0000

    Fill out token endpoint tests

commit 440dcbe3e53f058060c918429bea75911ddafdc1
Merge: 02a25b0 f60164f
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Dec 15 14:32:50 2017 +0000

    Merge pull request #77 from jonnybarnes/analysis-8KABW6

    Apply fixes from StyleCI

commit f60164fe81dbcc1d2343704145d26c6d6412579a
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Dec 15 14:27:40 2017 +0000

    Apply fixes from StyleCI

commit 02a25b083a0305f73d715feb3f9d34f9de8f67d4
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Dec 15 14:17:43 2017 +0000

    phpcs fix

commit 144998de0866bf11f235847d7edc076235294545
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Dec 15 14:16:59 2017 +0000

    Don’t pass the request object to service files, pass request()->all()

commit dd5e52010c51a359665efa349ff8c13d4d6dbf57
Merge: 97b270a 23b145e
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Sun Dec 10 20:01:03 2017 +0000

    Merge pull request #76 from jonnybarnes/analysis-86AVg6

    Apply fixes from StyleCI

commit 23b145e7bf67a358b3cb894ea0793984b65ecab5
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Sun Dec 10 19:50:53 2017 +0000

    Apply fixes from StyleCI

commit 97b270a89abe92e167e0d363029ae0b86608bbc9
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Sun Dec 10 19:43:38 2017 +0000

    Improve test coverage of the refactor

commit 244102264559e4fb0b0614d1738c0283703a71dc
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Dec 8 13:31:13 2017 +0000

    Refactor the note creation code

commit 22b4786cbd7ae508b51a47f0c8cf9a15535edbb1
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Thu Dec 7 17:39:41 2017 +0000

    Remove ununsed importsed classes in the controller
2017-12-18 15:51:02 +00:00

302 lines
9.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers;
use Monolog\Logger;
use Ramsey\Uuid\Uuid;
use App\Jobs\ProcessMedia;
use App\Services\TokenService;
use Illuminate\Http\UploadedFile;
use Monolog\Handler\StreamHandler;
use App\{Like, Media, Note, Place};
use Intervention\Image\ImageManager;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\{Request, Response};
use App\Exceptions\InvalidTokenException;
use Phaza\LaravelPostgis\Geometries\Point;
use Intervention\Image\Exception\NotReadableException;
use App\Services\Micropub\{HCardService, HEntryService, UpdateService};
class MicropubController extends Controller
{
protected $tokenService;
protected $hentryService;
protected $hcardService;
protected $updateService;
public function __construct(
TokenService $tokenService,
HEntryService $hentryService,
HCardService $hcardService,
UpdateService $updateService
) {
$this->tokenService = $tokenService;
$this->hentryService = $hentryService;
$this->hcardService = $hcardService;
$this->updateService = $updateService;
}
/**
* This function receives an API request, verifies the authenticity
* then passes over the info to the relavent Service class.
*
* @return \Illuminate\Http\Response
*/
public function post()
{
try {
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
} catch (InvalidTokenException $e) {
return $this->invalidTokenResponse();
}
if ($tokenData->hasClaim('scope') === false) {
return $this->tokenHasNoScopeResponse();
}
$this->logMicropubRequest(request()->all());
if ((request()->input('h') == 'entry') || (request()->input('type.0') == 'h-entry')) {
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->insufficientScopeResponse();
}
$location = $this->hentryService->process(request()->all(), $this->getCLientId());
return response()->json([
'response' => 'created',
'location' => $location,
], 201)->header('Location', $location);
}
if (request()->input('h') == 'card' || request()->input('type')[0] == 'h-card') {
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->insufficientScopeResponse();
}
$location = $this->hcardService->process(request()->all());
return response()->json([
'response' => 'created',
'location' => $location,
], 201)->header('Location', $location);
}
if (request()->input('action') == 'update') {
if (stristr($tokenData->getClaim('scope'), 'update') === false) {
return $this->insufficientScopeResponse();
}
return $this->updateService->process(request()->all());
}
return response()->json([
'response' => 'error',
'error_description' => 'unsupported_request_type',
], 500);
}
/**
* A GET request has been made to `api/post` with an accompanying
* token, here we check wether the token is valid and respond
* appropriately. Further if the request has the query parameter
* synidicate-to we respond with the known syndication endpoints.
*
* @return \Illuminate\Http\Response
*/
public function get()
{
try {
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
} catch (InvalidTokenException $e) {
return $this->invalidTokenResponse();
}
if (request()->input('q') === 'syndicate-to') {
return response()->json([
'syndicate-to' => config('syndication.targets'),
]);
}
if (request()->input('q') == 'config') {
return response()->json([
'syndicate-to' => config('syndication.targets'),
'media-endpoint' => route('media-endpoint'),
]);
}
if (substr(request()->input('q'), 0, 4) === 'geo:') {
preg_match_all(
'/([0-9\.\-]+)/',
request()->input('q'),
$matches
);
$distance = (count($matches[0]) == 3) ? 100 * $matches[0][2] : 1000;
$places = Place::near(new Point($matches[0][0], $matches[0][1]))->get();
return response()->json([
'response' => 'places',
'places' => $places,
]);
}
// default response is just to return the token data
return response()->json([
'response' => 'token',
'token' => [
'me' => $tokenData->getClaim('me'),
'scope' => $tokenData->getClaim('scope'),
'client_id' => $tokenData->getClaim('client_id'),
],
]);
}
/**
* Process a media item posted to the media endpoint.
*
* @return Illuminate\Http\Response
*/
public function media()
{
try {
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
} catch (InvalidTokenException $e) {
return $this->invalidTokenResponse();
}
if ($tokenData->hasClaim('scope') === false) {
return $this->tokenHasNoScopeResponse();
}
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->insufficientScopeResponse();
}
if ((request()->hasFile('file') && request()->file('file')->isValid()) === false) {
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'The uploaded file failed validation',
], 400);
}
$this->logMicropubRequest(request()->all());
$filename = $this->saveFile(request()->file('file'));
$manager = resolve(ImageManager::class);
try {
$image = $manager->make(request()->file('file'));
$width = $image->width();
} catch (NotReadableException $exception) {
// not an image
$width = null;
}
$media = Media::create([
'token' => request()->bearerToken(),
'path' => 'media/' . $filename,
'type' => $this->getFileTypeFromMimeType(request()->file('file')->getMimeType()),
'image_widths' => $width,
]);
ProcessMedia::dispatch($filename);
return response()->json([
'response' => 'created',
'location' => $media->url,
], 201)->header('Location', $media->url);
}
/**
* Get the file type from the mimetype of the uploaded file.
*
* @param string The mimetype
* @return string The type
*/
private function getFileTypeFromMimeType($mimetype)
{
//try known images
$imageMimeTypes = [
'image/gif',
'image/jpeg',
'image/png',
'image/svg+xml',
'image/tiff',
'image/webp',
];
if (in_array($mimetype, $imageMimeTypes)) {
return 'image';
}
//try known video
$videoMimeTypes = [
'video/mp4',
'video/mpeg',
'video/ogg',
'video/quicktime',
'video/webm',
];
if (in_array($mimetype, $videoMimeTypes)) {
return 'video';
}
//try known audio types
$audioMimeTypes = [
'audio/midi',
'audio/mpeg',
'audio/ogg',
'audio/x-m4a',
];
if (in_array($mimetype, $audioMimeTypes)) {
return 'audio';
}
return 'download';
}
private function getClientId(): string
{
return resolve(TokenService::class)
->validateToken(request()->bearerToken())
->getClaim('client_id');
}
private function logMicropubRequest(array $request)
{
$logger = new Logger('micropub');
$logger->pushHandler(new StreamHandler(storage_path('logs/micropub.log')), Logger::DEBUG);
$logger->debug('MicropubLog', $request);
}
private function saveFile(UploadedFile $file)
{
$filename = Uuid::uuid4() . '.' . $file->extension();
Storage::disk('local')->put($filename, $file);
return $filename;
}
private function insufficientScopeResponse()
{
return response()->json([
'response' => 'error',
'error' => 'insufficient_scope',
'error_description' => 'The tokens scope does not have the necessary requirements.',
], 401);
}
private function invalidTokenResponse()
{
return response()->json([
'response' => 'error',
'error' => 'invalid_token',
'error_description' => 'The provided token did not pass validation',
], 400);
}
private function tokenHasNoScopeResponse()
{
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'The provided token has no scopes',
], 400);
}
}