肇鑫的技术博客

业精于勤,荒于嬉

Vapor with SQLite

When using SQLite driver with Vapor, we have to install another package, or the project won't compile.

$ sudo apt install libsqlite3-dev

Vapor Behind NGINX in Ubuntu 20.04

  1. Install developing packages.

How To Install Vapor On A Virtual Machine

  1. Install NGINX
  2. Install Swift
  3. Install Vapor Toolbox
  4. Check RAM
  5. Create Vapor Sample Project
  6. Set Vapor Proxy Behind NGINX
  7. Install SuperVisor

My server was 1GB RAM VPS in Ubuntu 20.04 LTS. And I developed vapor on my iMac 5K. So the first thing I did was to create a Linux client.

I prefer VMWare Fusion. As it was free for open source use. I tried VirtualBox, it ran slow with iMac 5K. Parallels Desktop was also good. But it was too expensive.

After all, now you should have a working Ubuntu 20.04.

Install developing packages.

 $ sudo apt update
 $ sudo apt install \
          binutils \
          git \
          gnupg2 \
          libc6-dev \
          libcurl4 \
          libedit2 \
          libgcc-9-dev \
          libpython2.7 \
          libsqlite3-0 \
          libstdc++-9-dev \
          libxml2 \
          libz3-dev \
          pkg-config \
          tzdata \
          zlib1g-dev

Install NGINX

$ sudo apt install nginx

Open Firefox with "localhost".

Screenshot from 2021-12-02 18-48-53

Install Swift

Download the latest release Swift Toolchain for Ubuntu 20.04.

$ cd Downloads/
$ wget https://download.swift.org/swift-5.5.1-release/ubuntu2004/swift-5.5.1-RELEASE/swift-5.5.1-RELEASE-ubuntu20.04.tar.gz
$ tar xzf swift-5.5.1-RELEASE-ubuntu20.04.tar.gz
$ vi ~/.bashrc

Append

export PATH=/home/zhaoxin/Downloads/swift-5.5.1-RELEASE-ubuntu20.04/usr/bin:”${PATH}”

Save and Quit。

Quit Terminal and Open it again. Type

$ swift --version
Swift version 5.5.1 (swift-5.5.1-RELEASE)
Target: x86_64-unknown-linux-gnu

Install Vapor Toolbox

$ cd ~/Downloads
$ git clone https://github.com/vapor/toolbox.git
$ cd toolbox/
$ git checkout 18.3.3
$ swift build -c release --disable-sandbox --enable-test-discovery
$ sudo mv .build/release/vapor /usr/local/bin

Check RAM

When compile, Vapor needs at least 2GB RAM. So for some VPS with only 1GB RAM, you will get fatalError when compile. You can check RAM with command

$ free -h
              total        used        free      shared  buff/cache   available
Mem:          3.8Gi       1.2Gi       447Mi        14Mi       2.1Gi       2.3Gi
Swap:         923Mi       1.0Mi       921Mi

Above is my VMWare Fusion client. However, for you VPS, you may get a result as below

$ free -h
              total        used        free      shared  buff/cache   available
Mem:          981Mi       215Mi        95Mi       2.0Mi       670Mi       575Mi
Swap:            0B          0B          0B

If that is your case, you must create a swap or you get your Vapor project compiled.

Create Swap File

How To Add Swap Space on Ubuntu 20.04

Vapor Hello Sample Won’t Compile

$ sudo swapon --show

You may get nothing shown as there was no swap for you VPS. Check you disk free space.

df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            447M     0  447M   0% /dev
tmpfs            99M  944K   98M   1% /run
/dev/vda1        24G   12G   11G  51% /
tmpfs           491M     0  491M   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           491M     0  491M   0% /sys/fs/cgroup
/dev/loop0       43M   43M     0 100% /snap/certbot/1514
/dev/loop1       43M   43M     0 100% /snap/certbot/1582
/dev/loop2      100M  100M     0 100% /snap/core/11798
/dev/loop3      100M  100M     0 100% /snap/core/11993
/dev/loop4       62M   62M     0 100% /snap/core20/1169
/dev/loop5       62M   62M     0 100% /snap/core20/1242
tmpfs            99M     0   99M   0% /run/user/0

Look at "/dev/vda1 24G 12G 11G 51% /", That meant you had 11G free space.

Create swap file.

$ sudo fallocate -l 1G /swapfile
$ ls -lh /swapfile
-rw-r--r-- 1 root root 1.0G Apr 25 11:14 /swapfile

Enable swap file

