Logo

Web

KYLaNet Website Development

Table of Contents

Website Development

Webhost Integration

Timeline

This page will explain the programming framework and for this website and how the coding process is built to easily use templates or make updates to the contents/databases.

Future features to add:

  • Projects-Systems: History of Building Servers
  • Hobbies navigation bar: History of hobbies/music/art
  • Softaculous apps: Kanban/ToDoList
  • User Accounts

Basic Information

Server is built using ExpressJS application with:

  • handlebar templates
  • marked language

Folder Structure

Folder Structure

The files are all organized in the following folders:

Name Function
config contains database configuration for mySQL server
contents contains all the page contents in markdown files (.md)
controllers handles income requests/responses for routing subdomains
inactive-pages archive html/markdown files that are not used
middlewares handles authentication/validators for user accounts
models provide templates for navigation bars, lists, and database objects
public any assets (styles, js, html, resources) that is accessible through the browser
routes contains route paths for different subdomains with their actions
services contains backend functions to enable services for viewers
temp any test pages/scripts for the current interation
utils contains functions that simplify/organize the code
views contain pages/layouts to add specific partials to different templates

Automated:

Name Function
.vscode automate sftp connection betweeen VSCode and the webhost
bin contains the executable server the build configs during debug/release
node.modules contains third-party libraries for NodeJS Express app
package-lock.json an automated record of all the NodeJS packages/versions




Code Snippets

The following descriptions will show files that create the foundation of the website:

settings.json: allows automatic syncs through an FTP server

Upon saving in VSCode, the code automatically uploads/downloads files between the local computer and the remotePath on KYLaNet's webhost.

  • the port number is provided by the webhost
  • remote/key path required for ftp to login
  • ignore all the folders/files listed
{
    "name": "Diagnostic KYLaNet",
    "host": "ftp.***",
    "protocol": "sftp",
    "port": ***,
    "username": ***,
    "remotePath": ***,
    "uploadOnSave": true, 
    "privateKeyPath": ***,
    "passphrase": ***,
    "ignore": [
        ".vscode",
        ".git",
        ".DS_Store",
        "node_modules"
    ],
    "connectTimeout":***,
    "syncOption": {
        "delete": ***
    }
}

appconfig.js: configures an Express app engine on the HTTP server

Some of these configurations include:

  • create errors handler
  • paths
  • cookier parser
  • express engine using default layouts, helpers, partials and where to find them
  • create session keys
  • define url routers
  • create static sessions
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
const session = require('express-session');
var hbs = require('express-handlebars');

var app = express();

app.use(connectLiveReload());

app.engine('hbs', hbs.engine({
    allowProtoMethodsByDefault: true,
    extname: 'hbs', 
    defaultLayout: 'noside.layout.hbs', 
    helpers: require('***').helpers,
    layoutsDir: path.join(__dirname, '***'),
    partialsDir  : [
        //  path to your partials
        path.join(__dirname, '***'),
    ]
  }));

  app.use(session({
  secret: '***', 
  saveUninitialized: true,
  resave: false,
  cookie: { path: '/',
          httpOnly: true,
          secure: false,
          maxAge: *** }}));

//#region engine setup
app.set('views', [
  path.join(__dirname, '***')]);

app.set("view engine", "hbs");

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());

app.use(express.static(path.join(__dirname, "***")));

//#endregion

//#region routers

//routers
app.use("/",require("***/index.routes"));
app.use("/portfolio",require("***/portfolio.routes"));
app.use("/visualarts",require("***/creator.routes"));
app.use("/projects",require("***/projects.routes"));
//#endregion

//#region errors
// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  
  res.locals.errCode = err.status?err.status:'500';
  res.locals.errMsg = err.message;
  res.locals.errName = err.name;
  console.log('=== app Error ===');
  console.error(err.stack);
  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

//#endregion

module.exports = app;

tutorial.content.js: these text elements are parsed into HTML tags automatically

Some features:

  • regularly add numbering/bullet points
  • the [ ] symbols automatically create hyperlinks
  • ** show bolded
  • dashes or # create headers

The Resume page consists entirely of Markdown language


Header 1 
== 

Header 2
--

Continue typing
- then add a bullet point
- or (brackets) as normal

The hyperlinks are created [in these brackets]

###Markdown also applies to tables

| First | Second    | Third Column   |
| ----------- | ----------- | ----------- |
| This    | is the       | First Row |
| And    |  | So On |

The numbering 
1. is also
2. the same

user.controller.js : handles the sessions/cookies

