AJAX in Sweetalert 2

In this article, I am going to teach you, how you can use AJAX in sweetalert 2. As per sweetalert 2 documentation, you can use Fetch API for calling AJAX requests. Fetch API is good, but not for more control over your request, like using POST method, adding headers etc. Fetch API is not so suitable in those cases.

Video tutorial:

AJAX in Sweetalert 2

Following is your code that you get from sweetalert 2 documentation:

Swal.fire({
  title: 'Submit your Github username',
  input: 'text',
  inputAttributes: {
    autocapitalize: 'off'
  },
  showCancelButton: true,
  confirmButtonText: 'Look up',
  showLoaderOnConfirm: true,
  preConfirm: (login) => {
    return fetch(`//api.github.com/users/${login}`)
      .then(response => {
        if (!response.ok) {
          throw new Error(response.statusText)
        }
        return response.json()
      })
      .catch(error => {
        Swal.showValidationMessage(
          `Request failed: ${error}`
        )
      })
  },
  allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
  if (result.isConfirmed) {
    Swal.fire({
      title: `${result.value.login}'s avatar`,
      imageUrl: result.value.avatar_url
    })
  }
})

Replace that with the following function:

swal.fire({
	title: 'Submit your Github username',
	input: 'text',
	inputAttributes: {
		autocapitalize: 'off'
	},
	showCancelButton: true,
	confirmButtonText: 'Look up',
	showLoaderOnConfirm: true,
	preConfirm: (login) => {

		return new Promise(function (callback) {
			const ajax = new XMLHttpRequest()
			ajax.open("GET", "https://api.github.com/users/" + login, true)

				ajax.onreadystatechange = function () {
					if (this.readyState == 4 && this.status == 200) {
						callback( JSON.parse(this.responseText) )
					}
				}

			ajax.send()
		})
		
	},
	allowOutsideClick: () => !swal.isLoading()
}).then((result) => {
	if (result.isConfirmed) {
		swal.fire({
			title: `${result.value.login}'s avatar`,
			imageUrl: result.value.avatar_url
		})
	}
})

You can notice that the fetch() function has been replaced with a Promise. But now you can use XMLHttpRequest and have more control over your AJAX requests.

Learn more about promises from here.

Display ellipsis on long text HTML and CSS

In this article, I am going to show you how you can show an ellipsis on a long text using HTML and CSS. We will be using just 4 CSS properties.

Video tutorial:

For example, you have a paragraph like this:

<p>A quick brown fox jumps over the lazy dog.</p>

Then give it a width and you will start seeing it in wrap.

<p style="width: 200px;">A quick brown fox jumps over the lazy dog.</p>

To disable the wrap, you can give the following property:

<p style="width: 200px;
	white-space: nowrap;">A quick brown fox jumps over the lazy dog.</p>

Last thing you need to do, is to show ellipsis. To do that, just give the following 2 properties.

<p style="width: 200px;
	white-space: nowrap;
	text-overflow: ellipsis;
	overflow: hidden;
	display: block;">A quick brown fox jumps over the lazy dog.</p>

So that’s it. That’s how you can show an ellipsis on a long text using just HTML and CSS. If you face any problem in following this, kindly do let me know.

Split camel case using regex in Javascript

Hello. In this post, we will show you, how you can split a camel case word and capitalize it using regex (regular expression) in Javascript.

InputOutput
adnanTechComAdnan Tech Com

Following code will split the camel case into multiple words and then capitalize it:

// splitting camel case to multiple words
const str = "adnanTechCom".replace(/([a-z])([A-Z])/g, "$1 $2")

// capitalizing the string
const capitalize = str.charAt(0).toUpperCase() + str.slice(1)
console.log(capitalize)

capitalize variable will now have value:

Adnan Tech Com

This regular expression will search for all words that ends with small letter and begin with capital alphabet. Then give a space between each of such word.

The above replace call replaces the match of the regex (which happens to match everything) with the first capture group ($1) followed by a space, followed by the second capture group ($2).

For capitalization, you can also use a CSS property as well:

text-transform: capitalize;

MongoDB and MySQL equivalent queries

Hello. In this article, we are going to show you some MongoDB and MySQL equivalent queries. This will help you greatly if you want to convert a MySQL project into MongoDB or vice-versa.

Video tutorial:

Introduction

First, let’s give a small introduction to both of these databases.

MongoDB

MongoDB is schema-less database architecture. Means that the schema or structure of the database does not needs to be defined. Schema will automatically gets created as data comes in. It is used where data needs to be loosely-coupled.

MySQL

MySQL is a structured query language. The structure of the database and its tables needs to be defined before creating them. It is used where data needs to be tightly-coupled.

MongoDBMySQL
It is schema-less.Schema needs to be defined.
Non-relational database.Relational database.
It has collections.It has tables.
It has documents.It has rows.
Used for loosely-coupled data.Used for tightly-coupled data.
Horizontal scaling.Vertical scaling.
Each document can have different structure.Each row must have same structure.
Data is not dependent on other collections.Data might be dependent on other tables.
Uses nested objects and arrays.Uses separate tables and joins them using foreign keys.

1. Creating collections/tables

