How to configure Angular for staging environments in the cloud

You can find many tutorials about how to configure angular for different staging environments but what do you do when you have to deploy one existing app on different stages without building the app for every stage? In this tutorial I will show you one solution I used to solve this problem.

To understand the problem in more detail we will have a look at the deployment. We regard a typicall system with a deployment pipeline for all stages.

  • The first step build the app.
  • Secondly a docker container is beeing build.
  • At last this container is delivered to different stages.

So we have one builded app and have to configure it from outside.

1. Create and load configuration

We are creating a new file config.json in the directory “src/app/assets/”

# Example of src/app/assets/config.json
{
  "environment": {
    "name": "$ENV"
  }

}

You can insert any valid json in this file. For values which should be replaced from outside later you have to start with the “$” e.g. $VALUE_FROM_OUTSIDE.

2. Load configuration inside Angular

On the next step we have to load the configuration inside the Angular app. This can be done by adding the following code to the main.js.

As you can see, if a local config file is found it will also be loaded. We need this file to keep our local development working because we don’t have a staging environment if we work on our local machine.
Of course we replace the variables with actual values for development in the local file.

Important: add config-local.json to the .gitignore file

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import {APP_CONFIG, ConfigSettings} from "./app/config";

fetch('/assets/config.json')
.then(config => config.json())
.then((settings: ConfigSettings) => {
  if (settings.environment.name === '$ENV') {
    return fetch('/assets/config-local.json')
    .then(config => config.json())
    .then((settings: ConfigSettings) => { return settings})
  }
  return settings;
})
.then((settings: ConfigSettings) => {

      if (environment.production) {
        enableProdMode();
      }

      platformBrowserDynamic( [{ provide: APP_CONFIG, useValue: settings}])
      .bootstrapModule(AppModule)
      .catch(err => console.error(err));
    }
);

Also we need a model for holding the data and providing a service for accessing them. This will be done i an file called config.ts

import { InjectionToken } from '@angular/core';

export const APP_CONFIG = new InjectionToken<ConfigSettings>('settings from config.json');

export class ConfigSettings {

  environment!: {
    name: string;
  };

}

3. Summary

We now have two config files one for the staging and one for the local development. We also load these files at the start of angular.

Next we want top use the configured values inside our app and secondly we want to replace the staging values with the right values from environment.

4. Using the config-service

To get access to our config values we just have to inject the config service in the constructor. See following example:

