commit 6f25cb4691b5a64c86ddeca670cf9c2cb7074108
Author: mdivecky <matej@divecky.com>
Date:   Thu Oct 19 18:37:19 2023 +0200

    init

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ce2ccaa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*/__pycache__
+.env
+*.pyc
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0d51543
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+
+Aktuálně jsou hardcodované dvě zastávky v proměnné na začátku kódu, dalo by se hodit do proměné třeba jako json.
+Ryšánka - směr Budějovická (C) - U632Z3P
+Na Vrstevnici - směr Přístaviště (A) - U436Z1P
+
+Název zastávky a její stanoviště (A,B,C,D...) je možné najít na [webu PID](https://pid.cz/zastavkova-tabla/?stop=Na+Vrstevnici&stanoviste=&tab=3)
+Název je pak možné hodit do [Swaggeru od Golemia](https://api.golemio.cz/pid/docs/openapi/#/%F0%9F%A7%BE%20GTFS%20Static/get_gtfs_stops) (get_gtfs_stops) a ve výstupu najít odpovídající ID zastávky (`stop_id`)
+
+Před prvním spuštěním je potřeba dostat API key od Golemia, jde to zdarma [zde](https://api.golemio.cz/api-keys/auth/sign-in). Ten pak vložit do souboru .env jako proměnnou `API_KEY`.
+
+"Dockerizováno" primárně podle návodu [tady](https://github.com/docker/awesome-compose/blob/master/nginx-wsgi-flask/compose.yaml)
+
+## Todo
+* Auto refresh?
+* Cache pro data z Golemia?
+* Přesun IDs zastávek do proměnné
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..bea5316
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,31 @@
+version: "3.3"
+
+services:
+  nginx-proxy:
+    build: nginx
+    restart: always
+    volumes:
+      - ./nginx/default.conf:/tmp/default.conf
+    environment: 
+      - FLASK_SERVER_ADDR=flask-app:8000
+    ports:
+      - "127.0.0.1:7880:80"
+    depends_on:
+      - flask-app
+    healthcheck:
+      test: ["CMD-SHELL", "curl --silent --fail localhost:80/health-check || exit 1"]
+      interval: 10s
+      timeout: 10s
+      retries: 3
+    command: /app/start.sh
+  flask-app:
+    build: flask
+    restart: always
+    environment:
+      - API_KEY=${API_KEY}
+    healthcheck:
+      test: ["CMD-SHELL", "curl --silent --fail localhost:8000/flask-health-check || exit 1"]
+      interval: 10s
+      timeout: 10s
+      retries: 3
+    command: gunicorn -w 3 -t 60 -b 0.0.0.0:8000 app:app
diff --git a/env.sample b/env.sample
new file mode 100644
index 0000000..2387a6f
--- /dev/null
+++ b/env.sample
@@ -0,0 +1 @@
+API_KEY=
\ No newline at end of file
diff --git a/flask/Dockerfile b/flask/Dockerfile
new file mode 100644
index 0000000..e21b756
--- /dev/null
+++ b/flask/Dockerfile
@@ -0,0 +1,32 @@
+FROM python:alpine
+
+# upgrade pip
+RUN pip install --upgrade pip
+
+# get curl for healthchecks
+RUN apk add curl
+
+# permissions and nonroot user for tightened security
+RUN adduser -D nonroot
+RUN mkdir /home/app/ && chown -R nonroot:nonroot /home/app
+RUN mkdir -p /var/log/flask-app && touch /var/log/flask-app/flask-app.err.log && touch /var/log/flask-app/flask-app.out.log
+RUN chown -R nonroot:nonroot /var/log/flask-app
+WORKDIR /home/app
+USER nonroot
+
+# copy all the files to the container
+COPY --chown=nonroot:nonroot . .
+
+# venv
+ENV VIRTUAL_ENV=/home/app/venv
+
+# python setup
+RUN python -m venv $VIRTUAL_ENV
+ENV PATH="$VIRTUAL_ENV/bin:$PATH"
+RUN export FLASK_APP=app.py
+RUN pip install -r requirements.txt
+
+# define the port number the container should expose
+EXPOSE 5000
+
+CMD ["python", "app.py"]
diff --git a/flask/app.py b/flask/app.py
new file mode 100644
index 0000000..31c8cf0
--- /dev/null
+++ b/flask/app.py
@@ -0,0 +1,79 @@
+import os
+import requests
+from pprint import pprint
+from flask import Flask, render_template
+import datetime
+
+
+API_KEY = os.environ['API_KEY']
+
+API_URL = "https://api.golemio.cz/v2/pid/departureboards"
+
+STOPS = ["U632Z3P", "U436Z1P"]
+
+#STOPS = ["U632Z3P"]
+
+CAR_AMOUNT = 10
+
+app = Flask(__name__)
+
+
+def get_departures(api, key, stop):
+    headers = {"accept": "application/json", "X-Access-Token": key}
+    params = {"ids": stop, "limit": CAR_AMOUNT}
+    r = requests.get(api, params=params, headers=headers)
+    return r.json()
+
+
+def get_cars(stops):
+    cars = []
+    for stop in stops:
+        deps = get_departures(API_URL, API_KEY, stop)
+        stop_name = deps["stops"][0]["stop_name"]
+        stop_platform = deps["stops"][0]["platform_code"]
+        stop_display = stop_name #+ " (" + stop_platform + ")"
+
+        stop_cars = []
+
+        for car in deps["departures"]:
+            # print(car)
+            car_id = car["route"]["short_name"]
+            car_dir = car["trip"]["headsign"]
+            car_ac = car["trip"]["is_air_conditioned"]
+            car_departure = car["departure_timestamp"]["minutes"]
+            if car["delay"]["is_available"]:
+                # car_ahead = "+ "
+                car_delay_sec = (car["delay"]["minutes"] * 60) + car["delay"]["seconds"]
+                # if car_delay_sec < 0:
+                #     car_ahead = "- "
+                # elif car_delay_sec == 0:
+                #     car_ahead = ""
+
+
+                # m, s = divmod(car_delay_sec, 60)
+                # car_delay = car_ahead + str(m) + "min, " + str(s) + "sec"
+                car_delay = car_delay_sec
+            else:
+                car_delay = "?"
+
+            car_data = {
+                "id": car_id,
+                "dir": car_dir,
+                "ac": car_ac,
+                "departure": car_departure,
+                "delay": car_delay,
+            }
+
+            stop_cars.append(car_data)
+
+        cars.append({"id": stop, "name": stop_display, "cars": stop_cars})
+    return cars
+
+
+@app.route("/")
+def hello_world():
+    return render_template('index.html', data=get_cars(STOPS))
+
+@app.route('/flask-health-check')
+def flask_health_check():
+	return "success"
diff --git a/flask/requirements.txt b/flask/requirements.txt
new file mode 100644
index 0000000..0615934
--- /dev/null
+++ b/flask/requirements.txt
@@ -0,0 +1,3 @@
+requests
+gunicorn
+Flask
diff --git a/flask/templates/index.html b/flask/templates/index.html
new file mode 100644
index 0000000..f4a2ffb
--- /dev/null
+++ b/flask/templates/index.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <!-- Required meta tags -->
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    <!-- Bootstrap CSS -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
+    <title>Hello, world!</title>
+  </head>
+  <body>
+
+    <div class="container">
+
+        {% for stop in data %}
+        
+            <p style="font-size: 24px">{{ stop.name }}</p>
+
+            <table class="table table-striped table-sm">
+                
+                <thead>
+                <tr>
+                    <th scope="col">Linka</th>
+                    <th scope="col">Odjezd za</th>
+                    <!-- <th scope="col">Směr</th> -->
+                    <th scope="col">Zpoždění</th>
+                    <th scope="col">AC</th>
+                    <th></th>
+                </tr>
+                </thead>
+                <tbody>
+                {% for car in stop.cars %}
+                <tr>
+                    <th scope="row">{{ car.id }}</th>
+                    <td>{{ car.departure }} min</td>
+                    <!-- <td>{{ car.dir }}</td> -->
+                    <td>{{ car.delay }} sec</td>
+                    <td>{% if car.ac %} <i class="bi bi-snow"></i> {% endif %} </td>
+                </tr>
+                {% endfor %}
+                </tbody>
+            </table>
+
+        {% endfor %}
+        <caption>Odjezdy jsou včetně zpoždění</caption>
+    </div>  
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
+
+  </body>
+</html>
+
+
+
diff --git a/flask/wsgi.py b/flask/wsgi.py
new file mode 100644
index 0000000..b0efe6e
--- /dev/null
+++ b/flask/wsgi.py
@@ -0,0 +1,5 @@
+from app import app
+import os
+
+if __name__ == "__main__":
+    app.run(host='0.0.0.0', port=os.environ.get("FLASK_SERVER_PORT"), debug=True)
\ No newline at end of file
diff --git a/nginx/Dockerfile b/nginx/Dockerfile
new file mode 100644
index 0000000..ce35eee
--- /dev/null
+++ b/nginx/Dockerfile
@@ -0,0 +1,32 @@
+FROM nginx:1.25.2-alpine
+
+# Add bash for boot cmd
+RUN apk add bash
+
+# Add nginx.conf to container
+COPY --chown=nginx:nginx nginx.conf /etc/nginx/nginx.conf
+COPY --chown=nginx:nginx start.sh /app/start.sh
+
+# set workdir
+WORKDIR /app
+
+# permissions and nginx user for tightened security
+RUN chown -R nginx:nginx /app && chmod -R 755 /app && \
+        chown -R nginx:nginx /var/cache/nginx && \
+        chown -R nginx:nginx /var/log/nginx && \
+        chmod -R 755 /var/log/nginx; \
+        chown -R nginx:nginx /etc/nginx/conf.d
+RUN touch /var/run/nginx.pid && chown -R nginx:nginx /var/run/nginx.pid
+
+# # Uncomment to keep the nginx logs inside the container - Leave commented for logging to stdout and stderr
+# RUN mkdir -p /var/log/nginx
+# RUN unlink /var/log/nginx/access.log \
+#     && unlink /var/log/nginx/error.log \
+#     && touch /var/log/nginx/access.log \
+#     && touch /var/log/nginx/error.log \
+#     && chown nginx /var/log/nginx/*log \
+#     && chmod 644 /var/log/nginx/*log
+
+USER nginx
+
+CMD ["nginx", "-g", "'daemon off;'"]
\ No newline at end of file
diff --git a/nginx/default.conf b/nginx/default.conf
new file mode 100644
index 0000000..c8a8bcd
--- /dev/null
+++ b/nginx/default.conf
@@ -0,0 +1,18 @@
+proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:10m max_size=500m inactive=60m use_temp_path=off;
+
+server {
+  listen 80;
+
+  location / {
+    proxy_pass http://$FLASK_SERVER_ADDR;
+    proxy_set_header Host $host;
+    proxy_set_header X-Real-IP $remote_addr;
+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+  }
+
+  location /health-check {
+    add_header Content-Type text/plain;
+    return 200 "success";
+  }
+  
+}
\ No newline at end of file
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
new file mode 100644
index 0000000..d72a39b
--- /dev/null
+++ b/nginx/nginx.conf
@@ -0,0 +1,50 @@
+worker_processes auto;
+pid        /var/run/nginx.pid;
+
+events {
+    worker_connections  1024;
+}
+
+http {
+    include       /etc/nginx/mime.types;
+    default_type  application/octet-stream;
+
+    # Define the format of log messages.
+    log_format  main_ext  '$remote_addr - $remote_user [$time_local] "$request" '
+                      '$status $body_bytes_sent "$http_referer" '
+                      '"$http_user_agent" "$http_x_forwarded_for" '
+                      '"$host" sn="$server_name" '
+                      'rt=$request_time '
+                      'ua="$upstream_addr" us="$upstream_status" '
+                      'ut="$upstream_response_time" ul="$upstream_response_length" '
+                      'cs=$upstream_cache_status' ;
+
+    access_log  /var/log/nginx/access.log main_ext;
+    error_log  /var/log/nginx/error.log warn;
+
+    sendfile        on;
+
+    keepalive_timeout  65;
+
+    # Enable Compression
+    gzip  on;
+
+    # Disable Display of NGINX Version
+    server_tokens off;
+
+    # Size Limits
+    client_body_buffer_size 10K;
+    client_header_buffer_size 1k;
+    client_max_body_size 8m;
+    large_client_header_buffers 2 1k;
+
+    # # SSL / TLS Settings - Suggested for Security
+    # ssl_protocols TLSv1.2 TLSv1.3;
+	# ssl_session_timeout 15m;
+	# ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
+	# ssl_prefer_server_ciphers on;
+	# ssl_session_tickets off;
+
+    include /etc/nginx/conf.d/*.conf;
+
+}
\ No newline at end of file
diff --git a/nginx/start.sh b/nginx/start.sh
new file mode 100644
index 0000000..d3c7ac2
--- /dev/null
+++ b/nginx/start.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+envsubst '$FLASK_SERVER_ADDR' < /tmp/default.conf > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'
\ No newline at end of file