§2023-02-28

  1. Create Project dir
$ mkdir messgage-board && cd $_
$ bundler init
# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"
gem "sinatra"
gem "thin"
  1. Let's see, hello-world-sinatra.rb as
require 'sinatra'

# Listen on all interfaces in the development environment
# This is needed when we run from Cloud 9 environment
# source: https://gist.github.com/jhabdas/5945768
set :bind, '0.0.0.0'
set :port, 8080

get '/' do
    'hello world!'
end
$ ruby hello-world-sinatra.rb 
== Sinatra (v3.0.4) has taken the stage on 8080 for development with backup from Thin
2023-02-28 10:16:02 +0800 Thin web server (v1.8.1 codename Infinite Smoothie)
2023-02-28 10:16:02 +0800 Maximum connections set to 1024
2023-02-28 10:16:02 +0800 Listening on 0.0.0.0:8080, CTRL+C to stop
127.0.0.1 - - [28/Feb/2023:10:16:22 +0800] "GET / HTTP/1.1" 200 12 0.0122
127.0.0.1 - - [28/Feb/2023:10:16:22 +0800] "GET /favicon.ico HTTP/1.1" 404 469 0.0028
...
  1. Start PostgreSQL server
[alexlai@n2MnJaro ~]$ sudo systemctl status postgresql
● postgresql.service - PostgreSQL database server
     Loaded: loaded (/usr/lib/systemd/system/postgresql.service; disabled; preset: disabled)
     Active: active (running) since Tue 2023-02-28 10:35:14 CST; 6s ago
    Process: 53732 ExecStartPre=/usr/bin/postgresql-check-db-dir ${PGROOT}/data (code=exited, status=0/SUCCESS)
   Main PID: 53734 (postgres)
      Tasks: 6 (limit: 4120)
     Memory: 23.0M
        CPU: 282ms
     CGroup: /system.slice/postgresql.service
             ├─53734 /usr/bin/postgres -D /var/lib/postgres/data
             ├─53735 "postgres: checkpointer "
             ├─53736 "postgres: background writer "
             ├─53738 "postgres: walwriter "
             ├─53739 "postgres: autovacuum launcher "
             └─53740 "postgres: logical replication launcher "

Feb 28 10:35:13 n2MnJaro postgres[53734]: 2023-02-28 10:35:13.410 CST [53734] LOG:  listening on Unix socket "/run/postgresql/.s.PGSQL.5432"
  1. Install rerun gem and,
$ bundle exec rerun hello-world-sinatra.rb
[DEPRECATED] `Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env` (called at /opt/home/alexlai/.frum/versions/3.2.1/lib/ruby/gems/3.2.0/gems/rerun-0.14.0/lib/rerun/notification.rb:74)

10:47:20 [rerun] Message-board launched
10:47:20 [rerun] Rerun (53975) running Message-board (53993)
== Sinatra (v3.0.4) has taken the stage on 8080 for development with backup from Thin
2023-02-28 10:47:21 +0800 Thin web server (v1.8.1 codename Infinite Smoothie)
2023-02-28 10:47:21 +0800 Maximum connections set to 1024
2023-02-28 10:47:21 +0800 Listening on 0.0.0.0:8080, CTRL+C to stop
  1. Source Code
require 'sinatra'

# Listen on all interfaces in the development environment
# This is needed when we run from Cloud 9 environment
# source: https://gist.github.com/jhabdas/5945768
set :bind, '0.0.0.0'
set :port, 8080

get '/' do
  t_msg = [ 
    { nick: "Harri", msg: "Hello World!" }, 
    { nick: "Ioana", msg: "Buna dimineata" },
    { nick: "Alexandru", msg: "sunt fericit" } 
  ]
  erb :v_message, :layout => :l_main, :locals => {:t_msg => t_msg}
end
<!doctype html>
<html lang=en>
  <head>
    <link rel="stylesheet" type="text/css" href="/stylesheets/main.css">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
    <meta charset=utf-8>
    <title>Message Board</title>
  </head>
  <body>
    <div class="pageheader">
      <h1>Message Board</h1>
    </div>
  
    <div class="speech-bubble">
      <form action="/new" id="newmessage">
        <p class="nick"><input type="text" placeholder="Nickname" name="nickname"></p>
        <p class="message"><textarea placeholder="Message" name="message" form="newmessage"></textarea></p>
        <p class="submit"><button class="submit" type="submit" form="newmessage" value="Submit">I say this!</button></p>
      </form>
    </div>
    
    <%= yield %>

  </body>
</html>
<% t_msg.each  do |msg| %>
  <div class="speech-bubble">
    <p class="nick"><%= msg[:nick] %> says:</p>
    <p class="message">&nbsp;&nbsp;&nbsp;"<%= msg[:msg] %>"</p>
  </div>
<% end  %>

2nd step: Read messages from the db

  1. Create a system user messageboarduser
(FRUM) [alexlai@n2MnJaro message-board]$ sudo adduser messageboarduser
[sudo] password for alexlai: 

Login name for new user: messageboarduser

User ID ('UID') [ defaults to next available ]: 
egrep: warning: egrep is obsolescent; using grep -E
egrep: warning: egrep is obsolescent; using grep -E

Initial group [ users ]: 
Additional UNIX groups:

Users can belong to additional UNIX groups on the system.
For local users using graphical desktop login managers such
as XDM/KDM, users may need to be members of additional groups
to access the full functionality of removable media devices.

* Security implications *
Please be aware that by adding users to additional groups may
potentially give access to the removable media of other users.