import {Inject, Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {APP_CONFIG, ConfigSettings} from "../config";

@Injectable({
  providedIn: 'root'
})
export class TestService {

  constructor(@Inject(APP_CONFIG) private settings: ConfigSettings) {
    console.log('Environment', this.settings.environment.name);
  }


}

5. Staging the app

Usually, if staging is used, the app is getting deployed inside a docker file. If the docker file itself is started directly or inside a cloud does not matter. In this tutorial we will use nginx to deliver our app.

To mention here is that we set the variable CONFIGFILE and copy a start script to the right directory. At the end this copied script will be started. All the magic is done inside the new start script.

FROM docker.repository.muc.lv1871.de/nginxinc/nginx-unprivileged:1.18
#!/bin/sh

## Remove default nginx index page
USER root
RUN rm -rf /usr/share/nginx/html/*
USER nginx
# Copy from the stage1
COPY dist/app /usr/share/nginx/html
ENV CONFIGFILE=/usr/share/nginx/html/assets/config.json
COPY ./start-nginx.sh /usr/bin/start-nginx.sh
# Copy config file
COPY nginx.conf /etc/nginx/nginx.conf

USER root
RUN chmod +x /usr/bin/start-nginx.sh
USER nginx

EXPOSE 8080

ENTRYPOINT ["start-nginx.sh"]

And finally the new start script start-nginx.sh where we replaces the variables inside the config file with these from the environment. So we have one app, one dockerfile and one config-file but with different values depending on the environment variables.

#!/bin/bash
export EXISTING_VARS=$(printenv | awk -F= '{print $1}' | sed 's/^/\$/g' | paste -sd,);
cat $CONFIGFILE | envsubst $EXISTING_VARS | tee $CONFIGFILE
nginx -g 'daemon off;'

6. The end

In this article I showed you how to implement configuration over multiple stages inside an Angular app. If you have questions or hint or anything else feel free to comment or contact me.

CORS Handling in Springboot for Rest

@Configuration
public class CorsConfig {

  @Bean
  CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.applyPermitDefaultValues();
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
  }

}

Here we allow all requests independent from the requesting host. If you just want to allow specific ones than you have to change “/**”.

Make Docker running

It just happens these days that I had to reinstall my server and therefore I also wanted to install docker again. So I started searching for installing docker on ubuntu and found hundreds of sites explaining how to do it. On this sites you have to install prerequisites and after that you have to add the actual repository from docker, than add thes keys etc, etc…

Luckily I found a hint on one site to install docker with just one command. Thes should satisfy nearly 80% of the user whishing to install docker. The other ones feel free to search for the complete guides.

So the easiest way to install docker on a linux maschine is to use this command:

# curl -fsSL https://get.docker.com/ | sh

That’s it 🙂

If you also like to install docker-compose just use following:

# apt update
# apt install docker-compose

Mail forwarding with Postfix

Postfix is a commonly used MTA (Mail Transfer Agent) program that can receive, deliver or route emails. So if you have your own domain and a server then you can use postfix to setup emails on your domain like [email protected]

But having a mail server on your own is a lot of work, you not only have to configure the server to receive all mails, often you use a database for this, you also have to think about spam, virus detection and security.

Forwarding your mails to a third party mail server resolves the hassle mentioned above. The third party server handles all the security issues and spam detection.

So if you just want to use your own mail adresses is much easier to just forward them. In this tutorial you will learn all the steps required to achive this. This tutorial covers debian or ubuntu distributions but it should be the same on other linux distributions

1. Install Postfix

First of all you have to install postfix on your server. This replaces sendmail as mail transfer agent.

# sudo apt update
# sudo apt install postfix

During the installation you will be asked a few questions. Choose “satellite system” and insert your data.

After the installation has completed you can test it with following command:

# sudo service postfix status

The output should be similar like following:

postfix.service - Postfix Mail Transport Agent
     Loaded: loaded (/lib/systemd/system/postfix.service; enabled; vendor prese>
     Active: active (exited) since Sun 2021-10-10 17:41:38 CEST; 19h ago
   Main PID: 5779 (code=exited, status=0/SUCCESS)
      Tasks: 0 (limit: 60)
     Memory: 0B
     CGroup: /system.slice/postfix.service

2. Configure Postfix

For the next stage you have to tell Postfix which mails have to be forwarded to which address. Therefore you have to edit the main.cf in the directory /etc/postfix

# cd /etc/postfix
# nano main.cf

Add the following line if not present at the end of the file. This is needed if you have to use ipv4 for the targeting mail server (e.g. googlemail.com)

inet_protocols = ipv4

Now insert the next two lines at the end and safe the file. You can add more domains by seperating them with a blank space

virtual_alias_domains = yourdomain1.com yourdomain2.com
virtual_alias_maps = hash:/etc/postfix/virtual

Create a new file called “virtual” and insert the mail addresses you like to forward.
The first email is the address on which postfix shall receive emails, and the second is the address where postfix would forward the emails.

  • To forward to two email addresses
[email protected] [email protected]
[email protected] [email protected]
  • The mail can be forwarded to multiple destinations
[email protected] [email protected] [email protected]
  • To catch and forward emails to any address for a given domain, use the following notation
@yourdomain.com [email protected]

Now you must create the virtual database for Postfix. This easy step is done with the command:

# postmap /etc/postfix/virtual

2. Restart and test Postfix

At the end you have to tell Postfix to stop and start again. I experienced some problems with just reloading Postfix thats why I prefer stoping and starting the mail agent.

# service postfix stop
# service postfix start

At last make a test and send a test email to check if forwarding works correct.
If you encounter errors you can check whats going on in /var/log/mail. log and /var/log/mail.err

Please let me know if everything was working for you or if you encountered problems.