commit 7f24493901788ce488e8dd2ef43ec1c0841f682f
Author: Lukáš Vlček <vlcek0@students.zcu.cz>
Date:   Sun May 1 15:41:29 2022 +0000

    Re #9582

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..8eec633
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,49 @@
+image: gcr.io/kaniko-project/executor:debug
+
+stages:
+  - Build
+  - Deploy
+
+build-fe:
+  stage: Build
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "main"'
+      when: on_success
+    - if: "$CI_COMMIT_BRANCH == null"
+      when: never
+    - when: manual
+  script:
+    - mkdir -p /kaniko/.docker
+    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+    - /kaniko/executor --cache=true --context "$CI_PROJECT_DIR/webapp" --dockerfile "$CI_PROJECT_DIR/webapp/Dockerfile" --destination $CI_REGISTRY_IMAGE/frontend:$CI_COMMIT_SHA
+
+build-be:
+  stage: Build
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "main"'
+      when: on_success
+    - if: "$CI_COMMIT_BRANCH == null"
+      when: never
+    - when: manual
+  script:
+    - mkdir -p /kaniko/.docker
+    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+    - /kaniko/executor --cache=true --context "$CI_PROJECT_DIR/Backend" --dockerfile "$CI_PROJECT_DIR/Backend/Backend/Dockerfile" --destination $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHA
+
+deploy_webapp_production:
+  stage: Deploy
+  image:
+    name: bitnami/kubectl:latest
+  environment:
+    name: webapp_production
+    url: https://annotation.vlc3k.com
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "main"'
+      when: on_success
+    - if: "$CI_COMMIT_BRANCH == null"
+      when: never
+    - when: manual
+  script:
+    - kubectl --token=${KUBE_TOKEN} --server=${KUBE_SERVER} set image deployment/nio-annotation nio-annotation-frontend=$CI_REGISTRY_IMAGE/frontend:$CI_COMMIT_SHA --namespace=$KUBE_NAMESPACE --record --insecure-skip-tls-verify=true
+    - kubectl --token=${KUBE_TOKEN} --server=${KUBE_SERVER} set image deployment/nio-annotation nio-annotation-backend=$CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHA --namespace=$KUBE_NAMESPACE --record --insecure-skip-tls-verify=true 
+    - kubectl --token=${KUBE_TOKEN} --server=${KUBE_SERVER} --namespace=$KUBE_NAMESPACE --insecure-skip-tls-verify=true rollout status deployment nio-annotation
diff --git a/Backend/.dockerignore b/Backend/.dockerignore
deleted file mode 100644
index 3729ff0..0000000
--- a/Backend/.dockerignore
+++ /dev/null
@@ -1,25 +0,0 @@
-**/.classpath
-**/.dockerignore
-**/.env
-**/.git
-**/.gitignore
-**/.project
-**/.settings
-**/.toolstarget
-**/.vs
-**/.vscode
-**/*.*proj.user
-**/*.dbmdl
-**/*.jfm
-**/azds.yaml
-**/bin
-**/charts
-**/docker-compose*
-**/Dockerfile*
-**/node_modules
-**/npm-debug.log
-**/obj
-**/secrets.dev.yaml
-**/values.dev.yaml
-LICENSE
-README.md
\ No newline at end of file
diff --git a/Backend/Backend/.dockerignore b/Backend/Backend/.dockerignore
new file mode 100644
index 0000000..38bece4
--- /dev/null
+++ b/Backend/Backend/.dockerignore
@@ -0,0 +1,25 @@
+﻿**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/.idea
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/Backend/Backend/Dockerfile b/Backend/Backend/Dockerfile
index c90383c..b1996ca 100644
--- a/Backend/Backend/Dockerfile
+++ b/Backend/Backend/Dockerfile
@@ -1,22 +1,22 @@
-#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
-
-FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
+﻿FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
 WORKDIR /app
 EXPOSE 80
 EXPOSE 443
 
 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
 WORKDIR /src
