1const e=!1,s=` 2<div class="page-node"> 3 <div class="t-editor__page-title"> 4 <h1 id="python" data-scalar-type="heading" data-level="1" class="heading scroll-pad interaction-observer-heading t-editor__heading">Python</h1> 5 </div> 6 <p class="t-editor__paragraph" class="t-editor__paragraph">Koalaâs Minimal python image built on 0-deb through distroless design significantly reduces CVEs in the final image. The python image is production-ready image optimized for python workloads.</p> 7 <p class="t-editor__paragraph" class="t-editor__paragraph">Koalaâs python image is available at multiple registries, including <a target="_blank" rel="noopener noreferrer nofollow" class="t-editor__anchor" href="https://hub.docker.com/u/koalalab" class="t-editor__anchor">dockerhub regsitry</a> & <a target="_blank" rel="noopener noreferrer nofollow" class="t-editor__anchor" href="https://gallery.ecr.aws/b7m2q1o2" class="t-editor__anchor">koalaâs public ecr</a> .</p> 8 <h3 id="koalas-python-demo-flask-app" class="scroll-pad interaction-observer-heading t-editor__heading" data-level="3" data-scalar-type="heading"> 9 Koalaâs Python demo flask app 10 <button class="notify-container" aria-label="Copy link to heading"><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256" aria-hidden="true" class="icon notify"> 11 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 12 </svg> 13<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256" aria-hidden="true" class="icon"> 14 <path d="M224,88H175.4l8.47-46.57a8,8,0,0,0-15.74-2.86l-9,49.43H111.4l8.47-46.57a8,8,0,0,0-15.74-2.86L95.14,88H48a8,8,0,0,0,0,16H92.23L83.5,152H32a8,8,0,0,0,0,16H80.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,80,224a8,8,0,0,0,7.86-6.57l9-49.43H144.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,144,224a8,8,0,0,0,7.86-6.57l9-49.43H208a8,8,0,0,0,0-16H163.77l8.73-48H224a8,8,0,0,0,0-16Zm-76.5,64H99.77l8.73-48h47.73Z"></path> 15 </svg></button> 16 </h3> 17 <p class="t-editor__paragraph" class="t-editor__paragraph">Use the following command to download the koala python image locally:</p> 18 <div id="p6N2a0ZW2K1IvQBWd3HRJ" class="t-editor__code-container" data-lang="shell" style="position: relative;"> 19 <pre style=" 20 --line-digits: 2; 21 --line-number-start: -1; 22 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-shell" language="shell"><span class="t-code__line" data-line-number="0">docker pull koalalab/python</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">shell 23<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 24 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 25 </svg> 26<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 27 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 28 </svg></button> 29 </div> 30 <p class="t-editor__paragraph" class="t-editor__paragraph">This guide helps in setting a sample flask demo for building a full application on top of koalaâs minimal python image.</p> 31 <p class="t-editor__paragraph" class="t-editor__paragraph">Start by making a Koala Demo folder</p> 32 <div id="pGI4cI1ahC80UjacH6s77" class="t-editor__code-container" data-lang="shell" style="position: relative;"> 33 <pre style=" 34 --line-digits: 2; 35 --line-number-start: -1; 36 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-shell" language="shell"><span class="t-code__line" data-line-number="0">mkdir Koala-Demo</span> 37<span class="t-code__line" data-line-number="1">cd Koala-Demo</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">shell 38<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 39 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 40 </svg> 41<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 42 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 43 </svg></button> 44 </div> 45 <p class="t-editor__paragraph" class="t-editor__paragraph">Save the following file as demo.yml</p> 46 <div id="UGFWn18DoSYYLvj0r1K3g" class="t-editor__code-container" data-lang="yaml" style="position: relative;"> 47 <pre style=" 48 --line-digits: 2;
49 --line-number-start: -1; 50 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-yaml" language="yaml"><span class="t-code__line" data-line-number="0"><span class="hljs-attr">services:</span></span> 51<span class="t-code__line" data-line-number="1"> <span class="hljs-attr">web:</span></span> 52<span class="t-code__line" data-line-number="2"> <span class="hljs-attr">image:</span> <span class="hljs-string">public.ecr.aws/b7m2q1o2/flask-demo</span></span> 53<span class="t-code__line" data-line-number="3"> <span class="hljs-attr">ports:</span></span> 54<span class="t-code__line" data-line-number="4"> <span class="hljs-bullet">-</span> <span class="hljs-string">"0.0.0.0:5001:5001"</span></span> 55<span class="t-code__line" data-line-number="5"> <span class="hljs-attr">environment:</span></span> 56<span class="t-code__line" data-line-number="6"> <span class="hljs-bullet">-</span> <span class="hljs-string">DB_HOST=db</span></span> 57<span class="t-code__line" data-line-number="7"> <span class="hljs-attr">depends_on:</span></span> 58<span class="t-code__line" data-line-number="8"> <span class="hljs-bullet">-</span> <span class="hljs-string">db</span></span> 59<span class="t-code__line" data-line-number="9"> <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span></span> 60<span class="t-code__line" data-line-number="10"></span> 61<span class="t-code__line" data-line-number="11"></span> 62<span class="t-code__line" data-line-number="12"> <span class="hljs-attr">db:</span></span> 63<span class="t-code__line" data-line-number="13"> <span class="hljs-attr">image:</span> <span class="hljs-string">postgres:17</span></span> 64<span class="t-code__line" data-line-number="14"> <span class="hljs-attr">environment:</span></span> 65<span class="t-code__line" data-line-number="15"> <span class="hljs-bullet">-</span> <span class="hljs-string">POSTGRES_DB=flask_app</span></span> 66<span class="t-code__line" data-line-number="16"> <span class="hljs-bullet">-</span> <span class="hljs-string">POSTGRES_USER=flask_user</span></span> 67<span class="t-code__line" data-line-number="17"> <span class="hljs-bullet">-</span> <span class="hljs-string">POSTGRES_PASSWORD=your_actual_password</span></span> 68<span class="t-code__line" data-line-number="18"> <span class="hljs-attr">ports:</span></span> 69<span class="t-code__line" data-line-number="19"> <span class="hljs-bullet">-</span> <span class="hljs-string">"5432:5432"</span></span> 70<span class="t-code__line" data-line-number="20"> <span class="hljs-attr">volumes:</span></span> 71<span class="t-code__line" data-line-number="21"> <span class="hljs-bullet">-</span> <span class="hljs-string">pg_data:/var/lib/postgresql/data</span></span> 72<span class="t-code__line" data-line-number="22"> <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span></span> 73<span class="t-code__line" data-line-number="23"></span> 74<span class="t-code__line" data-line-number="24"></span> 75<span class="t-code__line" data-line-number="25"><span class="hljs-attr">volumes:</span></span> 76 <span class="hljs-attr">pg_data:</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">yaml 77<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 78 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 79 </svg> 80<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 81 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 82 </svg></button> 83 </div> 84 <p class="t-editor__paragraph" class="t-editor__paragraph">In your terminal, run the following commands</p> 85 <div id="F4KXpkJSy81IxoMHjmmBm" class="t-editor__code-container" data-lang="shell" style="position: relative;"> 86 <pre style=" 87 --line-digits: 2;
88 --line-number-start: -1; 89 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-shell" language="shell"><span class="t-code__line" data-line-number="0">brew install docker-compose</span> 90<span class="t-code__line" data-line-number="1">docker-compose -f demo.yml up -d</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">shell 91<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 92 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 93 </svg> 94<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 95 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 96 </svg></button> 97 </div> 98 <p class="t-editor__paragraph" class="t-editor__paragraph">After this you should be able to access flask demo app at <a target="_blank" rel="noopener noreferrer nofollow" class="t-editor__anchor" href="http://localhost:5001" class="t-editor__anchor">http://localhost:5001</a></p> 99 <p class="t-editor__paragraph" class="t-editor__paragraph">& would look something like this:</p> 100 <figure class="image-upload-container relative custom-image-size-custom t-editor__image"><a rel="noopener noreferrer" target="_blank"><img alt="" class="highlight-active" data-dark-mode-src="" data-scalar-type="image" data-scalar-name="scalar-image" data-src="https://api.scalar.com/cdn/images/mtqaQ-q233kXGmnMGs2Cw/JvIJfRLWq2CmKv0Zl4yHx.png" height="437" src="https://api.scalar.com/cdn/images/mtqaQ-q233kXGmnMGs2Cw/JvIJfRLWq2CmKv0Zl4yHx.png" width="760"></a> 101 <figcaption>Flask demo app built on koala's minimal python image</figcaption> 102 </figure> 103 <hr class="t-editor__line-break" class="t-editor__line-break"> 104 <hr class="t-editor__line-break" class="t-editor__line-break"> 105 <h3 id="editing-running-the-flask-demo" class="scroll-pad interaction-observer-heading t-editor__heading" data-level="3" data-scalar-type="heading"> 106 Editing & running the flask demo 107 <button class="notify-container" aria-label="Copy link to heading"><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256" aria-hidden="true" class="icon notify"> 108 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 109 </svg> 110<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256" aria-hidden="true" class="icon"> 111 <path d="M224,88H175.4l8.47-46.57a8,8,0,0,0-15.74-2.86l-9,49.43H111.4l8.47-46.57a8,8,0,0,0-15.74-2.86L95.14,88H48a8,8,0,0,0,0,16H92.23L83.5,152H32a8,8,0,0,0,0,16H80.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,80,224a8,8,0,0,0,7.86-6.57l9-49.43H144.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,144,224a8,8,0,0,0,7.86-6.57l9-49.43H208a8,8,0,0,0,0-16H163.77l8.73-48H224a8,8,0,0,0,0-16Zm-76.5,64H99.77l8.73-48h47.73Z"></path> 112 </svg></button> 113 </h3> 114 <p class="t-editor__paragraph" class="t-editor__paragraph">The above was a sample demo created by KoalaLab.</p> 115 <p class="t-editor__paragraph" class="t-editor__paragraph">If you want to build the image locally & run it yourself, here are the steps:</p> 116 <ol class="t-editor__list" class="t-editor__list t-editor__paragraph"> 117 <li> 118 <p class="t-editor__paragraph" class="t-editor__paragraph">Create a local dockerfile, save this as flask.dockerfile</p> 119 </li> 120 </ol> 121 <div id="CEwOGtvgwFoQ3lMgQsI4j" class="t-editor__code-container" data-lang="makefile" style="position: relative;"> 122 <pre style=" 123 --line-digits: 2; 124 --line-number-start: -1; 125 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-makefile" language="makefile"><span class="t-code__line" data-line-number="0">ARG VENV_NAME=koala</span> 126<span class="t-code__line" data-line-number="1"></span> 127<span class="t-code__line" data-line-number="2"><span class="hljs-comment"># Multi-arch build using koalalab/python images</span></span> 128<span class="t-code__line" data-line-number="3">FROM koalalab/python:latest-builder AS builder</span> 129<span class="t-code__line" data-line-number="4"></span> 130<span class="t-code__line" data-line-number="5">ARG VENV_NAME</span> 131<span class="t-code__line" data-line-number="6"></span> 132<span class="t-code__line" data-line-number="7">RUN python3 -m venv $VENV_NAME</span> 133<span class="t-code__line" data-line-number="8"></span> 134<span class="t-code__line" data-line-number="9">ENV PATH=<span class="hljs-string">
134"$WORKDIR/$VENV_NAME/bin:$PATH"</span></span> 135<span class="t-code__line" data-line-number="10"></span> 136<span class="t-code__line" data-line-number="11">COPY flask/requirements.txt flask/requirements.txt</span> 137<span class="t-code__line" data-line-number="12"></span> 138<span class="t-code__line" data-line-number="13">RUN pip install --no-cache-dir -r flask/requirements.txt</span> 139<span class="t-code__line" data-line-number="14"></span> 140<span class="t-code__line" data-line-number="15">COPY flask flask</span> 141<span class="t-code__line" data-line-number="16"></span> 142<span class="t-code__line" data-line-number="17">FROM koalalab/python</span> 143<span class="t-code__line" data-line-number="18"></span> 144<span class="t-code__line" data-line-number="19">ARG VENV_NAME</span> 145<span class="t-code__line" data-line-number="20"></span> 146<span class="t-code__line" data-line-number="21">COPY --from=builder $WORKDIR $WORKDIR</span> 147<span class="t-code__line" data-line-number="22"></span> 148<span class="t-code__line" data-line-number="23">ENV PATH=<span class="hljs-string">
148"$WORKDIR/$VENV_NAME/bin:$PATH"</span></span> 149<span class="t-code__line" data-line-number="24"></span> 150<span class="t-code__line" data-line-number="25"><span class="hljs-comment"># Expose Flask port</span></span> 151<span class="t-code__line" data-line-number="26">EXPOSE 5001</span> 152<span class="t-code__line" data-line-number="27"></span> 153<span class="t-code__line" data-line-number="28">CMD [<span class="hljs-string">"python3"</span>, <span class="hljs-string">"flask/app.py"</span>]</span> 154</code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">makefile 155<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 156 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 157 </svg> 158<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 159 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 160 </svg></button> 161 </div> 162 <p class="t-editor__paragraph" class="t-editor__paragraph">2. Create a sub-folder called flask</p> 163 <div id="LRnadhNZCE8OYlHCzBgDL" class="t-editor__code-container" data-lang="shell" style="position: relative;"> 164 <pre style=" 165 --line-digits: 2; 166 --line-number-start: -1; 167 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-shell" language="shell"><span class="t-code__line" data-line-number="0">mkdir flask</span> 168<span class="t-code__line" data-line-number="1">cd flask</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">shell 169<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 170 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 171 </svg> 172<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 173 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 174 </svg></button> 175 </div> 176 <p class="t-editor__paragraph" class="t-editor__paragraph">3. Create two files in the flask folder</p> 177 <ul class="t-editor__list" class="t-editor__list t-editor__paragraph"> 178 <li> 179 <p class="t-editor__paragraph" class="t-editor__paragraph">app.py</p> 180 </li> 181 </ul> 182 <ul class="t-editor__list" class="t-editor__list t-editor__paragraph"> 183 <li> 184 <p class="t-editor__paragraph" class="t-editor__paragraph">requirements.txt</p> 185 </li> 186 </ul> 187 <p class="t-editor__paragraph" class="t-editor__paragraph">4. Edit the python file app.py include the following code</p> 188 <div id="hbVKYtITkmzGPk4KzvzoM" class="t-editor__code-container" data-lang="python" style="position: relative;"> 189 <pre style=" 190 --line-digits: 3; 191 --line-number-start: -1; 192 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-python" language="python"><span class="t-code__line" data-line-number="0"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, request, jsonify, render_template_string</span> 193<span class="t-code__line" data-line-number="1"><span class="hljs-keyword">import</span> psycopg2</span> 194<span class="t-code__line" data-line-number="2"><span class="hljs-keyword">from</span> psycopg2.extras <span class="hljs-keyword">import</span> RealDictCursor</span> 195<span class="t-code__line" data-line-number="3"><span class="hljs-keyword">import</span> os</span> 196<span class="t-code__line" data-line-number="4"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">
196import</span> datetime</span> 197<span class="t-code__line" data-line-number="5"><span class="hljs-keyword">import</span> logging</span> 198<span class="t-code__line" data-line-number="6"></span> 199<span class="t-code__line" data-line-number="7"><span class="hljs-comment"># Configure logging</span></span> 200<span class="t-code__line" data-line-number="8">logging.basicConfig(level=logging.INFO)</span> 201<span class="t-code__line" data-line-number="9">logger = logging.getLogger(__name__)</span> 202<span class="t-code__line" data-line-number="10"></span> 203<span class="t-code__line" data-line-number="11">app = Flask(__name__)</span> 204<span class="t-code__line" data-line-number="12"></span> 205<span class="t-code__line" data-line-number="13"><span class="hljs-comment"># Database configuration</span></span> 206<span class="t-code__line" data-line-number="14">DATABASE_CONFIG = {</span> 207<span class="t-code__line" data-line-number="15"> <span class="hljs-string">'host'</span>: os.getenv(<span class="hljs-string">'DB_HOST'</span>, <span class="hljs-string">'localhost'</span>),</span> 208<span class="t-code__line" data-line-number="16"> <span class="hljs-string">'database'</span>: os.getenv(<span class="hljs-string">'DB_NAME'</span>, <span class="hljs-string">'flask_app'</span>),</span> 209<span class="t-code__line" data-line-number="17"> <span class="hljs-string">'user'</span>: os.getenv(<span class="hljs-string">'DB_USER'</span>, <span class="hljs-string">'flask_user'</span>),</span> 210<span class="t-code__line" data-line-number="18"> <span class="hljs-string">'password'</span>: os.getenv(<span class="hljs-string">'PASSWORD'</span>, <span class="hljs-string">'your_actual_password'</span>),</span> 211<span class="t-code__line" data-line-number="19"> <span class="hljs-string">'port'</span>: os.getenv(<span class="hljs-string">'DB_PORT'</span>, <span class="hljs-string">'5432'</span>)</span> 212<span class="t-code__line" data-line-number="20">}</span> 213<span class="t-code__line" data-line-number="21"></span> 214<span class="t-code__line" data-line-number="22"><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_db_connection</span>():</span> 215<span class="t-code__line" data-line-number="23"> <span class="hljs-string">"""Create and return a database connection"""</span></span> 216<span class="t-code__line" data-line-number="24"> <span class="hljs-keyword">try</span>:</span> 217<span class="t-code__line" data-line-number="25"> conn = psycopg2.connect(**DATABASE_CONFIG)</span> 218<span class="t-code__line" data-line-number="26"> <span class="hljs-keyword">return</span> conn</span> 219<span class="t-code__line" data-line-number="27"> <span class="hljs-keyword">except</span> psycopg2.Error <span class="hljs-keyword">as</span> e:</span> 220<span class="t-code__line" data-line-number="28"> logger.error(f"Database connection error: <span class="hljs-subst">{e}</span>")</span> 221<span class="t-code__line" data-line-number="29"> <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span></span> 222<span class="t-code__line" data-line-number="30"></span> 223<span class="t-code__line" data-line-number="31"><span class="hljs-keyword">def</span> <span class="hljs-title function_">init_database</span>():</span> 224<span class="t-code__line" data-line-number="32"> <span class="hljs-string">"""Initialize the database with required tables"""</span></span> 225<span class="t-code__line" data-line-number="33"> conn = get_db_connection()</span> 226<span class="t-code__line" data-line-number="34"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> conn:</span> 227<span class="t-code__line" data-line-number="35"> logger.error(<span class="hljs-string">"Could not connect to database for initialization"</span>)</span> 228<span class="t-code__line" data-line-number="36"> <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span></span> 229<span class="t-code__line" data-line-number="37"></span> 230<span class="t-code__line" data-line-number="38"> <span class="hljs-keyword">try</span>:</span> 231<span class="t-code__line" data-line-number="39">
231 <span class="hljs-keyword">with</span> conn.cursor() <span class="hljs-keyword">as</span> cursor:</span> 232<span class="t-code__line" data-line-number="40"> <span class="hljs-comment"># Create users table</span></span> 233<span class="t-code__line" data-line-number="41"> cursor.execute(<span class="hljs-string">"""</span></span> 234<span class="t-code__line" data-line-number="42"><span class="hljs-string"> CREATE TABLE IF NOT EXISTS users (</span></span> 235<span class="t-code__line" data-line-number="43"><span class="hljs-string"> id SERIAL PRIMARY KEY,</span></span> 236<span class="t-code__line" data-line-number="44"><span class="hljs-string"> name VARCHAR(100) NOT NULL,</span></span> 237<span class="t-code__line" data-line-number="45"><span class="hljs-string"> email VARCHAR(100) UNIQUE NOT NULL,</span></span> 238<span class="t-code__line" data-line-number="46"><span class="hljs-string"> created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP</span></span> 239<span class="t-code__line" data-line-number="47"><span class="hljs-string"> )</span></span> 240<span class="t-code__line" data-line-number="48"><span class="hljs-string"> """</span>)</span> 241<span class="t-code__line" data-line-number="49"> conn.commit()</span> 242<span class="t-code__line" data-line-number="50"> logger.info(<span class="hljs-string">"Database initialized successfully"</span>)</span> 243<span class="t-code__line" data-line-number="51"> <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span></span> 244<span class="t-code__line" data-line-number="52"> <span class="hljs-keyword">except</span> psycopg2.Error <span class="hljs-keyword">as</span> e:</span> 245<span class="t-code__line" data-line-number="53"> logger.error(f"Database initialization error: <span class="hljs-subst">{e}</span>")</span> 246<span class="t-code__line" data-line-number="54"> conn.rollback()</span> 247<span class="t-code__line" data-line-number="55"> <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span></span> 248<span class="t-code__line" data-line-number="56"> <span class="hljs-keyword">finally</span>:</span> 249<span class="t-code__line" data-line-number="57"> conn.close()</span> 250<span class="t-code__line" data-line-number="58"></span> 251<span class="t-code__line" data-line-number="59"><span class="hljs-comment"># HTML template for the web interface</span></span> 252<span class="t-code__line" data-line-number="60">HTML_TEMPLATE = <span class="hljs-string">"""</span></span> 253<span class="t-code__line" data-line-number="61"><span class="hljs-string"><!DOCTYPE html></span></span> 254<span class="t-code__line" data-line-number="62"><span class="hljs-string"><html></span></span> 255<span class="t-code__line" data-line-number="63"><span class="hljs-string"><head></span></span> 256<span class="t-code__line" data-line-number="64"><span class="hljs-string"> <title>Flask PostgreSQL App</title></span></span> 257<span class="t-code__line" data-line-number="65"><span class="hljs-string"> <style></span></span> 258<span class="t-code__line" data-line-number="66"><span class="hljs-string"> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }</span></span> 259<span class="t-code__line" data-line-number="67"><span class="hljs-string"> .form-group { margin: 10px 0; }</span></span> 260<span class="t-code__line" data-line-number="68"><span class="hljs-string"> input, button { padding: 8px; margin: 5px; }</span></span> 261<span class="t-code__line" data-line-number="69"><span class="hljs-string"> button { background: #007bff; color: white; border: none; cursor: pointer; }</span></span> 262<span class="t-code__line" data-line-number="70"><span class="hljs-string"> button:hover { background: #0056b3; }</span></span> 263<span class="t-code__line" data-line-number="71"><span class="hljs-string"> .user { background: #f8f9fa; padding: 10px; margin: 5px 0; border-radius: 5px; }</span></span> 264<span class="t-code__line" data-line-number="72"><span class="hljs-string"> .error { color: red; }</span></span> 265<span class="t-code__line" data-line-number="73"><span class="hljs-string"> .success { color: green; }</span></span> 266<span class="t-code__line" data-line-number="74"><span class="hljs-string"> </style></span></span> 267<span class="t-code__line" data-line-number="75"><span class="hljs-string"></head></span></span> 268<span class="t-code__line" data-line-number="76"><span class="hljs-string"><body></span></span> 269<span class="t-code__line" data-line-number="77"><span class="hljs-string"> <h1>Flask PostgreSQL User Management</h1></span></span> 270<span class="t-code__line" data-line-number="78"><span class="hljs-string"></span></span> 271<span class="t-code__line" data-line-number="79"><span class="hljs-string"> <div id="message"></div></span></span> 272<span class="t-code__line" data-line-number="80"><span class="hljs-string"></span></span> 273<span class="t-code__line" data-line-number="81"><span class="hljs-string"> <h2>Add New User</h2></span></span> 274<span class="t-code__line" data-line-number="82"><span class="hljs-string"> <form id="userForm"></span></span> 275<span class="t-code__line" data-line-number="83"><span class="hljs-string"> <div class="form-group"></span></span> 276<span class="t-code__line" data-line-number="84"><span class="hljs-string"> <input type="text" id="name" placeholder="Name" required></span></span> 277<span class="t-code__line" data-line-number="85"><span class="hljs-string"> </div></span></span> 278<span class="t-code__line" data-line-number="86"><span class="hljs-string"> <div class="form-group"></span></span> 279<span class="t-code__line" data-line-number="87"><span class="hljs-string"> <input type="email" id="email" placeholder="Email" required></span></span> 280<span class="t-code__line" data-line-number="88"><span class="hljs-string"> </div></span></span> 281<span class="t-code__line" data-line-number="89"><span class="hljs-string"> <button type="submit">Add User</button></span></span> 282<span class="t-code__line" data-line-number="90"><span class="hljs-string"> </form></span></span>
283<span class="t-code__line" data-line-number="91"><span class="hljs-string"></span></span> 284<span class="t-code__line" data-line-number="92"><span class="hljs-string"> <h2>Users</h2></span></span> 285<span class="t-code__line" data-line-number="93"><span class="hljs-string"> <div id="users"></div></span></span> 286<span class="t-code__line" data-line-number="94"><span class="hljs-string"> <button onclick="loadUsers()">Refresh Users</button></span></span> 287<span class="t-code__line" data-line-number="95"><span class="hljs-string"></span></span> 288<span class="t-code__line" data-line-number="96"><span class="hljs-string"> <script></span></span> 289<span class="t-code__line" data-line-number="97"><span class="hljs-string"> function showMessage(text, isError = false) {</span></span> 290<span class="t-code__line" data-line-number="98"><span class="hljs-string"> const messageDiv = document.getElementById('message');</span></span> 291<span class="t-code__line" data-line-number="99"><span class="hljs-string"> messageDiv.textContent = text;</span></span> 292<span class="t-code__line" data-line-number="100"><span class="hljs-string"> messageDiv.className = isError ? 'error' : 'success';</span></span> 293<span class="t-code__line" data-line-number="101"><span class="hljs-string"> setTimeout(() => messageDiv.textContent = '', 3000);</span></span> 294<span class="t-code__line" data-line-number="102"><span class="hljs-string"> }</span></span> 295<span class="t-code__line" data-line-number="103"><span class="hljs-string"></span></span> 296<span class="t-code__line" data-line-number="104"><span class="hljs-string"> document.getElementById('userForm').addEventListener('submit', async (e) => {</span></span> 297<span class="t-code__line" data-line-number="105"><span class="hljs-string"> e.preventDefault();</span></span> 298<span class="t-code__line" data-line-number="106"><span class="hljs-string"> const name = document.getElementById('name').value;</span></span> 299<span class="t-code__line" data-line-number="107"><span class="hljs-string"> const email = document.getElementById('email').value;</span></span> 300<span class="t-code__line" data-line-number="108"><span class="hljs-string"></span></span> 301<span class="t-code__line" data-line-number="109"><span class="hljs-string"> try {</span></span> 302<span class="t-code__line" data-line-number="110"><span class="hljs-string"> const response = await fetch('/api/users', {</span></span> 303<span class="t-code__line" data-line-number="111"><span class="hljs-string"> method: 'POST',</span></span> 304<span class="t-code__line" data-line-number="112"><span class="hljs-string"> headers: { 'Content-Type': 'application/json' },</span></span> 305<span class="t-code__line" data-line-number="113"><span class="hljs-string"> body: JSON.stringify({ name, email })</span></span> 306<span class="t-code__line" data-line-number="114"><span class="hljs-string"> });</span></span> 307<span class="t-code__line" data-line-number="115"><span class="hljs-string"></span></span> 308<span class="t-code__line" data-line-number="116"><span class="hljs-string"> const data = await response.json();</span></span> 309<span class="t-code__line" data-line-number="117"><span class="hljs-string"> if (response.ok) {</span></span> 310<span class="t-code__line" data-line-number="118"><span class="hljs-string"> showMessage('User added successfully!');</span></span> 311<span class="t-code__line" data-line-number="119"><span class="hljs-string"> document.getElementById('userForm').reset();</span></span> 312<span class="t-code__line" data-line-number="120"><span class="hljs-string"> loadUsers();</span></span> 313<span class="t-code__line" data-line-number="121"><span class="hljs-string"> } else {</span></span> 314<span class="t-code__line" data-line-number="122"><span class="hljs-string"> showMessage(data.error || 'Error adding user', true);</span></span> 315<span class="t-code__line" data-line-number="123"><span class="hljs-string"> }</span></span> 316<span class="t-code__line" data-line-number="124"><span class="hljs-string"> } catch (error) {</span></span> 317<span class="t-code__line" data-line-number="125"><span class="hljs-string"> showMessage('Network error', true);</span></span> 318<span class="t-code__line" data-line-number="126"><span class="hljs-string"> }</span></span> 319<span class="t-code__line" data-line-number="127"><span class="hljs-string"> });</span></span> 320<span class="t-code__line" data-line-number="128"><span class="hljs-string"></span></span> 321<span class="t-code__line" data-line-number="129"><span class="hljs-string"> async function loadUsers() {</span></span> 322<span class="t-code__line" data-line-number="130"><span class="hljs-string"> try {</span></span> 323<span class="t-code__line" data-line-number="131"><span class="hljs-string"> const response = await fetch('/api/users');</span></span> 324<span class="t-code__line" data-line-number="132"><span class="hljs-string"> const users = await response.json();</span></span> 325<span class="t-code__line" data-line-number="133"><span class="hljs-string"></span></span> 326<span class="t-code__line" data-line-number="134"><span class="hljs-string"> const usersDiv = document.getElementById('users');</span></span> 327<span class="t-code__line" data-line-number="135"><span class="hljs-string"> if (users.length === 0) {</span></span> 328<span class="t-code__line" data-line-number="136"><span class="hljs-string"> usersDiv.innerHTML = '<p>No users found</p>';</span></span> 329<span class="t-code__line" data-line-number="137"><span class="hljs-string"> } else {</span></span> 330<span class="t-code__line" data-line-number="138"><span class="hljs-string"> usersDiv.innerHTML = users.map(user =></span></span> 331<span class="t-code__line" data-line-number="139"><span class="hljs-string"> \`<div class="user"></span></span> 332<span class="t-code__line" data-line-number="140"><span class="hljs-string"> <strong>\${user.name}</strong> - \${user.email}</span></span> 333<span class="t-code__line" data-line-number="141"><span class="hljs-string"> <br><small>
333Created: \${new Date(user.created_at).toLocaleString()}</small></span></span> 334<span class="t-code__line" data-line-number="142"><span class="hljs-string"> <button onclick="deleteUser(\${user.id})" style="float: right; background: #dc3545;">Delete</button></span></span> 335<span class="t-code__line" data-line-number="143"><span class="hljs-string"> </div>\`</span></span> 336<span class="t-code__line" data-line-number="144"><span class="hljs-string"> ).join('');</span></span> 337<span class="t-code__line" data-line-number="145"><span class="hljs-string"> }</span></span> 338<span class="t-code__line" data-line-number="146"><span class="hljs-string"> } catch (error) {</span></span> 339<span class="t-code__line" data-line-number="147"><span class="hljs-string"> showMessage('Error loading users', true);</span></span> 340<span class="t-code__line" data-line-number="148"><span class="hljs-string"> }</span></span> 341<span class="t-code__line" data-line-number="149"><span class="hljs-string"> }</span></span> 342<span class="t-code__line" data-line-number="150"><span class="hljs-string"></span></span> 343<span class="t-code__line" data-line-number="151"><span class="hljs-string"> async function deleteUser(userId) {</span></span> 344<span class="t-code__line" data-line-number="152"><span class="hljs-string"> if (!confirm('Are you sure you want to delete this user?')) return;</span></span> 345<span class="t-code__line" data-line-number="153"><span class="hljs-string"></span></span> 346<span class="t-code__line" data-line-number="154"><span class="hljs-string"> try {</span></span> 347<span class="t-code__line" data-line-number="155"><span class="hljs-string"> const response = await fetch(\`/api/users/\${userId}\`, { method: 'DELETE' });</span></span> 348<span class="t-code__line" data-line-number="156"><span class="hljs-string"> const data = await response.json();</span></span> 349<span class="t-code__line" data-line-number="157"><span class="hljs-string"></span></span> 350<span class="t-code__line" data-line-number="158"><span class="hljs-string"> if (response.ok) {</span></span> 351<span class="t-code__line" data-line-number="159"><span class="hljs-string"> showMessage('User deleted successfully!');</span></span> 352<span class="t-code__line" data-line-number="160"><span class="hljs-string"> loadUsers();</span></span> 353<span class="t-code__line" data-line-number="161"><span class="hljs-string"> } else {</span></span> 354<span class="t-code__line" data-line-number="162"><span class="hljs-string"> showMessage(data.error || 'Error deleting user', true);</span></span> 355<span class="t-code__line" data-line-number="163"><span class="hljs-string"> }</span></span> 356<span class="t-code__line" data-line-number="164"><span class="hljs-string"> } catch (error) {</span></span> 357<span class="t-code__line" data-line-number="165"><span class="hljs-string"> showMessage('Network error', true);</span></span> 358<span class="t-code__line" data-line-number="166"><span class="hljs-string"> }</span></span> 359<span class="t-code__line" data-line-number="167"><span class="hljs-string"> }</span></span> 360<span class="t-code__line" data-line-number="168"><span class="hljs-string"></span></span> 361<span class="t-code__line" data-line-number="169"><span class="hljs-string"> // Load users on page load</span></span> 362<span class="t-code__line" data-line-number="170"><span class="hljs-string"> loadUsers();</span></span> 363<span class="t-code__line" data-line-number="171"><span class="hljs-string"> </script></span></span> 364<span class="t-code__line" data-line-number="172"><span class="hljs-string"></body></span></span> 365<span class="t-code__line" data-line-number="173"><span class="hljs-string"></html></span></span> 366<span class="t-code__line" data-line-number="174"><span class="hljs-string">"""</span></span> 367<span class="t-code__line" data-line-number="175"></span> 368<span class="t-code__line" data-line-number="176">@app.route(<span class="hljs-string">'/'</span>)</span> 369<span class="t-code__line" data-line-number="177"><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():</span> 370<span class="t-code__line" data-line-number="178"> <span class="hljs-string">"""Serve the main page"""</span></span> 371<span class="t-code__line" data-line-number="179"> <span class="hljs-keyword">return</span>
371 render_template_string(HTML_TEMPLATE)</span> 372<span class="t-code__line" data-line-number="180"></span> 373<span class="t-code__line" data-line-number="181">@app.route(<span class="hljs-string">'/health'</span>)</span> 374<span class="t-code__line" data-line-number="182"><span class="hljs-keyword">def</span> <span class="hljs-title function_">health_check</span>():</span> 375<span class="t-code__line" data-line-number="183"> <span class="hljs-string">"""Health check endpoint"""</span></span> 376<span class="t-code__line" data-line-number="184"> conn = get_db_connection()</span> 377<span class="t-code__line" data-line-number="185"> <span class="hljs-keyword">if</span> conn:</span> 378<span class="t-code__line" data-line-number="186"> conn.close()</span> 379<span class="t-code__line" data-line-number="187"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'status'</span>: <span class="hljs-string">'healthy'</span>, <span class="hljs-string">'database'</span>: <span class="hljs-string">'connected'</span>})</span> 380<span class="t-code__line" data-line-number="188"> <span class="hljs-keyword">else</span>:</span> 381<span class="t-code__line" data-line-number="189"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'status'</span>: <span class="hljs-string">'unhealthy'</span>, <span class="hljs-string">'database'</span>: <span class="hljs-string">'disconnected'</span>}), <span class="hljs-number">500</span></span> 382<span class="t-code__line" data-line-number="190"></span> 383<span class="t-code__line" data-line-number="191">@app.route(<span class="hljs-string">'/api/users'</span>, methods=[<span class="hljs-string">'GET'</span>])</span> 384<span class="t-code__line" data-line-number="192"><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_users</span>():</span> 385<span class="t-code__line" data-line-number="193"> <span class="hljs-string">"""Get all users"""</span></span> 386<span class="t-code__line" data-line-number="194"> conn = get_db_connection()</span> 387<span class="t-code__line" data-line-number="195"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> conn:</span> 388<span class="t-code__line" data-line-number="196"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Database connection failed'</span>}), <span class="hljs-number">500</span></span> 389<span class="t-code__line" data-line-number="197"></span> 390<span class="t-code__line" data-line-number="198"> <span class="hljs-keyword">try</span>:</span> 391<span class="t-code__line" data-line-number="199"> <span class="hljs-keyword">with</span> conn.cursor(cursor_factory=RealDictCursor) <span class="hljs-keyword">as</span> cursor:</span> 392<span class="t-code__line" data-line-number="200"> cursor.execute(<span class="hljs-string">"SELECT * FROM users ORDER BY created_at DESC"</span>)</span> 393<span class="t-code__line" data-line-number="201"> users = cursor.fetchall()</span> 394<span class="t-code__line" data-line-number="202"> <span class="hljs-keyword">return</span> jsonify([<span class="hljs-built_in">dict</span>(user) <span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users])</span> 395<span class="t-code__line" data-line-number="203"> <span class="hljs-keyword">except</span> psycopg2.Error <span class="hljs-keyword">as</span> e:</span> 396<span class="t-code__line" data-line-number="204"> logger.error(f"Error fetching users: <span class="hljs-subst">{e}</span>")</span> 397<span class="t-code__line" data-line-number="205"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Failed to fetch users'</span>}), <span class="hljs-number">500</span></span> 398<span class="t-code__line" data-line-number="206"> <span class="hljs-keyword">finally</span>:</span> 399<span class="t-code__line" data-line-number="207"> conn.close()</span> 400<span class="t-code__line" data-line-number="208"></span> 401<span class="t-code__line" data-line-number="209">@app.route(<span class="hljs-string">
401'/api/users'</span>, methods=[<span class="hljs-string">'POST'</span>])</span> 402<span class="t-code__line" data-line-number="210"><span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user</span>():</span> 403<span class="t-code__line" data-line-number="211"> <span class="hljs-string">"""Create a new user"""</span></span> 404<span class="t-code__line" data-line-number="212"> data = request.get_json()</span> 405<span class="t-code__line" data-line-number="213"></span> 406<span class="t-code__line" data-line-number="214"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> data <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> data.get(<span class="hljs-string">'name'</span>) <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> data.get(<span class="hljs-string">'email'</span>):</span> 407<span class="t-code__line" data-line-number="215"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Name and email are required'</span>}), <span class="hljs-number">400</span></span> 408<span class="t-code__line" data-line-number="216"></span> 409<span class="t-code__line" data-line-number="217"> conn = get_db_connection()</span> 410<span class="t-code__line" data-line-number="218"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> conn:</span> 411<span class="t-code__line" data-line-number="219"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Database connection failed'</span>}), <span class="hljs-number">500</span></span> 412<span class="t-code__line" data-line-number="220"></span> 413<span class="t-code__line" data-line-number="221"> <span class="hljs-keyword">try</span>:</span> 414<span class="t-code__line" data-line-number="222"> <span class="hljs-keyword">with</span> conn.cursor(cursor_factory=RealDictCursor) <span class="hljs-keyword">as</span> cursor:</span> 415<span class="t-code__line" data-line-number="223"> cursor.execute(</span> 416<span class="t-code__line" data-line-number="224"> <span class="hljs-string">"INSERT INTO users (name, email) VALUES (%s, %s) RETURNING *"</span>,</span> 417<span class="t-code__line" data-line-number="225"> (data[<span class="hljs-string">'name'</span>], data[<span class="hljs-string">'email'</span>])</span> 418<span class="t-code__line" data-line-number="226"> )</span> 419<span class="t-code__line" data-line-number="227"> user = cursor.fetchone()</span> 420<span class="t-code__line" data-line-number="228"> conn.commit()</span> 421<span class="t-code__line" data-line-number="229"> <span class="hljs-keyword">return</span> jsonify(<span class="hljs-built_in">dict</span>(user)), <span class="hljs-number">201</span></span> 422<span class="t-code__line" data-line-number="230"> <span class="hljs-keyword">except</span> psycopg2.IntegrityError:</span> 423<span class="t-code__line" data-line-number="231"> conn.rollback()</span> 424<span class="t-code__line" data-line-number="232"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Email already exists'</span>}), <span class="hljs-number">409</span></span> 425<span class="t-code__line" data-line-number="233"> <span class="hljs-keyword">except</span> psycopg2.Error <span class="hljs-keyword">as</span> e:</span> 426<span class="t-code__line" data-line-number="234"> logger.error(f"Error creating user: <span class="hljs-subst">{e}</span>")</span> 427<span class="t-code__line" data-line-number="235"> conn.rollback()</span> 428<span class="t-code__line" data-line-number="236"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Failed to create user'</span>}), <span class="hljs-number">500</span></span> 429<span class="t-code__line" data-line-number="237"> <span class="hljs-keyword">finally</span>:</span> 430<span class="t-code__line" data-line-number="238"> conn.close()</span> 431<span class="t-code__line" data-line-number="239"></span> 432<span class="t-code__line" data-line-number="240">@app.route(<span class="hljs-string">'/api/users/<int:user_id>'</span>, methods=[<span class="hljs-string">'GET'</span>])</span> 433<span class="t-code__line" data-line-number="241"><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id</span>):</span> 434<span class="t-code__line" data-line-number="242"> <span class="hljs-string">"""Get a specific user"""</span></span> 435<span class="t-code__line" data-line-number="243"> conn = get_db_connection()</span> 436<span class="t-code__line" data-line-number="244"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> conn:</span> 437<span class="t-code__line" data-line-number="245">
437 <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Database connection failed'</span>}), <span class="hljs-number">500</span></span> 438<span class="t-code__line" data-line-number="246"></span> 439<span class="t-code__line" data-line-number="247"> <span class="hljs-keyword">try</span>:</span> 440<span class="t-code__line" data-line-number="248"> <span class="hljs-keyword">with</span> conn.cursor(cursor_factory=RealDictCursor) <span class="hljs-keyword">as</span> cursor:</span> 441<span class="t-code__line" data-line-number="249"> cursor.execute(<span class="hljs-string">"SELECT * FROM users WHERE id = %s"</span>, (user_id,))</span> 442<span class="t-code__line" data-line-number="250"> user = cursor.fetchone()</span> 443<span class="t-code__line" data-line-number="251"></span> 444<span class="t-code__line" data-line-number="252"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> user:</span> 445<span class="t-code__line" data-line-number="253"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'User not found'</span>}), <span class="hljs-number">404</span></span> 446<span class="t-code__line" data-line-number="254"></span> 447<span class="t-code__line" data-line-number="255"> <span class="hljs-keyword">return</span> jsonify(<span class="hljs-built_in">dict</span>(user))</span> 448<span class="t-code__line" data-line-number="256"> <span class="hljs-keyword">except</span> psycopg2.Error <span class="hljs-keyword">as</span> e:</span> 449<span class="t-code__line" data-line-number="257"> logger.error(f"Error fetching user: <span class="hljs-subst">{e}</span>")</span> 450<span class="t-code__line" data-line-number="258"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Failed to fetch user'</span>}), <span class="hljs-number">500</span></span> 451<span class="t-code__line" data-line-number="259"> <span class="hljs-keyword">finally</span>:</span> 452<span class="t-code__line" data-line-number="260"> conn.close()</span> 453<span class="t-code__line" data-line-number="261"></span> 454<span class="t-code__line" data-line-number="262">@app.route(<span class="hljs-string">'/api/users/<int:user_id>'</span>, methods=[<span class="hljs-string">'DELETE'</span>])</span> 455<span class="t-code__line" data-line-number="263"><span class="hljs-keyword">def</span> <span class="hljs-title function_">delete_user</span>(<span class="hljs-params">user_id</span>):</span> 456<span class="t-code__line" data-line-number="264"> <span class="hljs-string">"""Delete a user"""</span></span> 457<span class="t-code__line" data-line-number="265"> conn = get_db_connection()</span> 458<span class="t-code__line" data-line-number="266"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> conn:</span> 459<span class="t-code__line" data-line-number="267"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Database connection failed'</span>}), <span class="hljs-number">500</span></span> 460<span class="t-code__line" data-line-number="268"></span> 461<span class="t-code__line" data-line-number="269"> <span class="hljs-keyword">try</span>:</span> 462<span class="t-code__line" data-line-number="270"> <span class="hljs-keyword">with</span> conn.cursor() <span class="hljs-keyword">as</span> cursor:</span> 463<span class="t-code__line" data-line-number="271"> cursor.execute(<span class="hljs-string">"DELETE FROM users WHERE id = %s"</span>, (user_id,))</span> 464<span class="t-code__line" data-line-number="272"></span> 465<span class="t-code__line" data-line-number="273"> <span class="hljs-keyword">if</span> cursor.rowcount == <span class="hljs-number">0</span>:</span> 466<span class="t-code__line" data-line-number="274"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'User not found'</span>}), <span class="hljs-number">404</span></span> 467<span class="t-code__line" data-line-number="275"></span> 468<span class="t-code__line" data-line-number="276"> conn.commit()</span> 469<span class="t-code__line" data-line-number="277">
469 <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'message'</span>: <span class="hljs-string">'User deleted successfully'</span>})</span> 470<span class="t-code__line" data-line-number="278"> <span class="hljs-keyword">except</span> psycopg2.Error <span class="hljs-keyword">as</span> e:</span> 471<span class="t-code__line" data-line-number="279"> logger.error(f"Error deleting user: <span class="hljs-subst">{e}</span>")</span> 472<span class="t-code__line" data-line-number="280"> conn.rollback()</span> 473<span class="t-code__line" data-line-number="281"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Failed to delete user'</span>}), <span class="hljs-number">500</span></span> 474<span class="t-code__line" data-line-number="282"> <span class="hljs-keyword">finally</span>:</span> 475<span class="t-code__line" data-line-number="283"> conn.close()</span> 476<span class="t-code__line" data-line-number="284"></span> 477<span class="t-code__line" data-line-number="285">@app.errorhandler(<span class="hljs-number">404</span>)</span> 478<span class="t-code__line" data-line-number="286"><span class="hljs-keyword">def</span> <span class="hljs-title function_">not_found</span>(<span class="hljs-params">error</span>):</span> 479<span class="t-code__line" data-line-number="287"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Not found'</span>}), <span class="hljs-number">404</span></span> 480<span class="t-code__line" data-line-number="288"></span> 481<span class="t-code__line" data-line-number="289">@app.errorhandler(<span class="hljs-number">500</span>)</span> 482<span class="t-code__line" data-line-number="290"><span class="hljs-keyword">def</span> <span class="hljs-title function_">internal_error</span>(<span class="hljs-params">error</span>):</span> 483<span class="t-code__line" data-line-number="291"> <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Internal server error'</span>}), <span class="hljs-number">500</span></span> 484<span class="t-code__line" data-line-number="292"></span> 485<span class="t-code__line" data-line-number="293"><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:</span> 486<span class="t-code__line" data-line-number="294"> <span class="hljs-comment"># Initialize database on startup</span></span> 487<span class="t-code__line" data-line-number="295"> <span class="hljs-keyword">if</span> init_database():</span> 488<span class="t-code__line" data-line-number="296"> logger.info(<span class="hljs-string">"Starting Flask application..."</span>)</span> 489<span class="t-code__line" data-line-number="297"> app.run(debug=<span class="hljs-literal">True</span>, host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">5001</span>)</span> 490<span class="t-code__line" data-line-number="298"> <span class="hljs-keyword">else</span>:</span> 491<span class="t-code__line" data-line-number="299"> logger.error(<span class="hljs-string">"Failed to initialize database. Exiting."</span>)</span> 492<span class="t-code__line" data-line-number="300"> exit(<span class="hljs-number">1</span>)</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">python 493<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 494 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 495 </svg> 496<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 497 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 498 </svg></button> 499 </div> 500 <p class="t-editor__paragraph" class="t-editor__paragraph">5. Edit the requirements.txt file to save the following code</p> 501 <div id="j0tzMTUnu1ykvCzQAftm9" class="t-editor__code-container" data-lang="plaintext" style="position: relative;"> 502 <pre style=" 503 --line-digits: 2;
504 --line-number-start: -1; 505 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-plaintext" language="plaintext"><span class="t-code__line" data-line-number="0">Flask==3.0.0</span> 506<span class="t-code__line" data-line-number="1">psycopg2-binary==2.9.10</span> 507<span class="t-code__line" data-line-number="2">python-dotenv==1.0.0</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">plaintext 508<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 509 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 510 </svg> 511<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 512 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 513 </svg></button> 514 </div> 515 <p class="t-editor__paragraph" class="t-editor__paragraph">6. Come back to previous folder</p> 516 <div id="teVMEX6UjohSfmSP4Uiw8" class="t-editor__code-container" data-lang="shell" style="position: relative;"> 517 <pre style=" 518 --line-digits: 2; 519 --line-number-start: -1; 520 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-shell" language="shell"><span class="t-code__line" data-line-number="0">cd ..</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">shell 521<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 522 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 523 </svg> 524<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 525 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 526 </svg></button> 527 </div> 528 <p class="t-editor__paragraph" class="t-editor__paragraph">7. Edit line 3 of demo.yml to the following line</p> 529 <div class="callout t-editor__callout callout__danger"> 530 <div class="callout-content"> 531 <div data-scalar-name="callout-icon" class="callout-content__icon"> 532 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" width="1em" height="1em"> 533 <path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z"></path> 534 </svg> 535 </div> 536 <div class="callout-content__text"> 537 <p class="t-editor__paragraph" class="t-editor__paragraph">Remove this</p> 538 </div> 539 </div> 540 </div> 541 <div id="S9gplGPhn2lP3Mspq3bf7" class="t-editor__code-container" data-lang="yaml" style="position: relative;"> 542 <pre style=" 543 --line-digits: 2; 544 --line-number-start: -1; 545 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-yaml" language="yaml"><span class="t-code__line" data-line-number="0"><span class="hljs-attr">image:</span> <span class="hljs-string">public.ecr.aws/b7m2q1o2/flask-demo</span></span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">yaml 546<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 547 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 548 </svg> 549<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 550 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 551 </svg></button> 552 </div> 553 <p class="t-editor__paragraph" class="t-editor__paragraph">to</p> 554 <div class="callout t-editor__callout callout__success"> 555 <div class="callout-content"> 556 <div data-scalar-name="callout-icon" class="callout-content__icon"> 557 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" width="1em" height="1em"> 558 <path d="M173.66,98.34a8,8,0,0,1,0,11.32l-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35A8,8,0,0,1,173.66,98.34ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z"></path> 559 </svg> 560 </div> 561 <div class="callout-content__text"> 562 <p class="t-editor__paragraph" class="t-editor__paragraph">Change to:</p> 563 </div> 564 </div> 565 </div> 566 <div id="YcTzwtHIN9AfDG2SQ1KXI" class="t-editor__code-container" data-lang="yaml" style="position: relative;"> 567 <pre style=" 568 --line-digits: 2;
569 --line-number-start: -1; 570 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-yaml" language="yaml"><span class="t-code__line" data-line-number="0"><span class="hljs-attr">image:</span> <span class="hljs-string">local-flask-demo</span></span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">yaml 571<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 572 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 573 </svg> 574<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 575 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 576 </svg></button> 577 </div> 578 <p class="t-editor__paragraph" class="t-editor__paragraph">8. Run the following command in terminal</p> 579 <div id="LErqmKs0fpDVIZZLPexuO" class="t-editor__code-container" data-lang="shell" style="position: relative;"> 580 <pre style=" 581 --line-digits: 2; 582 --line-number-start: -1; 583 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-shell" language="shell"><span class="t-code__line" data-line-number="0">docker build -t local-flask-demo -f flask.dockerfile .</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">shell 584<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 585 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 586 </svg> 587<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 588 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 589 </svg></button> 590 </div> 591 <p class="t-editor__paragraph" class="t-editor__paragraph">9. Run the demo through this terminal command</p> 592 <div id="vRYhxZGFFEhrsXG8xaABd" class="t-editor__code-container" data-lang="shell" style="position: relative;"> 593 <pre style=" 594 --line-digits: 2; 595 --line-number-start: -1; 596 " class="t-editor__code custom-code custom-scroll" data-focus="" data-highlight="" data-added="" data-removed="" data-line-number-start="0"><code class="hljs language-shell" language="shell"><span class="t-code__line" data-line-number="0">docker-compose -f demo.yml up -d</span></code></pre><button class="language-button t-editor__language-picker" data-scalar-type="code-block-copy" type="button">shell 597<svg class="lang-copy" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 598 <path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path> 599 </svg> 600<svg class="lang-success" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"> 601 <path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path> 602 </svg></button> 603 </div> 604 <p class="t-editor__paragraph" class="t-editor__paragraph">After this you should be able to access the local flask demo app at <a target="_blank" rel="noopener noreferrer nofollow" class="t-editor__anchor" href="http://localhost:5001" class="t-editor__anchor">http://localhost:5001</a></p> 605</div> 606`,n=`# Python 607 608Koalaâs Minimal python image built on 0-deb through distroless design significantly reduces CVEs in the final image. The python image is production-ready image optimized for python workloads. 609 610Koalaâs python image is available at multiple registries, including [dockerhub regsitry](https://hub.docker.com/u/koalalab) & [koalaâs public ecr](https://gallery.ecr.aws/b7m2q1o2) . 611 612### Koalaâs Python demo flask app 613 614Use the following command to download the koala python image locally: 615 616\`\`\`shell 617docker pull koalalab/python 618\`\`\` 619 620shell 621 622This guide helps in setting a sample flask demo for building a full application on top of koalaâs minimal python image. 623 624Start by making a Koala Demo folder 625 626\`\`\`shell 627mkdir Koala-Demo 628cd Koala-Demo 629\`\`\` 630 631shell 632 633Save the following file as demo.yml 634 635\`\`\`yaml 636services: 637 web: 638 image: public.ecr.aws/b7m2q1o2/flask-demo 639 ports: 640 - "0.0.0.0:5001:5001" 641 environment: 642 - DB_HOST=db 643 depends_on: 644 - db 645 restart: unless-stopped 646 647 648 db: 649 image: postgres:17 650 environment: 651 - POSTGRES_DB=flask_app 652 - POSTGRES_USER=flask_user 653 - POSTGRES_PASSWORD=your_actual_password 654 ports: 655 - "5432:5432" 656 volumes: 657 - pg_data:/var/lib/postgresql/data 658 restart: unless-stopped 659 660 661volumes: 662 pg_data: 663\`\`\` 664 665yaml 666 667In your terminal, run the following commands 668 669\`\`\`shell 670brew install docker-compose 671docker-compose -f demo.yml up -d 672\`\`\` 673 674shell 675 676After this you should be able to access flask demo app at <http://localhost:5001> 677 678& would look something like this: 679 680[]() 681 682Flask demo app built on koala's minimal python image 683 684*** 685 686*** 687 688### Editing & running the flask demo 689 690The above was a sample demo created by KoalaLab. 691 692If you want to build the image locally & run it yourself, here are the steps: 693 6941. Create a local dockerfile, save this as flask.dockerfile 695 696\`\`\`makefile 697ARG VENV_NAME=koala 698 699# Multi-arch build using koalalab/python images 700FROM koalalab/python:latest-builder AS builder 701 702ARG VENV_NAME 703 704RUN python3 -m venv $VENV_NAME 705
706ENV PATH="$WORKDIR/$VENV_NAME/bin:$PATH" 707 708COPY flask/requirements.txt flask/requirements.txt 709 710RUN pip install --no-cache-dir -r flask/requirements.txt 711 712COPY flask flask 713 714FROM koalalab/python 715 716ARG VENV_NAME 717 718COPY --from=builder $WORKDIR $WORKDIR 719 720ENV PATH="$WORKDIR/$VENV_NAME/bin:$PATH" 721 722# Expose Flask port 723EXPOSE 5001 724 725CMD ["python3", "flask/app.py"] 726\`\`\` 727 728makefile 729 7302\\. Create a sub-folder called flask 731 732\`\`\`shell 733mkdir flask 734cd flask 735\`\`\` 736 737shell 738 7393\\. Create two files in the flask folder 740 741* app.py 742 743- requirements.txt 744 7454\\. Edit the python file app.py include the following code 746 747\`\`\`python 748from flask import Flask, request, jsonify, render_template_string 749import psycopg2 750from psycopg2.extras import RealDictCursor 751import os 752from datetime import datetime 753import logging 754 755# Configure logging 756logging.basicConfig(level=logging.INFO) 757logger = logging.getLogger(__name__) 758 759app = Flask(__name__) 760 761# Database configuration 762DATABASE_CONFIG = { 763 'host': os.getenv('DB_HOST', 'localhost'), 764 'database': os.getenv('DB_NAME', 'flask_app'), 765 'user': os.getenv('DB_USER', 'flask_user'), 766 'password': os.getenv('PASSWORD', 'your_actual_password'), 767 'port': os.getenv('DB_PORT', '5432') 768} 769 770def get_db_connection(): 771 """Create and return a database connection""" 772 try: 773 conn = psycopg2.connect(**DATABASE_CONFIG) 774 return conn 775 except psycopg2.Error as e: 776 logger.error(f"Database connection error: {e}") 777 return None 778 779def init_database(): 780 """Initialize the database with required tables""" 781 conn = get_db_connection() 782 if not conn: 783 logger.error("Could not connect to database for initialization") 784 return False 785 786 try: 787 with conn.cursor() as cursor: 788 # Create users table 789 cursor.execute(""" 790 CREATE TABLE IF NOT EXISTS users ( 791 id SERIAL PRIMARY KEY, 792 name VARCHAR(100) NOT NULL, 793 email VARCHAR(100) UNIQUE NOT NULL, 794 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 795 ) 796 """) 797 conn.commit() 798 logger.info("Database initialized successfully") 799 return True 800 except psycopg2.Error as e: 801 logger.error(f"Database initialization error: {e}") 802 conn.rollback() 803 return False 804 finally: 805 conn.close() 806 807# HTML template for the web interface 808HTML_TEMPLATE = """ 809<!DOCTYPE html> 810<html> 811<head> 812 <title>Flask PostgreSQL App</title> 813 <style> 814 body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } 815 .form-group { margin: 10px 0; } 816 input, button { padding: 8px; margin: 5px; } 817 button { background: #007bff; color: white; border: none; cursor: pointer; } 818 button:hover { background: #0056b3; } 819 .user { background: #f8f9fa; padding: 10px; margin: 5px 0; border-radius: 5px; } 820 .error { color: red; } 821 .success { color: green; } 822 </style> 823</head> 824<body> 825 <h1>Flask PostgreSQL User Management</h1> 826 827 <div id="message"></div> 828 829 <h2>Add New User</h2> 830 <form id="userForm"> 831 <div class="form-group"> 832 <input type="text" id="name" placeholder="Name" required> 833 </div> 834 <div class="form-group"> 835 <input type="email" id="email" placeholder="Email" required> 836 </div> 837 <button type="submit">Add User</button> 838 </form> 839 840 <h2>Users</h2> 841 <div id="users"></div> 842 <button onclick="loadUsers()">Refresh Users</button> 843 844 <script> 845 function showMessage(text, isError = false) { 846 const messageDiv = document.getElementById('message'); 847 messageDiv.textContent = text; 848 messageDiv.className = isError ? 'error' : 'success'; 849 setTimeout(() => messageDiv.textContent = '', 3000); 850 } 851 852 document.getElementById('userForm').addEventListener('submit', async (e) => { 853 e.preventDefault(); 854 const name = document.getElementById('name').value; 855 const email = document.getElementById('email').value; 856 857 try { 858 const response = await fetch('/api/users', { 859 method: 'POST', 860 headers: { 'Content-Type': 'application/json' }, 861 body: JSON.stringify({ name, email }) 862 }); 863 864 const data = await response.json(); 865 if (response.ok) { 866 showMessage('User added successfully!'); 867 document.getElementById('userForm').reset(); 868 loadUsers(); 869 } else { 870 showMessage(data.error || 'Error adding user', true); 871 } 872 } catch (error) { 873 showMessage('Network error', true); 874 } 875 }); 876 877 async function loadUsers() { 878 try { 879 const response = await fetch('/api/users'); 880 const users = await response.json(); 881 882 const usersDiv = document.getElementById('users'); 883 if (users.length === 0) { 884 usersDiv.innerHTML = '<p>No users found</p>'; 885 } else { 886 usersDiv.innerHTML = users.map(user => 887 \`<div class="user"> 888 <strong>\${user.name}</strong> - \${user.email} 889 <br><small>
889Created: \${new Date(user.created_at).toLocaleString()}</small> 890 <button onclick="deleteUser(\${user.id})" style="float: right; background: #dc3545;">Delete</button> 891 </div>\` 892 ).join(''); 893 } 894 } catch (error) { 895 showMessage('Error loading users', true); 896 } 897 } 898 899 async function deleteUser(userId) { 900 if (!confirm('Are you sure you want to delete this user?')) return; 901 902 try { 903 const response = await fetch(\`/api/users/\${userId}\`, { method: 'DELETE' }); 904 const data = await response.json(); 905 906 if (response.ok) { 907 showMessage('User deleted successfully!'); 908 loadUsers(); 909 } else { 910 showMessage(data.error || 'Error deleting user', true); 911 } 912 } catch (error) { 913 showMessage('Network error', true); 914 } 915 } 916 917 // Load users on page load 918 loadUsers(); 919 <\/script> 920</body> 921</html> 922""" 923 924@app.route('/') 925def index(): 926 """Serve the main page""" 927 return render_template_string(HTML_TEMPLATE) 928 929@app.route('/health') 930def health_check(): 931 """Health check endpoint""" 932 conn = get_db_connection() 933 if conn: 934 conn.close() 935 return jsonify({'status': 'healthy', 'database': 'connected'}) 936 else: 937 return jsonify({'status': 'unhealthy', 'database': 'disconnected'}), 500 938 939@app.route('/api/users', methods=['GET']) 940def get_users(): 941 """Get all users""" 942 conn = get_db_connection() 943 if not conn: 944 return jsonify({'error': 'Database connection failed'}), 500 945 946 try: 947 with conn.cursor(cursor_factory=RealDictCursor) as cursor: 948 cursor.execute("SELECT * FROM users ORDER BY created_at DESC") 949 users = cursor.fetchall() 950 return jsonify([dict(user) for user in users]) 951 except psycopg2.Error as e: 952 logger.error(f"Error fetching users: {e}") 953 return jsonify({'error': 'Failed to fetch users'}), 500 954 finally: 955 conn.close() 956 957@app.route('/api/users', methods=['POST']) 958def create_user(): 959 """Create a new user""" 960 data = request.get_json() 961 962 if not data or not data.get('name') or not data.get('email'): 963 return jsonify({'error': 'Name and email are required'}), 400 964 965 conn = get_db_connection() 966 if not conn: 967 return jsonify({'error': 'Database connection failed'}), 500 968 969 try: 970 with conn.cursor(cursor_factory=RealDictCursor) as cursor: 971 cursor.execute( 972 "INSERT INTO users (name, email) VALUES (%s, %s) RETURNING *", 973 (data['name'], data['email']) 974 ) 975 user = cursor.fetchone() 976 conn.commit() 977 return jsonify(dict(user)), 201 978 except psycopg2.IntegrityError: 979 conn.rollback() 980 return jsonify({'error': 'Email already exists'}), 409 981 except psycopg2.Error as e: 982 logger.error(f"Error creating user: {e}") 983 conn.rollback() 984 return jsonify({'error': 'Failed to create user'}), 500 985 finally: 986 conn.close() 987 988@app.route('/api/users/<int:user_id>', methods=['GET']) 989def get_user(user_id): 990 """Get a specific user""" 991 conn = get_db_connection() 992 if not conn: 993 return jsonify({'error': 'Database connection failed'}), 500 994 995 try: 996 with conn.cursor(cursor_factory=RealDictCursor) as cursor: 997 cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) 998 user = cursor.fetchone() 999 1000 if not user: 1001 return jsonify({'error': 'User not found'}), 404 1002 1003 return jsonify(dict(user)) 1004 except psycopg2.Error as e: 1005 logger.error(f"Error fetching user: {e}") 1006 return jsonify({'error': 'Failed to fetch user'}), 500 1007 finally: 1008 conn.close() 1009 1010@app.route('/api/users/<int:user_id>', methods=['DELETE']) 1011def delete_user(user_id): 1012 """Delete a user""" 1013 conn = get_db_connection() 1014 if not conn: 1015 return jsonify({'error': 'Database connection failed'}), 500 1016 1017 try:
1018 with conn.cursor() as cursor: 1019 cursor.execute("DELETE FROM users WHERE id = %s", (user_id,)) 1020 1021 if cursor.rowcount == 0: 1022 return jsonify({'error': 'User not found'}), 404 1023 1024 conn.commit() 1025 return jsonify({'message': 'User deleted successfully'}) 1026 except psycopg2.Error as e: 1027 logger.error(f"Error deleting user: {e}") 1028 conn.rollback() 1029 return jsonify({'error': 'Failed to delete user'}), 500 1030 finally: 1031 conn.close() 1032 1033@app.errorhandler(404) 1034def not_found(error): 1035 return jsonify({'error': 'Not found'}), 404 1036 1037@app.errorhandler(500) 1038def internal_error(error): 1039 return jsonify({'error': 'Internal server error'}), 500 1040 1041if __name__ == '__main__': 1042 # Initialize database on startup 1043 if init_database(): 1044 logger.info("Starting Flask application...") 1045 app.run(debug=True, host='0.0.0.0', port=5001) 1046 else: 1047 logger.error("Failed to initialize database. Exiting.") 1048 exit(1) 1049\`\`\` 1050 1051python 1052 10535\\. Edit the requirements.txt file to save the following code 1054 1055\`\`\`plaintext 1056Flask==3.0.0 1057psycopg2-binary==2.9.10 1058python-dotenv==1.0.0 1059\`\`\` 1060 1061plaintext 1062 10636\\. Come back to previous folder 1064 1065\`\`\`shell 1066cd .. 1067\`\`\` 1068 1069shell 1070 10717\\. Edit line 3 of demo.yml to the following line 1072 1073Remove this 1074 1075\`\`\`yaml 1076image: public.ecr.aws/b7m2q1o2/flask-demo 1077\`\`\` 1078 1079yaml 1080 1081to 1082 1083Change to: 1084 1085\`\`\`yaml 1086image: local-flask-demo 1087\`\`\` 1088 1089yaml 1090 10918\\. Run the following command in terminal 1092 1093\`\`\`shell 1094docker build -t local-flask-demo -f flask.dockerfile . 1095\`\`\` 1096 1097shell 1098 10999\\. Run the demo through this terminal command 1100 1101\`\`\`shell 1102docker-compose -f demo.yml up -d 1103\`\`\` 1104 1105shell 1106 1107After this you should be able to access the local flask demo app at <http://localhost:5001> 1108`,a={BplxCmbkc3NkR4JOz8CE_:{name:"Koalaâs Python demo flask app",slug:"koalas-python-demo-flask-app",level:2,uid:"BplxCmbkc3NkR4JOz8CE_",parent:""},"DpNJ-vXhnXj3dm7NjIHvu":{name:"Editing & running the flask demo",slug:"editing-running-the-flask-demo",level:2,uid:"DpNJ-vXhnXj3dm7NjIHvu",parent:""}},l={error:!1,html:s,markdown:n,headings:a};export{l as default,e as error,a as headings,s as html,n as markdown}; 1109//# sourceMappingURL=VhokCtdT4yZVsxDP9YX3D-BVEs_P80.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.