MongoDB

As mentioned above, MongoDB collections does not needs to be created. They will be created automatically once a document is inserted in it.

MySQL

To create a table in MySQL database, you can run the following query:

CREATE TABLE IF NOT EXISTS users(
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name TEXT NOT NULL,
    age INTEGER DEFAULT 0
);

This will create a table named “users” having 3 columns “id”, “name” and “age”. ID will be a unique key that will automatically be incremented once a new row is inserted. While “name” will be a text string field and “age” will be an integer number.

2. Inserting documents/rows

MongoDB

To insert a document in a MongoDB collection, you can run the following query:

db.users.insertOne({
    name: "Adnan",
    age: 30
});

This will insert a new document in “users” collection. It will automatically assign unique ObjectId to it named “_id”.

{
	"_id": ObjectId("6474680ef3c486d92597e787"),
	"name": "Adnan",
	"age": 30
}

To insert a row in MySQL table, you would do:

INSERT INTO users(name, age) VALUES ("Adnan", 30);

3. Fetching data

MongoDB

To fetch multiple records from MongoDB collection, you can run the following query:

db.users.find({
    name: "Adnan"
}).toArray();

This will return all the documents where “name” is “Adnan”.

MySQL

In MySQL, you can run the following SQL query:

SELECT * FROM users WHERE name = "Adnan";

3.1 AND clause

You can use $and operator in MongoDB to fetch data where all conditions must met.

db.users.find({
    $and: [
        {
            name: "Adnan"
        },
        {
            age: 30
        }
    ]
}).toArray();

This will return all the users whose name is “Adnan” and their age is 30.

Same filter can be applied on MySQL using the following query:

SELECT * FROM users WHERE name = "Adnan" AND age = 30;

3.2 OR clause

You can use $or operator in MongoDB to fetch data where any of the condition met.

db.users.find({
    $or: [
        {
            name: "Adnan"
        },
        {
            age: 30
        }
    ]
}).toArray();

This will return all the users whose name is “Adnan” or if their age is 30.

In MySQL, we would apply the above filter like this:

SELECT * FROM users WHERE name = "Adnan" OR age = 30;

3.3 Limiting, sorting and skipping data

To limit the number of records to fetch, order them by their name in ascending order and skip 1 document, in MongoDB you would do:

db.users.find({
        age: 30
    })
    .sort({
        name: -1
    })
    .skip(1)
    .limit(1)
    .toArray()

This will sort the users documents by name in descending order, skip 1 record and return only 1 record.

Similar query can be run on MySQL in the following way:

SELECT * FROM users WHERE age = 30 ORDER BY id DESC LIMIT 1, 1

LIMIT {skip}, {limit} this is the syntax of LIMIT command in SQL.

3.4 Less or greater than

In MongoDB, you can use $lt (less than) and $gt (greater than) operators like this:

db.users.find({
    age: {
        $lt: 30
    }
}).toArray()

This will return the users whose age is less than 30. Following query will return the users whose age is greater than 30:

db.users.find({
    age: {
        $gt: 30
    }
}).toArray()

You can also use $lte and $gte operators for “less than and equal to” and “greater than and equal to” conditions respectively.

Above are the MongoDB queries, following are their equivalent MySQL queries:

/* less than */
SELECT * FROM users WHERE age < 30;

/* less than and equal to */
SELECT * FROM users WHERE age <= 30;

/* greater than */
SELECT * FROM users WHERE age > 30;

/* greater than and equal to */
SELECT * FROM users WHERE age >= 30;

4. Updating data

To update a document in MongoDB collection, you would do the following:

db.users.updateMany({
    name: "Adnan"
}, {
    $set: {
        age: 31
    }
})

This will set the age to 31 of all users having name “Adnan”. If you want to update only one user then you can use updateOne() function instead.

In MySQL, you can do:

UPDATE users SET age = 31 WHERE name = "Adnan" LIMIT 1

4.1 Incrementing/decrementing values

To increment the value, in MongoDB you can do:

db.users.updateOne({
    name: "Adnan"
}, {
    $inc: {
        age: 3
    }
})

This will increment the value of age by 3 where name is “Adnan”. Same thing can be done for decrementing, you can just set the value in negative.

db.users.updateOne({
    name: "Adnan"
}, {
    $inc: {
        age: -3
    }
})

It’s equivalent MySQL query would be:

UPDATE users SET age = age + 3 WHERE name = "Adnan"

5. Delete data

To delete a document from MongoDB collection, you can run the following query:

db.users.deleteOne({
    name: "Adnan"
})

This will delete one document from users collection whose name is “Adnan”. To delete multiple, you can use deleteMany() function instead.

In MySQL, you can do:

DELETE FROM users WHERE name = "Adnan" LIMIT 1

6. Relationships

MongoDB

MongoDB is not a relational database. Data saved in one collection is not dependent on another collection’s data.

For example, if you want to save job history of each user. You do not have to create a separate collection for that. You can simply push a new job in document’s array.

db.users.findOneAndUpdate({
    name: "Adnan"
}, {
    $push: {
        "jobs": {
            _id: ObjectId(),
            title: "Developer",
            company: "adnan-tech.com",
            period: "3 years"
        }
    }
})