Controllers handles get/post information between the front-end users and the server.

This is especially useful for:

  • pushing website features after authenticating proper sessions/cookies
  • pulling inputs from account creations and setting changes into the mySQL database
  • validating users using encryptions and web tokens
  • connect domain paths to models that decide the list of features or web servers to have
//Handling user login
const user_login_post = async (req, res) => {
    await userService.validateUser(req,res);
    let user = userService.getUserInfo();

    if (user.authenticated) {
        // simulating a database call
        session = req.session;
        session.user = user;
        res.redirect(302, ("/user/profile/" + user.id));
    } 
    else {
        res.render("./user/login", {
            
            title: "Login",
            status: user.status,
            layout: false
        });
    }
}

def.model.js: organizes account databases and template lists that apply to file names or navigation bars

A model of what routers will send to the front-end

   dataRouteModel: {
        route:'/',
        layout: false,
        title: '',
        session:null,
        content: '',
        modelSidenav: null,
        modelToplist: null,
   }

An example model of objects for the mySQL/mongoDB database

const Schema = new Schema({
  name: {
    type: String,
    required: true,
  },
  value: {
    type: Boolean,
    required: true,
    default: false,
  },

}, { timestamps: true });

def.script.js: public javascript file that loads default elements for every webpage

These functions initialize either when event handlers occur or when the page loads:

  • scrolltotop
  • hoverSections
  • animateTypewriter

noside.layout.js: a template layout that uses partial HTML files to piece together elements into one complete webpage

Some layouts used in KYLaNet:

  • default: includes all available components
  • messageOnly: modular view only
  • noSide: no side navigation bar included, it consists of:
    • top icons: home and user icons
    • top navigation bar: webpage categories with their dropdowns
    • button to scroll to the top
    • body section
    • footer
  • tbd: a plain page to show the website is in progress
  • error: page that displays error

The NoSide layout consists of:

<!DOCTYPE html>
<html>

<head>
  {{>head}}

</head>

<body class="defBody">
  {{>topnav}}
  {{>toplist}}

  {{>btnTop}}
  <section class="secMain">
    {{{body}}}
  </section>

  {{>footer}}
</body>

</html>

index.route.js: the main routing script for error pages, webservices, and their chosen layouts

Some webservices include emailing the host owner and changing the layout based on who logs in:

var express = require('express');
var router = express.Router();
const emailService = require('../services/email.service');
const validator = require('../middlewares/validators/user.validator');


/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { 
    title:'Home',
  src:'home',
  developed: true,
layout: 'def.layout.hbs' });
});

router.get('/contactus', function(req, res, next) {
  res.render('contactus', {
    title:'Contact Us',
  src:'home',
layout: 'message.layout.hbs'});

});

router.get('/privacypolicy', function(req, res, next) {
  res.render('./about/privacypolicy.about.hbs', {
    title:'Privacy Policy',
  src:'home',
layout: 'message.layout.hbs'});

});

router.post('/contactus', validator.emailUser,function (req,res,next){

   res.render('contactus',{
    title: 'Contact Us',
    
    layout:'message.layout.hbs',
    status: [{msg:'Email sent successfully!'},{msg:'Redirecting to home page in 3 seconds'}]
   });
  emailService.receiveEmail(req.body.name, req.body.email, req.body.subject, req.body.message) 
});
module.exports = router;

email.service.js: receives email from the Contact Us page

Nodemailer creates a transporter to send the mail from the email input to the email listed in the transporter:

const nodemailer = require("nodemailer");

module.exports = {

    receiveEmail: async (name, email, subject, message) => {
      
        console.log("=== email service -- Func receiveEmail === ");
        const transporter = nodemailer.createTransport({
            host:"***.kylanet.com",
            port: ***,
            secure: true,
            auth: {
                user:"***.com",
                pass: "***"
            },
        });
          const mailOption = {
            from: '***',
            to: '***',
            subject: subject,
            text: `You got a message from 
            Email : ${email}
            Name: ${name}
            Message: ${message}`,
          };
          try {
            await transporter.sendMail(mailOption);
            console.log('--receiveEmail-- Message sent successfully!');
            return Promise.resolve("Message Sent Successfully!");
          } catch (err) {
            console.log('--Error receiveEmail--', err);
          }
    } 
}

markdown.util.js: simplifies the coding process by parsing the text into HTML code

During routing, the page will render pased text from the Content folder into HTML:


