Laravel Sheduler doesnt reach Storage without Exeption and Error - php

I save live video to S3. Sometimes it takes some minutes. I want to check upload is ended with a scheduler job. The job stops and doesn't get any error or exception. When I call a function from the controller it works well.
Kernel.php
\Log::channel('fejeri')->info('sheduler Start');
$schedule->call('App\Helpers\UploadedFinished#start')->everyMinute()->when(function () {
return File::where('is_upload_ended','=', 0)->whereNotNull('storage')->count() > 0?true:false;
});
This time I try by a Helper function, but I tried by job and command.
The problem was same.
UploadedFinished.php
function start()
{
// upload limit 4 hours
// status 0 - not uploaded
// status 1 - uploaded
// status 3 - error
$files= File::where('is_upload_ended','=', 0)
->whereNotNull('storage')->where('created_at', '>',
Carbon::now()->subHours(4)->toDateTimeString())->get();
$i=0;
foreach($files as $file){
\Log::channel('fejeri')->info($file->filename);
try {
\Log::channel('fejeri')->info('log nr.1');
if(Storage::disk($file->storage)->exists($file->filename)){
\Log::channel('fejeri')->info('log nr.2');
$file->size = Storage::disk($file->storage)->size($file->filename);
$file->is_upload_ended=1;
$file->save();
}else{
\Log::channel('fejeri')->info('File not ready!');
}
\Log::channel('fejeri')->info('log nr.3');
}
catch(\Exeption $e) {
\Log::channel('fejeri')->info('Error type1:'.$e);
}catch(\Throwable $e) {
\Log::channel('fejeri')->info('Error type2:'.$e);
}catch(\Error $e) {
\Log::channel('fejeri')->info('Error type3:'.$e);
}
}
$files2= File::where('is_upload_ended','=', 0)
->whereNotNull('storage')->where('created_at', '<',
Carbon::now()->subHours(4)->toDateTimeString())->get();
foreach($files2 as $filez){
$filez->is_upload_ended=3;
$filez->save();
}
}
the log
[2022-12-15 16:07:01] local.INFO: sheduler
[2022-12-15 16:07:01] local.INFO: sport/sport798/vod/1670594132-196.mp4
[2022-12-15 16:07:01] local.INFO: log nr.1
[2022-12-15 16:08:01] local.INFO: sheduler
.....
After the foreach is stop and run again.
When call function from a controller or call command on terminal the log is
[2022-12-15 16:30:10] local.INFO: sport/sport798/vod/1670594132-196.mp4
[2022-12-15 16:30:10] local.INFO: log nr.1
[2022-12-15 16:30:10] local.INFO: log nr.2
[2022-12-15 16:30:10] local.INFO: log nr.3

Related

Do I need to clear local storage on the frontend if my laravel controller logout() is handling the logging out of a user?

I have a function in my controller that's supposed to log a user out. It returns a success message upon clicking Logout on the frontend. My question is, on the JS side, do I need to clear local storage?
I'm asking this because upon refreshing my page after a successful logout and landing on the login screen (when logging out, the user gets redirected from the dashboard to the login screen) - I get sent back to dashboard which to me indicates that my logout function isn't working as intended (or is it?).
Am I doing something wrong?
Here's my controller code:
public function logout(Request $request) {
try {
$this->_usersRepository->userLogout();
$loggedOut = $this->_usersRepository->userLogout()->getStatusCode();
if($loggedOut != 200) {
return response()->json(['message' => 'Error while logging out!'], 500);
}
return response()->json([
'message' => 'Successfully logged out',
'loggedOut' => $loggedOut
]);
} catch (\Exception $e) {
Log::error($e->getMessage());
throw new \Exception($e->getMessage(), $e->getCode(), $e);
}
}
Respository code for deleting oauth_access_tokens in the DB:
public function userLogout() {
DB::table('oauth_access_tokens')->where('user_id', Auth::id())->delete();
return response()->json(['message' => 'User successfully signed out'], 200);
}

Pusher Undefined property: stdClass::$channels in Laravel Lumen

