<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Build With Anant]]></title><description><![CDATA[Demystifying AI-powered software building, one app at a time]]></description><link>https://anantgarg.com</link><image><url>https://substackcdn.com/image/fetch/$s_!W4To!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9b8c19-a0ed-4111-b848-275e3b5e7cb6_225x225.png</url><title>Build With Anant</title><link>https://anantgarg.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 08 Apr 2026 10:46:17 GMT</lastBuildDate><atom:link href="https://anantgarg.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Anant Garg]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[buildwithanant@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[buildwithanant@substack.com]]></itunes:email><itunes:name><![CDATA[Anant Garg]]></itunes:name></itunes:owner><itunes:author><![CDATA[Anant Garg]]></itunes:author><googleplay:owner><![CDATA[buildwithanant@substack.com]]></googleplay:owner><googleplay:email><![CDATA[buildwithanant@substack.com]]></googleplay:email><googleplay:author><![CDATA[Anant Garg]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Chatbot Platform #7 - Going live!]]></title><description><![CDATA[A step by step guide to deploying and testing the Chatbot platform]]></description><link>https://anantgarg.com/p/chatbot-platform-7-going-live</link><guid isPermaLink="false">https://anantgarg.com/p/chatbot-platform-7-going-live</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Thu, 27 Feb 2025 06:04:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!bGJm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>Live Demo &amp; Source Code</h1><div class="pullquote"><p><strong>Check out the fully functional <a href="https://chatbot-builder-test.vercel.app">chatbot builder demo</a></strong></p></div><p>After weeks of back and forth in Cursor, trying to resolve the linting errors (due to Vercel&#8217;s strict deployment policies), I was finally able to solve them after upgrading to Claude 3.7. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bGJm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bGJm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 424w, https://substackcdn.com/image/fetch/$s_!bGJm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 848w, https://substackcdn.com/image/fetch/$s_!bGJm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 1272w, https://substackcdn.com/image/fetch/$s_!bGJm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bGJm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png" width="1346" height="606" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:606,&quot;width&quot;:1346,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105997,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bGJm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 424w, https://substackcdn.com/image/fetch/$s_!bGJm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 848w, https://substackcdn.com/image/fetch/$s_!bGJm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 1272w, https://substackcdn.com/image/fetch/$s_!bGJm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a599a3b-a2a8-42bf-b418-570ddbfb2757_1346x606.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you want the full source code, you can check it out my repo: <a href="https://github.com/anantgarg/chatbot-builder">https://github.com/anantgarg/chatbot-builder</a></p><p>Here is the final look of the Chatbot Builder-</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8jgf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8jgf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!8jgf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!8jgf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!8jgf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8jgf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png" width="1456" height="845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:845,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:541158,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8jgf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!8jgf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!8jgf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!8jgf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cfd5106-3b4f-4087-8d42-14c3391a018c_2806x1628.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Try it out yourself at:</p><p><a href="https://chatbot-builder-test.vercel.app">https://chatbot-builder-test.vercel.app</a></p><div><hr></div><h1>Deploy your own Chatbot Builder</h1><p>To publish your app to Vercel, here are a few simple steps:</p><ol><li><p>First and foremost, fork my <a href="https://github.com/anantgarg/chatbot-builder">chatbot builder repository</a> (and don&#8217;t forget to give it a star!)</p></li><li><p>Then signup and login to <a href="https://vercel.com">Vercel</a></p></li><li><p>Add a new project and select the forked repo from <strong>Import Git Repository</strong></p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9pE3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9pE3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 424w, https://substackcdn.com/image/fetch/$s_!9pE3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 848w, https://substackcdn.com/image/fetch/$s_!9pE3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 1272w, https://substackcdn.com/image/fetch/$s_!9pE3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9pE3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png" width="486" height="381.85714285714283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:924,&quot;width&quot;:1176,&quot;resizeWidth&quot;:486,&quot;bytes&quot;:110227,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9pE3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 424w, https://substackcdn.com/image/fetch/$s_!9pE3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 848w, https://substackcdn.com/image/fetch/$s_!9pE3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 1272w, https://substackcdn.com/image/fetch/$s_!9pE3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0af7e50a-3920-459c-a8d5-62418b949e4b_1176x924.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p> 4. Hit Deploy (don&#8217;t update the environment variables as yet)</p><ol start="5"><li><p>You&#8217;ll get an error. That&#8217;s okay. Click on the <strong>Goto Project</strong> button.</p></li><li><p>In your project, head to <strong>Storage</strong> and click on the <strong>Create Database</strong> button. Now select <strong>Neon</strong> (Serverless Postgres) from the options. Choose the free option and create the database.</p></li><li><p>Now head to <strong>Settings</strong> and click on <strong>Environment Variables</strong> from the side bar. Add `JWT_SECRET` key. Verify that `DATABASE_URL` key is already present (would have been auto created if the database was successfully created)</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M0-Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M0-Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 424w, https://substackcdn.com/image/fetch/$s_!M0-Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 848w, https://substackcdn.com/image/fetch/$s_!M0-Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 1272w, https://substackcdn.com/image/fetch/$s_!M0-Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M0-Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png" width="1456" height="235" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82051c60-89c2-41b1-9949-790571342e48_1820x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:235,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32656,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M0-Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 424w, https://substackcdn.com/image/fetch/$s_!M0-Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 848w, https://substackcdn.com/image/fetch/$s_!M0-Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 1272w, https://substackcdn.com/image/fetch/$s_!M0-Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82051c60-89c2-41b1-9949-790571342e48_1820x294.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div></li><li><p>You should see a toast asking you to redeploy. Click on the <strong>Redeploy</strong> button and follow the onscreen instructions.</p></li><li><p>Once successful, click on the <strong>Visit</strong> button to view your Chatbot builder</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k4vK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k4vK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 424w, https://substackcdn.com/image/fetch/$s_!k4vK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 848w, https://substackcdn.com/image/fetch/$s_!k4vK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 1272w, https://substackcdn.com/image/fetch/$s_!k4vK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k4vK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png" width="1456" height="799" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:799,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:295668,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k4vK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 424w, https://substackcdn.com/image/fetch/$s_!k4vK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 848w, https://substackcdn.com/image/fetch/$s_!k4vK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 1272w, https://substackcdn.com/image/fetch/$s_!k4vK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a693eea-0f6e-49b6-be1c-e498aa928ac1_2450x1344.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p></li></ol><div><hr></div><h1>Test your Chatbot Builder</h1><ol><li><p>Head to the deployed URL and sign up for an account</p></li><li><p>Go to <strong>Settings</strong> tab and input your <strong>OpenAI</strong> key. Click on <strong>Test API Key</strong> to test if the key has been correctly added.</p></li><li><p>Then head to <strong>Bots</strong> tab and create a new bot</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dFeF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dFeF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!dFeF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!dFeF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!dFeF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dFeF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png" width="1456" height="845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:845,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:541256,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dFeF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!dFeF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!dFeF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!dFeF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F830fdc95-72c5-4b9a-8be1-4f49e6671ba3_2806x1628.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p>Then head to <strong>Files</strong> tab and upload relevant files and attach them to the newly created bot</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YbEI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YbEI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!YbEI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!YbEI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!YbEI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YbEI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png" width="1456" height="845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:845,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:562050,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YbEI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!YbEI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!YbEI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!YbEI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18a15d0-06a7-49cf-b0b3-49dd9b728d62_2806x1628.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p>Then signup for an account at <a href="https://app.cometchat.com">CometChat</a> and head to <strong>Manage</strong> &gt; <strong>Users</strong> tab and create a new user.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_8ej!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_8ej!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 424w, https://substackcdn.com/image/fetch/$s_!_8ej!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 848w, https://substackcdn.com/image/fetch/$s_!_8ej!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 1272w, https://substackcdn.com/image/fetch/$s_!_8ej!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_8ej!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png" width="1456" height="877" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:877,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:792950,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_8ej!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 424w, https://substackcdn.com/image/fetch/$s_!_8ej!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 848w, https://substackcdn.com/image/fetch/$s_!_8ej!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 1272w, https://substackcdn.com/image/fetch/$s_!_8ej!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b3f7c94-8bbe-474c-a355-2c8d34eee720_2874x1732.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p>Then head back to your Chatbot Builder and next to the bot you created, click on <strong>Integrations</strong> and then <strong>Configure Integrations</strong> and select <strong>Enable CometChat Integration</strong>. Add these credentials from your <strong>CometChat Dashboard</strong> &gt; <strong>Application</strong> &gt; <strong>Credentials</strong> page.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uWq4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uWq4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!uWq4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!uWq4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!uWq4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uWq4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png" width="1456" height="845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:845,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:668962,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uWq4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 424w, https://substackcdn.com/image/fetch/$s_!uWq4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 848w, https://substackcdn.com/image/fetch/$s_!uWq4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!uWq4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88531e20-0de4-457a-9afa-3c195d2aee1e_2806x1628.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p>Hit Save and you should get a web hook URL</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HMH_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HMH_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 424w, https://substackcdn.com/image/fetch/$s_!HMH_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 848w, https://substackcdn.com/image/fetch/$s_!HMH_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 1272w, https://substackcdn.com/image/fetch/$s_!HMH_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HMH_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png" width="1456" height="886" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:886,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:689512,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HMH_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 424w, https://substackcdn.com/image/fetch/$s_!HMH_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 848w, https://substackcdn.com/image/fetch/$s_!HMH_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 1272w, https://substackcdn.com/image/fetch/$s_!HMH_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89d4671e-29b1-4029-b7f0-f7d960ed70d4_2806x1708.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p>Now head to <strong>CometChat Dashboard</strong> &gt;  <strong>AI Chatbot</strong> &gt; <strong>Non-AI Bots</strong> and create a new bot. In the UID, use the same UID as the user you had created and for the URL, use the Webhook URL generated from the Chatbot Builder. And finally hit <strong>Save</strong>.</p></li><li><p>Now head to <strong>Manage</strong> &gt; <strong>Groups</strong> and select <strong>Edit</strong> for Hiking Group and then Add Members. Add the new bot you just created.</p></li><li><p>Finally, run the sample <a href="https://github.com/cometchat/cometchat-uikit-react/tree/v6/sample-app">CometChat React app</a> by following the onscreen instructions</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BdBs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BdBs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 424w, https://substackcdn.com/image/fetch/$s_!BdBs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 848w, https://substackcdn.com/image/fetch/$s_!BdBs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 1272w, https://substackcdn.com/image/fetch/$s_!BdBs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BdBs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png" width="1456" height="877" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:877,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1260645,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BdBs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 424w, https://substackcdn.com/image/fetch/$s_!BdBs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 848w, https://substackcdn.com/image/fetch/$s_!BdBs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 1272w, https://substackcdn.com/image/fetch/$s_!BdBs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1afebd-739c-44c8-8293-e3659e032a2b_2874x1732.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p>If everything went as per plan, you should be able to head to the <em>Hiking Group</em> chat and ask any question and get a response from the newly created bot!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!F3Lw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!F3Lw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 424w, https://substackcdn.com/image/fetch/$s_!F3Lw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 848w, https://substackcdn.com/image/fetch/$s_!F3Lw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!F3Lw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!F3Lw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png" width="1456" height="641" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:641,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:689143,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://anantgarg.com/i/158014239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!F3Lw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 424w, https://substackcdn.com/image/fetch/$s_!F3Lw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 848w, https://substackcdn.com/image/fetch/$s_!F3Lw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!F3Lw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b52634-204d-45d2-a42f-3f9812a4246b_2874x1266.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li></ol><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://anantgarg.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Build With Anant! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Chatbot Platform #6 - MVP!]]></title><description><![CDATA[Step-by-Step Guide to Adding CometChat and Preparing the MVP]]></description><link>https://anantgarg.com/p/chatbot-platform-6-mvp</link><guid isPermaLink="false">https://anantgarg.com/p/chatbot-platform-6-mvp</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Thu, 30 Jan 2025 09:37:57 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/156082668/1279d619c611573c325be6e4015a5883.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Thanks for joining in today. I'm really excited because I've finally been able to get the flow up and running&#8212;the entire flow&#8212;for our bot platform. Users can now create a bot, upload files to it, and interact with it using CometChat. This means the entire circle is complete! When users chat via CometChat, the bot references the uploaded files to provide accurate responses, which is incredibly gratifying for me.</p><p><strong>Getting Started from Where We Left Off</strong></p><p>Let's jump right in from where we concluded our last session. Previously, I had enabled ngrok, but I hadn't set up the service to accept data yet. Today, I'm addressing that by creating a basic API endpoint. I have given an example of the type of content coming in from CometChat, and the endpoint has now been created. Let&#8217;s send a message and see what happens.</p><p>Upon sending a message, I noticed it was redirected to login. To bypass this during the demo, I&#8217;ve decided not to implement additional authentication right now. In the future, we can certainly add more security layers. For now, I've integrated the bot ID into the URL, allowing us to determine which bot is making the call. I've also included this information on the configuration page.</p><p><strong>Configuring the URL and Testing the Response</strong></p><p>With the bot ID, I've updated the URL in the CometChat dashboard for the bot. After testing, I found out it was still redirecting. Once fixed, we tried again, and this time the response returned a status OK. Then, I modified the setup to send out a response by integrating relevant code from CometChat's API documentation. Now, upon completion, there should be a response from the system&#8212;although initially, the response looped back to itself, sending the same message repeatedly. This was due to an incorrect URL end-point, which has since been corrected.</p><p><strong>The Breakthrough with Gym Bot</strong></p><p>After rectifying the endpoint, we successfully received a response from Gym bot. Moving forward, I decided to enhance echo bot further by fetching responses from OpenAI and forwarding these to the user. I've associated a workout file with Gym bot, configured all the settings with CometChat, and thoroughly tested this several times.</p><p>Running the test on a local system takes a bit longer than it would on a server, but ultimately, I received the desired response, which was extracted from the file. The next step in this developmental journey will be to remove markdown syntax and replace it with standard content to simplify responses.</p><p><strong>What&#8217;s Next?</strong></p><p>There you have it! We've built the whole flow with CometChat. In future episodes, I'll be adding other integrations, such as with Facebook Messenger and WhatsApp. Stay tuned for more advancements.</p><p>Thank you for following along.</p><p>Bye!</p>]]></content:encoded></item><item><title><![CDATA[Chatbot Platform #5 - Integrations]]></title><description><![CDATA[A step-by-step guide to adding integrations for bots]]></description><link>https://anantgarg.com/p/chatbot-platform-5-integrations</link><guid isPermaLink="false">https://anantgarg.com/p/chatbot-platform-5-integrations</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Tue, 28 Jan 2025 15:01:33 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/155926082/cebd183128334eea57baaeb41b7d0017.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Thanks for joining in on this journey. Today, I tackled the task of adding an integration feature to our chatbot. This integration is designed to allow connections to various destinations, including platforms like WhatsApp and Facebook Messenger. My first step was integrating with CometChat, enabling the chatbot builder to be entirely managed by CometChat's frontend capabilities. Let me walk you through the process.</p><p><strong>Beginning with Integrations</strong></p><p>To kick things off, I went ahead and prompted the system to add an integrations page for each bot. However, despite following the setup instructions, I noticed it wasn't taking effect as expected. After some trial and error, including messaging the same error message again, it still didn't work. When I checked the file, it appeared that the relevant code had been added, yet it wasn't reflecting in the final product. After a bit of investigation, I realized I had added it to the wrong file. Finally, after correcting the location, the integrations page appeared as intended.</p><p><strong>Integrating with CometChat</strong></p><p>Next was the exciting part: moving on to CometChat. I started by downloading CometChat's React v5 UI Kit and executed a series of commands, one at a time. Progressing to the dashboard, I navigated to the 'Bots' section to ensure I had created a gym bot. This was crucial because I was using a product called "ngrok." Ngrok is quite handy for turning localhost into an accessible web interface.</p><p>Once set up, I confirmed the gym bot's presence in the group's list. Sending a message to the group should trigger a receipt of that message on the other end, and indeed, upon checking ngrok, the message was successfully received.</p><p><strong>Looking Ahead</strong></p><p>In our next session, I'll be working on connecting all the dots to complete this communication flow. When a message comes in, the plan is to process it through OpenAI, enabling us to send an appropriate response back. This will wrap up the integration process neatly. Following this, I plan to focus on expanding the range of integrations even further.</p><p>That&#8217;s all for now. Thank you for reading, and I hope you found this insight into the integration process useful.</p><p>Until next time,</p><p>Anant</p>]]></content:encoded></item><item><title><![CDATA[Chatbot Platform #4 - Multi-tenancy]]></title><description><![CDATA[A step-by-step guide to add multi-tenancy support to the chatbot platform]]></description><link>https://anantgarg.com/p/chatbot-platform-multi-tenancy</link><guid isPermaLink="false">https://anantgarg.com/p/chatbot-platform-multi-tenancy</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Thu, 16 Jan 2025 13:00:07 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/154946067/1a0ad92a3010d586a5ca416d38615fff.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Hi folks,</p><p>Thanks for joining me. Today, I'm diving deeper into the exciting world of multi-tenancy as I continue to refine my chatbot platform.</p><h3>Debugging the Files Tab</h3><p>The journey begins where I left off last time - the files tab wasn't yet fully equipped for multi-tenancy. Previously, accessing the files tab fetched and displayed a list of files directly from OpenAI, which wasn't ideal. Now, I've revamped the code to pull these files from the database instead. However, after making this change, I encountered an error message: "failed to upload file." To troubleshoot, I looked into the issue and found that while the file was getting uploaded to OpenAI, it wasn't being added to my database. It turned out that the user ID being fetched wasn't correct. After checking and rectifying this problem, things seemed to work smoothly. A quick look at the files table confirmed - an entry had been added.</p><p>Next, I logged out and tried the process with a different user. I noticed the same file was showing up. To solve this, I uploaded a second file. The upload went through, but it became clear the files list was incorrect - it was displaying files like "mega guide" and "golden rules" across different users. I'd instructed the system to show files specifically for the logged-in user, prompting another round of code revisions. After these adjustments, each user could now see their respective files: user one saw "mega guide," and another user saw "golden rules."</p><h3>Fixing the Bot's Endpoint and Navigation</h3><p>While reviewing, I encountered an issue with the bot's endpoint and noted that the navigation panel looked awkward as it was only half the length. I made some modifications, extending the navigation to full length and fixing the bot's tab link. Both now work as intended.</p><h3>Enhancing the Files Tab</h3><p>Another task was to refine the associate feature within the files tab. The associate functionality was supposed to add the feature to the database, but wasn't doing so. Upon examining the issue, I discovered it was attempting to associate the same file twice in OpenAI, causing an error. Cursor was tasked to address this issue for me.</p><p>Furthermore, I wanted to ensure if a bot was removed from an association, it would also be erased from the vector store. This process would extend to bots too - deleting a bot should remove it from the database, the assistant, and the vector store. I pasted relevant code snippets to avoid confusion for Cursor. Subsequently, deleting Gym bot 2 confirmed the success of these steps, as it vanished from both the database and the vector store.</p><h3>Reaching the Goal</h3><p>With these hurdles overcome, my chatbot platform now stands fully functional. Users can log in, register, create bots, upload files, associate these files with multiple bots, and then interact with the bots to get responses. I feel I am one step closer to developing something genuinely impactful. I look forward to sharing more updates in the next episode.</p><p>Thank you for joining me on this journey.</p>]]></content:encoded></item><item><title><![CDATA[Chatbot Platform #3- Auth & Multi-tenancy]]></title><description><![CDATA[A step-by-step guide to adding authentication and multi-tenancy to the chatbot builder]]></description><link>https://anantgarg.com/p/chatbot-auth-and-multi-tenancy</link><guid isPermaLink="false">https://anantgarg.com/p/chatbot-auth-and-multi-tenancy</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Tue, 14 Jan 2025 10:58:21 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/154810595/65bc1ba07e951ec3b59c5f290d8dc3cb.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>In today's tech journey, I'm excited to bring you along as I dive deeper into enhancing my chatbot solution. If you tuned in for the previous episode, you might remember that I laid the foundation by creating a basic chatbot product using OpenAI to answer simple questions by uploading files. Now, I'm taking it a step further by adding an authentication layer and working towards enabling multi-tenancy for the application.</p><p>Let's get right into it. First on the agenda is adding a user login and registration feature. I informed the tool I'm using, Cursor, that this is what I want, and it efficiently handled the task. However, it didn't automatically restrict access to the dashboard I built, so I had to prompt it specifically to implement that feature as well.</p><p>Once accomplished, a login and registration screen became visible. I opted to sign up first and then attempted to log in using the same credentials. This is where I encountered a roadblock&#8212;a persistent error message indicating a mismatch in the expected string pattern. Debugging revealed that the registration input wasn't being recorded in the database, leading to this repetitive error.</p><p>I spent some time iterating with Cursor to resolve this. Even when experimenting with different versions of language models like GPT-4o and another variant, I initially couldn't identify the root cause. It finally dawned on me that the registration API endpoint was being protected and requests were rerouted to the login screen, causing the error. It was a testament to the fact that while language models can handle many tasks, there are times when a manual inspection, like reviewing console logs and network responses, is necessary for troubleshooting. Fortunately, once I recognized the issue and communicated it to Cursor, it was quick to correct it.</p><p>Having successfully registered an account, I verified its presence in the database and logged in without issues. However, there was a minor hiccup: I wasn't being redirected to the correct pages. After some more prompts to Cursor, I achieved the smooth navigation post-sign-in that I was aiming for.</p><p>The next major step was implementing multi-tenancy, where distinct users could have personalized access to their own sets of bots and files. For instance, when user 1 logs in, they should see a different suite compared to user 2. I initiated the process by directing Cursor to ensure logged-in users could only view bots or files they had created.</p><p>I went ahead and crafted several test accounts and checked them against the database. In the process, I ran into a 404 error triggered by an endpoint modification&#8212;from /bots to just /. Adjusting this was straightforward, and it was addressed promptly. Testing showed that after a minor code change to correctly fetch bots for the logged-in user, different accounts were consistently displaying unique data sets, aligning with the multi-tenancy objective.</p><p>With this progress, I'm getting progressively closer to building a fully operational SaaS-based multi-tenant chatbot creator. Soon, it will be robust enough for production use, and I'm thrilled to be expanding its capabilities. Stay tuned for upcoming updates, where I'll continue refining file management and other features, slowly but surely bringing this vision to fruition.</p><p>Thank you for joining me on this voyage. Until the next step in our development journey!</p>]]></content:encoded></item><item><title><![CDATA[Chatbot Platform #2 - Building File Upload and Retrieval Capabilities]]></title><description><![CDATA[A step-by-step guide to adding the ability to upload (and train) your own content to the chat bot platform]]></description><link>https://anantgarg.com/p/chatbot-platform-2-upload-your-own</link><guid isPermaLink="false">https://anantgarg.com/p/chatbot-platform-2-upload-your-own</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Thu, 09 Jan 2025 10:54:46 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/154461201/5cd34e497d5614d693dc5cf1d71a5ba4.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Hi there! Thanks for joining me today as I continue my journey with the chatbot platform. In this session, I focused on adding the ability for users to upload files, read from these files, and ultimately provide responses based on their contents. This is an exciting step forward in utilizing OpenAI's APIs extensively. Let's dive in!</p><h3>Initial Setup and Challenges</h3><p>To start, I tried using OpenAI's Assistant feature to enhance the bot's capabilities. Initially, I asked Cursor to create a bot that should automatically generate an assistant. However, I encountered an error, and the assistant wasn&#8217;t created. Realizing the issue, I decided to switch to the GPT-4o model, as it's better trained with OpenAI's documentation, instead of the Claude 3.5 model. After making this change, I was able to create a new bot, which then appeared under the Assistants feature.</p><h3>Developing File Search Capability</h3><p>With the bot successfully created, I wanted to integrate file search functionality within Assistants. The goal was to ensure that when a user uploads a new file, it gets associated with a particular Assistant. In OpenAI's framework, every Assistant can have multiple vector stores, and each vector store can contain multiple files. Thus, I decided to create one vector store for each bot, associating files to the corresponding vector store.</p><h3>Coding and Configuration</h3><p>I proceeded to write the code necessary for uploading files. Once the code was in place, I could successfully upload files and see them appear in storage. However, a vector store had not been created. To address this, I modified the code so that a new vector store is created whenever a new bot is set up. Using the GPT-4o model, I inserted code directly from the documentation for a smoother process.</p><p>To improve organization, I introduced a "Files" tab that lists all uploaded files, making it easier to manage them. I also corrected a mistake where the system used its own OpenAI key, ensuring it now uses the correct, pre-existing key.</p><h3>Testing and Troubleshooting</h3><p>Testing the newly set features, I created a new bot, a gym trainer, and verified its appearance in Assistants. With each Assistant now linked to a vector store, I ensured that user file uploads were stored correctly in the database. This setup allowed me to associate files accurately with their respective vector store IDs, a crucial step for retrieving relevant information during bot interactions.</p><h3>Finalizing Features</h3><p>The next step involved adding an association icon next to each file. This function lets me align each file with a specific vector store, ensuring that information retrieval when interacting with the bot is seamless.</p><p>Creating new bots, like a gym trainer, further allowed me to test the association process. Upon verification, I could see that the files attached correctly in the vector store and displayed in Assistants, confirming everything was linked as intended.</p><h3>Conclusion and Future Work</h3><p>By leveraging these developments, I enabled a feature where users can upload files, connect them with individual bots, and receive tailored responses after retrieving data from these files. </p><p>In summary, this session marked a significant advance in building an intelligent file-upload feature for chatbots. I'm excited about the possibilities this opens up and look forward to further enhancements. Thank you for following along with my progress today!</p>]]></content:encoded></item><item><title><![CDATA[Create a Chatbot Platform with Cursor]]></title><description><![CDATA[A step-by-step guide to creating a chatbot platform with Cursor, OpenAI & Next.js]]></description><link>https://anantgarg.com/p/create-a-chatbot-platform-with-cursor</link><guid isPermaLink="false">https://anantgarg.com/p/create-a-chatbot-platform-with-cursor</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Tue, 07 Jan 2025 09:16:40 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/154320519/80151e512ecef20ce3c26c785abf00af.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Creating a chatbot is a fascinating journey into the world of automated communication. Today, we'll guide you through the process of building a simple yet effective chatbot platform using Next.js, SQLite, and OpenAI. This platform will enable you to create multiple bots, send them messages, and receive responses.</p><h3>Getting Started with Next.js</h3><p>The adventure begins with setting up a simple Next.js project. When you start, be sure to choose the 'agent' option instead of the normal one. This crucial step allows the running of commands, which you'll find immensely useful as we proceed. Once configured, you'll be able to view the output in your web browser.</p><h3>Building an Admin Dashboard</h3><p>Next, we move on to creating an admin dashboard. This interface will have a look and feel akin to the popular project management tool, Linear. The functionalities include the ability to create, update, and edit bots, a versatile setup to manage your bots efficiently.</p><h3>Addressing Data Persistence</h3><p>One challenge you'll encounter is that initially, none of the bot states are saved. This is because there's no real database on the backend. To solve this, we'll set up a database. In this guide, we use SQLite, a convenient lightweight database. However, feel free to choose any database system you prefer, such as MySQL or PostgreSQL. Once implemented, your bots' states will be saved, and they will persist across page reloads.</p><h3>Creating an API for Bot Communication</h3><p>With the database in place, the next step is to create an API. This will allow you to invoke individual bots. We'll connect this API to OpenAI to enhance functionality. Additionally, the admin dashboard will feature an option to test messages easily. Connect to OpenAI by securing an API key, allowing interaction with their powerful servers.</p><h3>Troubleshooting and Enhancements</h3><p>While testing, you might encounter issues like improper model usage. For instance, there might be a prompt to use the <code>gpt-4o-mini</code> model, although it defaults to the <code>turbo-preview</code> model. It&#8217;s vital to ensure the correct model is used. A simple prompt adjustment can direct the system to utilize your desired model, <code>4o-mini</code>.</p><p>Another enhancement is updating the design to a light mode for a more soothing interface and implementing the <code>shadcn</code> library&#8212;though its pronunciation is a fun mystery&#8212;to achieve a clean, modern look.</p><h3>Testing With Real Scenarios</h3><p>With everything set up, it's time to test the bots in action. For variety, you might try scenarios like a gym trainer simulation. The bot's ability to provide a coherent response using the custom instruction will be a testament to the platform's efficacy.</p><h3>Conclusion and What's Next</h3><p>This brings us to the end of this section of our chatbot-building journey. In our next installment, we will explore how to upload and integrate your own content into the platform. Stay tuned for more insights and enhancements to your chatbot adventure.</p><p><strong>Thank you for exploring the fascinating world of chatbots with me!</strong></p>]]></content:encoded></item><item><title><![CDATA[Building a Canny Clone with Replit]]></title><description><![CDATA[A step-by-step guide to create a clone of Canny, a popular user feedback software using Replit]]></description><link>https://anantgarg.com/p/building-a-canny-clone-with-replit</link><guid isPermaLink="false">https://anantgarg.com/p/building-a-canny-clone-with-replit</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Fri, 03 Jan 2025 12:08:40 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/154062002/6b2d31d8e59f8ac13616a81d57af8c56.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Have you ever wanted to build your own version of Canny, a popular feature request software? Today, we're giving you a step-by-step guide on how you can create a Canny clone using Replit, a dynamic and user-friendly coding platform.</p><h3>Introduction to the Canny Clone</h3><p>Creating a feature request software like Canny allows users to upvote feature requests, post their own, and comment on any requests. Utilizing Replit simplifies the process, even for those with limited programming knowledge. Let&#8217;s dive into how you can leverage this platform to bring your vision to life.</p><h3>Getting Started with Replit</h3><p>For this tutorial, we begin by accessing Replit. With the prompt ready, hit <strong>start building</strong> to initiate your project. At this stage, it&#8217;s essential to focus on your initial plan. Adding too many features upfront can complicate things, so let&#8217;s keep it simple to start.</p><h3>The Building Process</h3><p>Replit makes building intuitive. As the product compilation process completes, it provides real-time results. One distinct advantage of using Replit is its ability to access the actual output and provide screenshots for verification. This helps ensure that your features are functioning as expected.</p><h3>Testing Your Canny Clone</h3><p>Now, let&#8217;s dive into some testing. Begin by registering with a test account. Though we encountered a minor login issue, it was swiftly resolved, allowing us to proceed with ease. The subsequent steps reaffirm the clone&#8217;s functionality&#8212;logging in, submitting new feedback, upvoting posts, commenting, and, ultimately, logging out.</p><h3>Why Replit?</h3><p>Replit stands out by making the building process as seamless as possible. You don't need deep programming knowledge. Simply input your feature requests, and Replit does the heavy lifting. If you aim to develop internal tools or simple software solutions, then Replit should be your platform of choice due to its simplicity and effectiveness.</p><h3>Conclusion</h3><p>Congratulations! By following these steps, you now have your very own Canny clone. This endeavor shows how Replit can transform complex ideas into reality with minimal effort. Whether you're a beginner or an experienced developer, Replit provides the tools and resources necessary to bring your projects to life.</p><p>We hope you found this guide helpful in navigating the creation of a Canny clone. Happy building, and thank you for reading!</p>]]></content:encoded></item><item><title><![CDATA[Building a Canny Clone with Cursor]]></title><description><![CDATA[A step-by-step guide to create a clone of Canny, a popular user feedback software using Cursor & Claude]]></description><link>https://anantgarg.com/p/building-a-canny-clone-with-cursor</link><guid isPermaLink="false">https://anantgarg.com/p/building-a-canny-clone-with-cursor</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Thu, 02 Jan 2025 12:44:53 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/153974805/3d86f54e69cafc096dc322a01a706d2a.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p><strong>Introduction</strong></p><p>Are you looking to create a feature-rich user feedback platform? Canny, a popular user feedback software, allows users to create and upvote feature requests with ease. In this post, I'll guide you through building a Canny clone using modern web technologies. This clone will help you gather and manage user feedback effectively.</p><p><strong>Setting Up the Project</strong></p><p>To get started, we'll create a new Next.js project. Next.js will serve as our frontend framework, enabling seamless integration with our backend services. Go ahead and run your Next.js project. You should see a default page indicating that your setup was successful.</p><p><strong>Choosing a Database: Prisma and SQLite</strong></p><p>Our next step is to choose a database. We&#8217;ll use Prisma, an ORM that simplifies database interactions. For this project, we're integrating Prisma with SQLite, a lightweight and efficient database. Although we're using SQLite here, you can opt for any database that suits your needs. Our database will store all feedback, track upvotes, and manage comments.</p><p><strong>Design and UI Customization</strong></p><p>With the database set, let&#8217;s focus on the UI. We want our Canny clone to mirror the look and feel of the original software. By closely emulating the Canny interface, users will have a familiar experience. Spend some time refining the UI until it matches your envisioned design.</p><p><strong>Connecting the Frontend and Backend</strong></p><p>Now that our interface is in place, let&#8217;s connect it to the backend. Initially, our app might not communicate in real time, so we&#8217;ll implement updates to ensure feature requests and upvotes are reflected instantly. Testing is crucial here; check if the data updates without reloading the page.</p><p><strong>Testing Functionality: Upvote and Comment Features</strong></p><p>Once the connection is established, it&#8217;s time to test key functionalities. Start with the upvote feature. After implementing it, try upvoting a feature request and see if it records in the database. Currently, it allows multiple upvotes per user, which we&#8217;ll limit to one per anonymous user to avoid skewing results.</p><p>Next, introduce individual feature request pages and the ability to add comments. Testing these features ensures the user can engage meaningfully with the feedback platform.</p><p><strong>Conclusion</strong></p><p>In a few simple steps, we've managed to build a nearly identical clone of Canny's feature request capability. While this tutorial covers the basics, there&#8217;s plenty more you can add to enhance your feedback platform. This foundation should get you started on creating an efficient tool for collecting user feedback. Happy coding, and thank you for following along!</p>]]></content:encoded></item><item><title><![CDATA[Hello World!]]></title><description><![CDATA[Create a simple Hello World app with Cursor]]></description><link>https://anantgarg.com/p/hello-world-e46</link><guid isPermaLink="false">https://anantgarg.com/p/hello-world-e46</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Mon, 30 Dec 2024 04:39:27 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/153787859/580375427bce4287f6e7043fccba7fb0.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p></p>]]></content:encoded></item><item><title><![CDATA[Introduction]]></title><description><![CDATA[The way we build software is about to change fundamentally.]]></description><link>https://anantgarg.com/p/coming-soon</link><guid isPermaLink="false">https://anantgarg.com/p/coming-soon</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Thu, 26 Dec 2024 10:46:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!W4To!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9b8c19-a0ed-4111-b848-275e3b5e7cb6_225x225.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The way we build software is about to change fundamentally. I am launching an ambitious effort to <strong>teach the next billion software builders</strong>. I intentionally use the word "builders" instead of "developers". AI-powered coding tools have become advanced enough to empower everyone, from non-technical to semi-technical to technical individuals. It&#8217;s one of the best times to be a builder.</p><p>However, using AI-powered tools to build software requires a new approach: rethinking how you code (for those who code) and learning how to structure your thoughts (for those who don&#8217;t) to create high-quality software. I plan to publish articles and videos for all builders, but having basic technical experience will be very helpful.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://anantgarg.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Please don&#8217;t forget to subscribe (it&#8217;s free) so that I can email you whenever I publish a new tutorial-</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p>If you&#8217;d like me to build a particular kind of application, feel free to message me on chat-</p><div class="community-chat" data-attrs="{&quot;url&quot;:&quot;https://open.substack.com/pub/buildwithanant/chat?utm_source=chat_embed&quot;,&quot;subdomain&quot;:&quot;buildwithanant&quot;,&quot;pub&quot;:{&quot;id&quot;:3586925,&quot;name&quot;:&quot;Build With Anant&quot;,&quot;author_name&quot;:&quot;Anant Garg&quot;,&quot;author_photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b23b5ac-6faa-4565-a594-32fb22e64c8a_225x225.png&quot;}}" data-component-name="CommunityChatRenderPlaceholder"></div><p></p><p>Looking forward!</p><p>Anant</p>]]></content:encoded></item><item><title><![CDATA[Kudos – Open Source GetKudos Alternative]]></title><description><![CDATA[Easiest way to manage testimonials]]></description><link>https://anantgarg.com/p/kudos-testimonials-management-php-software-getkudos-alternative</link><guid isPermaLink="false">https://anantgarg.com/p/kudos-testimonials-management-php-software-getkudos-alternative</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Tue, 28 Jun 2016 18:27:56 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7db59d96-64a9-40dd-95aa-87d997d0f5da_1264x601.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Easiest way to manage testimonials</h3><p>Kudos makes it easy for you to manage testimonials via multiple sources like Facebook, Twitter, Zendesk and forms. Kudos is a PHP based testimonials management script with many time saving features!</p><h3>Features</h3><p><strong>1. Automated Source Pulling</strong></p><p>There are multiple sources from which you can pull testimonials into Kudos; I have added the most common sources so that management is extremely simple and quick. Once setup, a team member will take only 5-10 minutes a week to quick accept/reject testimonials. Although most source pulling is automatic, every testimonial must go through a manual acceptance/rejection to ensure only the best testimonials appear on your site.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NGfC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NGfC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 424w, https://substackcdn.com/image/fetch/$s_!NGfC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 848w, https://substackcdn.com/image/fetch/$s_!NGfC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 1272w, https://substackcdn.com/image/fetch/$s_!NGfC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NGfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Multiple Sources for Testimonials&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Multiple Sources for Testimonials" title="Multiple Sources for Testimonials" srcset="https://substackcdn.com/image/fetch/$s_!NGfC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 424w, https://substackcdn.com/image/fetch/$s_!NGfC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 848w, https://substackcdn.com/image/fetch/$s_!NGfC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 1272w, https://substackcdn.com/image/fetch/$s_!NGfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc2548e-b2e9-4ca4-b7c3-09e043fd465d_1264x601.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p><strong>Source: Twitter</strong><br>&#8211; Automatically pull latest Twitter posts talking about your product/service<br>&#8211; Manually decide to showcase it to the world</p><p><strong>Source: Facebook</strong><br>&#8211; Automatically pull latest Facebook posts talking about your product/service<br>&#8211; Manually decide to showcase it to the world</p><p><strong>Source: Zendesk</strong><br>&#8211; Automatically pull latest Zendesk customer satisfaction reviews<br>&#8211; Manually decide to showcase it to the world</p><p><strong>Source: App Store</strong><br>&#8211; Manually add apps from the App Store (not reviews)<br>&#8211; Automatically pulls app information (icon &amp; description)</p><p><strong>Source: Form</strong><br>&#8211; Manually (or via API) add entries into Kudos</p><p><strong>Source: Showcase</strong><br>&#8211; Manually highlight key clients (logo &amp; name)</p><p><strong>2. Team</strong></p><p>You can add multiple team members to Kudos and enable them to accept/reject posts.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!I62Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I62Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 424w, https://substackcdn.com/image/fetch/$s_!I62Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 848w, https://substackcdn.com/image/fetch/$s_!I62Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 1272w, https://substackcdn.com/image/fetch/$s_!I62Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I62Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/acaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Manage Team Members&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Manage Team Members" title="Manage Team Members" srcset="https://substackcdn.com/image/fetch/$s_!I62Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 424w, https://substackcdn.com/image/fetch/$s_!I62Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 848w, https://substackcdn.com/image/fetch/$s_!I62Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 1272w, https://substackcdn.com/image/fetch/$s_!I62Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facaa1cd3-57ea-456d-88ad-17623bc50fbf_1263x369.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>There are three different user roles.</p><p><strong>User Role: Member</strong><br>A member can only add entries into Kudos but cannot make them live.</p><p><strong>User Role: Manager</strong><br>A manager can add entries as well as make them live.</p><p><strong>User Role: Administrator</strong><br>The administrator can do all of the above + manage teams + add new sources. The administrator can restrict access to certain sources as well for each member/manager.</p><p><strong>3. Posts</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DoC6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DoC6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 424w, https://substackcdn.com/image/fetch/$s_!DoC6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 848w, https://substackcdn.com/image/fetch/$s_!DoC6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 1272w, https://substackcdn.com/image/fetch/$s_!DoC6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DoC6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Posts - Managing Testimonials&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Posts - Managing Testimonials" title="Posts - Managing Testimonials" srcset="https://substackcdn.com/image/fetch/$s_!DoC6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 424w, https://substackcdn.com/image/fetch/$s_!DoC6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 848w, https://substackcdn.com/image/fetch/$s_!DoC6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 1272w, https://substackcdn.com/image/fetch/$s_!DoC6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e810e17-25e0-46f4-a900-465ec28b9bc3_884x556.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Posts is where all the magic happens. On the left side you will be able to see all the channels (i.e. sources) you have access to. For each source there are 3 tabs- Live, Inbox &amp; Add.</p><p><strong>Live</strong><br>These posts (i.e. testimonials) are live and visible to public.</p><p><strong>Inbox</strong><br>These are the incoming posts and need moderation. You can edit them, hide them or make them live.</p><p><strong>Add</strong><br>For certain sources like Form &amp; Showcase, the input is manual. So you will have to manually add an entry. However, for sources like Twitter, Zendesk and Facebook, it is automatic; so no entry is required.</p><p><strong>4. Displaying Testimonials</strong></p><p>Displaying testimonials requires a little bit of PHP experience. This is because the output is via API only. In future releases, I will write a method to show it directly using Javascript. But for now, it&#8217;s via API.</p><p>Add the following two functions to your code:</p><pre><code>define('KUDOS_URL','URL_WITH_TRAILING_SLASH');
define('KUDOS_API','API_KEY_MENTIONED_IN_CONFIG_FILE');