-COPY ["Backend/Backend.csproj", "Backend/"]
-RUN dotnet restore "Backend/Backend.csproj"
+COPY ["Backend/RestAPI.csproj", "Backend/"]
+COPY ["Core/Core.csproj", "Core/"]
+COPY ["Models/Models.csproj", "Models/"]
+RUN dotnet restore "Backend/RestAPI.csproj"
 COPY . .
 WORKDIR "/src/Backend"
-RUN dotnet build "Backend.csproj" -c Release -o /app/build
+RUN dotnet build "RestAPI.csproj" -c Release -o /app/build
 
 FROM build AS publish
-RUN dotnet publish "Backend.csproj" -c Release -o /app/publish
+RUN dotnet publish "RestAPI.csproj" -c Release -o /app/publish
 
 FROM base AS final
 WORKDIR /app
 COPY --from=publish /app/publish .
-ENTRYPOINT ["dotnet", "Backend.dll"]
\ No newline at end of file
+ENTRYPOINT ["dotnet", "RestAPI.dll"]
diff --git a/Backend/Backend/Program.cs b/Backend/Backend/Program.cs
index 7a12272..7772387 100644
--- a/Backend/Backend/Program.cs
+++ b/Backend/Backend/Program.cs
@@ -80,7 +80,8 @@ app.UseCors(x => x.WithOrigins(
         "http://localhost",
         "https://localhost:3000",
         "http://localhost:3000",
-        "http://localhost:5007")
+        "http://localhost:5007",
+        "https://annotation.vlc3k.com")
     .SetIsOriginAllowedToAllowWildcardSubdomains()
     .AllowAnyHeader()
     .AllowAnyMethod()
diff --git a/Backend/Backend/appsettings.Development.json b/Backend/Backend/appsettings.Development.json
index 0c208ae..63324f2 100644
--- a/Backend/Backend/appsettings.Development.json
+++ b/Backend/Backend/appsettings.Development.json
@@ -4,5 +4,11 @@
       "Default": "Information",
       "Microsoft.AspNetCore": "Warning"
     }
+  },
+  "AllowedHosts": "*",
+  "ConnectionString": "Host=localhost:5432;Database=dbo;Username=myuser;Password=password",  
+  "JwtConfig": {
+    "Secret": "CJgvc9BtwJjnCExzKLB2ndtxERW3YMhqMMPNrBZuTJaskPqRVfZQHRUnbRHH9Ekp5zsaTSeM73vQSKvq48w2u8jTnKUn7Uhk2zBeD9wLz48ssKMppNC9HDTXSk6RWP5PtXfCayJ67mbUWF6aknnteLg4sDKzqYeNYjyw8Jk6",
+    "Issuer": "0x00.server"
   }
-}
+}
\ No newline at end of file
diff --git a/Backend/Backend/appsettings.json b/Backend/Backend/appsettings.json
index 18e2da6..f2d4dc1 100644
--- a/Backend/Backend/appsettings.json
+++ b/Backend/Backend/appsettings.json
@@ -6,9 +6,8 @@
     }
   },
   "AllowedHosts": "*",
-  "ConnectionString": "Host=localhost:5432;Database=dbo;Username=myuser;Password=password",
   "JwtConfig": {
     "Secret": "CJgvc9BtwJjnCExzKLB2ndtxERW3YMhqMMPNrBZuTJaskPqRVfZQHRUnbRHH9Ekp5zsaTSeM73vQSKvq48w2u8jTnKUn7Uhk2zBeD9wLz48ssKMppNC9HDTXSk6RWP5PtXfCayJ67mbUWF6aknnteLg4sDKzqYeNYjyw8Jk6",
     "Issuer": "0x00.server"
   }