If you are creating a new user for remote shell access only,
users do not need to belong to any additional groups as standard,
so you may press ENTER at the next prompt.

Press ENTER to continue without adding any additional groups
Or press the UP arrow key to add/select/edit additional groups
:  

Home directory [ /home/messageboarduser ] 

Shell [ /bin/bash ] 

Expiry date (YYYY-MM-DD) []: 

New account will be created as follows:

---------------------------------------
Login name.......:  messageboarduser
UID..............:  [ Next available ]
Initial group....:  users
Additional groups:  [ None ]
Home directory...:  /home/messageboarduser
Shell............:  /bin/bash
Expiry date......:  [ Never ]

This is it... if you want to bail out, hit Control-C.  Otherwise, press
ENTER to go ahead and make the account.


Creating new account...


Changing finger information for messageboarduser.
Name []: MessageBoardUser
Office []: 
Office Phone []: 
Home Phone []: 


Finger information changed.
New password: messageboarduser
Retype new password: messageboarduser
passwd: password updated successfully


Account setup complete.
$ id messageboarduser
uid=1027(messageboarduser) gid=984(users) groups=984(users)
  1. Create a new user and database in PostgreSQL
(FRUM) [alexlai@n2MnJaro message-board]$ sudo -iu postgres
[postgres@n2MnJaro ~]$ psql
psql (15.1)
Type "help" for help.

postgres=# CREATE USER messageboarduser WITH PASSWORD 'messageboarduser';
CREATE ROLE
postgres=# CREATE DATABASE messageboard OWNER messageboarduser;
CREATE DATABASE
postgres=# \l
                                                       List of databases
     Name      |      Owner       | Encoding |   Collate   |    Ctype    | ICU Locale | Locale Provider |   Access privileges   
---------------+------------------+----------+-------------+-------------+------------+-----------------+-----------------------
 messageboard  | messageboarduser | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | 
 pgroonga_test | postgres         | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | 
 postgres      | postgres         | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | 
 template0     | postgres         | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/postgres          +
               |                  |          |             |             |            |                 | postgres=CTc/postgres
 template1     | postgres         | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/postgres          +
               |                  |          |             |             |            |                 | postgres=CTc/postgres
(5 rows)
  1. Sign into postgreSQL as messageboarduser and define tables
(FRUM) [alexlai@n2MnJaro message-board]$ sudo -iu messageboarduser
[messageboarduser@n2MnJaro ~]$ psql messageboard
psql (15.1)
Type "help" for help.

messageboard=> CREATE TABLE messageboardmessages (
 message_id varchar(36) PRIMARY KEY,
 nickname varchar(30) NOT NULL,
 message varchar(200) NOT NULL,
 timestamp varchar(50) NOT NULL
);
CREATE TABLE
messageboard=> \d
                    List of relations
 Schema |         Name         | Type  |      Owner       
--------+----------------------+-------+------------------
 public | messageboardmessages | table | messageboarduser
(1 row)
messageboard=> \d messageboardmessages
                 Table "public.messageboardmessages"
   Column   |          Type          | Collation | Nullable | Default 
------------+------------------------+-----------+----------+---------
 message_id | character varying(36)  |           | not null | 
 nickname   | character varying(30)  |           | not null | 
 message    | character varying(200) |           | not null | 
 timestamp  | character varying(50)  |           | not null | 
Indexes:
    "messageboardmessages_pkey" PRIMARY KEY, btree (message_id)

messageboard=> INSERT INTO messageboardmessages(message_id, nickname, message, timestamp) 
VALUES ('201519e8-f3e7-4fcf-9349-3f81b94e908c', 'PostgreSQLUser', 'Hello from PostgreSQL', '2018-11-30 10:48:19 +0000');
INSERT 0 1

messageboard=> SELECT * FROM messageboardmessages;
              message_id              |    nickname    |        message        |         timestamp         
--------------------------------------+----------------+-----------------------+---------------------------
 201519e8-f3e7-4fcf-9349-3f81b94e908c | PostgreSQLUser | Hello from PostgreSQL | 2018-11-30 10:48:19 +0000
(1 row)

  1. Enhance the app with reading from the db
# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"
gem "sinatra"
gem "thin"
gem "rerun"
gem "pg"
  1. enhanced app.rb
require 'sinatra'
require 'pg'

# Listen on all interfaces in the development environment
# This is needed when we run from Cloud 9 environment
# source: https://gist.github.com/jhabdas/5945768
set :bind, '0.0.0.0'
set :port, 8080

get '/' do
  t_msg = []

  begin
    # connect to the database
    conection = PG.connect :dbname => 'messageboard', :user => 'messageboarduser', :password => 'messageboarduser'

    # read data from the database
    t_messages = conection.exec 'SELECT * FROM messageboardmessages'

    # map data to t_msg, which is provided to the erb later
    t_messages.each do |s_message|
      t_msg.push({ nick: s_message['nickname'], msg: s_message['message'] })
    end

  rescue PG::Error => e
    val_error = e.message 

  ensure
    conection.close if conection
 
  end

  # call erb, pass parameters to it 
  erb :v_message, :layout => :l_main, :locals => {:t_msg => t_msg, :val_error => val_error}
end

3rd step: Enable posting of messages and save them in the database

  1. Generating unique identifiers
# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"
gem "sinatra"
gem "thin"
gem "rerun"
gem "pg"
gem 'uuidtools'
  1. Validation of input to avoid SQL injection
  1. Generating time stamp