From 94012c0140dc35f97689f8054638232d00b2b95b Mon Sep 17 00:00:00 2001
From: Arie Peterson <arie@greenhost.nl>
Date: Thu, 2 Feb 2023 16:18:35 +0100
Subject: [PATCH] Get necessary URLs from new backend endpoint

---
 README.md                                     |  2 -
 backend/areas/__init__.py                     | 12 +++++-
 backend/areas/apps/apps.py                    |  1 +
 .../helmchart/templates/configmaps.yaml       |  3 --
 docker-compose.yml                            |  7 ++-
 frontend/local.env.example                    |  2 -
 frontend/src/components/Header/Header.tsx     | 43 ++++++++++++++-----
 7 files changed, 47 insertions(+), 23 deletions(-)

diff --git a/README.md b/README.md
index 4d7d3465..d5b0e51e 100644
--- a/README.md
+++ b/README.md
@@ -90,8 +90,6 @@ configure it, create a `local.env` file in the `frontend` directory:
 
     cp local.env.example local.env
 
-and adjust the `REACT_APP_HYDRA_PUBLIC_URL` to the SSO URL of your cluster.
-
 #### 3. Setup hosts file
 
 The application will run on `http://stackspin_proxy`. Add the following line to
diff --git a/backend/areas/__init__.py b/backend/areas/__init__.py
index ae4261ed..b90dfee2 100644
--- a/backend/areas/__init__.py
+++ b/backend/areas/__init__.py
@@ -1,4 +1,6 @@
-from flask import Blueprint
+from flask import Blueprint, jsonify
+
+from config import *
 
 api_v1 = Blueprint("api_v1", __name__, url_prefix="/api/v1")
 
@@ -7,3 +9,11 @@ api_v1 = Blueprint("api_v1", __name__, url_prefix="/api/v1")
 @api_v1.route("/health")
 def api_index():
     return "Stackspin API v1.0"
+
+@api_v1.route("/environment")
+def api_environment():
+    environment = {
+        "HYDRA_PUBLIC_URL": HYDRA_PUBLIC_URL,
+        "KRATOS_PUBLIC_URL": KRATOS_PUBLIC_URL,
+    }
+    return jsonify(environment)
diff --git a/backend/areas/apps/apps.py b/backend/areas/apps/apps.py
index 1bd55644..aa61d041 100644
--- a/backend/areas/apps/apps.py
+++ b/backend/areas/apps/apps.py
@@ -29,6 +29,7 @@ def get_apps():
 
 @api_v1.route('/apps/<string:slug>', methods=['GET'])
 @jwt_required()
+@cross_origin()
 def get_app(slug):
     """Return data about a single app"""
     app = AppsService.get_app(slug)
diff --git a/deployment/helmchart/templates/configmaps.yaml b/deployment/helmchart/templates/configmaps.yaml
index 8aa1b69a..679c7004 100644
--- a/deployment/helmchart/templates/configmaps.yaml
+++ b/deployment/helmchart/templates/configmaps.yaml
@@ -19,9 +19,6 @@ data:
   KRATOS_PUBLIC_URL: {{ .Values.backend.kratos.publicUrl }}
   KRATOS_ADMIN_URL: {{ .Values.backend.kratos.adminUrl }}
   HYDRA_PUBLIC_URL: {{ .Values.backend.oidc.baseUrl }}
-  # React can only read env variables if they're prepended with REACT_APP.
-  REACT_APP_HYDRA_PUBLIC_URL: {{ .Values.backend.oidc.baseUrl }}
-  REACT_APP_KRATOS_PUBLIC_URL: {{ .Values.backend.kratos.publicUrl }}
   HYDRA_ADMIN_URL: {{ .Values.backend.hydra.adminUrl }}
   DASHBOARD_URL: {{ .Values.backend.dashboardUrl }}
   LOGIN_PANEL_URL: {{ .Values.backend.dashboardUrl }}/web
diff --git a/docker-compose.yml b/docker-compose.yml
index cfab7512..d355fdb7 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,13 +3,12 @@ services:
   frontend:
     build:
       context: ./frontend
-    working_dir: "/home/node/app"
     env_file: ./frontend/local.env