I'm making a POC with Lumen and Vue.JS. For now it just has to send a "hello world" message from the Lumen back-end to the Vue.JS front-end (which works). I have made an event which is triggered upon loading the page like this:
public function sendMessage(Request $request)
{
event(new MessageEvent('hello world'));
}
The MessageEvent looks like this (got this from the Pusher getting started help):
class MessageEvent extends Event implements ShouldBroadcast
{
/**
* Create a new event instance.
*
* #return void
*/
public $message;
public function __construct($message)
{
$this->message = $message;
}
public function broadcastOn()
{
return ['my-channel'];
}
public function broadcastAs()
{
return 'my-event';
}
}
This part is working, since I'm receiving this in the Vue.JS application:
Pusher : : ["Event recd",{"event":"my-event","channel":"my-channel","data":{"message":"hello world"}}]
Now comes the problem when i check the queue log. triggered with php artisan queue:listen, I see the following:
[2021-03-14 11:57:03][Bh7373O9EETAZc39M2RCSPmUTjwSbSmL] Processing: App\Events\MessageEvent
[2021-03-14 11:57:04][Bh7373O9EETAZc39M2RCSPmUTjwSbSmL] Failed: App\Events\MessageEvent
When I check the Lumen log files it says the following:
[2021-03-14 11:43:12] local.ERROR: Undefined property: stdClass::$channels {"exception":"[object] (ErrorException(code: 0): Undefined property: stdClass::$channels at /var/www/vendor/pusher/pusher-php-server/src/Pusher.php:538)
So I went ahead and checkt the Pusher.php file:
536: $result = json_decode($response['body']);
537:
538: if ($result->channels) {
539: $result->channels = get_object_vars($result->channels);
540: }
I decided to check what $response was, it gives the following:
[2021-03-14 11:57:04] local.INFO: array (
'body' => '{}',
'status' => 200,
)
Of course it can't get to $result->channels if response["body"]["channels"] doesn't exist.
When I go and check the Pusher API reference it says the following:
Which would mean that the body should indeed contain a JSON response. But when I scroll a bit further I see this:
Which should mean you don't have to set the info parameter, since it's optional and experimental.
[EXPERIMENTAL] If the info parameter is sent, then it returns a hash of unique channels that were triggered to. The hash maps from channel name to a hash of attributes for that channel (may be empty).
The response it expects with the info parameter set is this:
{
"channels": {
"presence-foobar": {
"user_count": 42,
"subscription_count": 51
},
"presence-another": {
"user_count": 123,
"subscription_count": 140
},
"another": {
"subscription_count": 13
}
}
}
Which is the channels object asked for.
My question is, did I miss something or is this a bug from Pusher? I'm really breaking a leg on this one so I hope someone can help me out.
Pusher API reference: https://pusher.com/docs/channels/library_auth_reference/rest-api
Fix composer.json
I have created an issue on the PHP package: https://github.com/pusher/pusher-http-php/issues/295
It is true this version is broken, but the fix should be in the composer.json file. Mine looked like this:
{
...
"require": {
...
"pusher/pusher-php-server": "5.0"
...
},
...
}
This means I specifically say that it should use version 5.0. But now I won't get version 5.2 for example, which means I won't get patches. According to the people who answered my issue on Github, I should change my composer.json and add a ^ in front of the version number so it does get the patches for that version. So it should be changed like this:
{
...
"require": {
...
"pusher/pusher-php-server": "^5.0"
...
},
...
}
Don't forget to run composer update afterwards.
According to Graham Campbell on Github:
Anyone who puts 5.0 in their composer.json file has made an error, since composer resolves this to the version 5.0.0.0 and not the version constraint ^5.0.
Fix pusher.php (not recommended)
Another workaround would be to directly edit /vendor/pusher/pusher-php-server/src/Pusher.php. Although not recommended, it does work.
536: $result = json_decode($response['body']);
537:
538: if ($result->channels) {
539: $result->channels = get_object_vars($result->channels);
540: }
This doesn't work since channels doesnt exist in the result object. It should check if the channels object exists first. You can do that by changing the above code to this:
536: $result = json_decode($response['body']);
537:
538: if (property_exists($result, 'channels')) {
539: $result->channels = get_object_vars($result->channels);
540: }

How to stop php script in Laravel when Axios request is cancelled?

I'm making a live search using Laravel, VueJs and axios, so every time the user types a new word, the previous request will be cancelled. My problem is that even when I cancel the previous request using the cancel token (https://github.com/axios/axios#cancellation), the php script is still running.
QUESTION: How can i stop the php script if the axios request has been cancelled?
my Vuejs code
fetchData(query) {
if(cancel != undefined)
cancel(); // cancel the previous request
axios.get("http://sample-link", {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
}),
params: {
query : query
}
}).then(response => {
console.log(response);
}).catch(error => {console.log(error.message)})
}
...
my php code
class SearchController extends Controller {
public function Search(Request $request)
{
$query = $request->input('query');
$accounts = Accounts::search($query, null, true, true)->get();
return response()->json($accounts);
}
...
}
I don't know exactly how the Axios cancellation works, but if the client disconnects the HTTP session (as it should) you could try using the ignore_user_abort setting.
public function Search(Request $request)
{
ignore_user_abort(false);
$query = $request->input('query');
$accounts = Accounts::search($query, null, true, true)->get();
return response()->json($accounts);
}
Other useful functions could be:
connection_status: Check the current status of the connection
connection_aborted: Check if the connection has been canceled
I just want to add that your function does not seem very intensive, so I'm not sure how useful such a check is. As Tauqeer suggested in the comment, the usual approach in those situations is to apply a debounce to your javascript search function, in order to only fire the request when the user finished typing.
An example using lodash:
const search = (query) => {
.. your axios request ...
}
const fetchData = _.debounce(search, 300)
Now you can just call fetchData(query) whenever the user types something and it will only send a request when the user stops typing for 300 milliseconds

