mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-29 08:07:59 +01:00
#8: Implemented the track upload API.
This commit is contained in:
parent
cf7ec3b6cf
commit
4d119ff758
22 changed files with 647 additions and 54 deletions
|
@ -70,7 +70,12 @@ class UploadTrackCommand extends CommandBase
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$user = \Auth::user();
|
$user = \Auth::user();
|
||||||
$trackFile = \Input::file('track');
|
$trackFile = \Input::file('track', null);
|
||||||
|
|
||||||
|
if (null === $trackFile) {
|
||||||
|
return CommandResponse::fail(['track' => ['You must upload an audio file!']]);
|
||||||
|
}
|
||||||
|
|
||||||
$audio = \AudioCache::get($trackFile->getPathname());
|
$audio = \AudioCache::get($trackFile->getPathname());
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php namespace Poniverse\Ponyfm;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class TrackFileNotFoundException
|
|
||||||
* @package Poniverse\Ponyfm
|
|
||||||
*
|
|
||||||
* This exception is used to indicate that the requested `TrackFile` object
|
|
||||||
* does not exist. This is useful when dealing with albums or playlists that
|
|
||||||
* contain tracks for which no lossless master is available (and thus, lossless
|
|
||||||
* `TrackFiles` don't exist for).
|
|
||||||
*/
|
|
||||||
class TrackFileNotFoundException extends ModelNotFoundException {}
|
|
32
app/Exceptions/InvalidAccessTokenException.php
Normal file
32
app/Exceptions/InvalidAccessTokenException.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2015 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Exceptions;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InvalidAccessTokenException
|
||||||
|
* @package Poniverse\Ponyfm
|
||||||
|
*
|
||||||
|
* This exception indicates that an access token we attempted to introspect
|
||||||
|
* through the Poniverse API is expired or otherwise unusable.
|
||||||
|
*/
|
||||||
|
class InvalidAccessTokenException extends AccessDeniedHttpException {};
|
34
app/Exceptions/TrackFileNotFoundException.php
Normal file
34
app/Exceptions/TrackFileNotFoundException.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2015 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Exceptions;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TrackFileNotFoundException
|
||||||
|
* @package Poniverse\Ponyfm
|
||||||
|
*
|
||||||
|
* This exception is used to indicate that the requested `TrackFile` object
|
||||||
|
* does not exist. This is useful when dealing with albums or playlists that
|
||||||
|
* contain tracks for which no lossless master is available (and thus, lossless
|
||||||
|
* `TrackFiles` don't exist for).
|
||||||
|
*/
|
||||||
|
class TrackFileNotFoundException extends ModelNotFoundException {}
|
|
@ -20,13 +20,56 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Http\Controllers\Api\V1;
|
namespace Poniverse\Ponyfm\Http\Controllers\Api\V1;
|
||||||
|
|
||||||
|
use Poniverse\Ponyfm\Commands\UploadTrackCommand;
|
||||||
|
use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase;
|
||||||
use Poniverse\Ponyfm\Image;
|
use Poniverse\Ponyfm\Image;
|
||||||
use Poniverse\Ponyfm\Track;
|
use Poniverse\Ponyfm\Track;
|
||||||
use Cover;
|
use Response;
|
||||||
use Illuminate\Support\Facades\Response;
|
|
||||||
|
|
||||||
class TracksController extends \ApiControllerBase
|
class TracksController extends ApiControllerBase
|
||||||
{
|
{
|
||||||
|
public function postUploadTrack() {
|
||||||
|
session_write_close();
|
||||||
|
|
||||||
|
$response = $this->execute(new UploadTrackCommand());
|
||||||
|
$commandData = $response->getData(true);
|
||||||
|
|
||||||
|
if (200 !== $response->getStatusCode()) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'id' => $commandData['id'],
|
||||||
|
'status_url' => action('Api\V1\TracksController@getUploadStatus', ['id' => $commandData['id']]),
|
||||||
|
'message' => "This track has been accepted for processing! Poll the status_url to know when it's ready to publish.",
|
||||||
|
];
|
||||||
|
|
||||||
|
$response->setData($data);
|
||||||
|
$response->setStatusCode(202);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getUploadStatus($trackId) {
|
||||||
|
$track = Track::findOrFail($trackId);
|
||||||
|
$this->authorize('edit', $track);
|
||||||
|
|
||||||
|
if ($track->status === Track::STATUS_PROCESSING) {
|
||||||
|
return Response::json(['message' => 'Processing...'], 202);
|
||||||
|
|
||||||
|
} elseif ($track->status === Track::STATUS_COMPLETE) {
|
||||||
|
return Response::json([
|
||||||
|
'message' => 'Processing complete! The artist must publish the track by visiting its edit_url.',
|
||||||
|
'edit_url' => action('ContentController@getTracks', ['id' => $trackId])
|
||||||
|
], 201);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// something went wrong
|
||||||
|
return Response::json(['error' => 'Processing failed!'], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getTrackRadioDetails($hash)
|
public function getTrackRadioDetails($hash)
|
||||||
{
|
{
|
||||||
$track = Track
|
$track = Track
|
||||||
|
|
|
@ -21,19 +21,19 @@
|
||||||
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Support\Facades\File;
|
use File;
|
||||||
|
use Poniverse\Ponyfm\Exceptions\InvalidEncodeOptionsException;
|
||||||
use Poniverse\Ponyfm\Commands\DeleteTrackCommand;
|
use Poniverse\Ponyfm\Commands\DeleteTrackCommand;
|
||||||
use Poniverse\Ponyfm\Commands\EditTrackCommand;
|
use Poniverse\Ponyfm\Commands\EditTrackCommand;
|
||||||
use Poniverse\Ponyfm\Commands\UploadTrackCommand;
|
use Poniverse\Ponyfm\Commands\UploadTrackCommand;
|
||||||
use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase;
|
use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase;
|
||||||
use Poniverse\Ponyfm\Jobs\EncodeTrackFile;
|
use Poniverse\Ponyfm\Jobs\EncodeTrackFile;
|
||||||
use Poniverse\Ponyfm\ResourceLogItem;
|
use Poniverse\Ponyfm\ResourceLogItem;
|
||||||
use Poniverse\Ponyfm\Track;
|
|
||||||
use Cover;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Input;
|
|
||||||
use Illuminate\Support\Facades\Response;
|
|
||||||
use Poniverse\Ponyfm\TrackFile;
|
use Poniverse\Ponyfm\TrackFile;
|
||||||
|
use Poniverse\Ponyfm\Track;
|
||||||
|
use Auth;
|
||||||
|
use Input;
|
||||||
|
use Response;
|
||||||
|
|
||||||
class TracksController extends ApiControllerBase
|
class TracksController extends ApiControllerBase
|
||||||
{
|
{
|
||||||
|
@ -44,16 +44,15 @@ class TracksController extends ApiControllerBase
|
||||||
try {
|
try {
|
||||||
return $this->execute(new UploadTrackCommand());
|
return $this->execute(new UploadTrackCommand());
|
||||||
|
|
||||||
} catch (\InvalidEncodeOptions $e) {
|
} catch (InvalidEncodeOptionsException $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUploadStatus($trackId)
|
public function getUploadStatus($trackId)
|
||||||
{
|
{
|
||||||
// TODO: authorize this
|
|
||||||
|
|
||||||
$track = Track::findOrFail($trackId);
|
$track = Track::findOrFail($trackId);
|
||||||
|
$this->authorize('edit', $track);
|
||||||
|
|
||||||
if ($track->status === Track::STATUS_PROCESSING){
|
if ($track->status === Track::STATUS_PROCESSING){
|
||||||
return Response::json(['message' => 'Processing...'], 202);
|
return Response::json(['message' => 'Processing...'], 202);
|
||||||
|
@ -65,7 +64,6 @@ class TracksController extends ApiControllerBase
|
||||||
// something went wrong
|
// something went wrong
|
||||||
return Response::json(['error' => 'Processing failed!'], 500);
|
return Response::json(['error' => 'Processing failed!'], 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postDelete($id)
|
public function postDelete($id)
|
||||||
|
|
|
@ -25,6 +25,13 @@ use Response;
|
||||||
|
|
||||||
abstract class ApiControllerBase extends Controller
|
abstract class ApiControllerBase extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* NOTE: This function is used by the v1 API. If the response JSON format
|
||||||
|
* it returns changes, don't break the API!
|
||||||
|
*
|
||||||
|
* @param CommandBase $command
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
protected function execute(CommandBase $command)
|
protected function execute(CommandBase $command)
|
||||||
{
|
{
|
||||||
if (!$command->authorize()) {
|
if (!$command->authorize()) {
|
||||||
|
|
|
@ -96,25 +96,13 @@ class AuthController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check by login name to see if they already have an account
|
// Check by login name to see if they already have an account
|
||||||
$localMember = User::where('username', '=', $poniverseUser['username'])
|
$user = User::findOrCreate($poniverseUser['username'], $poniverseUser['display_name'], $poniverseUser['email']);
|
||||||
->where('is_archived', false)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($localMember) {
|
if ($user->wasRecentlyCreated) {
|
||||||
return $this->loginRedirect($localMember);
|
return $this->loginRedirect($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = new User;
|
// We need to insert a new token row :O
|
||||||
|
|
||||||
$user->username = $poniverseUser['username'];
|
|
||||||
$user->display_name = $poniverseUser['display_name'];
|
|
||||||
$user->email = $poniverseUser['email'];
|
|
||||||
$user->created_at = gmdate("Y-m-d H:i:s", time());
|
|
||||||
$user->uses_gravatar = 1;
|
|
||||||
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
//We need to insert a new token row :O
|
|
||||||
|
|
||||||
$setData['user_id'] = $user->id;
|
$setData['user_id'] = $user->id;
|
||||||
$setData['external_user_id'] = $poniverseUser['id'];
|
$setData['external_user_id'] = $poniverseUser['id'];
|
||||||
|
|
|
@ -47,7 +47,9 @@ class Kernel extends HttpKernel
|
||||||
protected $routeMiddleware = [
|
protected $routeMiddleware = [
|
||||||
'auth' => \Poniverse\Ponyfm\Http\Middleware\Authenticate::class,
|
'auth' => \Poniverse\Ponyfm\Http\Middleware\Authenticate::class,
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
|
'auth.oauth' => \Poniverse\Ponyfm\Http\Middleware\AuthenticateOAuth::class,
|
||||||
'can' => \Poniverse\Ponyfm\Http\Middleware\Authorize::class,
|
'can' => \Poniverse\Ponyfm\Http\Middleware\Authorize::class,
|
||||||
|
'json-exceptions' => \Poniverse\Ponyfm\Http\Middleware\JsonExceptions::class,
|
||||||
'guest' => \Poniverse\Ponyfm\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => \Poniverse\Ponyfm\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
75
app/Http/Middleware/AuthenticateOAuth.php
Normal file
75
app/Http/Middleware/AuthenticateOAuth.php
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2015 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Http\Middleware;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use Closure;
|
||||||
|
use GuzzleHttp;
|
||||||
|
use Poniverse;
|
||||||
|
use Poniverse\Ponyfm\User;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
|
||||||
|
class AuthenticateOAuth
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Poniverse
|
||||||
|
*/
|
||||||
|
private $poniverse;
|
||||||
|
|
||||||
|
public function __construct(Poniverse $poniverse) {
|
||||||
|
$this->poniverse = $poniverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @param string $requiredScope
|
||||||
|
* @return mixed
|
||||||
|
* @throws \OAuth2\Exception
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next, $requiredScope)
|
||||||
|
{
|
||||||
|
// Ensure this is a valid OAuth client.
|
||||||
|
$accessToken = $request->get('access_token');
|
||||||
|
|
||||||
|
// check that access token is valid at Poniverse.net
|
||||||
|
$accessTokenInfo = $this->poniverse->getAccessTokenInfo($accessToken);
|
||||||
|
|
||||||
|
if (!$accessTokenInfo->getIsActive()) {
|
||||||
|
throw new AccessDeniedHttpException('This access token is expired or invalid!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($requiredScope, $accessTokenInfo->getScopes())) {
|
||||||
|
throw new AccessDeniedHttpException("This access token lacks the '${requiredScope}' scope!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log in as the given user, creating the account if necessary.
|
||||||
|
$this->poniverse->setAccessToken($accessToken);
|
||||||
|
$poniverseUser = $this->poniverse->getUser();
|
||||||
|
|
||||||
|
$user = User::findOrCreate($poniverseUser['username'], $poniverseUser['display_name'], $poniverseUser['email']);
|
||||||
|
Auth::login($user);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
55
app/Http/Middleware/JsonExceptions.php
Normal file
55
app/Http/Middleware/JsonExceptions.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2015 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class JsonExceptions
|
||||||
|
* @package Poniverse\Ponyfm\Http\Middleware
|
||||||
|
*
|
||||||
|
* This middleware turns any HTTP exceptions thrown during the request
|
||||||
|
* into a JSON response. To be used when implementing the API!
|
||||||
|
*/
|
||||||
|
class JsonExceptions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$response = $next($request);
|
||||||
|
|
||||||
|
} catch (HttpException $e) {
|
||||||
|
return \Response::json([
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
], $e->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,8 +50,9 @@ class Profiler
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$response = \Response::make([
|
$response = \Response::make([
|
||||||
'message' => $e->getMessage(),
|
'message' => $e->getMessage(),
|
||||||
|
'lineNumber' => $e->getLine(),
|
||||||
'exception' => $e->getTrace()
|
'exception' => $e->getTrace()
|
||||||
], 500);
|
], method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500);
|
||||||
$profiler->log('error', $e->__toString(), []);
|
$profiler->log('error', $e->__toString(), []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,6 @@ class VerifyCsrfToken extends BaseVerifier
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $except = [
|
protected $except = [
|
||||||
//
|
'api/*'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,11 +66,19 @@ Route::get('playlist/{id}-{slug}', 'PlaylistsController@getPlaylist');
|
||||||
Route::get('p{id}', 'PlaylistsController@getShortlink')->where('id', '\d+');
|
Route::get('p{id}', 'PlaylistsController@getShortlink')->where('id', '\d+');
|
||||||
Route::get('p{id}/dl.{extension}', 'PlaylistsController@getDownload' );
|
Route::get('p{id}/dl.{extension}', 'PlaylistsController@getDownload' );
|
||||||
|
|
||||||
Route::group(['prefix' => 'api/v1'], function() {
|
|
||||||
|
|
||||||
|
Route::group(['prefix' => 'api/v1', 'middleware' => 'json-exceptions'], function() {
|
||||||
Route::get('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails');
|
Route::get('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails');
|
||||||
Route::post('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails');
|
Route::post('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails');
|
||||||
|
|
||||||
|
Route::group(['middleware' => 'auth.oauth:ponyfm-upload-track'], function() {
|
||||||
|
Route::post('tracks', 'Api\V1\TracksController@postUploadTrack');
|
||||||
|
Route::get('/tracks/{id}/upload-status', 'Api\V1\TracksController@getUploadStatus');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Route::group(['prefix' => 'api/web'], function() {
|
Route::group(['prefix' => 'api/web'], function() {
|
||||||
Route::get('/taxonomies/all', 'Api\Web\TaxonomiesController@getAll');
|
Route::get('/taxonomies/all', 'Api\Web\TaxonomiesController@getAll');
|
||||||
|
|
||||||
|
|
220
app/Library/Poniverse/AccessTokenInfo.php
Normal file
220
app/Library/Poniverse/AccessTokenInfo.php
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2015 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AccessTokenInfo
|
||||||
|
*
|
||||||
|
* A container for the fields in the draft OAuth Token Introspection proposal.
|
||||||
|
*
|
||||||
|
* @link https://tools.ietf.org/html/draft-richer-oauth-introspection-06
|
||||||
|
* @package Poniverse
|
||||||
|
*/
|
||||||
|
class AccessTokenInfo {
|
||||||
|
protected $token;
|
||||||
|
|
||||||
|
protected $isActive;
|
||||||
|
protected $expiresAt;
|
||||||
|
protected $issuedAt;
|
||||||
|
protected $scopes;
|
||||||
|
protected $clientId;
|
||||||
|
protected $sub;
|
||||||
|
protected $userId;
|
||||||
|
protected $intendedAudience;
|
||||||
|
protected $issuer;
|
||||||
|
protected $tokenType;
|
||||||
|
|
||||||
|
public function __construct($accessToken) {
|
||||||
|
$this->token = $accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getToken() {
|
||||||
|
return $this->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getIsActive() {
|
||||||
|
return $this->isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $isActive
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setIsActive($isActive) {
|
||||||
|
$this->isActive = $isActive;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getExpiresAt() {
|
||||||
|
return $this->expiresAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $expiresAt
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setExpiresAt($expiresAt) {
|
||||||
|
$this->expiresAt = $expiresAt;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getIssuedAt() {
|
||||||
|
return $this->issuedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $issuedAt
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setIssuedAt($issuedAt) {
|
||||||
|
$this->issuedAt = $issuedAt;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getScopes() {
|
||||||
|
return $this->scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|string $scopes
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setScopes($scopes) {
|
||||||
|
if (is_array($scopes)) {
|
||||||
|
$this->scopes = $scopes;
|
||||||
|
} else {
|
||||||
|
$this->scopes = mb_split(' ', $scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getClientId() {
|
||||||
|
return $this->clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $clientId
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setClientId($clientId) {
|
||||||
|
$this->clientId = $clientId;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getSub() {
|
||||||
|
return $this->sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $sub
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setSub($sub) {
|
||||||
|
$this->sub = $sub;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getUserId() {
|
||||||
|
return $this->userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $userId
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setUserId($userId) {
|
||||||
|
$this->userId = $userId;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getIntendedAudience() {
|
||||||
|
return $this->intendedAudience;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $intendedAudience
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setIntendedAudience($intendedAudience) {
|
||||||
|
$this->intendedAudience = $intendedAudience;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getIssuer() {
|
||||||
|
return $this->issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $issuer
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setIssuer($issuer) {
|
||||||
|
$this->issuer = $issuer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getTokenType() {
|
||||||
|
return $this->tokenType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $tokenType
|
||||||
|
* @return AccessTokenInfo
|
||||||
|
*/
|
||||||
|
public function setTokenType($tokenType) {
|
||||||
|
$this->tokenType = $tokenType;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,9 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use OAuth2\Client;
|
||||||
|
use Poniverse\Ponyfm\Exceptions\InvalidAccessTokenException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Poniverse
|
* Class Poniverse
|
||||||
*
|
*
|
||||||
|
@ -111,4 +114,48 @@ class Poniverse {
|
||||||
|
|
||||||
return json_decode($result, true);
|
return json_decode($result, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets information about the given access token.
|
||||||
|
*
|
||||||
|
* @link https://tools.ietf.org/html/draft-richer-oauth-introspection-06
|
||||||
|
*
|
||||||
|
* @param $accessTokenToIntrospect
|
||||||
|
* @return \Poniverse\AccessTokenInfo
|
||||||
|
* @throws \Poniverse\Ponyfm\InvalidAccessTokenException
|
||||||
|
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
|
||||||
|
*/
|
||||||
|
public function getAccessTokenInfo($accessTokenToIntrospect)
|
||||||
|
{
|
||||||
|
$token = $this->client->getAccessToken(
|
||||||
|
Config::get('poniverse.urls.token'),
|
||||||
|
Client::GRANT_TYPE_CLIENT_CREDENTIALS,
|
||||||
|
[]
|
||||||
|
)['result']['access_token'];
|
||||||
|
|
||||||
|
|
||||||
|
$request = \Httpful\Request::post($this->urls['api']. 'meta/introspect?token='.$accessTokenToIntrospect);
|
||||||
|
|
||||||
|
/** @var Httpful\Response $result */
|
||||||
|
$result = $request
|
||||||
|
->addHeader('Accept', 'application/json')
|
||||||
|
->addHeader('Authorization', 'Bearer '.$token)
|
||||||
|
->send();
|
||||||
|
$data = json_decode($result, true);
|
||||||
|
|
||||||
|
if (404 === $result->code) {
|
||||||
|
throw new InvalidAccessTokenException('This access token is expired or invalid!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (200 !== $result->code) {
|
||||||
|
throw new \Symfony\Component\HttpKernel\Exception\HttpException(500, 'An unknown error occurred while contacting the Poniverse API.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenInfo = new \Poniverse\AccessTokenInfo($accessTokenToIntrospect);
|
||||||
|
$tokenInfo
|
||||||
|
->setIsActive($data['active'])
|
||||||
|
->setScopes($data['scope']);
|
||||||
|
|
||||||
|
return $tokenInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,10 +202,11 @@ class Client
|
||||||
/**
|
/**
|
||||||
* getAccessToken
|
* getAccessToken
|
||||||
*
|
*
|
||||||
* @param string $token_endpoint Url of the token endpoint
|
* @param string $token_endpoint Url of the token endpoint
|
||||||
* @param int $grant_type Grant Type ('authorization_code', 'password', 'client_credentials', 'refresh_token', or a custom code (@see GrantType Classes)
|
* @param int $grant_type Grant Type ('authorization_code', 'password', 'client_credentials', 'refresh_token', or a custom code (@see GrantType Classes)
|
||||||
* @param array $parameters Array sent to the server (depend on which grant type you're using)
|
* @param array $parameters Array sent to the server (depend on which grant type you're using)
|
||||||
* @return array Array of parameters required by the grant_type (CF SPEC)
|
* @return array Array of parameters required by the grant_type (CF SPEC)
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function getAccessToken($token_endpoint, $grant_type, array $parameters)
|
public function getAccessToken($token_endpoint, $grant_type, array $parameters)
|
||||||
{
|
{
|
||||||
|
|
35
app/Policies/TrackPolicy.php
Normal file
35
app/Policies/TrackPolicy.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2015 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Policies;
|
||||||
|
|
||||||
|
use Poniverse\Ponyfm\Track;
|
||||||
|
use Poniverse\Ponyfm\User;
|
||||||
|
|
||||||
|
class TrackPolicy
|
||||||
|
{
|
||||||
|
public function edit(User $user, Track $track) {
|
||||||
|
return $user->id === $track->user_id || $user->hasRole('admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(User $user, Track $track) {
|
||||||
|
return $user->id === $track->user_id || $user->hasRole('admin');
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,8 +20,10 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Providers;
|
namespace Poniverse\Ponyfm\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use PfmValidator;
|
use PfmValidator;
|
||||||
|
use Poniverse;
|
||||||
use Validator;
|
use Validator;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
@ -46,6 +48,8 @@ class AppServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
//
|
$this->app->bind(Poniverse::class, function(Application $app) {
|
||||||
|
return new Poniverse($app['config']->get('poniverse.client_id'), $app['config']->get('poniverse.secret'));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ use Illuminate\Contracts\Auth\Access\Gate as GateContract;
|
||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
use Poniverse\Ponyfm\Genre;
|
use Poniverse\Ponyfm\Genre;
|
||||||
use Poniverse\Ponyfm\Policies\GenrePolicy;
|
use Poniverse\Ponyfm\Policies\GenrePolicy;
|
||||||
|
use Poniverse\Ponyfm\Policies\TrackPolicy;
|
||||||
|
use Poniverse\Ponyfm\Track;
|
||||||
use Poniverse\Ponyfm\User;
|
use Poniverse\Ponyfm\User;
|
||||||
|
|
||||||
class AuthServiceProvider extends ServiceProvider
|
class AuthServiceProvider extends ServiceProvider
|
||||||
|
@ -34,7 +36,8 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $policies = [
|
protected $policies = [
|
||||||
Genre::class => GenrePolicy::class
|
Genre::class => GenrePolicy::class,
|
||||||
|
Track::class => TrackPolicy::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,7 +39,20 @@ class Track extends Model
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
protected $dates = ['deleted_at'];
|
protected $dates = ['deleted_at', 'published_at', 'released_at'];
|
||||||
|
protected $casts = [
|
||||||
|
'id' => 'integer',
|
||||||
|
'user_id' => 'integer',
|
||||||
|
'license_id' => 'integer',
|
||||||
|
'genre_id' => 'integer',
|
||||||
|
'track_type_id' => 'integer',
|
||||||
|
'is_vocal' => 'boolean',
|
||||||
|
'is_explicit' => 'boolean',
|
||||||
|
'cover_id' => 'integer',
|
||||||
|
'is_downloadable' => 'boolean',
|
||||||
|
'is_latest' => 'boolean',
|
||||||
|
'is_listed' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
use SlugTrait {
|
use SlugTrait {
|
||||||
SlugTrait::setTitleAttribute as setTitleAttributeSlug;
|
SlugTrait::setTitleAttribute as setTitleAttributeSlug;
|
||||||
|
|
38
app/User.php
38
app/User.php
|
@ -38,7 +38,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||||
use Authenticatable, CanResetPassword, Authorizable, RevisionableTrait;
|
use Authenticatable, CanResetPassword, Authorizable, RevisionableTrait;
|
||||||
|
|
||||||
protected $table = 'users';
|
protected $table = 'users';
|
||||||
protected $hidden1 = ['password_hash', 'password_salt', 'bio'];
|
protected $casts = [
|
||||||
|
'id' => 'integer',
|
||||||
|
'sync_names' => 'boolean',
|
||||||
|
'uses_gravatar' => 'boolean',
|
||||||
|
'can_see_explicit_content' => 'boolean',
|
||||||
|
'track_count' => 'integer',
|
||||||
|
'comment_count' => 'integer',
|
||||||
|
'avatar_id' => 'integer',
|
||||||
|
'is_archived' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
public function scopeUserDetails($query)
|
public function scopeUserDetails($query)
|
||||||
{
|
{
|
||||||
|
@ -53,6 +62,33 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||||
return !$query;
|
return !$query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $username
|
||||||
|
* @param string $displayName
|
||||||
|
* @param string $email
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public static function findOrCreate(string $username, string $displayName, string $email) {
|
||||||
|
$user = static::where('username', $username)
|
||||||
|
->where('is_archived', false)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (null !== $user) {
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$user = new User;
|
||||||
|
|
||||||
|
$user->username = $username;
|
||||||
|
$user->display_name = $displayName;
|
||||||
|
$user->email = $email;
|
||||||
|
$user->uses_gravatar = true;
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function avatar()
|
public function avatar()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('Poniverse\Ponyfm\Image');
|
return $this->belongsTo('Poniverse\Ponyfm\Image');
|
||||||
|
|
Loading…
Reference in a new issue