function kudos($url, $data) {
    $params = '';
&#9;foreach ($data as $key =&gt; $value) {
&#9;&#9;$params .= $key.'='.$value.'&amp;';
&#9;}

&#9;$params = trim($params, '&amp;');

&#9;&#9;$ch = curl_init();
&#9;&#9;curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
&#9;&#9;curl_setopt($ch, CURLOPT_MAXREDIRS, 10 );
&#9;&#9;curl_setopt($ch, CURLOPT_URL, KUDOS_URL.$url.'/'.KUDOS_API.'?'.$params);
&#9;&#9;curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
&#9;&#9;curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
&#9;&#9;curl_setopt($ch, CURLOPT_USERAGENT, "MozillaXYZ/1.0");
&#9;&#9;curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
&#9;&#9;curl_setopt($ch, CURLOPT_TIMEOUT, 10);
&#9;&#9;$output = curl_exec($ch);
&#9;&#9;curl_close($ch);
&#9;&#9;$decoded = json_decode($output);

&#9;&#9;return $decoded;
}

function kudos_post($url, $data) {
    $params = '';
&#9;foreach ($data as $key =&gt; $value) {
&#9;&#9;$params .= $key.'='.urlencode($value).'&amp;';
&#9;}

&#9;$params = trim($params, '&amp;');

&#9;$ch = curl_init();
&#9;curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
&#9;curl_setopt($ch, CURLOPT_MAXREDIRS, 10 );
&#9;curl_setopt($ch, CURLOPT_URL, KUDOS_URL.$url.'/'.KUDOS_API.'?'.$params);
&#9;curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
&#9;curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
&#9;curl_setopt($ch, CURLOPT_USERAGENT, "MozillaXYZ/1.0");
&#9;curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
&#9;curl_setopt($ch, CURLOPT_TIMEOUT, 10);
&#9;$output = curl_exec($ch);
&#9;curl_close($ch);
&#9;$decoded = $output;

&#9;return $decoded;

}
</code></pre><p>The first function is to pull results and the second function is to send posts (e.g. for manual entry).</p><p>Once this is done, you can pull results using the following function:</p><pre><code>$testimonials = kudos('posts',array('id'=&gt;KUDOSID,'limit'=&gt;'50', 'offset'=&gt;'0', 'orderby' =&gt; ''));
</code></pre><p>The KUDOSID will have to be manually found out. In Posts section (in Kudos), you can see the links with the ID (the number after the last / in the URL). Once you have the data, you can iterate through the array and display it wherever you like on your site. Since all the processing is done at PHP level, the code is super SEO friendly (unlike some Javascript).</p><p><strong>Requirements</strong><br>1. PHP 5+<br>2. mySQL</p><p><strong>Download</strong></p><p><a href="http://github.com/anantgarg/Kudos">Kudos is hosted at GitHub</a></p><p><strong>Getting Started</strong></p><p>Download and follow the instructions in install.md file</p><p><strong>Next Steps</strong></p><p>Feel free to contribute to the code. I have added some next steps in the todo.md file. Eventually I will create a testimonials plugin for WordPress.</p><p><strong>Comments/Suggestions?</strong><br>Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Spread The Word</strong><br>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>]]></content:encoded></item><item><title><![CDATA[FOUT (Flash of Unstyled Text) solution for Typekit and Chrome 50]]></title><description><![CDATA[The Problem]]></description><link>https://anantgarg.com/p/typekit-fout-fix</link><guid isPermaLink="false">https://anantgarg.com/p/typekit-fout-fix</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Wed, 22 Jun 2016 15:32:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!W4To!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9b8c19-a0ed-4111-b848-275e3b5e7cb6_225x225.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>The Problem</strong></p><p>If you are using Typekit to power fonts on your website, then you would have noticed the flash of unstyled text problem (or FOUT) in Chrome 50. The folks at Typekit have written a <a href="http://blog.typekit.com/2016/06/03/regarding-the-flash-of-unstyled-text-in-chrome-50/">fairly long post</a> about it; TL;DR- it&#8217;s not really considered a bug so the problem is here to stay.</p><p>There are a <a href="http://blog.typekit.com/2015/08/04/new-embed-code-for-asynchronous-font-loading/">couple</a> <a href="http://webdesign.tutsplus.com/articles/quick-tip-avoid-fout-by-adding-a-web-font-preloader--webdesign-8287">of</a> <a href="https://css-tricks.com/fout-foit-foft/">fixes</a> available for this problem but most posts are over a year old.</p><p><strong>The Solution</strong></p><p>I&#8217;ve modified some existing code by Sebastian Kippe (<a href="https://blog.5apps.com/2014/02/21/using-typekit-the-right-way-with-an-improved-loading-script.html">read his blog post</a>) and added a fix so that for a given session, if the browser is unable to load the fonts, then it will not try again till end of session. And if it is able to load the fonts, then the text will be invisible until the font is loaded.</p><p>Add the following just before the closing <strong>&lt;/HEAD&gt; tag</strong> of your site template-</p><pre><code>&lt;script&gt;
&#9;&#9;(function(d) {
&#9;&#9;&#9;loadFonts = 1;
&#9;&#9;&#9;if(window.sessionStorage){
&#9;&#9;&#9;&#9;if(sessionStorage.getItem('useTypekit')==='false'){
&#9;&#9;&#9;&#9;&#9;loadFonts = 0;
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;if (loadFonts == 1) {
&#9;&#9;&#9;&#9;&#9;var config = {
&#9;&#9;&#9;&#9;&#9;&#9;kitId: 'XXXXXXXX',
&#9;&#9;&#9;&#9;&#9;&#9;scriptTimeout: 3000,
&#9;&#9;&#9;&#9;&#9;&#9;async: true
&#9;&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;&#9;&#9;h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";if(window.sessionStorage){sessionStorage.setItem("useTypekit","false")}},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+="wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&amp;&amp;a!="complete"&amp;&amp;a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s);
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;)(document);
&#9;&#9;&lt;/script&gt;