-    # volumes:
-    #   - ./frontend/src:/home/node/app/src
+    volumes:
+      - ./frontend/src:/home/node/app/src
     ports:
       - "3000:3000"
-    # command: "yarn start"
+    command: "yarn run start"
   stackspin_proxy:
     image: nginx:1.23.3
     ports:
diff --git a/frontend/local.env.example b/frontend/local.env.example
index 48059cce..c32c7c4c 100644
--- a/frontend/local.env.example
+++ b/frontend/local.env.example
@@ -1,3 +1 @@
 REACT_APP_API_URL=http://stackspin_proxy:8081/api/v1
-REACT_APP_HYDRA_PUBLIC_URL=https://sso.init.stackspin.net
-REACT_APP_KRATOS_PUBLIC_URL=http://stackspin_proxy:8081/kratos
diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx
index 022c8dea..4d57a9ae 100644
--- a/frontend/src/components/Header/Header.tsx
+++ b/frontend/src/components/Header/Header.tsx
@@ -1,6 +1,7 @@
-import React, { Fragment, useMemo, useState } from 'react';
+import React, { Fragment, useEffect, useState } from 'react';
 import { Disclosure, Menu, Transition } from '@headlessui/react';
 import { MenuIcon, XIcon } from '@heroicons/react/outline';
+import { performApiCall } from 'src/services/api';
 import { useAuth } from 'src/services/auth';
 import Gravatar from 'react-gravatar';
 import { Link, useLocation } from 'react-router-dom';
@@ -9,9 +10,6 @@ import _ from 'lodash';
 
 import { UserModal } from '../UserModal';
 
-const HYDRA_LOGOUT_URL = `${process.env.REACT_APP_HYDRA_PUBLIC_URL}/oauth2/sessions/logout`;
-const KRATOS_PUBLIC_URL = process.env.REACT_APP_KRATOS_PUBLIC_URL;
-
 const navigation = [
   { name: 'Dashboard', to: '/dashboard', requiresAdmin: false },
   { name: 'Users', to: '/users', requiresAdmin: true },
@@ -30,16 +28,44 @@ function filterNavigationByDashboardRole(isAdmin: boolean) {
   return navigation.filter((item) => !item.requiresAdmin);
 }
 
+export interface Environment {
+  HYDRA_PUBLIC_URL: string;
+  KRATOS_PUBLIC_URL: string;
+}
+
+const defaultEnvironment: Environment = {
+  HYDRA_PUBLIC_URL: 'error-failed-to-load-env-from-backend',
+  KRATOS_PUBLIC_URL: 'error-failed-to-load-env-from-backend',
+};
+
 // eslint-disable-next-line @typescript-eslint/no-empty-interface
 interface HeaderProps {}
 
 const Header: React.FC<HeaderProps> = () => {
+  const [environment, setEnvironment] = useState(defaultEnvironment);
   const [currentUserModal, setCurrentUserModal] = useState(false);
   const [currentUserId, setCurrentUserId] = useState(null);
   const { logOut, currentUser, isAdmin } = useAuth();
 
   const { pathname } = useLocation();
 
+  useEffect(() => {
+    let active = true;
+    async function loadEnvironment() {
+      const result = await performApiCall({
+        path: '/environment',
+      });
+      if (!active) {
+        return;
+      }
+      setEnvironment(result.data);
+    }
+    loadEnvironment();
+    return () => {
+      active = false;
+    };
+  }, []);
+
   const currentUserModalOpen = (id: any) => {
     setCurrentUserId(id);
     setCurrentUserModal(true);
@@ -52,13 +78,8 @@ const Header: React.FC<HeaderProps> = () => {
 
   const navigationItems = filterNavigationByDashboardRole(isAdmin);
 
-  const signOutUrl = useMemo(() => {
-    return HYDRA_LOGOUT_URL;
-  }, []);
-
-  const kratosSettingsUrl = useMemo(() => {
-    return `${KRATOS_PUBLIC_URL}/self-service/settings/browser`;
-  }, []);
+  const signOutUrl = `${environment.HYDRA_PUBLIC_URL}/oauth2/sessions/logout`;
+  const kratosSettingsUrl = `${environment.KRATOS_PUBLIC_URL}/self-service/settings/browser`;
 
   return (
     <>
-- 
GitLab