mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
Bump version
This commit is contained in:
parent
ea18806f09
commit
950772a701
22 changed files with 128 additions and 103 deletions
|
@ -1,6 +1,7 @@
|
|||
APP_ENV=local
|
||||
APP_DEBUG=true
|
||||
APP_KEY=SomeRandomString
|
||||
JWT_SECRET=SomeRandom32CharString
|
||||
|
||||
# Username and password for the initial admin account
|
||||
# This info will be populated into the database during `php artisan db:seed`
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
|
||||
# https://github.com/tymondesigns/jwt-auth/wiki/Authentication
|
||||
RewriteCond %{HTTP:Authorization} ^(.*)
|
||||
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
|
|
|
@ -18,7 +18,7 @@ class Application extends IlluminateApplication
|
|||
*
|
||||
* @link https://github.com/phanan/koel/releases
|
||||
*/
|
||||
const VERSION = 'v1.1.0';
|
||||
const VERSION = 'v1.1.1';
|
||||
|
||||
/**
|
||||
* We have merged public path and base path.
|
||||
|
|
|
@ -20,7 +20,7 @@ class PlaylistController extends Controller
|
|||
$playlist = auth()->user()->playlists()->create($request->only('name'));
|
||||
$playlist->songs()->sync($request->input('songs'));
|
||||
|
||||
$playlist->songs = $playlist->songs->fetch('id');
|
||||
$playlist->songs = $playlist->songs->pluck('id');
|
||||
|
||||
return response()->json($playlist);
|
||||
}
|
||||
|
|
|
@ -2,32 +2,10 @@
|
|||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Streamers\PHPStreamer;
|
||||
use App\Http\Streamers\XAccelRedirectStreamer;
|
||||
use App\Http\Streamers\XSendFileStreamer;
|
||||
use App\Models\Song;
|
||||
|
||||
class SongController extends Controller
|
||||
{
|
||||
/**
|
||||
* Play a song.
|
||||
*
|
||||
* @link https://github.com/phanan/koel/wiki#streaming-music
|
||||
*
|
||||
* @param Song $song
|
||||
*/
|
||||
public function play(Song $song)
|
||||
{
|
||||
switch (env('STREAMING_METHOD')) {
|
||||
case 'x-sendfile':
|
||||
return (new XSendFileStreamer($song))->stream();
|
||||
case 'x-accel-redirect':
|
||||
return (new XAccelRedirectStreamer($song))->stream();
|
||||
default:
|
||||
return (new PHPStreamer($song))->stream();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra information about a song via Last.fm.
|
||||
*
|
||||
|
|
|
@ -3,13 +3,34 @@
|
|||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Requests\API\ProfileUpdateRequest;
|
||||
use App\Http\Requests\API\UserLoginRequest;
|
||||
use App\Http\Requests\API\UserStoreRequest;
|
||||
use App\Http\Requests\API\UserUpdateRequest;
|
||||
use App\Models\User;
|
||||
use Hash;
|
||||
use JWTAuth;
|
||||
use Tymon\JWTAuth\Exceptions\JWTException;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Log a user in.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function login(UserLoginRequest $request)
|
||||
{
|
||||
try {
|
||||
if (!$token = JWTAuth::attempt($request->only('email', 'password'))) {
|
||||
return response()->json(['error' => 'invalid_credentials'], 401);
|
||||
}
|
||||
} catch (JWTException $e) {
|
||||
return response()->json(['error' => 'could_not_create_token'], 500);
|
||||
}
|
||||
|
||||
return response()->json(compact('token'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user.
|
||||
*
|
||||
|
|
|
@ -3,15 +3,9 @@
|
|||
namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\Authenticate;
|
||||
use App\Http\Middleware\EncryptCookies;
|
||||
use App\Http\Middleware\RedirectIfAuthenticated;
|
||||
use App\Http\Middleware\VerifyCsrfToken;
|
||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
use Tymon\JWTAuth\Middleware\GetUserFromToken;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
|
@ -22,11 +16,6 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $middleware = [
|
||||
CheckForMaintenanceMode::class,
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -36,7 +25,6 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => Authenticate::class,
|
||||
'auth.basic' => AuthenticateWithBasicAuth::class,
|
||||
'guest' => RedirectIfAuthenticated::class,
|
||||
'jwt.auth' => GetUserFromToken::class,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
<?php
|
||||
|
||||
Route::get('login', 'Auth\AuthController@getLogin');
|
||||
Route::post('login', 'Auth\AuthController@postLogin');
|
||||
Route::get('logout', 'Auth\AuthController@getLogout');
|
||||
|
||||
Route::get('/', function () {
|
||||
//return redirect('/♫');
|
||||
return view('index');
|
||||
});
|
||||
|
||||
Route::get('♫', ['middleware' => 'auth', function () {
|
||||
return view('index');
|
||||
}]);
|
||||
Route::get('{song}/play', 'PlaybackController@play');
|
||||
|
||||
Route::group(['prefix' => 'api', 'middleware' => 'auth', 'namespace' => 'API'], function () {
|
||||
|
||||
Route::group(['prefix' => 'api', 'middleware' => 'jwt.auth', 'namespace' => 'API'], function () {
|
||||
Route::get('/', function () {
|
||||
// Just acting as a ping service.
|
||||
});
|
||||
|
@ -21,12 +15,11 @@ Route::group(['prefix' => 'api', 'middleware' => 'auth', 'namespace' => 'API'],
|
|||
Route::get('data', 'DataController@index');
|
||||
|
||||
Route::post('settings', 'SettingController@save');
|
||||
|
||||
Route::get('{song}/play', 'SongController@play');
|
||||
Route::get('{song}/info', 'SongController@getInfo');
|
||||
|
||||
Route::post('{song}/scrobble/{timestamp}', 'SongController@scrobble')->where([
|
||||
'timestamp' => '\d+',
|
||||
]);
|
||||
Route::get('{song}/info', 'SongController@getInfo');
|
||||
|
||||
Route::post('interaction/play', 'InteractionController@play');
|
||||
Route::post('interaction/like', 'InteractionController@like');
|
||||
|
@ -37,6 +30,7 @@ Route::group(['prefix' => 'api', 'middleware' => 'auth', 'namespace' => 'API'],
|
|||
Route::put('playlist/{playlist}/sync', 'PlaylistController@sync')->where(['playlist' => '\d+']);
|
||||
|
||||
Route::resource('user', 'UserController', ['only' => ['store', 'update', 'destroy']]);
|
||||
Route::post('me', 'UserController@login');
|
||||
Route::put('me', 'UserController@updateProfile');
|
||||
|
||||
Route::get('lastfm/connect', 'LastfmController@connect');
|
||||
|
|
|
@ -140,6 +140,7 @@ return [
|
|||
|
||||
PhanAn\CascadingConfig\CascadingConfigServiceProvider::class,
|
||||
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
|
@ -201,6 +202,8 @@ return [
|
|||
'Media' => App\Facades\Media::class,
|
||||
'Util' => App\Facades\Util::class,
|
||||
'Lastfm' => App\Facades\Lastfm::class,
|
||||
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
|
||||
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
|
||||
|
||||
],
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Back up your installation of Koel, including application files and database dump
|
|||
|
||||
``` bash
|
||||
git fetch --all
|
||||
git checkout v1.1.0
|
||||
git checkout v1.1.1
|
||||
```
|
||||
|
||||
#### 2. Upgrade Laravel, install packages, and migrate database
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"sinon": "^1.17.2",
|
||||
"vue": "^1.0.11",
|
||||
"vue-hot-reload-api": "^1.2.2",
|
||||
"vue-resource": "^0.1.17",
|
||||
"vue-resource": "~0.5.1",
|
||||
"vueify": "^7.0.2",
|
||||
"vueify-insert-css": "^1.0.0"
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div id="app" tabindex="0" v-show="currentUser.id"
|
||||
<div id="app" tabindex="0" v-show="authenticated"
|
||||
@keydown.space="togglePlayback"
|
||||
@keydown.j = "playNext"
|
||||
@keydown.k = "playPrev"
|
||||
|
@ -31,7 +31,7 @@
|
|||
import sharedStore from './stores/shared';
|
||||
import preferenceStore from './stores/preference';
|
||||
import playback from './services/playback';
|
||||
import userStore from './stores/user';
|
||||
import ls from './services/ls';
|
||||
|
||||
export default {
|
||||
components: { siteHeader, siteFooter, mainWrapper, overlay, loginForm },
|
||||
|
@ -41,7 +41,7 @@
|
|||
data() {
|
||||
return {
|
||||
prefs: preferenceStore.state,
|
||||
currentUser: userStore.state.current,
|
||||
authenticated: false,
|
||||
|
||||
overlayState: {
|
||||
showing: true,
|
||||
|
@ -53,7 +53,10 @@
|
|||
},
|
||||
|
||||
ready() {
|
||||
if (this.currentUser.id) {
|
||||
// The app has just been initialized, check if we can get the user data with an already existing token
|
||||
var token = ls.get('jwt-token')
|
||||
if (token) {
|
||||
this.authenticated = true;
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
|
@ -74,7 +77,7 @@
|
|||
|
||||
// Let all other compoenents know we're ready.
|
||||
this.$broadcast('koel:ready');
|
||||
});
|
||||
}, () => this.authenticated = false);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -201,6 +204,13 @@
|
|||
this.overlayState.dismissable = true;
|
||||
},
|
||||
},
|
||||
|
||||
events: {
|
||||
'user:loggedin': function () {
|
||||
this.authenticated = true;
|
||||
this.init();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
methods: {
|
||||
login() {
|
||||
userStore.login(this.email, this.password, () => {
|
||||
// Emit the event
|
||||
this.$dispatch('user:loggedin');
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,9 +1,38 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
import ls from './services/ls';
|
||||
|
||||
window.Vue = require('vue');
|
||||
Vue.config.debug = false;
|
||||
Vue.use(require('vue-resource'));
|
||||
Vue.http.options.root = '/api';
|
||||
Vue.http.headers.common['X-CSRF-TOKEN'] = $('meta[name="csrf-token"]').attr('content');
|
||||
Vue.config.debug = false;
|
||||
Vue.http.interceptors.push({
|
||||
request(request) {
|
||||
var token = ls.get('jwt-token');
|
||||
|
||||
if (token) {
|
||||
Vue.http.headers.common.Authorization = token;
|
||||
}
|
||||
|
||||
return request;
|
||||
},
|
||||
|
||||
response(response) {
|
||||
if (response.status && response.status.code == 401) {
|
||||
ls.remove('jwt-token');
|
||||
}
|
||||
|
||||
if (response.headers && response.headers.Authorization) {
|
||||
ls.set('jwt-token', response.headers.Authorization);
|
||||
}
|
||||
|
||||
if (response.data && response.data.token && response.data.token.length > 10) {
|
||||
ls.set('jwt-token', `Bearer ${response.data.token}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
|
||||
// Exit light,
|
||||
// Enter night,
|
||||
|
|
|
@ -9,43 +9,39 @@ import { extend } from 'lodash';
|
|||
* After all, even if there were errors, how bad can it be?
|
||||
*/
|
||||
export default {
|
||||
request(method, url, data, cb = null, options = {}) {
|
||||
options = extend({
|
||||
error: (data, status, request) => {
|
||||
if (status === 401) {
|
||||
document.location.href = "/login";
|
||||
}
|
||||
},
|
||||
}, options);
|
||||
|
||||
request(method, url, data, successCb = null, errorCb = null, options = {}) {
|
||||
switch (method) {
|
||||
case 'get':
|
||||
return Vue.http.get(url, data, cb, options);
|
||||
return Vue.http.get(url, data, options).then(successCb, errorCb);
|
||||
case 'post':
|
||||
return Vue.http.post(url, data, cb, options);
|
||||
return Vue.http.post(url, data, options).then(successCb, errorCb);
|
||||
case 'put':
|
||||
return Vue.http.put(url, data, cb, options);
|
||||
return Vue.http.put(url, data, cb, options).then(successCb, errorCb);
|
||||
case 'delete':
|
||||
return Vue.http.delete(url, data, cb, options);
|
||||
return Vue.http.delete(url, data, cb, options).then(successCb, errorCb);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
get(url, data = {}, cb = null, options = {}) {
|
||||
return this.request('get', url, data, cb, options);
|
||||
get(url, data = {}, successCb = null, errorCb = null, options = {}) {
|
||||
return this.request('get', url, data, successCb, errorCb, options);
|
||||
},
|
||||
|
||||
post(url, data, cb = null, options = {}) {
|
||||
return this.request('post', url, data, cb, options);
|
||||
post(url, data, successCb = null, errorCb = null, options = {}) {
|
||||
return this.request('post', url, data, successCb, errorCb, options);
|
||||
},
|
||||
|
||||
put(url, data, cb = null, options = {}) {
|
||||
return this.request('put', url, data, cb, options);
|
||||
patch(url, data, successCb = null, errorCb = null, options = {}) {
|
||||
return this.request('patch', url, data, successCb, errorCb, options);
|
||||
},
|
||||
|
||||
delete(url, data = {}, cb = null, options = {}) {
|
||||
return this.request('delete', url, data, cb, options);
|
||||
put(url, data, successCb = null, errorCb = null, options = {}) {
|
||||
return this.request('put', url, data, successCb, errorCb, options);
|
||||
},
|
||||
|
||||
delete(url, data = {}, successCb = null, errorCb = null, options = {}) {
|
||||
return this.request('delete', url, data, successCb, errorCb, options);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -90,7 +90,7 @@ export default {
|
|||
this.app.$broadcast('song:play', song);
|
||||
|
||||
$('title').text(`${song.title} ♫ Koel`);
|
||||
this.player.source(`/api/${song.id}/play`);
|
||||
this.player.source(`/${song.id}/play`);
|
||||
this.player.play();
|
||||
|
||||
// Register the play to the server
|
||||
|
|
|
@ -30,7 +30,7 @@ export default {
|
|||
this.remove(song);
|
||||
}
|
||||
|
||||
http.post('interaction/like', { id: song.id }, data => {
|
||||
http.post('interaction/like', { id: song.id }, () => {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ export default {
|
|||
_.each(songs, song => song.liked = true);
|
||||
this.state.songs = _.union(this.state.songs, songs);
|
||||
|
||||
http.post('interaction/batch/like', { ids: _.pluck(songs, 'id') }, data => {
|
||||
http.post('interaction/batch/like', { ids: _.pluck(songs, 'id') }, () => {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ export default {
|
|||
_.each(songs, song => song.liked = false);
|
||||
this.state.songs = _.difference(this.state.songs, songs);
|
||||
|
||||
http.post('interaction/batch/unlike', { ids: _.pluck(songs, 'id') }, data => {
|
||||
http.post('interaction/batch/unlike', { ids: _.pluck(songs, 'id') }, () => {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ export default {
|
|||
},
|
||||
|
||||
store(name, songs, cb = null) {
|
||||
http.post('playlist', { name, songs }, playlist => {
|
||||
http.post('playlist', { name, songs }, response => {
|
||||
var playlist = response.data;
|
||||
playlist.songs = songs;
|
||||
this.getSongs(playlist);
|
||||
this.state.playlists.push(playlist);
|
||||
|
|
|
@ -17,10 +17,6 @@ export default {
|
|||
},
|
||||
|
||||
update(cb = null, error = null) {
|
||||
http.post('settings', this.all(), msg => {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
}, { error });
|
||||
http.post('settings', this.all(), cb, error);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -26,9 +26,9 @@ export default {
|
|||
latestVersion: '',
|
||||
},
|
||||
|
||||
init(cb = null) {
|
||||
http.get('data', data => {
|
||||
assign(this.state, data);
|
||||
init(successCb = null, errorCb = null) {
|
||||
http.get('data', {}, response => {
|
||||
assign(this.state, response.data);
|
||||
|
||||
// If this is a new user, initialize his preferences to be an empty object.
|
||||
if (!this.state.currentUser.preferences) {
|
||||
|
@ -43,10 +43,14 @@ export default {
|
|||
queueStore.init();
|
||||
settingStore.init(this.state.settings);
|
||||
|
||||
window.useLastfm = this.state.useLastfm = data.useLastfm;
|
||||
window.useLastfm = this.state.useLastfm = response.data.useLastfm;
|
||||
|
||||
if (cb) {
|
||||
cb();
|
||||
if (successCb) {
|
||||
successCb();
|
||||
}
|
||||
}, error => {
|
||||
if (errorCb) {
|
||||
errorCb();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -94,7 +94,7 @@ export default {
|
|||
*/
|
||||
registerPlay(song) {
|
||||
// Increase playcount
|
||||
http.post('interaction/play', { id: song.id }, data => song.playCount = data.play_count);
|
||||
http.post('interaction/play', { id: song.id }, response => song.playCount = response.data.play_count);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -79,9 +79,7 @@ export default {
|
|||
* @param {Function} cb
|
||||
*/
|
||||
login(email, password, cb = null) {
|
||||
http.post('me', { email, password }, user => {
|
||||
this.current = user;
|
||||
|
||||
http.post('me', { email, password }, () => {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
|
@ -98,7 +96,7 @@ export default {
|
|||
password,
|
||||
name: this.current().name,
|
||||
email: this.current().email
|
||||
}, data => {
|
||||
}, () => {
|
||||
this.setAvatar();
|
||||
|
||||
if (cb) {
|
||||
|
@ -117,7 +115,9 @@ export default {
|
|||
* @param {Function} cb
|
||||
*/
|
||||
store(name, email, password, cb = null) {
|
||||
http.post('user', { name, email, password }, user => {
|
||||
http.post('user', { name, email, password }, response => {
|
||||
var user = response.data;
|
||||
|
||||
this.setAvatar(user);
|
||||
this.state.users.push(user);
|
||||
|
||||
|
|
Loading…
Reference in a new issue