-}
+}
\ No newline at end of file
diff --git a/webapp/Dockerfile b/webapp/Dockerfile
new file mode 100644
index 0000000..f973606
--- /dev/null
+++ b/webapp/Dockerfile
@@ -0,0 +1,33 @@
+FROM node:lts as build
+
+ENV NPM_CONFIG_LOGLEVEL warn
+WORKDIR app
+
+COPY package.json package.json
+COPY yarn.lock yarn.lock
+RUN yarn install --pure-lockfile
+
+COPY . .
+
+RUN npx browserslist@latest --update-db
+RUN yarn build
+
+FROM node:lts
+WORKDIR app
+
+COPY package.json package.json
+COPY yarn.lock yarn.lock
+RUN yarn install --pure-lockfile --production
+
+COPY --from=build app/.next .next
+COPY --from=build app/public public
+
+ENV NPM_CONFIG_LOGLEVEL warn
+
+
+ARG BACKEND_URL=$BACKEND_URL
+ENV BACKEND_URL=$BACKEND_URL
+
+EXPOSE 5000
+
+CMD yarn start -p 5000
\ No newline at end of file
diff --git a/webapp/constants.ts b/webapp/constants.ts
new file mode 100644
index 0000000..b0669d5
--- /dev/null
+++ b/webapp/constants.ts
@@ -0,0 +1,13 @@
+import env from '@beam-australia/react-env';
+
+const DEFAULT_BASE_PATH = 'https://localhost:7241';
+
+export function getApiBasePath() {
+    let e = env('BACKEND_URL');
+
+    if (!e) {
+        return DEFAULT_BASE_PATH;
+    } else {
+        return e;
+    }
+}
diff --git a/webapp/controllers.ts b/webapp/controllers.ts
index d78fc9b..c8f84de 100644
--- a/webapp/controllers.ts
+++ b/webapp/controllers.ts
@@ -2,8 +2,9 @@ import { UserApi, AuthApi, DocumentApi, AnnotationApi, TagApi } from './api';
 import { axios } from './utils/axios';
 import { Configuration } from './api';
 import { getToken } from './utils/login';
+import { getApiBasePath } from './constants';
 