This will create an array “jobs” if not already created and insert a new element in that array.

{
	"_id" : ObjectId("64748227f3c486d92597e78a"),
	"name" : "Adnan",
	"age" : 30,
	"jobs" : [
		{
			"_id" : ObjectId("647490a4f3c486d92597e78e"),
			"title" : "Developer",
			"company" : "adnan-tech.com",
			"period" : "3 years"
		}
	]
}

MySQL

Whereas, MySQL is a relational database. Data saved in one table might be dependent on another table’s data.

If you want to achieve the above in MySQL, you would have to create a separate table for that and create user_id as foreign key and reference it to your “users” table.

CREATE TABLE IF NOT EXISTS jobs(
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    title TEXT NOT NULL,
    company TEXT NOT NULL,
    period TEXT NOT NULL,
    user_id INTEGER NOT NULL,

    CONSTRAINT fk_user_id_jobs FOREIGN KEY (user_id) REFERENCES users(id)
)

After that, you can insert a new job of that user using the following query:

INSERT INTO jobs (title, company, period, user_id) VALUES ("Developer", "adnan-tech.com", "3 years", 1)

This will insert a new row in “jobs” table and link it with “users” table using its foreign ID “user_id”.

jobs-table - mongodb mysql equivalent queries
jobs-table

To fetch the data both from users and jobs table, you have to perform a join operation.

SELECT * FROM users INNER JOIN jobs ON users.id = jobs.user_id

This will return all the users along with their jobs.

jobs-table-join-users-table
jobs-table-join-users-table

So you have learned to create many MongoDB queries to their equivalent MySQL queries and vice-versa. If you have any question related to this, feel free to comment it in the section below. You can also check our more tutorials on MongoDB to learn more.

How to send money via Wise

Hello. In this article, I am going to show you, how you can send money to someone via Wise.

What is Wise ?

Wise is a foriegn exchange company that allows people all around the world to send money to their friends or family members. Most of our clients use Wise for buying our premium projects.

Table of contents – Send money via Wise

  1. Visit wise.com
  2. Enter amount to send
  3. Login or sign up
  4. Double check amount and currency
  5. Select recipient
  6. Enter account details
  7. Place of birth
  8. Reason for transfer
  9. Review transfer details
  10. Select payment method
  11. Pay with card
  12. Verification code
  13. Track money
  14. Money is being processed by Wise
  15. Money received
  16. Transfer complete

How to send money using Wise ?

Following are the simple steps that you can follow to send money.

Step 1: Visit wise.com

Visit wise.com in your favorite browser.

1. Visit wise.com - send money via wise
Visit wise.com

Step 2: Enter amount to send

Click on “Send money now” button and you will be redirected to a page where you can enter the amount to send. You need to select the currency from where you will be sending money. You are also required to select the recipient’s currency.

2. Enter amount to send
Enter amount to send

Step 3: Login or Sign up

After selecting the currencies and entering the amount, click the “Get started” button and you will be redirected to login page. Create an account if you do not already have one, otherwise, simply login. You can also do login via Google, Facebook or Apple.

Login or sign up
Login or sign up

Step 4: Double check amount and currency

After login, you will be redirected to the following screen. You can double check the amount you are sending and the currency.

4. Double check amount and currency - send money via wise
Double check amount and currency

Step 5: Select recipient

Then, select either you are sending the money to yourself or to someone else.

5. Select recipient
Select recipient

Step 6: Enter account details

Enter the bank account details of the recipient.

6. Enter account details
Enter account details

Step 7: Place of birth

Sometimes, Wise asks to enter the place of birth of the recipient, just for the confirmation that you are sending money to someone you know.

7. Place of birth of recipient
Place of birth of recipient

Step 8: Reason for transfer

Then you need to select the reason for transfer. Most of my clients select “Tuition fee or study expenses” while making payments because “Digital products” option is not yet available at Wise.

8. Reason for transfer - send money via wise
Reason for transfer

Step 9: Review details of transfer

After that, a page will open from where you can review the details of your transfer. It includes the amount you are sending and the recipient’s bank account details.

9. Review details of transfer
Review details of transfer

Step 10: Select payment method

Select your preferred payment method.

Tip: Most of our clients use “Credit card” option.

10. Select payment method
Select payment method

Step 11: Pay with card

If you wish to pay with your card, you simply need to enter your card details.

11. Pay with card - send money via wise
Pay with card

Step 12: Verification code

Sometimes, wise.com sends a verification code to your phone number via SMS for verification.

12. Verification code
Verification code

13. Track money

Once verified, the process is complete. Now sit back, relax and track your payment.

13. Track money
Track money

Step 14: Money is being processed by wise

Your money will be processed by wise.com. This step might take 10-15 minutes approximately.

14. Money is being processed by wise - send money via wise
Money is being processed by wise

15. Money received

Recipient will receive the money once the payment is processed.

15. Money received
Money received

16. Transfer complete

Now your transfer is complete and your recipient should have received the money in his bank account.

16. Transfer complete - send money via wise
Transfer complete

If you face any problem in following this, kindly do let me know.