</code></pre><pre><code>&lt;style&gt;
&#9;&#9;.wf-loading&#9;p, .wf-loading h1, .wf-loading h2, .wf-loading h3, .wf-loading h4 {
&#9;&#9;&#9;visibility: hidden;
&#9;&#9;}
&#9;&#9;.wf-active&#9;p, .wf-active h1, .wf-active h2, .wf-active h3, .wf-active h4 {
&#9;&#9;&#9;visibility: visible;
&#9;&#9;}
&#9;&#9;.wf-inactive&#9;p, .wf-inactive h1, .wf-inactive h2, .wf-inactive h3, .wf-inactive h4 {
&#9;&#9;&#9;visibility: visible;
&#9;&#9;}
&lt;/style&gt;
</code></pre><p>You will only need to replace &#8216;XXXXXXXX&#8217; in the above with your actual Typekit Kit ID. You can add more styles if need be.</p><p><strong>Flow chart</strong></p><p>1. The CSS styles hide all visible text (modify to add more styles if required)<br>2. The script tries to load the Typekit Javascript file<br>3. If the script is unable to do so within 3 seconds, it loads the standard fonts and sets a local storage variable to avoid trying to load the fonts again for the current session<br>4. All future calls for the same session show the standard fonts</p><p><strong>How to test if font&#8217;s don&#8217;t load?</strong></p><p>To check how your site will render if the Typekit fonts don&#8217;t load, simply replace &#8220;use.typekit.net&#8221; with &#8220;donotuse.typekit.net&#8221; (or anything else) in the above code. Don&#8217;t forget to replace it back once you&#8217;re done testing!</p><p>That&#8217;s all folks!</p><h3>Discuss, Share &amp; Subscribe</h3><p>Do let me know your suggestions on how I can improve this code. If you like what you are reading, then please help spread the word by re-tweeting, blogging and sharing this. Thank you.</p>]]></content:encoded></item><item><title><![CDATA[SocialTurn – Open Source PHP Buffer App Clone]]></title><description><![CDATA[Social Media Publishing for Teams]]></description><link>https://anantgarg.com/p/socialturn-open-source-php-buffer-app-clone</link><guid isPermaLink="false">https://anantgarg.com/p/socialturn-open-source-php-buffer-app-clone</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Tue, 24 Mar 2015 11:56:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1adb6872-b670-47f5-a9d5-1f82b233fe5c_1178x622.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Social Media Publishing for Teams</strong></p><p>SocialTurn makes it easy for teams to publish to social media. Drop the manual sharing of social passwords to each team member, and worrying about your security and get SocialTurn, the easy and free way to publish social updates for your whole team. SocialTurn is a PHP/mySQL based Buffer clone.</p><p><strong>Features</strong></p><p><strong>1. Social</strong><br>&#8211; Auto schedule social posts depending on day and time<br>&#8211; Share important posts via multiple accounts<br>&#8211; Use suggestions for posting generic updates<br>&#8211; Quick and simple updates and management</p><p><strong>2. Team</strong><br>&#8211; Manage multiple team members<br>&#8211; Assign privileges to members<br>&#8211; Give access to specific Twitter/Facebook accounts to each user<br>&#8211; Invite a new team member directly via email</p><p><strong>3. Connect</strong><br>&#8211; Manage multiple Twitter &amp; Facebook accounts<br>&#8211; Reconnect/delete old accounts with ease</p><p><strong>Demonstration</strong><br><a href="http://www.socialturn.com">Launch SocialTurn &#8211; Buffer Clone Demo</a></p><p><strong>Screenshot</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i1.wp.com/anantgarg.com/wp-content/uploads/2015/03/socialturn.jpg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RLfq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RLfq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RLfq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RLfq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RLfq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;socialturn&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i1.wp.com/anantgarg.com/wp-content/uploads/2015/03/socialturn.jpg&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="socialturn" title="socialturn" srcset="https://substackcdn.com/image/fetch/$s_!RLfq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RLfq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RLfq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RLfq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed2fca04-6b03-4242-99af-795a984cff1c_1178x622.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p><strong>Requirements</strong><br>1. PHP 5+<br>2. mySQL</p><p><strong>Getting Started</strong><br>Download and follow the instructions in README.md file</p><p><strong>Download</strong><br><a href="http://github.com/anantgarg/SocialTurn">SocialTurn is hosted at GitHub</a></p><p><strong>Comments/Suggestions?</strong><br>Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Spread The Word</strong><br>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>]]></content:encoded></item><item><title><![CDATA[Git FTP for Windows]]></title><description><![CDATA[Why are we still using FTP?]]></description><link>https://anantgarg.com/p/git-ftp-for-windows</link><guid isPermaLink="false">https://anantgarg.com/p/git-ftp-for-windows</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Wed, 25 Sep 2013 08:38:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!W4To!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9b8c19-a0ed-4111-b848-275e3b5e7cb6_225x225.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Why are we still using FTP?</h3><p>If you are using a shared host like GoDaddy, Asmallorange etc. they have limited options when it comes to uploading your code to their servers. Most will provide only FTP access. If you are using Git (or GitHub) for your development purpose, it becomes a huge inconvenience to use an FTP client to upload the files manually. If you have a large set of files, then you also need to manually upload only the changed files.</p><p>With this solution, you will not need to use an FTP client.</p><h3>Step 1</h3><p>Begin with downloading msysgit for Windows. At the time of writing this, the latest file is &#8220;msysGit-fullinstall-1.8.4-preview20130916.exe&#8221;. Click on the link below to download.</p><div class="captioned-image-container"><figure><p><a href="https://code.google.com/p/msysgit/downloads/list">Download msysgit</a> When prompted, extract to &#8220;c:\&#8221; and this will create a &#8220;c:\msysgit&#8221; folder. It may take a while to download and install. Step 2 On completion, it will launch c:\msysgit\msys.bat (you can run it manually if it doesn&#8217;t). Then simply run the following commands one-by-one: cd ~ git clone https://github.com/git-ftp/git-ftp git-ftp.git cd git-ftp.git &amp;&amp; chmod +x git-ftp cp ~/git-ftp.git/git-ftp /bin/git-ftp</p></figure></div><p>On completion, close this window and create a shortcut to &#8220;c:\msysgit\git-cmd.bat&#8221; (Note this is different from msys.bat)</p><h3>The Workflow</h3><p><strong>Committing Changes</strong></p><p>I use GitHub so I prefer using their simple tool for committing changes. You can use the command-line version as well if you need more control. If you need a good guide to learn Git, I suggest:</p><p><a href="http://net.tutsplus.com/tutorials/other/easy-version-control-with-git/">Easy version control with Git</a></p><p><strong>Pushing to FTP</strong></p><p>1. Simply launch git-cmd.bat<br>2. This will load a standard command prompt. Go to your working folder (which you want to synchronize) [must be git repo].<br>3. For the first time, add your FTP details</p><pre><code>git config git-ftp.user demouser
git config git-ftp.url ftp.example.com/demofolder
git config git-ftp.password demopassword
</code></pre><p>4. To initialize, run the following command for your first commit only</p><pre><code>git ftp init
</code></pre><p>5. After this, for all subsequent commits</p><pre><code>git ftp push
</code></pre><p>That&#8217;s all folks!</p><div><hr></div><h3>Discuss, Share &amp; Subscribe</h3><p>Do let me know your suggestions on how I can improve this tutorial. If you like what you are reading, then please help spread the word by re-tweeting, blogging and sharing this tutorial. Thank you.</p>]]></content:encoded></item><item><title><![CDATA[Amazon Beanstalk PHP hosting & database versioning for SaaS]]></title><description><![CDATA[This is part two of 3 posts that I intend to write about getting a SaaS app + site built from scratch.]]></description><link>https://anantgarg.com/p/amazon-beanstalk-php-hosting-database-versioning-for-saas</link><guid isPermaLink="false">https://anantgarg.com/p/amazon-beanstalk-php-hosting-database-versioning-for-saas</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Mon, 17 Jun 2013 08:30:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a14b0785-aaf5-4375-9d73-64c5a8a30e0c_617x445.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is part two of 3 posts that I intend to write about getting a SaaS app + site built from scratch. If you haven&#8217;t already, then you should read part 1 about <a href="http://anantgarg.com/2013/06/10/build-a-php-saas-app-from-scratch/">creating a PHP SaaS app</a>. In this part we will talk about 2 things:</p><p>1. Database versioning<br>2. Hosting, Database &amp; DNS</p><div><hr></div><h3>Part 1: Database versioning</h3><p>In the first part, we discuss about creating separate databases for every client. Now the problem is that these databases are empty and we need to fill them with some basic data + schema. The problem of creating this in the site code, is when you update your schema, you will have to manually update all the databases. This can be extremely tedious and difficult to achieve.</p><p>Thus to solve this problem, we assume our DB is empty. Then we create our version 1 of the schema and save it as db/1.sql. This will be our base i.e. when the user first visits the client&#8217;s site, it will run this SQL to create the require schema. Next version will be 2.sql (which may add/drop tables etc.) and so on.</p><p>In our app configuration file, we will add a variable (somewhere at the top) and set it to the latest schema version.</p><pre><code>$currentdbversion = 5;
</code></pre><p>Then after the DB connection, we will add the following code:</p><pre><code>...

