Publishing Traduora over sub path and a reverse proxy

I had a task in which I built an analytics dashboard as part of a supply chain management system. I wrote our localization for English language because it is almost universal. This project started as a prototype so I wrote hard coded labels and every display (my bad 🤯😈). The project was successful and our customers wanted to use it in production soon.

In this project there are a lot of translations that comes from fields in databases so I made a research for translations management platforms and I found Traduora. This project looks promising and was exactly what I was looking for and I integrated with ReactJS using react-i18next and I worked like a charm in development environment, however deploying Traduora server in production was another story, but because of our requirements and limitations.

We needed to deploy Traduora under a subpath in our domain due to routing policies (using NGINX); also we need to deploy Traduora using Docker only. Some changes in source code are necessary in order to deploy our configuration. In the following steps I describe the changes to make Traduora work under a subpath. I used the following version of source code:

Merge: 4050b93 70f52a2
Author: Anthony N. Simon <anthonynsimon@users.noreply.github.com>
Date: Mon Dec 9 17:56:53 2019 +0100
Merge pull request #89 from traduora/feature/translation-statsTranslation stats

User variables

Firstly I am going to define some variables, so you can replace with yours. I want to access Traduora in the endpoint https://api.awesome-site.com/localization/traduora/ (This is not real by the way, just for explanation purposes 😊), so this is my configuration:

✅{{domain}}✅ = api.awesome-site.com
✅{{basePath}}✅ = /localization
✅{{subPath}}✅ = /traduora
✅{{url}}✅ = /localization/traduora

✅{{prefix}}✅ = localization/traduora
✅{{dockerUser}}✅ = johndoe
✅{{traduoraDbData}}✅ = /docker/data/traduora/db/
✅{{gitPath}}✅ = /home/johndoe/code/traduora

Source code changes

First step is to clone the repo as explained before.

cd ✅{{gitPath}}✅ 
git clone https://github.com/traduora/traduora.git .
git reset --hard d0c4ed548278d41a5043381fa436bfbb3d0ed478

git diff

This is my diff with changes in source code to make traduora works (Replace variables with yours):

diff --git a/api/src/app.module.ts b/api/src/app.module.ts
index f5ccdf8..fd1e1fd 100644
--- a/api/src/app.module.ts
+++ b/api/src/app.module.ts
@@ -98,7 +98,7 @@ export const addPipesAndFilters = (app: NestExpressApplication) => {
}),
);

- app.useStaticAssets(config.publicDir, { index: false, redirect: false });
+ app.useStaticAssets(config.publicDir, { index: false, redirect: false, prefix: "✅{{url}}✅" });

app.setBaseViewsDir('src/templates');

