PageSourceSearch

https://docs.koalalab.com/assets/VhokCtdT4yZVsxDP9YX3D-BVEs_P80.js

js koalalab.com collected 2026-10-08 02:00:18 UTC 83,254 bytes, 1,109 lines download raw bytes

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> &#x26; <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">&#x26; 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 &#x26; 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 &#x26; 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">&#x3C;!DOCTYPE html></span></span>
254<span class="t-code__line" data-line-number="62"><span class="hljs-string">&#x3C;html></span></span>
255<span class="t-code__line" data-line-number="63"><span class="hljs-string">&#x3C;head></span></span>
256<span class="t-code__line" data-line-number="64"><span class="hljs-string">    &#x3C;title>Flask PostgreSQL App&#x3C;/title></span></span>
257<span class="t-code__line" data-line-number="65"><span class="hljs-string">    &#x3C;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">    &#x3C;/style></span></span>
267<span class="t-code__line" data-line-number="75"><span class="hljs-string">&#x3C;/head></span></span>
268<span class="t-code__line" data-line-number="76"><span class="hljs-string">&#x3C;body></span></span>
269<span class="t-code__line" data-line-number="77"><span class="hljs-string">    &#x3C;h1>Flask PostgreSQL User Management&#x3C;/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">    &#x3C;div id="message">&#x3C;/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">    &#x3C;h2>Add New User&#x3C;/h2></span></span>
274<span class="t-code__line" data-line-number="82"><span class="hljs-string">    &#x3C;form id="userForm"></span></span>
275<span class="t-code__line" data-line-number="83"><span class="hljs-string">        &#x3C;div class="form-group"></span></span>
276<span class="t-code__line" data-line-number="84"><span class="hljs-string">            &#x3C;input type="text" id="name" placeholder="Name" required></span></span>
277<span class="t-code__line" data-line-number="85"><span class="hljs-string">        &#x3C;/div></span></span>
278<span class="t-code__line" data-line-number="86"><span class="hljs-string">        &#x3C;div class="form-group"></span></span>
279<span class="t-code__line" data-line-number="87"><span class="hljs-string">            &#x3C;input type="email" id="email" placeholder="Email" required></span></span>
280<span class="t-code__line" data-line-number="88"><span class="hljs-string">        &#x3C;/div></span></span>
281<span class="t-code__line" data-line-number="89"><span class="hljs-string">        &#x3C;button type="submit">Add User&#x3C;/button></span></span>
282<span class="t-code__line" data-line-number="90"><span class="hljs-string">    &#x3C;/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">    &#x3C;h2>Users&#x3C;/h2></span></span>
285<span class="t-code__line" data-line-number="93"><span class="hljs-string">    &#x3C;div id="users">&#x3C;/div></span></span>
286<span class="t-code__line" data-line-number="94"><span class="hljs-string">    &#x3C;button onclick="loadUsers()">Refresh Users&#x3C;/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">    &#x3C;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 = '&#x3C;p>No users found&#x3C;/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">                        \`&#x3C;div class="user"></span></span>
332<span class="t-code__line" data-line-number="140"><span class="hljs-string">                            &#x3C;strong>\${user.name}&#x3C;/strong> - \${user.email}</span></span>
333<span class="t-code__line" data-line-number="141"><span class="hljs-string">                            &#x3C;br>&#x3C;small>
333Created: \${new Date(user.created_at).toLocaleString()}&#x3C;/small></span></span>
334<span class="t-code__line" data-line-number="142"><span class="hljs-string">                            &#x3C;button onclick="deleteUser(\${user.id})" style="float: right; background: #dc3545;">Delete&#x3C;/button></span></span>
335<span class="t-code__line" data-line-number="143"><span class="hljs-string">                        &#x3C;/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">    &#x3C;/script></span></span>
364<span class="t-code__line" data-line-number="172"><span class="hljs-string">&#x3C;/body></span></span>
365<span class="t-code__line" data-line-number="173"><span class="hljs-string">&#x3C;/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/&#x3C;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/&#x3C;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[![](https://api.scalar.com/cdn/images/mtqaQ-q233kXGmnMGs2Cw/JvIJfRLWq2CmKv0Zl4yHx.png)]()
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.