First create a directory for your application, where the source code will be:
mkdir -p notes cd notesCopied!
Create PostgreSQL user (with the same name as your logged on Linux user, so no password needed), and the database "db_app":
echo "create user $(whoami); create database db_app with owner=$(whoami); grant all on database db_app to $(whoami); \q" | sudo -u postgres psqlCopied!
Create a database configuration file to describe your PostgreSQL database above:
echo "user=$(whoami) dbname=db_app" > db_appCopied!
Create database objects we'll need - users table for application users, and notes table to hold their notes:
echo "create table if not exists notes (dateOf timestamp, noteId bigserial primary key, userId bigint, note varchar(1000)); create table if not exists users (userId bigserial primary key, email varchar(100), hashed_pwd varchar(100), verified smallint, verify_token varchar(30), session varchar(100)); create unique index if not exists users1 on users (email);" | psql -d db_appCopied!
Create application "notes" owned by your Linux user:
sudo mgrg -i -u $(whoami) notesCopied!
This executes before any other handler in an application, making sure all requests are authorized, file "before-handler.gliim":
vi before-handler.gliimCopied!
before-handler set-param displayed_logout = false, is_logged_in = false call-handler "/session/check" end-before-handlerCopied!
- Signup users, login, logout
This is a generic session management web service that handles user creation, verification, login and logout. Create file "session.gliim":
vi session.gliimCopied!
Copy and paste:
// Display link to login or signup %% /session/login-or-signup private @<a href="<<p-path "/session/user/login">>">Login</a> <a href="<<p-path "/session/user/new/form">>">Sign Up</a><hr/> %% // Login with email and password, and create a new session, then display home pag %% /session/login public get-param pwd, email hash-string pwd to hashed_pwd random-string to sess_id length 30 run-query @db_app = "select userId from users where email='%s' and hashed_pwd='%s'" output sess_user_id : email, hashed_pwd run-query @db_app no-loop = "update users set session='%s' where userId='%s'" input sess_id, sess_user_id affected-rows arows if-true arows not-equal 1 @Could not create a session. Please try again. <<call-handler "/session/login-or-signup">> <hr/> exit-handler end-if set-cookie "sess_user_id" = sess_user_id path "/", "sess_id" = sess_id path "/" call-handler "/session/check" call-handler "/session/show-home" exit-handler end-query @Email or password are not correct. <<call-handler "/session/login-or-signup">><hr/> %% // Starting point of the application. Either display login form or a home page: %% /session/start public get-param action, is_logged_in type bool if-true is_logged_in equal true if-true action not-equal "logout" call-handler "/session/show-home" exit-handler end-if end-if call-handler "/session/user/login" %% // Generic home page, you can call anything from here, in this case a list of note %% /session/show-home private call-handler "/notes/list" %% // Logout user and display home, which will ask to either login or signup %% /session/logout public get-param is_logged_in type bool if-true is_logged_in equal true get-param sess_user_id run-query @db_app = "update users set session='' where userId='%s'" input sess_user_id no-loop affected-rows arows if-true arows equal 1 set-param is_logged_in = false @You have been logged out.<hr/> commit-transaction @db_app end-if end-if call-handler "/session/show-home" %% // Check session based on session cookie. If session cookie corresponds to the email address, the request is a part of an authorized session %% /session/check private get-cookie sess_user_id="sess_user_id", sess_id="sess_id" set-param sess_id, sess_user_id if-true sess_id not-equal "" set-param is_logged_in = false run-query @db_app = "select email from users where userId='%s' and session='%s'" output email input sess_user_id, sess_id row-count rcount set-param is_logged_in = true get-param displayed_logout type bool if-true displayed_logout equal false get-param action if-true action not-equal "logout" @Hi <<p-out email>>! <a href="<<p-path "/session/logout">>">Logout</a><br/> end-if set-param displayed_logout = true end-if end-query if-true rcount not-equal 1 set-param is_logged_in = false end-if end-if %% // Check that email verification token is the one actually sent to the email address %% /session/verify-signup public get-param code, email run-query @db_app = "select verify_token from users where email='%s'" output db_verify : email if-true code equal db_verify @Your email has been verifed. Please <a href="<<p-path "/session/user/login">>">Login</a>. run-query @db_app no-loop = "update users set verified=1 where email='%s'" : email exit-handler end-if end-query @Could not verify the code. Please try <a href="<<p-path "/session/user/new/verify-form">>">again</a>. exit-handler %% // Display login form that asks for email and password %% /session/user/login public call-handler "/session/login-or-signup" @Please Login:<hr/> @<form action="<<p-path "/session/login">>" method="POST"> @<input name="email" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Email"> @<input name="pwd" type="password" value="" size="50" maxlength="50" required placeholder="Password"> @<button type="submit">Go</button> @</form> %% // Display form for a new user, asking for an email and password %% /session/user/new/form public @Create New User<hr/> @<form action="<<p-path "/session/user/new/create">>" method="POST"> @<input name="email" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Email"> @<input name="pwd" type="password" value="" size="50" maxlength="50" required placeholder="Password"> @<input type="submit" value="Sign Up"> @</form> %% // Send verification email %% /session/user/new/send-verify private get-param email, verify write-string msg @From: service@your-service.com @To: <<p-out email>> @Subject: verify your account @ @Your verification code is: <<p-out verify>> end-write-string exec-program "/usr/sbin/sendmail" args "-i", "-t" input msg status st if-true st not-equal 0 or true equal false @Could not send email to <<p-out email>>, code is <<p-out verify>> set-param verify_sent = false else-if set-param verify_sent = true end-if %% // Create new user from email and password %% /session/user/new/create public get-param email, pwd hash-string pwd to hashed_pwd random-string to verify length 5 number begin-transaction @db_app run-query @db_app no-loop = "insert into users (email, hashed_pwd, verified, verify_token, session) values ('%s', '%s', '0', '%s', '')" input email, hashed_pwd, verify affected-rows arows error err on-error-continue if-true err not-equal "0" or arows not-equal 1 call-handler "/session/login-or-signup" @User with this email already exists. rollback-transaction @db_app else-if set-param email, verify call-handler "/session/user/new/send-verify" get-param verify_sent type bool if-true verify_sent equal false rollback-transaction @db_app exit-handler end-if commit-transaction @db_app call-handler "/session/user/new/verify-form" end-if %% // Display form to enter the code emailed to user to verify the email address %% /session/user/new/verify-form public get-param email @Please check your email and enter verification code here: @<form action="<<p-path "/session/verify-signup">>" method="POST"> @<input name="email" type="hidden" value="<<p-out email>>"> @<input name="code" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Verification code"> @<button type="submit">Verify</button> @</form> %%Copied!
- Notes application
This is the actual application that uses above session management services. Create file "notes.gliim":
vi notes.gliimCopied!
Copy and paste:
// Delete a note %% /notes/delete public call-handler "/notes/header" get-param sess_user_id, note_id run-query @db_app = "delete from notes where noteId='%s' and userId='%s'" : note_id, sess_user_id \ affected-rows arows no-loop error errnote if-true arows equal 1 @Note deleted else-if @Could not delete note (<<p-out errnote>>) end-if %% // Display a form to add a note %% /notes/form-add public call-handler "/notes/header" @Add New Note @<form action="<<p-path "/notes/add">>" method="POST"> @<textarea name="note" rows="5" cols="50" required autofocus placeholder="Enter Note"></textarea> @<button type="submit">Create</button> @</form> %% // Add a note %% /notes/add public call-handler "/notes/header" get-param note, sess_user_id run-query @db_app = "insert into notes (dateOf, userId, note) values (now(), '%s', '%s')" : sess_user_id, note \ affected-rows arows no-loop error errnote if-true arows equal 1 @Note added else-if @Could not add note (<<p-out errnote>>) end-if %% // List all notes %% /notes/list public call-handler "/notes/header" get-param sess_user_id run-query @db_app = "select dateOf, note, noteId from notes where userId='%s' order by dateOf desc" \ input sess_user_id output dateOf, note, noteId match-regex "\n" in note replace-with "<br/>\n" result with_breaks status st cache if-true st equal 0 set-string with_breaks = note end-if @Date: <<p-out dateOf>> (<a href="<<p-path "/notes/ask-delete">>?note_id=<<p-out noteId>>">delete note</a>)<br/> @Note: <<p-out with_breaks>><br/> @<hr/> end-query %% // Display a question whether to delete a note or not %% /notes/ask-delete public call-handler "/notes/header" get-param note_id @Are you sure you want to delete a note? Use Back button to go back,\ or <a href="<<p-path "/notes/delete">>?note_id=<<p-out note_id>>">delete note now</a>. %% // Check if session is authorized, and display an appropriate header %% /notes/header private get-param is_logged_in type bool if-true is_logged_in equal false call-handler "/session/login-or-signup" end-if @<h1>Welcome to Notes!</h1><hr/> if-true is_logged_in equal false exit-handler end-if @<a href="<<p-path "/notes/form-add">>">Add Note</a> <a href="<<p-path "/notes/list">>">List Notes</a><hr/> %%Copied!
gg -q --db=postgres:db_appCopied!
mgrg notesCopied!
In order to use this example, you need to be able to email local users, which means email addresses such as \"myuser@localhost\". To do that, install postfix (or sendmail). On Debian systems (like Ubuntu):
sudo apt install postfix sudo systemctl start postfixCopied!
and on Fedora systems (like RedHat):
sudo dnf install postfix sudo systemctl start postfixCopied!
When the application sends an email to a local user, such as <OS user>@localhost, then you can see the email sent at:
sudo vi /var/mail/<OS user>Copied!
A web server sits in front of Gliimly application server, so it needs to be setup. This example is for Ubuntu, so edit Nginx config file there:
sudo vi /etc/nginx/sites-enabled/defaultCopied!
Add this in "server {}" section:
location /notes/ { include /etc/nginx/fastcgi_params; fastcgi_pass unix:///var/lib/gg/notes/sock/sock; }Copied!
Restart Nginx:
sudo systemctl restart nginxCopied!
Go to your web browser, and enter:
http://127.0.0.1/notes/session/startCopied!