Configure Status Bar in Progressive Web Apps

Android

<!-- Chrome, Firefox OS and Opera -->
<meta name="theme-color" content="#014473">

iOS

<meta name="apple-mobile-web-app-capable" content="yes">

Default

<meta name="apple-mobile-web-app-status-bar-style" content="default">

Black

<meta name="apple-mobile-web-app-status-bar-style" content="black">

Black-translucent

<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
body {
    background-color: #014473;
}

References
https://dev.to/akshaykumar6/progressive-web-apps-configure-status-bar-16fa

Push Notifications with Angular & Express

Adding the service worker module

ng add @angular/pwa

Generating a VAPID key-pair

yarn global add web-push
web-push generate-vapid-keys --json

Subscribing to push notifications

src/app/app.component.ts

import { Component } from '@angular/core'
import { SwPush } from '@angular/service-worker'
import { PushNotificationService } from './pushNotification.service'

const VAPID_PUBLIC =
  'BNOJyTgwrEwK9lbetRcougxkRgLpPs1DX0YCfA5ZzXu4z9p_Et5EnvMja7MGfCqyFCY4FnFnJVICM4bMUcnrxWg'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
class AppComponent {
  title = 'angular-push-notifications'

  constructor(swPush: SwPush, pushService: PushNotificationService) {
    if (swPush.isEnabled) {
      swPush
        .requestSubscription({
          serverPublicKey: VAPID_PUBLIC,
        })
        .then(subscription => {
          pushService.sendSubscriptionToTheServer(subscription).subscribe()
        })
        .catch(console.error)
    }
  }
}

src/app/pushNotification.service.ts

import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'

const SERVER_URL = 'http://localhost:3000/subscription'

@Injectable()
class PushNotificationService {
  constructor(private http: HttpClient) {}

  public sendSubscriptionToTheServer(subscription: PushSubscription) {
    return this.http.post(SERVER_URL, subscription)
  }
}

Setting up a new Express project

Installing dependencies

yarn add body-parser cors express web-push

Creating an Express server

const express = require('express')
const webpush = require('web-push')
const cors = require('cors')
const bodyParser = require('body-parser')

const PUBLIC_VAPID =
  'BNOJyTgwrEwK9lbetRcougxkRgLpPs1DX0YCfA5ZzXu4z9p_Et5EnvMja7MGfCqyFCY4FnFnJVICM4bMUcnrxWg'
const PRIVATE_VAPID = '_kRzHiscHBIGftfA7IehH9EA3RvBl8SBYhXBAMz6GrI'

const fakeDatabase = []

const app = express()

app.use(cors())
app.use(bodyParser.json())

webpush.setVapidDetails('mailto:[email protected]', PUBLIC_VAPID, PRIVATE_VAPID)

app.post('/subscription', (req, res) => {
  const subscription = req.body
  fakeDatabase.push(subscription)
})

app.post('/sendNotification', (req, res) => {
  const notificationPayload = {
    notification: {
      title: 'New Notification',
      body: 'This is the body of the notification',
      icon: 'assets/icons/icon-512x512.png',
    },
  }

  const promises = []
  fakeDatabase.forEach(subscription => {
    promises.push(
      webpush.sendNotification(
        subscription,
        JSON.stringify(notificationPayload)
      )
    )
  })
  Promise.all(promises).then(() => res.sendStatus(200))
})

app.listen(3000, () => {
  console.log('Server started on port 3000')
})

References
https://malcoded.com/posts/angular-push-notifications/
https://blog.angular-university.io/angular-push-notifications/
https://medium.com/@a.adendrata/push-notifications-with-angular-6-firebase-cloud-massaging-dbfb5fbc0eeb
https://developers.google.com/web/fundamentals/push-notifications/display-a-notification

Taking Command Line Arguments in Bash

Pass arguments through to another program

#!/bin/bash
# print_args.sh
echo "You provided the arguments:" "$@"

# You could pass all arguments to another program like this
# myProgram "$@"

Get the number of arguments passed

#!/bin/bash
echo "You provided $# arguments"

Accessing a specific argument by index

#!/bin/bash
echo "Arg 0: $0"
echo "Arg 1: $1"
echo "Arg 2: $2"

Argument 0 is the name of the script being invoked itself.

Iterating through each argument

#!/bin/bash
for arg in "$@"
do
    echo "$arg"
done

Check arguments for specific value