mysql_selectdb(DB_NAME,$dbh);

// Success! Now we can continue to use the app as-is!

$sql = ("select * from cometchat_settings where name = 'dbversion'");
$appsettings = mysql_fetch_row($sql);

$dbversion = 0;

if (!empty($appsettings['value'])) {
   $dbversion = $appsettings['value'];
}

while ($dbversion &lt; $currentdbversion) {
    $dbversion++;
    if (file_exists(dirname(__FILE__).'/db/'.($dbversion).'.php')) {
        include_once(dirname(__FILE__).'/db/'.($dbversion).'.php');
    }
}

</code></pre><p>In 1.sql, you can add your SQL queries, and add the bottom you will add:</p><pre><code>&lt;?php

$sql = ("CREATE TABLE  `app_settings` ( `name` varchar(255) NOT NULL,  `value` text NOT NULL, PRIMARY KEY  USING BTREE (`name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
$query = mysql_query($sql);

// Add other SQL queries

$sql = ("REPLACE INTO `app_settings` (name,value) values ('dbversion','1')");
$query = mysql_query($sql);

</code></pre><p>In 2.sql and all other SQL files, it will be:</p><pre><code>&lt;?php

// Add other SQL queries

$sql = ("REPLACE INTO `app_settings` (name,value) values ('dbversion','2')");
$query = mysql_query($sql);

</code></pre><p>So the workflow is as follows:</p><p>1. The site code only creates the DB. It does not populate the DB with any data/schema.<br>2. When the client visits the app for the first time, $dbversion is 0, so 1.sql is executed.<br>3. When you make changes to your SQL DB, you simply update $currentdbversion &amp; add the $currentdbversion.sql file to the db folder<br>4. When the app is run the next time, it will check if the SQL dbversion is lower than $currentdbversion; if yes, it will run all the SQL files till it reaches the latest version.</p><div><hr></div><h3>Part 2: Hosting on Elastic Beanstalk</h3><p>Hosting on Amazon&#8217;s Elastic Beanstalk is fairly simple.</p><p><strong>Step 1</strong></p><p>Signup for Amazon AWS and then go to &#8220;Elastic Beanstalk&#8221; and create a new application. Zip all your PHP code (for your app) and then attach it while creating:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/hosting1.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jTWt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 424w, https://substackcdn.com/image/fetch/$s_!jTWt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 848w, https://substackcdn.com/image/fetch/$s_!jTWt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 1272w, https://substackcdn.com/image/fetch/$s_!jTWt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jTWt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Amazon Elastic Beanstalk - Create new application&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/hosting1.png&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Amazon Elastic Beanstalk - Create new application" title="Amazon Elastic Beanstalk - Create new application" srcset="https://substackcdn.com/image/fetch/$s_!jTWt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 424w, https://substackcdn.com/image/fetch/$s_!jTWt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 848w, https://substackcdn.com/image/fetch/$s_!jTWt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 1272w, https://substackcdn.com/image/fetch/$s_!jTWt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F571d86d6-58d8-4c16-a686-7486bbc6a323_617x445.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Step 2</strong></p><p>Enter a URL for your app; I have used &#8220;mydummyapp&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i2.wp.com/anantgarg.com/wp-content/uploads/2013/06/host2.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xld_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 424w, https://substackcdn.com/image/fetch/$s_!Xld_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 848w, https://substackcdn.com/image/fetch/$s_!Xld_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 1272w, https://substackcdn.com/image/fetch/$s_!Xld_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xld_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Amazon Elastic Beanstalk - Enter a URL&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i2.wp.com/anantgarg.com/wp-content/uploads/2013/06/host2.png&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Amazon Elastic Beanstalk - Enter a URL" title="Amazon Elastic Beanstalk - Enter a URL" srcset="https://substackcdn.com/image/fetch/$s_!Xld_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 424w, https://substackcdn.com/image/fetch/$s_!Xld_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 848w, https://substackcdn.com/image/fetch/$s_!Xld_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 1272w, https://substackcdn.com/image/fetch/$s_!Xld_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b3819a8-f040-4fd8-9ab2-5a83d71e375b_618x429.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Step 3</strong></p><p>t1.micro is sufficient to begin with and will keep your costs low. Select it and then proceed with creation.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/host3.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fE2W!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 424w, https://substackcdn.com/image/fetch/$s_!fE2W!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 848w, https://substackcdn.com/image/fetch/$s_!fE2W!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 1272w, https://substackcdn.com/image/fetch/$s_!fE2W!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fE2W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63d4432c-62ed-46b4-a908-81281de459be_617x459.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Amazon Elastic Beanstalk - Choose server size&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/host3.png&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Amazon Elastic Beanstalk - Choose server size" title="Amazon Elastic Beanstalk - Choose server size" srcset="https://substackcdn.com/image/fetch/$s_!fE2W!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 424w, https://substackcdn.com/image/fetch/$s_!fE2W!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 848w, https://substackcdn.com/image/fetch/$s_!fE2W!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 1272w, https://substackcdn.com/image/fetch/$s_!fE2W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63d4432c-62ed-46b4-a908-81281de459be_617x459.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Step 4</strong></p><p>Go to &#8220;RDS&#8221;, &#8220;Launch new DB instance&#8221; and then create a new instance.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/db1.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kdZo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 424w, https://substackcdn.com/image/fetch/$s_!kdZo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 848w, https://substackcdn.com/image/fetch/$s_!kdZo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 1272w, https://substackcdn.com/image/fetch/$s_!kdZo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kdZo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Amazon RDS - Create new instance&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/db1.png&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Amazon RDS - Create new instance" title="Amazon RDS - Create new instance" srcset="https://substackcdn.com/image/fetch/$s_!kdZo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 424w, https://substackcdn.com/image/fetch/$s_!kdZo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 848w, https://substackcdn.com/image/fetch/$s_!kdZo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 1272w, https://substackcdn.com/image/fetch/$s_!kdZo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08e3c474-555c-4a3e-a6f4-1ddde8593d60_655x545.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Step 5</strong></p><p>Create a DB for your site (which will be used to store all clients info). The client specific DBs will be created on the fly (in the same instance).</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/db2.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yzq7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 424w, https://substackcdn.com/image/fetch/$s_!yzq7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 848w, https://substackcdn.com/image/fetch/$s_!yzq7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 1272w, https://substackcdn.com/image/fetch/$s_!yzq7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yzq7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Amazon RDS - Create Site DB&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/db2.png&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Amazon RDS - Create Site DB" title="Amazon RDS - Create Site DB" srcset="https://substackcdn.com/image/fetch/$s_!yzq7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 424w, https://substackcdn.com/image/fetch/$s_!yzq7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 848w, https://substackcdn.com/image/fetch/$s_!yzq7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 1272w, https://substackcdn.com/image/fetch/$s_!yzq7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3bccd1e-66a2-442c-a89f-3acbe26ffc03_657x498.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Once completed, add the connection details to your app and test if the connection is working fine. If you are unable to connect, then edit the &#8220;default&#8221; Security Group and add &#8220;CIDR/IP: 0.0.0.0/0&#8221;. This will allow you to connect to the instance from any IP. Later, you can modify this to only certain IPs.</p><p><strong>Step 6</strong></p><p>Go to &#8220;Route 53&#8221; and &#8220;Create Hosted Zone&#8221; and enter your domain URL. Then double click on the listing and then &#8220;Create Record Set&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i2.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns1.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o5q8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 424w, https://substackcdn.com/image/fetch/$s_!o5q8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 848w, https://substackcdn.com/image/fetch/$s_!o5q8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 1272w, https://substackcdn.com/image/fetch/$s_!o5q8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o5q8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Amazon Route 53 - Create new Record Set&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i2.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns1.png&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Amazon Route 53 - Create new Record Set" title="Amazon Route 53 - Create new Record Set" srcset="https://substackcdn.com/image/fetch/$s_!o5q8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 424w, https://substackcdn.com/image/fetch/$s_!o5q8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 848w, https://substackcdn.com/image/fetch/$s_!o5q8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 1272w, https://substackcdn.com/image/fetch/$s_!o5q8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F980a6ed1-131a-4c06-8c82-3846aaa3aa17_773x401.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Once done, get the server IP for your site hosting (from Asmallorange/Rackspace/site-host) and two <strong>A</strong> records:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns2.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!O2dB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 424w, https://substackcdn.com/image/fetch/$s_!O2dB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 848w, https://substackcdn.com/image/fetch/$s_!O2dB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 1272w, https://substackcdn.com/image/fetch/$s_!O2dB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!O2dB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Amazon Route 53&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns2.png&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Amazon Route 53" title="Amazon Route 53" srcset="https://substackcdn.com/image/fetch/$s_!O2dB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 424w, https://substackcdn.com/image/fetch/$s_!O2dB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 848w, https://substackcdn.com/image/fetch/$s_!O2dB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 1272w, https://substackcdn.com/image/fetch/$s_!O2dB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27ad3151-98d4-4a4f-9cbf-d77e347b1e56_616x89.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Finally, update your nameservers to point to those mentioned by Amazon.</p><div><hr></div><p>Once this is done, you can visit http://mydummyapp.elasticbeanstalk.com and check your app. You can use any third-party host like <a href="http://www.asmallorange.com">Asmallorange</a> or <a href="http://www.rackspace.com">RackSpace</a> to host your SaaS site. You do not need to use Amazon Beanstalk to host your SaaS site.</p><p>That&#8217;s all folks! Now you have successfully setup your server (Beanstalk), your DBs (RDS) and your DNS (Route 53). You should be now ready to start testing your app along with your site.</p><div><hr></div><h3>Where do we go from here?</h3><p>We have achieved a lot using this tutorial (and <a href="http://anantgarg.com/2013/06/10/build-a-php-saas-app-from-scratch/">part 1</a>). We have setup our app, site and have configured Amazon&#8217;s services to host our app. In the next part, we will talk about scaling and other tips &amp; tricks.</p><div><hr></div><h3>Discuss, Share &amp; Subscribe</h3><p>Do let me know your suggestions on how I can improve this tutorial. If you like what you are reading, then please help spread the word by re-tweeting, blogging and sharing this tutorial. Thank you.</p><p>To get a notification when the next part is ready, please subscribe using the form at the bottom of this page.</p>]]></content:encoded></item><item><title><![CDATA[Build a PHP SaaS app from scratch]]></title><description><![CDATA[This is the first of 3 posts which will talk about launching your own SaaS product (or converting your existing single tenant app to a SaaS app).]]></description><link>https://anantgarg.com/p/build-a-php-saas-app-from-scratch</link><guid isPermaLink="false">https://anantgarg.com/p/build-a-php-saas-app-from-scratch</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Mon, 10 Jun 2013 14:47:31 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f95525f2-a85a-4201-aff7-fe0e846272e8_630x269.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>This is the first of 3 posts which will talk about launching your own SaaS product (or converting your existing single tenant app to a SaaS app). We will cover various aspects from developing the app to building a SaaS site (to enable your clients to signup for it)</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0X-O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0X-O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 424w, https://substackcdn.com/image/fetch/$s_!0X-O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 848w, https://substackcdn.com/image/fetch/$s_!0X-O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 1272w, https://substackcdn.com/image/fetch/$s_!0X-O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0X-O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Typical SaaS Application&quot;,&quot;title&quot;:&quot;Typical SaaS Application&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Typical SaaS Application" title="Typical SaaS Application" srcset="https://substackcdn.com/image/fetch/$s_!0X-O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 424w, https://substackcdn.com/image/fetch/$s_!0X-O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 848w, https://substackcdn.com/image/fetch/$s_!0X-O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 1272w, https://substackcdn.com/image/fetch/$s_!0X-O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cad2172-d5e8-4527-ba91-5e4acfe0b1ab_630x269.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><h3><strong>We will be looking at&#8230;</strong></h3><p><strong>1. Creating your app</strong></p><p><strong>The app is the product that you are going to sell. If you already have this app ready, you will only have to modify it so that it is compatible for multiple clients. The app is the front-end for your client&#8217;s end-users.</strong></p><p><strong>2. Creating your SaaS site</strong></p><p><strong>The site is the front-end for your clients. They will be using your site to signup for the app.</strong></p><p><strong>3. Hosting et al.</strong></p><p><strong>We will talk about how to host your app + site, how to ensure scaling, setting up DNS &amp; SSL, database versioning etc.</strong></p><div><hr></div><h3><strong>Part 1: Create your app</strong></h3><p><strong>Before you begin with selling your product, you need to have a product. Let us first try and understand the SaaS architecture. Every app consists of two parts: code &amp; data. Now, every client has end-users; the data is shared between these end-users and (mostly) not with other clients&#8217; end-users. So every set of end-users (of each client) should access a different set of data. In most cases, the code used by all the clients can be the same.</strong></p><p><strong>Now there are various ways in which a SaaS app can be designed:</strong></p><div><hr></div><p><strong>Option 1 &#8211; Single database, single code-base</strong></p><p><strong>Use a single database and store all client data in a single database. You will have to add a field like &#8220;clientid&#8221; in all your tables and modify all your SQL queries to select the correct &#8220;clientid&#8221;. If you already have a single tenant app, then heavy modification is required in the code.</strong></p><p><strong>Advantages: Easy to manage database &amp; updates; no duplication.<br>Disadvantages: If you miss out a &#8220;clientid&#8221; in the SQL query, it may result in issues with another client.</strong></p><p><strong>Option 2 &#8211; Multiple database, single code-base</strong></p><p><strong>Create a separate database for every client. No modification is required to your existing single-tenant codebase. You will only have to modify the configuration file to select a database depending on the client.</strong></p><p><strong>Advantages: Very little codebase modification, easy to ensure client data does not get mixed.<br>Disadvantages: Multiple databases to manage so in the event of DB schema updates, you will have to update each and every database.</strong></p><p><strong>Option 3 &#8211; Multiple database, multiple code-base</strong></p><p><strong>Create a separate database &amp; duplicate the complete code for every client. No modification required at all.</strong></p><p><strong>Advantages: No codebase modification, easy to ensure client data does not get mixed, easy to perform client specific modifications.<br>Disadvantages: Multiple databases/code to manage so in the event of DB schema/code updates, you will have to manually update for each client.</strong></p><div><hr></div><p><strong>There are a <a href="http://stackoverflow.com/questions/13325383/multi-tenant-php-saas-seperate-dbs-for-each-client-or-group-them">number</a> <a href="http://programmers.stackexchange.com/questions/141261/multi-tenancy-single-database-vs-multiple-database">of</a> <a href="http://stackoverflow.com/questions/255616/should-i-use-a-single-or-multiple-database-setup-for-a-multi-client-application">articles</a> <a href="http://stackoverflow.com/questions/69128/saas-database-design-multiple-databases-split">discussing</a> which approach is the best but option 2 helps us develop a scalable SaaS solution in the quickest possible way. So we will stick to option 2 for this tutorial.</strong></p><p><strong>If you already have an app then you will only have to modify the configuration file as below. If you do not, then make sure your configuration file looks something like below and the rest of the app can be however you like it.</strong></p><p><strong>Let us begin with the PHP part. You should be ideally using a framework like <a href="http://symfony.com/">Symfony</a> or <a href="http://www.zend.com/">Zend</a> to create your app. To simplify, I am only going to be talking about config.php:</strong></p><p><strong>This is how a simple standard config.php file looks: (usually DB connection is not part of this, but for a clearer explanation, I have added it)</strong></p><pre><code>&lt;?php

define('DB_SERVER','localhost');
define('DB_PORT','3306');
define('DB_USERNAME','root');
define('DB_PASSWORD','password');
define('DB_NAME','dummyapp');

$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_USERNAME,DB_PASSWORD);
if (!$dbh) {
&#9;echo "Oops! Something went horribly wrong.";
&#9;exit();
}
mysql_selectdb(DB_NAME,$dbh);

