These are the sources for the CbrnAlert application. This application first consists in a REST API for preparing, running and getting the results of atmospheric dispersion models. The documentation of this API should be available here. The access to this API is restricted, but it's possible to install your proper instance of the application.
This repo also contains the sources for the Single Page Application developed with the Angular framework, which provides a GUI for calling the API.
The API is defined using the Open API specifications. The file with all the API definitions is availabe here. From these definitions, the API routes and data structures are generated in the Angular/Typescript world with ng-openapi-gen and in the Julia/Genie world with OpenAPI.jl and OpenAPI generator.
This section explains how to make the app ready both for development purpose and for production. The installation is meant to be done on Rocky Linux v9 or Centos 7, but it should be easy to adapt on other Linux systems. The main softwares needed for the application is Julia, nodejs, Angular, java and eccodes.
The application needs to retrieve weather forecasts from ECMWF. That means you'll need to have access to licensed datasets from ECMWF. To setup your credentials, you need to go on https://api.ecmwf.int/v1/key to get your API key, and write those lines on a file called .ecmwfapirc
in your $HOME
folder:
{
"url" : "https://api.ecmwf.int/v1",
"key" : "YOUR_API_KEY",
"email" : "YOUR_EMAIL"
}
Due to an issue with Flexpart.jl (see this), the application will only work with Julia v1.7. To easily install Julia v1.7, you can use Juliaup:
curl -fsSL https://install.julialang.org | sh
Accept the basic configuration, restart your shell session, and install the Julia v1.7 binary with:
juliaup add 1.7
Now you should be able to run this julia version with:
julia +1.7
The app needs at least nodejs v16.
On Rocky Linux v9
The registry version of nodejs is should be at least v16, so it can be installed globally:
sudo yum update nodejs
On CentOS 7
On CentOS 7, nodejs is limited to v14, so we'll need to install it locally with nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
- Uncomment NVM related lines in
~/.bashrc
chmod +x ~/.nvm/nvm.sh
source ~/.bashrc
nvm install 16
(v18 won't work because of glibc incompatibilities)
At the moment, Java is needed for the openapi-generator-cli
to work properly:
sudo yum install java-11-openjdk-devel
Unfortunately, the python program for flex_extract is executing the grib_set
command with subprocess.check_call()
. I couldn't find a way to make this command available in the PATH when running the python script. So eccodes
and the grib_*
commands must be available in the path.
sudo yum install eccodes
git clone https://github.com/tcarion/CbrnAlert
Install the Angular command line interface:
npm install @angular/cli
Download and install the required javascript librairies for the frontend:
cd CbrnAlert/frontend
npm install
You might get compats error from npm, if so, retry with npm install --force
Go to the backend folder and open a Julia REPL:
cd CbrnAlert/backend
julia +1.7 --project
Download the required Julia packages. You will have to enter the Pkg REPL by pressing ]
from the Julia REPL. Then, the following command will install all the required registered Julia packages:
(CbrnAlertApp) pkg> instantiate
You might get an error like that:
[ Info: Precompiling FlexExtract [5ad5ba56-8ec2-41bb-8b56-2065914898b5]
ERROR: LoadError: InitError: PyError (PyImport_ImportModule
The Python package ecmwfapi could not be imported by pyimport. Usually this means
that you did not install ecmwfapi in the Python version being used by PyCall.
The simplest way to overcome this is to configure PyCall to use a Julia-specific Python distribution. To do that:
ENV["PYTHON"] = ""
using Pkg
Pkg.build("PyCall")
Then restart julia, and you should be to import FlexExtract without error.
Still in the backend folder, run the repl
scripts:
cd CbrnAlert/backend
./bin/repl
This will open a Julia REPL and setup the Genie environment. This may take a few minutes. Once it's done and you can write in the Julia REPL, you need to set up the database with:
using SearchLight
using SearchLightSQLite
SearchLight.Migration.init()
SearchLight.Migration.status()
SearchLight.Migration.allup()
We need now to generate the keys for encoding and decoding the JSON Web Tokens authentication. Go to the backend folder and write:
openssl genrsa -out config/private.pem 2048
openssl rsa -in config/private.pem -out config/public.pem -outform PEM -pubout
Note: These lines won't work with OpenSSL v1
To get the app up and ready for development, you'll need to follow those few steps.
Go to the backend folder, and run the repl
scripts:
cd CbrnAlert/backend
./bin/repl
And finally start the server:
julia> up()
┌ Info: 2023-10-06 09:42:54
└ Web Server starting at http://127.0.0.1:8000
[ Info: 2023-10-06 09:42:54 Listening on: 127.0.0.1:8000, thread id: 1
Genie.Server.ServersCollection(Task (runnable) @0x00007f821a6e42f0, nothing)
You should see those lines, meaning that a server is listening on localhost:8000
.
If you want, you can also set up a screen
session like this (probably you will have to install screen
with sudo yum install screen
):
screen -S cbrnalert_backend
and then follow the above steps.
If you need to start the server again, you just need to run repl
script and run up()
in the Julia REPL:
./bin/repl
julia> up()
In case you're using screen
you can attach to your screen session with:
screen -R cbrnalert_backend
Go to the frontend folder and run this command:
cd CbrnAlert/frontend
npm run generate:all
This will read the OpenAPI specifications file to generate all the files needed for both the frontend and the backend.
Finally, run the frontend server that will listen to localhost:4200
:
npm run start
At this point you should be able to connect to localhost:4200
and start using the application. If you set everything up on a remote machine, you might need to use ssh
tunnels to redirect the application ports. When using the VS code editor, this is done automatically.
When getting on the app landing page, you're asked for an email and a password. That's because you need to be authenticated to use the application API. To add a new user in the database, you need to get into the Genie environment in the interactive mode:
cd PATH_TO_APP/CbrnAlert/backend
./bin/repl
Then you need to use the Users.add
function:
julia> using CbrnAlertApp.Users
julia> Users.add("USER_EMAIL", "USER_PW", username = "USERNAME")
Now you should be able to log in into the app by using USER_EMAIL
and USER_PW
.
We will deploy the app by using nginx to serve the Angular frontend static files and proxy the API traffic to the Genie backend.
Go to the frontend folder and run:
npm run build
This should create a dist
folder that will be served with nginx.
Go to the backend folder. You should install the screen
software and create a new screen for the backend. This will allow to close the terminal while keeping the Julia server up.
screen -S genie
Then start the Genie server in production mode:
export GENIE_ENV=prod
./bin/server
First install nginx and start it to see if it work (you'll need to do all of this in sudo
mode):
yum install nginx
systemctl start nginx
systemctl enable nginx
The next step is to configure nginx to serve the static files and forward the API traffic to the Genie backend. After installing nginx, you should have the default configuration in the /etc/nginx
directory. You can add the configuration for the application by creating a new configuration file:
touch /etc/nginx/conf.d/YOUR_DOMAIN.conf
where YOUR_DOMAIN
is the domain name you want to deploy. Then writing the following configuration in this file should do the trick:
server {
server_name YOUR_DOMAIN;
root PATH_TO_APP/CbrnAlert/frontend/dist;
location /api {
proxy_pass http://localhost:8000;
}
location /api/docs {
proxy_pass http://localhost:8000/docs;
}
location / {
try_files $uri $uri/ /index.html;
}
}
Now, for having a secured communication with HTTPS, we can use certbot and Let's Encrypt to get the certificates. First, you need to install certbot
with:
sudo yum install certbot python3-certbot-apache
and to get the Let's Encrypt certificates:
sudo certbot --nginx -d YOUR_DOMAIN
certbot
should have modified your configuration file that should look like this now:
server {
server_name YOUR_DOMAIN;
root PATH_TO_APP/CbrnAlert/frontend/dist;
location /api {
proxy_pass http://localhost:8000;
}
location /api/docs {
proxy_pass http://localhost:8000/docs;
}
location / {
try_files $uri $uri/ /index.html;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = YOUR_DOMAIN) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80 default_server;
listen [::]:80 default_server;
server_name YOUR_DOMAIN;
return 404; # managed by Certbot
}
You'll also need to open the port of the server machine to the world:
sudo firewall-cmd --add-port=80/tcp --permanent
sudo firewall-cmd --zone=public --permanent --add-service=https
sudo firewall-cmd --reload
If the machine uses SELinux, you might have a problem with connecting to the backend API. This can be solved with:
setsebool -P httpd_can_network_connect 1
Now, you should get the application landing page when you connect to your domain with a browser. However, it's possible that SELinux blocks the access of nginx to the PATH_TO_APP/CbrnAlert/frontend/dist
folder. This can be solved by changing the context of this folder:
sudo find PATH_TO_APP/CbrnAlert/frontend/dist/ -exec chcon -t httpd_sys_content_t {} \;
After all that, your instance of the application should be accessible on the Internet! Please open an issue if it's not the case. Don't forget that you'll need to add a new user to be able to connect (see the "Adding a user in the database" section).