How to call Node JS function from EJS

In this tutorial, you will learn how to call a function in your Node JS server file from the EJS template file.

First, we are going to install all the required modules. Run the following commands in your terminal:

> npm install http express ejs
> npm install -g nodemon
> nodemon server.js

Then, create a file named server.js and write the following code in it.

const app = require("express")()
const http = require("http").createServer(app)
const ejs = require("ejs")
app.set("view engine", "ejs")

const port = process.env.PORT || 3000
http.listen(port, function () {
    app.get("/", async function (request, result) {
        const html = await ejs.renderFile("views/index.ejs", null, {
            async: true
        })

        result.send(html)
        return
    })
})

Then, create a folder named views and inside this folder, create a file named index.ejs

To call a function from EJS file, we will first create a helper module in our server file. Create a file named helper.js at the root of your project. Following code will go in this file:

module.exports = {
	myFunc() {
		return "My function value."
	}
}

Then, include this module in your server.js file.

const helper = require("./helper")

And pass it in your renderFile function like this:

const html = await ejs.renderFile("views/index.ejs", {
    helper: helper
}, {
    async: true
})

Now you can easily call it from your index.ejs in the following way:

<%= helper.myFunc() %>

Since you are rendering the EJS file using await command, if you have to include another EJS file, you have to include it using await as well.

<%- await include ("header") %>

So that’s how you can call function in Node JS server file from your EJS template file. If you face any problem in following this, kindly do let me know.

Securely upload and view image in Node JS and Mongo DB

Learn how to securely upload and view image in Node JS and Mongo DB. Most of the time when we create a website where we allow users to upload images, we face a problem to secure them from unauthorized access. For example, you are creating a website where you are allowing users to upload pictures and share them with only selected people. Now you do not want any other person to view that picture.

This can be done easily with Node JS. First, we are going to create an empty project. So run the following commands in your terminal.

> npm init
> npm install express express-formidable ejs fs mongodb
> npm install -g nodemon
> nodemon server.js

Upload image

After that, create a file named server.js and write the following code in it.

// server.js

const express = require("express")
const app = express()
app.set("view engine", "ejs")

const port = process.env.PORT || 3000
app.listen(port, function () {
    console.log("Server started.")

    app.get("/", function (request, result) {
      result.render("index")
    })
})

Now, we will create a folder named views and inside this folder, create a file named index.ejs. Following will be the content of this file.

<!-- views/index.ejs -->

<form method="POST" action="/upload" enctype="multipart/form-data">
  <input type="file" name="file" />
  <input type="submit" value="Upload" />
</form>

This will create a form where user can select image. You can access this from the URL:

http://localhost:3000

Now in our server.js file, we will include the file system module, express formidable module and connect Mongo DB.

// server.js

const fs = require("fs")
const mongodb = require("mongodb")
const ObjectId = mongodb.ObjectId
const mongoClient = new mongodb.MongoClient("mongodb://localhost:27017")

const expressFormidable = require("express-formidable")
app.use(expressFormidable())

// Connect the client to the server (optional starting in v4.7)
await mongoClient.connect()

// Establish and verify connection
const db = await mongoClient.db("upload_view")
db.command({ ping: 1 })
console.log("Database connected")

After that, we will create a route that will handle this request.

// server.js

app.post("/upload", async function (request, result) {
  const file = request.files.file

  const fileData = await fs.readFileSync(file.path)
  if (!fileData) {
    console.error(fileData)
    return
  }

  const filePath = "uploads/" + (new Date().getTime()) + "-" + file.name
  fs.writeFileSync(filePath, fileData)

  await db.collection("files").insertOne({
    path: filePath,
    name: file.name,
    size: file.size
  })

  result.send("File has been uploaded.")
})

This will upload the file in your uploads folder. If you do not have that folder, you need to create it. This will also save the uploaded file data in Mongo DB.

You will not be able to view the images directly from the URL in the browser. So we need to create an API allowing users to view the image.

View image

First, we will fetch all the images from Mongo DB. So change your main route to the following:

// server.js

app.get("/", async function (request, result) {
  const files = await db.collection("files").find({}).toArray()
  result.render("index", {
    files: files
  })
})

Now, in your views/index.ejs, we will loop through all images and display their name and a link to view.

<!-- views/index.ejs -->

<% files.forEach(function (file) { %>
  <p>
    <a href="image/<%= file._id %>">
      <%= file.name %>
    </a>

    <img src="image/<%= file._id %>" style="width: 300px;" />
  </p>
<% }) %>

Finally, we need to create an API that will return the content of image. This will allow you to view the image on user side.

// server.js

app.get("/image/:_id", async function (request, result) {
  const _id = request.params._id
  const file = await db.collection("files").findOne({
    _id: new ObjectId(_id)
  })

  if (file == null) {
    result.json({
      status: "error",
      message: "File not found."
    })
    return
  }

  const fileData = await fs.readFileSync(file.path)
  if (!fileData) {
    console.error(fileData)
    return
  }
  result.writeHead(200, {
    "Content-Type": "image/png",
    "Content-Length": fileData.length
  })
  result.end(fileData)
})

