How to run the multiple react applications in a single port by Nginx with docker

371 views Asked by At

i want to deploy two apps(admin, interview) on same container, same origin. and it has own contextpath '/admin', '/interview'. in case of admin app,on first landing it supposed to redirect '/login' page. it works when i run on my local.

i tried following this article.

when i tried, its seem react apps build correctly in container. also when its browsed by chrome i checked browser network tab and its seem request access to path of static build files.

here is request initiator chain

http://localhost/admin
 > http://localhost/admin/
  > http://localhost/admin/static/js/main.44b88a4b.js
    http://localhost/admin/static/css/main.1270f831.css

but preview tab saying 'You need to enable JavaScript to run this app.' and app throw below html. i check browser setting for javascript, its enabled.

<!doctype html>
<html lang="en">
    <head>
        <title></title>
        <meta property="description" content="" data-id="description"/>
        <meta name="keywords" content=""/>
        <meta charset="utf-8"/>
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
        <meta name="theme-color" content="#000000"/>
        <meta property="og:site_name" content=""/>
        <meta property="og:description" content="" data-id="og:description"/>
        <meta property="og:title" content="" data-id="og:title"/>
        <meta property="og:url" content=""/>
        <meta property="og:type" content="website"/>
        <meta property="og:locale:alternate" content="ko_KR"/>
        <meta property="og:image" content="/admin/kakao_thumbnail.png"/>
        <meta name="naver-site-verification" content=""/>
        <link rel="icon" href="/admin/favicon.ico"/>
        <link rel="shortcut icon" href="/admin/android-chrome-192x192"/>
        <link rel="apple-touch-icon" href="/admin/apple-touch-icon.png"/>
        <link rel="/manifest" href="/admin/manifest.json"/>
        <link rel="canonical" data-id="canonical-link" data-rh="true"/>
        <script defer="defer" src="/admin/static/js/main.44b88a4b.js"></script>
        <link href="/admin/static/css/main.1270f831.css" rel="stylesheet">
    </head>
    <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
    </body>
</html>

here is my settings. please let me know if some of these are wrong or missed. each package.json of apps has definition like "homepage": "/admin", and "homepage": "/interview",

directories

>admin
 - app source
 - Dockerfile
 - package.json

>inerview
 - app source
 - Dockerfile
 - package.json

>nginx
 - nginx.conf
 - site.conf

docker-compose-test.yml

docker-compose-test.yml

version: '3.8'

services:
  nginx:
    image: nginx  
    ports:
      - "80:80"
    restart: always
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/site.conf:/etc/nginx/conf.d/default.conf
      - admin:/usr/share/nginx/html/admin
      - interview:/usr/share/nginx/html/interview
  admin:
    build:
      context: admin
      dockerfile: Dockerfile
    volumes:
     - admin:/app/server/client
  interview:
    build:
      context: interview
      dockerfile: Dockerfile
    volumes:
      - interview:/app/build
volumes:
  admin:
  interview:

Dockerfile (each directory of apps has same Dockerfile)

# build environment
FROM node:lts-alpine
WORKDIR /app
COPY . ./
RUN npm install
RUN npm run build


nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;



}

site.conf

server {
    listen 80;
    listen [::]:80;

    server_name localhost;


    location /admin {
        root /usr/share/nginx/html/;
        index index.html;
        try_files $uri $uri/ /admin/index.html;
    }

    location /interview {
        root /usr/share/nginx/html/;
        index index.html;
        try_files $uri $uri/ /interview/index.html;
    }
}

admin's App.tsx return

<BrowserRouter>
      <Helmet
        titleTemplate=""
        defaultTitle=""
        htmlAttributes={{ lang: i18n.language }}
      >
        <meta name="keywords" content="" />
      </Helmet>
      <ThemeConfig>
        <GlobalStyles />
        <Router />
      </ThemeConfig>
    </BrowserRouter>

admin's routes.tsx

const CatchAllRoutes: any[] = [
  {
    path: '/404',
    element: <Page404 />,
  },
  { path: '*', element: <Navigate to="/404" replace /> },
  { path: '/test', element: <SurveyServiceListPage /> },
];
const AuthRoute = {
  path: '/',
  element: <AuthLayout />,
  children: [
    { path: '/', element: <Navigate to="/login" /> },
    { path: 'login', element: <LoginPage /> },
    { path: 'account-retrieval/:mode', element: <AccountRetrievalPage /> },
  ],
};

...

export default function Router() {
  const { accessToken } = useRecoilValue(authState);
  const { state, contents: user } = useRecoilValueLoadable(userState);

  let routes: any[] = [];
  if (state === 'loading' && !(user as any)?.id) {
    routes = [{ path: '*', element: <Spinner loading={true} /> }];
  } else {
    routes = getUserRoutes(user, accessToken);
  }
  return useRoutes(routes);
}

function getUserRoutes(user, token) {
  if (!user?.id && !token) {
    return LoggedOutRoutes.concat(CatchAllRoutes);
  }
  if ([UserType.SUB_ADMIN, UserType.SUPER_ADMIN].includes(user?.type)) {
    return AdminLoggedInRoutes.concat(CatchAllRoutes);
  }
  if (user?.type === UserType.CUSTOMER_MANAGER) {
    return CustomerManagerLoggedInRoutes.concat(CatchAllRoutes);
  }
  if (user?.type === UserType.CUSTOMER_STAFF) {
    return CustomerStaffLoggedInRoutes.concat(CatchAllRoutes);
  }
  return AdminLoggedInRoutes.concat(CatchAllRoutes);
}


interview's App.tsx return


    <ChakraProvider resetCSS={true} theme={theme}>
      <BrowserRouter>
        <Helmet titleTemplate="" defaultTitle="">
          <meta name="keywords" content="" />
        </Helmet>
        <Router />
      </BrowserRouter>
    </ChakraProvider>
  

interview's routes.tsx

export default function Router() {
  const routes = [
    { path: '/selfcheck', element: <WelcomePage /> },
    { path: '/symptom', element: <SymptomPage /> },
    { path: '/noCombination', element: <NoCombinationPage /> },
    { path: '/:symptomId', element: <LandingPage /> },
    { path: '/start', element: <StartPage /> },
    { path: '/finish', element: <FinishPage /> },
    { path: '/expired', element: <ExpiredPage /> },
    { path: '/completed', element: <CompletedPage /> },
    { path: '/orthopedy', element: <MultisurveyLandingPage /> },
    { path: '/selectSymptom', element: <SelectSymptomPage /> },
    { path: '/surveyList', element: <SurveyListPage /> },
    {
      path: '/questionaire/:symptomId',
      element: <QuestionairePage />,
    },
    { path: '/404/', element: <Page404 /> },
    { path: '*', element: <Navigate to="/404/" replace /> },
  ];

  return useRoutes(routes);
0

There are 0 answers