const fs = require("fs");
var marked = require('marked');

  function loadContents(route) {
    console.log("=== Func markdownService--loadContents ===");
    let path = `./contents/${route}.content.md`;
    console.log(path);
    if (fs.existsSync(path)) {
      // return a promise so that the html is there before rendering.
      return new Promise(function (resolve, reject) {
        fs.readFile(path, "utf8", (err, data) => {
          
          let notFoundTemplate = `
     <h2><i class="fa fa-exclamation-triangle text-warning"></i> 204 No Content</h2>
     <p>The server successfully processed the request, but is not returning any content.</p>
     <p>The content may be available again in the future.</p>`;
          if (err) reject(notFoundTemplate);
          //return resolve(converter.makeHtml(data));
         
          return resolve(marked.parse(data));
         // return resolve(data);
        });
      });
    } 
  }

// &#63; to ? helper
function htmlEscapeToText (text)
{
  return text.replace(/\&\#[0-9]*;|&amp;/g, function (escapeCode) {
    if (escapeCode.match(/amp/)) {
      return '&';
    }
    return String.fromCharCode(escapeCode.match(/[0-9]+/));
  });
}

module.exports = {
  LoadContents: loadContents
};



Webhost Integration

KYLaNet primarily uses the NodeJS. To create NodeJS application:

  1. Log into the cPanel and click Setup Node.js App

  2. Configure the following:

    • Node.js version: the identical version that you're coding in
    • Application root: where the server is set up in cPanel Files
    • application URL: the URL in your domain
    • application startup file: the file where your server configurations is written in
    • passanger log file: where all the backend console/logs will exist
  3. Create package.json file in the NodeJS root folder

    • The root folder is in cPanel's File Manager
    • Create new file and name it as package.json
    • Edit the text to add the following template, with the proper node package dependencies:
    {
        "name": ***,
        "version": "1.0.0",
        "description": ***,
        "main": "app.js",
        "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "author": ***,
        "license": ***
       
    }
    
  4. Create an SSH connection to help install the proper node modules

  1. Once connected, add your required node packages through the SSH connection

  2. To sync your remote files locally, add an settings.json file in the .vscode folder

  3. Lastly, configure your NodeJS app with a more simplified web application framework; KYLaNet uses Express.js




KYLaNet Timeline

This website was first created in 2021, here is a full timeline of the modifications that were made:

November 01, 2021

Home-built server

Started by building the webhost from an Ubuntu server using my home network.

  • Abandoned due to network security
  • Tried VPN but maintenance is tedious

HostArmada with starting Node

Bought the webhost service through HostArmada with the domain on NameCheap.

  • Created a Node app on KYLaNet
  • Started a local livereload server to simplify coding on VSCode

November 9, 2021
Nov 15, 2021

NavBars and Express

After manually coding the styles for navigation bars, I decided to use an Express.js app engine to simplify coding:

  • Navbars are split into partials/layouts
  • Utilized markdown parsing language
  • Routes to retrieve utils and contents

This was the start of the Folder Structure.

Officially published

November 20, 2021
November 27, 2021

Enabled user accounts

Implemented multiple components/services:

  • Sequelize to connect to mySQL
  • Models to line up database connections
  • Cookies to save logins
  • Sessions to changes without re-authenticating
  • Encryption/tokens/validators for password hashing

Tested with WordPress

cPanel provides multiple website building options besides NodeJS (Node is what I picked to have full control over coding). There are Softaculous apps available:

  • WordPress: Added to a subdomain for testing
  • Kanboard: Manage projects using Kanban practices
  • LuxCal: Build your personal calendar
  • Coppermine: View gallery of pictures/images/videos

User services was removed to implement later, after all website features are complete.
Some features include Softaculous apps that provide calendars/lists.

September 8, 2022
December 17, 2022

KYLaNet Errors

After a while, KYLaNet started having multiple errors such as:

  • Internal Server Error
  • Error establishing a database connection
  • Forbidden permissions

Figured out that it was an issue with .htaccess

Reinvented into a Portfolio

Restructured the theme and colour pallette.
Removed all the noise and cleaned up code.

February 17, 2023
June 4, 2023

Database Error

Did not touch the website since the Reinvention in June but I could not access the website in a month.
There was an Error establishing a database connection error.

  • It showed up in every page
  • The cause was not .htaccess
  • Even rebuilt the Node app from scratch and the error persists
Reached out to HostArmada support page and the issue was resolved by the webhost; all I had to do was wait, then refresh.

Published Web Projects page

Added this current page in Projects to showcase my entire coding process in KYLaNet.

March 16, 2024

Note: This timeline view is sourced from dcode.