Now you will be able to view the image as well.

Convert datetime to local timezone – Javascript, PHP and MySQL

In this article, you will learn how to convert UTC datetime to user’s local timezone using Javascript.

Your database is centralized, but your user’s are located on different geolocations. Time stored in your database is usually UTC by default. But your users should see the time according to their timezone. So how are you going to do it ?

Table of contents:

  1. Save datetime in UTC
  2. Get user’s local timezone
  3. Convert UTC to timezone

Save datetime in UTC

First, we will create a users table. Run the following query in your phpMyAdmin.

CREATE TABLE IF NOT EXISTS users(
    id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name TEXT,
    created_at DATETIME
)

Then we will insert a row in this table.

INSERT INTO users(name, created_at) VALUES ('Adnan', UTC_TIMESTAMP())

Note that we are using UTC_TIMESTAMP() MySQL built-in function. This will return the current UTC date and time.

Get user’s local timezone

To get the user’s local timezone, we will use the Intl object of Javascript. It stands for internationalization.

const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone

DateTimeFormat() creates a new DateTimeFormat object.

resolvedOptions() will return a new object. Its object’s properties reflects the locale and datetime format sets during the initialization of Intl.DateTimeFormat() object.

timeZone will return the timezone of user’s local computer.

We saved it in a variable. Now we will call an AJAX request to fetch the records from database using PHP.

const ajax = new XMLHttpRequest()
ajax.open("POST", "get-data.php", true)

ajax.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      console.log(this.responseText)
    }
}

const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
const formData = new FormData()
formData.append("time_zone", timeZone)
ajax.send()

This will call an AJAX request and attach the timezone value with the request.

Convert UTC datetime to local timezone

To convert the UTC value from database to user’s local timezone, first we need to get the value of user’s timezone from the AJAX request. So create a file named get-data.php and inside this file, creates a connection with the MySQL database.

<?php

$conn = new PDO("mysql:host=localhost;dbname=your_db_name", "db_user", "db_password");

Then we will get the timezone from AJAX request and fetch the single user’s record.

$time_zone = $_POST["time_zone"] ?? "";
if (!empty($time_zone))
{
    date_default_timezone_set($time_zone);
}

$sql = "SELECT * FROM users";
$result = $conn->prepare($sql);
$result->execute([]);
$user = $result->fetchObject();

Finally, we will convert the timestamp from UTC to user’s local timezone and return it to the client.

echo date("d M, Y h:i:s a", strtotime($user->created_at . " UTC"));

If you open your browser’s console tab, you will see the date and time but in your own timezone.

Using DateTimeZone class

Calling date_default_timezone_set() will set the entire request’s timezone to the user timezone. If you want to convert only one column’s value to user’s local timezone, you can use PHP built-in DateTimeZone class.

First, you need to create its object:

$dateTimeZone = new DateTimeZone($time_zone);

Then, you can create a datetime object from timestamp stored in MySQL database and set it to use the timezone of user.

$dateTime = new DateTime($user->created_at);
$dateTime->setTimezone($dateTimeZone);

Finally, we can display the date and time in the format.

echo $dateTime->format("d M, Y h:i:s a");

That’s how you can convert UTC datetime to user’s local timezone using Javascript.

Video buffering and streaming in NodeJS

In this tutorial, we are going to teach you, how you can create video streaming and buffering like YouTube in NodeJS. In YouTube, you have noticed that the whole video is not downloaded in the browser at once. Only 10-15 seconds of the video has been downloaded in the browser. And more will be downloaded as user continues to watch.

Same technique it applied by Netflix where you watch just the 10-15 seconds of video and the next 10 seconds are fetched as you watch. This technique is useful in 2 terms:

  1. First, it provides a layer of security. You do not write the direct path of video in the <video> tag. You write the API route, and the API will fetch the video data.
  2. Second, it saves bandwidth. If a file is of 100 MB in size, user can download just 1 MB of at start. User can start watching the 1 MB and the next MBs will automatically gets downloaded periodically. User does not have to wait for whole 100 MBs to get downloaded.

Let’s get started

Start by creating an empty folder in your computer. Make sure you have NodeJS installed in your system. Open command prompt or terminal in that folder and run the following command in it:

> npm install express fs ejs

express module is used for routing. fs stands for File System, this module will be used to read the video file. ejs is a templating engine that will be used to render the HTML file. We will display the video in a <video> tag in an HTML file.

Once you ran the above commands, create a file named server.js. This is where we will do all the streaming and buffering work. Write the following code in that file:

const app = require("express")()
const fs = require("fs")

app.set("view engine", "ejs")

app.get("/", function (request, result) {
  result.render("video");
})

app.listen(3000, function () {
  console.log("Server started.")
})

Save the file and run the URL: http://localhost:3000 in the browser. You might see an error that the file video.ejs in not found in the views folder. You need to create a folder named views and inside this folder, create a file named video.ejs.

Following will be the code of views/video.ejs file.

<video id="videoPlayer" width="650" controls>
	<source src="http://localhost:3000/video" type="video/mp4" />
</video>

You can see that we have not set the direct path of the video. Instead we wrote the path of our API endpoint that will return the video data.

