💭 Bro I want to keep in touch with some of my old friends as well as make new friends could you help me with that? 🤔
Oh boy you have in at the right time. I have the just the thing for you. Keep In Touch helps you to well keep in touch with your old friends and make some new ones too
Keep In Touch includes the following features:
- Basic Authentication system including ability to upload your own avatar
- Email verfication for new registering users as well as forgot password feature
- Expiry of email verification and password reset links in 60 seconds
- Ability to send friend requests to people using their username
- Ability to accept or reject friend request as well as view incoming and sent friend request
- View your friends and chat with them
Feeling excited to know more about the website? Lets checkout a small demo video!
Demo of Project
KeepInTouch.webm
To manage the dependencies in the project composer
is used.To learn how to install composer visit https://getcomposer.org/doc/00-intro.md
. For using composer in the current project:
- install composer
- git clone the project
- run
composer install
to install all the dependencies present incomposer.json
- run
composer update
to update any dependency - To use any library installed via composer you need to require
vendor/autoload.php
. Vendor is a folder made by composer
vlucas/phpdotenv
: This is a library that helps you to load contents from a .env file into the project environment. github link:https://github.com/vlucas/phpdotenv
cloudinary/cloudinary_php
: This is the official sdk of cloudinary for php. github link:https://github.com/cloudinary/cloudinary_php
phpmailer/phpmailer
: A library to help you send mail securely and fast. To use this you need to use Gmail password generated usingApp password
For database I have made use of postgresql database hosted at supabase. For querying datanbase i have used php-pgsql
- For Windows users follow
https://github.com/NarutoUchiha39/EliteSolutions
- For Linux users :
-
First run
sudo apt install nginx
-
make sure you have
php-fpm
installed or runsudo apt install php-fpm
. To start fpm runsudo systemctl start php<version>-fpm
-
run
sudo nginx
to start nginx server athttp://localhost:80
. If nginx cant bind to port 80 runsudo fuser -k 80/tcp
to kill processes running at port 80 andsudo apache2ctl stop
to stop apache2 server, if present running at port 80 -
The nginx config files are present in
/etc/nginx/nginx.conf
. If you havesites-enabled
andsites-available
directories, then update the sites-enabled directory. -
Why is fpm needed? Servers like Apache server treat each request as a worker process and each worker process has an instance of php or any other language embeded into the process, giving the webserver ability to run code natively in the server itself and serve dynamic content. Nginx on the other hand treats each request as a thread and uses a seperate process to manage all the request that needs dynamic content. The process in our case is php fpm(fast proccess manager)
-
copy the project files in
var/www/
-
Use the following config in your
nginx.conf
file or default file in/etc/nginx/sites-enabled
:server { listen 80; listen [::]:80; server_name _; root /var/www/EliteSolutions; add_header X-Frame-Options "SAMEORIGIN"; # Stop our website from being embedded in i frames add_header X-Content-Type-Options "nosniff"; # Stop the browser from sniffing our pages and determining the mime type. index index.php index.html; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; # Try to serve our file from the loaction in uri then try to serve the file as a folder then if all fails send the uri to index file as query string } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { # ~ signifies regex. This regex matches all files ending with .php fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; # use fastcgi to connect to php-fpm socket running fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; # Pass the location of the file to be processed to fastcgi. $realpath_root gives the root of project (i.e. /var/www/EliteSolutions) and $fastcgi_script_name gives the file name. (i.e. /var/www/EliteSolutions/index.php) include fastcgi_params; # include all well known fastcgi parameters present in /etc/nginx/fastcgi_params } location ~ /\.(?!well-known).* { deny all; } }
-
Run
sudo ngnix -t
to test your nginx configuration and then runnginx -s relaod
to restart nginx server
-
-
cloudinaryConnection.php
manages the connection to the cloudinary server and helps in uploading images. After image is uploaded we get the link to image from this file. One small caveat: while loading environment variable by phpdotenv do not use thegetenv
method to retrieve environment variable as its not thread safe. Use$_ENV
instead. To upload the profile image, the temporary location of the image obtained through form, is passed to the fuction along with the name of the file. -
getURI.php
helps in returning the url of a file located on server. It checks whether the server has https in the$_SERVER
super global variable turned on and concatenates it with$_SERVER[HTTP_HOST]
. This makes the function flexible and enable correct url retrieval even in hosted environment. To get the url of a file just pass in the file location from root of the server and your good to go -
passwordEncryption.php
has two static functions.getEnv
returns the value of environment variable when its given the key.encrypt
is used to encrypt the user password with the use of openssl library. The function makes use ofaes-256-ctr
which is a block cypher. For the encryption process, a nonce is generated using theopenssl_random_pseudo_bytes
. The length is determined usingopenssl_cipher_iv_length
accoring to the cypher method used. The encryption takes place using a strong key, the nonce and the message. The nonce and the message is then concatenated andbase64 encoded
. For decrypting, first we decoade thebase64
. we get the nonce by slicing the string as we know the nonce length. Here themb_substr
is used for slicing. After that the decoding process takes place using the nonce obtained.
Note: I know obtaining hash of password usingpassword_hash
is safer than encrypting and decrypting password. Openssl was used just to explore the library -
DBConnect.php
is used for connecting to supabase. Since supabase is a postgres database, the functionpg_connect
is used to connect to supabase and the connection is returned from the function. -
Displaying of success and error messages
- Whenever a user is logged in or registered successfully we give an array of success messages in the session storage using
$_SESSION["success"][]="Success Message"
and then redirect the user. Same applies for error messages - The user is redirected using the
header("Location: $url")
snippet - On the HTML page we have a for loop that iterates over error messages in the session using
foreach
loop and at the end of the loop, we make sure that the error messages are flushed out using theunset
function so that new errors are not mixed with old ones - Make sure to have
session_start()
at the start of pages where session variables are used
- Whenever a user is logged in or registered successfully we give an array of success messages in the session storage using
- User tries to register
- using
pg_prepare
andpg_execute
the database is queried to check for users with same email and also checks the validity of the file getting uploaded and displays errors accordingly.
Note: pg_prepare has a small caveat wherein when you execute same prepared statement twice, it gives a warning stating that statement has already been prepared, indicating that prepared statements arent cached by pg_prepare. As far as i know PDO doesnt cause this issue and caches prepared statements. But inspite of warning the statemnt is executed
-
After validating user credentials, we store all the details obtained in
$_SESSION["temp"]["username"], ...
. This is done because the user is inserted into the database only after their email is verified, so we need some way to access the credentials inVerifyMail.php
. We also generate a unique id as user's code, email and the expiry time(60 seconds) in the DB. The expiry time is stored in the form of unix timestamp using phptime()
function. The user is sent a mail containing the code using the customsend_mail
function -
Next the user is redirected to the email page. Here if the user enters wrong code, appropriate error message is displayed and if the user exceeds the expiry time then the user is redirected back to the register page. On entering the correct code, the user is registered and redirected back to home. Some points to note :
- The temporary location of the file becomes invalid when we move to the other page, so the user's profile photo is temporarily stored in the uploads folder using the
move_uploaded_file()
command. - After the expiry or after the user successfully registers, the
cleanUp
function clears the users code from the database and deletes the temporary file in the uploads folder using theunlink
command. After time expiry the temporary session variables are removed and the cleanUp function in run to allow the user to try to register again
- The temporary location of the file becomes invalid when we move to the other page, so the user's profile photo is temporarily stored in the uploads folder using the
-
user can reset their passwords from login page. First the user is asked to enter the registered mail. If the user is not registered, an error message is shown otherwise the user is sent a mail containing a link to reset the password. The link expires within a minute. On clicking the link, the user is redirected to a page where, the user can enter new password and login to the system
-
The link contains a
nonce
key initialized with a random value generated using the phpunique(true)
function. On clicking the link the nonce is extracted using theexplode
function. The nonce is queried in the database and the corresponding mail of the user is obtained. Then a simpleUPDATE
query is used to update the users password and aDELETE
query is used to delete the user's nonce from the database. The user is redirected to the home page with a success message -
After successful login/register the user is redirected to the home page where the user can can add new friends. The username entered by the user is checked to see if the name exists. If the name doesnt exists user is shown an error else a friend request is sent
-
A user has tehe option to accept/reject friend request and can view all the requests from the bell icon in the home page
-
At the home page the user can see all the friends. The records are retrieved from db using and SQL query involving
union
andjoin
-
Finally the user can send and recieve messages. Users messages are to the left and the recieved messages are to the right. For this
JS fetch API
is used to asynchronously fetch data and populate it in the apropriate place using theDOM Manipulation Techniques
-
The user has the ability to send media. The uset can upload a
pdf
(max size: 1MB),jpeg,png,jpg
(file under 8MB). The files are stored and retrieved using cloudinary. For storing files other than images, theresource type
in the coloudinary api is changed to raw. -
For downlaoding a file we simply put
download
attribute in the anchor tag and put the link of the PDF in the src attribute -
For storing the multimedia, we introduce a new column called
type
in the DB with default value of text. The type attribute is changed according to the content of the message. If we send media then themessage
attribute stores the link generated by cloudinary, when we upload the file