diff --git a/api/src/main.ts b/api/src/main.ts
index 7e20dfb..4149b59 100644
--- a/api/src/main.ts
+++ b/api/src/main.ts
@@ -24,6 +24,7 @@ process.on('SIGINT', async () => {
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule, new ExpressAdapter());
addPipesAndFilters(app);
+ app.setGlobalPrefix('✅{{prefix}}✅');
closables.push(app);

// Run migrations
@@ -45,7 +46,7 @@ async function bootstrap() {
'Source code: https://github.com/traduora/traduora',
)
.setVersion(version)
- .setBasePath('/')
+ .setBasePath('✅{{url}}✅')
.addOAuth2('password', '/api/v1/auth/token', '/api/v1/auth/token')
.setSchemes('http', 'https')
.build();
diff --git a/bin/build-docker.sh b/bin/build-docker.sh
index 9c410a1..9697821 100755
--- a/bin/build-docker.sh
+++ b/bin/build-docker.sh
@@ -2,7 +2,7 @@

set -e

-DOCKER_REPO=traduora/traduora
+DOCKER_REPO=✅{{dockerUser}}✅/traduora

docker build -t "$DOCKER_REPO:latest" .

diff --git a/webapp/src/environments/environment.prod.ts b/webapp/src/environments/environment.prod.ts
index 3048ccf..afd977e 100644
--- a/webapp/src/environments/environment.prod.ts
+++ b/webapp/src/environments/environment.prod.ts
@@ -2,6 +2,6 @@ export const environment = {
production: true,
title: 'traduora',
homepage: 'https://traduora.com',
- apiEndpoint: `${window.location.protocol}//${window.location.host}/api/v1`,
+ apiEndpoint: `${window.location.protocol}//${window.location.host}✅{{url}}✅api/v1`,
inviteOnly: false,
};
diff --git a/webapp/src/index.html b/webapp/src/index.html
index 47b17d1..379bf7b 100644
--- a/webapp/src/index.html
+++ b/webapp/src/index.html
@@ -4,7 +4,7 @@
<head>
<meta charset="utf-8">
<title>traduora | translation platform for teams</title>
- <base href="/">
+ <base href="✅{{url}}✅">

<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" type="image/x-icon" href="favicon.ico">
@@ -33,4 +33,4 @@
<app-root></app-root>
</body>

-</html>
\ No newline at end of file
+</html>

Building a docker image

Now is time to build our custom image, in order to do it. We need to run the following commands:

cd ✅{{gitPath}}✅ 
bash bin/build-docker.sh

Wait until the image is built, and then continue to the next steps

Deploying the site

It is time to deploy our Traduora api under a subpath. Firstly we need to add a MySQL service and Traduora service. We need to reference our custom image, also we must specify our domain, using docker-compose the following configuration was used:

docker-compose.yml

version: "3"
services:
# ... OTHER SERVICES FOR EXAMPLE NGINX .... mysqldb:
image: mysql:5.7
container_name: "mysqldb"
ports:
- "3306:3306"
environment:
- MYSQL_DATABASE=tr_dev
- MYSQL_USER=root
- MYSQL_ALLOW_EMPTY_PASSWORD=true
volumes:
- "✅{{traduoraDbData}}✅:/var/lib/mysql"
traduora:
image: ✅{{dockerUser}}✅/traduora:latest
container_name: "traduora"
ports:
- "8080:8080"
environment:
- TR_VIRTUAL_HOST=✅{{domain}}
- TR_ACCESS_LOGS_ENABLED=true
- TR_DB_HOST=mysqldb
- TR_CORS_ENABLED=true
depends_on:
- mysqldb

Finally the next configuration was added to NGINX conf. Please note the variables defined before and also note the CORS configuration in order to use this server in another site:

nginx.conf

location ✅{{basePath}}✅ {
location ~ ✅{{subPath}}✅ {
rewrite ✅{{subPath}}✅/(.*)$ ✅{{url}}✅/$1 break;
proxy_pass http://traduora:8080;
proxy_redirect http://traduora:8080/ $scheme://$host✅{{subPath}}✅/;
add_header Access-Control-Allow-Methods POST,GET,PUT,DELETE,OPTIONS;
add_header Access-Control-Allow-Headers Authorization,Content-Type,Origin,Lang,Accept,X-Requested-With;
add_header Access-Control-Allow-Credentials true;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
client_max_body_size 100M;
}
}

Test

Now let’s visit https://api.awesome-site.com/localization/traduora/ (Please note the slash at the end). This page is displayed, let’s click “Go back home

This page is displayed:

Create an account, by clicking “Sign up” and you are going to be redirected to projects home page:

And that’s all, also your api is enabled at path: https://api.awesome-site.com/localization/traduora/api/v1/.

😥 Why does it always show 404 page?

I think it is because a configuration in source code (app.module.ts) and a conflict with my configuration in nginx.conf, however it only happens in the web application and we can click “Go back home” and navigation works again.

Disclaimer

Please review your nginx configuration, specifically any security issues. I am not an expert, maybe (very likely) this configuration has any security flaw. Suggestions are welcome. Let’s keep learning!

Passionated developer