Testing Development & Production modes side by side

This document is specific to React based components in Gawati. Specifically gawati-portal-ui and gawati-editor-ui.

The standard procedure for development testing is to run npm start and the React application starts up on port 3000, for production testing we run npm run build and the built output is provided in the build folder, from where it is served via Apache httpd (explained below see gawati.local : front-end (for production testing). For the application routing we use React Router, which intercepts client URL requests and channels them appropriately, however this causes problems when we proxy the data back-end on the same domain as the main system.

For instance, if the production site is running on http://www.domain.com , and the data back-end is being reverse proxied on http://www.domain.com/gwd/* and the media files (AKN pdf files) are being reverse proxied on http://www.domain.com/akn/* – in this scenario direct browser requests for PDF files will with a 404 because React Router detects that there is no specifed route to the /akn path.

For this reason, we recommend running the data-backend and media services on distinct domains or sub-domains.

For local production and development testing we have specified a setup below.

Specify multiple hosts

Specify distinct hosts for the front-end, back-end services and the media, all mapped to the localhost IP. Below we have specified 3 distinct hosts. In Linux the hosts file is typically /etc/hosts on Windows you will need to open C:/Windows/System32/drivers/etc/hosts, and add the settings there.

1
2
3
4
127.0.0.1       gawati.local  # front-end
127.0.0.1       data.local    # data services
127.0.0.1       media.local   # media files
127.0.0.1       auth.local    # auth / keycloak if you want be a purist

Then specify in your apache vhosts conf file, configurations for each host as follows.

gawati.local : front-end (for production testing)

This is relevant only for production testing. When you run npm start in development mode, the front-end runs on localhost:3000 and these settings are ignored. But when you build the production system using `` npm run build `` the production system can be accessed via the http://gawati.local url.

data.local : services back-end (for development & production testing)

We proxy the eXist-db services on the data.local host. Data is provided by different services running on different ports, data.local provides a single point proxy to access all these data services. Each data service is mapped to a different proxy root path:

The full apache configuration is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<VirtualHost 127.0.0.1:80>
    ProxyRequests off
    ServerName data.local
    ServerAlias data.local

    ### CORS BEGIN
    # Always set these headers.
    <IfModule mod_headers.c>
        SetEnvIfNoCase Origin "https?://(www\.)?(localhost|gawati\.local)(:\d+)?$" AccessControlAllowOrigin=$0
        Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
    </IfModule>
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
    Header always set Access-Control-Max-Age "1000"
    Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token"

    # Added a rewrite to respond with a 200 SUCCESS on every OPTIONS request.
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
    ### CORS END

    # gawati-data
    <Location ~ "/gwd/(.*)">
    AddType text/cache-manifest .appcache
    ProxyPassMatch  "http://localhost:8080/exist/restxq/gw/$1"
    ProxyPassReverse "http://localhost:8080/exist/restxq/gw/$1"
    ProxyPassReverseCookiePath /exist /
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    </Location>

    # gawati-portal-fe
    <Location ~ "/gwp/(.*)">
    AddType text/cache-manifest .appcache
    ProxyPassMatch  "http://localhost:9001/gwp/$1"
    ProxyPassReverse "http://localhost:9001/gwp/$1"
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    </Location>

    # gawati-client-data
    <Location ~ "/gwdc/(.*)">
    AddType text/cache-manifest .appcache
    ProxyPassMatch  "http://localhost:8080/exist/restxq/gwdc/$1"
    ProxyPassReverse "http://localhost:8080/exist/restxq/gwdc/$1"
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    </Location>

    # gawati-profiles-fe
    <Location ~ "/gwu/(.*)">
    AddType text/cache-manifest .appcache
    ProxyPassMatch  "http://localhost:9003/gwu/$1"
    ProxyPassReverse "http://localhost:9003/gwu/$1"
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    </Location>

    # gawati-editor-fe
    <Location ~ "/gwc/(.*)">
    AddType text/cache-manifest .appcache
    ProxyPassMatch  "http://localhost:9002/gwc/$1"
    ProxyPassReverse "http://localhost:9002/gwc/$1"
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    </Location>

    # gawati-xslt-transformer
    <Location ~ "/gwx/(.*)">
    AddType text/cache-manifest .appcache
    ProxyPassMatch  "http://localhost:9005/Gawati/xml/xslt/$1"
    ProxyPassReverse "http://localhost:9005/Gawati/xml/xslt/$1"
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    </Location>

    # gawati-viewer-service
    <Location ~ "/gwv/(.*)">
    AddType text/cache-manifest .appcache
    ProxyPassMatch  "http://localhost:9005/gwv/$1"
    ProxyPassReverse "http://localhost:9005/gwv/$1"
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    </Location>

</VirtualHost>

Note

Note the use of the CORS header in data.local, specifically:

<IfModule mod_headers.c>
    SetEnvIfNoCase Origin "https?://(www\.)?(localhost|gawati\.local)(:\d+)?$" AccessControlAllowOrigin=$0
    Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</IfModule>

Which allows requests coming in from both localhost:3000 and gawati.local hosts.

media.local : media files (for development & production testing)

The Akoma Ntoso PDF and thumbnail files are served via this host.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<VirtualHost 127.0.0.1:80>
    ProxyRequests off
    ServerName media.local
    ServerAlias media.local

    ### CORS BEGIN
    # Always set these headers.
    <IfModule mod_headers.c>
    SetEnvIfNoCase Origin "https?://(www\.)?(localhost|gawati\.local)(:\d+)?$" AccessControlAllowOrigin=$0
    Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
    </IfModule>
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
    Header always set Access-Control-Max-Age "1000"
    Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token"

    # Added a rewrite to respond with a 200 SUCCESS on every OPTIONS request.
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
    ### CORS END

    Alias /akn "/path/to/akn"
    <Directory "/path/to/akn">
        DirectoryIndex "index.html"
        Require all granted
        AllowOverride All
    </Directory>

</VirtualHost>

Set the PROXY Params in the front-end

In public/index.html set the 2 proxy parameters as below.

<script>
  gawati = {
    GAWATI_PROXY: "http://data.local",
    GAWATI_DOCUMENT_SERVER: "http://media.local"
  };
</script>

Set the PROXY Param in package.json

This parameter is used only in development mode, set it to http://data.local

{
"name": "gawati-portal-ui",
"version": "2.0.22",
"private": true,
"proxy": "http://data.local",  <<<<<<
....
}

With all this set-up restart Apache HTTPD and you are all set to use both development and production mode testing side by side.

Apache configs explained

Apache configuration for portal-ui

For example: if you want to serve the portal from the /ui virtual directory of your domain, and your files are located in /home/web/apps/gawati-portal-ui, then use the following apache configuration –

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  Alias /ui "/home/web/apps/gawati-portal-ui"
  <Directory "/home/web/apps/gawati-portal-ui">
    DirectoryIndex "index.html"
    Require all granted
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>

  ##
  ## added for portal-ui v 2.0.10
  ##

  Alias /locales "/home/web/apps/gawati-portal-ui/locales"
  <Directory "/home/web/apps/gawati-portal-ui/locales">
    Require all granted
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>

Apache configuration for profiles-ui

For example: if you want to serve the portal from the /profiles virtual directory of your domain, and your files are located in /home/web/apps/gawati-profiles-ui, then use the following apache configuration –

1
2
3
4
5
6
7
8
  Alias /profiles "/home/web/apps/gawati-profiles-ui"
  <Directory "/home/web/apps/gawati-profiles-ui">
    DirectoryIndex "index.html"
    Require all granted
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>

Apache configuration for binary data

The Apache configuration will allow accessing gawati data server services over a web-browser using the URL:

To do this, open the httpd.conf (or equivalent) file of your apache installation and add the following:

1
2
3
4
5
6
7
8
  Alias /akn "/home/data/akn_pdf"
  <Directory "/home/data/akn_pdf">
    Require all granted
    Options Includes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>

Apache configuration for gawati data services

The services provided by Gawati Data to access the XML documents in Gawati are not directly exposed to the outside, they are reverse proxied using Apache. The full configuration of apache config entries is provided below:

1
2
3
4
5
6
7
8
<Location ~ "/gwd/(.*)">
  AddType text/cache-manifest .appcache
  ProxyPassMatch  "http://localhost:8080/exist/restxq/gw/$1"
  ProxyPassReverse "http://localhost:8080/exist/restxq/gw/$1"
  ProxyPassReverseCookiePath /exist /
  SetEnv force-proxy-request-1.0 1
  SetEnv proxy-nokeepalive 1
</Location>
The above assumes:
  • eXist-db is running on port 8080 (if that is not the case in your installation change it appropriately in line 16 and 17)

Note

On Windows the Apache Alias directory path need to use the back slash instead of the standard windows forward slash. For e.g. if the templates are in: d:\code\gawati-templates then the path in the Apache configuration should be: d:/code/gawati-templates

Apache configuration for portal server services

Add the following Apache entry for it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<Location ~ "/gwp/(.*)">
  AddType text/cache-manifest .appcache
  ProxyPassMatch  "http://localhost:9001/gwp/$1"
  ProxyPassReverse "http://localhost:9001/gwp/$1"
  SetEnv force-proxy-request-1.0 1
  SetEnv proxy-nokeepalive 1
</Location>

# gawati-viewer-service
<Location ~ "/gwv/(.*)">
  AddType text/cache-manifest .appcache
  ProxyPassMatch  "http://localhost:9005/gwv/$1"
  ProxyPassReverse "http://localhost:9005/gwv/$1"
  SetEnv force-proxy-request-1.0 1
  SetEnv proxy-nokeepalive 1
</Location>

Apache configuration for gawati client

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# for gawati-client-data
<Location ~ "/gwdc/(.*)">
AddType text/cache-manifest .appcache
ProxyPassMatch  "http://localhost:8080/exist/restxq/gwdc/$1"
ProxyPassReverse "http://localhost:8080/exist/restxq/gwdc/$1"
SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
</Location>

# for gawati-client-server
<Location ~ "/gwc/(.*)">
AddType text/cache-manifest .appcache
ProxyPassMatch  "http://localhost:9002/gwc/$1"
ProxyPassReverse "http://localhost:9002/gwc/$1"
SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
</Location>

FAQ

EPERM errors when running npm run build on windows

You attempt to build on windows and you get EPERM errors related to symlinking. This happens because on Windows you have to explicitly allow your user symlink permission prior to running npm run build. You can remove the permission after completion.

The permission is configured in Group Policies of local Computer at Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment . Please see here for more details click here.