$ sudo chmod 600 /swapfile
$ ls -lh /swapfile
-rw------- 1 root root 1.0G Apr 25 11:14 /swapfile
$ sudo mkswap /swapfile
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=6e965805-2ab9-450f-aed6-577e74089dbf
$ sudo swapon /swapfile
$ sudo swapon --show
NAME      TYPE  SIZE USED PRIO
/swapfile file 1024M   0B   -2
$ free - h
              total        used        free      shared  buff/cache   available
Mem:        1004716      166880      147152        2724      690684      643212
Swap:       1048572        5940     1042632

Save Swap File

$ sudo cp /etc/fstab /etc/fstab.bak
$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Create Vapor Sample Project

$ cd
$ mkdir vapor
$ cd vapor
$ git config --global user.email "you@email.com"
$ git config --global user.name "Your Name"
$ vapor new hello -n
$ cd hello/
$ swift run
...
[1686/1686] Build complete!
[ NOTICE ] Server starting on http://127.0.0.1:8080

Open Firefox and type "localhost:8080".
Screenshot from 2021-12-02 19-53-32

Set Vapor Proxy Behind NGINX

403 forbidden behind nginx

$ cd /etc/nginx/sites-available/
$ sudo cp default default.bak
$ sudo rm default
$ sudo vi default

Copy and paste below to default and save.

server {
        listen 80 default_server;
        listen [::]:80 default_server;

root /var/www/html;

index index.html index.htm index.nginx-debian.html;

server_name _;
        try_files $uri @proxy;
        location @proxy {
                proxy_pass http://127.0.0.1:8080;
                proxy_pass_header Server;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass_header Server;
                proxy_connect_timeout 3s;
                proxy_read_timeout 10s;
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                #try_files $uri $uri/ =404;
        }
}

Restart NGINX.

sudo systemctl restart nginx

How To Install Vapor On A Virtual Machine

Install SuperVisor

$ sudo apt install supervisor
$ cd /etc/supervisor/conf.d/
$ sudo vi hello.conf

Copy and paste below code and save.

[program:hello]
command=/home/zhaoxin/vapor/hello/.build/debug/Run serve --env production
directory=/home/zhaoxin/vapor/hello
user=zhaoxin
stdout_logfile=/var/log/supervisor/%(program_name)-stdout.log
stderr_logfile=/var/log/supervisor/%(program_name)-stderr.log
$ sudo supervisorctl reread
$ sudo supervisorctl add hello
$ sudo supervisorctl start hello

Supervisor

A Little Research on How PasscodeLock Works

PasscodeLock is the sample project of AppLocker. I took some hours and found how it worked.

Let The App Run

The original project used Carthage to manage the framework. It depended Valet. However, the latest Valet is 4.x but PasscodeLock depends 3.x. So we need to specify the version.

pod 'Valet' , '< 4.0'

I switched Carthage to CocoaPods. So I also had to remove the Carthage part in building phase.

Core

sc

// AppALConstants.swift
// MARK: - Keyboard
@IBAction func keyboardPressed(_ sender: UIButton) {
    switch sender.tag {
    case ALConstants.button.delete.rawValue:
        drawing(isNeedClear: true)
    case ALConstants.button.cancel.rawValue:
        clearView()
        dismiss(animated: true) {
            self.onSuccessfulDismiss?(nil)
        }
    default:
        drawing(isNeedClear: false, tag: sender.tag)
    }
}

private func drawing(isNeedClear: Bool, tag: Int? = nil) { // Fill or cancel fill for indicators
    let results = pinIndicators.filter { $0.isNeedClear == isNeedClear }
    let pinView = isNeedClear ? results.last : results.first
    pinView?.isNeedClear = !isNeedClear
    
    UIView.animate(withDuration: ALConstants.duration, animations: {
        pinView?.backgroundColor = isNeedClear ? .clear : .white
    }) { _ in
        isNeedClear ? self.pin = String(self.pin.dropLast()) : self.pincodeChecker(tag ?? 0)
    }
}

private func pincodeChecker(_ pinNumber: Int) {
    if pin.count < ALConstants.maxPinLength {
        pin.append("\(pinNumber)")
        if pin.count == ALConstants.maxPinLength {
            switch mode {
            case .create:
                createModeAction()
            case .change:
                changeModeAction()
            case .deactive:
                deactiveModeAction()
            case .validate:
                validateModeAction()
            }
        }
    }
}

When a user presses a key, private func drawing(isNeedClear: Bool, tag: Int? = nil) is called.

  1. pinView is the current indicator. It is set to turn white.
  2. then func pincodeChecker(_ pinNumber: Int) is called, the result is pin.
  3. when pin.count is 4, the indicators are cleared.