diff --git a/CHANGELOG.md b/CHANGELOG.md
index 225e05505e4952bbe211a6df09a2f61c759eb784..84886e704ed5371f0c3246d2ae3d59a93ef4dd56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Updates
 
+## Update Jumat 22 November 2019
+- Dibuat Dockerfile sehingga aplikasi dapat dijalankan dengan container
+- Handle CORS
+
 ## Update Jumat 15 November 2019
 - Refactor webapp menjadi lebih modular
 - Implementasi tampilan UI
diff --git a/Dockerfile b/Dockerfile
index baf8f4c166f0766765901e8e94e59aa772e0621b..91aee2d62e0ed07e8ec4d282a3f3e8b34b62959d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,17 +1,17 @@
 # base image
 FROM python:3.7-alpine
 
-# set working directory
-RUN mkdir /usr/src/app
-WORKDIR /usr/src/app
+# copy requirements
+COPY ./requirements.txt /app/requirements.txt
 
-# install and cache app dependencies
-COPY requirements.txt /usr/src/app/requirements.txt
-RUN pip install -r /usr/src/app/requirements.txt
+WORKDIR /app
+
+RUN pip install -r requirements.txt
+
+COPY . /app
 
-# add app
-COPY . /usr/src/app
 RUN python tools.py initdb
 
 # start app
-CMD python tools.py start
+EXPOSE 5000
+CMD python app.py
\ No newline at end of file
diff --git a/app.py b/app.py
index 6c95cc516d8ee15313f3c43b62d503707e8c8846..02413d87c1a48b05860537f740f48813245dabac 100644
--- a/app.py
+++ b/app.py
@@ -28,4 +28,4 @@ def about_page():
     return about.about_page()
 
 if __name__ == "__main__":