PHP SDK not sending errors to Sentry when invoked from IBM Cloud Functions

I am using Serverless framework to deploy my PHP code as IBM Cloud Function.
Here is the code from the action PHP file:
function main($args): array {
Sentry\init(['dsn' => 'SENTRY_DSN' ]);
try {
throw new \Exception('Some error')
} catch (\Throwable $exception) {
Sentry\captureException($exception);
}
}
And this is the serverless.yml file:
service: cloudfunc
provider:
name: openwhisk
runtime: php
package:
individually: true
exclude:
- "**"
include:
- "vendor/**"
functions:
test-sentry:
handler: actions/test-sentry.main
annotations:
raw-http: true
events:
- http:
path: /test-sentry
method: post
resp: http
package:
include:
- actions/test-sentry.php
plugins:
- serverless-openwhisk
When I test the action handler from my local environment(NGINX/PHP Docker containers) the errors are being sent to Sentry.
But when I try to invoke the action from IBM Cloud nothing appears in the Sentry console.
Edit:
After some time trying to investigate the source of the problem I saw that its related with the async nature of sending the http request to Sentry(I have other libraries that make HTTP/TCP connections to Loggly, RabbitMQ, MySQL and they all work as expected):
vendor/sentry/sentry/src/Transport/HttpTransport.php
in the send method where the actual http request is being sent:
public function send(Event $event): ?string
{
$request = $this->requestFactory->createRequest(
'POST',
sprintf('/api/%d/store/', $this->config->getProjectId()),
['Content-Type' => 'application/json'],
JSON::encode($event)
);
$promise = $this->httpClient->sendAsyncRequest($request);
//The promise state here is "pending"
//This line here is being logged in the stdout of the invoked action
var_dump($promise->getState());
// This function is defined in-line so it doesn't show up for type-hinting
$cleanupPromiseCallback = function ($responseOrException) use ($promise) {
//The promise state here is "fulfilled"
//This line here is never logged in the stdout of the invoked action
//Like the execution never happens here
var_dump($promise->getState());
$index = array_search($promise, $this->pendingRequests, true);
if (false !== $index) {
unset($this->pendingRequests[$index]);
}
return $responseOrException;
};
$promise->then($cleanupPromiseCallback, $cleanupPromiseCallback);
$this->pendingRequests[] = $promise;
return $event->getId();
}
The requests that are registered asynchronously are sent in the destructor of the HttpTransport instance or when PHP shuts down as a shutdown function is registered. In OpenWhisk we never shut down as we run in a never-ending loop until the Docker container is killed.
Update: You can now call $client-flush() and don't need to worry about reflection.
main() now looks like this:
function main($args): array {
Sentry\init(['dsn' => 'SENTRY_DSN' ]);
try {
throw new \Exception('Some error')
} catch (\Throwable $exception) {
Sentry\captureException($exception);
}
$client = Sentry\State\Hub::getCurrent()->getClient();
$client->flush();
return [
'body' => ['result' => 'ok']
];
}
Original explanation:
As a result, to make this work, we need to call the destructor of the $transport property of the Hub's $client. Unfortunately, this private, so the easiest way to do this is to use reflection to make it visible and then call it:
$client = Sentry\State\Hub::getCurrent()->getClient();
$property = (new ReflectionObject($client))->getProperty('transport');
$property->setAccessible(true);
$transport = $property->getValue($client);
$transport->__destruct();
This will make the $transport property visible so that we can retrieve it and call its destructor which will in turn call cleanupPendingRequests() that will then send the requests to sentry.io.
The main() therefore looks like this:
function main($args): array {
Sentry\init(['dsn' => 'SENTRY_DSN' ]);
try {
throw new \Exception('Some error')
} catch (\Throwable $exception) {
Sentry\captureException($exception);
}
$client = Sentry\State\Hub::getCurrent()->getClient();
$property = (new ReflectionObject($client))->getProperty('transport');
$property->setAccessible(true);
$transport = $property->getValue($client);
$transport->__destruct();
return [
'body' => ['result' => 'ok']
];
}
Incidentally, I wonder if this Sentry SDK works with Swoole?
Function runtimes are "paused" between requests by the platform. This means any background processes will be blocked if they aren't finished when the function returns.
It looks like the asynchronous HTTP request doesn't get a chance to complete before the runtime pauses.
You will need to find some way to block returning from the function until that request is completed. If the Sentry SDK has some callback handler or other mechanism to be notified when messages have been sent, you could use that?