#!/bin/bash
for arg in "$@"
do
    if [ "$arg" == "--help" ] || [ "$arg" == "-h" ]
    then
        echo "Help argument detected."
    fi
done

References
https://www.devdungeon.com/content/taking-command-line-arguments-bash

Best way to test if a systemd service is running in bash script

systemctl is-active application.service
systemctl is-enabled application.service
systemctl is-active --quiet service

will exit with status zero if service is active, non-zero otherwise, making it ideal for scripts:

systemctl is-active --quiet service && echo Service is running

References
https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
https://unix.stackexchange.com/questions/396630/the-proper-way-to-test-if-a-service-is-running-in-a-script

Check if a File or Directory Exists in Bash

Check if File Exist

FILE=/etc/resolv.conf
if test -f "$FILE"; then
    echo "$FILE exist"
fi
FILE=/etc/resolv.conf
if [ -f "$FILE" ]; then
    echo "$FILE exist"
fi
FILE=/etc/resolv.conf
if [[ -f "$FILE" ]]; then
    echo "$FILE exist"
fi

Check if Directory Exist

FILE=/etc/docker
if [ -d "$FILE" ]; then
    echo "$FILE is a directory"
fi

Check if File does Not Exist

FILE=/etc/docker
if [ ! -f "$FILE" ]; then
    echo "$FILE does not exist"
fi

Check if Multiple Files Exist

FILE=/etc/docker
if [ -f /etc/resolv.conf -a -f /etc/hosts ]; then
    echo "$FILE is a directory"
fi
FILE=/etc/docker
if [ -f /etc/resolv.conf && -f /etc/hosts ]; then
    echo "$FILE is a directory"
fi

File test operators

  • -f FILE – True if the FILE exists and is a regular file (not a directory or device).
  • -G FILE – True if the FILE exists and has the same group as the user running the command.
  • -h FILE – True if the FILE exists and is a symbolic link.
  • -g FILE – True if the FILE exists and has set-group-id (sgid) flag set.
  • -k FILE – True if the FILE exists and has a sticky bit flag set.
  • -L FILE – True if the FILE exists and is a symbolic link.
  • -O FILE – True if the FILE exists and is owned by the user running the command.
  • -p FILE – True if the FILE exists and is a pipe.
  • -r FILE – True if the FILE exists and is readable.
  • -S FILE – True if the FILE exists and is socket.
  • -s FILE – True if the FILE exists and has nonzero size.
  • -u FILE – True if the exists and set-user-id (suid) flag is set.
  • -w FILE – True if the FILE exists and is writable.
  • -x FILE – True if the FILE exists and is executable.

References
https://linuxize.com/post/bash-check-if-file-exists/

Using environment variables in systemd units

Environment directive

Environment="ONE=one" 'TWO=two two'
ExecStart=/bin/echo $ONE $TWO ${TWO}

EnvironmentFile directive

EnvironmentFile similar to Environment directive but reads the environment variables from a text file. The text file should contain new-line-separated variable assignments.This environment file can then be sourced and its variables used
Example file : /run/metadata/coreos

COREOS_DIGITALOCEAN_IPV4_ANCHOR_0=X.X.X.X
COREOS_DIGITALOCEAN_IPV4_PRIVATE_0=X.X.X.X
COREOS_DIGITALOCEAN_HOSTNAME=test.example.com
COREOS_DIGITALOCEAN_IPV4_PUBLIC_0=X.X.X.X
COREOS_DIGITALOCEAN_IPV6_PUBLIC_0=X:X:X:X:X:X:X:X
[Unit]
Requires=coreos-metadata.service
After=coreos-metadata.service

[Service]
EnvironmentFile=/run/metadata/coreos
ExecStart=
ExecStart=/usr/bin/etcd2 \
  --advertise-client-urls=http://${COREOS_DIGITALOCEAN_IPV4_PUBLIC_0}:2379 \
  --initial-advertise-peer-urls=http://${COREOS_DIGITALOCEAN_IPV4_PRIVATE_0}:2380 \
  --listen-client-urls=http://0.0.0.0:2379 \
  --listen-peer-urls=http://${COREOS_DIGITALOCEAN_IPV4_PRIVATE_0}:2380 \
  --initial-cluster=%m=http://${COREOS_DIGITALOCEAN_IPV4_PRIVATE_0}:2380

References
https://stackoverflow.com/questions/37864999/referencing-other-environment-variables-in-systemd
https://coreos.com/os/docs/latest/using-environment-variables-in-systemd-units.html