</code></pre><p><strong>In the above code, when the user visits a PHP page, it calls config.php, which stores the database configuration &amp; connects the database. Then the remaining PHP files, calling this configuration file, can connect to the DB.</strong></p><p><strong>The problem in the above code is that it is built for a single client (with a single set of end-users). We need to modify this code so that for each client, a different database is used. So depending on how the app is called (i.e. depending on the URL), we will connect to a different database. If you look at SaaS apps online, you will find that they assign a unique sub-domain to every client. This technique will come in handy.</strong></p><p><strong>So if your site is mydummyapp.com, then when a client registers, he will be assigned a URL like anantgarg.mydummyapp.com, yourname.mydummyapp.com etc. Now, all end-users of the client will connect to the same sub-domain.</strong></p><p><strong>To make our app SaaS friendly we need to check the sub-domain and then accordingly connect to a database. We will perform the following checks:<br><br>1. Let the end-user visit the app using *.mydummyapp.com (DNS configuration in next post)<br>2. Check a central table if the sub-domain is registered; if yes, to whom? Then fetch the DB details<br>3. Connect the end-user to the client&#8217;s DB<br></strong></p><p><strong>Here is how a modified config.php will look:</strong></p><pre><code>&lt;?php

$data = explode('.',$_SERVER['SERVER_NAME']); // Get the sub-domain here