Laravel returning HttpException object instead of showing custom error pages

I'm using spatie permissions module for controlling roles and permissions within my site. I have added a bit to the Authenticate middleware. My Handle now looks like this:
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest())
{
if ($request->ajax() || $request->wantsJson())
return response('Unauthorized.', 401);
return redirect()->guest('login');
}
if ( ! Auth::user()->can('access acp') )
{
if ($request->ajax() || $request->wantsJson())
return response('Unauthorised.', 403);
abort(403, "You do not have permission to access the Admin Control Panel. If you believe this is an error please contact the admin who set your account up for you.");
}
return $next($request);
}
So if the user isn't logged in we send them to the login page, otherwise we check if the have permissions to access the acp, and if not show them a 403 error. I've added a 403.blade.php to the views/errors folder. However when I run that code I just get a Whoops! and the developer tools show a 500 ISE is being returned. I don't understand why I'm not seeing my custom error page.
So far I've tried switching the environment to production and turning debug mode off but that doesn't show the page. I've also tried throwing an authorisation exception but that doesn't do anything different. I also tried using App::abort() but again, I still got the 500 ISE.
I've tried Googling the issue but I can't find anyone else having this issue. I would really appreciate any help in getting this working.
Whoops returns
If I modify the code thusly
try
{
abort(403, "You do not have permission to access the Admin Control Panel. If you believe this is an error please contact the admin who set your account up for you.");
} catch ( HttpException $e )
{
dd($e);
}
then I get an instance of HttpException with my error code and message, so why isn't that then showing a custom error page?
I've managed to get around this problem with the the code below (note that it is a Lumen app but it should work with Laravel)
routes.php
$app->get('/test', function () use ($app) {
abort(403, 'some string from abort');
});
resources/views/errors/403.blade.php
<html>
<body>
{{$msg}}
<br>
{{$code}}
</body>
</html>
app/Exceptions/Handler.php, modify render() function as below
public function render($request, Exception $e)
{
if ($e instanceof HttpException) {
$statusCode = $e->getStatusCode();
if (view()->exists('errors.'.$statusCode)) {
return response(view('errors.'.$statusCode, [
'msg' => $e->getMessage(),
'code' => $statusCode
]), $statusCode);
}
}
return parent::render($request, $e);
}
It does what the Laravel should do according to docs

Categories