Setting up MongoDB on Ubuntu

(MongoDB: Install MongoDB Community Edition on Ubuntu)
(Linode: Install MongoDB on Ubuntu 16.04 (Xenial))
(Digital Ocean: How To Install MongoDB on Ubuntu 14.04)

Before You Begin

  1. Check for updates
    sudo apt-get update && sudo apt-get upgrade

Add the MongoDB repository

  1. Import the MongoDB public GPG key for package signing
    sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
  2. Add the MongoDB repository to your sources.list.d directory
    echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
  3. Update your repositories. This allows apt to read from the newly added MongoDB repo
    sudo apt-get update
  4. Install MongoDB
    sudo apt-get install mongodb-org
  5. Enable auto-start on reboot
    sudo systemctl enable mongod.service
  6. Open a port in ufw
    sudo ufw allow 27017

Running MongoDB

  1. Start MongoDB
    sudo service mongod start
  2. Verify MongoDB started
    sudo nano /var/log/mongodb/mongod.log
    and look for
    [initandlisten] waiting for connections on port 27017
  3. Stop MongoDB
    sudo service mongod stop
  4. Restart MongoDB
    sudo service mongod restart

Setup MongoDB users

  1. Open MongoDB
    mongo --port 27017
  2. Create an admin database
    use admin
  3. Add a mongodbAdminUsername and mongodbAdminPassword (letters and numbers, no funny characters)
    db.createUser({user: "mongodbAdminUsername", pwd: "mongodbAdminPassword", roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]})
  4. Exit
    exit
  5. Restart MongoDB
    sudo service mongod restart

Mini series

  1. Setting up a server at Linode
  2. Install Let’s Encrypt to Create SSL Certificates on Ubuntu
  3. Setting up MongoDB on Ubuntu
  4. Setting up nginx on Ubuntu for parse-server
  5. Setting up parse-server on Ubuntu

Setting up a server at Linode

Getting started

(Linode: Getting Started)
(Digital Ocean: Additional Recommended Steps for New Ubuntu 14.04 Servers)

  1. Sign up
  2. Add a Linode
    • Select
      Linode 2048
    • Select
      Location
    • Click
      Add this Linode!
  3. Click linodexxxxxxx
  4. Click Deploy an Image
  5. Cofigure deployment
    • Image Ubuntu 16.04 LTS
    • Deployment Disk Size max
    • Swap Disk 512MB
    • Root Password ••••••••
    • Click Deploy
  6. Boot
    • Click Boot
    • Confirm boot
  7. SSH
    • Click Remote Access tab
    • Click SSH Access link ssh root@xxx.xxx.xxx.xxx
    • Click Allow to open in Terminal
    • Terminal should open (first run has some additional Allows and yeses)
    • Enter password in Terminal
  8. Update software via ssh apt-get update && apt-get upgrade
  9. Choose a newhostname and set it hostnamectl set-hostname newhostname
  10. Update /etc/hosts
    • nano /etc/hosts
    • Add IP address and newhostname separated by a tab below:
      127.0.0.1 localhost
      127.0.1.1 ubuntu.members.linode.com

      xxx.xxx.xxx.xxx newhostname
    • Exit ^X
    • Save y
    • File Name to Write: /etc/hosts ↩︎
  11. Setup timezone (I like UTC)
    • dpkg-reconfigure tzdata
    • Arrow around, ↩︎, ⎋
  12. Setup NTP network time synchronisation
    • sudo apt-get install ntp

Securing the server

(Linode: Securing Your Server)

  1. Login
    • ssh root@xxx.xxx.xxx.xxx
    • Password
  2. Add a limited user account
    • adduser example_user
    • Password
    • Retype password
    • Enter Full name
    • Room number
    • Work phone number
    • Home phone number
    • Other
    • Is the info correct? Y
  3. Add user to admin group
    • adduser example_user sudo
  4. Logout exit
  5. Login again, but as the new user
    • ssh example_user@xxx.xxx.xxx.xxx
    • Password
  6. Harden SSH access
    • Create an ssh directory on the Linode machine
      mkdir -p ~/.ssh && sudo chmod -R 700 ~/.ssh/
    • (open a new Terminal window)
    • Check if you have an RSA key-pair on your local Mac
      ls ~/.ssh/id_rsa*
      If NOT,
      ssh-keygen -b 4096
    • Copy the public key from your Mac to the Linode machine
      scp ~/.ssh/id_rsa.pub example_user@xxx.xxx.xxx.xxx:~/.ssh/authorized_keys
    • Swap back to linode terminal
    • Set permissions on the Linode machine
      sudo chmod 700 -R ~/.ssh && chmod 600 ~/.ssh/authorized_keys
    • Logout
      exit
    • Login again, as the new user
      ssh example_user@xxx.xxx.xxx.xxx
      (no password required)
  7. Edit SSH Daemon Options
    • sudo nano /etc/ssh/sshd_config
    • Password
    • Disallow root logins over SSH: change PermitRootLogin yes to PermitRootLogin no
    • Maybe disable SSH password authentication: change PasswordAuthentication yes to PasswordAuthentication no
    • Restart SSH Daemon sudo systemctl restart sshd
  8. Automatic updates (Ubuntu: Automatic Updates)
    • Install package sudo apt install unattended-upgrades
    • Make schedule sudo nano /etc/apt/apt.conf.d/10periodic
      Add lines:
      APT::Periodic::Update-Package-Lists "1";
      APT::Periodic::Download-Upgradeable-Packages "1";
      APT::Periodic::AutocleanInterval "7";
      APT::Periodic::Unattended-Upgrade "1";
      Exit ^X
      Save y
      Write ↩︎
    • Setup notifications sudo apt install apticron
  9. Enable firewall
    sudo ufw allow proto tcp from any to any port 22
    sudo ufw enable
  10. Use Fail2Ban to block multiple unsuccessful login attempts
    (Linode: Using Fail2ban to Secure Your Server)
  11. Install Fail2ban
    sudo apt-get install fail2ban
  12. Configure fail2ban
    • cd /etc/fail2ban
    • Copy fail2ban.conf file and # all lines
      sed 's/\(^[[:alpha:]]\)/# \1/' fail2ban.conf | sudo tee fail2ban.local &> /dev/null
    • Copy jail.conf file and # all lines
      sed 's/\(^[a-z tab]\)/# \1/' jail.conf | sudo tee jail.local &> /dev/null

