[{"content":"Tailwind CSS 4 brings modern utility-first styling capabilities to the table, making it a perfect choice for building fast, customizable, and scalable UIs. However, when integrating it with Angular 19, there are some nuances and workarounds you need to be aware of. This guide will walk you through the process step-by-step.\n1. Prerequisites Before starting, ensure you have the following installed on your system:\nNode.js (version 18 or higher recommended) Angular CLI 19 2. Install Tailwind CSS 4 and Dependencies The main command to install Tailwind CSS 4, along with its dependencies, is:\nnpm install tailwindcss @tailwindcss/postcss postcss --force The --force flag is necessary to resolve any potential version conflicts, as Tailwind CSS 4 requires specific versions of its dependencies that might not align with Angular\u0026rsquo;s default setup.\n3. Configure PostCSS Create a .postcssrc.json file in the root of your project. Add the following configuration:\n{ \u0026#34;plugins\u0026#34;: { \u0026#34;@tailwindcss/postcss\u0026#34;: {} } } This configuration ensures that Tailwind CSS is properly integrated with PostCSS, enabling its utilities to be processed during the build.\n4. Workaround for Compatibility Issues Currently, @angular-devkit/build-angular does not officially support Tailwind CSS v4. You must be receiving error like below:\n$ npm install npm error code ERESOLVE npm error ERESOLVE could not resolve npm error npm error While resolving: @angular-devkit/build-angular@19.1.4 npm error Found: tailwindcss@4.0.0 npm error node_modules/tailwindcss npm error tailwindcss@\u0026#34;^4.0.0\u0026#34; from the root project npm error tailwindcss@\u0026#34;4.0.0\u0026#34; from @tailwindcss/node@4.0.0 npm error node_modules/@tailwindcss/node npm error @tailwindcss/node@\u0026#34;^4.0.0\u0026#34; from @tailwindcss/postcss@4.0.0 npm error node_modules/@tailwindcss/postcss npm error @tailwindcss/postcss@\u0026#34;^4.0.0\u0026#34; from the root project npm error 1 more (@tailwindcss/postcss) However, you can override the version constraints to make it work. Open your package.json file and add the following overrides section:\n{ \u0026#34;overrides\u0026#34;: { \u0026#34;@angular-devkit/build-angular\u0026#34;: { \u0026#34;tailwindcss\u0026#34;: \u0026#34;^4.0.0\u0026#34; } } } This tells Angular to use Tailwind CSS v4 despite the lack of direct support, allowing you to leverage its latest features.\nSoon Tailwind 4 be supported for Angular without using --force or above workaround.\n5. Initialize Tailwind CSS Once the installation is complete, initialize Tailwind CSS by creating a tailwind.config.js file in your project root. Run the following command:\nnpx tailwindcss init This generates a basic Tailwind configuration file. You can customize it to suit your project needs. For example, add the paths to your Angular components for purging unused styles:\nmodule.exports = { content: [ \u0026#34;./src/**/*.{html,ts}\u0026#34; ], theme: { extend: {}, }, plugins: [], } 6. Update Angular\u0026rsquo;s Global Styles In the src/styles.css (or src/styles.scss) file of your Angular project, import Tailwind\u0026rsquo;s base, components, and utilities:\n@import \u0026#34;tailwindcss\u0026#34;; 7. Build and Verify Run the Angular development server to verify that Tailwind CSS is working correctly:\nng serve Inspect your application and confirm that Tailwind\u0026rsquo;s classes are applied properly. You can test this by adding some utility classes like bg-blue-500 or text-white to your HTML elements.\nConclusion By following this guide, you can successfully set up and use Tailwind CSS 4 with Angular 19. While there are a few compatibility challenges, the outlined steps provide a clear workaround to ensure smooth integration. With Tailwind\u0026rsquo;s utility-first approach, you can rapidly develop modern and responsive UIs within your Angular applications.\n","permalink":"https://dwij.net/posts/how-to-use-tailwind-4-with-angular-19/","summary":"Tailwind CSS 4 brings modern utility-first styling capabilities to the table, making it a perfect choice for building fast, customizable, and scalable UIs. However, when integrating it with Angular 19, there are some nuances and workarounds you need to be aware of. This guide will walk you through the process step-by-step.\n1. Prerequisites Before starting, ensure you have the following installed on your system:\nNode.js (version 18 or higher recommended) Angular CLI 19 2.","title":"How to Use Tailwind 4 with Angular 19"},{"content":"PostgreSQL is a powerful, open-source relational database management system widely used for building scalable web applications. In this article, we\u0026rsquo;ll walk through the process of installing PostgreSQL on MacOS using Homebrew, a popular package manager for MacOS, and setting up pgAdmin4, a web-based administration tool for PostgreSQL.\nPrerequisites Before we begin, make sure you have Homebrew installed on your MacOS system. If not, you can install it by running the following command in your terminal:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; Step 1: Install PostgreSQL with Homebrew To install PostgreSQL using Homebrew, simply run the following command in your terminal:\nbrew install postgresql@15 This will download and install PostgreSQL along with its dependencies on your MacOS system.\nStep 2: Start and Enable PostgreSQL Service After installing PostgreSQL, you need to start and enable the PostgreSQL service. Run the following commands in your terminal:\nbrew services start postgresql@15 Step 3: Add PostgreSQL in PATH Run sudo nano /etc/paths and add this path on new line.\n/opt/homebrew/opt/postgresql@15/bin Step 4: Create a New PostgreSQL User and Database Start postgres in terminal\npsql postgres Create user postgres as Super User:\nCREATE USER postgres SUPERUSER; Create normal user for project\n# Switch to the postgres system user psql postgres # Create a new PostgreSQL user CREATE USER admin WITH PASSWORD \u0026#39;admin\u0026#39;; # Create a new PostgreSQL database CREATE DATABASE myproject; # Grant all privileges on the database to the new user GRANT ALL PRIVILEGES ON DATABASE myproject TO admin; # Exit psql prompt \\q Step 5: Install pgAdmin4 pgAdmin4 is a web-based administration tool for PostgreSQL. To install pgAdmin4, go to https://www.pgadmin.org/download/pgadmin-4-macos/ and select your suitable version.\nStep 6: Start pgAdmin4 To start pgAdmin4, you can either run it from the Applications folder.\nClick on Add New Server:\nName: LocalPG\nHost name: localhost\nUsername: postgres\nAnd Save.\nYou will get pgAdmin4 up and running!\nConclusion Congratulations! You\u0026rsquo;ve successfully installed PostgreSQL on MacOS using Homebrew and set up pgAdmin4 for administering your PostgreSQL databases. You can now use pgAdmin4 to manage your PostgreSQL databases, run queries, and perform various administrative tasks with ease.\n","permalink":"https://dwij.net/posts/install-postgres-on-macos-with-homebrew-along-with-pgadmin4/","summary":"PostgreSQL is a powerful, open-source relational database management system widely used for building scalable web applications. In this article, we\u0026rsquo;ll walk through the process of installing PostgreSQL on MacOS using Homebrew, a popular package manager for MacOS, and setting up pgAdmin4, a web-based administration tool for PostgreSQL.\nPrerequisites Before we begin, make sure you have Homebrew installed on your MacOS system. If not, you can install it by running the following command in your terminal:","title":"Install Postgres on MacOS with Homebrew along with pgAdmin4"},{"content":"WebSockets provide a full-duplex communication channel over a single TCP connection, enabling real-time communication between clients and servers. In this article, we\u0026rsquo;ll build an anonymous chat server in Golang using the net/http and golang.org/x/net/websocket packages, and we\u0026rsquo;ll style the chat interface using Tailwind CSS.\nPrerequisites Ensure you have Go installed on your system. You can download it from here. Also, make sure you have tailwindcss installed or you can link it via CDN.\nSetting Up the Project Let\u0026rsquo;s start by setting up the project structure and installing the necessary dependencies.\nCreate a new directory for your project and initialize it as a Go module:\nmkdir chat cd chat go mod init chat Next, create the following files within the project directory:\nmain.go: This file will contain the Golang server code. index.html: This file will contain the HTML code for the chat interface. go.mod: This file specifies the module\u0026rsquo;s name and its dependencies. main.go package main import ( \u0026#34;fmt\u0026#34; \u0026#34;io\u0026#34; \u0026#34;net/http\u0026#34; \u0026#34;golang.org/x/net/websocket\u0026#34; ) // Chat Server with connection pool type Server struct { connections map[*websocket.Conn]string } // Create New Server which holds WebSocket connections func NewServer() *Server { return \u0026amp;Server{ connections: make(map[*websocket.Conn]string), } } // Listen on WebSocket\u0026#39;s for messages func (s *Server) listen(name string, chatWS *websocket.Conn) { // Buffer for fetching chat data buffer := make([]byte, 1024) for { // Read connection in buffer dataLength, err := chatWS.Read(buffer) if err != nil { // If WebSocket is terminated if err == io.EOF { break } // Log Read error and continue listening fmt.Println(\u0026#34;Read Error:\u0026#34;, err) continue } msg := string(buffer[:dataLength]) fmt.Println(name+\u0026#34;:\u0026#34;, msg) // Broadcast message msgJson := \u0026#34;{\\\u0026#34;name\\\u0026#34;: \\\u0026#34;\u0026#34; + name + \u0026#34;\\\u0026#34;, \\\u0026#34;message\\\u0026#34;: \\\u0026#34;\u0026#34; + msg + \u0026#34;\\\u0026#34;}\u0026#34; s.broadcast([]byte(msgJson)) } } // Broadcast Message to all users/connections func (s *Server) broadcast(data []byte) { // Loop all connections for ws, name := range s.connections { // Start new process to send message to connection go func(ws *websocket.Conn, name string) { if _, err := ws.Write(data); err != nil { fmt.Println(\u0026#34;Write Error (\u0026#34;+name+\u0026#34;): \u0026#34;, err, \u0026#34; -\u0026gt; closing connection\u0026#34;) // Terminate \u0026amp; Delete connection if closed ws.Close() delete(s.connections, ws) } }(ws, name) } } // Initiate Chat Websocket Connection func (s *Server) handleChatWS(chatWS *websocket.Conn) { // Get Name of User from Parameters urlParams := chatWS.Request().URL.Query() name := urlParams.Get(\u0026#34;name\u0026#34;) fmt.Println(\u0026#34;New Connection: \u0026#34;, name+\u0026#34; (\u0026#34;+chatWS.RemoteAddr().String()+\u0026#34;)\u0026#34;) // Add websocket connection to server pool s.connections[chatWS] = name // Start listening on socket s.listen(name, chatWS) } func main() { server := NewServer() // Load Chat Page http.HandleFunc(\u0026#34;/\u0026#34;, func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, \u0026#34;index.html\u0026#34;) }) // Handle Chat WebSocket\u0026#39;s http.Handle(\u0026#34;/chatWS\u0026#34;, websocket.Handler(server.handleChatWS)) // Start Server fmt.Println(\u0026#34;Server listening on :8000\u0026#34;) http.ListenAndServe(\u0026#34;:8000\u0026#34;, nil) } Run go get which will load golang.org/x/net into the project go.mod.\nindex.html \u0026lt;!doctype html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Golang Tailwind Chat\u0026lt;/title\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34;\u0026gt; \u0026lt;script src=\u0026#34;https://cdn.tailwindcss.com\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body translate=\u0026#34;no\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;max-w-screen-md mx-auto w-full flex flex-col h-screen max-h-screen overflow-hidden\u0026#34; id=\u0026#34;chat\u0026#34;\u0026gt; \u0026lt;div id=\u0026#34;messagesDiv\u0026#34; class=\u0026#34;messages flex-1 overflow-y-scroll border-box\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;inline-flex items-center justify-center w-full\u0026#34;\u0026gt; \u0026lt;hr class=\u0026#34;w-96 h-px my-8 border-0 bg-gray-300\u0026#34;\u0026gt; \u0026lt;span id=\u0026#34;chatTitle\u0026#34; class=\u0026#34;absolute px-3 font-medium text-center -translate-x-1/2 left-1/2 text-grey bg-white\u0026#34;\u0026gt; Golang Tailwind Chat \u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;message-input bg-gray-100 p-4 flex flex-row\u0026#34;\u0026gt; \u0026lt;input id=\u0026#34;chatMsg\u0026#34; class=\u0026#34;flex-1 p-2 rounded\u0026#34; type=\u0026#34;text\u0026#34; placeholder=\u0026#34;Write message and press enter\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;script\u0026gt; // DOM elements var msgField = document.getElementById(\u0026#34;chatMsg\u0026#34;); var messagesDiv = document.getElementById(\u0026#39;messagesDiv\u0026#39;); var chatTitle = document.getElementById(\u0026#39;chatTitle\u0026#39;); // Random Name List var nameList = [ \u0026#39;Time\u0026#39;, \u0026#39;Past\u0026#39;, \u0026#39;Future\u0026#39;, \u0026#39;Dev\u0026#39;, \u0026#39;Fly\u0026#39;, \u0026#39;Flying\u0026#39;, \u0026#39;Soar\u0026#39;, \u0026#39;Soaring\u0026#39;, \u0026#39;Power\u0026#39;, \u0026#39;Falling\u0026#39;, \u0026#39;Fall\u0026#39;, \u0026#39;Jump\u0026#39;, \u0026#39;Cliff\u0026#39;, \u0026#39;Mountain\u0026#39;, \u0026#39;Rend\u0026#39;, \u0026#39;Red\u0026#39;, \u0026#39;Blue\u0026#39;, \u0026#39;Green\u0026#39;, \u0026#39;Yellow\u0026#39;, \u0026#39;Gold\u0026#39;, \u0026#39;Demon\u0026#39;, \u0026#39;Demonic\u0026#39;, \u0026#39;Panda\u0026#39;, \u0026#39;Cat\u0026#39;, \u0026#39;Kitty\u0026#39;, \u0026#39;Kitten\u0026#39;, \u0026#39;Zero\u0026#39;, \u0026#39;Memory\u0026#39;, \u0026#39;Trooper\u0026#39;, \u0026#39;XX\u0026#39;, \u0026#39;Bandit\u0026#39;, \u0026#39;Fear\u0026#39;, \u0026#39;Light\u0026#39;, \u0026#39;Glow\u0026#39;, \u0026#39;Tread\u0026#39;, \u0026#39;Deep\u0026#39;, \u0026#39;Deeper\u0026#39;, \u0026#39;Deepest\u0026#39;, \u0026#39;Mine\u0026#39;, \u0026#39;Your\u0026#39;, \u0026#39;Worst\u0026#39;, \u0026#39;Enemy\u0026#39;, \u0026#39;Hostile\u0026#39;, \u0026#39;Force\u0026#39;, \u0026#39;Video\u0026#39;, \u0026#39;Game\u0026#39;, \u0026#39;Donkey\u0026#39;, \u0026#39;Mule\u0026#39;, \u0026#39;Colt\u0026#39;, \u0026#39;Cult\u0026#39;, \u0026#39;Cultist\u0026#39;, \u0026#39;Magnum\u0026#39;, \u0026#39;Gun\u0026#39;, \u0026#39;Assault\u0026#39;, \u0026#39;Recon\u0026#39;, \u0026#39;Trap\u0026#39;, \u0026#39;Trapper\u0026#39;, \u0026#39;Redeem\u0026#39;, \u0026#39;Code\u0026#39;, \u0026#39;Script\u0026#39;, \u0026#39;Writer\u0026#39;, \u0026#39;Near\u0026#39;, \u0026#39;Close\u0026#39;, \u0026#39;Open\u0026#39;, \u0026#39;Cube\u0026#39;, \u0026#39;Circle\u0026#39;, \u0026#39;Geo\u0026#39;, \u0026#39;Genome\u0026#39;, \u0026#39;Germ\u0026#39;, \u0026#39;Spaz\u0026#39;, \u0026#39;Shot\u0026#39;, \u0026#39;Echo\u0026#39;, \u0026#39;Beta\u0026#39;, \u0026#39;Alpha\u0026#39;, \u0026#39;Gamma\u0026#39;, \u0026#39;Omega\u0026#39;, \u0026#39;Seal\u0026#39;, \u0026#39;Squid\u0026#39;, \u0026#39;Money\u0026#39;, \u0026#39;Cash\u0026#39;, \u0026#39;Lord\u0026#39;, \u0026#39;King\u0026#39;, \u0026#39;Duke\u0026#39;, \u0026#39;Rest\u0026#39;, \u0026#39;Fire\u0026#39;, \u0026#39;Flame\u0026#39;, \u0026#39;Morrow\u0026#39;, \u0026#39;Break\u0026#39;, \u0026#39;Breaker\u0026#39;, \u0026#39;Numb\u0026#39;, \u0026#39;Ice\u0026#39;, \u0026#39;Cold\u0026#39;, \u0026#39;Rotten\u0026#39;, \u0026#39;Sick\u0026#39;, \u0026#39;Sickly\u0026#39;, \u0026#39;Janitor\u0026#39;, \u0026#39;Camel\u0026#39;, \u0026#39;Rooster\u0026#39;, \u0026#39;Sand\u0026#39;, \u0026#39;Desert\u0026#39;, \u0026#39;Dessert\u0026#39;, \u0026#39;Hurdle\u0026#39;, \u0026#39;Racer\u0026#39;, \u0026#39;Eraser\u0026#39;, \u0026#39;Erase\u0026#39;, \u0026#39;Big\u0026#39;, \u0026#39;Small\u0026#39;, \u0026#39;Short\u0026#39;, \u0026#39;Tall\u0026#39;, \u0026#39;Sith\u0026#39;, \u0026#39;Bounty\u0026#39;, \u0026#39;Hunter\u0026#39;, \u0026#39;Cracked\u0026#39;, \u0026#39;Broken\u0026#39;, \u0026#39;Sad\u0026#39;, \u0026#39;Happy\u0026#39;, \u0026#39;Joy\u0026#39;, \u0026#39;Joyful\u0026#39;, \u0026#39;Crimson\u0026#39;, \u0026#39;Destiny\u0026#39;, \u0026#39;Deceit\u0026#39;, \u0026#39;Lies\u0026#39;, \u0026#39;Lie\u0026#39;, \u0026#39;Honest\u0026#39;, \u0026#39;Destined\u0026#39;, \u0026#39;Bloxxer\u0026#39;, \u0026#39;Hawk\u0026#39;, \u0026#39;Eagle\u0026#39;, \u0026#39;Hawker\u0026#39;, \u0026#39;Walker\u0026#39;, \u0026#39;Zombie\u0026#39;, \u0026#39;Sarge\u0026#39;, \u0026#39;Capt\u0026#39;, \u0026#39;Captain\u0026#39;, \u0026#39;Punch\u0026#39;, \u0026#39;One\u0026#39;, \u0026#39;Two\u0026#39;, \u0026#39;Uno\u0026#39;, \u0026#39;Slice\u0026#39;, \u0026#39;Slash\u0026#39;, \u0026#39;Melt\u0026#39;, \u0026#39;Melted\u0026#39;, \u0026#39;Melting\u0026#39;, \u0026#39;Fell\u0026#39;, \u0026#39;Wolf\u0026#39;, \u0026#39;Hound\u0026#39;, \u0026#39;Legacy\u0026#39;, \u0026#39;Sharp\u0026#39;, \u0026#39;Dead\u0026#39;, \u0026#39;Mew\u0026#39;, \u0026#39;Chuckle\u0026#39;, \u0026#39;Bubba\u0026#39;, \u0026#39;Bubble\u0026#39;, \u0026#39;Sandwich\u0026#39;, \u0026#39;Smasher\u0026#39;, \u0026#39;Extreme\u0026#39;, \u0026#39;Multi\u0026#39;, \u0026#39;Universe\u0026#39;, \u0026#39;Ultimate\u0026#39;, \u0026#39;Death\u0026#39;, \u0026#39;Ready\u0026#39;, \u0026#39;Monkey\u0026#39;, \u0026#39;Elevator\u0026#39;, \u0026#39;Wrench\u0026#39;, \u0026#39;Grease\u0026#39;, \u0026#39;Head\u0026#39;, \u0026#39;Theme\u0026#39;, \u0026#39;Grand\u0026#39;, \u0026#39;Cool\u0026#39;, \u0026#39;Kid\u0026#39;, \u0026#39;Boy\u0026#39;, \u0026#39;Girl\u0026#39;, \u0026#39;Vortex\u0026#39;, \u0026#39;Paradox\u0026#39; ]; // Get Random Name for chat const name = nameList[Math.floor(Math.random() * nameList.length)]; // Update Chat Title for name chatTitle.innerHTML = \u0026#34;Golang Tailwind Chat (\u0026#34; + name + \u0026#34;)\u0026#34;; document.title = \u0026#34;Chat: \u0026#34; + name; // Initiate WebSocket let socket = new WebSocket(\u0026#34;ws://localhost:8000/chatWS?\u0026#34; + new URLSearchParams({ name: name })); console.log(\u0026#34;Connected to Chat WebSocket as\u0026#34;, name); socket.onmessage = (event) =\u0026gt; { console.log(\u0026#34;Received: \u0026#34;, event.data); data = JSON.parse(event.data); // if message is from sender if (data.name == name) { messagesDiv.innerHTML += \u0026#39;\u0026lt;div class=\u0026#34;message-row flex flex-row-reverse text-white\u0026#34;\u0026gt;\\ \u0026lt;div class=\u0026#34;message m-2 p-4 bg-pink-500 rounded max-w-full inline relative shadow\u0026#34;\u0026gt;\\ \u0026lt;p class=\u0026#34;message-content\u0026#34;\u0026gt;\u0026#39;+ data.message + \u0026#39;\u0026lt;/p\u0026gt;\\ \u0026lt;/div\u0026gt;\\ \u0026lt;/div\u0026gt;\u0026#39;; } else { messagesDiv.innerHTML += \u0026#39;\u0026lt;div class=\u0026#34;message-row flex flex-row\u0026#34;\u0026gt;\\ \u0026lt;div class=\u0026#34;message m-2 p-4 pb-8 bg-gray-100 rounded max-w-full inline relative shadow\u0026#34;\u0026gt;\\ \u0026lt;p class=\u0026#34;message-content\u0026#34;\u0026gt;\u0026#39;+ data.message + \u0026#39;\u0026lt;/p\u0026gt;\\ \u0026lt;div class=\u0026#34;message-name absolute bg-gray-300 px-2 py-1 text-xs rounded-bl rounded-tr left-0 bottom-0\u0026#34;\u0026gt;\u0026#39;+ data.name + \u0026#39;\u0026lt;/div\u0026gt;\\ \u0026lt;/div\u0026gt;\\ \u0026lt;/div\u0026gt;\u0026#39;; } // Scroll to bottom messagesDiv.scrollTop = messagesDiv.scrollHeight; } // Add event listener for keypress to get chat message msgField.addEventListener(\u0026#34;keypress\u0026#34;, function (event) { // Check if the Enter key is pressed if (event.keyCode === 13) { // Retrieve the value of the input field var msg = msgField.value; // Send message to socket socket.send(msg); // Clear the input field (optional) msgField.value = \u0026#34;\u0026#34;; } }); \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Running the Server To run the server, execute the following command in your terminal:\ngo run . This will start the server on port 8000.\nOutput:\n$ go run . Server listening on :8000 New Connection: Camel (http://localhost:8000) New Connection: Duke (http://localhost:8000) Camel: Hello there, I am Camel Duke: Hi, I am Duke Camel: Duke, Isn\u0026#39;t Golang fun ? Duke: Sure it is! Duke: And Tailwind too Accessing the Chat Interface Open your web browser and navigate to http://localhost:8000. You should see the chat interface where you can enter messages and see them displayed in real-time.\nConclusion In this article, we\u0026rsquo;ve built an anonymous chat server in Golang using WebSockets and styled the chat interface using Tailwind CSS. WebSockets provide a powerful mechanism for real-time communication between clients and servers, making them ideal for building chat applications and other real-time systems.\nHappy coding! \u0026#x1f680;\nReferences Build Chat And Data Feed With WebSockets In Golang by Anthony GG https://www.youtube.com/watch?v=JuUAEYLkGbM\u0026t=631s Tailwind Chat - https://codepen.io/brookesb91/details/OJpdqOm ","permalink":"https://dwij.net/posts/websockets-anonymous-chat-server-in-golang-tailwind/","summary":"WebSockets provide a full-duplex communication channel over a single TCP connection, enabling real-time communication between clients and servers. In this article, we\u0026rsquo;ll build an anonymous chat server in Golang using the net/http and golang.org/x/net/websocket packages, and we\u0026rsquo;ll style the chat interface using Tailwind CSS.\nPrerequisites Ensure you have Go installed on your system. You can download it from here. Also, make sure you have tailwindcss installed or you can link it via CDN.","title":"WebSockets: Build Anonymous Chat Server in Golang and Tailwind"},{"content":"IP throttling, also known as rate limiting, is a technique used to control the rate of requests from a client to a server. It helps prevent abuse, ensure fair usage of resources, and protect against denial-of-service attacks. In this article, we\u0026rsquo;ll explore how to implement IP throttling in Go using the net/http package and the golang.org/x/time/rate package, which provides a rate limiter implementation.\nPrerequisites Before we proceed, make sure you have Go installed on your system. You can download and install it from the official Go website.\nSetting up the Project First, create a new directory for your project and initialize it as a Go module:\nmkdir ip-throttling-example cd ip-throttling-example go mod init ip-throttling-example Next, install the golang.org/x/time/rate package:\ngo get golang.org/x/time/rate Implementing IP Throttling Now, let\u0026rsquo;s create a Go program main.go that implements IP throttling using the net/http and golang.org/x/time/rate packages.\nExample Code: package main import ( \u0026#34;fmt\u0026#34; \u0026#34;net\u0026#34; \u0026#34;net/http\u0026#34; \u0026#34;sync\u0026#34; \u0026#34;golang.org/x/time/rate\u0026#34; ) // IPRateLimiter represents an IP rate limiter. type IPRateLimiter struct { ips map[string]*rate.Limiter mu *sync.RWMutex limiter *rate.Limiter } // NewIPRateLimiter creates a new instance of IPRateLimiter with the given rate limit. func NewIPRateLimiter(r rate.Limit, burst int) *IPRateLimiter { return \u0026amp;IPRateLimiter{ ips: make(map[string]*rate.Limiter), mu: \u0026amp;sync.RWMutex{}, limiter: rate.NewLimiter(r, burst), } } // Allow checks if the request from the given IP is allowed. func (lim *IPRateLimiter) Allow(ip string) bool { lim.mu.RLock() rl, exists := lim.ips[ip] lim.mu.RUnlock() if !exists { lim.mu.Lock() rl, exists = lim.ips[ip] if !exists { rl = rate.NewLimiter(lim.limiter.Limit(), lim.limiter.Burst()) lim.ips[ip] = rl } lim.mu.Unlock() } return rl.Allow() } func main() { // Create a new IP rate limiter with a rate limit of 1 request per second and a burst limit of 3 requests. limiter := NewIPRateLimiter(1, 3) // Create an HTTP handler function that applies IP rate limiting. handler := func(w http.ResponseWriter, r *http.Request) { ip, _, _ := net.SplitHostPort(r.RemoteAddr) if !limiter.Allow(ip) { http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) return } fmt.Fprintf(w, \u0026#34;Hello, your request from IP %s is allowed.\\n\u0026#34;, ip) } // Register the handler function with the / endpoint. http.HandleFunc(\u0026#34;/\u0026#34;, handler) // Start the HTTP server on port 8080. fmt.Println(\u0026#34;Server listening on :8080\u0026#34;) http.ListenAndServe(\u0026#34;:8080\u0026#34;, nil) } In this example:\nWe define an IPRateLimiter struct that represents an IP rate limiter. It contains a map of IP addresses to rate limiters (ips), a mutex (mu) to synchronize access to the map, and a default rate limiter (limiter) for IPs that are not in the map. The NewIPRateLimiter function creates a new instance of IPRateLimiter with the given rate limit and burst limit. The Allow method checks if the request from the given IP address is allowed based on the rate limiter associated with that IP. If the IP is not in the map, a new rate limiter is created and added to the map. In the main function, we create a new instance of IPRateLimiter with a rate limit of 1 request per second and a burst limit of 3 requests. We define an HTTP handler function that applies IP rate limiting. It extracts the IP address from the request, checks if the request is allowed using the rate limiter, and responds accordingly. Finally, we register the handler function with the / endpoint and start an HTTP server listening on port 8080. Testing the Implementation To test the implementation, start the Go server by running the following command:\ngo run . Open your web browser and navigate to http://localhost:8080. You should see a message indicating that your request is allowed. If you refresh the page multiple times within a second, you\u0026rsquo;ll eventually receive a 429 Too Many Requests response, indicating that you\u0026rsquo;ve exceeded the rate limit.\nConclusion In this article, we explored how to implement IP throttling, also known as rate limiting, in Go using the net/http package and the golang.org/x/time/rate package. We created a custom IP rate limiter that tracks requests from different IP addresses and applies rate limiting based on the specified rate limit and burst limit. By implementing IP throttling, you can control the rate of requests from clients, prevent abuse, and ensure fair usage of resources in your Go applications.\nThe best way to Fix a problem is to fix it before it\u0026rsquo;s a problem.\nHappy coding! \u0026#x1f680;\n","permalink":"https://dwij.net/posts/ip-throttling-rate-limiting-in-golang-using-net-http-and-time-rate/","summary":"IP throttling, also known as rate limiting, is a technique used to control the rate of requests from a client to a server. It helps prevent abuse, ensure fair usage of resources, and protect against denial-of-service attacks. In this article, we\u0026rsquo;ll explore how to implement IP throttling in Go using the net/http package and the golang.org/x/time/rate package, which provides a rate limiter implementation.\nPrerequisites Before we proceed, make sure you have Go installed on your system.","title":"IP Throttling or Rate Limiting in Golang using net/http and time/rate"},{"content":"To trigger a Jenkins job from a Laravel application using Guzzle, you can use the Jenkins API endpoint to build a job. Follow these steps:\nCreate Jenkins Token In Jenkins, click on your User Name from Top Right Navbar, inside that page go to \u0026ldquo;Configure\u0026rdquo;. There you will see section \u0026ldquo;API Token \u0026ldquo;. Click on \u0026ldquo;Add new token \u0026quot; and create token.\nCopy the generated token and save it for future use.\nEnable Trigger for Jenkins Job Open your Job in Jenkins which you want to trigger.\nOn that page under Build Triggers section, enable checkbox Trigger builds remotely and paste you token name.\nOnce done save the Jenkins Job.\nInstall Guzzle in Laravel Project If you haven\u0026rsquo;t already installed Guzzle, do so using Composer:\ncomposer require guzzlehttp/guzzle Use Guzzle to Trigger Jenkins Job Here is an example of how you can trigger a Jenkins job using Guzzle in a Laravel controller or wherever you need it:\n\u0026lt;?php namespace App\\Http\\Controllers; use GuzzleHttp\\Client; class JenkinsController extends Controller { public function triggerJob() { $JENKINS_USERNAME = \u0026#39;JENKINS_USERNAME\u0026#39;; $JENKINS_TOKEN_NAME = \u0026#39;JENKINS_TOKEN_NAME\u0026#39;; $JENKINS_TOKEN = \u0026#39;JENKINS_TOKEN\u0026#39;; $client = new Client(); $response = $client-\u0026gt;post(\u0026#34;http://YOUR_JENKINS_URL/job/JOB_NAME/build?token=$JENKINS_TOKEN_NAME\u0026amp;cause=deploy-app\u0026#34;, [ \u0026#39;auth\u0026#39; =\u0026gt; [$JENKINS_USERNAME, $JENKINS_TOKEN], ]); // Returns the HTTP status code return $response-\u0026gt;getStatusCode(); } } Make sure to replace the placeholders like YOUR_JENKINS_URL , JOB_NAME , JENKINS_USERNAME , JENKINS_TOKEN_NAME and JENKINS_TOKEN with your actual Jenkins server details.\nJob Trigger Now, you can access the triggerJob method from your routes and run Jenkins Job.\nRemember to secure your Laravel application and Jenkins instance, especially if it\u0026rsquo;s exposed to the internet. This might include using HTTPS, authentication, and access control measures.\nAlso, ensure that your Laravel application has the necessary permissions to make external HTTP requests. If your application is running in a Docker container, make sure it has network access to reach the Jenkins server.\n","permalink":"https://dwij.net/posts/trigger-jenkins-job-from-laravel-via-guzzle-request/","summary":"To trigger a Jenkins job from a Laravel application using Guzzle, you can use the Jenkins API endpoint to build a job. Follow these steps:\nCreate Jenkins Token In Jenkins, click on your User Name from Top Right Navbar, inside that page go to \u0026ldquo;Configure\u0026rdquo;. There you will see section \u0026ldquo;API Token \u0026ldquo;. Click on \u0026ldquo;Add new token \u0026quot; and create token.\nCopy the generated token and save it for future use.","title":"Trigger Jenkins job from Laravel via Guzzle Request"},{"content":"TL;DR: Access Laravel Passport API\u0026rsquo;s from Shell Script using CURL and JQ. This combination can be helpful in running remote Automation Jobs and Builds.\nLaravel Passport is a full OAuth2 server implementation for Laravel. It provides a simple and convenient way to authenticate users and secure API endpoints. In this article, we\u0026rsquo;ll walk through the process of logging in to a Laravel Passport API using a shell script. We\u0026rsquo;ll use curl to make HTTP requests and jq to parse JSON responses.\nPrerequisites Laravel Passport installed on your Laravel project. A valid Client ID and ** Client Secret** generated from Laravel Passport. Basic knowledge of ** shell scripting** , ** curl** , and ** jq**. ** For Passport Setup \u0026amp; Complete Code refer https://github.com/gdbhosale/laravel-passport-api-shell-curl-access**.\nSet up the Shell Script Open your favorite text editor and create a new file named login_script.sh. This will be our shell script file.\nNext, define the variables that we\u0026rsquo;ll be using. These include the base URL of your Laravel project, the client ID, and the client secret.\n#!/bin/bash base_url=\u0026#34;http://your-laravel-app.com\u0026#34; client_id=\u0026#34;your-client-id\u0026#34; client_secret=\u0026#34;your-client-secret\u0026#34; Get Access Token We\u0026rsquo;ll now use curl to request an access token from the Laravel Passport API. This token will be used to authenticate subsequent API requests.\naccess_token=$(curl -s -X POST -H \u0026#34;Accept: application/json\u0026#34; \\ -d \u0026#34;grant_type=password\u0026#34; \\ -d \u0026#34;client_id=$client_id\u0026#34; \\ -d \u0026#34;client_secret=$client_secret\u0026#34; \\ -d \u0026#34;username=your-username\u0026#34; \\ -d \u0026#34;password=your-password\u0026#34; \\ \u0026#34;$base_url/oauth/token\u0026#34; | jq -r \u0026#39;.access_token\u0026#39;) In this command:\n-s to get output silently -X POST specifies that we\u0026rsquo;re making a POST request. -H \u0026quot;Accept: application/json\u0026quot; sets the header to accept JSON responses. -d flags are used to send data in the request body. jq -r '.access_token' extracts the access token from the JSON response. Make API Requests Now that we have the access token, we can use it to make authenticated API requests. For example, let\u0026rsquo;s say we want to retrieve user information:\nuser_info=$(curl -s -X GET -H \u0026#34;Accept: application/json\u0026#34; \\ -H \u0026#34;Authorization: Bearer $access_token\u0026#34; \\ \u0026#34;$base_url/api/user\u0026#34;) Here, we\u0026rsquo;re using the access token obtained in the previous step as part of the request header using -H \u0026quot;Authorization: Bearer $access_token\u0026quot;.\nStep 6: Parse JSON Response Finally, use jq to parse the JSON response and extract the information you need.\nusername=$(echo \u0026#34;$user_info\u0026#34; | jq -r \u0026#39;.name\u0026#39;) email=$(echo \u0026#34;$user_info\u0026#34; | jq -r \u0026#39;.email\u0026#39;) echo \u0026#34;Username: $username\u0026#34; echo \u0026#34;Email: $email\u0026#34; In this example, we\u0026rsquo;re extracting the name and email fields from the JSON response.\nFinal script:\n#!/bin/bash base_url=\u0026#34;http://laravel-passport-api-shell-curl-access.test\u0026#34; client_id=\u0026#34;3\u0026#34; client_secret=\u0026#34;AU43oYLwMrBVMuaxxhG636yMqsJytSaYVrIcikjU\u0026#34; access_token=$(curl -s -X POST -H \u0026#34;Accept: application/json\u0026#34; \\ -d \u0026#34;grant_type=password\u0026#34; \\ -d \u0026#34;client_id=$client_id\u0026#34; \\ -d \u0026#34;client_secret=$client_secret\u0026#34; \\ -d \u0026#34;username=john@example.com\u0026#34; \\ -d \u0026#34;password=secret\u0026#34; \\ \u0026#34;$base_url/oauth/token\u0026#34; | jq -r \u0026#39;.access_token\u0026#39;) user_info=$(curl -s -X GET -H \u0026#34;Accept: application/json\u0026#34; \\ -H \u0026#34;Authorization: Bearer $access_token\u0026#34; \\ \u0026#34;$base_url/api/user\u0026#34;) username=$(echo \u0026#34;$user_info\u0026#34; | jq -r \u0026#39;.name\u0026#39;) email=$(echo \u0026#34;$user_info\u0026#34; | jq -r \u0026#39;.email\u0026#39;) echo \u0026#34;Username: $username\u0026#34; echo \u0026#34;Email: $email\u0026#34; Step 7: Execute the Script Make the script executable by running:\nchmod +x login_script.sh Then, you can execute it:\n./login_script.sh Output:\nUsername: John Doe Email: john@example.com Find complete code on :https://github.com/gdbhosale/laravel-passport-api-shell-curl-access\nConclusion In this article, we\u0026rsquo;ve demonstrated how to log in to a Laravel Passport API using a shell script. We used curl to make HTTP requests and jq to parse JSON responses. This script can serve as a foundation for automating API interactions in your Laravel project. Remember to handle sensitive information, such as client IDs and secrets, with care, and consider using environment variables or other secure methods for storage.\n","permalink":"https://dwij.net/posts/how-to-access-laravel-passport-api-from-shell-script-using-curl-and-jq/","summary":"TL;DR: Access Laravel Passport API\u0026rsquo;s from Shell Script using CURL and JQ. This combination can be helpful in running remote Automation Jobs and Builds.\nLaravel Passport is a full OAuth2 server implementation for Laravel. It provides a simple and convenient way to authenticate users and secure API endpoints. In this article, we\u0026rsquo;ll walk through the process of logging in to a Laravel Passport API using a shell script. We\u0026rsquo;ll use curl to make HTTP requests and jq to parse JSON responses.","title":"How to access Laravel Passport API from Shell Script using CURL and JQ"},{"content":"TL;DR: Web Framework Recommendation for Performance and Ease of coding in 2023. Golang and Express excel in high-performance scenarios, while Laravel, Lumen, and Django prioritise developer experience and feature completeness\nChoosing the right web framework for your project is a critical decision that can significantly impact the success of your project. Developers have a plethora of options available, each with its own set of advantages and trade-offs. In this benchmark comparison, we\u0026rsquo;ll evaluate five popular web frameworks: Golang Fiber, Node.js Express, Laravel, Lumen, and Django. We\u0026rsquo;ll explore various aspects including latency, requests per second, philosophical differences, developer perspective, and coding time to help you make an informed decision.\nLatency and Requests Per Second Latency and requests per second (RPS) are crucial performance metrics for web frameworks as they directly affect the user experience. Lower latency and higher RPS generally indicate better performance and responsiveness.\nAverage Latency Language ** Framework** ** Average Latency (ms) (64)** ** Average Latency (ms) (256)** ** Average Latency (ms) (512)** ** go (1.21)** fiber (2.49) 0.68 ms 1.74 ms 3.32 ms ** javascript (ES2019)** express (4.18) 2.98 ms 11.44 ms 24.03 ms ** php (8.2)** lumen (10.1) 9.63 ms 37.70 ms 76.14 ms ** php (8.2)** laravel (10.21) 24.24 ms 95.24 ms 188.60 ms ** python (3.11)** django (4.2) 18.28 ms 85.30 ms 186.12 ms Requests served / second ** Language** ** Framework** ** Requests / Second (64)** ** Requests / Second (256)** ** Requests / Second (512)** ** go (1.21)** fiber (2.49) 161,336 171,497 173,488 ** javascript (ES2019)** express (4.18) 22,707 22,750 23,110 ** php (8.2)** lumen (10.1) 6,783 6,766 6,694 ** php (8.2)** laravel (10.21) 2,696 2,687 2,690 ** python (3.11)** django (4.2) 1,823 1,839 1,718 Reference: Web Frameworks Benchmark by The Benchmarker\nGolang Fiber: Golang Fiber is known for its impressive performance. Its asynchronous and event-driven architecture helps minimize latency and maximize RPS. In benchmark tests, Fiber consistently demonstrates low latency and high RPS, making it an excellent choice for high-performance applications.\nFiber follows the \u0026ldquo;Express-like\u0026rdquo; minimalist approach, emphasizing performance and simplicity. It encourages developers to write efficient code and provides a solid foundation for building high-performance applications. Developers familiar with Go\u0026rsquo;s syntax and principles will find Fiber straightforward and efficient. It has a growing community and ecosystem of packages, making it an attractive choice for Go enthusiasts.\nFiber\u0026rsquo;s simplicity and performance optimizations can accelerate development. However, developers must be proficient in Go to fully leverage its benefits.\nNode.js Express: Node.js Express is known for its speed and scalability. While its performance is generally good, it may not match the raw speed of Golang Fiber in all scenarios. However, Express is highly optimized and can handle a substantial number of requests with relatively low latency.\nExpress values simplicity and flexibility. It empowers developers to choose libraries and tools that best fit their project\u0026rsquo;s needs. This minimalistic approach often results in faster development but may require more decision-making. Express is popular among JavaScript developers. Its familiarity and extensive community support ensure a wealth of resources and third-party packages. Developers can rapidly build applications with JavaScript across the full stack.\nExpress\u0026rsquo;s minimalist approach allows for rapid development. JavaScript developers can quickly create RESTful APIs and web applications using familiar tools and libraries.\nLaravel: Laravel, a PHP-based framework, offers solid performance but may have higher latency compared to Golang Fiber and Express. It\u0026rsquo;s important to note that PHP, as an interpreted language, can introduce some overhead, but Laravel\u0026rsquo;s extensive caching mechanisms help mitigate this issue.\nLaravel prioritizes developer experience and elegant syntax. It aims to make common tasks easy while maintaining code readability. This framework is suitable for developers who value convention over configuration. Laravel provides an elegant and expressive syntax, making it easy for PHP developers to create robust applications. Its documentation is comprehensive, and the community is active, making it a top choice for PHP projects.\nLaravel\u0026rsquo;s elegant syntax and built-in features can significantly reduce development time. It offers solutions for common tasks like authentication, routing, and database management, streamlining the development process.\nLumen: Lumen, also from the Laravel family, is designed for microservices and APIs. It boasts excellent performance with lower latency compared to its bigger sibling, Laravel. Its minimalist approach results in a faster response time, making it suitable for projects where high performance is essential.\nLumen\u0026rsquo;s philosophy is similar to Laravel but leans more towards microservices and APIs. It provides the essentials for building fast and lightweight applications while allowing developers to leverage Laravel\u0026rsquo;s ecosystem when needed. Lumen\u0026rsquo;s developer experience is similar to Laravel, but it\u0026rsquo;s more streamlined for microservices and API development. Developers transitioning from Laravel will find it easy to adapt to Lumen.\nLumen inherits many of Laravel\u0026rsquo;s developer-friendly features, enabling faster microservices and API development. Developers with Laravel experience will find Lumen\u0026rsquo;s learning curve minimal.\nDjango: Django, built on Python, is known for its robustness and versatility rather than raw speed. While it may not offer the same level of performance as Golang Fiber or Express, it compensates with an array of features and tools for developers to optimize their applications.\nDjango adheres to the \u0026ldquo;batteries-included\u0026rdquo; philosophy, offering a comprehensive set of tools and libraries out of the box. It emphasizes DRY (Don\u0026rsquo;t Repeat Yourself) principles and encourages best practices. Django is an excellent choice for projects that require extensive features and security. Django is well-loved by Python developers for its clean and organized code structure. Its extensive documentation and built-in admin interface simplify development. Python developers will appreciate its readability and maintainability.\nWhile Django may involve more upfront setup due to its comprehensive feature set, it ultimately accelerates development by handling many common tasks. Its admin interface, in particular, saves significant development time.\nConclusion The choice of a web framework ultimately depends on your project\u0026rsquo;s specific needs and your team\u0026rsquo;s familiarity with the language and ecosystem. Golang Fiber and Node.js Express excel in high-performance scenarios, while Laravel, Lumen, and Django prioritize developer experience and feature completeness.\nConsider your performance requirements, development philosophy, and team expertise when making your decision. Ultimately, all these frameworks are capable of delivering robust web applications, so choose the one that aligns best with your project goals.\nFind more golang related articles on https://GolanGuru.com.\n","permalink":"https://dwij.net/posts/next-best-web-framework-benchmark-golang-fiber-node-js-express-laravel-lumen-and-django/","summary":"TL;DR: Web Framework Recommendation for Performance and Ease of coding in 2023. Golang and Express excel in high-performance scenarios, while Laravel, Lumen, and Django prioritise developer experience and feature completeness\nChoosing the right web framework for your project is a critical decision that can significantly impact the success of your project. Developers have a plethora of options available, each with its own set of advantages and trade-offs. In this benchmark comparison, we\u0026rsquo;ll evaluate five popular web frameworks: Golang Fiber, Node.","title":"Next Best Web Framework: Benchmark Comparison for Golang Fiber, Node.js Express, Laravel, Lumen, and Django"},{"content":"Error handling is an essential aspect of robust software development. In Go, error handling is straightforward yet powerful, thanks to the built-in panic and recover mechanisms, along with the ability to wrap errors for improved context. In this article, we\u0026rsquo;ll explore these error handling strategies in detail, along with comprehensive code examples.\n1. Panic and Recover What is Panic? panic is a built-in function in Go that stops the ordinary flow of control and begins panicking. When the function panic() is called, the execution of the current function is stopped immediately, and the control passes to its deferred functions. If no deferred functions are present or if none of them recover from the panic, the program terminates.\nWhat is Recover? recover is another built-in function in Go that is used to regain control of a panicking goroutine. It\u0026rsquo;s only useful inside deferred functions. When called inside a deferred function, recover stops the panic and returns the value passed to the panic function. If the goroutine is not panicking, recover returns nil.\nExample: package main import \u0026#34;fmt\u0026#34; func recoverFromPanic() { if r := recover(); r != nil { fmt.Println(\u0026#34;Recovered from panic:\u0026#34;, r) } } func example() { defer recoverFromPanic() fmt.Println(\u0026#34;Starting the example function\u0026#34;) panic(\u0026#34;Oops! Something went wrong!\u0026#34;) fmt.Println(\u0026#34;This line will not be executed\u0026#34;) } func main() { example() fmt.Println(\u0026#34;Continuing after panic\u0026#34;) } Output\n$ go run error-handling.go Starting the example function Recovered from panic: Oops! Something went wrong! Continuing after panic In this example:\nThe example function defers the recoverFromPanic function, which will be executed when a panic occurs. When panic(\u0026quot;Oops! Something went wrong!\u0026quot;) is encountered, the execution of the example function is immediately stopped. The control passes to the deferred recoverFromPanic function, which prints the message \u0026ldquo;Recovered from panic\u0026rdquo; along with the panic value. The program continues to execute after the panic is recovered. 2. Error Wrapping What is Error Wrapping? Error wrapping is a technique used to provide additional context to errors by adding more information to them. It allows you to attach context to an error without losing the original error message.\nExample: package main import ( \u0026#34;errors\u0026#34; \u0026#34;fmt\u0026#34; ) func main() { // Original error err := errors.New(\u0026#34;something went wrong\u0026#34;) // Wrap the error with additional context wrappedErr := fmt.Errorf(\u0026#34;additional context: %w\u0026#34;, err) // Print the wrapped error fmt.Println(wrappedErr) // Unwrap the wrapped error to get the original error originalErr := errors.Unwrap(wrappedErr) fmt.Println(originalErr) // Check if the error contains the original error if errors.Is(wrappedErr, err) { fmt.Println(\u0026#34;The wrapped error contains the original error\u0026#34;) } } Output:\n$ go run error-handling-2.go additional context: something went wrong something went wrong The wrapped error contains the original error In this example:\nWe create an original error using errors.New(\u0026quot;something went wrong\u0026quot;). We wrap the original error with additional context using fmt.Errorf(\u0026quot;additional context: %w\u0026quot;, err). The %w verb is used to wrap the original error. It allows us to attach the original error to the wrapped error. We print the wrapped error and then unwrap it using errors.Unwrap to obtain the original error. Finally, we check if the wrapped error contains the original error using errors.Is. Conclusion In Go, error handling strategies such as panic, recover, and error wrapping provide developers with powerful tools to handle exceptional situations gracefully. By understanding these mechanisms and incorporating them into your codebase, you can write more robust and reliable Go applications that gracefully handle errors and provide valuable context when things go wrong.\nDebugging is like being the detective in a crime movie where you are also the murderer.\nHappy coding! \u0026#x1f680;\n","permalink":"https://dwij.net/posts/error-handling-strategies-in-golang-panic-recover-wrapping/","summary":"Error handling is an essential aspect of robust software development. In Go, error handling is straightforward yet powerful, thanks to the built-in panic and recover mechanisms, along with the ability to wrap errors for improved context. In this article, we\u0026rsquo;ll explore these error handling strategies in detail, along with comprehensive code examples.\n1. Panic and Recover What is Panic? panic is a built-in function in Go that stops the ordinary flow of control and begins panicking.","title":"Error Handling Strategies in Go: Panic, Recover, and Error Wrapping"},{"content":"Enable GZIP Compression on Ubuntu + Apache\nGZIP compression is a technique used to reduce the size of files transmitted over the internet, which can significantly improve website performance and decrease load times. In this article, we will guide you through the steps to enable GZIP compression on an Ubuntu server with Apache.\nStep 1: Check if mod_deflate is installed\nBefore proceeding, we need to check if the mod_deflate module is installed and enabled in Apache. To do this, open a terminal and enter the following command:\napache2ctl -t -D DUMP_MODULES | grep deflate If the output shows deflate_module (shared), it means the module is already installed and enabled. If not, proceed to Step 2.\nStep 2: Enable the mod_deflate module\nIf the mod_deflate module is not installed, we need to enable it. To do this, use the following command:\nsudo a2enmod deflate Step 3: Edit the Apache configuration\nNext, we need to configure Apache to enable GZIP compression for specific file types. Open the Apache configuration file using a text editor. In this example, we\u0026rsquo;ll use nano, but you can use your preferred text editor:\nsudo nano /etc/apache2/apache2.conf Inside the apache2.conf file, add the following lines to enable GZIP compression for various file types:\n\u0026lt;IfModule mod_deflate.c\u0026gt; AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE text/javascript AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/json \u0026lt;/IfModule\u0026gt; The above configuration tells Apache to compress specific MIME types, such as HTML, CSS, JavaScript, and JSON, before sending them to the client.\nAfter adding the GZIP compression settings, save the changes and exit the text editor. In nano, you can do this by pressing Ctrl + X, then Y, and finally Enter.\nStep 4: Restart Apache\nTo apply the changes, you need to restart the Apache web server. Use the following command:\nsudo service apache2 restart Conclusion\nEnabling GZIP compression on your Ubuntu server with Apache can significantly improve website performance by reducing file sizes and speeding up page load times. By following the steps outlined in this article, you\u0026rsquo;ll be able to enable GZIP compression and optimize your web server for a better user experience. Enjoy the improved performance of your website!\n","permalink":"https://dwij.net/posts/enable-gzip-compression-on-ubuntu-apache/","summary":"Enable GZIP Compression on Ubuntu + Apache\nGZIP compression is a technique used to reduce the size of files transmitted over the internet, which can significantly improve website performance and decrease load times. In this article, we will guide you through the steps to enable GZIP compression on an Ubuntu server with Apache.\nStep 1: Check if mod_deflate is installed\nBefore proceeding, we need to check if the mod_deflate module is installed and enabled in Apache.","title":"Enable GZIP Compression on Ubuntu + Apache"},{"content":"Node Version Manager (NVM) is normally installed for a single user, typically under ~/.nvm. This works well for development environments, but it can become problematic on servers where multiple users or services need Node.js.\nFor example, on an Ubuntu server you may need Node.js for:\nJenkins CI/CD jobs Supervisor-managed applications www-data Deployment scripts Regular SSH users A better approach for this scenario is to install NVM in a shared system location such as /opt/nvm.\nThis article explains how to install NVM system-wide while keeping the installation owned by root.\n1. Install Prerequisites Update the package list and install the required tools:\nsudo apt update sudo apt install -y curl git 2. Create a Shared NVM Directory Instead of installing NVM under /root/.nvm, create a shared directory:\nsudo mkdir -p /opt/nvm Set the directory ownership and permissions:\nsudo chown -R root:root /opt/nvm sudo chmod -R 755 /opt/nvm The important part here is that NVM remains owned by root.\nOther users should be able to read and execute Node.js, but they should not be able to modify the installation.\n3. Install NVM in /opt/nvm Set the NVM_DIR environment variable:\nexport NVM_DIR=/opt/nvm Then install NVM:\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | NVM_DIR=/opt/nvm bash Verify the installation:\nls -la /opt/nvm You should see files such as:\nnvm.sh bash_completion versions/ 4. Load NVM NVM is a shell function, so it needs to be loaded before using the nvm command.\nRun:\nexport NVM_DIR=/opt/nvm source /opt/nvm/nvm.sh Verify:\nnvm --version 5. Install Node.js Install the required Node.js version.\nFor example:\nnvm install 20 Set it as the default version:\nnvm alias default 20 Verify:\nnode -v npm -v Example:\nv20.20.2 10.x.x The actual npm version will depend on the Node.js release.\n6. Make NVM Available to Login Shells Create a global profile file:\nsudo nano /etc/profile.d/nvm.sh Add:\nexport NVM_DIR=\u0026#34;/opt/nvm\u0026#34; if [ -s \u0026#34;$NVM_DIR/nvm.sh\u0026#34; ]; then . \u0026#34;$NVM_DIR/nvm.sh\u0026#34; fi Set appropriate permissions:\nsudo chmod 644 /etc/profile.d/nvm.sh Now load the configuration:\nsource /etc/profile.d/nvm.sh Verify:\nnvm --version node -v npm -v New login sessions will automatically load this configuration.\n7. Allow Other Users to Execute Node.js Make sure the shared NVM directory is readable and executable:\nsudo chown -R root:root /opt/nvm sudo chmod -R a+rX /opt/nvm This allows users such as:\nroot jenkins www-data to execute the installed Node.js binaries.\nIt does not give them permission to modify the NVM installation.\n8. Verify Node.js as Jenkins Jenkins jobs usually run in a non-interactive shell.\nTherefore, you should not assume that /etc/profile.d/nvm.sh will always be loaded.\nTest Node.js explicitly as the Jenkins user:\nsudo -u jenkins bash -c \u0026#39; export NVM_DIR=/opt/nvm source \u0026#34;$NVM_DIR/nvm.sh\u0026#34; node -v npm -v \u0026#39; If everything is configured correctly, you should see the installed Node.js and npm versions.\n9. Use NVM in Jenkins Jobs For Jenkins shell scripts, explicitly load NVM:\nexport NVM_DIR=\u0026#34;/opt/nvm\u0026#34; source \u0026#34;$NVM_DIR/nvm.sh\u0026#34; node -v npm -v For example:\n#!/bin/bash set -e export NVM_DIR=\u0026#34;/opt/nvm\u0026#34; source \u0026#34;$NVM_DIR/nvm.sh\u0026#34; echo \u0026#34;Node version:\u0026#34; node -v echo \u0026#34;NPM version:\u0026#34; npm -v npm install npm run build This is more reliable than assuming Jenkins will load a user\u0026rsquo;s normal shell profile.\n10. Using Node.js with Supervisor Supervisor also does not automatically load NVM.\nTherefore, avoid configurations such as:\ncommand=node server.js because node may not exist in Supervisor\u0026rsquo;s PATH.\nInstead, use the absolute path to the Node.js binary.\nFirst find the installed Node.js path:\nsource /opt/nvm/nvm.sh which node For example:\n/opt/nvm/versions/node/v20.20.2/bin/node Then configure Supervisor:\n[program:my-node-app] directory=/var/www/my-node-app command=/opt/nvm/versions/node/v20.20.2/bin/node server.js user=www-data autostart=true autorestart=true stdout_logfile=/var/log/supervisor/my-node-app.log stderr_logfile=/var/log/supervisor/my-node-app-error.log You can also explicitly configure the PATH:\nenvironment=NODE_ENV=\u0026#34;production\u0026#34;,PATH=\u0026#34;/opt/nvm/versions/node/v20.20.2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\u0026#34; A complete example:\n[program:my-node-app] directory=/var/www/my-node-app command=/opt/nvm/versions/node/v20.20.2/bin/node server.js user=www-data environment=NODE_ENV=\u0026#34;production\u0026#34;,PATH=\u0026#34;/opt/nvm/versions/node/v20.20.2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\u0026#34; autostart=true autorestart=true stdout_logfile=/var/log/supervisor/my-node-app.log stderr_logfile=/var/log/supervisor/my-node-app-error.log After changing the Supervisor configuration:\nsudo supervisorctl reread sudo supervisorctl update sudo supervisorctl restart my-node-app 11. Verify Node.js as www-data If your Supervisor application runs as www-data, verify that user can execute Node.js:\nsudo -u www-data bash -c \u0026#39; export NVM_DIR=/opt/nvm source \u0026#34;$NVM_DIR/nvm.sh\u0026#34; node -v npm -v \u0026#39; If this works, the www-data user can access the shared Node.js installation.\n12. Recommended Permissions The recommended ownership is:\n/opt/nvm └── root:root with read/execute access for other users.\nCheck:\nls -ld /opt/nvm A typical result would be:\ndrwxr-xr-x root root /opt/nvm Avoid doing this:\nsudo chown -R jenkins:jenkins /opt/nvm because it makes Jenkins the owner of the system-wide Node.js installation.\nJenkins should generally only need permission to execute Node.js.\n13. NVM Architecture With this configuration, the server has a single shared NVM installation:\nUbuntu Server │ ▼ /opt/nvm │ ┌───────────┴───────────┐ │ │ ▼ ▼ Node.js 20.x NVM scripts │ ┌───────┼────────┐ │ │ │ ▼ ▼ ▼ Jenkins www-data Users │ │ ▼ ▼ CI/CD Supervisor Interactive users can load NVM through:\n/etc/profile.d/nvm.sh Jenkins can explicitly load:\nsource /opt/nvm/nvm.sh Supervisor can directly execute:\n/opt/nvm/versions/node/\u0026lt;version\u0026gt;/bin/node 14. Why Use /opt/nvm Instead of /root/.nvm? A normal NVM installation looks like:\n/root/.nvm This is appropriate when only the root user needs Node.js.\nHowever, Jenkins and Supervisor are normally running under different users:\nroot jenkins www-data They cannot reliably use:\n/root/.nvm A shared installation:\n/opt/nvm provides a common Node.js installation while still allowing each service to run under its own Linux user.\n15. Important: NVM Is Not Really a System Service NVM is primarily a shell-based version manager.\nTherefore:\nnvm use 20 only affects the current shell environment.\nIt does not globally change the node binary for every process on the server.\nThis is particularly important for:\nJenkins Supervisor systemd cron deployment scripts For these services, explicitly configure the Node.js environment or use the absolute path to the Node.js binary.\n16. Recommended Setup for Jenkins + Supervisor For a production Ubuntu server, the following setup is simple and predictable:\nNVM /opt/nvm Ownership root:root Interactive shells /etc/profile.d/nvm.sh Jenkins export NVM_DIR=/opt/nvm source \u0026#34;$NVM_DIR/nvm.sh\u0026#34; Supervisor command=/opt/nvm/versions/node/v20.20.2/bin/node server.js Security Do not give Jenkins write access to:\n/opt/nvm Only give it the permissions required to execute Node.js.\nFinal Verification Run all of the following:\nnode -v sudo -u jenkins bash -c \u0026#39; export NVM_DIR=/opt/nvm source \u0026#34;$NVM_DIR/nvm.sh\u0026#34; node -v \u0026#39; sudo -u www-data bash -c \u0026#39; export NVM_DIR=/opt/nvm source \u0026#34;$NVM_DIR/nvm.sh\u0026#34; node -v \u0026#39; And finally verify the Supervisor application:\nsudo supervisorctl status If all three users can execute Node.js, you have a proper shared NVM installation suitable for a server running Jenkins + Supervisor + Node.js applications.\n","permalink":"https://dwij.net/posts/install-nvm-node-for-all-users-including-jenkins/","summary":"Node Version Manager (NVM) is normally installed for a single user, typically under ~/.nvm. This works well for development environments, but it can become problematic on servers where multiple users or services need Node.js.\nFor example, on an Ubuntu server you may need Node.js for:\nJenkins CI/CD jobs Supervisor-managed applications www-data Deployment scripts Regular SSH users A better approach for this scenario is to install NVM in a shared system location such as /opt/nvm.","title":"Install NVM / Node for all users including Jenkins"},{"content":"In Go, interfaces play a significant role in achieving polymorphism and abstraction. They define a set of method signatures that a type must implement to satisfy the interface contract. In this article, we\u0026rsquo;ll delve into what interfaces are, why they are useful, explore the concept of empty interfaces, and discuss some common useful interfaces in Go, accompanied by comprehensive code examples.\nWhat is an Interface in Go? An interface in Go is a type that specifies a set of method signatures. It serves as a contract that a type implicitly satisfies if it implements all the methods declared by that interface. Interfaces provide a way to achieve polymorphism and abstraction, enabling different types to be used interchangeably.\nInterface Example: package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; \u0026#34;reflect\u0026#34; ) // Define an interface named Shape type Shape interface { Area() float64 Perimeter() float64 } // Define a struct named Rectangle type Rectangle struct { Width float64 Height float64 } // Define a struct named Circle type Circle struct { Radius float64 } // Implement the Shape interface for Rectangle func (r Rectangle) Area() float64 { return r.Width * r.Height } func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) } // Stringer Interface: Override Default String Method of Struct for Rectangle func (r Rectangle) String() string { return reflect.TypeOf(r).String() + fmt.Sprintf(\u0026#34;{ Width: %f, Height: %f }\u0026#34;, r.Width, r.Height) } // Implement the Shape interface for Circle func (r Circle) Area() float64 { return math.Pi * r.Radius * r.Radius } func (r Circle) Perimeter() float64 { return 2 * math.Pi * r.Radius } // Stringer Interface: Override Default String Method of Struct for Circle func (c Circle) String() string { return reflect.TypeOf(c).String() + fmt.Sprintf(\u0026#34;{ Radius: %f }\u0026#34;, c.Radius) } // Method with interface type as an argument func Measure(shape Shape) { fmt.Println(\u0026#34;\\n------------------\u0026#34;) fmt.Println(\u0026#34;Shape:\u0026#34;, shape) fmt.Println(\u0026#34;Area:\u0026#34;, shape.Area()) fmt.Println(\u0026#34;Perimeter:\u0026#34;, shape.Perimeter()) fmt.Println(\u0026#34;------------------\u0026#34;) } func main() { // Create a Rectangle instance rectangle := Rectangle{Width: 5, Height: 3} circle := Circle{Radius: 5} // Call methods defined by the Shape interface fmt.Println(\u0026#34;Rectangle Area:\u0026#34;, rectangle.Area()) // Pass objects who implements Shape Interface Measure(rectangle) Measure(circle) } In this example, the Shape interface defines two methods: Area() and Perimeter(). The Rectangle and Circle struct implements these methods, satisfying the interface contract. Creating Measure method with interface type as an argument will process given shape implementation.\nOutput:\n$ go run interface.go Rectangle Area: 15 ------------------ Shape: main.Rectangle{ Width: 5.000000, Height: 3.000000 } Area: 15 Perimeter: 16 ------------------ ------------------ Shape: main.Circle{ Radius: 5.000000 } Area: 78.53981633974483 Perimeter: 31.41592653589793 ------------------ Why are Interfaces Useful? Interfaces offer several advantages in Go, making them a fundamental feature of the language. Some key benefits of using interfaces include:\nFlexibility: Interfaces allow code to be more flexible by enabling different types to satisfy the same interface contract, promoting code reuse and extensibility. Polymorphism: Interfaces enable polymorphic behavior, allowing different types to be treated uniformly, leading to cleaner and more modular code. Abstraction: Interfaces provide a level of abstraction, allowing users to work with types without needing to know their specific implementations. Empty Interface An empty interface in Go is an interface with zero methods. It serves as a type that can hold values of any type. While powerful, the use of empty interfaces should be judicious, as it can lead to loss of type safety.\nExample: package main import \u0026#34;fmt\u0026#34; // Function accepting an empty interface as an argument func describe(i interface{}) { fmt.Printf(\u0026#34;Type: %T, Value: %v\\n\u0026#34;, i, i) } func main() { // Usage of empty interface describe(42) describe(\u0026#34;hello\u0026#34;) describe(true) } Output:\n$ go run interface.go Type: int, Value: 42 Type: string, Value: hello Type: bool, Value: true In this example, the describe() function accepts an empty interface as an argument, allowing it to accept values of any type.\nCommon Useful Interfaces Go provides several common useful interfaces that are widely used in various scenarios. Some of the most common ones include:\n1. Stringer Interface: The Stringer interface defines the String() method, which returns a human-readable string representation of an object.\nExample: type Stringer interface { String() string } We have already used this by creating String() implementation on Rectangle and Circle.\n2. Reader and Writer Interfaces: The Reader and Writer interfaces define methods for reading from and writing to data streams, respectively.\nExample: type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } 3. Error Interface: The error interface is a built-in interface that defines the Error() method, which returns an error message.\nExample: type error interface { Error() string } Code example: package main import ( \u0026#34;errors\u0026#34; \u0026#34;fmt\u0026#34; ) // CustomError is a custom error type that implements the Error() method type CustomError struct { message string } // Error returns the error message for CustomError func (ce CustomError) Error() string { return ce.message } // MethodThatMayFail simulates a function that may return an error func MethodThatMayFail(input int) (int, error) { if input \u0026lt; 0 { return 0, CustomError{\u0026#34;Input should be a non-negative number\u0026#34;} } return input * 2, nil } func main() { // Call MethodThatMayFail with a valid input result, err := MethodThatMayFail(5) if err != nil { fmt.Println(\u0026#34;Error:\u0026#34;, err) } else { fmt.Println(\u0026#34;Result:\u0026#34;, result) } // Call MethodThatMayFail with an invalid input result, err = MethodThatMayFail(-1) if err != nil { fmt.Println(\u0026#34;Error:\u0026#34;, err) } else { fmt.Println(\u0026#34;Result:\u0026#34;, result) } } Output:\n$ go run interface.go Result: 10 Error: Input should be a non-negative number In this example:\nWe define a custom error type CustomError that implements the Error() method of the error interface. The MethodThatMayFail function simulates a function that may return an error. If the input is negative, it returns a CustomError instance with an error message. Otherwise, it returns the doubled input along with nil. In the main function, we call MethodThatMayFail twice - once with a valid input (5) and once with an invalid input (-1). When an error occurs, the function returns the error message encapsulated in a CustomError instance. We handle these errors by checking if the err variable is nil. If it\u0026rsquo;s not nil, we print the error message using fmt.Println. Otherwise, we print the result. Conclusion Interfaces are a powerful feature in Go that enable polymorphism, abstraction, and flexibility in code. By defining a set of method signatures, interfaces allow different types to satisfy the same interface contract, promoting code reuse and modularity. Understanding interfaces and how to use them effectively is essential for writing clean, maintainable, and scalable Go code. With the examples provided in this article, you should now have a solid understanding of interfaces in Go and how to leverage them in your own code.\nSimplicity is the soul to efficiency.\nHappy coding! \u0026#x1f680;\n","permalink":"https://dwij.net/posts/interfaces-in-golang-a-comprehensive-guide/","summary":"In Go, interfaces play a significant role in achieving polymorphism and abstraction. They define a set of method signatures that a type must implement to satisfy the interface contract. In this article, we\u0026rsquo;ll delve into what interfaces are, why they are useful, explore the concept of empty interfaces, and discuss some common useful interfaces in Go, accompanied by comprehensive code examples.\nWhat is an Interface in Go? An interface in Go is a type that specifies a set of method signatures.","title":"Interfaces in Golang: A Comprehensive Guide"},{"content":"Jenkins normally runs shell commands as the jenkins user. However, some server-maintenance tasks require root privileges—for example, cleaning APT caches, removing old packages, or managing system files.\nA common but unsafe approach is to give the Jenkins user unrestricted sudo access:\njenkins ALL=(ALL) NOPASSWD: ALL This allows Jenkins jobs to execute arbitrary commands as root.\nA much safer approach is to allow Jenkins to run only one specific maintenance script as root.\nApproach: Allow Jenkins to Run Only One Script with sudo In this example, we will create a cleanup script at:\n/var/www/cleanup.sh Jenkins will be allowed to execute this script as root, but it will not receive general root access.\n1. Create the Root-Level Shell Script Create the script on the server:\nsudo nano /var/www/cleanup.sh For example:\n#!/bin/bash set -e echo \u0026#34;Running cleanup as:\u0026#34; whoami echo \u0026#34;------------ Check space before cleanup\u0026#34; df -h echo \u0026#34;------------ Clean the APT Cache\u0026#34; du -sh /var/cache/apt/archives apt-get clean du -sh /var/cache/apt/archives echo \u0026#34;------------ Remove Old Kernels\u0026#34; apt-get autoremove -y echo \u0026#34;------------ Check space after cleanup\u0026#34; df -h The important part is that the script contains the commands that require root privileges.\n2. Set the Script Ownership and Permissions Make the script executable:\nsudo chmod 750 /var/www/cleanup.sh Set the owner to root:\nsudo chown root:root /var/www/cleanup.sh You can verify the permissions:\nls -l /var/www/cleanup.sh You should see something similar to:\n-rwxr-x--- 1 root root ... /var/www/cleanup.sh This prevents the Jenkins user from modifying the script itself.\nThat is important because if Jenkins could modify the script, restricting sudo to that script would provide little security benefit.\n3. Create a Dedicated Sudoers Configuration Instead of modifying the main /etc/sudoers file, create a dedicated configuration file:\nsudo visudo -f /etc/sudoers.d/jenkins-cleanup Add:\njenkins ALL=(root) NOPASSWD: /var/www/cleanup.sh This rule means:\njenkins — applies to the Jenkins user ALL — applies on the current host (root) — the command can run as root NOPASSWD — Jenkins does not need to provide a password /var/www/cleanup.sh — Jenkins can run only this specific command Save and exit.\nUsing visudo is recommended because it validates the sudoers syntax before installing the configuration.\n4. Test the Sudo Permission Before configuring Jenkins, test the command as the jenkins user:\nsudo -u jenkins sudo /var/www/cleanup.sh You can also run it with Bash tracing:\nsudo -u jenkins sudo bash -x /var/www/cleanup.sh You should see:\nRunning cleanup as: root This confirms that the script is actually being executed with root privileges.\nNote: Running sudo -u jenkins bash -x /var/www/cleanup.sh by itself does not test the sudo permission. It only runs the script as the jenkins user. To test the root-level permission, use sudo -u jenkins sudo /var/www/cleanup.sh.\n5. Configure Jenkins In your Jenkins job, use an Execute shell build step:\n#!/bin/bash set -e echo \u0026#34;Starting server cleanup...\u0026#34; sudo /var/www/cleanup.sh echo \u0026#34;Server cleanup completed.\u0026#34; Jenkins will execute:\nsudo /var/www/cleanup.sh The sudoers rule allows this command to run without requiring a password.\n6. Verify That Jenkins Does Not Have General Root Access The important security property of this setup is that Jenkins should not be able to execute arbitrary commands as root.\nFor example, this should fail:\nsudo -u jenkins sudo whoami While this should succeed:\nsudo -u jenkins sudo /var/www/cleanup.sh You can also check the permissions available to Jenkins:\nsudo -u jenkins sudo -l The output should show that Jenkins is allowed to run only the cleanup script.\n7. Why This Approach Is Safer Avoid giving Jenkins unrestricted sudo access:\njenkins ALL=(ALL) NOPASSWD: ALL With unrestricted access, a compromised Jenkins job could potentially execute commands such as:\nsudo rm -rf / or modify system configuration, users, SSH keys, and other sensitive resources.\nInstead, use a narrowly scoped rule:\njenkins ALL=(root) NOPASSWD: /var/www/cleanup.sh This follows the principle of least privilege: Jenkins receives only the root permission it actually needs.\n8. Important Security Considerations Keep the Script Owned by Root Do not allow Jenkins to modify the script:\nsudo chown root:root /var/www/cleanup.sh sudo chmod 750 /var/www/cleanup.sh If Jenkins can write to a root-executable script, it could potentially insert arbitrary root commands into that script.\nUse an Absolute Script Path Use:\nsudo /var/www/cleanup.sh rather than:\nsudo cleanup.sh The absolute path makes the sudoers rule precise and avoids ambiguity around the executable being invoked.\nAvoid User-Controlled Arguments If you want Jenkins to run:\nsudo /var/www/cleanup.sh do not unnecessarily allow:\njenkins ALL=(root) NOPASSWD: /var/www/cleanup.sh * The latter allows Jenkins to pass arbitrary arguments to the script, which can introduce additional security concerns.\nBe Careful With Commands Inside the Script Since the script runs as root, every command inside it effectively has root privileges.\nFor example:\nrm -rf \u0026#34;$SOME_DIRECTORY\u0026#34; should be carefully validated if the directory path can ever be influenced by external input.\nFinal Configuration The resulting setup is simple:\nJenkins | | sudo /var/www/cleanup.sh | v sudoers | | Allows only this command v /var/www/cleanup.sh | | runs as root v Server cleanup operations The key configuration is:\nScript:\nsudo chown root:root /var/www/cleanup.sh sudo chmod 750 /var/www/cleanup.sh Sudoers:\njenkins ALL=(root) NOPASSWD: /var/www/cleanup.sh Jenkins:\nsudo /var/www/cleanup.sh This gives Jenkins the ability to perform the required root-level maintenance task while avoiding unrestricted root access.\n","permalink":"https://dwij.net/posts/how-to-run-root-level-shell-script-in-jenkins/","summary":"Jenkins normally runs shell commands as the jenkins user. However, some server-maintenance tasks require root privileges—for example, cleaning APT caches, removing old packages, or managing system files.\nA common but unsafe approach is to give the Jenkins user unrestricted sudo access:\njenkins ALL=(ALL) NOPASSWD: ALL This allows Jenkins jobs to execute arbitrary commands as root.\nA much safer approach is to allow Jenkins to run only one specific maintenance script as root.","title":"How to Run a Root-Level Shell Script in Jenkins"},{"content":"Requirements Add the repository key to the system and append the Debian package repository.\nwget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add - wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo apt-key add - sudo sh -c \u0026#39;echo deb http://pkg.jenkins.io/debian-stable binary/ \u0026gt; /etc/apt/sources.list.d/jenkins.list\u0026#39; sudo apt update Install Java 17 sudo apt install openjdk-17-jre-headless Update JAVA_HOME in .bashrc : nano ~/.bashrc\nexport JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 Install Jenkins sudo apt install jenkins Allow firewall for 8080 port\nsudo ufw allow 8080 sudo ufw status Start Jenkins\nsudo systemctl start jenkins Check Jenkins Status\nsudo systemctl status jenkins Enable Port in AWS If you are using AWS for hosting, you will need to add 8080 port to security group. Open your Security Group and check Inbound rules. Then click on Edit inbound rules.\nClick on Add rule button at bottom left and add new port, once done Save rules.\nGet default password and create your own user.\nsudo cat /var/lib/jenkins/secrets/initialAdminPassword Use root as Jenkins User for running Jobs sudo nano /etc/default/jenkins Update JENKINS_USER and JENKINS_GROUP at end of file.\n# Change Default Jenkins user - Username JENKINS_USER=root JENKINS_GROUP=root Update Jenkins Service for user\nsudo nano /usr/lib/systemd/system/jenkins.service # Change Default Jenkins user - Username User=root Group=root Reload units by\nsudo systemctl daemon-reload Restart jenkins\nsudo service jenkins restart Verify by adding whoami in Job script.\nProvide necessary ssh keys to Jenkins cd /var/lib/jenkins/ mkdir /var/lib/jenkins/.ssh/ Copy your private key here\ncp ~/.ssh/id_mykey /var/lib/jenkins/.ssh/ How to Load SSH Key in Job\neval `keychain --eval id_mykey` Run Job via Webhook when Github Push Event occurs Github Settings Add Webhook URL to your github project by Settings \u0026gt; Webhooks \u0026gt; Add Webhook. Use following as Payload URL\nhttp://mydomain.com:8080/github-webhook/ Content type: application/json\nSecret: Keep Empty.\nJust the push event\nCheck Active\nClick Add webhook button\nJenkins Job Settings Now in Jenkins, Create / Open a Job and do following settings:\nGeneral \u0026gt; GitHub project \u0026gt; Project url: Put Your Github Web URL.\nSource Code Management \u0026gt; Git \u0026gt; Repository URL:\ngit@github.com:username/my-repository.git In Credentials, Click on Add and Select Jenkings.\nDomain : Global\nKind : SSH Username with private key\nScope : Global\nID: Keep Empty\nDescription : Keep Empty\nUsername: Use Github Username\nPrivate Key \u0026gt; Check Enter directly:\nPrivate Key \u0026gt; Key \u0026gt; Click Add\nNow Open terminal and get contents of your priavte key by cat ~/.ssh/id_mykey. Copy that key content to Textarea.\nPassphrase : Enter if given at the time of Private Key Creation.\nClick on Add and select in Credentials list.\nBuild Triggers \u0026gt; Check GitHub hook trigger for GITScm polling\nSave Job.\nNo try pushing commit to your repository.\nI hope this helps you !\nReferences:\nhttps://www.digitalocean.com/community/tutorials/how-to-install-jenkins-on-ubuntu-20-04 https://stackoverflow.com/a/73075113/1785209 https://github.com/iandmyhand/boilerplates/blob/master/Jenkins/jenkins-with-github-private-repository-webhook.md ","permalink":"https://dwij.net/posts/setup-jenkins-on-ubuntu-20-04-and-run-jobs-are-root-user/","summary":"Requirements Add the repository key to the system and append the Debian package repository.\nwget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add - wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo apt-key add - sudo sh -c \u0026#39;echo deb http://pkg.jenkins.io/debian-stable binary/ \u0026gt; /etc/apt/sources.list.d/jenkins.list\u0026#39; sudo apt update Install Java 17 sudo apt install openjdk-17-jre-headless Update JAVA_HOME in .bashrc : nano ~/.bashrc\nexport JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 Install Jenkins sudo apt install jenkins Allow firewall for 8080 port","title":"Setup Jenkins on Ubuntu 20.04 / 22.04 and run Jobs are root user"},{"content":"Bootstrap is a popular CSS framework that provides a set of ready-to-use components and styles for building responsive web applications. In this article, we will explore how to enable Bootstrap Tooltip in an Angular application using an attribute directive.\nInstall Bootstrap:\nnpm install bootstrap Import Bootstrap SCSS in styles.scss:\n// For Setting Project Specific Bootstrap Variable. Like $primary Color @import \u0026#34;assets/scss/variables.scss\u0026#34;; // Import Bootstrap @import \u0026#34;../node_modules/bootstrap/scss/bootstrap.scss\u0026#34;; // App Specific components @import \u0026#34;assets/scss/app.scss\u0026#34;; Import Bootstrap Bundle JS in angular.json -\u0026gt; architect -\u0026gt; build -\u0026gt; options:\n\u0026#34;scripts\u0026#34;: [ \u0026#34;node_modules/bootstrap/dist/js/bootstrap.bundle.min.js\u0026#34; ] Add bootstrap as a global variable in src/typings.d.ts\ndeclare var bootstrap: any; Now create a Directive TooltipDirective:\nsrc/app/_components/tooltip.directive.ts:\nimport { AfterViewInit, Directive, ElementRef, OnDestroy } from \u0026#34;@angular/core\u0026#34;; @Directive({ selector: \u0026#34;[bTooltip]\u0026#34;, }) export class TooltipDirective implements AfterViewInit, OnDestroy { private tooltip: any; constructor(private elementRef: ElementRef) {} ngAfterViewInit() { const domElement: HTMLElement = this.elementRef.nativeElement; this.tooltip = new bootstrap.Tooltip(domElement); } ngOnDestroy(): void { this.tooltip.dispose(); } } src/app/_components/tooltip.directive.spec.ts\nimport { ElementRef } from \u0026#34;@angular/core\u0026#34;; import { TooltipDirective } from \u0026#34;./tooltip.directive\u0026#34;; describe(\u0026#34;TooltipDirective\u0026#34;, () =\u0026gt; { it(\u0026#34;should create an instance\u0026#34;, () =\u0026gt; { const elementRefMock: ElementRef = {} as ElementRef; const directive = new TooltipDirective(elementRefMock); expect(directive).toBeTruthy(); }); }); Declare Tooltip directive in src/app/app.module.ts:\nimport { TooltipDirective } from \u0026#39;./_components/tooltip.directive\u0026#39;; @NgModule({ declarations: [ ... TooltipDirective ] }) Use the directive as below to create a tooltip:\n\u0026lt;a class=\u0026#34;btn btn-success\u0026#34; bTooltip title=\u0026#34;Your Message\u0026#34;\u0026gt;MyButton\u0026lt;/a\u0026gt; Now you will be able to use Tooltip with all it\u0026rsquo;s attributes like data-bs-placement.\nI think with similar approach you can use most of the Bootstrap components in Angular.\nStackoverflow Answer \u0026gt;\n","permalink":"https://dwij.net/posts/enable-bootstrap-tooltip-in-angular-using-attribute-directive/","summary":"Bootstrap is a popular CSS framework that provides a set of ready-to-use components and styles for building responsive web applications. In this article, we will explore how to enable Bootstrap Tooltip in an Angular application using an attribute directive.\nInstall Bootstrap:\nnpm install bootstrap Import Bootstrap SCSS in styles.scss:\n// For Setting Project Specific Bootstrap Variable. Like $primary Color @import \u0026#34;assets/scss/variables.scss\u0026#34;; // Import Bootstrap @import \u0026#34;../node_modules/bootstrap/scss/bootstrap.scss\u0026#34;; // App Specific components @import \u0026#34;assets/scss/app.","title":"Enable Bootstrap Tooltip in Angular using Attribute Directive"},{"content":"Concurrency is a powerful aspect of Go (Golang) that allows developers to execute multiple tasks concurrently, enabling efficient resource utilization and improved performance. In this article, we\u0026rsquo;ll explore two key concurrency primitives in Go: goroutines and channels.\nUnderstanding Goroutines Goroutines are lightweight threads managed by the Go runtime. They enable concurrent execution of functions or methods independently of other parts of the program. Goroutines are more lightweight than operating system threads, allowing Go programs to create thousands or even millions of them without significant overhead.\nCreating Goroutines Creating a goroutine is as simple as prefixing a function call with the go keyword. For example:\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func sayHello() { fmt.Println(\u0026#34;Hello from Goroutine!\u0026#34;) } func main() { // Start a new goroutine go sayHello() // Print from main goroutine fmt.Println(\u0026#34;Hello from Main!\u0026#34;) // Allow time for goroutine to execute time.Sleep(time.Second) } Output:\n$ go run goroutines.go Hello from Main! Hello from Goroutine! In this example, sayHello() is executed concurrently as a goroutine, while the main goroutine continues execution.\nGoroutine Pitfalls Lack of Synchronization: Goroutines execute independently, so there\u0026rsquo;s no inherent synchronization between them. Care must be taken to synchronize access to shared resources to prevent race conditions. Resource Management: Creating too many goroutines concurrently can exhaust system resources. It\u0026rsquo;s essential to limit the number of concurrently executing goroutines or use techniques like a worker pool. Using WaitGroups to manage goroutines Using sync.WaitGroup in Go is a powerful way to manage multiple goroutines, ensuring that the program waits for all of them to finish before proceeding further. WaitGroup has be passed to methods as pointer. Here\u0026rsquo;s how you can use WaitGroups to manage multiple goroutines effectively:\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; \u0026#34;time\u0026#34; ) func routineWork(id int, wg *sync.WaitGroup) { fmt.Println(\u0026#34;routine \u0026#34;, id, \u0026#34; starting\u0026#34;) // Imitate processing for goroutine time.Sleep(time.Second) fmt.Println(\u0026#34;routine \u0026#34;, id, \u0026#34; done\u0026#34;) // Decrement the WaitGroup counter when done defer wg.Done() } func main() { // Create WaitGroup var wg sync.WaitGroup for i := 1; i \u0026lt;= 5; i++ { // Increment the WaitGroup counter wg.Add(1) // Start new routine worker go routineWork(i, \u0026amp;wg) } // Wait for all workers to finish wg.Wait() } Output:\n$ go run goroutines.go routine 5 starting routine 1 starting routine 3 starting routine 4 starting routine 2 starting routine 2 done routine 1 done routine 5 done routine 3 done routine 4 done Understanding Channels Channels in Go are typed conduits that allow goroutines to communicate with each other and synchronize their execution. They provide a safe and efficient way for goroutines to exchange data without the need for explicit locking mechanisms.\nChannel Types There are two main types of channels in Go: unbuffered and buffered channels.\nUnbuffered Channels (Synchronous) Unbuffered channels have no capacity to store data. Every send operation on an unbuffered channel blocks until there\u0026rsquo;s a corresponding receive operation, and vice versa. This will lead to deadlock. This makes unbuffered channels ideal for synchronization between goroutines. Buffered Channels (Asynchronous) Buffered channels have a fixed capacity to store data. Send operations on a buffered channel block only when the buffer is full. Receive operations block when the buffer is empty. Buffered channels allow for asynchronous communication between goroutines. Creating Channels Channels are created using the make() function, specifying the channel type. Here\u0026rsquo;s how you can create (synchronous) or buffered (asynchronous) channels:\n// Unbuffered channel ch := make(chan int) // Buffered channel with capacity 10 ch := make(chan int, 10) Sending and Receiving Values The \u0026lt;- operator is used to send and receive values through channels. Sending blocks until the receiver is ready, and receiving blocks until a value is available.\n// Sending a value to the channel ch \u0026lt;- value // Receiving a value from the channel value := \u0026lt;-ch Channel Operations Close: Channels can be closed to indicate that no more values will be sent. Receivers can check if a channel is closed using the second return value from a receive operation. Select: The select statement allows for non-blocking communication with multiple channels. It chooses which case to run based on the readiness of the channels. select { case value := \u0026lt;-ch1: // Handle value received from ch1 case ch2 \u0026lt;- value: // Send value to ch2 case \u0026lt;-time.After(time.Second): // Timeout after 1 second } Using Unbuffered Channels Unbuffered channels are often used for synchronization between goroutines, ensuring that they coordinate their execution. Let\u0026rsquo;s see an example:\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func worker(ch chan string) { fmt.Println(\u0026#34;Worker: Started\u0026#34;) // Simulate work time.Sleep(time.Second) // Send result to channel ch \u0026lt;- \u0026#34;Done\u0026#34; fmt.Println(\u0026#34;Worker: Finished\u0026#34;) } func main() { // Define unbuffered channel with string data type ch := make(chan string) fmt.Println(\u0026#34;Main: Start worker goroutine\u0026#34;) // Start worker goroutine go worker(ch) fmt.Println(\u0026#34;Main: Waiting for result...\u0026#34;) // Wait to receive result from channel result := \u0026lt;-ch fmt.Println(\u0026#34;Main: Received result:\u0026#34;, result) fmt.Scanln() } Output:\n$ go run channel-ub.go Main: Start worker goroutine Main: Waiting for result... Worker: Started Main: Received result: Done Worker: Finished In this example:\nWe create an unbuffered channel ch. The worker() function is a goroutine that simulates work and sends the result to the channel. The main() function starts the worker goroutine and then waits to receive the result from the channel. Using Buffered Channels Buffered channels are useful when you want to decouple senders and receivers, allowing for asynchronous communication. Let\u0026rsquo;s look at an example:\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func producer(ch chan int) { for i := 0; i \u0026lt; 5; i++ { fmt.Println(\u0026#34;Producer: Sent:\u0026#34;, i) // Send value to channel ch \u0026lt;- i // Simulate work time.Sleep(time.Second / 2) } // Close the channel when done close(ch) fmt.Println(\u0026#34;Producer: Closed\u0026#34;) } func main() { // Buffered channel with capacity 3 ch := make(chan int, 3) fmt.Println(\u0026#34;Main: Start worker goroutine\u0026#34;) // Start producer goroutine go producer(ch) // Wait for 1 second before receiving time.Sleep(time.Second) fmt.Println(\u0026#34;Main: Waiting for result...\u0026#34;) // Receive values from channel for val := range ch { fmt.Println(\u0026#34;Main: Received:\u0026#34;, val) } fmt.Println(\u0026#34;Main: Finished\u0026#34;) } Output:\n$ go run channel-bu.go Main: Start worker goroutine Producer: Sent: 0 Producer: Sent: 1 Main: Waiting for result... Producer: Sent: 2 Main: Received: 0 Main: Received: 1 Main: Received: 2 Producer: Sent: 3 Main: Received: 3 Producer: Sent: 4 Main: Received: 4 Producer: Closed Main: Finished In this example:\nWe create a buffered channel ch with a capacity of 3. The producer() function sends values to the channel asynchronously. The main() function receives values from the channel and prints them. Channels are a fundamental feature of Go\u0026rsquo;s concurrency model, providing a safe and efficient mechanism for goroutines to communicate and synchronize their execution. Understanding the differences between unbuffered and buffered channels, and how to use them effectively, is essential for writing concurrent Go programs. By leveraging channels, you can write scalable and robust concurrent applications in Go.\nChannel Pitfalls Deadlocks: Goroutines can deadlock if they\u0026rsquo;re expecting data from a channel that\u0026rsquo;s not being sent or if they\u0026rsquo;re waiting to send data to a channel that\u0026rsquo;s not being received. Buffered Channels: Be cautious when using buffered channels, as they can lead to increased memory usage if not managed properly. Conclusion Goroutines and channels are fundamental concurrency primitives in Go that enable developers to write concurrent and scalable programs efficiently. By leveraging goroutines for concurrent execution and channels for communication and synchronization between goroutines, Go provides a robust model for building concurrent software. Understanding these concepts is essential for effectively utilizing Go\u0026rsquo;s concurrency features and writing robust concurrent programs.\nRemember, Something is usable if it behaves exactly as expected.\nHappy coding! \u0026#x1f680;\n","permalink":"https://dwij.net/posts/concurrency-in-golang-goroutines-channels-explained/","summary":"Concurrency is a powerful aspect of Go (Golang) that allows developers to execute multiple tasks concurrently, enabling efficient resource utilization and improved performance. In this article, we\u0026rsquo;ll explore two key concurrency primitives in Go: goroutines and channels.\nUnderstanding Goroutines Goroutines are lightweight threads managed by the Go runtime. They enable concurrent execution of functions or methods independently of other parts of the program. Goroutines are more lightweight than operating system threads, allowing Go programs to create thousands or even millions of them without significant overhead.","title":"Concurrency in Golang: Goroutines and Channels Explained"},{"content":"Laravel, an open-source PHP framework, provides built-in functionalities for developers to write tests for their applications. However, running tests, especially database tests, can sometimes be slow and time-consuming, affecting the productivity of developers. This blog post will elaborate on how to speed up Laravel unit tests using the schema::dump command.\nThe Problem While performing unit tests in Laravel, developers often face a problem: migrations. Each time you run a test, Laravel runs all migrations, creating a database schema. If your application has a lot of migration files, the process of migrating them every time can be quite slow. This can be a significant slowdown when running a large number of tests, resulting in a longer time taken to conduct the tests.\nThe Solution To resolve this, Laravel introduced an artisan command, schema::dump, in Laravel 8.x, which generates a schema file from your migrations. It dumps the database schema into a SQL file, which can then be run instead of the individual migrations. This significantly speeds up the execution of tests.\nHere\u0026rsquo;s how you can implement it:\nStep 1: Create a Schema Dump Firstly, you need to create a schema dump. Before running dump command make sure you migrate your existing migrations to DB:\nphp artisan migrate You can do this by running the following Artisan command:\nphp artisan schema:dump This will create a file called mysql-schema.dump inside your database/schema directory. This file will contain all the SQL necessary to create your database schema based on your current mysql database.\nStep 2: Configure PHPUnit Next, you need to tell PHPUnit to use the schema dump instead of running all migrations. Open your phpunit.xml file and add the following lines:\n\u0026lt;env name=\u0026#34;DB_CONNECTION\u0026#34; value=\u0026#34;mysql\u0026#34;/\u0026gt; \u0026lt;env name=\u0026#34;DB_DATABASE\u0026#34; value=\u0026#34;mydb\u0026#34;/\u0026gt; \u0026lt;env name=\u0026#34;USE_SCHEMA_DUMP\u0026#34; value=\u0026#34;true\u0026#34;/\u0026gt; The USE_SCHEMA_DUMP environment variable will tell Laravel to use the schema dump instead of the migrations.\nStep 3: Update Your CreatesApplication Trait The CreatesApplication trait is used by Laravel to set up the testing environment. You need to modify it to check for the USE_SCHEMA_DUMP environment variable and load the schema dump if it\u0026rsquo;s set.\nOpen the CreatesApplication trait located in tests/CreatesApplication.php and update the createApplication method:\npublic function createApplication() { $app = require __DIR__.\u0026#39;/../bootstrap/app.php\u0026#39;; $app-\u0026gt;make(Kernel::class)-\u0026gt;bootstrap(); // check if we should use the schema dump if (env(\u0026#39;USE_SCHEMA_DUMP\u0026#39;)) { $this-\u0026gt;loadSchemaDump(); } else { $this-\u0026gt;runDatabaseMigrations(); } return $app; } protected function loadSchemaDump() { // turn off foreign key checks DB::statement(\u0026#39;SET FOREIGN_KEY_CHECKS=0;\u0026#39;); // get all table names $tables = DB::select(\u0026#39;SHOW TABLES\u0026#39;); // drop all tables foreach ($tables as $table) { // TODO Replace \u0026#39;mydb\u0026#39; with your database name $tableName = $table-\u0026gt;Tables_in_mydb; DB::statement(\u0026#34;DROP TABLE {$tableName}\u0026#34;); } // turn foreign key checks back on DB::statement(\u0026#39;SET FOREIGN_KEY_CHECKS=1;\u0026#39;); // load the schema dump DB::unprepared(file_get_contents(database_path(\u0026#39;schema/mysql-schema.dump\u0026#39;))); } protected function runDatabaseMigrations() { $this-\u0026gt;artisan(\u0026#39;migrate\u0026#39;); } This code will check if the USE_SCHEMA_DUMP environment variable is set. If it is, it will load the schema dump; otherwise, it will run the migrations.\nConclusion Using Schema::dump can significantly speed up your Laravel unit tests, especially if you have a lot of migrations. It allows you to dump your database schema into a SQL file and use that file instead of the individual migrations. This can save you a ot of time and help improve your productivity.\nHowever, please remember that while the use of schema::dump is great for speeding up tests, you should be careful when using it in a production environment. It\u0026rsquo;s important to remember that the schema dump file represents the current database schema at the point when the dump was created. Therefore, if you make changes to your migrations, you need to remember run those migrations and then create a new schema dump. Otherwise, your tests could be running against an outdated schema.\nAs with any tool, it\u0026rsquo;s essential to understand how and when to use it. schema::dump is a powerful tool, but it isn\u0026rsquo;t always the right solution. For example, if your application has a few migrations, or if your migrations change often, using the traditional migration system might be simpler and more efficient.\nIn conclusion, Laravel\u0026rsquo;s Schema::dump can be an excellent tool for speeding up your unit tests. By reducing the time by almost 80 - 90% it takes to set up the test database, you can spend more time writing and refining your tests. This can help you catch bugs earlier, improve the quality of your code, and deliver a better product to your users. Happy testing!\n","permalink":"https://dwij.net/posts/how-to-speed-up-laravel-unit-tests-using-schemadump/","summary":"Laravel, an open-source PHP framework, provides built-in functionalities for developers to write tests for their applications. However, running tests, especially database tests, can sometimes be slow and time-consuming, affecting the productivity of developers. This blog post will elaborate on how to speed up Laravel unit tests using the schema::dump command.\nThe Problem While performing unit tests in Laravel, developers often face a problem: migrations. Each time you run a test, Laravel runs all migrations, creating a database schema.","title":"How to speed up Laravel Unit tests using Schema:Dump"},{"content":"The Singleton pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. While Go does not have traditional classes like object-oriented languages, it does allow for the implementation of the Singleton pattern using various approaches. We are going to use Goroutines in testing our approaches in Multi-threading. In this article, we\u0026rsquo;ll explore different approaches to implement the Singleton pattern in Go, along with code examples.\nApproach 1: Using Sync.Once Go\u0026rsquo;s sync.Once package provides a thread-safe way to initialize a value exactly once. This makes it ideal for implementing a Singleton pattern without the need for explicit locking mechanisms.\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; ) type singleton struct { // Add any necessary fields here } var once sync.Once var instance *singleton func getInstance() *singleton { once.Do(func() { instance = \u0026amp;singleton{} fmt.Println(\u0026#34;Instance created\u0026#34;) }) return instance } func main() { fmt.Println(\u0026#34;Main method started\u0026#34;) for i := 0; i \u0026lt; 10; i++ { go fmt.Println(\u0026#34;Instance at\u0026#34;, i, \u0026#34; = \u0026#34;, getInstance()) } fmt.Scanln() } Output:\n$ go run singleton.go Main method started Instance created Instance at 3 = \u0026amp;{} Instance at 2 = \u0026amp;{} Instance at 9 = \u0026amp;{} Instance at 7 = \u0026amp;{} Instance at 8 = \u0026amp;{} Instance at 4 = \u0026amp;{} Instance at 5 = \u0026amp;{} Instance at 0 = \u0026amp;{} Instance at 1 = \u0026amp;{} Instance at 6 = \u0026amp;{} Approach 2: Using init Function In this approach, we leverage Go\u0026rsquo;s init function to initialize the singleton instance. The init function is called automatically when the package is initialized, ensuring that the instance is created before any other code execution.\npackage main import ( \u0026#34;fmt\u0026#34; ) type singleton struct { // Add any necessary fields here } var instance *singleton func init() { fmt.Println(\u0026#34;Instance created\u0026#34;) instance = \u0026amp;singleton{} } func getInstance() *singleton { return instance } func main() { fmt.Println(\u0026#34;Main method started\u0026#34;) for i := 0; i \u0026lt; 10; i++ { go fmt.Println(\u0026#34;Instance at\u0026#34;, i, \u0026#34; = \u0026#34;, getInstance()) } fmt.Scanln() } Output:\n$ go run singleton.go Instance created Main method started Instance at 6 = \u0026amp;{} Instance at 1 = \u0026amp;{} Instance at 9 = \u0026amp;{} Instance at 7 = \u0026amp;{} Instance at 8 = \u0026amp;{} Instance at 5 = \u0026amp;{} Instance at 3 = \u0026amp;{} Instance at 0 = \u0026amp;{} Instance at 4 = \u0026amp;{} Instance at 2 = \u0026amp;{} Approach 3: Using synchronization with Mutex Locks This approach ensures that only one instance of the single struct is created even in a concurrent environment. The use of mutex locks (sync.Mutex) provides thread safety by synchronizing access to the critical section where the instance is created. Once the instance is created, subsequent calls to getInstance() return the same instance without creating new ones.\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; ) type singleton struct { // Add any necessary fields here } var lock = \u0026amp;sync.Mutex{} var instance *singleton func getInstance() *singleton { if instance == nil { // Only one goroutine at a time can go next lock.Lock() defer lock.Unlock() if instance == nil { fmt.Println(\u0026#34;Instance created\u0026#34;) instance = \u0026amp;singleton{} } else { fmt.Println(\u0026#34;Instance exists\u0026#34;) } } else { fmt.Println(\u0026#34;Instance exists\u0026#34;) } return instance } func main() { fmt.Println(\u0026#34;Main method started\u0026#34;) for i := 0; i \u0026lt; 10; i++ { go fmt.Println(\u0026#34;Instance at\u0026#34;, i, \u0026#34; = \u0026#34;, getInstance()) } fmt.Scanln() } Output:\n$ go run singleton.go Main method started Instance created Instance exists Instance exists Instance exists Instance exists Instance exists Instance exists Instance exists Instance exists Instance exists Instance at 3 = \u0026amp;{} Instance at 1 = \u0026amp;{} Instance at 6 = \u0026amp;{} Instance at 0 = \u0026amp;{} Instance at 4 = \u0026amp;{} Instance at 5 = \u0026amp;{} Instance at 8 = \u0026amp;{} Instance at 7 = \u0026amp;{} Instance at 9 = \u0026amp;{} Instance at 2 = \u0026amp;{} This approach is not very good considering performance and additional complexity.\nConclusion In Go, implementing the Singleton pattern can be achieved using different approaches, each with its own advantages. Whether you prefer simplicity, thread safety, or initialization control, Go provides the flexibility to choose the most suitable approach for your specific requirements. Experiment with these approaches and choose the one that best fits your project needs.\nHappy coding! \u0026#x1f680;\n","permalink":"https://dwij.net/posts/singleton-pattern-approaches-in-golang/","summary":"The Singleton pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. While Go does not have traditional classes like object-oriented languages, it does allow for the implementation of the Singleton pattern using various approaches. We are going to use Goroutines in testing our approaches in Multi-threading. In this article, we\u0026rsquo;ll explore different approaches to implement the Singleton pattern in Go, along with code examples.","title":"Singleton Pattern Approaches in Golang"},{"content":"Simple Hello World Program in Golang for beginners\nSetup: Firstly make sure you have done Golang setup as per https://go.dev/doc/install.\nYou can start programming once you could run go version command.\nProgram: Create a file named hello.go:\npackage main import \u0026#34;fmt\u0026#34; func main() { fmt.Println(\u0026#34;Hello World\u0026#34;) } Package main is a way to group functions inside the directory. fmt package has methods for console printing and text formatting. main is default method which runs and prints Hello World. Run program by:\ngo run hello.go You can build it and run by:\ngo build hello.go ./hello Don\u0026rsquo;t watch the clock; do what it does.\nKeep going! :thumbs_up:\n","permalink":"https://dwij.net/posts/golang-hello-world/","summary":"\u003cp\u003eSimple Hello World Program in Golang for beginners\u003c/p\u003e","title":"Golang: Hello World"},{"content":"We always find need to know which php files (Malware or otherwise) consuming the CPU and Server resources, so that you can fix those files.\nFPM status page give this exact information. For that it needs to be enabled and configured in your apache confirguration where you want it to be displayed.\nFind where your FPM configuration is and edit www.conf. nano /etc/php/7.4/fpm/pool.d/www.conf Update file for:\npm.status_path = /fpm7.4-status Restart FPM sudo service php7.4-fpm restart Edit any of your website\u0026rsquo;s Apache Configuration and add following LocationMatch directive. \u0026lt;LocationMatch \u0026#34;/fpm7.4-status\u0026#34;\u0026gt; Order Allow,Deny Allow from all ProxyPass \u0026#34;unix:/run/php/php7.4-fpm.sock|fcgi://localhost/fpm7.4-status\u0026#34; \u0026lt;/LocationMatch\u0026gt; To secure this /fpm7.4-status uri you can restrict it for IP by changing\nAllow from 100.100.100.100 Once done reload apache configurations sudo service apache2 reload Check FPM Status\nCheck your website for http://mywebsite.com/fpm7.4-status, This will return summary.\nFor List of processes add ?full parameter like this http://mywebsite.com/fpm7.4-status?full. Example output:\npid: 12840 state: Idle start time: 09/Apr/2023:12:32:10 +0530 start since: 583 requests: 43 request duration: 1390055 request method: POST request URI: /index.php content length: 0 user: - script: /var/www/html/index.php last request cpu: 92.08 last request memory: 23068672 Variable details:\npid - the PID of the process; state - the state of the process (Idle, Running, ...); start time - the date and time the process has started; start since - the number of seconds since the process has started; requests - the number of requests the process has served; request duration - the duration in µs of the requests; request method - the request method (GET, POST, ...); request URI - the request URI with the query string; content length - the content length of the request (only with POST); user - the user (PHP_AUTH_USER) (or \u0026#39;-\u0026#39; if not set); script - the main script called (or \u0026#39;-\u0026#39; if not set); last request cpu - the %cpu the last request consumed it\u0026#39;s always 0 if the process is not in Idle state because CPU calculation is done when the request processing has terminated; last request memory - the max amount of memory the last request consumed it\u0026#39;s always 0 if the process is not in Idle state because memory calculation is done when the request processing has terminated; ","permalink":"https://dwij.net/posts/php-fpm-get-list-of-running-php-files/","summary":"We always find need to know which php files (Malware or otherwise) consuming the CPU and Server resources, so that you can fix those files.\nFPM status page give this exact information. For that it needs to be enabled and configured in your apache confirguration where you want it to be displayed.\nFind where your FPM configuration is and edit www.conf. nano /etc/php/7.4/fpm/pool.d/www.conf Update file for:\npm.status_path = /fpm7.4-status Restart FPM sudo service php7.","title":"PHP-FPM Get list of running php files"},{"content":"Every Laravel Queue has it\u0026rsquo;s own cached version of Laravel Models, Jobs \u0026amp; Notification. You will be in serious panic if you are working on Jobs \u0026amp; Notification as Queue will not update the version of those file dynamically. You will need to restart Queue every time, by finding it in processes and killing it.\nSupervisor is a client/server system that allows its users to control a number of processes on UNIX-like operating systems.\nInstall Supervisor using PIP On an Ubuntu, install Supervisor with apt:\nsudo apt update sudo apt install supervisor -y Enable and start the service:\nsudo systemctl enable supervisor sudo systemctl start supervisor Check that it\u0026rsquo;s running:\nsudo systemctl status supervisor You should see:\nActive: active (running) Verify Supervisor sudo supervisorctl status If there are no configured processes yet, that\u0026rsquo;s fine.\nWhere to configure workers Supervisor configurations are normally placed in:\n/etc/supervisor/conf.d/ Create new Queue Create a Queue Configuration file:\ncd /etc/supervisor/conf.d nano queue-worker-prod.conf [program:queue-prod] process_name=%(program_name)s_%(process_num)02d command=php /var/www/html/project/artisan queue:work autostart=true autorestart=true user=root numprocs=1 redirect_stderr=true stdout_logfile=/var/www/html/project/storage/logs/supervisord.log Load the Queue Configuration\nsupervisorctl update Check status of queues\nsupervisorctl status Restart the Queue after Model / Job / Notification Updates\nsupervisorctl restart queue-prod:queue-prod_00 Find more help on http://supervisord.org\n","permalink":"https://dwij.net/posts/manage-laravel-queues-using-supervisor/","summary":"Every Laravel Queue has it\u0026rsquo;s own cached version of Laravel Models, Jobs \u0026amp; Notification. You will be in serious panic if you are working on Jobs \u0026amp; Notification as Queue will not update the version of those file dynamically. You will need to restart Queue every time, by finding it in processes and killing it.\nSupervisor is a client/server system that allows its users to control a number of processes on UNIX-like operating systems.","title":"Manage Laravel Queues using Supervisor"},{"content":"You must be here because your Self-Hosted server suddenly reports 100% CPU Utilization and making all server websites slow. There are lot of hackers who use brute-force attack on websites to gain access of it.\nLet\u0026rsquo;s start with list of defences:\n1. Using different username apart from admin Make Hackers Job More Difficult by choosing Site Specific Usernames\n2. Use difficult Passwords with Alphanumerics along with spcial charactors like % # * 3. Disable xmlrpc.php which is exploted by hackers by DDOS Attacks XML-RPC is feature of WordPress that enables data to be transferred with other systems like posting the articles from Mobile Apps. Most of the websites don\u0026rsquo;t need this feature. It\u0026rsquo;s better to block access to these file.\nEdit file /etc/apache2/apache2.conf\n\u0026lt;Files xmlrpc.php\u0026gt; Order Deny,Allow Deny from all \u0026lt;/Files\u0026gt; 4. Enable Fail2ban for WordPress Create a wordpress filter file wordpress.conf in /etc/fail2ban/filter.d/ with following content\n[Definition] failregex = ^\u0026lt;HOST\u0026gt; .* \u0026#34;POST .*wp-login.php ignoreregex = Let\u0026rsquo;s create a Fail2ban Configuration in /etc/fail2ban/jail.d/wordpress.conf\n[wordpress] enabled = true port = http,https filter = wordpress action = iptables-multiport[name=wordpress, port=\u0026#34;http,https\u0026#34;, protocol=tcp] logpath = /var/log/apache2/access*log maxretry = 5 findtime = 3600 bantime = 1296000 logpath - Apache Access log file maxretry - Maximum number of failed password trials allowed findtime - Time period within which maxretry limit is crossed. 3600 for 1 hour. bantime - Time in seconds for which IP will remain blocked. 1296000 for 15 days. -1 for Permanent block. Once configuration is done you can check IP Blocking mechanism in log file /var/log/fail2ban.log\n2021-09-13 10:15:27,078 fail2ban.filter [3412]: INFO [wordpress] Found 162.158.166.36 2021-09-13 10:15:27,412 fail2ban.filter [3412]: INFO [wordpress] Found 162.158.166.36 2021-09-13 10:15:27,774 fail2ban.filter [3412]: INFO [wordpress] Found 162.158.166.36 2021-09-13 10:15:28,106 fail2ban.filter [3412]: INFO [wordpress] Found 162.158.166.36 2021-09-13 10:15:28,447 fail2ban.filter [3412]: INFO [wordpress] Found 162.158.166.36 2021-09-13 10:15:28,467 fail2ban.actions [3412]: NOTICE [wordpress] Ban 162.158.166.36 Restart service just to make sure that it\u0026rsquo;s running well.\nsudo service fail2ban restart Reference: https://www.plesk.com/blog/various/using-fail2ban-to-secure-your-server/\nHow to unblock IP fail2ban-client set wordpress unbanip 162.158.166.36 How to add extra IP in Blacklist fail2ban-client set wordpress banip 162.158.166.36 Get banned IP\u0026rsquo;s for wordpress filter $ fail2ban-client status wordpress Status for the jail: wordpress |- Filter | |- Currently failed: 142 | |- Total failed: 188 | `- File list: /var/log/apache2/access.log `- Actions |- Currently banned: 8 |- Total banned: 8 `- Banned IP list: 162.158.165.150 162.158.166.36 162.158.167.238 172.69.135.210 172.69.135.216 198.204.234.254 66.115.176.18 172.68.144.56 How to top 10 IP\u0026rsquo;s from which requests are made Note: Make sure to ignore your own IP and IP of Server.\nawk \u0026#39;{ print $1}\u0026#39; /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -n 10 This is for now. We wil be adding more such defense techniques soon\u0026hellip;\n","permalink":"https://dwij.net/posts/secure-wordpress-websites-from-brute-force-attacks-using-apache-config-fail2ban/","summary":"You must be here because your Self-Hosted server suddenly reports 100% CPU Utilization and making all server websites slow. There are lot of hackers who use brute-force attack on websites to gain access of it.\nLet\u0026rsquo;s start with list of defences:\n1. Using different username apart from admin Make Hackers Job More Difficult by choosing Site Specific Usernames\n2. Use difficult Passwords with Alphanumerics along with spcial charactors like % # * 3.","title":"Secure WordPress Websites from Brute Force Attacks using Fail2ban on LAMP Server"},{"content":"Laravel Valet is one of the most used tool on Mac by Laravel developers. Deploying test domains is lot way convenient than working on localhost or 127.0.0.1.\nMake sure that all your libraries you are installing via ** brew** only.\n1. Brew, Composer \u0026amp; PHP Installation If you don\u0026rsquo;t have Brew or Composer Installed, Install it with following commands first.\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\u0026#34; Install php using\nbrew install php@7.4 Once installed start php in background with following command. You will have to do it only once. It will start automatically after every Mac restart.\nbrew services start php@7.4 Install Composer\nbrew install composer 2. Valet Installation composer global require laravel/valet To use the valet command directly we shall add it to path using sudo nano /etc/paths and adding new line at end.\n~/.composer/vendor/bin Once done, open the new terminal and run\nvalet install This will configure and install Valet and DnsMasq, and register Valet\u0026rsquo;s daemon to launch when your system starts. Now give necessary permissions by\ncd ~/.composer/ sudo chown -R $(whoami) vendor Once Valet is installed, try pinging any *.test domain on your terminal using a command such as ping foobar.test. If Valet is installed correctly you should see this domain responding on 127.0.0.1.\nValet will automatically start its daemon each time your machine boots. There is no need to run valet start or valet install ever again once the initial Valet installation is complete.\n3. Start Valet for Workspace folder I have Sites folder in my Mac on /Users/ganesh/Sites/ where I want to store all my websites.\ncd ~/Sites valet park Now let\u0026rsquo;s create a website http://mysite.test by\nmkdir mysite touch mysite/index.html You will start seeinng blank website.\n4. Database Setup We will be using MySql 5.7 Database.\nbrew install mysql@5.7 Start database in background using\nbrew services start mysql@5.7 Note: We\u0026rsquo;ve installed your MySQL database without a root password. To secure it run mysql_secure_installation.\nAdd mysql to path using sudo nano /etc/paths.\n/usr/local/opt/mysql@5.7/bin 5. Install PHPMyAdmin brew install phpmyadmin Setup Valet for phpmyadmin\ncd /usr/local/share/phpmyadmin valet domain test valet link Done!\n","permalink":"https://dwij.net/posts/laravel-valet-setup-including-phpmyadmin/","summary":"Laravel Valet is one of the most used tool on Mac by Laravel developers. Deploying test domains is lot way convenient than working on localhost or 127.0.0.1.\nMake sure that all your libraries you are installing via ** brew** only.\n1. Brew, Composer \u0026amp; PHP Installation If you don\u0026rsquo;t have Brew or Composer Installed, Install it with following commands first.\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\u0026#34; Install php using\nbrew install php@7.","title":"Laravel Valet setup including PHPMyAdmin"},{"content":"When you upgrade or downgrade PHP Version of Laravel Valet it creates this issue of getting 502 Bad Gateway Error while accessing any parked .test domain.\nSimple way to solve this by updating composer first.\ncomposer global update Once done reinstall Valet again. It takes very less time.\nvalet install Run this command to make sure that you use correct version of php\nvalet use php@7.4 ","permalink":"https://dwij.net/posts/laravel-valet-502-bad-gateway-solved/","summary":"When you upgrade or downgrade PHP Version of Laravel Valet it creates this issue of getting 502 Bad Gateway Error while accessing any parked .test domain.\nSimple way to solve this by updating composer first.\ncomposer global update Once done reinstall Valet again. It takes very less time.\nvalet install Run this command to make sure that you use correct version of php\nvalet use php@7.4 ","title":"Laravel Valet : 502 Bad Gateway [SOLVED]"},{"content":"Zoho has created amazing Product ecosystem but is very poor while implementing the Clean API\u0026rsquo;s for it\u0026rsquo;s users.\nReference API Docs: https://www.zoho.com/cliq/help/restapi/v2/\nIn this article we will be demonstrating How to send a simple Message to Cliq Channel and ** Bot Subscribers**.\n1. Setup a Server-based Application Zoho Admin Panel Link: https://api-console.zoho.com\nAdd http://app.test/cliq-redirect url as \u0026lsquo;Authorized Redirect URIs \u0026lsquo;.\nCreate a Organization Channel in Cliq named mychannel.\nCreate a Bot with ** Listen** and ** Send** Permission and name it mybot.\nCreate a new Laravel Config config\\cliq.php\u0026quot;\n\u0026lt;?php return [ \u0026#39;client_id\u0026#39; =\u0026gt; env(\u0026#39;CLIQ_CLIENT_ID\u0026#39;, \u0026#39;1000.----Generate-This----\u0026#39;), \u0026#39;client_secret\u0026#39; =\u0026gt; env(\u0026#39;CLIQ_CLIENT_SECRET\u0026#39;, \u0026#39;----Generate-This----\u0026#39;), \u0026#39;default_channel\u0026#39; =\u0026gt; env(\u0026#39;CLIQ_DEFAULT_CHANNEL\u0026#39;, \u0026#39;mychannel\u0026#39;) \u0026#39;default_bot\u0026#39; =\u0026gt; env(\u0026#39;CLIQ_DEFAULT_BOT\u0026#39;, \u0026#39;mybot\u0026#39;) ]; 2. Authenticating the API\u0026rsquo;s Create a Auth URL with Scope ZohoCliq.Webhooks.CREATE and redirect in Controller and url http://app.test/cliq-auth\nRequest:\nGET https://cliq.zoho.com/oauth/v2/auth URL Parameters:\nParameter Description scope For sending Messages via Cliq, it\u0026rsquo;s ZohoCliq.Webhooks.CREATE client_id Client id obtained during client registration. state A generated value that correlates the callback with its associated authorization request response_type code redirect_uri Redirect uri mentioned during client registration. access_type Access type will be either online or offline . Implementation:\n$params = [ \u0026#39;scope\u0026#39; =\u0026gt; \u0026#39;ZohoCliq.Webhooks.CREATE\u0026#39;, \u0026#39;client_id\u0026#39; =\u0026gt; config(\u0026#34;cliq.client_id\u0026#34;), \u0026#39;state\u0026#39; =\u0026gt; \u0026#39;1234\u0026#39;, \u0026#39;response_type\u0026#39; =\u0026gt; \u0026#39;code\u0026#39;, \u0026#39;redirect_uri\u0026#39; =\u0026gt; url(\u0026#39;cliq-redirect\u0026#39;), \u0026#39;access_type\u0026#39; =\u0026gt; \u0026#39;offline\u0026#39;, ]; return Response::make(\u0026#39;\u0026#39;, 302 )-\u0026gt;header( \u0026#39;Location\u0026#39;, \u0026#39;https://accounts.zoho.com/oauth/v2/auth?\u0026#39; . http_build_query($params) ); After hitting the url http://app.test/cliq-auth, you will be asked to login and give Permissions. Once successfull, you will receive a code in GET request of http://app.test/cliq-redirect.\n3. Generate the access_token and refresh_token from received code Request:\nPOST https://cliq.zoho.com/oauth/v2/token Form Parameters:\nParameter Description code Authorization code obtained during grant token generation. client_id Client id obtained during client registration. client_secret Client secret obtained during client registration. redirect_uri Redirect uri mentioned during client registration. grant_type authorization_code scope For sending Messages via Cliq, it\u0026rsquo;s ZohoCliq.Webhooks.CREATE state A generated value that correlates the callback with its associated authorization request.Has to be maintained the same during the entire process for authenticity. Implementation:\n$code = $req-\u0026gt;input(\u0026#39;code\u0026#39;); $client = new Client([ \u0026#39;base_uri\u0026#39; =\u0026gt; \u0026#39;https://accounts.zoho.com/oauth/v2/\u0026#39;, \u0026#39;timeout\u0026#39; =\u0026gt; 10.0, ]); $params = [ \u0026#39;code\u0026#39; =\u0026gt; $code, \u0026#39;client_id\u0026#39; =\u0026gt; config(\u0026#34;cliq.client_id\u0026#34;), \u0026#39;client_secret\u0026#39; =\u0026gt; config(\u0026#34;cliq.client_secret\u0026#34;), \u0026#39;redirect_uri\u0026#39; =\u0026gt; url(\u0026#39;cliq-redirect\u0026#39;), \u0026#39;grant_type\u0026#39; =\u0026gt; \u0026#39;authorization_code\u0026#39;, \u0026#39;scope\u0026#39; =\u0026gt; \u0026#39;ZohoCliq.Webhooks.CREATE\u0026#39;, \u0026#39;state\u0026#39; =\u0026gt; \u0026#39;1234\u0026#39;, ]; $response = $client-\u0026gt;request(\u0026#39;POST\u0026#39;, \u0026#39;token\u0026#39;, [ \u0026#39;form_params\u0026#39; =\u0026gt; $params ]); $body = json_decode($response-\u0026gt;getBody()); if(isset($body-\u0026gt;access_token)) { // Save the access_token in DB // Save the refresh_token in DB // Save access_token timeout time of one hour in DB $cliq_token_exp = Carbon::now()-\u0026gt;addHour()-\u0026gt;timestamp } 4. Make sure to get new access_token after every hour using refresh_token Request:\nPOST https://cliq.zoho.com/oauth/v2/token Form Parameters:\nParameter Description client_id Client id obtained during client registration. client_secret Client secret obtained during client registration. redirect_uri Redirect uri mentioned during client registration. grant_type refresh_token scope For sending Messages via Cliq, it\u0026rsquo;s ZohoCliq.Webhooks.CREATE refresh_token The refresh token obtained during access token generation. Implementation:\n$refresh_token = fromDB(); $cliq_token_exp = fromDB(); $cliq_token_exp = intval($cliq_token_exp); // Check if Access Token has expired (1 hour) if(Carbon::now()-\u0026gt;timestamp \u0026gt;= $cliq_token_exp) { // Regenerate access_token $client = new Client([ \u0026#39;base_uri\u0026#39; =\u0026gt; \u0026#39;https://accounts.zoho.com/oauth/v2/\u0026#39;, \u0026#39;timeout\u0026#39; =\u0026gt; 10.0, ]); $params = [ \u0026#39;client_id\u0026#39; =\u0026gt; config(\u0026#34;cliq.client_id\u0026#34;), \u0026#39;client_secret\u0026#39; =\u0026gt; config(\u0026#34;cliq.client_secret\u0026#34;), \u0026#39;redirect_uri\u0026#39; =\u0026gt; url(\u0026#39;/cliq-redirect\u0026#39;), \u0026#39;grant_type\u0026#39; =\u0026gt; \u0026#39;refresh_token\u0026#39;, \u0026#39;scope\u0026#39; =\u0026gt; \u0026#39;ZohoCliq.Webhooks.CREATE\u0026#39;, \u0026#39;refresh_token\u0026#39; =\u0026gt; $refresh_token, ]; $response = $client-\u0026gt;request(\u0026#39;POST\u0026#39;, \u0026#39;token\u0026#39;, [ \u0026#39;form_params\u0026#39; =\u0026gt; $params ]); $body = json_decode($response-\u0026gt;getBody()); if(isset($body-\u0026gt;access_token)) { // Save the access_token in DB // Save access_token timeout time of one hour in DB $cliq_token_exp = Carbon::now()-\u0026gt;addHour()-\u0026gt;timestamp } } 5. Send Message as a Bot to it\u0026rsquo;s Subscribers Request:\nPOST https://cliq.zoho.com/api/v2/bots/{BOT_UNIQUE_NAME}/message Payload:\n{ \u0026#34;text\u0026#34;: \u0026#34;Hello there\u0026#34;, \u0026#34;broadcast\u0026#34;: \u0026#34;true\u0026#34; } Implementation:\n$payload = [ \u0026#34;text\u0026#34; =\u0026gt; \u0026#34;Hello there\u0026#34;, \u0026#34;broadcast\u0026#34; =\u0026gt; \u0026#34;true\u0026#34; ]; $client = new Client([ \u0026#39;base_uri\u0026#39; =\u0026gt; \u0026#39;https://cliq.zoho.com/api/v2/\u0026#39;, \u0026#39;timeout\u0026#39; =\u0026gt; 10.0, ]); $response = $client-\u0026gt;request(\u0026#39;POST\u0026#39;, \u0026#39;bots/\u0026#39;.config(\u0026#34;cliq.default_bot\u0026#34;).\u0026#39;/message\u0026#39;, [ \u0026#39;json\u0026#39; =\u0026gt; $payload, \u0026#39;headers\u0026#39; =\u0026gt; [ \u0026#39;Authorization\u0026#39; =\u0026gt; \u0026#39;Zoho-oauthtoken \u0026#39;.$access_token, \u0026#39;Content-Type\u0026#39; =\u0026gt; \u0026#39;application/json\u0026#39; ] ]); 6. Send Message to Channel Request:\nPOST https://cliq.zoho.com/api/v2/channelsbyname/{CHANNEL_UNIQUE_NAME}/message Payload:\n{ \u0026#34;text\u0026#34;: \u0026#34;Hello there\u0026#34;, } Implementation:\n$payload = [ \u0026#34;text\u0026#34; =\u0026gt; \u0026#34;Hello there\u0026#34;, ]; $client = new Client([ \u0026#39;base_uri\u0026#39; =\u0026gt; \u0026#39;https://cliq.zoho.com/api/v2/\u0026#39;, \u0026#39;timeout\u0026#39; =\u0026gt; 10.0, ]); $response = $client-\u0026gt;request(\u0026#39;POST\u0026#39;, \u0026#39;channelsbyname/\u0026#39;.config(\u0026#34;cliq.default_channel\u0026#34;).\u0026#34;/message\u0026#34;, [ \u0026#39;json\u0026#39; =\u0026gt; $payload, \u0026#39;headers\u0026#39; =\u0026gt; [ \u0026#39;Authorization\u0026#39; =\u0026gt; \u0026#39;Zoho-oauthtoken \u0026#39;.$access_token, \u0026#39;Content-Type\u0026#39; =\u0026gt; \u0026#39;application/json\u0026#39; ] ]); I hope this helps you in achieving the proper implementation. You can create a dedicated CliqController to handle auth and redirect requests and CliqAPI Helper to create the API calls. Make sure to create proper CliqNotification in Laravel along with a JobCliqNotification to actually send the Notification.\nFor any issues in Implementation please put comment below.\n","permalink":"https://dwij.net/posts/integrate-zoho-cliq-oauth-apis-in-php-laravel-using-guzzle/","summary":"Zoho has created amazing Product ecosystem but is very poor while implementing the Clean API\u0026rsquo;s for it\u0026rsquo;s users.\nReference API Docs: https://www.zoho.com/cliq/help/restapi/v2/\nIn this article we will be demonstrating How to send a simple Message to Cliq Channel and ** Bot Subscribers**.\n1. Setup a Server-based Application Zoho Admin Panel Link: https://api-console.zoho.com\nAdd http://app.test/cliq-redirect url as \u0026lsquo;Authorized Redirect URIs \u0026lsquo;.\nCreate a Organization Channel in Cliq named mychannel.\nCreate a Bot with ** Listen** and ** Send** Permission and name it mybot.","title":"Integrate Zoho Cliq OAuth API in PHP Laravel using Guzzle"},{"content":"It\u0026rsquo;s very tedious for a developer to check error logs every day and make sure that everything is running fine. But you can reduce this daily overhead by creating simple developer notifications to Admin Mail or Slack for example.\n1. Handle Exceptions in Laravel Handler There is default Handler for all these exceptions at app\\Exceptions\\Handler.php in Laravel 8. We handled the situation in reportable callback.\n\u0026lt;?php namespace App\\Exceptions; use App\\Jobs\\JobDevNotification; use Illuminate\\Foundation\\Exceptions\\Handler as ExceptionHandler; use Throwable; class Handler extends ExceptionHandler { . . . /** * Register the exception handling callbacks for the application. * * @return void */ public function register() { $this-\u0026gt;reportable(function (Throwable $e) { // Create Notification Data $exception = [ \u0026#34;name\u0026#34; =\u0026gt; get_class($e), \u0026#34;message\u0026#34; =\u0026gt; $e-\u0026gt;getMessage(), \u0026#34;file\u0026#34; =\u0026gt; $e-\u0026gt;getFile(), \u0026#34;line\u0026#34; =\u0026gt; $e-\u0026gt;getLine(), ]; // Create a Job for Notification which will run after 5 seconds. $job = (new JobDevNotification($exception))-\u0026gt;delay(5); // Dispatch Job and continue dispatch($job); }); } } 2. Create Mail for Alert Create the Mail app\\Mail\\ErrorAlert.php in Laravel using below command. Refer Mail documentation on https://laravel.com/docs/8.x/mail\nphp artisan make:mail ErrorAlert \u0026lt;?php namespace App\\Mail; use Illuminate\\Bus\\Queueable; use Illuminate\\Contracts\\Queue\\ShouldQueue; use Illuminate\\Mail\\Mailable; use Illuminate\\Queue\\SerializesModels; class ErrorAlert extends Mailable { use Queueable, SerializesModels; public $exception; /** * Create a new message instance. * * @return void */ public function __construct($exception) { $this-\u0026gt;exception = $exception; } /** * Build the message. * * @return $this */ public function build() { return $this-\u0026gt;from(\u0026#39;system@example.com\u0026#39;, \u0026#39;System\u0026#39;)-\u0026gt;subject(\u0026#34;Error Alert on Server\u0026#34;) -\u0026gt;view(\u0026#39;emails.error_alert\u0026#39;); } } You will also need a resources\\views\\emails\\error_alert.blade.php to format a mail. Here you can directly use $exception variable as it is set public in ErrorAlert.php.\nHello Admin,\u0026lt;br\u0026gt;\u0026lt;br\u0026gt; There is a \u0026lt;b\u0026gt;{{ $exception[\u0026#39;name\u0026#39;] }}\u0026lt;/b\u0026gt; on Laravel Server.\u0026lt;br\u0026gt;\u0026lt;br\u0026gt; \u0026lt;b\u0026gt;Error\u0026lt;/b\u0026gt;: {{ $exception[\u0026#39;message\u0026#39;] }}\u0026lt;br\u0026gt;\u0026lt;br\u0026gt; \u0026lt;b\u0026gt;File\u0026lt;/b\u0026gt;: {{ $exception[\u0026#39;file\u0026#39;].\u0026#34;:\u0026#34;.$exception[\u0026#39;line\u0026#39;] }}\u0026lt;br\u0026gt;\u0026lt;br\u0026gt; \u0026lt;b\u0026gt;Time\u0026lt;/b\u0026gt; {{ date(\u0026#34;Y-m-d H:i:s\u0026#34;) }}\u0026lt;br\u0026gt;\u0026lt;br\u0026gt; Please do the needful. 3. Create Database Queue to process Jobs in background Now to run the Jobs in background we need to setup a database queue drivers. So we will update .env as below. Refer Queue documentation on https://laravel.com/docs/8.x/queues.\nQUEUE_CONNECTION=database Create the Queue Table in Database with following commands\n$ php artisan queue:table Migration created successfully! $ php artisan migrate Migrating: 2021_08_26_180839_create_jobs_table Migrated: 2021_08_26_180839_create_jobs_table (52.85ms) 4. Create Job for Alert Let\u0026rsquo;s create a simple Job app\\Jobs\\JobDevNotification.php. Creating Job will make sure that your ongoing operations will not be affected.\n\u0026lt;?php namespace App\\Jobs; use App\\Mail\\ErrorAlert; use Illuminate\\Bus\\Queueable; use Illuminate\\Contracts\\Queue\\ShouldQueue; use Illuminate\\Foundation\\Bus\\Dispatchable; use Illuminate\\Queue\\InteractsWithQueue; use Illuminate\\Queue\\SerializesModels; use Illuminate\\Support\\Facades\\Mail; /** * Developer Notification Job */ class JobDevNotification implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $exception = []; /** * Create a new job instance. * * @param array $exception * @return void */ public function __construct($exception) { $this-\u0026gt;exception = $exception; } /** * Execute the job. * * @return void */ public function handle() { // Send Mail Mail::to(\u0026#34;admin@example.com\u0026#34;)-\u0026gt;send(new ErrorAlert($this-\u0026gt;exception)); } } 5. Start Queue, Setup SMTP \u0026amp; Done php artisan queue:listen Now update you .env file for SMTP Gateway Details and you are good to go!\nFind code for this article on https://github.com/gdbhosale/Laravel-Exception-Alerts\n","permalink":"https://dwij.net/posts/send-email-alert-to-admin-when-error-exceptions-occurs-in-laravel/","summary":"It\u0026rsquo;s very tedious for a developer to check error logs every day and make sure that everything is running fine. But you can reduce this daily overhead by creating simple developer notifications to Admin Mail or Slack for example.\n1. Handle Exceptions in Laravel Handler There is default Handler for all these exceptions at app\\Exceptions\\Handler.php in Laravel 8. We handled the situation in reportable callback.\n\u0026lt;?php namespace App\\Exceptions; use App\\Jobs\\JobDevNotification; use Illuminate\\Foundation\\Exceptions\\Handler as ExceptionHandler; use Throwable; class Handler extends ExceptionHandler { .","title":"Send Email Alert to Admin when Error Exceptions occurs in Laravel"},{"content":"You might find various use cases of Recursive Vue Components where the Source is based on some Nested Array. What we are going to demonstrate here is Recursive Element Implementation from Tags. Let\u0026rsquo;s say we want to create a Sidebar for Admin Panel and want to make it look minimal in coding.\n\u0026lt;sidebar-item name=\u0026#34;Dashboard\u0026#34; link=\u0026#34;#\u0026#34;\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Dashboard Default\u0026#34; link=\u0026#34;/dashboard\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Dashboard eCommerce\u0026#34; link=\u0026#34;/dashboard-ecommerce\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; \u0026lt;/sidebar-item\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Components\u0026#34; link=\u0026#34;/components\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; Actual implementation of each sidebar item would be more like this:\n\u0026lt;li class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;#\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;nav-link-text\u0026#34;\u0026gt;Dashboard\u0026lt;/span\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; To achieve this we will make use of Vue slots. We will be creating a SidebarItem.vue Component.\n\u0026lt;template\u0026gt; \u0026lt;li class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; :href=\u0026#34;link\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;nav-link-text\u0026#34;\u0026gt;{{ name }}\u0026lt;/span\u0026gt; \u0026lt;/a\u0026gt; \u0026lt;div class=\u0026#34;nav-item-inner\u0026#34; v-if=\u0026#34;childrenExists\u0026#34;\u0026gt; \u0026lt;slot\u0026gt;\u0026lt;/slot\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; export default { name: \u0026#34;sidebar-item\u0026#34;, props: { name: { type: String, }, link: { type: String, }, }, computed: { childrenExists() { if (this.$slots.default) { return !!this.$slots.default; } else { return false; } }, }, }; \u0026lt;/script\u0026gt; In above code \u0026lt;slot\u0026gt;\u0026lt;/slot\u0026gt; will make sure than inner components will be automatically processed. Method childrenExists makes sure to disable nav-item-inner if inner components is empty.\n\u0026lt;sidebar-item name=\u0026#34;Dashboard\u0026#34; link=\u0026#34;#\u0026#34;\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Dashboard Default\u0026#34; link=\u0026#34;/dashboard\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Dashboard eCommerce\u0026#34; link=\u0026#34;/dashboard-ecommerce\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; \u0026lt;/sidebar-item\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Components\u0026#34; link=\u0026#34;/components\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; . . . // Import SidebarItem component import SidebarItem from \u0026#34;@/components/SidebarItem.vue\u0026#34;; Vue.component(\u0026#34;sidebar-item\u0026#34;, SidebarItem); Output would be\n\u0026lt;li class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;#\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;nav-link-text\u0026#34;\u0026gt;Dashboard\u0026lt;/span\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;div class=\u0026#34;nav-item-inner\u0026#34;\u0026gt; \u0026lt;li class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;/dashboard\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;nav-link-text\u0026#34;\u0026gt;Dashboard Default\u0026lt;/span\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;li class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;/dashboard-ecommerce\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;nav-link-text\u0026#34;\u0026gt;Dashboard eCommerce\u0026lt;/span\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;li class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;/components\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;nav-link-text\u0026#34;\u0026gt;Components\u0026lt;/span\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; ","permalink":"https://dwij.net/posts/recursive-components-in-vue-3-using-slots/","summary":"You might find various use cases of Recursive Vue Components where the Source is based on some Nested Array. What we are going to demonstrate here is Recursive Element Implementation from Tags. Let\u0026rsquo;s say we want to create a Sidebar for Admin Panel and want to make it look minimal in coding.\n\u0026lt;sidebar-item name=\u0026#34;Dashboard\u0026#34; link=\u0026#34;#\u0026#34;\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Dashboard Default\u0026#34; link=\u0026#34;/dashboard\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Dashboard eCommerce\u0026#34; link=\u0026#34;/dashboard-ecommerce\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; \u0026lt;/sidebar-item\u0026gt; \u0026lt;sidebar-item name=\u0026#34;Components\u0026#34; link=\u0026#34;/components\u0026#34;\u0026gt;\u0026lt;/sidebar-item\u0026gt; Actual implementation of each sidebar item would be more like this:","title":"Recursive Components in Vue 3 using Slots"},{"content":"There are sometimes requirement on server side to keep background process from terminating. Follow following article to achieve it using tmux.\nssh into the remote machine\nStart tmux by typing tmux into the shell\nStart the process you want inside tmux session. In this I will be running Cloud Commander cloudcmd.\ncloudcmd Leave / Detach the tmux session by typing Ctrl+b and then d.\nList all tmux sessions by\ntmux list-sessions Attach / Resume to a running session with\ntmux attach-session -t \u0026lt;session-name\u0026gt; ","permalink":"https://dwij.net/posts/ubuntu-keep-process-running-in-background/","summary":"There are sometimes requirement on server side to keep background process from terminating. Follow following article to achieve it using tmux.\nssh into the remote machine\nStart tmux by typing tmux into the shell\nStart the process you want inside tmux session. In this I will be running Cloud Commander cloudcmd.\ncloudcmd Leave / Detach the tmux session by typing Ctrl+b and then d.\nList all tmux sessions by\ntmux list-sessions Attach / Resume to a running session with","title":"Ubuntu: Keep Process running in background using Tmux"},{"content":"No doubt ListView is one of the most used Container Widget in any Mobile Application as it gives us a seamless scrolling effect and a lot of space to put our content neatly. You are here because you might have received the following error:\nVertical viewport was given unbounded height. The relevant error-causing widget was ListView Your code might look like this:\nListView( children: [ Container( child: ListView.builder( itemCount: snapshot.data.length, itemBuilder: (context, index) {} ), ), ] ) Now add shrinkWrap: true, physics: ScrollPhysics(), to ListView.builder Widget and your issue will be resolved.\nListView.builder( shrinkWrap: true, physics: ScrollPhysics(), itemCount: snapshot.data.length, itemBuilder: (context, index) {} ), Adding this will allow ListView Builder to maintain its finite height state without having an internal scrolling effect.\nSolved.\n","permalink":"https://dwij.net/posts/flutter-putting-listview-builder-inside-another-listview/","summary":"No doubt ListView is one of the most used Container Widget in any Mobile Application as it gives us a seamless scrolling effect and a lot of space to put our content neatly. You are here because you might have received the following error:\nVertical viewport was given unbounded height. The relevant error-causing widget was ListView Your code might look like this:\nListView( children: [ Container( child: ListView.builder( itemCount: snapshot.data.length, itemBuilder: (context, index) {} ), ), ] ) Now add shrinkWrap: true, physics: ScrollPhysics(), to ListView.","title":"Flutter: Putting ListView.builder inside another ListView"},{"content":"if you are facing any of the following errors while integrating Google Maps in Flutter, follow this article.\n\u0026lsquo;GoogleMaps/GoogleMaps.h\u0026rsquo; file not found The \u0026lsquo;Pods-Runner\u0026rsquo; target has frameworks with conflicting names: googlemaps.framework. warning: \u0026lsquo;sqlite3_wal_checkpoint_v2\u0026rsquo; is only available on iOS 5.0 or newer ld: targeted OS version does not support use of thread local variables in XYZ for architecture x86_64 Steps to Integrate Google Maps in Flutter References:\nhttps://developers.google.com/maps/documentation/ios-sdk/start https://developers.google.com/maps/documentation/android-sdk/intro https://codelabs.developers.google.com/codelabs/google-maps-in-flutter Step 1: Import Flutter Library Add google_maps_flutter library to pubspec.yaml\ndependencies: google_maps_flutter: ^0.5.28+1 Step 2: Get Google Maps API Key Follow article on Get an API Key | Maps SDK for Android, Activate Library \u0026ldquo;Maps SDK for Android\u0026rdquo; \u0026amp; \u0026ldquo;Maps SDK for iOS\u0026rdquo; and get API Key. Once Done edit file AndroidManifest.xml:\n\u0026lt;manifest ... \u0026lt;application ... \u0026lt;meta-data android:name=\u0026#34;com.google.android.geo.API_KEY\u0026#34; android:value=\u0026#34;YOUR_ANDROID_SDK_API_KEY_HERE\u0026#34;/\u0026gt; Now open ios/Runner/AppDelegate.m and put lines as shown:\n#include \u0026#34;AppDelegate.h\u0026#34; #include \u0026#34;GeneratedPluginRegistrant.h\u0026#34; #import \u0026#34;GoogleMaps/GoogleMaps.h\u0026#34; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Add the following line with your API key. [GMSServices provideAPIKey:@\u0026#34;YOUR_ANDROID_SDK_API_KEY_HERE\u0026#34;]; [GeneratedPluginRegistrant registerWithRegistry:self]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @end You will also need to enable embedded_views_preview in file Info.plist:\n\u0026lt;key\u0026gt;io.flutter.embedded_views_preview\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; Step 3: Import SDK for iOS Once this is done we have to make sure that Google Maps SDK is available to XCode. To do that we need to update few things.\nOpen file Podfile and uncomment following line:\nplatform :ios, \u0026#39;9.0\u0026#39; Now open your ios folder from XCode as a Project and then open file Runner as show in image below. here change the deployment target to 9.0 as shown.\nOnce done. Close XCode and run following command in folder /ios in terminal:\nsudo gem install cocoapods pod update This will successfully update your pod and then we can move ahead with creating our first Map Screen:\nStep 4: Create first Map Widget import \u0026#39;dart:async\u0026#39;; import \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:google_maps_flutter/google_maps_flutter.dart\u0026#39;; class GoogleMapApp extends StatefulWidget { @override _GoogleMapAppState createState() =\u0026gt; _GoogleMapAppState(); } class _GoogleMapAppState extends State\u0026lt;GoogleMapApp\u0026gt; { Completer\u0026lt;GoogleMapController\u0026gt; _controller = Completer(); static const LatLng centerLoc = const LatLng(45.521563, -122.677433); void _onMapCreated(GoogleMapController controller) { _controller.complete(controller); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text(\u0026#39;Google Map Example\u0026#39;), backgroundColor: AppTheme.topBarColor, leading: IconButton( icon: Icon(Icons.arrow_back, color: AppTheme.white), onPressed: () =\u0026gt; Navigator.of(context).pop(), ), ), body: GoogleMap( onMapCreated: _onMapCreated, initialCameraPosition: CameraPosition( target: centerLoc, zoom: 8.0, ), ), ), ); } } Here we go\n","permalink":"https://dwij.net/posts/integrate-google-maps-in-flutter-application/","summary":"if you are facing any of the following errors while integrating Google Maps in Flutter, follow this article.\n\u0026lsquo;GoogleMaps/GoogleMaps.h\u0026rsquo; file not found The \u0026lsquo;Pods-Runner\u0026rsquo; target has frameworks with conflicting names: googlemaps.framework. warning: \u0026lsquo;sqlite3_wal_checkpoint_v2\u0026rsquo; is only available on iOS 5.0 or newer ld: targeted OS version does not support use of thread local variables in XYZ for architecture x86_64 Steps to Integrate Google Maps in Flutter References:\nhttps://developers.google.com/maps/documentation/ios-sdk/start https://developers.google.com/maps/documentation/android-sdk/intro https://codelabs.developers.google.com/codelabs/google-maps-in-flutter Step 1: Import Flutter Library Add google_maps_flutter library to pubspec.","title":"Integrate Google Maps in Flutter Application"},{"content":"If you need to extract metadata such as title, description, keywords, Open Graph tags, canonical URL, and Twitter Card information from an HTML page, Python makes this easy using requests and BeautifulSoup.\nInstall Required Packages Install the required Python packages:\npip install requests beautifulsoup4 Basic HTML Metadata Scraper Create a Python file named metadata.py:\nimport requests from bs4 import BeautifulSoup url = \u0026#34;https://example.com\u0026#34; response = requests.get( url, headers={ \u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0\u0026#34; }, timeout=10 ) response.raise_for_status() soup = BeautifulSoup(response.text, \u0026#34;html.parser\u0026#34;) title = soup.title.string.strip() if soup.title and soup.title.string else None description = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;name\u0026#34;: \u0026#34;description\u0026#34;} ) keywords = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;name\u0026#34;: \u0026#34;keywords\u0026#34;} ) print(\u0026#34;Title:\u0026#34;, title) print(\u0026#34;Description:\u0026#34;, description.get(\u0026#34;content\u0026#34;) if description else None) print(\u0026#34;Keywords:\u0026#34;, keywords.get(\u0026#34;content\u0026#34;) if keywords else None) Run it:\npython metadata.py Example output:\nTitle: Example Domain Description: This domain is for use in illustrative examples. Keywords: example, domain Extract Open Graph Metadata Many modern websites use Open Graph metadata for sharing pages on Facebook, LinkedIn, WhatsApp, and other platforms.\nA typical HTML page contains:\n\u0026lt;meta property=\u0026#34;og:title\u0026#34; content=\u0026#34;My Website\u0026#34; /\u0026gt; \u0026lt;meta property=\u0026#34;og:description\u0026#34; content=\u0026#34;My website description\u0026#34; /\u0026gt; \u0026lt;meta property=\u0026#34;og:image\u0026#34; content=\u0026#34;https://example.com/image.jpg\u0026#34; /\u0026gt; \u0026lt;meta property=\u0026#34;og:url\u0026#34; content=\u0026#34;https://example.com/\u0026#34; /\u0026gt; You can extract these tags with:\nog_title = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;property\u0026#34;: \u0026#34;og:title\u0026#34;} ) og_description = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;property\u0026#34;: \u0026#34;og:description\u0026#34;} ) og_image = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;property\u0026#34;: \u0026#34;og:image\u0026#34;} ) og_url = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;property\u0026#34;: \u0026#34;og:url\u0026#34;} ) print(\u0026#34;OG Title:\u0026#34;, og_title.get(\u0026#34;content\u0026#34;) if og_title else None) print(\u0026#34;OG Description:\u0026#34;, og_description.get(\u0026#34;content\u0026#34;) if og_description else None) print(\u0026#34;OG Image:\u0026#34;, og_image.get(\u0026#34;content\u0026#34;) if og_image else None) print(\u0026#34;OG URL:\u0026#34;, og_url.get(\u0026#34;content\u0026#34;) if og_url else None) Extract Twitter Card Metadata Twitter/X uses Twitter Card metadata to control how a URL appears when shared.\nFor example:\n\u0026lt;meta name=\u0026#34;twitter:card\u0026#34; content=\u0026#34;summary_large_image\u0026#34; /\u0026gt; \u0026lt;meta name=\u0026#34;twitter:title\u0026#34; content=\u0026#34;My Website\u0026#34; /\u0026gt; \u0026lt;meta name=\u0026#34;twitter:description\u0026#34; content=\u0026#34;My website description\u0026#34; /\u0026gt; \u0026lt;meta name=\u0026#34;twitter:image\u0026#34; content=\u0026#34;https://example.com/image.jpg\u0026#34; /\u0026gt; Extract them using:\ntwitter_card = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;name\u0026#34;: \u0026#34;twitter:card\u0026#34;} ) twitter_title = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;name\u0026#34;: \u0026#34;twitter:title\u0026#34;} ) twitter_description = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;name\u0026#34;: \u0026#34;twitter:description\u0026#34;} ) twitter_image = soup.find( \u0026#34;meta\u0026#34;, attrs={\u0026#34;name\u0026#34;: \u0026#34;twitter:image\u0026#34;} ) print(\u0026#34;Twitter Card:\u0026#34;, twitter_card.get(\u0026#34;content\u0026#34;) if twitter_card else None) print(\u0026#34;Twitter Title:\u0026#34;, twitter_title.get(\u0026#34;content\u0026#34;) if twitter_title else None) print(\u0026#34;Twitter Description:\u0026#34;, twitter_description.get(\u0026#34;content\u0026#34;) if twitter_description else None) print(\u0026#34;Twitter Image:\u0026#34;, twitter_image.get(\u0026#34;content\u0026#34;) if twitter_image else None) Extract Canonical URL The canonical URL is usually defined as:\n\u0026lt;link rel=\u0026#34;canonical\u0026#34; href=\u0026#34;https://example.com/page\u0026#34; /\u0026gt; Extract it with:\ncanonical = soup.find( \u0026#34;link\u0026#34;, attrs={\u0026#34;rel\u0026#34;: \u0026#34;canonical\u0026#34;} ) print( \u0026#34;Canonical:\u0026#34;, canonical.get(\u0026#34;href\u0026#34;) if canonical else None ) Create a Reusable Metadata Function Instead of extracting each tag separately, you can create a reusable function:\nimport requests from bs4 import BeautifulSoup def get_meta(soup, *, name=None, property=None): if name: tag = soup.find(\u0026#34;meta\u0026#34;, attrs={\u0026#34;name\u0026#34;: name}) else: tag = soup.find(\u0026#34;meta\u0026#34;, attrs={\u0026#34;property\u0026#34;: property}) return tag.get(\u0026#34;content\u0026#34;) if tag else None def scrape_metadata(url): response = requests.get( url, headers={ \u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0\u0026#34; }, timeout=10 ) response.raise_for_status() soup = BeautifulSoup(response.text, \u0026#34;html.parser\u0026#34;) return { \u0026#34;title\u0026#34;: soup.title.string.strip() if soup.title and soup.title.string else None, \u0026#34;description\u0026#34;: get_meta( soup, name=\u0026#34;description\u0026#34; ), \u0026#34;keywords\u0026#34;: get_meta( soup, name=\u0026#34;keywords\u0026#34; ), \u0026#34;canonical\u0026#34;: ( soup.find(\u0026#34;link\u0026#34;, rel=\u0026#34;canonical\u0026#34;).get(\u0026#34;href\u0026#34;) if soup.find(\u0026#34;link\u0026#34;, rel=\u0026#34;canonical\u0026#34;) else None ), \u0026#34;og_title\u0026#34;: get_meta( soup, property=\u0026#34;og:title\u0026#34; ), \u0026#34;og_description\u0026#34;: get_meta( soup, property=\u0026#34;og:description\u0026#34; ), \u0026#34;og_image\u0026#34;: get_meta( soup, property=\u0026#34;og:image\u0026#34; ), \u0026#34;og_url\u0026#34;: get_meta( soup, property=\u0026#34;og:url\u0026#34; ), \u0026#34;twitter_card\u0026#34;: get_meta( soup, name=\u0026#34;twitter:card\u0026#34; ), \u0026#34;twitter_title\u0026#34;: get_meta( soup, name=\u0026#34;twitter:title\u0026#34; ), \u0026#34;twitter_description\u0026#34;: get_meta( soup, name=\u0026#34;twitter:description\u0026#34; ), \u0026#34;twitter_image\u0026#34;: get_meta( soup, name=\u0026#34;twitter:image\u0026#34; ), } url = \u0026#34;https://example.com\u0026#34; metadata = scrape_metadata(url) for key, value in metadata.items(): print(f\u0026#34;{key}: {value}\u0026#34;) Return Metadata as JSON If you want to use the scraper as part of an API or another application, returning JSON is more useful.\nimport json metadata = scrape_metadata(\u0026#34;https://example.com\u0026#34;) print( json.dumps( metadata, indent=4, ensure_ascii=False ) ) Example:\n{ \u0026#34;title\u0026#34;: \u0026#34;Example Domain\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;This domain is for use in illustrative examples.\u0026#34;, \u0026#34;keywords\u0026#34;: null, \u0026#34;canonical\u0026#34;: \u0026#34;https://example.com/\u0026#34;, \u0026#34;og_title\u0026#34;: \u0026#34;Example Domain\u0026#34;, \u0026#34;og_description\u0026#34;: \u0026#34;Example website\u0026#34;, \u0026#34;og_image\u0026#34;: \u0026#34;https://example.com/image.jpg\u0026#34;, \u0026#34;og_url\u0026#34;: \u0026#34;https://example.com/\u0026#34;, \u0026#34;twitter_card\u0026#34;: \u0026#34;summary_large_image\u0026#34;, \u0026#34;twitter_title\u0026#34;: \u0026#34;Example Domain\u0026#34;, \u0026#34;twitter_description\u0026#34;: \u0026#34;Example website\u0026#34;, \u0026#34;twitter_image\u0026#34;: \u0026#34;https://example.com/image.jpg\u0026#34; } Scrape All Meta Tags Sometimes you don\u0026rsquo;t know in advance which metadata tags a website uses. In that case, extract all \u0026lt;meta\u0026gt; tags:\nfor tag in soup.find_all(\u0026#34;meta\u0026#34;): name = tag.get(\u0026#34;name\u0026#34;) property_name = tag.get(\u0026#34;property\u0026#34;) content = tag.get(\u0026#34;content\u0026#34;) if content: print( name or property_name, \u0026#34;:\u0026#34;, content ) This is particularly useful when building a generic SEO metadata scraper because different websites may use different metadata conventions.\nHandle HTTP Errors Production code should handle network errors and invalid URLs.\nimport requests try: response = requests.get( url, headers={ \u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0\u0026#34; }, timeout=10 ) response.raise_for_status() except requests.exceptions.Timeout: print(\u0026#34;Request timed out\u0026#34;) except requests.exceptions.RequestException as error: print(\u0026#34;Request failed:\u0026#34;, error) Important: JavaScript-Rendered Websites requests downloads the HTML returned by the server. It does not execute JavaScript.\nTherefore, if a website generates its metadata dynamically using JavaScript, BeautifulSoup may not see the final metadata.\nFor example:\nBrowser │ ├── Download HTML ├── Execute JavaScript └── Render final page Whereas:\nPython requests │ └── Download HTML only For JavaScript-heavy websites, tools such as Playwright or Selenium can be used to load the page in a real browser before extracting the metadata.\nConclusion For most server-rendered websites, the combination of:\nrequests + BeautifulSoup is sufficient for scraping SEO and social metadata.\nYou can extract important fields such as:\nPage title Meta description Meta keywords Canonical URL Open Graph title Open Graph description Open Graph image Open Graph URL Twitter Card Twitter title Twitter description Twitter image This approach is also a good foundation for building an SEO metadata checker, URL preview generator, content crawler, or website auditing tool.\n","permalink":"https://dwij.net/posts/scrape-html-page-metadata-using-python/","summary":"If you need to extract metadata such as title, description, keywords, Open Graph tags, canonical URL, and Twitter Card information from an HTML page, Python makes this easy using requests and BeautifulSoup.\nInstall Required Packages Install the required Python packages:\npip install requests beautifulsoup4 Basic HTML Metadata Scraper Create a Python file named metadata.py:\nimport requests from bs4 import BeautifulSoup url = \u0026#34;https://example.com\u0026#34; response = requests.get( url, headers={ \u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0\u0026#34; }, timeout=10 ) response.","title":"Scrape HTML Page Metadata Using Python"},{"content":"Flutter is now a popular tool for creating UI Designs in no time. Still sometimes you will need an initial push to get going. So presenting here some of the best Freebie UI Templates in Flutter:\n1. Best-Flutter-UI-Templates by Mitesh Chodvadiya Mitesh has done a wonderful job in Designing and Developing this Template. This template gives 3 different UI options and very well coded considering in mind user expectations.\nDownload | Author\n1. Timy Messenger app by janoodleFTW Main Features\nMultiple groups (similar to Teams in Slack). Multiple open or private channels within groups. Sharing of photos and photo collections. React to messages with emoji. Push-notifications for the message and channel updates. Specific channels for events (e.g. containing date, venue). Editing of event channels. Calendar for all upcoming and past events aggregated over all groups and channels. English and German localization. RSVP for events. Download | Author\nSmart course by TheAlphamerc (Sonu Sharma) Smart course app is built in flutter by Sonu Sharma. App design is based on Smart Course designed by Nugraha Jati Utama\nDownload | Author\nNextBusSG by Ninest An app to show everything bus related in Singapore, including bus arrival times and a directory, with extra features.\nDownload - Author\nFlutter Healthcare App by TheAlphamerc (Sonu Sharma) Healthcare app is a design implementaion of Healthcare Mobile App designed by Chirag Chauhan\nDownload | Author\n","permalink":"https://dwij.net/posts/best-freebie-flutter-ui-templates/","summary":"Flutter is now a popular tool for creating UI Designs in no time. Still sometimes you will need an initial push to get going. So presenting here some of the best Freebie UI Templates in Flutter:\n1. Best-Flutter-UI-Templates by Mitesh Chodvadiya Mitesh has done a wonderful job in Designing and Developing this Template. This template gives 3 different UI options and very well coded considering in mind user expectations.\nDownload | Author","title":"Best Freebie Flutter UI Templates"},{"content":"Let’s Encrypt provides free, automated SSL/TLS certificates that can be used to enable HTTPS on an Ubuntu server. Certbot is one of the most popular tools for obtaining and automatically renewing Let’s Encrypt certificates.\nThis guide covers installing Certbot, configuring SSL for Apache and Nginx, testing HTTPS, automatic renewal, and common troubleshooting commands.\nPrerequisites Before installing Certbot, make sure:\nYou have an Ubuntu server. You have sudo or root access. Your domain points to the server\u0026rsquo;s public IP address. Ports 80 and 443 are accessible from the Internet. Your web server is already configured for the domain. You can verify DNS resolution with:\ndig +short example.com Or:\nnslookup example.com Replace example.com with your actual domain.\nInstall Certbot On modern Ubuntu versions, the recommended approach is to install Certbot using Snap.\nFirst, make sure Snap is available:\nsudo apt update sudo apt install snapd Install the Certbot package:\nsudo snap install snapd sudo snap refresh snapd Then install Certbot:\nsudo snap install --classic certbot Create a symbolic link so that certbot is available from the standard command path:\nsudo ln -s /snap/bin/certbot /usr/bin/certbot Verify the installation:\ncertbot --version You should see output similar to:\ncertbot 5.x.x Get SSL Certificate for Apache If you are using Apache, Certbot can automatically detect your VirtualHost configuration and update it to use HTTPS.\nRun:\nsudo certbot --apache Certbot will:\nDetect your Apache configuration. Ask for your email address. Ask you to accept the Let\u0026rsquo;s Encrypt Terms of Service. Ask which domains should use HTTPS. Obtain the SSL certificate. Update the Apache VirtualHost configuration. Optionally configure HTTP → HTTPS redirection. After completion, open:\nhttps://example.com Your website should now be accessible over HTTPS.\nApache Certificate for a Specific Domain You can also explicitly specify the domain:\nsudo certbot --apache -d example.com -d www.example.com This requests a certificate covering both:\nexample.com www.example.com Get SSL Certificate for Nginx For Nginx, use:\nsudo certbot --nginx Certbot will detect the Nginx server configuration and configure HTTPS automatically.\nYou can also specify domains explicitly:\nsudo certbot --nginx -d example.com -d www.example.com After the certificate is installed, test:\nhttps://example.com Certificate-Only Installation Sometimes you don\u0026rsquo;t want Certbot to modify your web server configuration.\nIn that case, you can use:\nsudo certbot certonly --webroot -w /var/www/example.com -d example.com For multiple domains:\nsudo certbot certonly \\ --webroot \\ -w /var/www/example.com \\ -d example.com \\ -d www.example.com The certificate files will normally be stored under:\n/etc/letsencrypt/live/example.com/ The important files are:\ncert.pem chain.pem fullchain.pem privkey.pem For most web server configurations, you will use:\nfullchain.pem privkey.pem SSL Certificate Location List all Let\u0026rsquo;s Encrypt certificates:\nsudo ls -la /etc/letsencrypt/live/ For a specific domain:\nsudo ls -la /etc/letsencrypt/live/example.com/ You can inspect the certificate:\nsudo certbot certificates Example output:\nCertificate Name: example.com Domains: example.com www.example.com Expiry Date: 2026-11-07 12:00:00+00:00 Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem Configure Apache Manually If you installed the certificate using certonly, you can configure Apache manually.\nExample HTTPS VirtualHost:\n\u0026lt;VirtualHost *:443\u0026gt; ServerName example.com ServerAlias www.example.com DocumentRoot /var/www/example.com/public SSLEngine on SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem \u0026lt;Directory /var/www/example.com/public\u0026gt; AllowOverride All Require all granted \u0026lt;/Directory\u0026gt; ErrorLog ${APACHE_LOG_DIR}/example.com-error.log CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined \u0026lt;/VirtualHost\u0026gt; Make sure the required Apache modules are enabled:\nsudo a2enmod ssl sudo a2enmod rewrite Then test the configuration:\nsudo apache2ctl configtest Expected output:\nSyntax OK Restart Apache:\nsudo systemctl restart apache2 Configure Nginx Manually For Nginx, a typical HTTPS configuration looks like:\nserver { listen 443 ssl; listen [::]:443 ssl; server_name example.com www.example.com; root /var/www/example.com/public; index index.html index.php; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; location / { try_files $uri $uri/ /index.php?$query_string; } } Test the Nginx configuration:\nsudo nginx -t Then reload Nginx:\nsudo systemctl reload nginx Redirect HTTP to HTTPS After SSL is working, it is recommended to redirect HTTP traffic to HTTPS.\nFor Apache:\n\u0026lt;VirtualHost *:80\u0026gt; ServerName example.com ServerAlias www.example.com Redirect permanent / https://example.com/ \u0026lt;/VirtualHost\u0026gt; Alternatively, Certbot can configure the redirect automatically:\nsudo certbot --apache For Nginx:\nserver { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://example.com$request_uri; } Then:\nsudo nginx -t sudo systemctl reload nginx Test SSL Renewal Let\u0026rsquo;s Encrypt certificates have a limited validity period, so automatic renewal is important.\nBefore relying on automatic renewal, perform a dry run:\nsudo certbot renew --dry-run If everything is configured correctly, Certbot should report that the renewal simulation was successful.\nCheck Certbot Renewal Timer When Certbot is installed through Snap, automatic renewal is normally handled by a systemd timer.\nCheck it with:\nsudo systemctl list-timers | grep certbot You can also check the service:\nsudo systemctl status snap.certbot.renew.service Check the timer:\nsudo systemctl status snap.certbot.renew.timer Manually Renew Certificates To renew certificates that are close to expiration:\nsudo certbot renew Certbot automatically determines which certificates need renewal.\nYou generally do not need to manually specify every domain.\nForce Renewal If you specifically need to force certificate renewal:\nsudo certbot renew --force-renewal Use this carefully. There is normally no reason to force renewal on every run.\nFor normal server maintenance, prefer:\nsudo certbot renew Renew a Specific Certificate You can first check the certificate name:\nsudo certbot certificates Then renew a specific certificate:\nsudo certbot renew --cert-name example.com Reload Apache or Nginx After Renewal If your web server needs to reload after certificate renewal, you can use a Certbot deploy hook.\nFor Apache:\nsudo certbot renew --deploy-hook \u0026#34;systemctl reload apache2\u0026#34; For Nginx:\nsudo certbot renew --deploy-hook \u0026#34;systemctl reload nginx\u0026#34; A deploy hook runs only when a certificate is successfully renewed.\nCheck Certificate Expiry You can inspect the certificate using OpenSSL:\nsudo openssl x509 \\ -in /etc/letsencrypt/live/example.com/fullchain.pem \\ -noout \\ -dates Output:\nnotBefore=Aug 09 12:00:00 2026 GMT notAfter=Nov 07 12:00:00 2026 GMT You can also check the certificate directly from the server:\necho | openssl s_client \\ -connect example.com:443 \\ -servername example.com 2\u0026gt;/dev/null \\ | openssl x509 -noout -dates Check Which Certificate Is Being Served This is particularly useful when multiple SSL certificates or web servers are configured.\nRun:\necho | openssl s_client \\ -connect example.com:443 \\ -servername example.com 2\u0026gt;/dev/null \\ | openssl x509 -noout -subject -issuer -dates This shows:\nCertificate subject Certificate issuer Certificate start date Certificate expiration date List All Certbot Certificates Use:\nsudo certbot certificates This is one of the most useful commands when managing multiple websites on the same Ubuntu server.\nDelete an SSL Certificate First list certificates:\nsudo certbot certificates Then delete the certificate:\nsudo certbot delete --cert-name example.com Important: deleting a certificate from Certbot does not necessarily remove or disable the corresponding Apache/Nginx configuration. Make sure your web server configuration no longer references the deleted certificate.\nCommon Certbot Troubleshooting Port 80 Is Not Accessible The HTTP-01 challenge commonly requires the domain to be reachable over HTTP.\nCheck whether Apache or Nginx is listening:\nsudo ss -lntp | grep \u0026#39;:80\u0026#39; Check firewall rules:\nsudo ufw status Allow HTTP and HTTPS if required:\nsudo ufw allow 80/tcp sudo ufw allow 443/tcp Port 443 Is Not Accessible Check:\nsudo ss -lntp | grep \u0026#39;:443\u0026#39; Then check:\nsudo ufw status Allow HTTPS:\nsudo ufw allow 443/tcp If your server is hosted on AWS, DigitalOcean, Azure, or another cloud provider, also check the provider\u0026rsquo;s firewall/security-group rules.\nDNS Is Pointing to the Wrong Server Check:\ndig +short example.com Compare the result with your server\u0026rsquo;s public IP address.\nFor www:\ndig +short www.example.com Both DNS records need to resolve to an appropriate server.\nApache Configuration Error Run:\nsudo apache2ctl configtest Then inspect the logs:\nsudo tail -f /var/log/apache2/error.log Check the Apache service:\nsudo systemctl status apache2 Nginx Configuration Error Run:\nsudo nginx -t Then check:\nsudo systemctl status nginx And:\nsudo tail -f /var/log/nginx/error.log Certbot Logs Certbot logs are stored under:\n/var/log/letsencrypt/ View the latest log:\nsudo ls -lt /var/log/letsencrypt/ You can inspect the main log:\nsudo less /var/log/letsencrypt/letsencrypt.log Check Certbot Version certbot --version Check where Certbot is installed:\nwhich certbot If installed through Snap:\nsnap list certbot Useful Certbot Commands Here is a quick reference:\nCommand Purpose certbot --version Check Certbot version sudo certbot certificates List certificates sudo certbot --apache Configure SSL for Apache sudo certbot --nginx Configure SSL for Nginx sudo certbot certonly Obtain certificate without configuring web server sudo certbot renew Renew certificates sudo certbot renew --dry-run Test renewal sudo certbot renew --force-renewal Force renewal sudo certbot delete --cert-name example.com Delete certificate sudo systemctl status snap.certbot.renew.timer Check automatic renewal timer Recommended SSL Setup Workflow For a new Ubuntu server, a typical workflow is:\nsudo apt update sudo apt install snapd Install Certbot:\nsudo snap install --classic certbot Create the command symlink:\nsudo ln -s /snap/bin/certbot /usr/bin/certbot For Apache:\nsudo certbot --apache -d example.com -d www.example.com Or for Nginx:\nsudo certbot --nginx -d example.com -d www.example.com Then test renewal:\nsudo certbot renew --dry-run Finally, verify the certificate:\nsudo certbot certificates Final Checklist Before considering the SSL setup complete, verify:\nDomain DNS points to the correct server. Port 80 is accessible. Port 443 is accessible. Certbot is installed. SSL certificate has been issued. HTTPS works correctly. HTTP redirects to HTTPS. Apache/Nginx configuration passes its syntax check. Automatic renewal is enabled. certbot renew --dry-run succeeds. Certificate expiration date is correct. With Certbot and Let\u0026rsquo;s Encrypt, SSL certificates can be issued and renewed automatically without purchasing or manually replacing certificates. For Ubuntu servers hosting multiple Apache or Nginx websites, certbot certificates, certbot renew --dry-run, and the web-server configuration tests are especially useful commands to keep in your regular server-maintenance toolkit.\n","permalink":"https://dwij.net/posts/complete-guide-letsencrypt-certbot-ssl-for-ubuntu/","summary":"Let’s Encrypt provides free, automated SSL/TLS certificates that can be used to enable HTTPS on an Ubuntu server. Certbot is one of the most popular tools for obtaining and automatically renewing Let’s Encrypt certificates.\nThis guide covers installing Certbot, configuring SSL for Apache and Nginx, testing HTTPS, automatic renewal, and common troubleshooting commands.\nPrerequisites Before installing Certbot, make sure:\nYou have an Ubuntu server. You have sudo or root access. Your domain points to the server\u0026rsquo;s public IP address.","title":"Complete guide - LetsEncrypt Certbot SSL for Ubuntu"},{"content":"Flutter is Google\u0026rsquo;s UI toolkit for crafting beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Flutter not only enables us to create lavish Mobile apps but it gives whole meaningful Software Architecture to create Platform Independent Mobile Apps.\nFlutter codes are highly reusable as compared to Android or iOS Codes. All you need is to put the library you want to import and Use the codes directly. Now it\u0026rsquo;s more like building Web Application.\nThis Tutorial will discuss from Installing Flutter to Developing your first Hello World Application.\nPrerequisite:\nBasics of Object-Oriented Programming (OOP) Dart (Very similar to JavaScript) Know more about it on https://dart.dev/guides/language/language-tour 1. Installing Flutter The best way to install Flutter is to download the installer from the Flutter website.\nmacOS Linux Windows Once the flutter is downloaded make sure that it\u0026rsquo;s bin folder is added into the path. For macOS or Linux Users\nexport PATH=\u0026#34;$PATH:`pwd`/flutter/bin\u0026#34; Window\u0026rsquo;s users will have to add flutter\\bin path to env (environment variables).\nNow verify your installation by putting command flutter --version in the terminal.\nNow run command flutter doctor to check for missing dependancies.\nSetting up Visual Studio Code (VSCode) VSCode is one of the best lightweight Flutter Editor with almost all functionalities. You can install it from code.visualstudio.com/download. Once downloaded you will need flutter Plugins/Extension.\nSearch for \u0026lsquo;flutter\u0026rsquo; \u0026amp; download \u0026lsquo;** Flutter 3.11.0** \u0026lsquo;+. Now we are all Done with Setup.\n2. Create a Flutter Project Press Cmd+shift+P in VSCode. Now search for \u0026lsquo;Flutter: New Project\u0026rsquo;. Give your project a name in lowercase format joining words with an underscore. Choose the folder where you want to place your project. Once done it will a minute to create a Project. Once project is successfully created you will see the message by VSCode. Create an iOS Simulator Make sure to install XCode before this. Go to the terminal of your Mac are run:\nsudo xcode-select --switch /Applications/Xcode.app/Contents/Developer sudo xcodebuild -runFirstLaunch sudo xcodebuild -license This may ask you to accept the XCode Licence Agreement. Now open a Simulator by running:\nopen -a Simulator Run your Project You will simply have to go to Menu -\u0026gt; Run -\u0026gt; \u0026ldquo;Run without Debugging\u0026rdquo; This will ask you to either create an emulator or select a Simulator. For this project, I will choose iOS Simulator. Note: While running iOS Simulator you may receive the error below. You can check this article for a solution.\nWarning: CocoaPods not installed. Skipping pod install. You application will look like this:\n","permalink":"https://dwij.net/posts/getting-started-with-google-flutter-vscode/","summary":"Flutter is Google\u0026rsquo;s UI toolkit for crafting beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Flutter not only enables us to create lavish Mobile apps but it gives whole meaningful Software Architecture to create Platform Independent Mobile Apps.\nFlutter codes are highly reusable as compared to Android or iOS Codes. All you need is to put the library you want to import and Use the codes directly.","title":"Getting started with Google Flutter on VSCode"},{"content":"Heroku provides a simple way to deploy PHP and Laravel applications without managing the underlying web server. In this guide, we will deploy a Laravel application to Heroku using Git, configure the application environment, and connect it to a MySQL database.\nHeroku\u0026rsquo;s PHP runtime automatically handles PHP, Composer, Apache/Nginx, and PHP-FPM. ([Heroku Dev Center][2])\n1. Prerequisites Before deploying the Laravel application, make sure the following are installed and working on your local machine:\nPHP Composer Git Heroku CLI A Heroku account An existing Laravel application On macOS, install the Heroku CLI using Homebrew:\nbrew install heroku/brew/heroku Login to Heroku:\nheroku login Verify that PHP, Composer, and Git are available:\nphp --version composer --version git --version heroku --version 2. Prepare the Laravel Application Go inside your Laravel project:\ncd /path/to/your/laravel-project Make sure your Laravel project has a composer.json and composer.lock file.\nIt is also a good practice to test the application locally before deploying:\nphp artisan serve If your Laravel application uses frontend assets, make sure those assets are built before deployment or configure the appropriate Node.js build process in Heroku.\n3. Create a Procfile Laravel\u0026rsquo;s document root is the public/ directory. Therefore, Heroku needs to be instructed to serve the application from that directory.\nCreate a file named exactly Procfile in the root of your Laravel project:\necho \u0026#34;web: heroku-php-apache2 public/\u0026#34; \u0026gt; Procfile The Procfile must not have an extension such as .txt and must be located in the project root. ([Heroku Dev Center][3])\nThe contents should be:\nweb: heroku-php-apache2 public/ Add and commit the file:\ngit add Procfile git commit -m \u0026#34;Add Heroku Procfile\u0026#34; 4. Create a Heroku Application Create a new Heroku application:\nheroku create laplus-heroku This creates the Heroku application and adds a heroku Git remote to your local repository.\nYou can verify it using:\ngit remote -v You should see something similar to:\nheroku https://git.heroku.com/laplus-heroku.git 5. Configure the PHP Buildpack For a traditional Heroku Cedar application, you can explicitly configure the PHP buildpack:\nheroku buildpacks:set heroku/php --app laplus-heroku Heroku can also automatically detect PHP applications from files such as composer.json, but explicitly setting the buildpack can be useful when a project contains multiple types of application files. ([Heroku Dev Center][4])\n6. Configure the Laravel APP_KEY Laravel requires an application encryption key.\nGenerate the key locally and configure it as a Heroku Config Var:\nheroku config:set APP_KEY=\u0026#34;$(php artisan key:generate --show --no-ansi)\u0026#34; --app laplus-heroku Check the configured environment variables:\nheroku config --app laplus-heroku Do not commit your .env file to Git.\nHeroku Config Vars are the appropriate place for production environment variables such as:\nAPP_KEY DB_HOST DB_DATABASE DB_USERNAME DB_PASSWORD 7. Configure Laravel Logging Heroku collects application output from stdout and stderr and provides it through its logging system. ([Heroku Dev Center][2])\nFor older Laravel applications, you may have configuration such as:\n\u0026#39;log\u0026#39; =\u0026gt; \u0026#39;errorlog\u0026#39;, However, for newer Laravel applications, logging is normally configured through the LOG_CHANNEL environment variable.\nFor example:\nheroku config:set LOG_CHANNEL=errorlog --app laplus-heroku The exact logging configuration depends on your Laravel version and config/logging.php.\nYou can view the application logs using:\nheroku logs --tail --app laplus-heroku This is generally preferable to writing application logs to local files because Heroku\u0026rsquo;s filesystem is ephemeral.\n8. Deploy Laravel Application Make sure all changes are committed:\ngit add . git commit -m \u0026#34;Prepare Laravel application for Heroku\u0026#34; Heroku deployments should use the main branch:\ngit push heroku main If your local branch is not named main, you can explicitly push it:\ngit push heroku your-branch:main During deployment, Heroku detects the PHP application, installs the required PHP runtime and Composer dependencies, and starts the process defined in the Procfile. ([Heroku Dev Center][2])\n9. Open the Laravel Application Once deployment is complete:\nheroku open --app laplus-heroku Or get the application URL:\nheroku info --app laplus-heroku Your Laravel application should now be accessible through the Heroku URL.\n10. Setup MySQL Database You can add a MySQL-compatible database through a Heroku Add-on such as JawsDB Maria or JawsDB MySQL. Both are currently listed in Heroku\u0026rsquo;s Add-ons documentation. ([Heroku Dev Center][5])\nFor example, if your Heroku account has access to JawsDB Maria:\nheroku addons:create jawsdb:maria --app laplus-heroku The exact plan name can vary, so you can first check the available plans:\nheroku addons:plans jawsdb:maria After adding the database, check the Config Vars:\nheroku config --app laplus-heroku The add-on may provide a database URL such as:\nJAWSDB_URL Depending on the add-on and Laravel version, you can either configure Laravel using the individual database variables or parse the provided database URL.\nFor example:\nDB_CONNECTION=mysql DB_HOST=xxxxxxxx DB_PORT=3306 DB_DATABASE=xxxxxxxx DB_USERNAME=xxxxxxxx DB_PASSWORD=xxxxxxxx Set them using Heroku Config Vars:\nheroku config:set DB_CONNECTION=mysql --app laplus-heroku heroku config:set DB_HOST=xxxxxxxx --app laplus-heroku heroku config:set DB_PORT=3306 --app laplus-heroku heroku config:set DB_DATABASE=xxxxxxxx --app laplus-heroku heroku config:set DB_USERNAME=xxxxxxxx --app laplus-heroku heroku config:set DB_PASSWORD=xxxxxxxx --app laplus-heroku Alternatively, these values can be configured from:\nHeroku Dashboard → Application → Settings → Config Vars\n11. Configure Application URL Configure the Laravel application URL:\nheroku config:set APP_URL=https://laplus-heroku.herokuapp.com --app laplus-heroku If your application uses a custom environment variable such as REDIRECT_HTTPS, configure it as well:\nheroku config:set REDIRECT_HTTPS=true --app laplus-heroku Your application might therefore have Config Vars similar to:\nAPP_ENV=production APP_KEY=base64:xxxxxxxx APP_URL=https://laplus-heroku.herokuapp.com DB_CONNECTION=mysql DB_HOST=xxxxxxxx DB_PORT=3306 DB_DATABASE=xxxxxxxx DB_USERNAME=xxxxxxxx DB_PASSWORD=xxxxxxxx LOG_CHANNEL=errorlog REDIRECT_HTTPS=true 12. Run Laravel Database Migration Once the database connection is configured, run Laravel migrations using a Heroku one-off dyno:\nheroku run php artisan migrate --app laplus-heroku If you also need to seed the database:\nheroku run php artisan migrate --seed --app laplus-heroku For a development/test environment where you intentionally want to recreate all tables:\nheroku run php artisan migrate:fresh --seed --app laplus-heroku Be careful with migrate:fresh in production because it drops all database tables before recreating them.\nYour original command:\nheroku run --app laplus-heroku php artisan migrate:refresh --seed is valid Laravel syntax, but migrate:refresh rolls back migrations and then runs them again. For normal production deployments, prefer:\nheroku run php artisan migrate --app laplus-heroku 13. Clear Laravel Cache After changing environment variables or configuration, clear Laravel\u0026rsquo;s cached configuration:\nheroku run php artisan optimize:clear --app laplus-heroku You can also run:\nheroku run php artisan config:clear --app laplus-heroku For production optimization, Laravel can cache configuration:\nheroku run php artisan config:cache --app laplus-heroku 14. Check Heroku Application Status Check the running dynos:\nheroku ps --app laplus-heroku You should see something similar to:\n=== web (Basic): heroku-php-apache2 public/ (1) web.1: up Heroku\u0026rsquo;s web process is special because it receives HTTP traffic from Heroku\u0026rsquo;s routing layer. ([Heroku Dev Center][3])\n15. View Laravel / Heroku Logs To continuously monitor logs:\nheroku logs --tail --app laplus-heroku You can also view recent logs:\nheroku logs --app laplus-heroku This is especially useful when Laravel returns a 500 Internal Server Error.\nFor example:\nheroku logs --tail --app laplus-heroku Then open the application in your browser and reproduce the error.\n16. Deploy Future Changes Once the initial deployment is complete, future deployments are simple.\nMake your Laravel changes:\ngit add . git commit -m \u0026#34;Update Laravel application\u0026#34; Then deploy:\ngit push heroku main If database migrations are included:\nheroku run php artisan migrate --app laplus-heroku Then clear the Laravel cache if required:\nheroku run php artisan optimize:clear --app laplus-heroku 17. Useful Heroku Commands Here are some commands that are useful when managing a Laravel application on Heroku:\n# Login heroku login # List applications heroku apps # Application information heroku info --app laplus-heroku # View Config Vars heroku config --app laplus-heroku # Set Config Var heroku config:set KEY=value --app laplus-heroku # View logs heroku logs --tail --app laplus-heroku # Check dynos heroku ps --app laplus-heroku # Open application heroku open --app laplus-heroku # Open Heroku bash heroku run bash --app laplus-heroku # Run Laravel Artisan command heroku run php artisan migrate --app laplus-heroku # Clear Laravel cache heroku run php artisan optimize:clear --app laplus-heroku Final Deployment Flow The basic Laravel deployment process can be summarized as:\n# Install Heroku CLI brew install heroku/brew/heroku # Login heroku login # Go to Laravel project cd /path/to/laravel-project # Create Procfile echo \u0026#34;web: heroku-php-apache2 public/\u0026#34; \u0026gt; Procfile # Commit git add . git commit -m \u0026#34;Prepare Laravel application for Heroku\u0026#34; # Create Heroku application heroku create laplus-heroku # Configure PHP buildpack heroku buildpacks:set heroku/php --app laplus-heroku # Configure Laravel application key heroku config:set APP_KEY=\u0026#34;$(php artisan key:generate --show --no-ansi)\u0026#34; --app laplus-heroku # Configure application URL heroku config:set APP_URL=https://laplus-heroku.herokuapp.com --app laplus-heroku # Deploy git push heroku main # Run migrations heroku run php artisan migrate --app laplus-heroku # Clear Laravel cache heroku run php artisan optimize:clear --app laplus-heroku # Open application heroku open --app laplus-heroku Done!\n","permalink":"https://dwij.net/posts/how-to-setup-laravel-application-on-heroku/","summary":"Heroku provides a simple way to deploy PHP and Laravel applications without managing the underlying web server. In this guide, we will deploy a Laravel application to Heroku using Git, configure the application environment, and connect it to a MySQL database.\nHeroku\u0026rsquo;s PHP runtime automatically handles PHP, Composer, Apache/Nginx, and PHP-FPM. ([Heroku Dev Center][2])\n1. Prerequisites Before deploying the Laravel application, make sure the following are installed and working on your local machine:","title":"How to setup Laravel Application on Heroku"},{"content":"When you install New Copy of XCode and try to run your application on iOS Simulator from VSCode. You may see this error:\nWarning: CocoaPods not installed. Skipping pod install. CocoaPods is used to retrieve the iOS and macOS platform side\u0026#39;s plugin code that responds to your plugin usage on the Dart side. Without CocoaPods, plugins will not work on iOS or macOS. For more info, see https://flutter.dev/platform-plugins To install: sudo gem install cocoapods CocoaPods not installed or not in valid state. Error launching application on iPhone SE (2nd generation). Exited (sigterm) Solve this by Installing Cocoapods package in brew:\nsudo gem install cocoapods Do Pod Setup\npod setup Done. Now try running Application again\n","permalink":"https://dwij.net/posts/warning-cocoapods-not-installed-mac-flutter-vscode-xcode-ios-simulator/","summary":"When you install New Copy of XCode and try to run your application on iOS Simulator from VSCode. You may see this error:\nWarning: CocoaPods not installed. Skipping pod install. CocoaPods is used to retrieve the iOS and macOS platform side\u0026#39;s plugin code that responds to your plugin usage on the Dart side. Without CocoaPods, plugins will not work on iOS or macOS. For more info, see https://flutter.dev/platform-plugins To install: sudo gem install cocoapods CocoaPods not installed or not in valid state.","title":"Warning: CocoaPods not installed [Mac + Flutter + VSCode + XCode + iOS Simulator]"},{"content":"How to Set Up an Nginx Server on Ubuntu Nginx is one of the most popular web servers for hosting websites and web applications. It is lightweight, fast, and commonly used as a reverse proxy, load balancer, and web server.\nIn this guide, we will set up Nginx on an Ubuntu server and configure it to serve a website.\nPrerequisites You should have:\nAn Ubuntu server SSH access to the server A user with sudo privileges A domain name pointing to the server\u0026rsquo;s IP address For this example, we will use:\nwebsite.com And the website files will be stored in:\n/var/www/website.com 1. Update Ubuntu Packages First, update the package list:\nsudo apt update You can also upgrade the installed packages:\nsudo apt upgrade -y 2. Install Nginx Install Nginx using apt:\nsudo apt install nginx -y After installation, check the Nginx version:\nnginx -v You should see something similar to:\nnginx version: nginx/1.24.x 3. Check Nginx Service Check whether Nginx is running:\nsudo systemctl status nginx If it is not running, start it:\nsudo systemctl start nginx Enable Nginx to start automatically after a server reboot:\nsudo systemctl enable nginx You can verify that it is enabled with:\nsudo systemctl is-enabled nginx 4. Configure the Firewall If you are using UFW, allow HTTP and HTTPS traffic.\nsudo ufw allow \u0026#39;Nginx Full\u0026#39; Check the firewall status:\nsudo ufw status You should see ports 80 and 443 allowed for Nginx.\nIf UFW is disabled, you don\u0026rsquo;t need to enable it just for Nginx. Be careful when changing firewall settings on a remote server because incorrect rules can lock you out of SSH.\n5. Create the Website Directory Create a directory for your website:\nsudo mkdir -p /var/www/website.com Create a simple HTML page:\nsudo nano /var/www/website.com/index.html Add:\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;website.com\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello from Nginx!\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;Nginx is working correctly.\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Save the file.\n6. Set Website Permissions A simple approach is to make the website directory readable by Nginx:\nsudo chown -R www-data:www-data /var/www/website.com Set directory permissions:\nsudo find /var/www/website.com -type d -exec chmod 755 {} \\; Set file permissions:\nsudo find /var/www/website.com -type f -exec chmod 644 {} \\; This gives directories execute/read permissions and files read permissions for the web server.\n7. Create an Nginx Server Block Nginx configuration files are commonly stored under:\n/etc/nginx/sites-available/ Create a configuration file:\nsudo nano /etc/nginx/sites-available/website.com Add:\nserver { listen 80; listen [::]:80; server_name website.com www.website.com; root /var/www/website.com; index index.html; location / { try_files $uri $uri/ =404; } access_log /var/log/nginx/website.com.access.log; error_log /var/log/nginx/website.com.error.log; } Understanding the Configuration The important directives are:\nlisten 80; Nginx listens for HTTP requests on port 80.\nserver_name website.com www.website.com; Defines the domain names handled by this server block.\nroot /var/www/website.com; Defines the document root of the website.\nindex index.html; Defines the default file served when a directory is requested.\ntry_files $uri $uri/ =404; Attempts to find the requested file or directory. If it doesn\u0026rsquo;t exist, Nginx returns HTTP 404.\n8. Enable the Website Nginx uses two directories for virtual host configurations:\n/etc/nginx/sites-available/ and:\n/etc/nginx/sites-enabled/ The configuration is created in sites-available and enabled by creating a symbolic link in sites-enabled.\nRun:\nsudo ln -s /etc/nginx/sites-available/website.com /etc/nginx/sites-enabled/website.com Check the symlink:\nls -l /etc/nginx/sites-enabled/ You should see something similar to:\nwebsite.com -\u0026gt; /etc/nginx/sites-available/website.com 9. Disable the Default Nginx Website Ubuntu\u0026rsquo;s Nginx installation usually enables a default website.\nYou can disable it:\nsudo rm /etc/nginx/sites-enabled/default This prevents the default Nginx page from being served when your server block should handle the request.\n10. Test the Nginx Configuration Before reloading Nginx, always test the configuration:\nsudo nginx -t A successful result looks like:\nnginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful This is an important habit when managing Nginx servers.\nNever blindly reload Nginx after changing configuration. Test it first.\n11. Reload Nginx If the configuration test succeeds:\nsudo systemctl reload nginx You can also use:\nsudo nginx -s reload For normal configuration changes, a reload is preferable to restarting the service because existing connections can continue to be handled gracefully.\n12. Verify the Website Make sure your domain\u0026rsquo;s DNS record points to the server.\nFor example:\nA website.com YOUR_SERVER_IP A www.website.com YOUR_SERVER_IP Then open:\nhttp://website.com You should see:\nHello from Nginx! Nginx is working correctly. You can also test from the command line:\ncurl -I http://website.com A successful response should contain:\nHTTP/1.1 200 OK 13. Check Nginx Logs Nginx logs are extremely useful when troubleshooting.\nFor our website:\nsudo tail -f /var/log/nginx/website.com.access.log For errors:\nsudo tail -f /var/log/nginx/website.com.error.log You can also inspect the general Nginx logs:\nsudo tail -f /var/log/nginx/access.log and:\nsudo tail -f /var/log/nginx/error.log When debugging a website, the error log should usually be one of the first places you check.\n14. Useful Nginx Commands Here are some commands you will frequently use while managing Nginx.\nCheck status sudo systemctl status nginx Start Nginx sudo systemctl start nginx Stop Nginx sudo systemctl stop nginx Restart Nginx sudo systemctl restart nginx Reload configuration sudo systemctl reload nginx Test configuration sudo nginx -t Check version nginx -v View enabled websites ls -la /etc/nginx/sites-enabled/ View available websites ls -la /etc/nginx/sites-available/ 15. Nginx Configuration Structure After installation, an Ubuntu Nginx server typically has a structure similar to:\n/etc/nginx/ ├── nginx.conf ├── sites-available/ │ ├── default │ └── website.com ├── sites-enabled/ │ └── website.com -\u0026gt; ../sites-available/website.com ├── conf.d/ ├── snippets/ └── mime.types The main configuration file is:\n/etc/nginx/nginx.conf Website-specific server blocks are generally maintained under:\n/etc/nginx/sites-available/ and enabled through:\n/etc/nginx/sites-enabled/ This separation makes it easier to manage multiple websites on the same server.\n16. Hosting Multiple Websites One of Nginx\u0026rsquo;s strengths is the ability to host multiple websites on a single server.\nFor example:\n/var/www/ ├── website.com/ ├── example.com/ └── api.example.com/ Each website can have its own server block:\n/etc/nginx/sites-available/ ├── website.com ├── example.com └── api.example.com and corresponding symbolic links:\n/etc/nginx/sites-enabled/ ├── website.com ├── example.com └── api.example.com Nginx determines which configuration should handle a request based primarily on the requested hostname and port.\n17. Recommended Workflow for Nginx Changes When making changes to an Nginx server, use this workflow:\nsudo nano /etc/nginx/sites-available/website.com Then test:\nsudo nginx -t If successful:\nsudo systemctl reload nginx Then verify:\ncurl -I http://website.com And if something goes wrong:\nsudo tail -f /var/log/nginx/website.com.error.log This simple workflow prevents many common Nginx configuration problems.\nConclusion Setting up Nginx on Ubuntu is straightforward once you understand the basic structure of Nginx server blocks.\nThe typical process is:\nInstall Nginx ↓ Create website directory ↓ Create server block ↓ Enable server block ↓ Test configuration ↓ Reload Nginx ↓ Verify website The most important commands to remember are:\nsudo apt install nginx -y sudo nginx -t sudo systemctl reload nginx sudo systemctl status nginx Once the basic Nginx setup is working, you can build on it with HTTPS/SSL using Certbot, PHP-FPM for PHP applications, reverse proxy configuration for Node.js applications, caching, compression, security headers, rate limiting, and load balancing.\n","permalink":"https://dwij.net/posts/how-to-setup-nginx-server-on-ubuntu/","summary":"How to Set Up an Nginx Server on Ubuntu Nginx is one of the most popular web servers for hosting websites and web applications. It is lightweight, fast, and commonly used as a reverse proxy, load balancer, and web server.\nIn this guide, we will set up Nginx on an Ubuntu server and configure it to serve a website.\nPrerequisites You should have:\nAn Ubuntu server SSH access to the server A user with sudo privileges A domain name pointing to the server\u0026rsquo;s IP address For this example, we will use:","title":"How to setup Nginx Server on Ubuntu"},{"content":"Use multiple PHP versions on same server and manage currently running (default) php version for terminal.\nInstall multiple PHP Versions:\nsudo add-apt-repository ppa:ondrej/php sudo apt-get update sudo apt-get install php8.1 sudo apt-get install php8.4 sudo a2enmod php8.1 sudo a2enmod php8.4 Switch:\nsudo update-alternatives --config php ","permalink":"https://dwij.net/posts/how-to-switch-php-versions-on-lamp-server/","summary":"Use multiple PHP versions on same server and manage currently running (default) php version for terminal.\nInstall multiple PHP Versions:\nsudo add-apt-repository ppa:ondrej/php sudo apt-get update sudo apt-get install php8.1 sudo apt-get install php8.4 sudo a2enmod php8.1 sudo a2enmod php8.4 Switch:\nsudo update-alternatives --config php ","title":"How to switch PHP Versions on LAMP Server"},{"content":"from PIL import Image import shutil import os, sys basewidth = 1600 def compress(filename, destfilename, quality_num, cropsize = (), whitebg = False): # Make PNG Background White if whitebg: fill_color = (255,255,255) im = Image.open(filename) im = im.convert(\u0026#39;RGBA\u0026#39;) background = Image.new(im.mode[:-1], im.size, fill_color) background.paste(im, im.split()[-1]) # omit transparency original = background else: original = Image.open(filename) # Check PNG Images with no Trasperant Data if filename.endswith(\u0026#34;.png\u0026#34;): original = original.convert(\u0026#39;RGBA\u0026#39;) datas = original.getdata() flagTrasperant = False for item in datas: if(len(item) \u0026lt; 4): flagTrasperant = False break elif item[3] == 0: flagTrasperant = True break if not flagTrasperant: # If given image does not contain transperant pixels -\u0026gt; convert it into JPG destfilename = destfilename.replace(\u0026#34;.png\u0026#34;, \u0026#34;.jpg\u0026#34;) original = original.convert(\u0026#39;RGB\u0026#39;) # Resize Logic width, height = original.size if width \u0026gt; 1600 or height \u0026gt; 1600: width2, height2 = getResizeSize(width, height) original = original.resize((width2, height2), Image.ANTIALIAS) original.save(destfilename, quality = quality_num) else: original.save(destfilename, quality = quality_num) # Crop Logic if(len(cropsize) \u0026gt; 0): # left, top, right, bottom original = Image.open(destfilename) original = original.crop(cropsize) original.save(destfilename, quality = quality_num) return destfilename def getResizeSize(width, height): if ( width - height ) \u0026gt; 50: # Horizontal wpercent = (basewidth / float(width)) hsize = int((float(height) * float(wpercent))) return basewidth, hsize elif ( height - width ) \u0026gt; 50: # Vertical wpercent = (basewidth / float(height)) wsize = int((float(width) * float(wpercent))) return wsize, basewidth else: # Comparitively Square wpercent = (basewidth / float(width)) hsize = int((float(height) * float(wpercent))) return basewidth, hsize print \u0026#34;----------------------------------- Start -----------------------------------\u0026#34; print \u0026#34;folder\u0026#34;, \u0026#34;\\t\u0026#34;, \u0026#34;width\u0026#34;, \u0026#34;x\u0026#34;, \u0026#34;height\u0026#34;, \u0026#34;\\t \u0026#34;, \u0026#34;width new\u0026#34;, \u0026#34;x\u0026#34;, \u0026#34;height new\u0026#34;, \u0026#34;\\t\u0026#34;, \u0026#34;size old\u0026#34;, \u0026#34;\\t\u0026#34;, \u0026#34;size new\u0026#34;, \u0026#34;\\t\u0026#34;, \u0026#34;Size Diff\u0026#34;, \u0026#34;\\t\u0026#34;, \u0026#34;filename\u0026#34;, \u0026#34;\\t\u0026#34;, \u0026#34;destfilename\u0026#34;, \u0026#34;\\t\u0026#34;, \u0026#34;Is Converted\u0026#34; count = 0 count_dim = 0 count_highsize = 0 count_versions = 0 folders = [\u0026#34;Multiple\u0026#34;, \u0026#34;Input\u0026#34;, \u0026#34;Folders\u0026#34;] for folder in folders: for filename in os.listdir(folder): if os.path.exists(\u0026#34;Converted/\u0026#34; + folder + \u0026#34;/\u0026#34; + filename): continue if filename.endswith(\u0026#34;.png\u0026#34;) or filename.endswith(\u0026#34;.jpg\u0026#34;): if \u0026#34;x\u0026#34; not in filename: # print folder + \u0026#34;/\u0026#34; + filename im = Image.open(folder + \u0026#34;/\u0026#34; + filename) width, height = im.size size = os.stat(folder + \u0026#34;/\u0026#34; + filename).st_size / 1000 if width \u0026gt; 1600 or height \u0026gt; 1600: width2, height2 = getResizeSize(width, height) destfilename = compress(folder + \u0026#34;/\u0026#34;+filename, \u0026#34;Converted/\u0026#34;+folder+\u0026#34;/\u0026#34;+filename, 90) size_new = os.stat(destfilename).st_size / 1000 if size_new \u0026gt; size: os.remove(destfilename) continue converted = \u0026#34;\u0026#34; if filename not in destfilename: converted = \u0026#34;TRUE\u0026#34; destfilename_short = destfilename.replace(\u0026#34;Converted/\u0026#34;+folder+\u0026#34;/\u0026#34;, \u0026#34;\u0026#34;) print folder, \u0026#34;\\t\u0026#34;, width, \u0026#34;x\u0026#34;, height, \u0026#34;\\t \u0026#34;, width2, \u0026#34;x\u0026#34;, height2, \u0026#34;\\t\u0026#34;, size, \u0026#34;\\t\u0026#34;, size_new, \u0026#34;\\t\u0026#34;, str(size - size_new), \u0026#34;\\t\u0026#34;, filename, \u0026#34;\\t\u0026#34;, destfilename_short, \u0026#34;\\t\u0026#34;, converted count = count + 1 count_dim = count_dim + 1 else: if size \u0026gt; 500: print folder, \u0026#34;\\t\u0026#34;, width, \u0026#34;x\u0026#34;, height, \u0026#34;\\t\\t\u0026#34;, size, \u0026#34;\\t\u0026#34;, filename count = count + 1 count_highsize = count_highsize + 1 else: count_versions = count_versions + 1 print \u0026#34;Total count: \u0026#34;, count print \u0026#34;Total count_dim: \u0026#34;, count_dim print \u0026#34;Total count_highsize: \u0026#34;, count_highsize print \u0026#34;Total count_versions: \u0026#34;, count_versions print \u0026#34;----------------------------------- Done -----------------------------------\u0026#34; Download source file from dwij.net/p/resize_compress.py.\n","permalink":"https://dwij.net/posts/compress-resize-bulk-images-in-python/","summary":"from PIL import Image import shutil import os, sys basewidth = 1600 def compress(filename, destfilename, quality_num, cropsize = (), whitebg = False): # Make PNG Background White if whitebg: fill_color = (255,255,255) im = Image.open(filename) im = im.convert(\u0026#39;RGBA\u0026#39;) background = Image.new(im.mode[:-1], im.size, fill_color) background.paste(im, im.split()[-1]) # omit transparency original = background else: original = Image.open(filename) # Check PNG Images with no Trasperant Data if filename.endswith(\u0026#34;.png\u0026#34;): original = original.convert(\u0026#39;RGBA\u0026#39;) datas = original.","title":"Compress + Resize Bulk Images in python"},{"content":"Laravel provides a convenient way to define scheduled tasks using its task scheduler. Instead of creating a separate Linux cron entry for every Laravel command, you can define your scheduled tasks inside the Laravel application and let Laravel handle when each task should run.\nOn Ubuntu, the most common approach is to use CronTab to execute Laravel\u0026rsquo;s schedule:run command every minute.\nThis article focuses on two practical ways to run Laravel\u0026rsquo;s scheduler:\nUsing Ubuntu CronTab with www-data Using Supervisor as an alternative to CronTab 1. Configure CronTab for the www-data User For a Laravel application hosted by Apache or Nginx, PHP processes commonly run as the www-data user.\nIt is therefore a good practice to run Laravel\u0026rsquo;s scheduler as the same user that owns or operates the application.\nOpen the www-data user\u0026rsquo;s crontab:\nsudo crontab -u www-data -e This opens the CronTab specifically for the www-data user.\nAdd the following entry:\n* * * * * cd /var/www/website.com \u0026amp;\u0026amp; php artisan schedule:run \u0026gt;\u0026gt; /var/www/website.com/storage/cron.log 2\u0026gt;\u0026amp;1 This runs every minute.\nCheck the Cron Configuration After saving the crontab, verify it:\nsudo crontab -u www-data -l You should see:\n* * * * * cd /var/www/website.com \u0026amp;\u0026amp; php artisan schedule:run \u0026gt;\u0026gt; /var/www/website.com/storage/cron.log 2\u0026gt;\u0026amp;1 You can then monitor the Laravel scheduler log:\nsudo tail -f /var/www/website.com/storage/cron.log If Laravel\u0026rsquo;s scheduled commands produce output, it will be written to this file.\n2. Alternative: Running schedule:run Using Supervisor CronTab is the traditional approach, but another option is to use Supervisor to keep the Laravel scheduler process running.\nFirst, install Supervisor:\nsudo apt update sudo apt install supervisor Create a Supervisor configuration:\nsudo nano /etc/supervisor/conf.d/laravel-scheduler.conf Add:\n[program:laravel-scheduler] process_name=%(program_name)s command=/usr/bin/php /var/www/website.com/artisan schedule:run directory=/var/www/website.com autostart=true autorestart=true user=www-data redirect_stderr=true stdout_logfile=/var/www/website.com/storage/cron.log stopwaitsecs=3600 Then reload Supervisor:\nsudo supervisorctl reread sudo supervisorctl update Start the scheduler:\nsudo supervisorctl start laravel-scheduler Check its status:\nsudo supervisorctl status You should see something similar to:\nlaravel-scheduler RUNNING Important Difference There is an important distinction between CronTab and Supervisor.\nWith CronTab:\n* * * * * cd /var/www/website.com \u0026amp;\u0026amp; php artisan schedule:run \u0026gt;\u0026gt; /var/www/website.com/storage/cron.log 2\u0026gt;\u0026amp;1 the operating system starts schedule:run every minute.\nWith Supervisor, the configured command is kept running and Supervisor restarts it if the process exits.\nHowever, php artisan schedule:run is designed as a short-lived scheduler invocation: it checks which tasks are due and then exits. Because of that, using Supervisor directly with schedule:run can cause it to repeatedly restart the process.\nFor a continuously running Laravel scheduler, Laravel\u0026rsquo;s long-running scheduling command is generally a better fit where supported by the Laravel version.\nFor example:\ncommand=/usr/bin/php /var/www/website.com/artisan schedule:work This keeps Laravel\u0026rsquo;s scheduler running continuously, while Supervisor handles process monitoring and automatic restarts.\nThe configuration would then be:\n[program:laravel-scheduler] process_name=%(program_name)s command=/usr/bin/php /var/www/website.com/artisan schedule:work directory=/var/www/website.com autostart=true autorestart=true user=www-data redirect_stderr=true stdout_logfile=/var/www/website.com/storage/cron.log stopwaitsecs=3600 After changing the configuration:\nsudo supervisorctl reread sudo supervisorctl update sudo supervisorctl restart laravel-scheduler CronTab vs Supervisor Feature CronTab Supervisor Setup Simple More configuration schedule:run Excellent fit Not ideal as a persistent process schedule:work Not required Good fit Process monitoring No Yes Automatic restart No Yes Log handling Shell redirection Supervisor logging Recommended for basic scheduling Yes Optional Recommended Setup For most Laravel applications on Ubuntu, the simplest setup is:\nsudo crontab -u www-data -e Then:\n* * * * * cd /var/www/website.com \u0026amp;\u0026amp; php artisan schedule:run \u0026gt;\u0026gt; /var/www/website.com/storage/cron.log 2\u0026gt;\u0026amp;1 This is lightweight, easy to troubleshoot, and follows Laravel\u0026rsquo;s traditional scheduler deployment model.\nIf you specifically want a continuously running process with process supervision, use Supervisor with:\nphp artisan schedule:work rather than repeatedly restarting:\nphp artisan schedule:run The key principle is simple:\nCronTab triggers Laravel\u0026rsquo;s scheduler; Laravel decides which scheduled tasks should actually run.\n","permalink":"https://dwij.net/posts/laravel-task-scheduling-in-ubuntu-using-cron-tabs/","summary":"Laravel provides a convenient way to define scheduled tasks using its task scheduler. Instead of creating a separate Linux cron entry for every Laravel command, you can define your scheduled tasks inside the Laravel application and let Laravel handle when each task should run.\nOn Ubuntu, the most common approach is to use CronTab to execute Laravel\u0026rsquo;s schedule:run command every minute.\nThis article focuses on two practical ways to run Laravel\u0026rsquo;s scheduler:","title":"Laravel Task Scheduling in Ubuntu using Cron Tabs vs Supervisor"},{"content":" Open functions.php from your WordPress Child Theme and add the code below:\nadd_action(\u0026#39;wp_footer\u0026#39;, \u0026#39;add_whatsapp_icon\u0026#39;); function add_whatsapp_icon() { echo\u0026#39;\u0026lt;a style=\u0026#34;display:block;position:fixed;left:15px;bottom:10px;z-index:1000;\u0026#34; href=\u0026#34;https://wa.me/918888888888?text=Hi\u0026#34;\u0026gt;\u0026lt;img src=\u0026#34;https://dwij.net/resources/whatsapp.svg\u0026#34; style=\u0026#34;width:50px;height:50px;\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\u0026#39;; } Make sure to update Phone Number.\nYou may also need to update whatsapp.svg url to support Cross-origin Resource Sharing (CORS). Download SVG file from https://dwij.net/resources/whatsapp.svg.\n","permalink":"https://dwij.net/posts/add-floating-whatsapp-button-in-your-wordpress-website/","summary":"Open functions.php from your WordPress Child Theme and add the code below:\nadd_action(\u0026#39;wp_footer\u0026#39;, \u0026#39;add_whatsapp_icon\u0026#39;); function add_whatsapp_icon() { echo\u0026#39;\u0026lt;a style=\u0026#34;display:block;position:fixed;left:15px;bottom:10px;z-index:1000;\u0026#34; href=\u0026#34;https://wa.me/918888888888?text=Hi\u0026#34;\u0026gt;\u0026lt;img src=\u0026#34;https://dwij.net/resources/whatsapp.svg\u0026#34; style=\u0026#34;width:50px;height:50px;\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\u0026#39;; } Make sure to update Phone Number.\nYou may also need to update whatsapp.svg url to support Cross-origin Resource Sharing (CORS). Download SVG file from https://dwij.net/resources/whatsapp.svg.","title":"Add Floating WhatsApp Button in your WordPress Website"},{"content":" Create Folder abcdefg-child Create CSS File style.css /* Theme Name: ABCDEFG Child Theme URL: http://themes.webdevia.com/osterisk-voip-cloud-services-wordpress-theme Description: ABCDEFG Theme Child Author: Dwij IT Solutions Author URL: https://dwijitsolutions.com Template: abcdefg Version: 1.0.0 Text Domain: abcdefg-child */ Create Functions.php Files \u0026lt;?php add_action( \u0026#39;wp_enqueue_scripts\u0026#39;, \u0026#39;enqueue_child_styles\u0026#39;, 100); function enqueue_child_styles() { wp_enqueue_style( \u0026#39;child-style\u0026#39;, get_stylesheet_directory_uri().\u0026#39;/style.css\u0026#39;); } ?\u0026gt; ","permalink":"https://dwij.net/posts/quick-setup-for-wordpress-child-themes/","summary":" Create Folder abcdefg-child Create CSS File style.css /* Theme Name: ABCDEFG Child Theme URL: http://themes.webdevia.com/osterisk-voip-cloud-services-wordpress-theme Description: ABCDEFG Theme Child Author: Dwij IT Solutions Author URL: https://dwijitsolutions.com Template: abcdefg Version: 1.0.0 Text Domain: abcdefg-child */ Create Functions.php Files \u0026lt;?php add_action( \u0026#39;wp_enqueue_scripts\u0026#39;, \u0026#39;enqueue_child_styles\u0026#39;, 100); function enqueue_child_styles() { wp_enqueue_style( \u0026#39;child-style\u0026#39;, get_stylesheet_directory_uri().\u0026#39;/style.css\u0026#39;); } ?\u0026gt; ","title":"Quick Setup for WordPress Child Themes"},{"content":"sudo apt-get install npm npm i cloudcmd -g Create Configuration:\nsudo nano ~/.cloudcmd.json Put Content\n{ \u0026#34;name\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;auth\u0026#34;: true, \u0026#34;username\u0026#34;: \u0026#34;root\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;algo\u0026#34;: \u0026#34;SHA1\u0026#34;, \u0026#34;editor\u0026#34;: \u0026#34;edward\u0026#34;, \u0026#34;packer\u0026#34;: \u0026#34;zip\u0026#34;, \u0026#34;diff\u0026#34;: true, \u0026#34;zip\u0026#34;: true, \u0026#34;buffer\u0026#34;: true, \u0026#34;dirStorage\u0026#34;: true, \u0026#34;online\u0026#34;: true, \u0026#34;open\u0026#34;: false, \u0026#34;oneFilePanel\u0026#34;: false, \u0026#34;keysPanel\u0026#34;: true, \u0026#34;port\u0026#34;: 8000, \u0026#34;ip\u0026#34;: null, \u0026#34;root\u0026#34;: \u0026#34;/var/www\u0026#34;, \u0026#34;prefix\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;progress\u0026#34;: true, \u0026#34;confirmCopy\u0026#34;: true, \u0026#34;confirmMove\u0026#34;: true, \u0026#34;showConfig\u0026#34;: false, \u0026#34;showFileName\u0026#34;: true, \u0026#34;contact\u0026#34;: true, \u0026#34;configDialog\u0026#34;: true, \u0026#34;console\u0026#34;: true, \u0026#34;syncConsolePath\u0026#34;: false, \u0026#34;terminal\u0026#34;: false, \u0026#34;terminalPath\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;terminalCommand\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;terminalAutoRestart\u0026#34;: true, \u0026#34;vim\u0026#34;: false, \u0026#34;columns\u0026#34;: \u0026#34;name-size-date-owner-mode\u0026#34;, \u0026#34;export\u0026#34;: false, \u0026#34;exportToken\u0026#34;: \u0026#34;root\u0026#34;, \u0026#34;import\u0026#34;: false, \u0026#34;import-url\u0026#34;: \u0026#34;http://localhost:8000\u0026#34;, \u0026#34;importToken\u0026#34;: \u0026#34;root\u0026#34;, \u0026#34;importListen\u0026#34;: false, \u0026#34;log\u0026#34;: true } Put Password:\nsudo cloudcmd --username root --password \u0026#34;ABCDEFG\u0026#34; --auth --save --no-server Open Ports:\nsudo ufw allow 8000 Check Port Status:\nsudo ufw status Enable CloudCmd at Startup:\nsudo crontab -e Put Line:\n@reboot /usr/bin/cloudcmd Start Server:\ncloudcmd \u0026amp; ","permalink":"https://dwij.net/posts/getting-started-with-cloud-commander-best-file-manager-for-your-vps/","summary":"sudo apt-get install npm npm i cloudcmd -g Create Configuration:\nsudo nano ~/.cloudcmd.json Put Content\n{ \u0026#34;name\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;auth\u0026#34;: true, \u0026#34;username\u0026#34;: \u0026#34;root\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;algo\u0026#34;: \u0026#34;SHA1\u0026#34;, \u0026#34;editor\u0026#34;: \u0026#34;edward\u0026#34;, \u0026#34;packer\u0026#34;: \u0026#34;zip\u0026#34;, \u0026#34;diff\u0026#34;: true, \u0026#34;zip\u0026#34;: true, \u0026#34;buffer\u0026#34;: true, \u0026#34;dirStorage\u0026#34;: true, \u0026#34;online\u0026#34;: true, \u0026#34;open\u0026#34;: false, \u0026#34;oneFilePanel\u0026#34;: false, \u0026#34;keysPanel\u0026#34;: true, \u0026#34;port\u0026#34;: 8000, \u0026#34;ip\u0026#34;: null, \u0026#34;root\u0026#34;: \u0026#34;/var/www\u0026#34;, \u0026#34;prefix\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;progress\u0026#34;: true, \u0026#34;confirmCopy\u0026#34;: true, \u0026#34;confirmMove\u0026#34;: true, \u0026#34;showConfig\u0026#34;: false, \u0026#34;showFileName\u0026#34;: true, \u0026#34;contact\u0026#34;: true, \u0026#34;configDialog\u0026#34;: true, \u0026#34;console\u0026#34;: true, \u0026#34;syncConsolePath\u0026#34;: false, \u0026#34;terminal\u0026#34;: false, \u0026#34;terminalPath\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;terminalCommand\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;terminalAutoRestart\u0026#34;: true, \u0026#34;vim\u0026#34;: false, \u0026#34;columns\u0026#34;: \u0026#34;name-size-date-owner-mode\u0026#34;, \u0026#34;export\u0026#34;: false, \u0026#34;exportToken\u0026#34;: \u0026#34;root\u0026#34;, \u0026#34;import\u0026#34;: false, \u0026#34;import-url\u0026#34;: \u0026#34;http://localhost:8000\u0026#34;, \u0026#34;importToken\u0026#34;: \u0026#34;root\u0026#34;, \u0026#34;importListen\u0026#34;: false, \u0026#34;log\u0026#34;: true } Put Password:","title":"Getting started with Cloud Commander – Best File Manager for your VPS"},{"content":"Visual Studio Code Tut https://laracasts.com/series/visual-studio-code-for-php-developers Keyboard changes: Go to Symbol in File -\u0026gt; Cmd + R Go to Symbol in Workspace -\u0026gt; Cmd + shift + R User Settings { \u0026#34;extensions.ignoreRecommendations\u0026#34;: true, \u0026#34;typescript.check.npmIsInstalled\u0026#34;: false, \u0026#34;editor.tabSize\u0026#34;: 4, \u0026#34;editor.insertSpaces\u0026#34;: true, \u0026#34;emmet.syntaxProfiles\u0026#34;: { \u0026#34;blade\u0026#34;: \u0026#34;html\u0026#34; }, \u0026#34;workbench.activityBar.visible\u0026#34;: true, \u0026#34;editor.minimap.enabled\u0026#34;: false, \u0026#34;workbench.panel.location\u0026#34;: \u0026#34;right\u0026#34;, \u0026#34;explorer.openEditors.visible\u0026#34;: 0, \u0026#34;editor.detectIndentation\u0026#34;: false, \u0026#34;git.confirmSync\u0026#34;: false, \u0026#34;git.autofetch\u0026#34;: true, \u0026#34;window.zoomLevel\u0026#34;: 0, \u0026#34;explorer.confirmDelete\u0026#34;: false, \u0026#34;todohighlight.keywords\u0026#34;: [ { \u0026#34;text\u0026#34;: \u0026#34;NOTE:\u0026#34;, \u0026#34;isWholeLine\u0026#34;: false, \u0026#34;color\u0026#34;: \u0026#34;#ff0000\u0026#34;, \u0026#34;backgroundColor\u0026#34;: \u0026#34;yellow\u0026#34;, \u0026#34;overviewRulerColor\u0026#34;: \u0026#34;magenta\u0026#34; }, { \u0026#34;text\u0026#34;: \u0026#34;TODO:\u0026#34;, \u0026#34;isWholeLine\u0026#34;: true, \u0026#34;color\u0026#34;: \u0026#34;white\u0026#34;, \u0026#34;border\u0026#34;: \u0026#34;1px solid #FFFFFF\u0026#34;, \u0026#34;borderRadius\u0026#34;: \u0026#34;20px\u0026#34;, \u0026#34;backgroundColor\u0026#34;: \u0026#34;#48b0f7\u0026#34;, \u0026#34;overviewRulerColor\u0026#34;: \u0026#34;#48b0f7\u0026#34; } ], \u0026#34;todohighlight.defaultStyle\u0026#34;: { \u0026#34;color\u0026#34;: \u0026#34;#FFFFFF\u0026#34;, \u0026#34;backgroundColor\u0026#34;: \u0026#34;#ff6a00\u0026#34; }, \u0026#34;todohighlight.isEnable\u0026#34;: true, \u0026#34;[html]\u0026#34;: { \u0026#34;editor.defaultFormatter\u0026#34;: \u0026#34;lonefy.vscode-JS-CSS-HTML-formatter\u0026#34; } } Required Plugins\nBookmarks GitLens indent-rainbow Laravel Blade Snippets SCSS IntelliSense TODO Highlight Wakatime PHP Intelephense snippet-creator ","permalink":"https://dwij.net/posts/all-you-need-for-vscode-laravel-setup/","summary":"Visual Studio Code Tut https://laracasts.com/series/visual-studio-code-for-php-developers Keyboard changes: Go to Symbol in File -\u0026gt; Cmd + R Go to Symbol in Workspace -\u0026gt; Cmd + shift + R User Settings { \u0026#34;extensions.ignoreRecommendations\u0026#34;: true, \u0026#34;typescript.check.npmIsInstalled\u0026#34;: false, \u0026#34;editor.tabSize\u0026#34;: 4, \u0026#34;editor.insertSpaces\u0026#34;: true, \u0026#34;emmet.syntaxProfiles\u0026#34;: { \u0026#34;blade\u0026#34;: \u0026#34;html\u0026#34; }, \u0026#34;workbench.activityBar.visible\u0026#34;: true, \u0026#34;editor.minimap.enabled\u0026#34;: false, \u0026#34;workbench.panel.location\u0026#34;: \u0026#34;right\u0026#34;, \u0026#34;explorer.openEditors.visible\u0026#34;: 0, \u0026#34;editor.detectIndentation\u0026#34;: false, \u0026#34;git.confirmSync\u0026#34;: false, \u0026#34;git.autofetch\u0026#34;: true, \u0026#34;window.zoomLevel\u0026#34;: 0, \u0026#34;explorer.confirmDelete\u0026#34;: false, \u0026#34;todohighlight.keywords\u0026#34;: [ { \u0026#34;text\u0026#34;: \u0026#34;NOTE:\u0026#34;, \u0026#34;isWholeLine\u0026#34;: false, \u0026#34;color\u0026#34;: \u0026#34;#ff0000\u0026#34;, \u0026#34;backgroundColor\u0026#34;: \u0026#34;yellow\u0026#34;, \u0026#34;overviewRulerColor\u0026#34;: \u0026#34;magenta\u0026#34; }, { \u0026#34;text\u0026#34;: \u0026#34;TODO:\u0026#34;, \u0026#34;isWholeLine\u0026#34;: true, \u0026#34;color\u0026#34;: \u0026#34;white\u0026#34;, \u0026#34;border\u0026#34;: \u0026#34;1px solid #FFFFFF\u0026#34;, \u0026#34;borderRadius\u0026#34;: \u0026#34;20px\u0026#34;, \u0026#34;backgroundColor\u0026#34;: \u0026#34;#48b0f7\u0026#34;, \u0026#34;overviewRulerColor\u0026#34;: \u0026#34;#48b0f7\u0026#34; } ], \u0026#34;todohighlight.","title":"All you need for VSCode Laravel Setup"},{"content":"Many times while displaying time on website we need to do something extra.\nThis function script can give you time in days/hour/minutes/seconds ago format.\nYou just need to pass the mysql time to this function. like this:\necho timeago(\u0026#34;2013-11-24 18:00:09\u0026#34;); function timeago($timeStr) { $time = strtotime($timeStr); $output = \u0026#34;\u0026#34;; $time_difference = time() - $time; $seconds = $time_difference ; $minutes = round($time_difference / 60 ); $hours = round($time_difference / 3600 ); $days = round($time_difference / 86400 ); $weeks = round($time_difference / 604800 ); $months = round($time_difference / 2419200 ); $years = round($time_difference / 29030400 ); if($seconds \u0026lt;= 60) { // Seconds $output = \u0026#34;$seconds seconds ago\u0026#34;; } else if($minutes \u0026lt;=60) { //Minutes if($minutes==1) { $output = \u0026#34;one minute ago\u0026#34;; } else { $output = \u0026#34;$minutes minutes ago\u0026#34;; } } else if($hours \u0026lt;=24) { //Hours if($hours==1) { $output = \u0026#34;one hour ago\u0026#34;; } else { $output = \u0026#34;$hours hours ago\u0026#34;; } } else if($days \u0026lt;= 7) { //Days if($days==1) { $output = \u0026#34;one day ago\u0026#34;; } else { $output = \u0026#34;$days days ago\u0026#34;; } } else if($weeks \u0026lt;= 4) { //Weeks if($weeks==1) { $output = \u0026#34;one week ago\u0026#34;; } else { $output = \u0026#34;$weeks weeks ago\u0026#34;; } } else if($months \u0026lt;=12) { //Months if($months==1) { $output = \u0026#34;one month ago\u0026#34;; } else { $output = \u0026#34;$months months ago\u0026#34;; } } else { //Years if($years==1) { $output = \u0026#34;one year ago\u0026#34;; } else { $output = \u0026#34;$years years ago\u0026#34;; } } return $output; } Note : Sometimes you may get time error while using this function because of timezone.\nIn such case you might need to correct time difference by subtracting the difference from time-stamp. You jst need to modify the first line of function by $time = strtotime($timeStr) - 37800; where 37800 is time difference in milliseconds.\n","permalink":"https://dwij.net/posts/creating-timeago-function-in-php/","summary":"Many times while displaying time on website we need to do something extra.\nThis function script can give you time in days/hour/minutes/seconds ago format.\nYou just need to pass the mysql time to this function. like this:\necho timeago(\u0026#34;2013-11-24 18:00:09\u0026#34;); function timeago($timeStr) { $time = strtotime($timeStr); $output = \u0026#34;\u0026#34;; $time_difference = time() - $time; $seconds = $time_difference ; $minutes = round($time_difference / 60 ); $hours = round($time_difference / 3600 ); $days = round($time_difference / 86400 ); $weeks = round($time_difference / 604800 ); $months = round($time_difference / 2419200 ); $years = round($time_difference / 29030400 ); if($seconds \u0026lt;= 60) { // Seconds $output = \u0026#34;$seconds seconds ago\u0026#34;; } else if($minutes \u0026lt;=60) { //Minutes if($minutes==1) { $output = \u0026#34;one minute ago\u0026#34;; } else { $output = \u0026#34;$minutes minutes ago\u0026#34;; } } else if($hours \u0026lt;=24) { //Hours if($hours==1) { $output = \u0026#34;one hour ago\u0026#34;; } else { $output = \u0026#34;$hours hours ago\u0026#34;; } } else if($days \u0026lt;= 7) { //Days if($days==1) { $output = \u0026#34;one day ago\u0026#34;; } else { $output = \u0026#34;$days days ago\u0026#34;; } } else if($weeks \u0026lt;= 4) { //Weeks if($weeks==1) { $output = \u0026#34;one week ago\u0026#34;; } else { $output = \u0026#34;$weeks weeks ago\u0026#34;; } } else if($months \u0026lt;=12) { //Months if($months==1) { $output = \u0026#34;one month ago\u0026#34;; } else { $output = \u0026#34;$months months ago\u0026#34;; } } else { //Years if($years==1) { $output = \u0026#34;one year ago\u0026#34;; } else { $output = \u0026#34;$years years ago\u0026#34;; } } return $output; } Note : Sometimes you may get time error while using this function because of timezone.","title":"Creating TimeAgo function in PHP"},{"content":"For doing this there is one good plugin called \u0026ldquo;Mobile Detect\u0026rdquo;.\nJust download library from https://github.com/serbanghita/Mobile-Detect Put Mobile_Detect.php inside libraries folder of ci application.\nWhenever you need to detect the OS or any other parameter, use following code:\npublic function index() { $this -\u0026gt; load -\u0026gt; library(\u0026#39;Mobile_Detect\u0026#39;); $detect = new Mobile_Detect(); if ($detect-\u0026gt;isMobile() || $detect-\u0026gt;isTablet() || $detect-\u0026gt;isAndroidOS()) { header(\u0026#34;Location: \u0026#34;.$this-\u0026gt;config-\u0026gt;item(\u0026#39;base_url\u0026#39;).\u0026#34;/mobile\u0026#34;); exit; } } You can find variety of functions like this in here:\n// Include and instantiate the class. require_once \u0026#39;Mobile_Detect.php\u0026#39;; $detect = new Mobile_Detect; // Any mobile device (phones or tablets). if ( $detect-\u0026gt;isMobile() ) { } // Any tablet device. if( $detect-\u0026gt;isTablet() ){ } // Exclude tablets. if( $detect-\u0026gt;isMobile() \u0026amp;\u0026amp; !$detect-\u0026gt;isTablet() ){ } // Check for a specific platform with the help of the magic methods: if( $detect-\u0026gt;isiOS() ){ } if( $detect-\u0026gt;isAndroidOS() ){ } // Alternative method is() for checking specific properties. // WARNING: this method is in BETA, some keyword properties will change in the future. $detect-\u0026gt;is(\u0026#39;Chrome\u0026#39;) $detect-\u0026gt;is(\u0026#39;iOS\u0026#39;) $detect-\u0026gt;is(\u0026#39;UC Browser\u0026#39;) // [...] // Batch mode using setUserAgent(): $userAgents = array( \u0026#39;Mozilla/5.0 (Linux; Android 4.0.4; Desire HD Build/IMM76D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19\u0026#39;, \u0026#39;BlackBerry7100i/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/103\u0026#39;, // [...] ); foreach($userAgents as $userAgent){ $detect-\u0026gt;setUserAgent($userAgent); $isMobile = $detect-\u0026gt;isMobile(); $isTablet = $detect-\u0026gt;isTablet(); // Use the force however you want. } // Get the version() of components. // WARNING: this method is in BETA, some keyword properties will change in the future. $detect-\u0026gt;version(\u0026#39;iPad\u0026#39;); // 4.3 (float) $detect-\u0026gt;version(\u0026#39;iPhone\u0026#39;) // 3.1 (float) $detect-\u0026gt;version(\u0026#39;Android\u0026#39;); // 2.1 (float) $detect-\u0026gt;version(\u0026#39;Opera Mini\u0026#39;); // 5.0 (float) // [...] Find documentation for this on: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples.\nAll the best !!! And dont forget to like our page 🙂\n","permalink":"https://dwij.net/posts/mobile-os-detection-in-php-codeigniter/","summary":"For doing this there is one good plugin called \u0026ldquo;Mobile Detect\u0026rdquo;.\nJust download library from https://github.com/serbanghita/Mobile-Detect Put Mobile_Detect.php inside libraries folder of ci application.\nWhenever you need to detect the OS or any other parameter, use following code:\npublic function index() { $this -\u0026gt; load -\u0026gt; library(\u0026#39;Mobile_Detect\u0026#39;); $detect = new Mobile_Detect(); if ($detect-\u0026gt;isMobile() || $detect-\u0026gt;isTablet() || $detect-\u0026gt;isAndroidOS()) { header(\u0026#34;Location: \u0026#34;.$this-\u0026gt;config-\u0026gt;item(\u0026#39;base_url\u0026#39;).\u0026#34;/mobile\u0026#34;); exit; } } You can find variety of functions like this in here:","title":"Mobile OS detection in PHP CodeIgniter"},{"content":"So many times it happens where we need to deploy our Software/Embedded Project on Hardware. Only problem comes is creating Hardware based on ARM Processor \u0026amp; all its time consuming interfacing.\nBut there is a way to skip all this lengthy stuff known as Raspberry Pi [Official Website: http://www.raspberrypi.org].\nSo What is Raspberry Pi:\nThe Raspberry Pi is a credit-card-sized single-board computer developed in the UK by the Raspberry Pi Foundation with the intention of promoting the teaching of basic computer science in schools.\nCurrently there are two models of Raspberry Pi Model A \u0026amp; B. Initial sales are of the Model B, with plans to release the Model A in early 2013. Model A has one USB port and no Ethernet controller, and will cost less than the Model B with two USB ports and a 10/100 Ethernet controller.\n** Technical Specifications:**\nModel A Model B Target price: ~US$25 ~Rs.3125 SoC: Broadcom BCM2835 (CPU, GPU, DSP, SDRAM, and single USB port) CPU: 700 MHz ARM1176JZF-S core (ARM11 family, ARMv6 instruction set) GPU: Broadcom VideoCore IV @ 250 MHzOpenGL ES 2.0 (24 GFLOPS)MPEG-2 and VC-1 (with license), 1080p 30 h.264/MPEG-4 AVC high-profile decoder and encoder Memory (SDRAM): 256 MB (shared with GPU) 512 MB (shared with GPU) as of 15 October 2012 USB 2.0 ports: 1 (direct from BCM2835 chip) 2 (via the built in integrated 3-port USB hub) Video input: A CSI input connector allows for the connection of a RPF designed camera module Video outputs: Composite RCA (PAL and NTSC), HDMI (rev 1.3 \u0026amp; 1.4), raw LCD Panels via DSI14 HDMI resolutions from 640×350 to 1920×1200 plus various PAL and NTSC standards. Audio outputs: 3.5 mm jack, HDMI, and, as of revision 2 boards, I²S audio (also potentially for audio input) Onboard storage: SD / MMC / SDIO card slot (3,3V card power support only) Onboard network: None 10/100 Ethernet (8P8C) USB adapter on the third port of the USB hub Low-level peripherals: 8 × GPIO, UART, I²C bus, SPI bus with two chip selects, I²S audio +3.3 V, +5 V, ground Power ratings: 300 mA (1.5 W) 700 mA (3.5 W) Power source: 5 volt via MicroUSB or GPIO header Size: 85.60 mm × 53.98 mm (3.370 in × 2.125 in) Weight: 45 g (1.6 oz) Operating systems: Arch Linux ARM, Debian GNU/Linux, Fedora, FreeBSD, NetBSD, Plan 9, Raspbian OS, RISC OS, Slackware Linux So whats so ** special** with this Raspberry Pi: You get a lot of freedom in choosing the OS which you want to install on Raspberry Pi.\nThis is a list of operating systems that have been, or are being, ported to Raspberry Pi.\nFull OS:\nAROS GEORGE 3 - within an emulator. Haiku[139] Linux Android Android 2.3 (Gingerbread) Android 4.0 (Ice Cream Sandwich) Arch Linux ARM R_Pi Bodhi Linux[140] Debian ARM architecture ports, but not the Debian ARMhf architecture ports (introduced with Debian 7 Wheezy), since these are compiled for ARMv7 and the Raspberry Pi CPU is ARMv6 Raspbian[141] (Debian 7 Wheezy ARMhf backported for ARMv6) Firefox OS Puppy Linux[142] Gentoo Linux[143] Google Chromium OS PiBang Linux[144] Raspberry Pi Fedora Remix Slackware ARM (formerly ARMedslack) QtonPi a cross-platform application framework based Linux distribution based on the Qt framework WebOS Open webOS[145] Plan 9 from Bell Labs[146][147] RISC OS Unix FreeBSD[148] NetBSD[149][150] Multi-purpose light distributions:\nMoebius operating system, a light ARM HF distribution based on Debian. It uses Raspbian repository, but it fits in a 1 GB SD card. It has just minimal services and its memory usage is optimized to keep a small footprint. Minibian, another light ARM HF distribution based on Raspbian repository. OpenWrt \u0026ldquo;Attitude Adjustment\u0026rdquo; 12.09 Squeezed Arm Puppy, a version of Puppy Linux (Puppi) for the ARMv6 (sap6) specifically for the Raspberry Pi.[151] Kali Linux Single-purpose light distributions:\nIPFire OpenELEC Raspbmc XBian RasPlex Raspberry Digital signage, an operating system for digital signage purposes (web and media views). Now you can start with your own PI Kit by using this quick start guide.\nThese kits are also available in Pune at various shops. These shops are enlisted here: http://dwij.co.in/electronics-robotics-components-shops-in-pune\nYou can also order your kit from https://www.crazypi.com/raspberry-pi-products.\n","permalink":"https://dwij.net/posts/raspberry-pi-in-pune/","summary":"So many times it happens where we need to deploy our Software/Embedded Project on Hardware. Only problem comes is creating Hardware based on ARM Processor \u0026amp; all its time consuming interfacing.\nBut there is a way to skip all this lengthy stuff known as Raspberry Pi [Official Website: http://www.raspberrypi.org].\nSo What is Raspberry Pi:\nThe Raspberry Pi is a credit-card-sized single-board computer developed in the UK by the Raspberry Pi Foundation with the intention of promoting the teaching of basic computer science in schools.","title":"Raspberry Pi in Pune"}]