-    app.run()
\ No newline at end of file
+    app.run(host='0.0.0.0', port=5000)
\ No newline at end of file
diff --git a/nodemcu/webserver.ino b/nodemcu/webserver.ino
index f321df73400c6a9dc4d25ee2c5578008094963d9..9fcc2926636f33d9a12b93253aaee9827c801e50 100644
--- a/nodemcu/webserver.ino
+++ b/nodemcu/webserver.ino
@@ -36,6 +36,8 @@ void setup() {
   server.on("/api/gpio/toggle", HTTP_POST, toggleGpio);
   server.on("/api/scan", HTTP_GET, handleScan);
   server.on("/api/gpio", HTTP_GET, getGpioSetup);
+
+  server.onNotFound(handleNotFound);
   
   server.begin(); //Start the server
   Serial.println("Server listening");
@@ -77,6 +79,10 @@ char* compareToChar(int var1, int var2) {
 }
 
 void getGpio() {
+  server.sendHeader("Access-Control-Allow-Origin", "*");
+  server.sendHeader("Access-Control-Max-Age", "10000");
+  server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
+  server.sendHeader("Access-Control-Allow-Headers", "*");
   int pin = digitalRead(server.arg("pin").toInt());
   char * out = compareToChar(pin, HIGH);
   char *json = strconcat("{\"success\":true, \"gpioValue\":", out);
@@ -85,6 +91,10 @@ void getGpio() {
 }
 
 void toggleGpio() {
+  server.sendHeader("Access-Control-Allow-Origin", "*");
+  server.sendHeader("Access-Control-Max-Age", "10000");
+  server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
+  server.sendHeader("Access-Control-Allow-Headers", "*");
   int pinToChange = server.arg("pin").toInt();
   int pin = digitalRead(pinToChange);
   char * out = compareToChar(pin, LOW);
@@ -99,10 +109,18 @@ void handleIndex() {
 }
 
 void handleScan() {
+  server.sendHeader("Access-Control-Allow-Origin", "*");
+  server.sendHeader("Access-Control-Max-Age", "10000");
+  server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
+  server.sendHeader("Access-Control-Allow-Headers", "*");
   server.send(200, "application/json", "{\"success\":true, \"available\":true}");
 }
 
 void getGpioSetup() {
+  server.sendHeader("Access-Control-Allow-Origin", "*");
+  server.sendHeader("Access-Control-Max-Age", "10000");
+  server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
+  server.sendHeader("Access-Control-Allow-Headers", "*");
   char *json = "{\"success\":true, \"digitalOutPin\":";
   json = strconcat(json, arrayToString(digitalOut, sizeof(digitalOut)/sizeof(*digitalOut)));
   json = strconcat(json, ", \"digitalInPin\":");
@@ -110,3 +128,19 @@ void getGpioSetup() {
   json = strconcat(json, "}");
   server.send(200, "application/json", json);
 }
+
+void handleNotFound()
+{
+    if (server.method() == HTTP_OPTIONS)
+    {
+        server.sendHeader("Access-Control-Allow-Origin", "*");
+        server.sendHeader("Access-Control-Max-Age", "10000");
+        server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
+        server.sendHeader("Access-Control-Allow-Headers", "*");
+        server.send(204);
+    }
+    else
+    {
+        server.send(404, "text/plain", "API Not Found");
+    }
+}
\ No newline at end of file
diff --git a/webapp/html/device/device-lists.html b/webapp/html/device/device-lists.html
index c41bacc1db8f5105536f6a32dad1e4f7e14ecf7f..47da333ac98a04c4c8c723adc7e6ce48a4608654 100644
--- a/webapp/html/device/device-lists.html
+++ b/webapp/html/device/device-lists.html
@@ -3,26 +3,44 @@
 {% block content %}
 <div>
 
+    <h1>Device List</h1>
+
+    {% if query %}
+    <table class="table">
+        <thead>
+            <tr>
+                <th>#</th>
+                <th>Device IP</th>
+                <th></th>
+            </tr>
+        </thead>
+        <tbody>
+            {% for q in query%}
+            <tr>
+                <td>{{ loop.index }}</td>
+                <td>{{ q.url }}</td>
+                <td><button type="button" class="btn btn-default" onclick="window.location.href='/device/{{q.id}}'">Edit</button></td>
+            </tr>
+            {% endfor %}
+        </tbody>
+    </table>
+    {% else %}
+    <p>Device not found</p>
+    {% endif %}
+
     <!-- Trigger/Open The Modal -->
     <button id="myBtn" type="button" class="btn btn-default">Scan</button>
 
     <!-- The Modal -->
     <div id="myModal" class="modal" data-backdrop="static" data-keyboard="false">
 
-    <!-- Modal content -->
-    <div class="modal-content">
-        <!-- <span class="close">&times;</span> -->
-        <p>Please wait..</p>
-    </div>
+        <!-- Modal content -->
+        <div class="modal-content">
+            <!-- <span class="close">&times;</span> -->
+            <p>Please wait..</p>
+        </div>
 
-    </div>     
+    </div>
 
-    {% if query %}
-    {% for q in query%}
-    <p>Device : {{ q.url }}  ( <a href="/device/{{q.id}}">Edit</a> )</p>
-    {% endfor %}
-    {% else %}
-    <p>Device not found</p>
-    {% endif %}
 </div>
 {% endblock %}
\ No newline at end of file
diff --git a/webapp/html/device/device-monitor.html b/webapp/html/device/device-monitor.html
index a9dcc4269e0501f51abf430e39b0f0150073a187..5f8aec5f95576c61d0913def3550546a5d4083e6 100644
--- a/webapp/html/device/device-monitor.html
+++ b/webapp/html/device/device-monitor.html
@@ -5,7 +5,7 @@
     <p>Device URL : {{ uri }}</p>
 
     
-    <ul id="mytabs" class="nav nav-justified nav-tabs">
+    <ul id="mytabs" class="nav nav-tabs">
         <li class="active"><a href="#f" data-toggle="tab">Statistic</a></li>
         <li><a href="#f1" data-toggle="pill">GPIO In</a></li>
         <li><a href="#f2" data-toggle="pill">GPIO Out</a></li>
@@ -51,7 +51,7 @@
                     <tr>
                         <td>{{ loop.index }}</td>
                         <td>Pin {{ d }}</td>
-                        <td>LOW</td>
+                        <td><p id="gpio-{{ d }}">LOW</p></td>
                         <td><button type="button" class="btn btn-default" onclick="toggle({{ d }})">Toggle</button></td>
                     </tr>
                     {% endfor %}
@@ -63,13 +63,47 @@
     <script type="text/javascript" src="https://desain.cs.ui.ac.id/static/js/vendor/jquery-1.10.1.js"></script>
     <script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
     <script type="text/javascript" src="{{ url_for('static', filename='js/index.js') }}"></script>
+    <script type="text/javascript" src="{{ url_for('static', filename='js/device-monitor.js') }}"></script>
     <script type="text/javascript">
         function toggle(pin) {
+            var pinToChange = pin
             $.post("http://{{ uri }}/api/gpio/toggle?pin=" + pin, function(res) {
-                console.log(res)
+                if(res.gpioValue === true) {
+                    $("#gpio-" + pinToChange).text("HIGH");
+                } else {
+                    $("#gpio-" + pinToChange).text("LOW");
+                }
             })
         }
 
+        function test() {
+            $.get("http://{{ uri }}/api/gpio/get?pin=16", function(res) {
+                
+            });
+            setTimeout(test, 5000);
+        }
+
+        $(document).ready(function() {
+            $.get("http://{{ uri }}/api/gpio", function(res) {
+                var digitalOutPin = res.digitalOutPin;
+                var digitalInPin = res.digitalInPin;
+                var pin = digitalOutPin.concat(digitalInPin);
+                console.log(pin)
+
+                for (var i=0; i<pin.length; i++) {
+                    var pinToChange = pin[i]
+                    $.get("http://{{ uri }}/api/gpio/get?pin=" + pin[i], function(res) {
+                        if(res.gpioValue === true) {
+                            $("#gpio-" + pinToChange).text("HIGH");
+                        } else {
+                            $("#gpio-" + pinToChange).text("LOW");
+                        }
+                    });
+                }
+            });
+        });
+
+        test();
         $("#mytabs").bootstrapDynamicTabs();
     </script>
 </div>
diff --git a/webapp/static/js/device-monitor.js b/webapp/static/js/device-monitor.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391