-let baseURL = 'https://localhost:7241';
+let baseURL = getApiBasePath();
 
 const defaultConfig: Configuration = new Configuration({
     basePath: baseURL,
diff --git a/webapp/package.json b/webapp/package.json
index 3f4850d..df45022 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -3,15 +3,15 @@
   "version": "0.1.0",
   "private": true,
   "scripts": {
-    "dev": "next dev",
+    "dev": "react-env --env BACKEND_URL -- next dev",
     "build": "next build",
-    "start": "next start",
+    "start": "react-env --prefix ASWI_APP -- next start",
     "lint": "next lint",
     "generate-api": "openapi-generator-cli generate -i http://localhost:5241/swagger/v1/swagger.json -g typescript-axios -o api",
-    "postinstall": "yarn generate-api",
     "deduplicate": "yarn-deduplicate yarn.lock"
   },
   "dependencies": {
+    "@beam-australia/react-env": "^3.1.1",
     "@fortawesome/fontawesome-svg-core": "^6.1.1",
     "@fortawesome/free-solid-svg-icons": "^6.1.1",
     "@fortawesome/react-fontawesome": "^0.1.18",
diff --git a/webapp/pages/_app.tsx b/webapp/pages/_app.tsx
index d9d4c87..d45f973 100644
--- a/webapp/pages/_app.tsx
+++ b/webapp/pages/_app.tsx
@@ -20,8 +20,10 @@ function MyApp({
         <LoggedUserProvider>
             <Head>
                 <link rel="shortcut icon" href={'/favicon.ico'} />
-                <title>Annotation Tool</title>
+                <title>Annotation Tool (AV ČR)</title>
+                <script src="/__ENV.js" defer />
             </Head>
+
             {Component.auth ? (
                 <Auth minRole={Component.auth?.minRole}>
                     <Component {...pageProps} />
diff --git a/webapp/pages/login/index.tsx b/webapp/pages/login/index.tsx
index d115be9..56bcb49 100644
--- a/webapp/pages/login/index.tsx
+++ b/webapp/pages/login/index.tsx
@@ -8,6 +8,7 @@ import { useRouter } from 'next/router';
 import { ShowToast } from '../../utils/alerts';
 import { getTokenData } from '../../utils/login';
 import { LoginResponse } from '../../api';
+import env from '@beam-australia/react-env';
 
 /**
  * Creates a login screen.
@@ -16,6 +17,7 @@ import { LoginResponse } from '../../api';
 function Login() {
     const { login, role } = useContext(LoggedUserContext);
     const router = useRouter();
+
     /**
      * Handles submission a form when its fields were successfully validated.
      * @param values Fields of the login form.
diff --git a/webapp/public/__ENV.js b/webapp/public/__ENV.js
new file mode 100644
index 0000000..4a19ed4
--- /dev/null
+++ b/webapp/public/__ENV.js
@@ -0,0 +1 @@
+window.__ENV = {};
\ No newline at end of file
diff --git a/webapp/utils/axios.ts b/webapp/utils/axios.ts
index f34985b..6e78fa4 100644
--- a/webapp/utils/axios.ts
+++ b/webapp/utils/axios.ts
@@ -4,9 +4,10 @@ import { StatusCodes } from 'http-status-codes';
 import { getToken } from './login';
 import { useContext } from 'react';
 import { LoggedUserContext } from '../contexts/LoggedUserContext';
+import { getApiBasePath } from '../constants';
 
 export const axios = axiosLib.create({
-    baseURL: 'https://localhost:7241',
+    baseURL: getApiBasePath(),
     timeout: 60000,
 });
 
diff --git a/webapp/yarn.lock b/webapp/yarn.lock
index e061d19..d5dad8e 100644
--- a/webapp/yarn.lock
+++ b/webapp/yarn.lock
@@ -51,6 +51,16 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@beam-australia/react-env@^3.1.1":
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/@beam-australia/react-env/-/react-env-3.1.1.tgz#63cb8316861b8fbdb4b9c550a62139cd90675e40"
+  integrity sha512-LdWzgqmu116t9+sOvONyB21bBmI8dm8g8s3KhnJVzCcK93GrdSisuIOtOkQPMYgenmVGTWQwWnbLAgoka/jAFw==
+  dependencies:
+    cross-spawn "^6.0.5"
+    dotenv "^8.0.0"
+    dotenv-expand "^5.1.0"
+    minimist "^1.2.0"
+
 "@ctrl/tinycolor@^3.4.0":
   version "3.4.1"
   resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz#75b4c27948c81e88ccd3a8902047bcd797f38d32"
@@ -778,6 +788,17 @@ core-js-pure@^3.20.2:
   resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.22.0.tgz#0eaa54b6d1f4ebb4d19976bb4916dfad149a3747"
   integrity sha512-ylOC9nVy0ak1N+fPIZj00umoZHgUVqmucklP5RT5N+vJof38klKn8Ze6KGyvchdClvEBr6LcQqJpI216LUMqYA==
 
+cross-spawn@^6.0.5:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+  dependencies:
+    nice-try "^1.0.4"
+    path-key "^2.0.1"
+    semver "^5.5.0"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
 cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -887,6 +908,16 @@ dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1:
     "@babel/runtime" "^7.8.7"
     csstype "^3.0.2"
 
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^8.0.0:
+  version "8.6.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
+  integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
+
 easy-table@1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.1.0.tgz#86f9ab4c102f0371b7297b92a651d5824bc8cb73"
@@ -1849,6 +1880,11 @@ next@12.1.0:
     "@next/swc-win32-ia32-msvc" "12.1.0"
     "@next/swc-win32-x64-msvc" "12.1.0"
 
+nice-try@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
 node-fetch@^2.6.1:
   version "2.6.7"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
@@ -2008,6 +2044,11 @@ path-is-absolute@^1.0.0:
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
   integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
 
+path-key@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
 path-key@^3.1.0:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
@@ -2669,6 +2710,11 @@ scroll-into-view-if-needed@^2.2.25:
   dependencies:
     compute-scroll-into-view "^1.0.17"
 
+semver@^5.5.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
 semver@^6.3.0:
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@@ -2686,6 +2732,13 @@ shallowequal@^1.1.0:
   resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
   integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
 
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+  dependencies:
+    shebang-regex "^1.0.0"
+
 shebang-command@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -2693,6 +2746,11 @@ shebang-command@^2.0.0:
   dependencies:
     shebang-regex "^3.0.0"
 
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
 shebang-regex@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
@@ -3014,6 +3072,13 @@ which-boxed-primitive@^1.0.2:
     is-string "^1.0.5"
     is-symbol "^1.0.3"
 
+which@^1.2.9:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+  dependencies:
+    isexe "^2.0.0"
+
 which@^2.0.1:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