Buffer the video

Now we need to create an API endpoint that will handle this request. Create the following GET route in your server.js file.

app.get("/video", function (req, res) {
  // Ensure there is a range given for the video
  const range = req.headers.range
  if (!range) {
    res.status(400).send("Requires Range header")
  }

  // get video stats (about 100MB)
  const videoPath = "video.mp4"
  const videoSize = fs.statSync(videoPath).size

  // Parse Range
  // Example: "bytes=32324-"
  const CHUNK_SIZE = 10 ** 6 // 1MB
  const start = Number(range.replace(/\D/g, ""))
  const end = Math.min(start + CHUNK_SIZE, videoSize - 1)

  // Create headers
  const contentLength = end - start + 1
  const headers = {
    "Content-Range": `bytes ${start}-${end}/${videoSize}`,
    "Accept-Ranges": "bytes",
    "Content-Length": contentLength,
    "Content-Type": "video/mp4",
  }

  // HTTP Status 206 for Partial Content
  res.writeHead(206, headers)

  // create video read stream for this particular chunk
  const videoStream = fs.createReadStream(videoPath, { start, end })

  // Stream the video chunk to the client
  videoStream.pipe(res)
})

** operator is used to raise the first operand to the power of second operand. That’s how you can create a video streaming and buffering engine in NodeJS. Comments has been added with each line for explanation. If you refresh the page now, you will start seeing the video with buffer on it. If you face any problem in following this, kindly do let me know.

End-to-end encryption in Javascript, PHP and MySQL

End-to-end encrypted chats are more secured than the ones where encryption is done on the server side. Because the messages get encrypted even before sending them to the server. This will prevent any read or alter operation of messages in-transit. Let’s learn how to do it.

We will be using Javascript for encryption and decryption. And we will be using PHP for handling AJAX requests. All the encrypted messages will be stored in MySQL database.

Table of contents

  1. Setup database
  2. Private and public keys
  3. Encrypt message
  4. Decrypt message

Setup database

First, open your phpMyAdmin and create a database named end_to_end_encryption. Then create a file named db.php and write the following code in it.

<?php

$conn = new PDO("mysql:host=localhost;dbname=end_to_end_encryption", "root", "");

The second and third parameters are username and password to the database. You can change them as per your server. Then we will create a file named index.php and write the following code in it.

<?php

require_once "db.php";

$sql = "CREATE TABLE IF NOT EXISTS users(
  id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) NOT NULL,
  privateKey TEXT DEFAULT NULL,
  publicKey TEXT DEFAULT NULL
)";
$conn->prepare($sql)->execute();

$sql = "CREATE TABLE IF NOT EXISTS messages(
  id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  sender VARCHAR(255) NOT NULL,
  receiver VARCHAR(255) NOT NULL,
  message TEXT NOT NULL,
  iv TEXT NOT NULL
)";
$conn->prepare($sql)->execute();

?>

This will create 2 tables. One for users where we will store each user’s private and public key. Second table where we will store all our encrypted messages. We will also store IV (initialization vector) required for decrypting the message. The IV will also be encrypted. Run the following URL in the browser.

http://localhost/end-to-end-encryption-js-php-mysql/index.php

You need to insert 2 users manually in the user’s table to properly understand the mechanism of end-to-end encryption.

We assume that you have a folder named end-to-end-encryption-js-php-mysql where you placed your index.php file. After running the above URL in the browser, you need to check your phpMyAdmin. You will now see your 2 tables created.

Private and public keys

Private and public keys of each user is unique and it is used to encrypt and decrypt the messages. We will encrypt the message using sender’s private key with receiver’s public key. Similarly, we will decrypt the message using logged-in user’s private and other user’s public key. So we will create a form in our index.php file.

<form onsubmit="doLogin(this)">
  <input type="email" name="email" id="email" placeholder="Enter email" />
  <input type="submit" value="Login" />
</form>

You need to perform the function below in your own login module. We are not going into the authentication because that is not in the scope of this tutorial. When the form submits, we will call an AJAX request to authenticate the user.

<script>
  function doLogin() {
    event.preventDefault()
    
    const form = event.target
    const formData = new FormData(form)
    
    const ajax = new XMLHttpRequest()
    ajax.open("POST", "login.php", true)
    ajax.onreadystatechange = function () {
      if (this.readyState == 4 && this.status == 200) {
        if (!this.responseText) {
          updateKeys()
        }
      }
    }

    ajax.send(formData)
  }
</script>

Create a file named login.php that will tell if the logged-in user has private and public keys.

<?php

require_once "db.php";
$email = $_POST["email"];

$sql = "SELECT publicKey FROM users WHERE email = ?";
$result = $conn->prepare($sql);
$result->execute([
  $email
]);
$user = $result->fetchObject();

echo ($user && $user->publicKey != null);
exit();

This will return true or false indicating if the user has public key in the database. If not, then the client side will call another AJAX request from the function updateKeys() to generate the keys and save them in database.