Point a domain name at your new IP

(Linode: DNS Manager Overview)
(Linode: Common DNS Configurations)

  1. Login to your domain registrar and change the zone file (DNS management) to point to Linode’s name servers.
    • ns1.linode.com
    • ns2.linode.com
    • ns3.linode.com
    • ns4.linode.com
    • ns5.linode.com
  2. Create a new domain zone
    • Login to Linode Manager
    • Click on DNS Manager tab
    • Click Add a domain zone
    • Domain your domain
    • SOA Email A good email address
    • Insert Default Records Yes, ...
    • Click Add a Master Zone

Mini series

  1. Setting up a server at Linode
  2. Install Let’s Encrypt to Create SSL Certificates on Ubuntu
  3. Setting up MongoDB on Ubuntu
  4. Setting up nginx on Ubuntu for parse-server
  5. Setting up parse-server on Ubuntu

Shortcut characters

  • command
  • option
  • control
  • function fn
  • shift
  • caps lock
  • tab
  • back tab
  • escape
  • power
  • eject
  • delete
  • forward delete
  • return ↩︎
  • enter
  • arrows ← ↑ ↓ →
  • page up/down ⇞ ⇟
  • home/end ↖︎ ↘︎
  • space space
  • apple
  • refresh

Shorthand if statement (Ternary Conditional Operator)

I never remember the syntax, or name, for Ternary Conditional Operator.

let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
    rowHeight = contentHeight + 50
} else {
    rowHeight = contentHeight + 20
}
// rowHeight is equal to 90

becomes

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight is equal to 90

Apple documentation here.

Transparently redirect your root directory to a subdirectory

DreamHost has instructions here.
Make a text file called .htaccess with the following code and upload it to the domain’s root directory.

RewriteEngine on
RewriteBase /

RewriteCond %{REQUEST_URI} !^/blog/

# Rewrites all URLS [Replace "domain" with the actual domain, without the TLD (.com, .net, .biz, etc)]
RewriteCond %{HTTP_HOST} ^(www\.)?domain\.

# Rewrite all those to insert /folder
RewriteRule ^(.*)$ /blog/$1 [L]

Alternatively.

RewriteEngine on
RewriteCond %{HTTP_HOST} example\.com [NC]
RewriteCond %{REQUEST_URI} ^/$
RewriteRule ^(.*)$ /blog/$1 [L]

How to do a lightweight Core Data migration in Swift

Here’s a stack overflow post on the topic.
Define migrationOptions to be used in persistentStoreCoordinator

let migrationOptions = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]

Inclue options in:

try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: migrationOptions)

It should end up like this:

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
        // Create the coordinator and store
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
        let migrationOptions = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true] // Migration options added to standard Core Data stack
        var failureReason = "There was an error creating or loading the application's saved data."
        do {
            try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: migrationOptions)
        } catch {
            // Report any error we got.
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReason

            dict[NSUnderlyingErrorKey] = error as NSError
            let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
            // Replace this with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
            abort()
        }
        
        return coordinator
    }()

Similarly in Objective C. Thanks @rockvotes

NSDictionary *migrationOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

Submitting apps to the App Store with Dynamic Libraries

I was trying to submit a proof of concept app to the app store for TestFlight distribution. Submission failed.
Looked up Mapbox to see if there was a new version. No.
Looked up Mapbox to see if I was missing something in their instructions. No
A quick google search, lead me to a stack overflow post, which linked to another, which linked to this: Stripping Unwanted Architectures From Dynamic Libraries In Xcode
Copy, paste,

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"

# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
    FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
    FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
    echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"

    EXTRACTED_ARCHS=()

    for ARCH in $ARCHS
    do
        echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
        lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
        EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
    done

    echo "Merging extracted architectures: ${ARCHS}"
    lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
    rm "${EXTRACTED_ARCHS[@]}"

    echo "Replacing original executable with thinned version"
    rm "$FRAMEWORK_EXECUTABLE_PATH"
    mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"

done

(might need to follow step 0 from here)
Archive, submit.
Upload Successful