Bump version

This commit is contained in:
An Phan 2015-12-29 08:35:22 +07:00
parent ea18806f09
commit 950772a701
22 changed files with 128 additions and 103 deletions

View file

@ -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`

View file

@ -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>

View file

@ -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.

View file

@ -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);
}

View file

@ -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.
*

View file

@ -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.
*

View file

@ -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,
];
}

View file

@ -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');

View file

@ -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,
],

View file

@ -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

View file

@ -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"
},

View file

@ -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();
},
},
};
/**

View file

@ -20,7 +20,7 @@
methods: {
login() {
userStore.login(this.email, this.password, () => {
// Emit the event
this.$dispatch('user:loggedin');
});
},
},

View file

@ -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,

View file

@ -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);
},
/**

View file

@ -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

View file

@ -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();
}

View file

@ -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);

View file

@ -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);
},
};

View file

@ -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();
}
});
},

View file

@ -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);
},
/**

View file

@ -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);