async function updateKeys() {
      const keyPair = await window.crypto.subtle.generateKey(
          {
              name: "ECDH",
              namedCurve: "P-256",
          },
          true,
          ["deriveKey", "deriveBits"]
      )

      const publicKeyJwk = await window.crypto.subtle.exportKey(
          "jwk",
          keyPair.publicKey
      )

      const privateKeyJwk = await window.crypto.subtle.exportKey(
          "jwk",
          keyPair.privateKey
      )

      const formData = new FormData()
      formData.append("email", document.getElementById("email").value)
      formData.append("publicKey", JSON.stringify(publicKeyJwk))
      formData.append("privateKey", JSON.stringify(privateKeyJwk))

      const ajax = new XMLHttpRequest()
      ajax.open("POST", "update-keys.php", true)
      ajax.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
          console.log(this.responseText)
        }
      }

      ajax.send(formData)
}

We are using P-256 curve algorithm to generate a key pair. Then we are exporting private and public keys JWK (JSON Web Token). To save them in database, we are converting them to JSON string. Now we need to create a file named update-keys.php that will update those keys in user’s table.

<?php

require_once "db.php";

$email = $_POST["email"];
$publicKey = $_POST["publicKey"];
$privateKey = $_POST["privateKey"];

$sql = "UPDATE users SET publicKey = ?, privateKey = ? WHERE email = ?";
$result = $conn->prepare($sql);
$result->execute([
  $publicKey,
  $privateKey,
  $email
]);

echo "Updated";
exit();

Try running the index.php file again. Enter any of the user’s email address from database and hit “Login”. You will see the message “Updated” in the browser console. But you will see it just once, because once the public keys are updated, this function won’t gets called. If you check your phpMyAdmin, you will see that the private and public key of that user will be updated. You should do that for both users so each user will have its own private and public keys.

Encrypt message

Now that each user has its own private and public keys, we can use them to encrypt messages and save them in database. Create a file named send.php that will display a form to enter sender and receiver’s email addresses and a message to encrypt.

<form onsubmit="sendMessage()" id="form-message">
  <input type="email" name="sender" placeholder="Sender email" />
  <input type="email" name="receiver" placeholder="Receiver email" />
  <textarea name="message" placeholder="Message"></textarea>
  <input type="submit" value="Send" />
</form>

We will create 2 Javascript variables that will hold the sender’s private key and receiver’s public key values.

<script>
  let publicKey = ""
  let privateKey = ""
</script>

We are using let because these values will be updated later. Create a function that will be called when the above form submits.

function sendMessage() {
  event.preventDefault()

  if (publicKey == "" || privateKey == "") {
    const form = event.target
    const formData = new FormData(form)
    
    const ajax = new XMLHttpRequest()
    ajax.open("POST", "get-keys.php", true)
    ajax.onreadystatechange = function () {
      if (this.readyState == 4 && this.status == 200) {
        const response = JSON.parse(this.responseText)
        privateKey = JSON.parse(response[0])
        publicKey = JSON.parse(response[1])
        doSendMessage()
      }
    }

    ajax.send(formData)
  } else {
    doSendMessage()
  }
}

This will first check if the private and public keys are already fetched. If fetched, then it will call doSendMessage() function that we will create later. If not fetched, then we will first fetch the keys and then call the 2nd function. We are using this check because if you are sending multiple messages to the same recipient, then it should not get private and public keys on each send message request.

Now we will create a file named get-keys.php to fetch the sender’s private key and receiver’s public key.

<?php

require_once "db.php";

$sender = $_POST["sender"];
$receiver = $_POST["receiver"];

$sql = "SELECT privateKey FROM users WHERE email = ?";
$result = $conn->prepare($sql);
$result->execute([
  $sender
]);
$userSender = $result->fetchObject();

$sql = "SELECT publicKey FROM users WHERE email = ?";
$result = $conn->prepare($sql);
$result->execute([
  $receiver
]);
$userReceiver = $result->fetchObject();

echo json_encode([
  $userSender->privateKey,
  $userReceiver->publicKey
]);
exit();

When the keys are returned on the client side, the variables will be updated and the second function will be called to send the message.

async function doSendMessage() {
      const form = document.getElementById("form-message")
      const formData = new FormData()
      formData.append("sender", form.sender.value)
      formData.append("receiver", form.receiver.value)

      const publicKeyObj = await window.crypto.subtle.importKey(
          "jwk",
          publicKey,
          {
              name: "ECDH",
              namedCurve: "P-256",
          },
          true,
          []
      )

      const privateKeyObj = await window.crypto.subtle.importKey(
          "jwk",
          privateKey,
          {
              name: "ECDH",
              namedCurve: "P-256",
          },
          true,
          ["deriveKey", "deriveBits"]
      )

      const derivedKey = await window.crypto.subtle.deriveKey(
          { name: "ECDH", public: publicKeyObj },
          privateKeyObj,
          { name: "AES-GCM", length: 256 },
          true,
          ["encrypt", "decrypt"]
      )

      const encodedText = new TextEncoder().encode(form.message.value)
      const iv = new TextEncoder().encode(new Date().getTime())
      const encryptedData = await window.crypto.subtle.encrypt(
          { name: "AES-GCM", iv: iv },
          derivedKey,
          encodedText
      )
      const uintArray = new Uint8Array(encryptedData)
      const string = String.fromCharCode.apply(null, uintArray)
      const base64Data = btoa(string)
      const b64encodedIv = btoa(new TextDecoder("utf8").decode(iv))

      formData.append("message", base64Data)
      formData.append("iv", b64encodedIv)
  
      const ajax = new XMLHttpRequest()
      ajax.open("POST", "send-message.php", true)
      ajax.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
          console.log(this.responseText)
        }
      }

      ajax.send(formData)
}

