[{"content":"","date":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories"},{"content":"","date":null,"permalink":"/categories/development/","section":"Categories","summary":"","title":"Development"},{"content":"","date":null,"permalink":"/tags/horizon/","section":"Tags","summary":"","title":"Horizon"},{"content":"","date":null,"permalink":"/tags/job/","section":"Tags","summary":"","title":"Job"},{"content":"","date":null,"permalink":"/tags/laravel/","section":"Tags","summary":"","title":"Laravel"},{"content":"","date":null,"permalink":"/tags/octane/","section":"Tags","summary":"","title":"Octane"},{"content":" Let\u0026rsquo;s dive in the ocean of ones and zeros 🌊 ","date":null,"permalink":"/programming/","section":"Programming","summary":"","title":"Programming"},{"content":"","date":null,"permalink":"/tags/queue/","section":"Tags","summary":"","title":"Queue"},{"content":"","date":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags"},{"content":"","date":null,"permalink":"/","section":"Throwing Exceptions 🚧","summary":"","title":"Throwing Exceptions 🚧"},{"content":" Familiarity with Laravel Horizon and Laravel Octane is required. TL;DR # Uncomment FlushUploadedFiles and CollectGarbage event listeners in Octane config. Check memory_limit in Horizon, PHP-FPM and PHP-CLI config and increase if necessary. Check Horizon timeout configs and increase if necessary. Check max_execution_time in Octane, PHP-FPM, and PHP-CLI config and increase if necessary. Even though I was aware of what Octane does and the super-fast performance it offers for Laravel applications, this was the first time we used it in a recent project. From the Octane documentation and numerous YouTube videos, I knew there were special considerations to keep in mind when designing the architecture of the application while using Octane. Memory leaks were one of them.\nIn our application, we have heavy usage of WebSockets. After launching a second release, usage skyrocketed, and we started encountering thousands of failed events. I began investigating the issue, and the first error I found was this:\nIlluminate\\Broadcasting\\BroadcastException: Pusher error: cURL error 28: Operation timed out after 30001 milliseconds with 0 bytes received (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://******/apps/******/events?auth_key=******\u0026amp;auth_timestamp=1725733807\u0026amp;auth_version=1.0\u0026amp;body_md5=159784e0a20c9b122f08b6d7d7389678\u0026amp;auth_signature=a424a1859671b8af49b1d242edaf85ec37bf8cb23b08bda58b195b2bbc5ec260. in /******/forge/******/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php:164 So, it was a timeout error; some long-running events were failing for some reason. I double-checked the horizon and queue configs for all the timeout values. Everything seemed fine. The stacktrace also gave no useful clue. We use Laravel Forge to maintain our apps, and we recently swtiched from Soketi to Laravel Reverb. I went into the server logs and checked the daemon logs for all services (Horizon, Octane, and Reverb). In the Reverb log, I found something interesting:\nPHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 20480 bytes) in /******/forge/******/vendor/laravel/telescope/src/IncomingEntry.php on line 79 From my understanding, due to memory exhaustion, PHP was crashing along with Reverb, causing the Pusher connection to fail, which resulted in the timeout error. 536870912 bytes is around ~536 megabytes, and we had set the memory limit to 512MB in our PHP config. We had enough memory available, so we increased the limit significantly. For a while, everything seemed normal. However, later that day, memory was exhausted again.\nTo resolve the issue quickly, we increased the limit to 8GB for the moment. Pretty insane for a Laravel Application, I know. But when you serve an application using Octane, it’s expected to consume more memory than usual since everything is running in memory. Despite this, we suspected a potential memory leak when it still didn’t hold up for long. Even for an in-memory application, the consumption seemed unusually high based on our estimates. It was driving us crazy because it felt like the solution was right before our eyes, yet we were missing it.\nI started looking into Octane\u0026rsquo;s config. Our application handles a lot of file uploads. Octane provides a variety of event listeners in its config file, but you need to enable them based on your requirements. First thing I enabled was FlushUploadedFiles for RequestTerminated event:\n//... \u0026#39;listeners\u0026#39; =\u0026gt; [ //... RequestTerminated::class =\u0026gt; [ FlushUploadedFiles::class, ], //... ], //... Another event listener I uncommented was CollectGarbage for OperationTerminated event. This one is very important.\n//... \u0026#39;listeners\u0026#39; =\u0026gt; [ //... OperationTerminated::class =\u0026gt; [ //... CollectGarbage::class, ], //... ], //... Along with that, I increased the Garbage Collection Threshold to 512MB:\n//... \u0026#39;garbage\u0026#39; =\u0026gt; 512, //... After these changes, we saw a dramatic improvement in the memory leak situation. The memory usage dropped almost 50%. The funny thing is, I overlooked another very crucial detail right after the garbage collection settings.\nFor nearly a day, no more errors were reported. Unfortunately I started seeing failed events again due to the timeout issue. But this time, neither PHP nor Reverb was crashing; it was purely timeout issue. We spent days tweaking various settings, including increasing the max_execution_time in both PHP-FPM and PHP-CLI configs, but there was no noticeable improvement. So, I decided to go through the Octane config once more. To my disbelief, I found myself dumbfounded for missing this the first time:\n//... \u0026#39;max_execution_time\u0026#39; =\u0026gt; 30, This is the description of the settings:\nThe following setting configures the maximum execution time for requests being handled by Octane. You may set this value to 0 to indicate that there isn\u0026rsquo;t a specific time limit on Octane request execution time.\nThe value is in seconds. From our error message, we know that it was 30001 ms which is exactly 30s. I increased this to 120s.\nAdditionally, I changed default_socket_timeout to 120s from 60s in PHP config which I felt necessary for our use case. My colleague also adjusted some Reverb connection settings. So far, we haven\u0026rsquo;t faced anymore memory leak or timeout issue since then. 🥂\n","date":"September 11, 2024","permalink":"/programming/timeout-and-memory-leak-issues-in-horizon-with-laravel-octane/","section":"Programming","summary":"Tweak Octane, Horizon, PHP-FPM, and PHP-CLI configs to mitigate memory leak and timeout issue in long running processes.","title":"Timeout and Memory Leak Issues in Horizon With Laravel Octane"},{"content":"","date":null,"permalink":"/tags/family/","section":"Tags","summary":"","title":"Family"},{"content":"","date":null,"permalink":"/tags/grief/","section":"Tags","summary":"","title":"Grief"},{"content":"","date":null,"permalink":"/tags/introspection/","section":"Tags","summary":"","title":"Introspection"},{"content":" Reflections on life\u0026rsquo;s moments 🌇 ","date":null,"permalink":"/musing/","section":"Musing","summary":"","title":"Musing"},{"content":"","date":null,"permalink":"/categories/odyssey/","section":"Categories","summary":"","title":"Odyssey"},{"content":"","date":null,"permalink":"/tags/personal-growth/","section":"Tags","summary":"","title":"Personal Growth"},{"content":"","date":null,"permalink":"/tags/reflection/","section":"Tags","summary":"","title":"Reflection"},{"content":"","date":null,"permalink":"/tags/resilience/","section":"Tags","summary":"","title":"Resilience"},{"content":"In his final days, Vincent Van Gogh spoke these haunting words (the title of the article) from the depths of his enigmatic mind. When I first came across this quote, it stuck in my brain—I loved it so much. I think it beautifully mirrors the bittersweet longing that arises as life\u0026rsquo;s seasons unfold, when we start losing people from our lives.\nI lost my aunt–my mom\u0026rsquo;s last living sister–4 days ago. It was such a strange and daunting experience, witnessing her transformation into ashes right before my eyes. While writing this, I\u0026rsquo;ve lived through 34 springs of my life, encountering a multitude of experiences and dealing with all sorts of people along the way. These life lessons have sort of toughened me up and made me less emotional. Yet, my eyes were foggy and my breath grew heavy at various moments throughout that day.\nI was not close to my aunt. So why the outburst, I asked myself. Was it because I just saw my mom losing her only living sister, realizing how hard it would be for her to not being to talk to her beloved sibling again? Or perhaps it stemmed from the connection these individuals held to my childhood, with each loss feeling like a fragment of my cherished past slipping away. Or was it the fear that where my cousins stand today, I may find myself in the very same position tomorrow?\nAll of these reasons hold true, but their precedence remains unknown to me. What I do know is that, like a lingering fog over a swamp in winter, these sadness will endure until I am the one who is getting burned.\n","date":"March 17, 2024","permalink":"/musing/the-sadness-will-last-forever/","section":"Musing","summary":"When you grow up you start losing people, and you lose a part of your childhood with them. This endless longing becomes your undetachable companion for life.","title":"The Sadness Will Last Forever"},{"content":"","date":null,"permalink":"/tags/transformation/","section":"Tags","summary":"","title":"Transformation"},{"content":"","date":null,"permalink":"/tags/vincent-van-gogh/","section":"Tags","summary":"","title":"Vincent Van Gogh"},{"content":"","date":null,"permalink":"/tags/devops/","section":"Tags","summary":"","title":"Devops"},{"content":"","date":null,"permalink":"/tags/digitalocean/","section":"Tags","summary":"","title":"Digitalocean"},{"content":"","date":null,"permalink":"/tags/droplets/","section":"Tags","summary":"","title":"Droplets"},{"content":"","date":null,"permalink":"/tags/laravel-forge/","section":"Tags","summary":"","title":"Laravel-Forge"},{"content":"","date":null,"permalink":"/tags/servers/","section":"Tags","summary":"","title":"Servers"},{"content":"Imagine you have an app in production on a DigitalOcean droplet which is managed by Laravel Forge. Now for some reason you want to transfer it to another DigitalOcean account without creating everything from scratch and also avoiding the risk of breaking what is working perfectly. Within your same account you can just take a snapshot and create a new droplet from that snapshot. But what if the new droplet is to be created in a separate DigitalOcean account? 🤔\nIt\u0026rsquo;s just an extra step to be taken care of. Take a snapshot and select Transfer Snapshot from the menu.\nSelect Transfer Snapshot from menu You can either transfer to an email address or a team. If you are a member of the team in the other account, this would be much easier. Otherwise select email address. The person should receive an invitation to accept the transfer in their email.\nTransfer to email or team Now in the other account, just create a new droplet from the snapshot. Voila! 🙌\nRemember to keep the root password same as the previous server. Okay, you\u0026rsquo;ve successfully cloned the server, but the new server is not yet connected. You need to update your DNS settings in your domain control panel so that your xyz.com domain points to the new server\u0026rsquo;s IP address. Once the DNS propagation is complete, you can safely delete the old server.\nBut what about Forge?\nAfter deleting the old server, your Forge connection will be disrupted. To reestablish it with the new server, go to the Meta tab and under Server Metadata section. Update both the public and private IP addresses to match the new server. Then click on the Connect button at the top-right corner. If your IP addresses are correct it should be connected within a few seconds.\nUpdate Forge server metadata Since the new server is a clone of your old server; all apps, daemons, and schedulers should work seamlessly without requiring any changes. 👌\nIf you also need to transfer the server between Forge accounts, simply enter an email in the Transfer Server section and send the transfer request. Once the new owner accepts the request, it will be handed over to them without any additional configuration needed.\nTransfer server between forge accounts ","date":"October 01, 2023","permalink":"/programming/transfer-servers-in-digitalocean-and-forge/","section":"Programming","summary":"A guide to transfer droplets from one DigitalOcean account to another DigitalOcean account which is connected to a Laravel Forge account.","title":"Transfer Droplets Within DigitalOcean and Forge Accounts"},{"content":"","date":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"Ai"},{"content":"","date":null,"permalink":"/categories/artificial-intelligence/","section":"Categories","summary":"","title":"Artificial-Intelligence"},{"content":"","date":null,"permalink":"/tags/automation/","section":"Tags","summary":"","title":"Automation"},{"content":"","date":null,"permalink":"/tags/chatgpt/","section":"Tags","summary":"","title":"Chatgpt"},{"content":"Thinking is a complicated process. Every creative task in the world requires a tremendous amount of thinking and it involves multiple levels of consideration. For instance, I need to figure out a few things before I write this article, roughly —\nOutlines/points Language and tone Title Proofreading All of these don\u0026rsquo;t bear the same priority and weight. If I were you, I would happily leave the job of finding an appropriate headline and proofreading of this article to an assistant because it is boring. Unfortunately I\u0026rsquo;m not in the position of having an \u0026ldquo;human assistant\u0026rdquo; yet 🫠. But, I\u0026rsquo;ve got one better\u0026hellip;\nWALL-E Well, not WALL-E. It\u0026rsquo;s ChatGPT. I told it the underlying idea behind this article and it generated some \u0026ldquo;thoughtful\u0026rdquo; headlines to pick one up. It also works as my proofreader and editor. I humbly acknowledge that my writing prowess may not be exemplary, thus, I seek its aid in the art of rephrasing sentences, a task it performs with remarkable finesse. Did you get it? The original line was, \u0026ldquo;I am not a very skilled person when it comes to writing so I seek its help in rephrasing sentences which it does very well.\u0026rdquo;\nI have empowered the AI to think on my behalf. I wouldn\u0026rsquo;t let it write the entire article for me, as that would compromise its originality and I would not be considered as the author anymore. I did one more thing, I needed to generate a slug of the title and I did not want to write it out myself. So this is what I did\u0026hellip;\nRewrite the headline in kebab case Few days back I was converting an excel file into HTML. It was neither large enough to warrant the use of a special tool nor small enough to write it manually. And hey, I\u0026rsquo;m an Engineer. I automate the boring stuff, that\u0026rsquo;s my superpower! ⚡ So I reached out to ChatGPT for help. I prompted it some data from my excel sheet and an example structure of the HTML table. After that I just gave it the data and in return got the formatted HTML. The data I copy-pasted from the excel file was like this:\nHeading 1\nValue 1.1\nValue 1.2\nHeading 2\nValue 2.1\nValue 2.2\nAnd it has to be formatted like this:\n\u0026lt;tr\u0026gt; \u0026lt;th rowspan=2\u0026gt;Heading 1\u0026lt;/th\u0026gt; \u0026lt;td\u0026gt;Value 1.1\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;Value 1.2\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th rowspan=2\u0026gt;Heading 2\u0026lt;/th\u0026gt; \u0026lt;td\u0026gt;Value 2.1\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;Value 2.2\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; So that I get the following output:\nHeading 1 Value 1.1 Value 1.2 Heading 2 Value 2.1 Value 2.2 Formatting all the data manually would have taken a considerable amount of time, and the risk of making mistakes was high. With the help of ChatGPT, the task was completed in just a couple of minutes. Most interestingly, one row was there with heading but no data. There was another heading immediately after that. I didn\u0026rsquo;t even notice that when I gave the data to ChatGPT. But it detected the heading with missing data, kept empty \u0026lt;td\u0026gt;\u0026lt;/td\u0026gt; where the data was missing and notified me about this in the reply. It also properly formatted the next row with its heading and data. I was amazed!\nAt my workplace as well, AI plays a crucial role, particularly in data analysis. You can easily filter out unnecessary data from a large dataset. This reduces calculation time, making your app faster and more robust. You can instruct the AI to generate the output in a specific format, e.g. JSON, which makes it plug and play. The most crucial aspect here is providing the prompt correctly. If your prompt is vague and unspecific, it may produce unexpected results and, consequently, bugs.\nAI is an incredibly powerful tool. What I\u0026rsquo;ve shown here are just the basics. We all know there are countless \u0026ldquo;unthinkable\u0026rdquo; things that can be accomplished with AI. It\u0026rsquo;s not here to steal your job or dethrone humanity; rather, it\u0026rsquo;s here to make your life and work easier. Embrace its power.\n","date":"July 24, 2023","permalink":"/programming/empower-ai-for-streamlining-repetitive-tasks/","section":"Programming","summary":"Teach AI to think like you and perform the less thoughtful jobs for you.","title":"Empower AI for Streamlining Repetitive Tasks"},{"content":"In social media and sometimes in conversations, I often see one very common cliché,\nGoddamn autocomplete!\nPeople say it when they misspell a word and the autocomplete tries to correct it but changes it to something totally different in the attempt modifying the meaning of the sentence. I bet all of you have seen it and even said it at some point.\nSo, why am I expounding upon this? See, I\u0026rsquo;ve never understood this problem because I simply turn the autocomplete off. As a multilingual person I switch between layouts often and sometimes I type Bengali using English alphabets. And it\u0026rsquo;s very common (I do not encourage it but when in hurry it\u0026rsquo;s faster and easier). Now if I keep autocomplete on, chances are most of my Bengali words will be corrected into something totally different. It\u0026rsquo;s annoying and time wasting. Do I always know the spelling of each and every word? No. I left the autosuggestion and spell-checker on. Whenever I make a mistake, it underlines the word and I can correct or leave the word as is. Great success! ✌\nMy point is, WE HAVE A CHOICE. I choose not to be bothered by the \u0026ldquo;goddamn autocomplete\u0026rdquo;. I take the responsibility of my typing and what I\u0026rsquo;m trying to say. If it bothers you so much, just turn the damn thing off, why keep whining about it?\nAutocomplete was just an example. I\u0026rsquo;ve been using social media since a long time. I opened my facebook account in 2008, when very few people in my country knew about it. Then all on a sudden — circa 2011-2012 — it became very crowded, and by 2017 people were doing almost everything on facebook. And one more thing they did, whining about it\u0026rsquo;s toxicity. Yes, facebook is toxic, it creates a very false and imaginary image about our surroundings. I faced it too. For that, I left the platform for good in 2019. I kept it deactivated for nearly a year and later in 2020 I deleted all the data permanently. Again, I made a choice. I had many amazing people in my friend list. Losing contact with them was tough, nevertheless, my mental peace had the highest priority.\nI see this often. People regret about things in their life but refuse to take a step to change the situation. You can complain about your relation, your job, your study\u0026hellip;but none of them will solve themselves. Taking an action is necessary. God is not gonna help you unless you help yourself first. You\u0026rsquo;re the one who can deal with your shit; the sooner you understand this, the faster you will recover. 🧠\nWhere spring, the lord of seasons reigneth, There the unstruck music sounds of itself, There the streams of light flow in all directions, Few are the men who can cross to that shore! — Kabir Das, translated by Rabindranath Tagore\n","date":"July 09, 2023","permalink":"/musing/autocomplete-was-never-an-issue/","section":"Musing","summary":"If you\u0026rsquo;re in trouble, take an action. Don\u0026rsquo;t just sit and whine.","title":"Autocomplete Was Never an Issue"},{"content":"","date":null,"permalink":"/tags/mental-health/","section":"Tags","summary":"","title":"Mental Health"},{"content":"","date":null,"permalink":"/tags/responsibility/","section":"Tags","summary":"","title":"Responsibility"},{"content":"","date":null,"permalink":"/tags/social-media/","section":"Tags","summary":"","title":"Social Media"},{"content":"","date":null,"permalink":"/tags/toxicity/","section":"Tags","summary":"","title":"Toxicity"},{"content":"As per Laravel documentation1, the belongsTo, hasOne, hasOneThrough, and morphOne relationships allow us to define a default model if no related model is found. This pattern is known as Null Object Pattern in software engineering world. This can be very useful from time to time and it is very easy to achieve, thanks to the eloquence of Laravel.\nConsider the following polymorphic relationship between the User and the File model for a profile picture.\n1 2 3 4 public function profilePicture(): MorphOne { return $this-\u0026gt;morphOne(File::class, \u0026#39;fileable\u0026#39;)-\u0026gt;withDefault(); } Aside from traditional $this-\u0026gt;morphOne(File::class, 'fileable'), we are returning an empty File object using withDefault() method if no corresponding profile picture is found for the user. Let\u0026rsquo;s take a look inside our File model.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 namespace App\\Models; use Illuminate\\Database\\Eloquent\\Casts\\Attribute; use Illuminate\\Database\\Eloquent\\Model; use Illuminate\\Database\\Eloquent\\Relations\\MorphTo; use Storage; class File extends Model { protected $fillable = [ \u0026#39;name\u0026#39;, \u0026#39;path\u0026#39;, \u0026#39;fileable_id\u0026#39;, \u0026#39;fileable_type\u0026#39;, ]; public function fileable(): MorphTo { return $this-\u0026gt;morphTo(); } public function url(): Attribute { return Attribute::get(function ($value, $attributes) { // If path is empty or file is not found in path, return empty string if (empty($attributes[\u0026#39;path\u0026#39;]) || ! Storage::exists($attributes[\u0026#39;path\u0026#39;])) { return \u0026#39;\u0026#39;; } return Storage::url($attributes[\u0026#39;path\u0026#39;]); }); } } Here we have pretty standard stuff. Some attributes in the $fillable property, the polymorphic relationship fileable() and an accessor url to sort out the actual URL from the path of the file.\nWithout doing any additional thing to the withDefault() method in User model relationship, we would get an empty File model. Sometimes, this is enough. But often we need more than that. For example, in this case, we would want to generate some sort of SVG for missing profile picture which is a standard practice. We can do so:\n1 2 3 4 5 6 7 public function profilePicture(): MorphOne { return $this-\u0026gt;morphOne(File::class, \u0026#39;fileable\u0026#39;)-\u0026gt;withDefault([ \u0026#39;name\u0026#39; =\u0026gt; \u0026#34;Default User\u0026#34;, \u0026#39;url\u0026#39; =\u0026gt; \\Svg::for(\u0026#34;Default User\u0026#34;)-\u0026gt;toUrl(), ]); } We can pass an array inside withDefault() with the attributes we want to populate. For the url attribute, we are using my SVG Avatar Generator library for Laravel to generate an SVG on the fly based on the string we are passing.\nBut \u0026ldquo;Houston, we have a problem\u0026rdquo;2. All of our users now have same profile picture cause we are passing same \u0026ldquo;Default User\u0026rdquo; string. That\u0026rsquo;s underwhelming. How can we make it more exciting?\nTurns out, Laravel makes it very effortless (yeah, the level of attention to detail is 🤯). Instead of array, we can pass a closure to the withDefault(). The closure receives two parameters; first, the related model; and second, the parent model:\n1 2 3 4 5 6 7 8 public function profilePicture(): MorphOne { return $this-\u0026gt;morphOne(File::class, \u0026#39;fileable\u0026#39;) -\u0026gt;withDefault(function (File $file, User $user) { $file-\u0026gt;name = $user-\u0026gt;name; $file-\u0026gt;path = \\Svg::for($user-\u0026gt;name)-\u0026gt;toUrl(); }); } Great! Now all users have customized SVGs based on their names. 💅\nIn the array we passed the value of url but in the closure we set the value of path. I don\u0026rsquo;t like to set an accessor value directly, I would rather generate the value based on the path. This is just a personal preference. If you do something like $file-\u0026gt;url = Svg::for($user-\u0026gt;name)-\u0026gt;toUrl() it would work as well. Also, we have to modify the url accessor on the File model slightly. Since we are setting the URL directly in path, we can no longer just call Storage::url() over another URL. There should be a check if the path is a file path or a URL. For this we can use Laravel\u0026rsquo;s URL facade.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 namespace App\\Models; //... use URL; class File extends Model { //... public function url(): Attribute { return Attribute::get(function ($value, $attributes) { // If path is a valid URL, return it if (isset($attributes[\u0026#39;path\u0026#39;]) \u0026amp;\u0026amp; URL::isValidUrl($attributes[\u0026#39;path\u0026#39;])) { return $attributes[\u0026#39;path\u0026#39;]; } //... return Storage::url($attributes[\u0026#39;path\u0026#39;]); }); } } That\u0026rsquo;s all. Now if we set a URL from the parent relation, it will not be accidentally overwritten by the accessor. Note that, there is no additional trickery outside Laravel, it\u0026rsquo;s just simple things like this what makes the framework beautiful.\nhttps://laravel.com/docs/master/eloquent-relationships#default-models\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhttps://en.wikipedia.org/wiki/Houston,_we_have_a_problem\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"May 08, 2023","permalink":"/programming/customize-model-accessor-over-null-object-pattern/","section":"Programming","summary":"Set accessor value depending on data passed via Laravel\u0026rsquo;s default model mechanism.","title":"Customize Model Accessor Over Null Object Pattern"},{"content":"","date":null,"permalink":"/tags/default-models/","section":"Tags","summary":"","title":"Default-Models"},{"content":"","date":null,"permalink":"/tags/eloquent/","section":"Tags","summary":"","title":"Eloquent"},{"content":" Here are a few open-source projects I\u0026rsquo;ve created — SVG Avatar Generator #🔗 Source: GitHub\nAn offline avatar generator for Laravel apps with bunch of cool customizations. The key features are:\nSupports gradient background. Supports random gradients based on defined presets in config. Multiple shapes: rectangular, rounded-rectangular, or circular. Customizable fonts from Google fonts and other sources. Ability to customize initials and extractor. Unlike some other available options, doesn\u0026rsquo;t require heavy-weight image processing libraries like Intervention. Doesn\u0026rsquo;t have any binary dependency, so nothing needs to be installed on server. Laravel ShurjoPay #🔗 Source: GitHub\nEnhanced ShurjoPay payment gateway client for Laravel apps. It has a few benefits over the official client:\nAuto discovery for Laravel 5.5+ projects. ShurjoPay configurations can be defined on the fly. Uses Guzzle instead of cURL by default. ZKConnect #🔗 Source: GitHub\nPython script to relay real-time attendance data from ZK Teco devices to an external API. I needed this on one of my project at work. Once connected, it will keep sending attendance data to the API you provided. It is designed to run using a service like Supervisor.\nLordCommander #🔗 Source: GitHub\nA python CLI app to execute commands recursively through multiple directories. Built for some DevOps purpose at work. My old workplace had some legacy apps running on shared hosting where using advanced tools was kinda cumbersome. So I made this simple app to perform some tasks in an easier way.\nWormhole #🔗 Source: GitHub\nUpload files with ease in Laravel apps. File uploading is already very convenient in Laravel, yet I found myself copy-pasting same code over and over. To make it reusable I made this with a few additional features. It has a Vue and Bootstrap/UIKit based front-end built-in which shows realtime uploading progress for large files.\nLaravel UIKit #🔗 Source: GitHub\nLaravel UIKit is a handy package for integrating your Laravel app with the excellent front-end framework UIKit easily. Besides that, a pre-built admin panel/application layout is provided along with some authentication pages, which you can extend and get your application running within minutes. However, you can customize each and everything that comes with this package.\n","date":null,"permalink":"/open-source/","section":"Throwing Exceptions 🚧","summary":"","title":"Open Source"},{"content":"Hardware #Machines # My daily driver is a MacBook Air 2020 (M1/16GB) which runs on MacOS Ventura. I do have Manjaro Linux installed on another older Asus laptop (Intel Core i5/8GB). I was a Linux user for a long time before jumping to Mac. Mouse # I\u0026rsquo;m using Logitech MX Master 3 with my MacBook. I was in doubt purchasing a non Apple mouse for an Apple device, but they works really good together. And with the LogiOptions+ app, you can mimic all gestures via your mouse. Software #Development # PhpStorm: Fantastic piece of software from Jetbrains. All my Laravel and Vue.js related coding are done in this app. Laravel IDEA: This is a very useful plugin I use with PhpStorm. It makes me so much faster while coding. Laravel Tinker: This plugin works as a PHP interactive shell inside PhpStorm. For running quick PHP code snippets I use it everyday. Laravel Herd: I replaced Valet with Herd few months back. Not the paid version though. Compared to Valet, Herd\u0026rsquo;s PHP version management is much more sophisticated. With Valet you need to maintain them by brew which sometimes a bit problematic. However, if you want maximum independence without paying extra bucks Valet with PHP Monitor is still a fantastic combination. LaraDumps: I replaced Laravel Ray with it\u0026rsquo;s open source equivalent LaraDumps for a while. Like Ray, it offers different options for debugging your applications. TablePlus: For database management. I\u0026rsquo;m using the free version but I might purchase the full version someday. It\u0026rsquo;s absolutely unmatched. Well, PhpStorm comes with a built-in database management tool which is very feature-rich and powerful. I\u0026rsquo;m trying to be fully accustomed with it. MySQL Workbench: Another tool for database management provided by MySQL. I use it sometimes for some use cases. VS Code: For projects other than PHP, I use VS Code as the editor. Zed: Zed is a new player in the editor world. Like Sublime text it is blazing fast. It is written in Rust. It took the place of Sublime Text for me. Zed got built-in AI support, excellent plug-in collections to give you a smooth editing experience. Warp: This is my default terminal app in MacOS. Very handy! Postman: Everyone knows Postman, the app for testing and documenting API endpoints. Productivity # Raycast: My Spotlight replacement in MacOS. Raycast is a brilliant app to do staff without opening multiple apps, clipboard maintenance, searching files and folders etc. Microsoft Edge: I don\u0026rsquo;t like Chrome. Though it might be an unpopular opinion I find Edge much more mature, streamlined, and feature rich than Chrome. Edge is my default browser in laptop and in mobile. I do have Chromium installed on my machine by the way. BitWarden: I\u0026rsquo;ve been using Bitwarden for couple of years. It is my password and 2FA manager everywhere. Notion: For taking notes and writings, Notion is my preferred app. Velja: A tiny app that helps me opening links from app with my intended browser. It gives you little pop-up to choose between your browsers. MoneyLover: I started using this multi-platform app to manage my financial activities. It helped me a lot to make budget and to see an overview of my expenditures. LocalSend: An open source, multi-platform file sharing tool. Very useful when you need to transfer files from Apple to Non-Apple devices. ChatGPT: Well, you know what it does. Velja in action ","date":"April 29, 2023","permalink":"/uses/","section":"Throwing Exceptions 🚧","summary":"Tools I\u0026rsquo;m using on a daily basis","title":"Uses"},{"content":"Hi, I\u0026rsquo;m Sowren, a Software Engineer from Bangladesh. I specialize in creating robust web applications with a primary focus on using Laravel and LiveWire (also Vue.js) as my core technologies. Besides that, I heavily use Python and Go now a days, especially when I\u0026rsquo;m working on microservices, AI related stuff, or CLI apps. I am very passionate about clean coding.\nWith around 7 years of professional experience, I\u0026rsquo;ve been engaged as a Full-Stack Engineer at Revealize Inc. since March 2021. Although my role is Full-Stack, I primarily focus on back-end and devops related tasks. My favorite thing to work on and to tweak my brain with is Queues and optimizing Eloquent queries in Laravel. Lately, with the advent of AI I am digging deeper into this field. From fine-tuning models and crafting datasets to experimenting with prompt engineering, I\u0026rsquo;ve explored quite a bit in this arena.\nProgramming is a continuous learning process. In my free time, I try playing with different languages and tools in order to keep myself familiar with modern infrastructures. All these test projects and open source tools are available in my public Github repository.\nTo know about the tools and apps I use, check out the Uses page. I enjoy reading books, listening to music, and watching movies and series. I am a die-hard fan of George R. R. Martin\u0026rsquo;s A Song of Ice and Fire universe. I\u0026rsquo;m optimistically waiting for the sixth book of the main series, (rolling your eyes? 🫠) but I also know:\nWords are wind! 🍃\nAnd I love science. To be specific, things about physics and space. Carl Sagan has a huge influence on me. He made me realize how insignificant we are in this unimaginably large universe, yet how much significant our life can be. ✨\nI thank you for reading this far and your curiosity towards a stranger. Nice to meet you, fellow star stuff! ☄️\nFor any kind of collaboration or consultation, you can reach out to me at: talkto[at]sowrensen.dev.\nAcknowledgement: This website is built using Hugo. It is using the Congo theme with slight modification.\n","date":null,"permalink":"/about/","section":"Throwing Exceptions 🚧","summary":"","title":"Who Am I?"},{"content":"","date":null,"permalink":"/tags/authentication/","section":"Tags","summary":"","title":"Authentication"},{"content":" Disclaimer: This is not the only way or the most efficient way to do it. It worked for me and I decided to share. The problem #Imagine you\u0026rsquo;re running a headless Laravel application which has bunch of APIs with only token based authentication. Well, in an API only application, authenticating Telescope and Horizon is not that straightforward. But you need both of these dashboards in your production environment to monitor your application\u0026rsquo;s performance and queue system. Leaving it wide open for everyone is not ideal scenario, as they may contain sensitive or confidential information.\nSo, what to do now? #In my company, we faced this several times. Fortunately, like other parts of Laravel, you can customize the authentication system of these packages as well and it\u0026rsquo;s pretty simple. We planned to make use of that and to do some shenanigans to go our way. The idea is basically to set a secret hashed key for authentication. We can put this in .env and retrieve when we need. Then while visiting the dashboard we can append the unhashed password to the URL and match it against our secret key. We would go one step further so that we don\u0026rsquo;t have to append this key every time we go to a new link, e.g. /telescope/requests, /telescope/jobs.\nLet\u0026rsquo;s dive in\u0026hellip; #First pick a secure password and put the hash of it in .env. We will call it INSPECTION_SECRET.\nINSPECTION_SECRET=\u0026#39;$2y$10$L/4eZD6mYq/xiORTOhOY9OtDZlKnjrFJZViOx.LpZakEuiQhggt4O\u0026#39; # P@55W0RD (pretty secure, huh?) Since it is not a good idea to access environment variables via env() in production environment, we should keep it in one of our config files. Let\u0026rsquo;s put it in config/app.php.\n\u0026#39;inspection_secret\u0026#39; =\u0026gt; env(\u0026#39;INSPECTION_SECRET\u0026#39;), Great! Now we can get the hash using config('app.inspection_secret'). 🙌\nWe are using same secret for Telescope and Horizon. Feel free to define separate keys. Okay, how to use this key? 🗝️ #Now head over to the TelescopeServiceProvider class. There you will see a gate() method, but it requires a $user object which we don\u0026rsquo;t have. So it is not useful to us, instead, we would override the authorization() method of the TelescopeApplicationServiceProvider class.\nLaravel\\Telescope\\TelescopeApplicationServiceProvider 20 21 22 23 24 25 26 27 28 29 30 31 32 33 /** * Configure the Telescope authorization services. * * @return void */ protected function authorization() { $this-\u0026gt;gate(); Telescope::auth(function ($request) { return app()-\u0026gt;environment(\u0026#39;local\u0026#39;) || Gate::check(\u0026#39;viewTelescope\u0026#39;, [$request-\u0026gt;user()]); }); } Notice that it calls the gate() method, we will get rid of it. And inside the auth() method, we will pass our own closure. In TelescopeServiceProvider class, override the authorization() method with the following body:\nApp\\Providers\\TelescopeServiceProvider 73 74 75 76 77 78 protected function authorization() { Telescope::auth(function($request) { //... }); } Closure #Now, let\u0026rsquo;s fill up the body of the closure.\nApp\\Providers\\TelescopeServiceProvider 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 protected function authorization() { Telescope::auth(function($request) { // If app is in development mode, // don\u0026#39;t require any password if (app()-\u0026gt;environment(\u0026#39;local\u0026#39;)) { return true; } // If request has a password, check // it against our stored secret if ($request-\u0026gt;filled(\u0026#39;password\u0026#39;)) { return Hash::check($request-\u0026gt;get(\u0026#39;password\u0026#39;), config(\u0026#39;app.inspection_secret\u0026#39;)); } return false; }); } Alright, now to test, change your APP_ENV to production and hit telescope route, e.g. http://localhost:8000/telescope. Voila! You will see a 403 Forbidden page from Laravel. To open the gate, append ?password=P@55W0RD to the URL, e.g. http://localhost:8000/telescope?password=P@55W0RD and you will land in the dashboard.\nBut wait\u0026hellip; #Yes, it keeps loading forever, right? Open the network tab in browser developer tools and check. You would see swarm of forbidden requests. Why? Because we\u0026rsquo;ve locked the way and these requests do not have the key. To resolve this issue, we should save the key somewhere. How about session? Change your authorization() method like following.\nApp\\Providers\\TelescopeServiceProvider 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 protected function authorization() { Telescope::auth(function($request) { // If app is in development mode, // don\u0026#39;t require any password if (app()-\u0026gt;environment(\u0026#39;local\u0026#39;)) { return true; } // A password is already saved in session if (Session::has(\u0026#39;inspection_secret\u0026#39;)) { return Hash::check( Session::get(\u0026#39;inspection_secret\u0026#39;), config(\u0026#39;app.inspection_secret\u0026#39;) ); } // If request has a password, check // it against our stored secret if ($request-\u0026gt;filled(\u0026#39;password\u0026#39;) \u0026amp;\u0026amp; Hash::check($request-\u0026gt;get(\u0026#39;password\u0026#39;), config(\u0026#39;app.inspection_secret\u0026#39;))) { // Store the key for future requests in session Session::put(\u0026#39;inspection_secret\u0026#39;, $request-\u0026gt;get(\u0026#39;password\u0026#39;)); return true; } // In case any change in password, wipe out the old one Session::forget(\u0026#39;inspection_secret\u0026#39;); return false; }); } Few things we\u0026rsquo;ve added. First, we are checking if the session has any previous password stored and matching against it. Second, if the request has a password, we are saving it for future requests in this session. So when we land in the dashboard our password is already saved in the session and all background requests will be authenticated using the stored key. Finally, we are deleting the key from session if password is changed in .env, so it is effective immediately.\nWe are done #Now our telescope dashboard is secured. But what about Horizon? You could do exactly the same for Horizon in HorizonServiceProvider class. However, instead of writing same code again, I would suggest to create a service/support class and use that on both places for the authentication. Again, there might be plenty of room for improvement as there is no one true way to achieve the same goal. If you do find some improvement or a better way please share with the world. 🤝\n","date":"December 02, 2022","permalink":"/programming/auth-for-telescope-and-horizon-in-headless-app/","section":"Programming","summary":"Use secret key to protect Telescope and Horizon dashboard","title":"How Did We Protect Telescope And Horizon In Headless Laravel App?"},{"content":"","date":null,"permalink":"/tags/telescope/","section":"Tags","summary":"","title":"Telescope"},{"content":"Manjaro/Arch Linux has no smooth way to install MySQL server since they dropped support of it and replaced it with MariaDB. It is an excellent alternative of MySQL and works absolutely fine. But sometimes you just need MySQL server itself.\nThere is a AUR package for MySQL which — from my experience — doesn’t go well often 😏. It builds from source and I couldn\u0026rsquo;t do it after waiting an hour or so. The alternate easiest way to have MySQL in your system is installing it via docker. Let\u0026rsquo;s begin.\nInstall docker #Installing docker on Manjaro is similar like installing any other standard packages.\nsudo pacman -Syu sudo pacman -S docker Next, you should enable docker service.\nsudo systemctl start docker.service sudo systemctl enable docker.service Create volumes #These are storage for keeping your data. Let’s create two volumes, one for MySQL and another for phpMyAdmin.\ndocker volume create mysql-volume docker volume create phpmyadmin-volume This will create a volume for the MySQL container.\nIf you want to remove a volume for some reason run:\ndocker volume rm \u0026lt;volume_name\u0026gt; Creating/Running containers #MySQL #To create the MySQL container run:\ndocker run --name=mysql-8.0 -p3306:3306 -v mysql-volume:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -d --restart unless-stopped mysql/mysql-server:8.0.29 Here we are using MySQL image version 8.0.29.\n--name flag sets a name for the container. -p3306:3306 is telling it to bind host port 3306 to the container port 3306. -v flag attaches the created volumes with the container. -e flag sets a environment variable MYSQL_ROOT_PASSWORD to set the root password which we will be using to login. The user name is root by default. -d flag tells the container to run in detached mode, so that it keeps running even after the command is exited. --restart unless-stopped option tells the container to keep running even when the computer restarts. Finally we provide the image name with the tag we want. phpMyAdmin #With the MySQL container created and running, let’s create the phpMyAdmin container:\ndocker run --name=phpmyadmin -v phpmyadmin-volume:/etc/phpmyadmin/config.user.inc.php --link mysql-8.0:db -p 8080:80 -d --restart unless-stopped -e UPLOAD_LIMIT=50M phpmyadmin/phpmyadmin Here most of the flags are same. Except,\n--link flag tells it to link the MySQL container with phpMyAdmin. -e sets an UPLOAD_LIMIT environment variable set the upload limit to 50 megabytes for phpMyAdmin. To bind with docker container\u0026rsquo;s port 80 we choose port 8080 of the host machine, you can choose any port you want. Health check #If both containers are successfully created, run following command to list out running containers:\ndocker ps To stop any container run:\ndocker stop \u0026lt;container_name_or_id\u0026gt; And to start it:\ndocker start \u0026lt;container_name_or_id\u0026gt; To delete a container:\ndocker rm \u0026lt;container_name\u0026gt; Access and additional configuration #To enter into a container run:\ndocker exec -it \u0026lt;container_name\u0026gt; bash Now we need to change MySQL connection restriction so that our host machine can connect to it because by default MySQL disables this for security purpose. To do that, first enter into the MySQL container, then run:\nmysql -u root -p Then enter the password (in this root) and we will land into MySQL console. Run:\nupdate mysql.user set host = \u0026#39;%\u0026#39; where user=\u0026#39;root\u0026#39;; Restart the containers and hit http://localhost:8080. You’ll see phpMyAdmin is waiting for you! 🗃️ You would only need to do all these steps only once, your containers will keep running unless stopped manually.\n","date":"June 12, 2022","permalink":"/programming/install-mysql-on-manjaro-linux/","section":"Programming","summary":"The easiest way to install MySQL on Manjaro and Arch Linux machine","title":"Install MySQL On Manjaro Linux"},{"content":"","date":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux"},{"content":"","date":null,"permalink":"/tags/mysql/","section":"Tags","summary":"","title":"Mysql"}]