// Add some sanitization for $data

if (!empty($data[0])) {
    $subdomain = $data[0]; // The * of *.mydummyapp.com will be now stored in $subdomain
}

// Contact a central database (which stores information about all clients

define('SITE_DB_SERVER','localhost');
define('SITE_DB_PORT','3306');
define('SITE_DB_USERNAME','root');
define('SITE_DB_PASSWORD','password');
define('SITE_DB_NAME','dummysite');

$sitedbh = mysql_connect(SITE_DB_SERVER.':'.SITE_DB_PORT,SITE_DB_USERNAME,SITE_DB_PASSWORD);

// Get client's DB details (make sure $subdomain is already sanitized)

$sitemysql = mysql_query('select id,dbusername,dbpassword from clients where subdomain = '.$subdomain.');
$appdata = mysql_fetch_row($sitemysql);

// You can add caching code here to skip the above DB call

if (empty($appdata['id'])) {
    // If the client does not exist i.e. the end-user types the wrong sub-domain

    echo "Oops! Sorry, we are unable to find you! Please email us at support@mydummyapp.com";
    exit();
}

define('DB_SERVER','localhost');
define('DB_PORT','3306');
define('DB_USERNAME',$appdata['dbusername']);
define('DB_PASSWORD',$appdata['dbpassword']);
define('DB_NAME','dummyapp_'.$appdata['id']);

$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_USERNAME,DB_PASSWORD);
if (!$dbh) { exit(); }