We will use the same P-256 curve algorithm to import the private and public keys we used to exporting it. Then we will create derived key from both (private and public) keys. We will use the derived key, IV and encoded message to encrypt the message. Once the message is encrypted, we will convert the encrypted message and IV to base64 string and send them in the AJAX request. IV will be used to decrypt the message. Then we will create a file named send-message.php to save the data in the database.

<?php

require_once "db.php";

$sender = $_POST["sender"];
$receiver = $_POST["receiver"];
$message = $_POST["message"];
$iv = $_POST["iv"];

$sql = "INSERT INTO messages(sender, receiver, message, iv) VALUES (?, ?, ?, ?)";
$result = $conn->prepare($sql);
$result->execute([
  $sender,
  $receiver,
  $message,
  $iv
]);
echo $conn->lastInsertId();

Run the file send.php in the browser. Enter sender and receiver’s email address, type the message and hit “send”. If all goes well, then you will see the inserted message ID in the browser console.

Decrypt message

Now we need to decrypt the encrypted messages. Create a file named read.php. Here we will create a form to enter sender and receiver’s email address to fetch their messages.

<form onsubmit="readMessages()" id="form-read">
  <input type="email" name="sender" placeholder="Sender email" />
  <input type="email" name="receiver" placeholder="Receiver email" />
  <input type="submit" value="Read" />
</form>

Then we will create a Javascript function that will be called when the above form submits.

function readMessages() {
  event.preventDefault()

  const form = event.target
  const formData = new FormData(form)
  
  const ajax = new XMLHttpRequest()
  ajax.open("POST", "get-messages.php", true)
  ajax.onreadystatechange = async function () {
    if (this.readyState == 4 && this.status == 200) {
      const response = JSON.parse(this.responseText)

      const publicKeyObj = await window.crypto.subtle.importKey(
          "jwk",
          JSON.parse(response.publicKey),
          {
              name: "ECDH",
              namedCurve: "P-256",
          },
          true,
          []
      )

      const privateKeyObj = await window.crypto.subtle.importKey(
          "jwk",
          JSON.parse(response.privateKey),
          {
              name: "ECDH",
              namedCurve: "P-256",
          },
          true,
          ["deriveKey", "deriveBits"]
      )

      const derivedKey = await window.crypto.subtle.deriveKey(
          { name: "ECDH", public: publicKeyObj },
          privateKeyObj,
          { name: "AES-GCM", length: 256 },
          true,
          ["encrypt", "decrypt"]
      )

      for (let a = 0; a < response.messages.length; a++) {
        const iv = new Uint8Array(atob(response.messages[a].iv).split("").map(function(c) {
            return c.charCodeAt(0)
        }))
        const initializationVector = new Uint8Array(iv).buffer
        const string = atob(response.messages[a].message)
        const uintArray = new Uint8Array(
            [...string].map((char) => char.charCodeAt(0))
        )
        const decryptedData = await window.crypto.subtle.decrypt(
            {
                name: "AES-GCM",
                iv: initializationVector,
            },
            derivedKey,
            uintArray
        )
        const message = new TextDecoder().decode(decryptedData)
        console.log(message)
      }
    }
  }

  ajax.send(formData)
}

This will call an AJAX request to get the messages. The API will also return the private and public keys required to decrypt the message. Same code can be used to import the keys that we used for sending the message. Create a file named get-messages.php and write the following code in it.

<?php

require_once "db.php";

$sender = $_POST["sender"];
$receiver = $_POST["receiver"];

$sql = "SELECT * FROM messages WHERE sender = ? AND receiver = ?";
$result = $conn->prepare($sql);
$result->execute([
  $sender,
  $receiver
]);
$messages = $result->fetchAll(PDO::FETCH_OBJ);

$sql = "SELECT privateKey FROM users WHERE email = ?";
$result = $conn->prepare($sql);
$result->execute([
  $sender
]);
$userSender = $result->fetchObject();

$sql = "SELECT publicKey FROM users WHERE email = ?";
$result = $conn->prepare($sql);
$result->execute([
  $receiver
]);
$userReceiver = $result->fetchObject();

echo json_encode([
  "messages" => $messages,
  "privateKey" => $userSender->privateKey,
  "publicKey" => $userReceiver->publicKey
]);
exit();

If you run the read.php file now, you will see the decrypted messages in console tab. However, if you see the “network” tab of browser, you will see that the messages are being returned encrypted from the server. That means that your messages are decrypted online when they arrived on the client side. Thus, they are safe in-transit.

end to end encryption
end to end encryption

That’s how you can do end-to-end encryption in Javascript with PHP and MySQL. No external library has been used in this tutorial, so the code used here will work on all frameworks.