mysql_selectdb(DB_NAME,$dbh);

// Success! Now we can continue to use the app as-is!

</code></pre><p><strong>If you are modifying your existing app, then you only need to modify your configuration file and make it similar to the above. The rest of the app should not need any modification (as long as you are not saving any files). If you have files, then you will have to modify the file upload code and add a folder e.g. /uploads/$subdomain/$filename.</strong></p><p><strong>If you are testing on localhost, you can simply add something like the following to your hosts file:</strong></p><pre><code>127.0.0.1      test.mydummyapp.com
127.0.0.1      test2.mydummyapp.com
</code></pre><div><hr></div><h3><strong>Part 2: Create your SaaS site</strong></h3><p><strong>I assume you can create a simple SaaS site with user registration. On your registration page, you will need to have something like:</strong></p><pre><code>Username: _ _ _ _ _ _ _ _
Password: _ _ _ _ _ _ _ _
Subdomain: _ _ _ _ _ _ _ _ (.mydummyapp.com): 
</code></pre><p><strong>You will need to create a common DB (e.g. dummysite) with a table called &#8220;clients&#8221;. In your real site, you will also need other tables like &#8220;subscriptions&#8221; which will hold payment information.</strong></p><pre><code>create table clients (
  id int default 0 auto_increment,
  username varchar(50),
  password varchar(255),
  subdomain varchar(50),
  dbusername varchar(50),
  dbpassword varchar(50)
)
</code></pre><p><strong>Now for every client who signs up on your site, you will have to add an entry into the above table. You will also have to create random dbusername &amp; dbpassword. And finally, you need to create a new DB for this client.</strong></p><p><strong>The PHP code for that will be:</strong></p><pre><code>// Connect to database instance (which stores all app DBs)
$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_MASTERUSERNAME,DB_MASTERPASSWORD);

// Create DB ($id will have value of client's inserted ID)
mysql_query("CREATE DATABASE mydummyapp_".$id."",$dbh) or die(mysql_error());

// Create user and grant all privileges for the above DB
mysql_query("GRANT ALL ON mydummyapp_".$id.".* to  ".$dbusername." identified by '".$dbpassword."'",$dbh) or die(mysql_error());
</code></pre><p><strong>Once this is done, you should be able to put all the pieces together and get your first SaaS app up and running.</strong></p><div><hr></div><h3><strong>Part 3: Putting it all together</strong></h3><p><strong>The site structure will be as follows-</strong></p><p><strong>www.mydummyapp.com: SaaS site (where the client registers)<br>*.mydummyapp.com: SaaS app (catch-all points to the actual app)</strong></p><p><strong>The workflow is as follows:</strong></p><p><strong>1. The client visits www.mydummyapp.com<br>2. The client registers on the site<br>3. The site creates the a DB (and user) for the client (and adds an entry in the site&#8217;s DB)<br>4. The client then uses the new URL (i.e. clientsubdomain.mydummyapp.com) for himself &amp; his end-users<br>5. The end-users directly visit clientsubdomain.mydummyapp.com which is basically the app using the client&#8217;s DB</strong></p><div><hr></div><h3><strong>Where do we go from here?</strong></h3><p><strong>We have achieved quite a bit using this tutorial. We have setup our app and site and can now allow clients to register and use the app. In the next parts will we discuss about managing the databases, setting up DNS, handling hosting, scaling and other tips &amp; tricks.</strong></p><div><hr></div><h3><strong>Discuss, Share &amp; Subscribe</strong></h3><p><strong>Do let me know your suggestions on how I can improve this tutorial. If you like what you are reading, then please help spread the word by re-tweeting, blogging and sharing this tutorial. Thank you.</strong></p><p><strong>To get a notification when the next part is ready, please subscribe using the form at the bottom of this page.</strong></p>]]></content:encoded></item><item><title><![CDATA[Busting the cookies and privacy myth]]></title><description><![CDATA[I am going to try and make today&#8217;s post as simple as possible.]]></description><link>https://anantgarg.com/p/busting-the-cookies-and-privacy-myth</link><guid isPermaLink="false">https://anantgarg.com/p/busting-the-cookies-and-privacy-myth</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Sat, 18 Feb 2012 10:10:45 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/60a2c0d8-1feb-4d7d-b407-5206802ebc23_604x234.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am going to try and make today&#8217;s post as simple as possible. Note some explanations maybe a little too simple for my technical audience.</p><p>I was referenced in the WSJ article- <a href="http://j.mp/ag-wsj">Google&#8217;s iPhone tracking</a> regarding my <a href="http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/">cross-domain cookies</a> post.</p><p>For my technical audience, here is a <a href="http://webpolicy.org/2012/02/17/safari-trackers/">detailed version of how my code was actually used</a>.</p><p><strong>So let us begin from scratch&#8230; what are cookies?</strong></p><p>Cookies (data on your computer used to identify you) are stored whenever you visit a site. Usually, this data is used to keep you logged into a site or to figure out if you are a repeat visitor.</p><p>This is fine and in-order for the web to function in it&#8217;s current state, in many ways, essential.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zJgo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zJgo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 424w, https://substackcdn.com/image/fetch/$s_!zJgo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 848w, https://substackcdn.com/image/fetch/$s_!zJgo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!zJgo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zJgo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Site's store cookies&quot;,&quot;title&quot;:&quot;Site's store cookies&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Site's store cookies" title="Site's store cookies" srcset="https://substackcdn.com/image/fetch/$s_!zJgo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 424w, https://substackcdn.com/image/fetch/$s_!zJgo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 848w, https://substackcdn.com/image/fetch/$s_!zJgo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!zJgo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F969238ee-1d72-4479-84fc-803d61e799a0_604x234.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>In the above diagram, when you visit siteA.com, a cookie is set for siteA.com on your computer.</p><p>When you visit siteB.com, a cookie is set for siteB.com on your computer.</p><p>But siteB.com, <strong>CANNOT</strong> read the data from the cookies set in siteA.com and vice-versa. Thus siteB.com does not know if you ever visited siteA.com or what you did there.</p><p><strong>Let&#8217;s introduce the ad-companies</strong></p><p>Ad-companies started displaying ads on sites depending on the content of the page. So, if you are a webmaster, you add a line of JavaScript code and a neat looking advertisement is displayed on your site page.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m-KY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m-KY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 424w, https://substackcdn.com/image/fetch/$s_!m-KY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 848w, https://substackcdn.com/image/fetch/$s_!m-KY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!m-KY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m-KY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Ads are displayed&quot;,&quot;title&quot;:&quot;Ads are displayed&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Ads are displayed" title="Ads are displayed" srcset="https://substackcdn.com/image/fetch/$s_!m-KY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 424w, https://substackcdn.com/image/fetch/$s_!m-KY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 848w, https://substackcdn.com/image/fetch/$s_!m-KY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!m-KY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e0ca98-86ad-445e-a6d8-81cdd489eae8_224x224.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>But the information on the page is not enough to generate targeted ads. So the ad-companies wanted to know where you&#8217;ve been before.</p><p>Now here is where the privacy issue unfolds.</p><p><strong>Tracking your moves</strong></p><p>Inorder to track you, as you moved from one site to another (both displaying sites ads from the same ad-company), the companies need to store cookies.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!roT6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!roT6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 424w, https://substackcdn.com/image/fetch/$s_!roT6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 848w, https://substackcdn.com/image/fetch/$s_!roT6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!roT6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!roT6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cookies of one site cannot be accessed by another&quot;,&quot;title&quot;:&quot;Cookies of one site cannot be accessed by another&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cookies of one site cannot be accessed by another" title="Cookies of one site cannot be accessed by another" srcset="https://substackcdn.com/image/fetch/$s_!roT6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 424w, https://substackcdn.com/image/fetch/$s_!roT6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 848w, https://substackcdn.com/image/fetch/$s_!roT6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!roT6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ece16da-109d-4126-9a7e-98e4c4838af7_603x231.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The problem is that cookies set on one site CANNOT be read by another site. In other words, cookies set on siteA.com cannot be read by siteB.com.</p><p>So the work-around used is to store a third-party cookie (i.e. a cross-domain cookie). When a user visits siteA.com, a cookie is not only set for siteA.com but also for a <strong>common domain</strong> which is accessed by all sites that use the ad-company&#8217;s code.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RCfb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RCfb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RCfb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RCfb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RCfb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RCfb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;siteA.com and siteB.com set cookies for their domain as well as for adsite.com&quot;,&quot;title&quot;:&quot;siteA.com and siteB.com set cookies for their domain as well as for adsite.com&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="siteA.com and siteB.com set cookies for their domain as well as for adsite.com" title="siteA.com and siteB.com set cookies for their domain as well as for adsite.com" srcset="https://substackcdn.com/image/fetch/$s_!RCfb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RCfb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RCfb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RCfb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F771d37e5-43a6-4160-95a3-5e99cd82882a_599x298.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Thus, siteA.com, siteB.com and all other sites displaying ads from adsite.com, store and access the same third-party cookie.</p><p>So when you visit siteA.com, a third-party cookie is created on adsite.com. When you visit siteB.com, the <strong>same</strong> cookie is read.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OYJD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OYJD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 424w, https://substackcdn.com/image/fetch/$s_!OYJD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 848w, https://substackcdn.com/image/fetch/$s_!OYJD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!OYJD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OYJD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;siteA.com and siteB.com access their own site cookies and the cookies of adsite.com&quot;,&quot;title&quot;:&quot;siteA.com and siteB.com access their own site cookies and the cookies of adsite.com&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="siteA.com and siteB.com access their own site cookies and the cookies of adsite.com" title="siteA.com and siteB.com access their own site cookies and the cookies of adsite.com" srcset="https://substackcdn.com/image/fetch/$s_!OYJD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 424w, https://substackcdn.com/image/fetch/$s_!OYJD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 848w, https://substackcdn.com/image/fetch/$s_!OYJD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!OYJD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c9ddb64-e8ec-4423-ba87-aae60f4048dd_522x492.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Thus adsite.com now knows that you have been to siteA.com before going to siteB.com. On the basis of this knowledge, a targeted ad can be shown. For an ad-company which provides ads to only two sites, this is not very useful. But imagine if you have 70% of the sites using your code.</p><p>Then, nearly most of the times, the same cookie can be read and your movements can be tracked as you browse through the internet. So now, the ad-company knows exact what kind of sites you are visiting and serve ads accordingly.</p><p><strong>But why does the WSJ article only talk about Safari?</strong></p><p>Safari, by default, <strong>does not allow third-party cookies</strong> to be created.</p><p><em>However, most major browsers- Firefox, Internet Explorer (using the correct header tags), Chrome etc. do ALLOW setting third-party cookies.</em></p><p><strong>Where does your technique come in?</strong></p><p>The technique <a href="http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/">I posted</a> two years ago, allows setting third-party cookies for Safari as well. This helps in offering a consistent user experience irrespective of the browser used.</p><p>Note that other browsers already allow creation of third-party cookies. So Safari is the only exception.</p><p><strong>How was this discovered?</strong></p><p>A Stanford researcher, <a href="http://stanford.edu/~jmayer">Jonathan Mayer</a> found that many large companies like Google, MIG, PointRoll etc. used a variation of my technique to circumvent Safari&#8217;s policy.</p><p>Infact, Facebook&#8217;s official <a href="https://developers.facebook.com/docs/best-practices/">developer best practices</a> article linked to my post (the page has since been removed).</p><p>You must remember that this issue affects Safari users ONLY.</p><p>Other browsers already allow third-party cookies and thus they CAN track you <em>as they do NOT have any such policy</em>.</p><p><strong>How do I protect myself? Should I disable cookies?</strong></p><p>NO. Do not disable cookies altogether. This will cause most of your sites (which have a login system) to stop working.</p><p>Third-party cookies also have a number of other uses like logging in users to third-party sites and are NOT always used for tracking. They cannot steal your data (a myth noted by some users in comments).</p><p>If you want, you can disable third-party cookies in browsers. Dennis O&#8217;Reilly has written a guide on <a href="http://howto.cnet.com/8301-11310_39-20042703-285/disable-third-party-cookies-in-ie-firefox-and-google-chrome/">how to disable third-party cookies</a> for major browsers.</p><p>You may also want to use incognito/private browsing modes when using a public PC.</p><p><strong>Questions/Suggestions?</strong></p><p>If you have any questions about how this affects you or need further explanation, feel free to post a comment or contact me.</p>]]></content:encoded></item><item><title><![CDATA[Exploit Scanner]]></title><description><![CDATA[The Problem]]></description><link>https://anantgarg.com/p/exploit-scanner</link><guid isPermaLink="false">https://anantgarg.com/p/exploit-scanner</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Wed, 15 Sep 2010 07:26:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!W4To!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9b8c19-a0ed-4111-b848-275e3b5e7cb6_225x225.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>The Problem</strong></p><p>As your site grows, so do your chances of getting hacked. Most of these hacks are due to known exploits in existing open/closed source software. One of the biggest worries is a user getting full access to your site using a simple PHP upload.</p><p><strong>So what&#8217;s the solution?</strong></p><p>This is no silver bullet but if a user uploads any malicious files to your server, you should be able to quickly check what files have been modified/added.</p><p><strong>PHP Exploit Scanner</strong></p><p>The Exploit Scanner is a single PHP file which generates MD5 hash for all files of a particular software and then allows you to compare that with software you think has been modified.</p><p><strong>Sample Usage</strong></p><p>Lets say you want to check for any exploits in your WordPress installation</p><p>1. Download a fresh copy of WordPress from the official site (make sure you download correct version)<br>2. Upload WordPress to your server/localhost<br>3. Run <em>exploitscanner.php?action=generate</em> in the new WordPress folder (you do not need to install WordPress)<br>4. Check filehashes.php and verify output is correct (i.e. an array of files with their hashes)<br>5. Now upload exploitscanner.php and filehashes.php to your live WordPress folder<br>6. Run <em>exploitscanner.php?action=scan</em> to generate a list of modified/added files<br>7. Check output of scanresults.txt</p><p><strong>Generating Hashes</strong></p><p>Upload exploitscanner.php to the folder for which you want to generate file hashes and then point your browser to:</p><p>http://www.yoursite.com/untouchedsoftware/exploitscanner.php?action=generate</p><p>Only output is filehashes.php</p><p>Note: Be sure that the software is not already exploited. Ideally use a fresh copy from the software creators (make sure you check the version)</p><p><strong>Scanning For Exploits</strong></p><p>Upload exploitscanner.php to the folder for which you want to check file hashes and then point your browser to:</p><p>http://www.yoursite.com/hackedsoftware/exploitscanner.php?action=scan</p><p>Only output is scanresults.txt<br><em>Legend-</em> F: New file | M: Modified file</p><p>First search for all .php files to see what is changed. If the file is tagged M, you can use a difference tool like <a href="http://winmerge.org">WinMerge</a> to find out what has changed.</p><p><strong>Download</strong></p><p>Download <a href="http://www.anantgarg.com/exploit-scanner.zip">PHP Exploit Scanner</a> free</p><p><strong>License</strong></p><p>Exploit Scanner is licensed under MIT license. Let me know if you make any interesting use of the script.</p><p><strong>Comments/Suggestions?</strong></p><p>If you would like to assist in creating a community where users can quickly download filehashes.php for their software then feel free to contact me using the form below.</p><p>Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Future Updates</strong></p><p>1. Improve recursive function to avoid time out on shared servers<br>2. Create a community where users can upload hashes for known software like phpBB, vBulletin etc.<br>3. Add GUI<br>4. Database integrity<br>5. Malicious code insertion in template files</p><p><strong>Spread The Word</strong></p><p>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>]]></content:encoded></item><item><title><![CDATA[Cross-domain cookies/sessions in Safari and all other browsers]]></title><description><![CDATA[Want to read a simplified version of this article?]]></description><link>https://anantgarg.com/p/cross-domain-cookies-in-safari</link><guid isPermaLink="false">https://anantgarg.com/p/cross-domain-cookies-in-safari</guid><dc:creator><![CDATA[Anant Garg]]></dc:creator><pubDate>Thu, 18 Feb 2010 12:20:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!W4To!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9b8c19-a0ed-4111-b848-275e3b5e7cb6_225x225.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Want to read a simplified version of this article? <strong><a href="http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/">Read my follow-up article regarding Google&#8217;s iPhone Tracking</a></strong></p><p><strong>The Problem</strong></p><p>Safari does not allow cross-domain cookies. In other words, if on X.com, you load an iFrame with contents of Y.com and set a cookie in the iFrame, Safari will not save the cookie. This problem also occurs in IE6/7 but can be resolved by sending a P3P header.</p><p><strong>The Solution</strong></p><p>I am not entirely sure why this works, but the cookie gets saved if a post is made to the iFrame. This solution relies of jQuery but can be adapted to any other.</p><p>Here is the code that goes on the remote domain:</p><pre><code>&lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script&gt;
var isSafari = (/Safari/.test(navigator.userAgent));
var firstTimeSession = 0;

function submitSessionForm() {
&#9;if (firstTimeSession == 0) {
&#9;&#9;firstTimeSession = 1;
&#9;&#9;$("#sessionform").submit();
&#9;&#9;setTimeout(processApplication(),2000);
  &#9;}&#9;
}

if (isSafari) {
&#9;$("body").append('&lt;iframe id="sessionframe" name="sessionframe" onload="submitSessionForm()" src="http://www.yourdomain.com/blank.php" style="display:none;"&gt;&lt;/iframe&gt;&lt;form id="sessionform" enctype="application/x-www-form-urlencoded" action="http://www.yourdomain.com/startsession.php" target="sessionframe" action="post"&gt;&lt;/form&gt;');
} else {
&#9;processApplication();
}

function processApplication() {
&#9;alert('Session has been set. Now you can start your application!');
}
&lt;/script&gt;
</code></pre><p>The contents for startsession.php would be as simple as:</p><pre><code>&lt;?php
header('P3P: CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"'); 
session_start();
</code></pre><p>To make sure that your site is compatible with IE6/7, always output the following header:</p><pre><code>&lt;?php
header('P3P: CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"'); 
</code></pre><p>Unfortunately, there is no way to find out when the browser has completed submitting the form. Thus, I have placed a delay of 2 seconds. This form needs to be submitted only once per user session. Then you can set your session data in PHP and it will be picked up by your remote domain. You can adapt the same example if you want to set cookies instead of starting a PHP session.</p><p><strong>Comments/Suggestions?</strong><br>Do let me know your suggestions on how we can improve this code.</p><p><strong>Spread The Word</strong><br>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>]]></content:encoded></item></channel></rss>