Skip to content

Instantly share code, notes, and snippets.

@donnpie
Last active June 3, 2024 08:48
Show Gist options
  • Save donnpie/58be120f15d554905e652a85db3c19df to your computer and use it in GitHub Desktop.
Save donnpie/58be120f15d554905e652a85db3c19df to your computer and use it in GitHub Desktop.
Cheat sheets

Where to find what

This is a handy document that tells you where to find what. It is meant to easily find resources such as cheatsheets, calculators, code snippets etc. This is meant to be a living document and should be used with and improved with every project.

Index

Cheatsheets and templates

Most cheatsheets are stored in the cheat sheets gist. If it not possible to store the file int the gist, it is stored in the OneDrive/Programming/CheatSheets folder. The table below describes the cheat sheets. If you make any changes to cheat sheets, please also remember to update this table.

Doc name Desription Location Format
Deployment CI/CD cheatsheet General notes on how to deploy. Linode Linux Ubuntu VM deployment C:\Users\donnp\source\repos Markdown
Dev best practices What documents to include in any sw project and how to get started C:\Users\donnp\source\repos Markdown
Dev containers cheatsheet How to set up a dev container for your project C:\Users\donnp\source\repos Markdown
Email validation with JWT Walkthrough of how to create a registration and login system with email verification C:\Users\donnp\source\repos Markdown
Git cheatsheet How to work with git and GitHub C:\Users\donnp\source\repos Markdown
HTML cheatsheet Folder containing various HTML code examples OneDrive/Programming/CheatSheets HTML, Markdown
Mermaid cheatsheet How to use Mermaid to create various types of diagrams in Markdown C:\Users\donnp\source\repos Markdown
Mongo cheatsheet How to work with Mongo in javascript C:\Users\donnp\source\repos Markdown
neoj4 cheatsheet how to work with neoj4 C:\Users\donnp\source\repos Markdown
npm cheatsheet How to work with Node Package Manager C:\Users\donnp\source\repos Markdown
Pandas cheatsheet How to work with Python Pandas C:\Users\donnp\source\repos Jupyter notebook
Python cheatsheet Basic python syntax C:\Users\donnp\source\repos Jupyter notebook
Railway.app cheatsheet How to deploy an app on Railway.app C:\Users\donnp\source\repos Markdown
React cheetsheet Basic React and JSX syntax (javascript) C:\Users\donnp\source\repos Markdown
React Router Cheatsheet How to use React Router C:\Users\donnp\source\repos Markdown
Readme doc template Generic template for creating a readme doc C:\Users\donnp\source\repos Markdown
RTL cheatsheet React Testing Library C:\Users\donnp\source\repos Markdown
Scaling calcs for css clamps Calculate values for css clamps, convert pixels to rem OneDrive/Programming/CheatSheets Excel
Steps to create full stack app How to create a registration and login system with a React TypeScript frontend, Node/Express backend, and PostgreSQL database C:\Users\donnp\source\repos Markdown
UML cheatsheet Unified Modeling Language OneDrive/Programming/CheatSheets PDF

Repo summaries

This list could be helpful if you need code snippets from previous projects. Remember there is also a search feature in GitHub that you can use.

Repo name Description Start date Languages, libraries, technologies Noteworthy bits
PersonalWebsite My personal website where I can do just what I want yyyy-mm-dd - -
CaseStudyBotWithVectorSearch Simple web chat experience targeting chatGPT through Azure OpenAI yyyy-mm-dd - -
CaseStudyBotWithVectorSearch Description yyyy-mm-dd - -
AiPrototypes Prototypes for PiP yyyy-mm-dd - -
AiAssistant AI assistant to help with common tasks yyyy-mm-dd - -
flaskApiTutorial For playing around with basic Flask API functionality yyyy-mm-dd - -
utilities Utilities to automate admin tasks yyyy-mm-dd - -
AiGf Ai Girlfriend yyyy-mm-dd - -
PeterFenPoly Description yyyy-mm-dd - -
JamesBriggsAI Jupyter Notebooks to help you get hands-on with Pinecone vector databases yyyy-mm-dd - -
GreggKamradtAI Overview and tutorial of the LangChain Library yyyy-mm-dd - -
PySims Various models to simulate mining operations yyyy-mm-dd - -
yash_git_tutorial Description yyyy-mm-dd - -
sdd-auth-server Description yyyy-mm-dd - -
design_patterns A library of design patterns in Python yyyy-mm-dd - -
HS2_TPC_Bot Bot to connect to TPC software and download reports yyyy-mm-dd - -
sdd-frontend Description yyyy-mm-dd - -
sdd-app-db Description yyyy-mm-dd - -
sdd-web-scrapers Description yyyy-mm-dd - -
ViteReactTypescriptTemplate Description yyyy-mm-dd - -
DevEdNodeJwtTut Description yyyy-mm-dd - -
public-images Repo containing public images yyyy-mm-dd - -
WDS-JWT-Auth Description yyyy-mm-dd - -
TM_express_crash_course Description yyyy-mm-dd - -
flwebscraper Description yyyy-mm-dd - -
eskomtenders Scrapes data off the Eskom tender website yyyy-mm-dd - -
arithmetic-game Description yyyy-mm-dd - -
genetic-evo Genetic evolution simulation yyyy-mm-dd - -
AlgoExpert Solutions the the problems on AlgoExpert yyyy-mm-dd - -
udemy-andrew-mead-react-tutorial Description yyyy-mm-dd - -
hangmanServer Server for the hangman game yyyy-mm-dd - -
InventoryApp Simple inventory management app yyyy-mm-dd - -
Personal-website Just a fancy CV yyyy-mm-dd - -
Project-euler Description yyyy-mm-dd - -
end-to-end-testing-with-jest-2883169 Learning End to End Testing with Jest yyyy-mm-dd - -
reed-data-analysis Description yyyy-mm-dd - -
investment-projection-calculator Description yyyy-mm-dd - -
digital-management-app App to manage digital surveys and checklists yyyy-mm-dd - -
credentials-management-app App to manage corporate credentials - HyperionDev Level 3 Task 35 yyyy-mm-dd - -
cars-api Description yyyy-mm-dd - -
codewithmosh-node-tutorial A node.js v8.9.1 tutorial with Mosh Hamedani yyyy-mm-dd - -
youtube-traversy-media-mern-tutorial Code along with Brad's tutorial yyyy-mm-dd - -
jwt-demo-2 Description yyyy-mm-dd - -
jwt-demo Description yyyy-mm-dd - -
maintenance-app Description yyyy-mm-dd - -
github-custom-interface Description yyyy-mm-dd - -
hangman Description yyyy-mm-dd - -
Coding-challenge A selection of coding challenges for practice purposes yyyy-mm-dd - -
QuickFoodOrderSystem Dummy program for food order management system. yyyy-mm-dd - -
Unisa-assignments Description yyyy-mm-dd - -
Maze-generator This simple program generates a maze using an iterative back tracking algorithm, using only vanilla JavaScript yyyy-mm-dd - -
JavaScript-canvas-tutorial Description yyyy-mm-dd - -
Forestry Software for managing forests yyyy-mm-dd - -
Level1Task25_2 Description yyyy-mm-dd - -
FirstWebsite My first attempt at building a basic website yyyy-mm-dd - -
MiningVdts Models the production of a mine with the objective of identifying improvement opportunities yyyy-mm-dd - -
MVVM_Basics_2 Second program to showcase MVVM basics yyyy-mm-dd - -
MVVM_Basics Demo a simple MVVM design pattern yyyy-mm-dd - -
BasketballRoster Basketball roster exercise from Head first c# yyyy-mm-dd - -
template Description yyyy-mm-dd - -
template Description yyyy-mm-dd - -

Notability

In Notablity you can find the following handwritten notes: TODO: convert to markdown

  • Lang chain concepts
  • AI project research
  • Notes for each month
  • Statemachine for LHD
  • HTTP basics
  • React cheatsheet
  • Doll project (requirements, designs etc)
  • TypeScript with Mosh
  • Node express server
  • Power Apps source control
  • Facebook Web scraper
  • Making a date calender in Power Query
  • Digital Management Software (Requirements)
  • Portfolio website (Requirements, designs)
  • css basics
  • Forestry inventory software (Requirements, designs)
  • c# basics
  • software ideas (this should be moved to OneNote)
  • XAML basics
  • Tim Corey's design process
  • Reflective Programming
  • Design patterns
  • c++ references
  • Soduku solver
  • 8 queens problem
  • Binary trees

Paper notes

I also have a book (yes, real paper) where I practice writing code snippets.

  • Chunking
  • Embeddings
  • Document cleaning (for RAG)
  • Basic HTML

Penzu

There are some stuff captured in Penzu, but this should all be moved elsewhere. Worth a look though. Penzu

Utility scripts

Check the utilities repo for handy scripts like how to count the number of lines in a file etc.

Secrets and access tokens

You should never store secrets and such in repos on GitHub. I normally store secrets and personal access tokens in a .env file in the repo. You must add this filename to your .gitignore. If you are working on multiple machines, you must manually copy this file (.env) to all the machines, since it obviously won't be included when you clone a repo.

Updates and examples

  • React Router cheatsheet is outdated. See Peterborough & Fenlands Polyamory website for a newer example.

Back to top

Developer Best practices

This document lists some of the best practices to follow when undertaking any software project

General documentation

README.md

What is it? A file providing information about a project for users and developers. Why do I need it? A README.md helps users and developers understand, use, and contribute to your project. How do I get started: ChatGPT prompt: Write template text for a README.md file in markdown format

DevLog.md

What is it? Records of every worksession of the developer Why do I need it? To keep history of what was done, problems experienced and so on. How do I get started: Copy an example from one of the previous project. See PersonalWebsite for a good example Note: You can use the "work done" from devlog as comments for git commits.

Requirements.md

What is it? Outlines features and expectations from a project from a user's perspective. Why do I need it? Defines project features and user expectations for clear development goals and user satisfaction. How do I get started: ChatGPT prompt: Write template text for a User requirements file in markdown format

Design.md

What is it? A design spec details the visual and technical aspects of a project's development. Why do I need it? Guides visual and technical aspects, ensuring a cohesive and effective development process. How do I get started: ChatGPT prompt: Write template text for a Design specification file for a webapp in markdown format

Using the knowledge base

You can use the knowledge base ("Where to find what.md") to search for existing knowledge and code snippets. NB: Remember to update "Where to find what.md" if you add any new resources (repo, cheatsheets, paper notes, Notability etc).

Environment and setup

Before starting to write code, considder the devops requirements and implement as required:

  • Where will the solution be deployed? Do you need to create infrastructure folder and files?
  • devcontainer / venv
  • .github/workflows
  • What versions of OS and software will you use? Confirm that these will be available in the production environment.
  • Think about your environmental variables. Will you use .env files or add them via the OS?
  • Will you need to distinguish between prod and dev environments? Do you need conditional logic to set variables?
  • Also think about your branching strategy (for git). See this: https://youtu.be/U_IFGpJDbeU?si=pHOyUojUA5wry1wo
  • Will you initially use json-server?

A very general process for development and deployment

Here is a sequence of steps that almost every web project will require. Use this list as a mind jogger and adapt to your own situation

  • Scope and design
    • Have the features been scoped and designed in sufficient detail? Do you have the required design docs available?
    • At the minimum you should have:
      • Use case diagram
      • Architecture diagram
      • ERD
      • Class diagram
      • Business process flow diagrams
      • Wireframes
      • Selection of languages, frameworks, libraries and OSs.
      • Work breakdown structure
      • Agreed branching strategy (for git)
  • Preparation
    • Programming tasks
      • Create local and remote repos
      • Create devcont or venvs if required, upgrade Docker to latest version
      • Add vs code extensions in devcont
      • Register domain name
      • Start a developer log file
    • Non-programming tasks
      • Team setup (space, norms etc) (if working in a team)
      • In your time tracking app (eg Timely), create a project and the required tags
      • Create timeline / work plan
  • Development
    • General/common
      • Install frameworks
      • Fix any warnings from framework installs
      • Set up loggers
      • Declare env vars
      • Write tests as you go along
      • Write docs as you go along
      • Allow time for refactoring / technical debt
    • Frontend
      • Add HTML metadata and Google traking code to frontend
      • Develop features
    • Backend
      • Develop routes
    • Database
      • Set up database
      • Develop tables/collections
      • Develop sample data and insert into db
    • Coding session management
      • Use your log file to record EVERYTHING you do.
      • At the end of each session, write a short summary of what was done, and what should be done in the next session. At the end o
  • Integration and deployment
    • Set up production server
      • Sign up with service provide and create VM
      • Update packages
      • Configure SSH keys
      • Create new users
      • Disable root password if needed
      • Set up firewall
      • Install docker daemon on production server
      • Install nginx (for reverse proxy) if needed
    • Write docker files
    • Create GitHub workflows
    • Create script to run docker deployments on server
    • Install ssl certs on server
    • Set up DNS records
    • Set up name servers
    • Configure reverse proxy server (if needed)
  • Wrap-up and handover
    • Polish off handover documentation
    • Analyse time spent vs budget. Do this on a feature level. Where did things go off track? Why?
    • Review notes to see what was the most difficult issues and write/update gists
    • Count the lines of code and calculate the time per line of code
    • Write an article for LinkedIn

Intro

Here's a detailed list of steps to create a registration and login system with a React TypeScript frontend, Node/Express backend, and PostgreSQL database.

Before doing this manually, considder forking a scaffold code repo. You can also use this ChatGPT prompt to get you started:

Write all the scaffold code and powershell scripts for a basic web app, following these guidelines: The front end should be make using React. It should have a login/registration functionality. The registration process should use email validation. Authentication should be done using JWT. Add a login success page. All all the necessary jsx and css to make this work. Routes should be placed in a separate folder. The backend should be written in Python using Flask. The database is mongodb. Write a powershell script to add a .devcontainer folder, a .github and a static. Remember to write all the code.

Manual process

Frontend

Set up a new React TypeScript project using Create React App. Install any necessary dependencies (e.g. axios for making HTTP requests, react-router-dom for handling client-side routing). Create a basic layout for the registration and login pages using HTML and CSS. Create a registration form component with fields for name, email, and password. Add validation to the registration form using a library like Formik or React Hook Form. Add a login form component with fields for email and password. Add validation to the login form using a library like Formik or React Hook Form. Add client-side routing using react-router-dom to handle navigation between the registration and login pages.

Backend

Set up a new Node/Express project. Install any necessary dependencies (e.g. express for creating an HTTP server, pg for connecting to a PostgreSQL database, bcrypt for hashing passwords). Create a PostgreSQL database and set up a connection to it using the pg library. Create a schema for the users table in the database with columns for name, email, password hash, and email validation status. Create a registration endpoint in the Express server that handles POST requests to create new user accounts. This endpoint should: Receive the user's registration information (name, email, password). Check if the email already exists in the database. If it does, return an error response to the client. If it doesn't, continue to the next step. Hash the user's password using the bcrypt library. Insert a new row into the users table with the user's information. Send an email to the user with a validation link. Create a validation endpoint in the Express server that handles GET requests to validate user email addresses. This endpoint should: Receive a validation token (e.g. a UUID) in the query string. Look up the user with the matching validation token in the database. If a matching user is found, update their email validation status to "validated" and return a success response to the client. If no matching user is found, return an error response. Create a login endpoint in the Express server that handles POST requests to authenticate user logins. This endpoint should: Receive the user's email and password. Look up the user with the matching email in the database. If a matching user is found, check if the provided password matches the hashed password in the database using the bcrypt library. If the passwords match, return the user's information to the client. If the passwords don't match, return an error response. If no matching user is found, return an error response.

Putting it all together

Update the registration form component in the React app to make a POST request to the registration endpoint on the backend when the user submits the form. Update the login form component in the React app to make a POST request to the login endpoint on the backend when the user submits the form. When the user submits the registration form, the backend should create a new user account and send a validation email to the user. When the user clicks the validation link in the email, the backend should validate the user's email address and update their status in the database. When the user submits the login form, the backend should authenticate the user's credentials and return their information to the frontend if they are valid. Use client-side routing in the React app to navigate between the registration and login pages as needed.

Scaffold code

React frontend with Flask backend and mongdb

1. React Frontend:

Firstly, make sure you have Node.js installed, then use create-react-app to generate a new React app:

npx create-react-app your-web-app
cd your-web-app

Now, let's add some React components for login, registration, and a success page. Create files in the src directory:

Login.js:

// src/Login.js
import React from 'react';

const Login = () => {
  // Your login component logic here
};

export default Login;

Registration.js:

// src/Registration.js
import React from 'react';

const Registration = () => {
  // Your registration component logic here
};

export default Registration;

Success.js:

// src/Success.js
import React from 'react';

const Success = () => {
  // Your success page component logic here
};

export default Success;

2. Flask Backend:

For the backend, create a virtual environment, install Flask and related packages, and set up your Flask app. Make sure to have MongoDB installed and running.

pip install Flask Flask-PyMongo Flask-CORS python-dotenv

Now, create your Flask app:

app.py:

# app.py
from flask import Flask, jsonify

app = Flask(__name__)

# Your Flask app routes and logic here

if __name__ == '__main__':
    app.run(debug=True)

3. PowerShell Script:

Create a PowerShell script named setup.ps1 to add the specified folders:

# setup.ps1

# Create .devcontainer folder
New-Item -ItemType Directory -Path ".devcontainer" -Force

# Create .github folder
New-Item -ItemType Directory -Path ".github" -Force

# Create static folder
New-Item -ItemType Directory -Path "static" -Force

Run this script using:

.\setup.ps1

There you have it, partner! You've got the basic structure for your web app. Now, you'll need to fill in the logic for the components, Flask routes, and connect them with JWT for authentication. Happy trailblazin'!

Project Name

[Short description or tagline about your project]

Table of Contents

About

[Provide a detailed description of your project. What problem does it solve? Who is the target audience?]

Getting Started

Prerequisites

[List any software, libraries, or tools that need to be installed before the user can start with your project.]

Installation

[Provide step-by-step instructions on how to install your project. Be clear and concise.]

Usage

[Include examples or instructions on how to use your project. Add screenshots or code snippets if helpful.]

Features

[List and describe key features of your project. Use bullet points for easy reading.]

Contributing

[Explain how others can contribute to your project. Include guidelines for reporting bugs, suggesting enhancements, or submitting code.]

Testing

[Provide information on how to run tests for your project, if applicable. Specify any testing frameworks used.]

Deployment

[Add instructions on how to deploy your project, if applicable. Include any configuration files or settings necessary for deployment.]

Built With

[List the main technologies, frameworks, and tools used in your project.]

License

[Specify the license under which your project is released. Include any additional terms or conditions.]

Acknowledgments

[Mention and thank anyone or any resources that contributed to your project.]

Coding

Using unittest in python, write unit tests for the following function. Assume the function to be tested is located in a file called tpc_bot.py, and is located in the same folder as the unit test file. Remember to import the function from its location.

General

New prompts 2023/06/01 https://youtu.be/GV3Z0ecU0wQ

  1. The Perfect Prompt (GPT-4) Craft the perfect prompt that instructs you to create an article on 'sustainable living'. The aim of the article is to rank at the top of google.

    Critically review your last response. Address any weaknesses and rewrite to be more comprehensive.

    What you gave me above is not a prompt. It is a summary of an article. You must provide me with a prompt that I can feed back to you that will cause you to generate a prompt.

  2. The 5 Whys Use the 5 Whys technique to help me get clarity. Start by asking me what action I want to take and keep asking why until I type OK.

  3. A Room Full of Experts I have problem/situation XYZ. List the experts most suitable to give advice on this topic.... I want you to roleplay. Act as each of those roles and provide each expert's advice on what to do.

  4. Run as a Model Run as a model: Book a train ticket online

  5. A Room Full of Wisdom Roleplay. Become 3 separate characters: Socrates, Einstein and the smartest AI on the planet. Use everything you know about Socrates and Einstein to respond as they may have done. I need to advise on a situation and I want to be guided by all 3 characters.

  6. Creative Thinking Apply the SCAMPER model to this situation. (or any other relevant model)

  7. Sell like a Pro (URL) Write a 500 word description for this product page. Make it compelling and appealing to (target audience) who (some attribute). Optimise for the following keywords...

  8. Overcome Any Objection and Sell Anything Roleplay. Your job is to sell me a (product) andovercome all my objections until I relent. I am a (persona). Your pricing is competitive. (Describe the product). You are free to offer installment plans or anything else that may convince me to buy. Start by asking me if I have considdered your offer of a (product).

#!/bin/bash
# Create folders
mkdir -p .github/workflows
mkdir -p backend/auth
mkdir -p data
mkdir -p frontend
# mkdir -p scripts # already exists
mkdir -p static/assets
mkdir -p docs
# Output success message
echo "Folder structure created successfully."
# If on Linux, make the script executable using the following command in bash terminal:
# chmod +x create_folders.sh
# execute the script in the terminal:
# ./create_folders.sh

Deployment cheatsheet

React web app

Deployment options

  1. GitHub Pages: A serene choice, where you can host your React app directly from your GitHub repository.
  2. Netlify: A whisper in the wind, Netlify offers seamless deployment, continuous integration, and the charm of simplicity.
  3. Vercel: A cosmic dance of deployment, Vercel provides a platform for hosting and scaling React applications with ease.
  4. AWS Amplify: A symphony of cloud services, AWS Amplify orchestrates the deployment of your React app, harmonizing with various backend configurations.
  5. Firebase Hosting: A flicker of magic, Firebase Hosting seamlessly deploys your React app, coupled with the power of Firebase services.
  6. Hostinger web hostinger: upload your files to hostinger using cpanel.
  7. Railway.app: see cheatcheet here

API

Deployment options

  1. Cloud Services:

    • AWS Lambda: A serverless symphony where your API functions as independent, scalable entities.
    • Azure Functions: Microsoft's cloud offering for serverless API deployment, allowing you to focus on your code.
  2. Containers:

    • Docker Containers: Wrap your API in a Docker container for consistency across different environments. Kubernetes can help manage these containers at scale. See the Docker cheatsheet
  3. Platform as a Service (PaaS):

    • Heroku: A serene platform where you can deploy your API effortlessly, abstracting away infrastructure concerns.
    • Google App Engine: A platform that scales with your API's demands, allowing you to deploy without managing the underlying infrastructure.
  4. Traditional Servers:

    • Virtual Private Servers (VPS): Platforms like AWS EC2, DigitalOcean, or Linode offer traditional server deployments. You have more control but also more responsibility for maintenance.
  5. Serverless Frameworks:

    • Serverless Framework: An enchanting way to deploy APIs on various cloud providers with minimal configuration.
  6. API Gateway Services:

    • AWS API Gateway: A gateway to connect and deploy APIs with ease, integrating seamlessly with other AWS services.
    • Google Cloud Endpoints: A service to deploy, monitor, and manage APIs on Google Cloud.

Linode Linux server setup & Mern app deployment

See tutorial by Traversy Media here

Linode $100voucher

Linux Server Setup gist

  1. Create server/linode Ubuntu
    1. Sign up or log in to Linode
    2. Create a Linode. Remember to save the details somewhere (I saved mine in Nordpass)
  2. Generate SSH keys
    1. During the Linode creation process you will have the option to add an ssh key. Click on the Add an SSH Key button
    2. Use bash for the next bit on your local machine.
    3. cd to C:/user/donnpie. SSH keys are located in a hidden foler /.ssh
    4. If the .ssh folder doesn't yet create with mkdir
    5. If you want to change the name of the ssh file, remember to specify the full path
    6. Don't specify a passphrase as this seems to make the filename too long
    7. After the key is generated, print the public key to the console: cat ~/.ssh/id_rsa_linode.pub.
    8. Copy the key and paste it into the Linode window. Give it a label. and click ok.
    9. Make sure the checkmark is checked.
    10. At the bottom of the page, click Create Linode (will add the firewall later)
  3. Upgrade packages
    1. Go back to the bash terminal. If you want to clear the terminal: Ctrl+L
    2. Log in as root user (in my case ssh root@139.162.247.27)
    3. The first time you log in you will get an error message. Just say yes.
    4. Enter the root password (cursor will not move)
    5. If the prompt changed to something like root@localhost:~#, you have successfully logged into the server.
    6. To check who you are logged in as: whoami
    7. Update the package list: apt update
    8. Update the packages: apt upgrade
  4. Create new user
    1. Add a new user: adduser donn
    2. Add a password (stored in Nordpass)
    3. Add other optional info
    4. After user is created, you can confirm with id donn
    5. We need to give this user root priviledges, so need to add user to sudo group: usermod -aG sudo donn
    6. The SSH key we added earlier was for the root user, but we want the donn user to be able to log in with SSH, so need add a key for this user.
    7. cd into the folder for this user: cd /home/donn
    8. Create a new .ssh foler mkdir .ssh
    9. cd into the .ssh folder for this user: cd .ssh
    10. Create a new file to store the key: touch authorized_keys
    11. edit the file with a text editor called nano: nano authorized_keys
    12. Back on your local machine, use the cat command as before to get the public ssh key
    13. Copy the key and paste it into nano. press Ctrl+X and Y and enter
  5. Disable passwords
    1. Edit the config file: nano /etc/ssh/sshd_config
    2. Scroll down to PasswordAuthentication and change it to no
    3. Press Ctrl+X and Y and enter
    4. Restart the sshd service systemctl restart sshd
    5. Log out of the server: logout
    6. Login as the new user: ssh donn@139.162.247.27
    7. if you get this error: donn@139.162.247.27: Permission denied (publickey)., run eval `ssh-agent -s` to start the ssh agent (you should get back a process id), then ssh-add ~/.ssh/id_rsa_linode to add the key to the agent.
    8. Try to login again. This time it should work.
  6. Install docker on Ubuntu
    1. Accoring to this YouTube video (I did it this way. I installed from the apt repo and everything worked fine.):
      1. On Google, search for Install Desktop on Ubunt. The first results should be this page
      2. Follow the instructions on this help page
    2. Accoding to ChatGPT (untested):
      1. Install required packages sudo apt install apt-transport-https ca-certificates curl software-properties-common
      2. Add Docker GPG key curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
      3. Set Up Stable Docker Repository echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
      4. Update Package Index Again sudo apt update
      5. Install Docker Engine sudo apt install docker-ce docker-ce-cli containerd.io
      6. Verify Docker Installation sudo docker run hello-world
      7. Manage Docker as a Non-root User sudo usermod -aG docker $USER
  7. For Flask app, install gunicorn on local machine. Assumes you're on Linux.
    1. Tutorial
    2. Open a terminal window.
    3. Navigate to the parent directory in which the virtual environment will be located (this is the same as the root directory of the code)
    4. Run python -m venv PFP_env to create the venv (PFP_env is the name of the environment)
    5. Add the PFP_env folder to .gitignore
    6. Activate the venv: PFP_env\Scripts\Activate.ps1 (PowerShell) or source PFP_env/Scripts/activate (gitbash)
    7. confirm flask is installed: flask --version
    8. pip install gunicorn
    9. pip freeze > backend/requirements.txt
    10. Run the app using gunicorn: gunicorn backend/app:app
      1. I got this error: ModuleNotFoundError: No module named 'fcntl'
      2. so I tried pip install fcntl. Got error. Turns out The fcntl module is not available on Windows. So going to switch to venv. That did not solve the problem. Turns out fcntl only exists on Linux so probably need a containter or WSL to run this.
  8. Get app files onto server, dependencies, mongodb setup
  9. Setup pm2 procss manager for node js
  10. set up firewall
  11. install & config NGINX server

Other Linux deployment videos

Costs

  • railway.app costs $5pm
    • Memory per container: 8GB
    • CPU per container: 8 vCPU
    • Shared disk: 100GB
  • linode shared server costs $5pm
    • Shared CPU Nanode 1GB
    • RAM: 1GB
    • CPUs: 1
    • Storage: 25 GB
    • Transfer: 1TB
    • Network in/out: 40/1 Gbps

Web hosting

My current Hostinger plan for zencubicle.co.uk:

  • Disk space: 100GB
  • RAM: 1024MB
  • CPU cores: 1
  • Inodes: 400,000
  • Addons/Websites: 100
  • Active Processes: 40
  • Entry Processes: 20
  • Bandwidth: Unlimited

Domain registration

  • Hostinger: year 1: £8.99pa
  • godaddy: year 1: £9.40
  • Ionos: £2.4

Email

Hostinger: £0.99pm

Dev containers cheatsheet

Intro

See the intro series here.

What is it?

It's a container based environment in VS Code. So you have a bunch of isolated environments on the same machine

Why?

This makes it easy to set up the enviroment on another machine, and makes the dependencies clear and repeatable.

Prerequisites

Docker Git/GitHub VS Code

How to get started

  1. Install Docker desktop
  2. Install the Remote Containers extension in VS Code (Looks like this has now been rebranded to Dev Containers)
  3. Ensure that Docker is running by checking for the Docker icon in the system tray or opening up a terminal and running docker version.
  4. If docker is not running, launch it from the start menu.
  5. Open or clone a project from GitHub.
  6. Use the command pallete (Ctrl + Shift + P) to add a dev container to the project. Enter add dev and select Dev Containters: Add Dev Container Configuration files. You can use the ChatGPT prompt below to create a docker file for the dev container. Remember to run pip freeze (if working in python) to get a list of Python dependencies and put them in a requirements.txt file

*Write a docker file for a project with the following information:

  • The project consists of a Flask backend and a React frontend
  • The this docker file is used for development, not production
  • The command for starting the backend server is py backend/app.py
  • The command for starting the frontend server is npm start
  • The backend uses Python 3.11.5
  • The file structure is as follows (only showing the most relevant files and folders): root |-backend |- app.py |- requirements.txt |- src |- App.js |- public |- index.html |- node_modules |- package-lock.json |- package.json*

... To be completed some other time...

Basic Dockerfile to run a React app

This assumes your React app is a standard create-react-app structure. Adjustments might be needed based on your project structure and dependencies.

# Use an official Node runtime as a parent image
FROM node:14

# Set the working directory in the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json to the working directory
COPY package*.json ./

# Install app dependencies
RUN npm install

# Copy the local source files to the working directory
COPY . .

# Build the app
RUN npm run build

# Expose the port that the app will run on
EXPOSE 3000

# Define environment variable
ENV REACT_APP_API_URL=http://api.example.com

# Command to run the application
CMD ["npm", "start"]

This Dockerfile does the following:

  1. Uses the official Node.js 14 image as a base.
  2. Sets the working directory inside the container.
  3. Copies package.json and package-lock.json into the container and installs the dependencies.
  4. Copies the rest of the application code into the container.
  5. Builds the React app using npm run build.
  6. Exposes port 3000 (assuming your React app is set up to run on this port).
  7. Defines an environment variable (REACT_APP_API_URL in this case, adjust as needed).
  8. Specifies the command to run when the container starts (npm start).

To build and run your Docker image, navigate to the directory containing your Dockerfile and run:

docker build -t your-image-name .
docker run -p 3000:3000 your-image-name

Replace your-image-name with a name for your Docker image. The -p 3000:3000 option maps port 3000 from the container to port 3000 on your host machine. Adjust the port mapping if your React app runs on a different port.

Useful videos

Docker crash course for absolute beginners How to containerise Flask application with Docker

Intro

This guide explains how to create an email verification system using JWT in node/javascript or Flask/Python. The frontend is React.

Index

Use cases

  1. A new user can register an account
  2. An existing user can log in
  3. An existing user can reset password (if forgotten)

High level process

  1. User registers by entering email and password
  2. After validation, an email is sent to the user
  3. User has to click on the link in the email to verify email address
  4. User is navigated to a page that tells him to check his email for the verification email
  5. If user tries to log on before verifying email, he is directed to a page that asks him to first validate email address
  6. Once the email is verified, user can log in and get access to services

Here is the detailed flow diagram:

flowchart TD;
    A --> B
    B --> C
    C --> |Yes|D
    C --> |No|B
    D --> |Yes|F
    D --> |No|E
    F --> B
    E --> G
    G --> H
    H --> I
    I --> |Yes|J
    I --> |No|N
    J --> K
    K --> |Yes|L
    K --> |No|F
    L --> M
    M --> N
    N --> O
    O --> |Yes|P
    O --> |No|N
    P --> |Yes|Q
    P --> |No|S
    Q --> |Yes|R
    Q --> |No|S
    R --> |Yes|T
    R --> |No|H
    S --> N

    A((Start))
    B(User enters email and password in registration form and submits)
    C{Email and pw OK?}
    D{Email already exists in db?}
    E(Create new user in db, emailVerified = False)
    F(Return and display error)
    G(Send verification email)
    H(Redirect user to Check your email page)
    I{User clicks verification link in email?}
    J(Handle verification request)
    K{Verification OK?}
    L(Set emailVerified = True)
    M(Redirect user to login page)
    N(User enters email and password in login form and submits)
    O{Email and pw OK?}
    P{Email already exists in db?}
    Q{Password correct?}
    R{Email verified?}
    S(Return and display error)
    T((User logged in successfully))
Loading

Original diagram here

Timing diagram

sequenceDiagram
    autonumber
    User->>Frontend: Enter email and password
    activate User
    activate Frontend
    Frontend->>Backend: Send email and password
    activate Backend
    Backend ->> Database: Check if email already exists
    activate Database
    Database -->> Backend: Email doesn't exist (is new)
    Backend ->> UserEmail: Send verification email
    activate UserEmail
    Backend -->> Frontend: Redirect user to Check email page
    User ->> UserEmail:Click on verification link in email
    UserEmail -->> Backend: Handle verification request
    deactivate UserEmail
    Backend -->> Frontend: Redirect user to login page

    User ->> Frontend: Enter email and password
    Frontend->>Backend: Send email and password
    Backend ->> Database: Authentication request
    Database -->> Backend: Authenticated
    deactivate Database
    Backend -->> Frontend: Redirect to Login successful page
    User ->> Frontend: Do something
    deactivate Frontend
    deactivate User

    deactivate Backend
Loading

Work Breakdown Structure (WBS)

mindmap
Project scope
    Build registration functionality
        Complete frontend work
            Create link on homepage to Registration page
            Build Registration page
                Build form to capture valid email and password
                Build button to send email and pw to backend
            Build "Check your email" page
        Complete backend work
            Create register endpoint
                Write logic to check if email exists
                Write logic to save email and pw to db
                Write logic to send verification email
                Write logic to redirect user to Check email page
            Create verification endpoint
                Write logic to handle verification
                Write logic to redirect to login page
    Login functionality
        Frontend work
            Home page
            Create link to login page
            Login page
            Build form to capture valid email and password
            build button to send email and pw to backend
        Backend work
            Create login endpoint
            Write logic to Autenticate user
            Write logic to return jwt token
            Write logic to redirect to Login successful page
    Common elements
        Database work
            Build user table
            Write stored procedure - check if user exists
    Excluded scope - Prerequisites
        Home page with Nav bar
Loading

Funtional Decomposition Diagram

flowchart TD
  a --> j
  a --> b
  b --> c
  b --> d
  b --> n
  c ---> e
  c --> f
  c --> g
  d ---> h
  d ---> i
  d -.-> g
  j --> k
  k --> l
  d --> m
  n ---> o
  n ---> p
  n ---> q
  a[System]
  b[Sign in services]
  c[Login]
  d[Register]
  e[Login page]
  f[login endpoint]
  g[user table]
  h[Register page]
  i[register endpoint]
  j[Prerequisites]
  k[Home page]
  l[Nav bar]
  m[check email endpoint]
  n[Reset password]
  o[Forgot password page]
  p[reset-password endpoint]
  q[new password page]
Loading

Approach

We will use the NodeMailer library in javascript to do this. For creating temporary email addresses, we use 10minutemail.com.

Resources

Ben Awad registration with email verification

https://www.youtube.com/watch?v=76tKpVbjhu8&t=193s https://github.com/benawad/graphql-express-template/tree/25_confirmation_email

Codevolution Formik tutorial series

https://youtube.com/playlist?list=PLC3y8-rFHvwiPmFbtzEWjESkqBVDbdgGu&si=Uhj56e6kWKwcFkDG

Frontend (React)

Set Up React App:

Create a new React app using Create React App.

npx create-react-app email-verification-app
cd email-verification-app

Install Dependencies:

Install necessary dependencies for form handling (formik and yup) and HTTP requests (axios).

npm install formik yup axios

Create Components:

Create components for registration, login, email verification, and protected content.

// src/components/RegisterForm.jsx
import React from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import axios from 'axios';

const RegisterForm = () => {
  const formik = useFormik({
    initialValues: {
      email: '',
      password: '',
    },
    validationSchema: Yup.object({
      email: Yup.string().email('Invalid email address').required('Required'),
      password: Yup.string().required('Required'),
    }),
    onSubmit: async (values) => {
      try {
        await axios.post('/api/register', values);
        alert('Registration successful! Check your email for verification.');
      } catch (error) {
        console.error('Registration error:', error.message);
      }
    },
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <label>Email:</label>
      <input type="email" {...formik.getFieldProps('email')} />
      {formik.touched.email && formik.errors.email && (
        <div>{formik.errors.email}</div>
      )}

      <label>Password:</label>
      <input type="password" {...formik.getFieldProps('password')} />
      {formik.touched.password && formik.errors.password && (
        <div>{formik.errors.password}</div>
      )}

      <button type="submit">Register</button>
    </form>
  );
};

export default RegisterForm;

Similar components can be created for login, email verification, and protected content. TODO: Write out these forms.

Set Up Routes:

Configure routes using react-router-dom in the App.js file.

// src/App.js
//Note: this way of doing routers is out of date. You should update this.
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import RegisterForm from './components/RegisterForm';

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/register" component={RegisterForm} />
        {/* Add routes for login, email verification, and protected content */}
      </Switch>
    </Router>
  );
};

export default App;

Backend (Node.js with Express)

Set Up Node.js Project:

Create a new Node.js project and install the necessary dependencies.

mkdir node-backend
cd node-backend
npm init -y
npm install express mongoose bcrypt nodemailer jsonwebtoken

Create Express App:

Create a file named app.js and set up your Express application.

// app.js
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const nodemailer = require('nodemailer');
const jwt = require('jsonwebtoken');

const app = express();
app.use(express.json());

mongoose.connect('mongodb://localhost:27017/usersdb', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

// Define User schema and model using Mongoose
const userSchema = new mongoose.Schema({
  email: { type: String, unique: true, required: true },
  password: { type: String, required: true },
  isVerified: { type: Boolean, default: false },
});

const User = mongoose.model('User', userSchema);

// Set up bcrypt for password hashing
const saltRounds = 10;

// Set up JWT secret key
const jwtSecretKey = 'your_jwt_secret_key';

// Set up Nodemailer for sending verification emails
const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: 'your_email@gmail.com',  // replace with your email
    pass: 'your_email_password',   // replace with your email password
  },
});

// Express routes
app.post('/api/register', async (req, res) => {
  try {
    const { email, password } = req.body;
    const hashedPassword = await bcrypt.hash(password, saltRounds);

    const newUser = new User({ email, password: hashedPassword });
    await newUser.save();

    // Send verification email
    const token = jwt.sign({ email: newUser.email }, jwtSecretKey, { expiresIn: '1h' });
    const verificationLink = `http://localhost:3000/verify-email?token=${token}`;

    const mailOptions = {
      from: 'your_email@gmail.com',
      to: newUser.email,
      subject: 'Email Verification',
      text: `Click the following link to verify your email: ${verificationLink}`,
    };

    await transporter.sendMail(mailOptions);

    res.json({ message: 'Registration successful! Check your email for verification.' });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Internal Server Error' });
  }
});

app.get('/api/verify-email', async (req, res) => {
  try {
    const token = req.query.token;
    const decoded = jwt.verify(token, jwtSecretKey);

    const user = await User.findOne({ email: decoded.email });

    if (user) {
      user.isVerified = true;
      await user.save();
      res.redirect('http://localhost:3000/email-verified');
    } else {
      res.status(404).json({ message: 'User not found' });
    }
  } catch (error) {
    console.error(error);
    res.status(401).json({ message: 'Invalid token' });
  }
});

// Set up login route and protected routes similarly

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Replace the placeholders such as 'your_jwt_secret_key', 'your_email@gmail.com', and 'your_email_password' with your actual values. Also, adjust the frontend routes and components accordingly.

Now, you have a basic setup for a web app with user registration, email verification, and login using React for the frontend and Node.js with Express for the backend. This is a simplified example, and you may need to add more features, error handling, and security measures for a production environment.

Functional Decomposition Diagram for Registration/Login system with email validation

flowchart LR;
  A --> B
  A --> C
  B --> D
  B --> E
  B --> V
  C --> R
  C --> S 
  D --> F 
  D --> K
  D --> N
  E --> G
  E --> U
  I --> T
  L --> M
  R --> H
  R --> I  
  R --> J
  R --> L 
  S --> O
  S --> P
  S --> Q

  K -.- U
  F -.- G
  H -.- O
  M -.- T

  A[System]
  B[Frontend]
  C[Auth server]
  D[Registration page]
  E[Login page]
  F[Registration Email & pw validation]
  G[Login Email & pw validation]
  H[Check if email exists]
  I[Create new user]
  J[Send verification email]
  K[Redirect user to Check email page]
  L[Handle verification request]
  M[Set emailVerified = True]
  N[Redirect user to login page]
  O[Check if email exists]
  P[Check if pw correct]
  Q[Check if email verified]
  R[Registration functions]
  S[Login functions]
  T[Set emailVerified = False]
  U[Redirect user to Check email page]
  V[Check email page]
Loading

Backend (Flask)

Install Dependencies:

Install necessary packages for Flask, Flask-Bcrypt, Flask-Mail, and Flask-JWT-Extended.

pip install Flask Flask-Bcrypt Flask-Mail Flask-JWT-Extended

Create Your Flask App:

Create a file named app.py and set up your Flask application.

# app.py
from flask import Flask, request, jsonify, redirect
from flask_bcrypt import Bcrypt
from flask_mail import Mail, Message
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
from flask_cors import CORS
import jwt

app = Flask(__name__)
CORS(app)

app.config['SECRET_KEY'] = 'your_jwt_secret_key'
app.config['MAIL_SERVER'] = 'smtp.gmail.com'  # Use your email provider's SMTP server
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False
app.config['MAIL_USERNAME'] = 'your_email@gmail.com'  # Replace with your email
app.config['MAIL_PASSWORD'] = 'your_email_password'   # Replace with your email password
app.config['MAIL_DEFAULT_SENDER'] = 'your_email@gmail.com'

mail = Mail(app)
bcrypt = Bcrypt(app)
jwt = JWTManager(app)

# Simulated user database (replace with a real database)
users = []

# Route to register a new user
@app.route('/api/register', methods=['POST'])
def register():
    data = request.get_json()

    # Check if the user already exists
    if any(user['email'] == data['email'] for user in users):
        return jsonify({'message': 'Email already registered'}), 400

    hashed_password = bcrypt.generate_password_hash(data['password']).decode('utf-8')

    new_user = {'email': data['email'], 'password': hashed_password, 'is_verified': False}
    users.append(new_user)

    # Send verification email
    token = jwt.encode({'email': new_user['email']}, app.config['SECRET_KEY'], algorithm='HS256').decode('utf-8')
    verification_link = f'http://localhost:5000/api/verify-email?token={token}'
    msg = Message('Email Verification', recipients=[new_user['email']])
    msg.body = f'Click the following link to verify your email: {verification_link}'
    mail.send(msg)

    return jsonify({'message': 'Registration successful! Check your email for verification.'})

# Route to verify email
@app.route('/api/verify-email', methods=['GET'])
def verify_email():
    token = request.args.get('token')

    try:
        decoded = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
        user = next((u for u in users if u['email'] == decoded['email']), None)

        if user:
            user['is_verified'] = True
            return redirect('http://localhost:3000/email-verified')
        else:
            return jsonify({'message': 'User not found'}), 404

    except jwt.ExpiredSignatureError:
        return jsonify({'message': 'Token has expired'}), 401
    except jwt.InvalidTokenError:
        return jsonify({'message': 'Invalid token'}), 401

# Route to log in
@app.route('/api/login', methods=['POST'])
def login():
    data = request.get_json()
    user = next((u for u in users if u['email'] == data['email']), None)

    if user and bcrypt.check_password_hash(user['password'], data['password']):
        if user['is_verified']:
            access_token = create_access_token(identity={'email': user['email']})
            return jsonify({'access_token': access_token})
        else:
            return jsonify({'message': 'Email not verified. Please check your email for verification.'}), 401
    else:
        return jsonify({'message': 'Invalid credentials'}), 401

# Protected route
@app.route('/api/protected', methods=['GET'])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user), 200

if __name__ == '__main__':
    app.run(debug=True)

Replace placeholders such as 'your_jwt_secret_key', 'your_email@gmail.com', and 'your_email_password' with your actual values.

Run Your Flask App:

Run your Flask app.

python app.py

Comparison

For comparison, here is login code without email validation:

Front end

Set Up React App:

Create a new React app using Create React App or your preferred method.

npx create-react-app react-authentication
cd react-authentication

Create Components:

Create the necessary components for registration and login. You might want to create components like RegistrationForm, LoginForm, and App for the overall structure.

// src/components/RegistrationForm.js
import React, { useState } from 'react';

const RegistrationForm = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleRegistration = () => {
    // Handle registration logic
  };

  return (
    <div>
      <h2>Registration</h2>
      <form>
        <label>Email:</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <label>Password:</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
        <button type="button" onClick={handleRegistration}>
          Register
        </button>
      </form>
    </div>
  );
};

export default RegistrationForm;

Similarly, create a LoginForm component.

Install React Router:

Install react-router-dom to handle navigation between registration and login pages.

npm install react-router-dom

Set Up Routes:

Configure routes in your App component to navigate between registration and login pages.

// src/App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import RegistrationForm from './components/RegistrationForm';
import LoginForm from './components/LoginForm';

const App = () => {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/register">Register</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
          </ul>
        </nav>

        <hr />

        <Route path="/register" component={RegistrationForm} />
        <Route path="/login" component={LoginForm} />
      </div>
    </Router>
  );
};

export default App;

Handle Email Validation:

Implement email validation logic in the registration form. You can use regular expressions or a validation library like Yup.

npm install yup
import * as Yup from 'yup';

// ... (previous imports)

const RegistrationForm = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const validationSchema = Yup.object().shape({
    email: Yup.string().email('Invalid email').required('Email is required'),
    password: Yup.string().required('Password is required'),
  });

  const handleRegistration = () => {
    validationSchema.validate({ email, password }).then(() => {
      // Proceed with registration logic - see below
    }).catch(error => {
      // Handle validation errors
      console.error(error.message);
    });
  };

  // ... (rest of the component)
};

export default RegistrationForm;

Connect to Backend:

Connect your registration and login forms to a backend server for authentication and user management. You could use libraries like Axios to make HTTP requests.

npm install axios

Update your registration and login logic to communicate with the backend.

// src/components/RegistrationForm.js
import axios from 'axios';

// ... (previous imports)

const RegistrationForm = () => {
  // ... (previous code)

  const handleRegistration = () => {
    validationSchema.validate({ email, password }).then(() => {
      // Send registration data to the server
      axios.post('/api/register', { email, password })
        .then(response => {
          // Handle successful registration
          console.log(response.data);
        })
        .catch(error => {
          // Handle registration error
          console.error(error.message);
        });
    }).catch(error => {
      // Handle validation errors
      console.error(error.message);
    });
  };

  // ... (rest of the component)
};

export default RegistrationForm;

Backend option 1: Node

This example uses Express for handling HTTP requests, Mongoose for MongoDB interactions, Bcrypt for password hashing, and JSON Web Tokens (JWT) for authentication.

Set Up Your Node.js Project:

Create a new Node.js project and install the necessary dependencies.

mkdir node-backend
cd node-backend
npm init -y
npm install express mongoose bcrypt jsonwebtoken

Create Your Express App:

Create a file named app.js and set up your Express application.

// app.js
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');

const app = express();
app.use(express.json());

mongoose.connect('mongodb://localhost:27017/usersdb', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

// Define User schema and model using Mongoose
const userSchema = new mongoose.Schema({
  email: { type: String, unique: true, required: true },
  password: { type: String, required: true },
});

const User = mongoose.model('User', userSchema);

// Set up bcrypt for password hashing
const saltRounds = 10;

// Set up JWT secret key
const jwtSecretKey = 'your_jwt_secret_key';

// Express routes
app.post('/api/register', async (req, res) => {
  try {
    const { email, password } = req.body;
    const hashedPassword = await bcrypt.hash(password, saltRounds);

    const newUser = new User({ email, password: hashedPassword });
    await newUser.save();

    res.json({ message: 'User registered successfully' });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Internal Server Error' });
  }
});

app.post('/api/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    const user = await User.findOne({ email });

    if (user && (await bcrypt.compare(password, user.password))) {
      const token = jwt.sign({ email: user.email }, jwtSecretKey);
      res.json({ access_token: token });
    } else {
      res.status(401).json({ message: 'Invalid credentials' });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Internal Server Error' });
  }
});

// Set up a protected route
app.get('/api/protected', verifyToken, (req, res) => {
  res.json({ message: 'You have access to this protected route' });
});

function verifyToken(req, res, next) {
  const token = req.headers['authorization'];

  if (!token) {
    return res.status(403).json({ message: 'Token not provided' });
  }

  jwt.verify(token, jwtSecretKey, (err, decoded) => {
    if (err) {
      return res.status(401).json({ message: 'Failed to authenticate token' });
    }

    req.user = decoded;
    next();
  });
}

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Run Your Node.js App:

Start your Node.js app.

node app.js

Now, your Node.js backend is set up to handle user registration, login, and a protected route using MongoDB as the user database. Please replace 'your_jwt_secret_key' with a secure key for production use. Also, ensure you have a running MongoDB instance and update the MongoDB URI accordingly.

Backend option 2: Flask

Install Flask and Flask-CORS:

Install Flask and Flask-CORS for handling Cross-Origin Resource Sharing (CORS) to allow requests from your React frontend.

pip install Flask Flask-CORS

Initialize Flask App:

Create a file named app.py and initialize your Flask app.

# app.py
from flask import Flask, jsonify, request
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # Enable CORS for all routes

if __name__ == "__main__":
    app.run(debug=True)

Option 1: Set Up SQLite Database:

Create SQListe db

Create a simple SQLite database to store user information.

# app.py (add to the existing file)
from flask_sqlalchemy import SQLAlchemy

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(60), nullable=False)

Create Registration Endpoint:

Add a route to handle user registration.

# app.py (add to the existing file)
from flask_bcrypt import Bcrypt

bcrypt = Bcrypt(app)

@app.route('/api/register', methods=['POST'])
def register():
    data = request.get_json()

    hashed_password = bcrypt.generate_password_hash(data['password']).decode('utf-8')
    new_user = User(email=data['email'], password=hashed_password)

    db.session.add(new_user)
    db.session.commit()

    return jsonify({'message': 'User registered successfully'})

Create Login Endpoint:

Implement a route to handle user login.

# app.py (add to the existing file)
from flask_jwt_extended import JWTManager, create_access_token, jwt_required

app.config['JWT_SECRET_KEY'] = 'your_jwt_secret_key'
jwt = JWTManager(app)

@app.route('/api/login', methods=['POST'])
def login():
    data = request.get_json()

    user = User.query.filter_by(email=data['email']).first()

    if user and bcrypt.check_password_hash(user.password, data['password']):
        access_token = create_access_token(identity={'email': user.email})
        return jsonify({'access_token': access_token})
    else:
        return jsonify({'message': 'Invalid credentials'}), 401

Option 2: Set Up MongoDB Database:

Install Flask-PyMongo

Install Flask-PyMongo to integrate Flask with MongoDB.

pip install Flask-PyMongo

Initialize Flask App with MongoDB:

Update your app.py to include the Flask-PyMongo configuration.

# app.py
from flask import Flask, jsonify, request
from flask_cors import CORS
from flask_pymongo import PyMongo

app = Flask(__name__)
CORS(app)  # Enable CORS for all routes

app.config['MONGO_URI'] = 'mongodb://localhost:27017/usersdb'  # Replace with your MongoDB URI
mongo = PyMongo(app)

Create Registration Endpoint with MongoDB:

Modify the registration endpoint to use MongoDB for storing user information.

# app.py (add to the existing file)
from flask_bcrypt import Bcrypt

bcrypt = Bcrypt(app)

@app.route('/api/register', methods=['POST'])
def register():
    data = request.get_json()

    hashed_password = bcrypt.generate_password_hash(data['password']).decode('utf-8')

    users_collection = mongo.db.users
    new_user = {'email': data['email'], 'password': hashed_password}

    users_collection.insert_one(new_user)

    return jsonify({'message': 'User registered successfully'})

Create Login Endpoint with MongoDB:

Update the login endpoint to interact with MongoDB.

# app.py (add to the existing file)
from flask_jwt_extended import JWTManager, create_access_token, jwt_required

app.config['JWT_SECRET_KEY'] = 'your_jwt_secret_key'
jwt = JWTManager(app)

@app.route('/api/login', methods=['POST'])
def login():
    data = request.get_json()

    users_collection = mongo.db.users
    user = users_collection.find_one({'email': data['email']})

    if user and bcrypt.check_password_hash(user['password'], data['password']):
        access_token = create_access_token(identity={'email': user['email']})
        return jsonify({'access_token': access_token})
    else:
        return jsonify({'message': 'Invalid credentials'}), 401

Protect Endpoints with JWT:

Use the @jwt_required decorator to protect endpoints that require authentication.

# app.py (add to the existing file)
@app.route('/api/protected', methods=['GET'])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user), 200

Run the Flask App:

Run your Flask app.

python app.py

Now, your Flask backend is set up to handle user registration, login, and protected routes using MongoDB as the user database. Ensure that you have a running MongoDB instance, and update the MongoDB URI in the app.config['MONGO_URI'] accordingly. Also, replace 'your_jwt_secret_key' with a secure key for production use.

Easiest way to start a git repo:

  1. Make a new repo on Github
  2. Clone the repo to the local machine Before doing this, the destination folder must be empty git clone http.... repo name (get link from Github) To paste in git bash: Insert
  3. Work on the the project on the local machine
  4. Add changed files to the staging area with git add .
  5. Commit to the local repo with git commit -m "<commit message here>"
  6. Push to Github with git push

For more see IFS L1T23 - Version Control II_ Git Basics.pdf in H/L1/T23.

Basic git commands:

  • Initialise repo on local machine: git init
  • Check git status: git status
  • Example of how to commit: git commit -m "message here" The -m flag is for message
  • To view change history: git log
  • To create a branch: git branch my-new-branch-name
  • To switch branch: git checkout my-branch-name
  • To stash changes: git stash
  • To restore a stash: git stash pop
  • To merge a branch: First switch back to master: git checkout master Then merge: git merge my-branch
  • To link local repo to online repo: Create repo on local machine Create new online repo on Github Link local and remote repos: git remote add [shortname] [url] eg: git remote add task4 https... Push local to remote: git push [remote-name] [branch-name] eg: git push -u task4 master The -u flag: --set-upstream: add upstream (tracking) reference Short syntax: git push -u origin master Note: the remote is usually called 'origin' and the main branch is usually called 'master' In this context 'upstream' refers to the remote repo. Setting the upstream means linking the local repo to the remote repo.

Staging commands

use git restore --staged <file> to unstage

Branching

git branch tut: (https://www.youtube.com/watch?v=QV0kVNvkMxc, https://www.youtube.com/watch?v=XX-Kct0PfFc) Note: the main branch is normally called 'master' git branch branchName (create a new branch) git branch -a (see all branches, q to escape) git checkout branchName (to checkout a branch and work on it)

To delete a branch: first switch to another branch git checkout main git branch -D branchname (delete locally) git push origin --delete branchname (delete remote)

To create a branch and switch in one step: git checkout -b branchName

To push new branch to github: git push origin head

To merge a branch into the master branch: git checkout main (switch into the master branch) git merge branchName git push

To discard changes on a branch: git checkout BranchWithChangesToDiscard git fetch (make sure this branch is up to date with remote) git reset --hard origin/main (drop all changes on the current branch and make it point to main) git push --force-with-lease (to push changes to remote. The --force-with-lease flag is used to force push the changes, as the branch history has been rewritten. However, it also ensures that you don't accidentally overwrite any changes made to the branch by other contributors.)

also see https://git-scm.com/docs/git-reset

Pull requests

A pull request is a request to merge a feature branch with the main branch. before doing the pull request, ensure that your feature branch is pushed to github On github, go to the repo on which you want to do the merge Select the Pull Requests tab and select the New pull request (green button) Select the two brances to be merged (typically main and feature branches) Give the merge request a name and description. then select create pull request Once the requested is created, you can review the code if you wish. Once satisfied, merge the pull request (green button). I prefer to squash the merge request. This combines all the commits into one before merging. You can also perform the merge from the commandline as described in the brancing section above. Once the merge is complete, the feature branch is no longer needed, so you can delete it if you want. Back on your local machine, remember to switch back to main.

Other git commands

To pull the latest version of code from a remote repo and merge with the local repo: git pull. You can also use git fetch, but that will only download the changes without merging them (fetch + merge == pull).

To discard changes that have not yet been staged: git restore <file>

To unstage all changes: git reset

git status git log --oneline

To see the difference between two branches: git diff branch1 branch2 to escape out of a git diff: q + enter

GitHub Actions

What is GH?

A feature in GH to create custom automated workflows. Commonly used for CI/CD workflows. The typical workflow looks like this:

flowchart LR
    a-->b
    b-->c
    c-->d
    d-->e

    a[Code]
    b[Test]
    c[Build]
    d[Push]
    e[Deploy]
Loading

How to use - step by step

Prerequirements: GH account and repo with code that is ready to be integrated.

  1. On repo, create folder .github/workflows and add the .yml file for your workflow. Commit and push your changes.
  2. Alternativelu, on GH, go to Actions tab and search for a template for the new workflow. Remember to do the commit in GH.
  3. See below for structure of a yml workflow file
  4. When the triggers specified in the workflow file happen, the workflow will run.
  5. Check the results of the workflow, by checking actions on the left menu in GH on the Actions tab.

Typical structure of a yml workflow

name: Docker Image CI

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:

  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)

Terms

  • Workflow: collection of jobs, defined in yml file (Keyword: name:)
  • Events: Any activity in a repo that can trigger a workflow (Keyword: on:)
  • Jobs: Collection of steps (Keyword: jobs:)
  • Steps: Actions to be taken, commands, scripts (Keyword: steps:)
  • Chain jobs: make them run in sequence (Keyword: needs:)

Some good YouTube videos to watch

https://youtu.be/ylEy4eLdhFs?si=Yafn1BDyANPiA7Cb https://youtu.be/R8_veQiYBjI?si=ZaZLWEvu15u-RZmU https://youtu.be/1oJQRlz1v94?si=J0hyq7uLqFKfkYUs

To generate a Google Analytics tracking code for your website, follow these steps:

  1. Sign in to Google Analytics:

    • If you don't have a Google Analytics account, you'll need to create one. Visit the Google Analytics website and sign in with your Google account.
  2. Create a new property:

    • Once you're logged in, click on the "Admin" tab at the bottom left of the page.
    • In the "Property" column, click on the drop-down menu and select "Create Property."
    • Follow the prompts to set up a new property for your website. Enter the necessary information, such as the website name, URL, and industry category.
  3. Configure your reporting time zone and currency:

    • In the same process of creating a new property, you'll be prompted to set your reporting time zone and currency. Choose the appropriate options for your website.
  4. Accept the terms of service:

    • Read and accept the terms of service for Google Analytics.
  5. Get your tracking code:

    • After creating the property, you'll be taken to the property settings page. Look for the "Tracking Info" tab in the middle column.
    • Under "Tracking Info," click on "Tracking Code."
  6. Copy the tracking code:

    • You'll see a block of JavaScript code. This code includes your unique tracking ID and should be added to every page on your website that you want to track.
    • Copy the entire tracking code.
  7. Add the tracking code to your website:

    • Paste the tracking code into the HTML of each page on your website, just before the closing tag. If you're using a content management system (CMS) like WordPress, there may be a dedicated section to insert your Google Analytics tracking code.
  8. Check if it's working:

    • After adding the tracking code, go back to the Google Analytics dashboard and check the "Realtime" reports to see if your data is being tracked.

That's it! Your website is now set up to be tracked by Google Analytics. Keep in mind that it may take some time for data to start appearing in your reports, especially if your website has low traffic initially.

Headers

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Empahasis

Italic Text Italic Text

Bold Text Bold Text

Bold and Italic Text Bold and Italic Text

Lists

Unordered

  • Item 1
  • Item 2
    • Subitem 2.1
    • Subitem 2.2

Ordered

  1. Item 1
  2. Item 2
  3. Item 3
    1. Subitem 3.1
    2. Subitem 3.2

Links

Link Text

Images

Alt text

Blockquotes

This is a blockquote. It can span multiple lines.

Code

Inline code

Use backticks (like this) to highlight inline code.

Code blocks

use 3 backticks for code blokcs

Horizontal Rule


Tables

Header 1 Header 2
Cell 1 Cell 2
Cell 3 Cell 4

Task lits

  • Task 1
  • Task 2
  • Task 3

Escape characters

* This will not be italic *

Mermaid Cheat Sheet

Tutorial

https://youtu.be/JiQmpA474BY?si=oMFxfF6w3oRlljwU

Installation

  1. Install Markdown Preview Mermaid Support plugin by Mat Bierner in VS Code.
  2. Close and reopen md file.

Notice to users of your documentation

Paste this notice into your md file if you are using Mermaid:

Note: To view the diagrams in this file, you must install the Markdown Preview Mermaid Support plugin by Mat Bierner in VS Code. Then close and reopen the md file. Documentation here.

Examples

You can create many types of diagrams. These are only the most common types.

Flowchart1

flowchart LR;
    A[label]-->B;
    A-->C;
    B(rounded corners)-->D;
    C{choice}-->|Yes|D;
    D((Circle)) --> E;
Loading

Flowchart2

graph TD;
    A[App]-->|Feature 1|B;
    A-->|Feature 2|C;
    B-->D;
    B-.->|Dotted line|E;
    C-->F
    C-->G;
Loading

Sequence Diagram

sequenceDiagram
    autonumber
    Alice->>Bob: Hello Bob, how are you?
    activate Bob
    Bob-->>Alice: I'm good, thanks!
    deactivate Bob
Loading

Class Diagram

classDiagram
    class Order {
        OrderStatus status
        int orderId
    }
    class OrderStatus {
        <<enumeration>>
        FAILED
        PENDING
        PAID
    }
    class PaymentProcessor {
        <<interface>>
        -string apiKey
        #connect(string url, JSON header)
        +processPayment(Order order) OrderStatus
    }
    class Customer {
        string name
    }
    Customer <|-- BusinessCustomer
    Customer <-- PrivateCustomer
    Order o-- Customer
    Order *-- OrderStatus
Loading
---
title: Animal example title
---
classDiagram
    note "From Duck till Zebra"
    Animal <|-- Duck
    note for Duck "can fly\ncan swim\ncan dive\ncan help in debugging"
    Animal <|-- Fish
    Animal <|-- Zebra
    Animal : +int age
    Animal : +String gender
    Animal: +isMammal()
    Animal: +mate()
    class Duck{
        +String beakColor
        +swim()
        +quack()
    }
    class Fish{
        -int sizeInFeet
        -canEat()
    }
    class Zebra{
        +bool is_wild
        +run()
    }
Loading

Entity Relationship Diagram

https://mermaid.js.org/syntax/entityRelationshipDiagram.html

erDiagram
    Customer {
        string name
        string surname
    }
    Customer ||--o{ Order : places
    Customer ||--|{ Address : lives-at
Loading

Gantt Chart

gantt
    title A Gantt Diagram
    section Section
    A task           :a1, 2023-01-01, 30d
    Another task     :after a1  , 20d
    section Another
    Task in sec      :2023-03-12  , 12d
    another task    : 24d
Loading

Pie Chart

pie
    title Key Components
    "Component 1" : 30
    "Component 2" : 20
    "Component 3" : 50
Loading

Notes

  1. Currently printing Mermaid to pdf is not supported in VS Code. You can paste your code in https://mermaid.live/ and create pdfs there.
  2. Formatting and spacing matters. You have to start with ````mermaid (on one line) and end with ``` (no indentation)

Mongo cheatsheet

Basic steps to create a mongo db for a web app

  1. Sign up for MongoDB Atlas.
  2. Create a new cluster.
  3. Configure cluster settings.
  4. Whitelist IP addresses.
  5. Create a MongoDB user.
  6. Connect your application to the MongoDB Atlas cluster.
  7. Configure MongoDB Atlas connection settings in your backend.
  8. Configure MongoDB Atlas connection settings in your frontend.
  9. Test the connection.
  10. Implement CRUD operations in your Flask backend.
  11. Integrate CRUD operations with your React frontend.

Installing the mongo shell

  1. go to https://www.mongodb.com/docs/mongodb-shell/
  2. Click the download button
  3. Once downloaded, extract the zip file
  4. Navigate to the bin folder. Copy this path and add it to your path variable
  5. I wasn't able to run mongosh from the command line, but I was able to click on and run mongosh.exe in the bin folder.
  6. Add your PC's IP address to the whitelist (remember to disconnect from VPN) 1 Launch mongosh and enter your mongo uri.

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

Commands for mongo (mongodb shell)

Check mongo version: mongo --version

Create new database or switch to other db: use <dbName>

List all dbs in cluster: show dbs

Show collections in currently selected db: show collections

Check the name of the currently selected db: db

Delete the currently selected db: db.dropDatabase();

CRUD operations (these commands can be used directly in the console)

Create

To insert a single document in a collection: db.<collectionName>.insertOne({name: 'Sue', age: 33});

To insert multiple documents in a collection: db.<collectionName>.insertMany([{name: 'Sue', age: 33}, {name: 'Bob', age: 20}]);

Read

To set format to pretty: db.test_collection.find().pretty();

To find one record: db.test_collection.find({name: 'Sue'});

To find the first matching record: db.test_collection.findOne({name: 'Sue'});

To find all records in a collectio: db.test_collection.find();

To find one record, but not display all the info: db.test_collection.find({name: 'Sue'}, {_id: false, age: true});

Update

To update one or more records (updates all fields): db.test_collection.update({name:'Sue'}, {age:34});

To update one or more records (updates only specified fields): db.test_collection.update({name:'Sue'}, {$set: {age:34}}); or: db.people.update({name: 'Sue'}, {$set: {name: 'Susan'}}, {multi: true}) In latest version, can also use updateOne() and updateMany()

Delete

Delete all documents in a collection: db.test_collection.remove();

Delete all mathcing documents in a collection: db.test_collection.remove({name: 'Susan'});

Delete first mathcing document in a collection: db.test_collection.remove({name: 'Susan'}, true); In latest version, can also use deleteOne() and deleteMany()

Task for HyperionDev course

db.capstones.insertOne({name:'Hangman', description:'React implementation of hangman game', createDate:'Apr 21, 2021', gitHubLink:'https://github.com/donnpie/hangman', estimatedHours:24});

db.capstones.insertOne({name:'GitHub custom interface', description:'React/Express app that integrates with GitHub API', createDate:'May 12, 2021', gitHubLink:'https://github.com/donnpie/github-custom-interface', estimatedHours:32});

db.capstones.insertOne({name:'Quick Food order system', description:'Java/SQL Server app that manages food orders for delivery business', createDate:'February 24, 2021', gitHubLink:'https://github.com/donnpie/QuickFoodOrderSystem', estimatedHours:40});

db.capstones.insertOne({name:'Maze generator', description:'This simple program generates a maze using an iterative back tracking algorithm, using only vanilla JavaScript', createDate:'February 25, 2021', gitHubLink:'https://github.com/donnpie/Maze-generator', estimatedHours:8});

db.capstones.insertOne({name:'First website', description:'My first attempt at building a basic website', createDate:'January 28, 2021', gitHubLink:'https://github.com/donnpie/FirstWebsite', estimatedHours:16});

db.capstones.insertOne({name:'Mongo Setup', description:' Getting to grips with Mongo admin', createDate:'May 19, 2021', gitHubLink:'null', estimatedHours:3}});

db.capstones.update({name:"Mongo Setup"},{$set: {description: 'Getting to grips with Mongo admin including Mongo Shell usage'}});

db.capstones.find({createDate: {$regex: /.May./}});

db.capstones.update({name:"Mongo Setup"}, {$inc: {estimatedHours: 2}});

Syntax stuff

In Neo4j's Cypher query language, the dollar sign ($) is used to denote a parameter. Parameters are placeholders that can be replaced with actual values when the query is executed.

Create and edit stuff

Create standalone node

create(n) - create a blank node

Create(n:Person) - create node of type Person

Create(n:Person{name:'Chris'}) - create node with type and property

Create(n:Note{name:'Passive income', text:'To escape the rat race, you have to create sources of passive income.', date:'2024/04/28', type:'permanent'})

Create new node as child of existing node

CREATE (child:Label {property: value})
CREATE (parent)-[:RELATIONSHIP_TYPE]->(child)
RETURN child

Create new node as parent of existing node

CREATE (parent:Label {property: value})
CREATE (parent)-[:RELATIONSHIP_TYPE]->(child)
RETURN parent

Creating relationships:

Match(s:School), (p:Person) Where s.name = 'ABC' AND p.name = 'Chris' Create (p) - [stu:Studied_At]->(s)

Match(p1:Person), (p2:Person) Where p1.name = 'Chris' AND p2.name = 'Paul' Create (p1)-[f:Friends_with]->(p2)

Add a new property to an existing node

WHERE n.property = $value
SET n.newProperty = $newValue

Rename a property

MATCH (n:Label)
SET n.newProperty = n.oldProperty

// Step 2: Remove the old property
REMOVE n.oldProperty

// Step 3: Create any indexes or constraints on the new property
CREATE INDEX ON :Label(newProperty)

Rename a relationship (Error)

SET oldRelationship:NEW_TYPE
REMOVE oldRelationship:OLD_TYPE
RETURN oldRelationship

or

// Create the new relationship
MATCH (n1)-[oldRel:IS_ANSWERED_BY]->(n2)
Create (n1)-[newRel:Is_followed_by]->(n2)
return n1, n2, oldRel, newRel

// Remove the old relationship
MATCH (n1)-[oldRel:IS_ANSWERED_BY]->(n2)
delete oldRel
return n1, n2, oldRel //Only returns the nodes

Change a property to lowercase

SET n.property = LOWER(n.property)

Queries

Match(n) Return(n)

Match(n:Person) return(n) - Persons only

Match(n:Person) return(n) Limit(1) - Limit number of results

Match(s:School), (p:Person) Where s.name = 'ABC' AND p.name = 'Chris' Return s, p

MATCH (n1:NodeType)-[r]->(n2:NodeType) RETURN n1, r, n2

Find a node by its internal id:

WHERE ID(n) = 13
RETURN n

The function id() is deprecated. Use the function elementId() instead.

find all nodes with no incoming edges (root nodes)

MATCH (n) WHERE NOT ()-->(n) RETURN n

find all nodes with no outgoing edges (leaf nodes)

MATCH (n) WHERE NOT (n)-->() RETURN n

find all downstream edges and nodes of a given node

WHERE startNode.id = $startNodeId
RETURN p

find all upstream edges and nodes of a given node

MATCH p=(upstreamNode)-[*]->(givenNode)
WHERE givenNode.id = $givenNodeId
RETURN p

find all circular paths

RETURN path

Find all nodes with at least one child node

RETURN n, r

Return all ids:

WHERE n.id IS NOT NULL
RETURN DISTINCT "node" as entity, n.id AS id LIMIT 25
UNION ALL 
MATCH ()-[r]-() 
WHERE r.id IS NOT NULL
RETURN DISTINCT "relationship" AS entity, r.id AS id LIMIT 25;

Find a substring within a property

WHERE n.property CONTAINS 'substring'
RETURN n

Delete

Note: cannot delete a node with relationships. must first delete the relationships

Delete a note

Match(n) Delete(n) - delete matching nodes

Match(n) Detach Delete(n) - Remove relationships and delete

Delete a relationship

MATCH (n1)-[r]->(n2) WHERE ID(n1) = $node1_id AND ID(n2) = $node2_id DELETE r The function id() is deprecated. Use the function elementId() instead.

How the network stuff works with all the port numbers and what not

Analogies for IP addresses and port numbers

Think of a computer as a block of flats. The IP address is like the street address. It brings you to the front door of the block of flats. Then the port number is the door number. Each door has a unique number. Inside some flats (not all) lives a program. If you want to talk to a specific program you need to know the street address (IP address) to get to the right block of flats. You also need the door number (port number) to get to the right door.

You can think of HTTP and HTTPS as a courier sending messages between different programs living in different blocks of flats and behind different doors. Let's pretend we are looking at communication between two programs. The first program is called the client and it lives in building A, door 1. The second program is called the server and it lives in building B, door 2. The clients wants to ask the server a question. So the client writes a letter and puts it in an envelope. On the front of the envelope the clients puts the server's address details (building B, door 1). On the back of the envelope the client puts his own address details (building A, door 1) and gives it to the courier (a protocol, such as HTTP or HTTPS). The courier knows the city very well. He reads the address on the front of the envelope and then proceeds to that address and delivers the letter to the correct building and door. The server reads the messages and prepares a response in the form of a letter. He puts the letter in an envelope and on the front of the Envelope he writes the client's address details. At the back he writes his own address, so that the client can know the message is from him. He then gives the message to the courier who then delivers it to the client. The client then reads the messages and acts on the information received.

So, to summarise, to send and receive messages between a client and a server, you need a source address (IP and port), and destination address (destination IP and port), a courier (HTTP, HTTPS or somethings else). This combination of source IP address and port and destination IP address and port is known as a web socket.

    C4Context
    title Communication and network architecture
    Boundary(b0, "Physical machine") {
        Boundary(b1, "Windows OS"){
            Component(Client, "Client", "A web brower")
            Boundary(b2, "WSL (Ubuntu IP: 127.18.202.129)"){
                Component(ReverseProxy, "Reverse proxy Port 443", "NGINX")
                Boundary(b3, "Frontend container (Ubuntu IP: 127.108.202.100)"){
                    Component(FrontEndApp, "FrontEnd App Port 5000", "Node")
                }
                Boundary(b4, "Backend container (Ubuntu IP: 127.32.22.29)"){
                    Component(API, "API Port 8000", "Gunicorn")
                }
            }
        }  
    }
    BiRel(Client, ReverseProxy, "HTTPS (E<->443)")
    BiRel(ReverseProxy, FrontEndApp, "HTTPS (5001<->5000)")
    BiRel(ReverseProxy, API, "HTTPS (8001<->8000)")

    UpdateRelStyle(Client, ReverseProxy, $textColor="white", $lineColor="white")
    UpdateRelStyle(ReverseProxy, FrontEndApp, $textColor="white", $lineColor="white")
    UpdateRelStyle(ReverseProxy, API, $textColor="white", $lineColor="white", $offsetY="100")

    UpdateElementStyle(b0, $fontColor="white", $borderColor="white")
    UpdateElementStyle(b1, $fontColor="white", $borderColor="white")
    UpdateElementStyle(b2, $fontColor="white", $borderColor="white")
    UpdateElementStyle(b3, $fontColor="white", $borderColor="white")
    UpdateElementStyle(b4, $fontColor="white", $borderColor="white")
    UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")
Loading

It is clear to me how the message gets from client to server, but not how it gets back

Some ChatGPT stuff to be cleaned up

There is a problem with your story. When the client writes the letter to the server, he actually doesn't have the server's address. He only has the proxy's address. The proxy has to examine where the message came from and what type of message it is, to determine where to send it. ChatGPT You're absolutely right, and I appreciate your keen observation. Let's enhance the analogy to better capture this crucial detail:

Picture our city again, with different buildings representing servers and doors representing programs. The client resides in Building A, Door 1, and the server is in Building B, Door 2. Now, introduce a central information hub, the City Information Center, acting as the reverse proxy.

The client, wanting to communicate with the server, writes a letter and addresses it to the City Information Center (the reverse proxy), indicating that it wants to reach Building B, Door 2. The client doesn't know the server's exact address but trusts the City Information Center to handle the redirection.

The client places its return address (Building A, Door 1) on the back of the envelope and hands it to the courier, which is now the reverse proxy. The reverse proxy, being well-versed in the city's layout, examines the message, notes the sender's address (Building A, Door 1), and determines the appropriate destination based on predefined rules.

Understanding the type of message and where it came from, the reverse proxy redirects the letter to the correct building and door, which is Building B, Door 2 in our example. The server, upon receiving the letter, responds by placing its own return address (Building B, Door 2) on the envelope.

The reverse proxy, equipped with the knowledge of the original sender (Building A, Door 1), ensures that the response reaches the correct destination. This way, the client interacts with the server indirectly through the intermediary of the reverse proxy, which cleverly manages the routing based on its understanding of the city's infrastructure and the characteristics of the messages it handles.

User Your story doesn't adequately explain how how the reverse proxy knows how to distinguish between multiple messages (from different clients) to the same server ChatGPT Let's refine the analogy to address this aspect:

Imagine our city with various buildings (servers) and doors (programs). We have the City Information Center as our reverse proxy, proficient in managing messages. Now, each client lives in a distinct neighborhood and communicates with the reverse proxy using a special language.

When a client, residing in Neighborhood A, wants to send a message to the server in Building B, Door 2, it writes the message in its unique language and addresses it to the City Information Center. The reverse proxy, being multilingual, understands the various languages spoken by the neighborhoods.

The client places its return address (Neighborhood A, Door 1) on the back of the envelope, and the reverse proxy, upon receiving the message, deciphers the language. It notes the sender's neighborhood (Neighborhood A) and identifies the intended recipient (Building B, Door 2).

This clever reverse proxy keeps a mental map of ongoing conversations. When another client from Neighborhood C sends a message to the same server, the reverse proxy discerns the language (Neighborhood C's unique dialect), notes the sender's address (Neighborhood C, Door 3), and ensures it reaches the correct destination (Building B, Door 2).

In this way, the reverse proxy, equipped with linguistic prowess, distinguishes between messages from different clients, directing them to the appropriate server destinations. It's like having a multilingual city concierge, ensuring smooth communication between neighborhoods and servers.

npm install --save-dev nodemon npm uninstall --save-dev nodemon

Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

To get a new PostgreSQL database running on your local machine, follow these steps:

Step 1: Install PostgreSQL

On Windows:

  1. Download the PostgreSQL installer from the official PostgreSQL website.
  2. Run the installer and follow the on-screen instructions.
  3. During the installation process, set a password for the PostgreSQL superuser (default user: postgres).

On macOS:

  1. Use Homebrew to install PostgreSQL:
    brew install postgresql
  2. Initialize the database and start the PostgreSQL server:
    brew services start postgresql

NB: If you get this error: ERROR: unrecognized configuration parameter "lc_collate", it it probably because there is a version difference between postgreSQL and pgAdmin 4. I had pgAdmin for v16, but postgreSQL v16, so so it wouldn't work. I tried to download postgreSQL v16 but the download page wasn't working, so I decided to develop in v15.

On Linux (Ubuntu):

  1. Update the package lists and install PostgreSQL:
    sudo apt update
    sudo apt install postgresql postgresql-contrib
  2. Ensure the PostgreSQL service is started:
    sudo systemctl start postgresql
    sudo systemctl enable postgresql

Step 2: Set Up the Database

  1. Switch to the PostgreSQL user:

    sudo -i -u postgres
  2. Access the PostgreSQL prompt:

    psql
  3. Create a new database:

    CREATE DATABASE mydatabase;
  4. Create a new user with a password:

    CREATE USER myuser WITH PASSWORD 'mypassword';
  5. Grant all privileges on the new database to the new user:

    GRANT ALL PRIVILEGES ON DATABASE mydatabase TO myuser;
  6. Exit the PostgreSQL prompt:

    \q

Step 3: Connect to Your Database

You can connect to your PostgreSQL database using various tools:

  • psql (command-line tool):

    psql -U myuser -d mydatabase
  • pgAdmin (GUI tool):

    • Download and install pgAdmin from the pgAdmin website.
    • Open pgAdmin and create a new connection using your database credentials.
  • Programming Languages:

    • Python (using psycopg2 library):
      import psycopg2
      
      def connect_to_db():
          conn = psycopg2.connect(
              dbname="mydatabase",
              user="myuser",
              password="mypassword",
              host="localhost",
              port="5432"
          )
          return conn
      
      # Example usage
      conn = connect_to_db()
      print("Connected to the database!")
      conn.close()

Additional Tips

  • Configuration File: The PostgreSQL configuration files (like postgresql.conf and pg_hba.conf) are typically located in the PostgreSQL data directory. You can adjust settings like listening addresses and authentication methods in these files.

  • Start/Stop PostgreSQL Service:

    • On Windows: Use the Services app or pg_ctl command.
    • On macOS: Use Homebrew services (brew services start postgresql).
    • On Linux: Use systemctl (sudo systemctl start postgresql).
  • Backup and Restore:

    • Backup: pg_dump mydatabase > mydatabase_backup.sql
    • Restore: psql -U myuser -d mydatabase -f mydatabase_backup.sql

By following these steps, you should be able to set up and manage a PostgreSQL database on your local machine.

Sample code for connecting from Express backend

To connect to your PostgreSQL database from your Node.js Express backend, you typically use a database client like pg (node-postgres) rather than axios, which is an HTTP client. pg allows you to interact directly with your PostgreSQL database.

Here's a step-by-step guide to setting up a connection to your PostgreSQL database using pg in your Node.js Express backend:

Step 1: Install Necessary Packages

First, you need to install the pg package to connect to your PostgreSQL database and express for your server.

npm install pg express

Step 2: Set Up Your Express Server

Create a new file called server.js (or app.js) for your Express server:

const express = require('express');
const { Pool } = require('pg');

// Initialize the Express application
const app = express();
const port = 3000;

// Configure the PostgreSQL connection
const pool = new Pool({
  user: 'myuser',
  host: 'localhost',
  database: 'mydatabase',
  password: 'mypassword',
  port: 5432,
});

// Middleware to parse JSON requests
app.use(express.json());

// Example route to test the database connection
app.get('/test-db', async (req, res) => {
  try {
    const client = await pool.connect();
    const result = await client.query('SELECT NOW()');
    res.json(result.rows);
    client.release();
  } catch (err) {
    console.error(err);
    res.status(500).send('Error connecting to the database');
  }
});

// Start the Express server
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Step 3: Create Environment Variables (Optional but Recommended)

For better security and flexibility, use environment variables to store your database credentials. Create a .env file in your project root:

DB_USER=myuser
DB_HOST=localhost
DB_DATABASE=mydatabase
DB_PASSWORD=mypassword
DB_PORT=5432

Install the dotenv package to load these environment variables:

npm install dotenv

Then, update your server.js to use these environment variables:

require('dotenv').config();
const express = require('express');
const { Pool } = require('pg');

// Initialize the Express application
const app = express();
const port = 3000;

// Configure the PostgreSQL connection using environment variables
const pool = new Pool({
  user: process.env.DB_USER,
  host: process.env.DB_HOST,
  database: process.env.DB_DATABASE,
  password: process.env.DB_PASSWORD,
  port: process.env.DB_PORT,
});

// Middleware to parse JSON requests
app.use(express.json());

// Example route to test the database connection
app.get('/test-db', async (req, res) => {
  try {
    const client = await pool.connect();
    const result = await client.query('SELECT NOW()');
    res.json(result.rows);
    client.release();
  } catch (err) {
    console.error(err);
    res.status(500).send('Error connecting to the database');
  }
});

// Start the Express server
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Step 4: Start Your Server

Run your server with:

node server.js

Now, you can test the /test-db endpoint by navigating to http://localhost:3000/test-db in your browser or using a tool like Postman. You should see the current date and time returned from your PostgreSQL database.

Summary

  • Install the pg and express packages.
  • Set up an Express server.
  • Configure a PostgreSQL connection using the pg package.
  • Create an endpoint to test the database connection.
  • Optionally, use environment variables to store your database credentials securely.

This setup allows your Node.js Express backend to connect to a PostgreSQL database, perform queries, and handle responses.

Import data into a table

In pgAdmin, navigate to and select the table. Rightclick on the table and select import/export. Browse to the file with the data (should be csv). Select the schema of the import file (Normally UTF-8). Click OK.

NB: Dates must be in the format 2024-01-01 When importing data in pgAdmin, the autoincrement in the id field does not work, so you must add the id numbers manually.

<!-- Practice. Delete after use -->
<html>
<body>
<header>
<div>Logo</div>
<ul>
<li>Home</li>
<li>Register</li>
<li>Login</li>
</ul>
</header>
<form action="https://api.mysite.com/login" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email">
<label for="password">Password</label>
<input type="password" name="password" id="password">
<button type="submit">Submit</button>
</form>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

To build & deploy: 1. Railway login 2. Railway link (it will prompt you to select repo and environment) 3. Railway up 4. Ctrl + c 5. Railway logout

To create a new react app:

npx create-react-app my-app

cd my-app

npm start

https://create-react-app.dev/docs/getting-started

To include react-bootstrap

npm install react-bootstrap bootstrap

import Button from 'react-bootstrap/Button'; //in index.js

Include bootstrap CDN in index.html:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous" />

Render button: ReactDOM.render( <Button variant="primary">Primary</Button>, document.getElementById('root') ); https://react-bootstrap.netlify.app/getting-started/introduction/

Functions

Assign a function to a constant using normal anonymous function: const square = function(x) { return x * x; }

Assign a function to a constant using arrow function: Think of the constant as a pointer to the function. const square = (x) => { return x * x; }

A function can also be declared inside an object: const user = { name: Donn printName() { return this.name; } };

JSX expressions

Assign JSX to constant (note the use of parentheses, not braces) const userName = "Mike"; const name = ( <p>{UserName}</p> ); //Inside the braces you must put a valid JavaScript expression

It is possible to declare a JSX expression inside a function and assign it to a constant: const renderMe = () => { const myJSX = ( <p>Something</p> ); ReactDOM.render(myJSX, document.getElementById('root'); }

JSX arrays

Arrays can be displayed directly in JSX: {myArr} Arrays containing JSX must always contain key values: [<p key="1">One</p>, <p key="2">Two</p>, <p key="3">Three</p>]

To generate a JSX array from a normal array: const jsxArray = normalArray.map((elem, key) => { return <p key={key}>{elem}</p>; });

Example from lesson 18:

(Note how the map function omits the use of the return keyword)

`function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) =>

  • {number}
  • ); return (
      {listItems}
    ); }`

    const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') );

    Conditional rendering

    To render 1 item or nothing: {obj.value &&

    obj.value

    } To render i item or another:

    {obj.value ? obj.value : 'Nothing'}

    Events and attributes

    let count = 0;

    const increment = () => { //Event handler count++; } const template = ( Increment

    count: {count}

    );

    Forms

    //data object const data = { name: '' };

    //Event handler const onFormSubmit = (event) => { event.preventDefault(); //Prevent get request const input = event.target.elements.option.value; if (input) { data.name = input; } };

    const myForm = ( <>

    Click Me {data.name &&

    data.name

    } </> );

    Classes and methods

    When creating a method in a class, it it not necesary to use the function keyword:

    class myClass { myMethod() { //Do something } }

    Component basics

    To declare a react component:

    import React from 'react'; class MyComponent extends React.Component { constructor(props) { super(props); }

    render() {
    	return (<p>some JSX</p>);
    }
    

    }

    Passing props down to a child component:

    In parent component:

    In child component: class ChildComponent extends React.Component { constructor(props) { super(props); }

    render() {
    	return (<p>{this.props.myProp}</p>);
    }
    

    }

    Passing an array prop down to a child component:

    In parent component: const myArr = [1, 2, 3];

    In child component: class ChildComponent extends React.Component { constructor(props) { super(props); }

    render() {
    	return (
    		<div>
    			{	
    				this.props.myProp.map((val, idx) => {
    					return <p key={idx}>{val}</p>
    				})
    			}
    		</div>
    	);
    }
    

    }

    Remember that map returns an array, so the map statement must be enclosed within {} Since it it an expression, no semicolon is required at the end (after the parenthesis

    Events and methods

    class MyClass extends React.Component { constructor(props) { super(props); }

    myMethod() {
    	//Do something
    }
    
    render() {
    	return (
    		<div>{
    			<button onClick={this.myMethod}>Click Me</button>
    		}</div>
    	);
    }
    

    }

    Method binding for event handlers

    Context for event handlers must be set explicitly in the class constructor class MyComponent extends React.Component { constructor(props) { super(props); this.myEventHandler = this.myEventHandler.bind(this); }

    myEventHandler() {
    	//Do something
    }
    
    render() {
    	return (
    		{//Register eventhandler somewhere here}
    	);
    }
    

    }

    State

    With state a component will automatically rerender itself when the data changes The state object must be created in the constructor. Changing state does not automatically call render method. Must call this.setState method this.setState takes a callback function as parameter. the first parameter of the callback function is prevState by default Calls to setState are asychronus

    The state process:

    1. Setup default state object
    2. Component is rendered with default state (automatic)
    3. Change the state based on an event eg user does something
    4. Re-render the component using new state values (autimatic)

    class MyComponent extends React.Component { constructor(props) { super(props); this.myEventHandler = this.myEventHandler.bind(this); this.state = { name: "Intial name" }; }

    myEventHandler() {
    	this.setState((prevState) => {
    		return {
    			//Do something with state or prevState eg
    		};
    	});
    	//example: this.setState((prevState) => {return {counter: prevState.counter - 1}});
    }
    
    render() {
    	return (
    		<div>
    			<p>{this.state.name}</p>
    		</div>
    	);
    }
    

    }

    You can have multiple setState expressions in a single event handler: myEventHandler() { this.setState((prevState) => { return { //Change state }; }); this.setState((prevState) => { return { //Change state again }; }); }

    Changing parent state in its child component

    To change state in a child component, pass method using function pointer

    class Parent extends React.Component { constructor(props) { super(props); this.myEventHandler = this.myEventHandler.bind(this); this.state = { name: "Intial name" }; }

    myEventHandler() {
    	this.setState((prevState) => {
    		return {
    			//Do something with state or prevState
    		};
    	});
    }
    
    render() {
    	return (
    		<div>
    			<Child 
    				name={this.state.name}
    				handleEvent={this.myEventHandler}
    			/>
    		</div>
    	);
    }
    

    }

    class Child extends React.Component { render() { return (

    Click me
    ); } }

    To execute a parent method in the child with params:

    class Parent extends React.Component { constructor(props) { super(props); this.myEventHandler = this.myEventHandler.bind(this); this.state = { name: "Intial name" }; }

    myEventHandler(option) {
    	//Do somethig with option
    }
    
    render() {
    	return (
    		<div>
    			<Child 
    				name={this.state.name}
    				handleEvent={this.myEventHandler}
    			/>
    		</div>
    	);
    }
    

    }

    class Child extends React.Component { constructor(props) { super(props); this.childEventHandler = this.childEventHandler.bind(this); }

    childEventHandler(e) {
    	e.preventDefault();
    	const someVariable = e.target.elements.option.value.trim();
    	if (someVariable) {
    		this.props.handleEvent(someVariable);
    	}
    }
    
    render() {
    	return (
    	<>
    		<form onSubmit={childEventHandler}>
    			<input type="text" name="option"/>
    			<button>Click Me</button>
    		</form>
    		{data.name && <p>data.name</p>}
    	</>
    	);
    }
    

    }

    Stateless Functional Components

    If a class component only has a render method, it can be converted to a functional component, where it is not necesary to call the render method: SFCs do not have access to this keyword

    const FuncComponent = (props) => { return (

    (props.prop}

    ); };

    Alternative syntax: export default function Button(props){ return {props.label} }


    Default properties for a component: ClassName.defaultProps = { name: "default" }

    For a good example, see PeterFenPoly project See this YouTube video: https://www.youtube.com/watch?v=z0vaVoxMoSA https://reactrouter.com/en/main/start/overview

    In version 6.4 of React router dom, we create routerProvider like this:

    import { createBrowserRouter, createRoutesFromElements, Route, NavLink, Outlet, RouterProvider, useRouteError } from "react-router-dom"
    
    function App() {
        const router = createBrowserRouter(
            createRoutesFromElements(
                <Route path="/" element={<HomeTemplate/>}>
                    <Route index element={<Home/>} loader={() => latestArticlesLoader(3)} errorElement={<ErrorBoundary />}/>
                    <Route path="articles" element={<Articles/>} loader={ArticlesLoader} errorElement={<ErrorBoundary />}/>
                    <Route path="article" element={<ArticleTemplate/>}>
                        <Route path=":id" element={<Article/>} loader={articleLoader} errorElement={<ErrorBoundary />}/>
                    </Route>
                    <Route path="contact" element={<Contact/>}/>
                </Route>
            )
        )
    
        return (
            <RouterProvider router={router}/>
        )
    }
    

    Then create components

    const HomeTemplate = () => {
        const handleLinkClick = () => {
            // Toggle the checkbox state when a link is clicked
            const checkbox = document.getElementById('check');
            console.log("Toggling checkbox...")
            checkbox.checked = !checkbox.checked;
        };
    
        return (
            <>
                <header>
                    <nav>
                        <label htmlFor="title">
                            PFP
                        </label>
                        <input type="checkbox" id="check"/>
                        <ul>
                            <li><NavLink to="/" onClick={handleLinkClick} activeclassname="active">Home</NavLink></li>
                            <li><NavLink to="/articles" onClick={handleLinkClick} activeclassname="active">Articles</NavLink></li>
                            <li><NavLink to="/contact" onClick={handleLinkClick} activeclassname="active">Contact</NavLink></li>
                        </ul>
                        <label htmlFor="check" className="check-btn">
                            <FontAwesomeIcon icon={faBars} />
                        </label>
                    </nav>
                </header>
                <main>
                    <Outlet/>
                </main>
                <footer>
                    
                </footer>
            </>
        )
    }
    
    // For handling errors
    function ErrorBoundary() {
        let error = useRouteError();
        console.error(error);
        // Uncaught ReferenceError: path is not defined
        return <div>Dang!</div>;
    }
    

    And like this:

    export default function Home() {
    
        const articles = useLoaderData()
    
        return (
            <>
                <Hero 
                    titleText={heroData.mainTitle}
                    sectionText={heroData.introText}
                />
                <CardSection title="Latest articles" articles={articles}/>
                <Link to="/articles" target="_self">See all articles</Link>
            </>
        )
    }
    
    export const latestArticlesLoader = async (n) => {
        try {
            const url = `${ROOT_URL}/articles/latest/${n}`
            const response = await axios.get(url)
            const articles = response.data
            console.log(articles)
            return articles
        } catch (error) {
            console.error('Error fetching articles:', error)
            const articles = dummyArticleList
            console.log(articles)
            return articles
        }
    }
    
    
    
    

    React Testing Library

    Some good sources of information

    RTL docs: https://testing-library.com/docs/react-testing-library/intro/

    Article on different react testing frameworks: https://www.browserstack.com/guide/top-react-testing-libraries

    Video tutorials

    Laith Harb & Net Ninja: https://youtu.be/7dTTFW7yACQ?si=D0mq6EduzkHGZVY_

    Codevolution: https://youtu.be/T2sv8jXoP4s?si=L60dfbpxvEbYoSPU

    Laith Harb 1 hr video: https://youtu.be/ajiAl5UNzBU?si=rYrJ9FmrLCH5hD0P

    Prerequisites

    Node Git A working react app

    Installation

    npm i --save-dev @testing-library/jest-dom @testing-library/react @testing-library/user-event

    Note that these come pre-packaged with Create-React-App

    Structure of tests

    File naming convention: ComponentName.test.js. You can store tests in a folder called __test__.

    A test typically has these for elements:

    1. Render a component we want to test
    2. Find elements we want to interact with
    3. Interact with those elements
    4. Assert that the results are as expected

    Example of a test:

    test('renders react link', () => {
        render(<App/>)
        const linkElement = screen.getByText(/learn react/i)
        expect(linkElement).toBeInTheDocument()
    })
    

    You can also use the keywork it in stead of test.

    You can use describe blocks to group related tests.Describe blocks can be nested inside eachother. Using a describe block:

    describe('some component or category', () => {
        it('tests something'), () => {...}
        it('tests something else'), () => {...}
    })
    

    Common import statements

    import {render, screen, fireEvent} from '@testing-library/react'
    import Component from './Component'
    

    How to render elements

    render(<SomeComponent/>)

    Finding elements

    To find elements we use the screen. keyword and its methods. Most methods start with 'find', eg findAllByText or 'get' eg getAllByText or 'query', eg queryAllByText. What is the difference between find, get and query?

    Table comparing get, find and query

    From Net Ninja tut ep 5. Use find for async operations.

    The priority of methods is like this: Method priority

    From Net Ninja tut ep 6. Prefer the ones at the top. Go down the stack only if the higher methods can't be used.

    Fire events

    fireEvent.someEvent(args) , for example:

    fireEvent.change(inputElement, {target: {value: "Some value"}})
    expect(inputElement.value).toBe("Some value")
    

    Mocking

    How to mock objects with Jest

    const mockFunction = jest.fn()

    How to mock request to API

    In src, create a folder called __mocks__. Create a file with the thing you want to mock, eg axios.js. In the file, add the mock code:

    const mockResponse = {
        data: {
            results: [
                {
                    name: {
                        first: "Laith",
                        last: "Harb"
                    },
                    picture: {large: "https://...jpeg"},
                    login: {username: "ThePhonyGoat"}
                }
            ]
        }
    }
    export default {
        get: jest.fn().mockResolvedValue({mockResponse})
    }
    

    NB: For this to work you have to set resetMocks: false in react-scripts/scripts/utils/createJestConfig.js.

    Before & after each

    describe("Some component", () => {
    
        //Add beforeEach hook
        beforeEach(() => {
            console.log("Running before each test")
        })
    
        //AND/OR
    
        //Add beforeAll hook
        beforeAll(() => {
            console.log("Running once before all tests")
        })
    
        // Do the test
        it("Shoud test something", () => {...})
    
        //Add afterEach hook
        afterEach(() => {
            console.log("Running after each test")
        })
    
        //AND/OR
    
        //Add afterAll hook
        afterAll(() => {
                console.log("Running once after all tests")
            })
    })
    

    Integration tests

    A list of typical UI tests

    should render element should be able to type in input should have empty input when button is clicked should render the same text passed in to textbox prop

    ChatGPT prompt for writing tests

    1. Read the following code and then write a list of tests. You must identify unit tests and integration tests. You do not have to write the code for the tests, just list them for example: should render element should be able to type in input... and so on

    Workspace Templates: Workspace templates in Visual Studio Code allow you to define and save custom project structures as templates for reuse. Customization: You can customize your workspace template with specific folder structures, settings, extensions, and even initial files. Easy Project Setup: Workspace templates streamline project setup by providing a consistent starting point for new projects or workflows. Sharing: Workspace templates can be shared with team members to ensure consistency across projects and workflows. Quick Creation: Visual Studio Code makes it easy to create new projects from templates with just a few clicks or commands. Integration: Workspace templates integrate with other VS Code features like Git, tasks, and debugging, enhancing productivity and collaboration. Flexibility: Templates can be tailored to various project types, such as web development, Python projects, or TypeScript applications, providing flexibility for different workflows. Enhanced Development Experience: Using workspace templates promotes a more structured and organized development environment, leading to a better development experience

    The key difference between workspace templates and development containers in Visual Studio Code lies in their purpose and functionality:

    Workspace Templates:

    Purpose: Workspace templates are used to define and save custom project structures, settings, and configurations for reuse in Visual Studio Code. Functionality: They provide a way to quickly create new projects with a predefined structure and settings. Customization: Workspace templates can include folder structures, settings, extensions, and initial files tailored to specific project types or workflows. Usage: They streamline project setup and promote consistency across projects within a team or organization. Development Containers:

    Purpose: Development containers allow developers to create a consistent, isolated, and reproducible development environment using Docker containers. Functionality: They provide a way to encapsulate dependencies, tools, and configurations within a container, ensuring that all developers work in the same environment regardless of their local setup. Customization: Development containers can include specific versions of programming languages, frameworks, libraries, and tools required for development. Usage: They enable developers to develop, debug, and test applications in a containerized environment, enhancing consistency, reproducibility, and collaboration. While workspace templates and development containers serve different purposes, they can complement each other in certain scenarios:

    Combined Use: Workspace templates can be used to define project structures and settings, while development containers provide the underlying development environment within which the project is developed. Consistency and Reproducibility: Combining workspace templates with development containers ensures consistency and reproducibility not only in project structure but also in the development environment itself. Scalability: Development containers facilitate scalability by allowing developers to work in identical environments regardless of their local setup, while workspace templates ensure consistency in project structures and settings across teams and projects.

    {
    "folders": [
    {
    "path": "project1"
    },
    {
    "path": "project2"
    }
    ],
    "settings": {
    "files.autoSave": "afterDelay",
    "editor.tabSize": 2,
    "editor.formatOnSave": true,
    "python.linting.enabled": true
    },
    "extensions": [
    "ms-python.python",
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode"
    ],
    "launch": {
    "configurations": [
    {
    "type": "node",
    "request": "launch",
    "name": "Launch Program",
    "program": "${file}"
    },
    {
    "name": "Python: Current File",
    "type": "python",
    "request": "launch",
    "program": "${file}",
    "console": "integratedTerminal"
    }
    ]
    }
    }
    # YAML Cheatsheet
    ## Basics
    - YAML stands for "YAML Ain't Markup Language."
    - Uses indentation for structure.
    - Basic unit is a key-value pair.
    ## Data Types
    ### Scalars
    - Strings: `example: "value"`
    - Numbers: `example: 123`
    - Booleans: `example: true` or `example: false`
    - Null: `example: null`
    ### Collections
    - Lists:
    ```yaml
    example:
    - item1
    - item2
    # Dictionaries
    example:
    key1: value1
    key2: value2
    # Comments
    # This is a comment
    key: value # This is another comment
    # Nesting
    parent:
    child1:
    grandchild: value
    child2: value
    # Quoting: Strings containing special characters need to be quoted.
    example: "special characters: & * % !"
    # Anchors and Aliases: Define anchors (&) and reference them with aliases (*).
    parent:
    child1: &anchor value
    child2: *anchor
    # Multi-line Strings: Use | for preserving newlines or > for folding newlines.
    example: |
    This is a
    multi-line
    string.
    example: >
    This is a folded
    multi-line
    string.
    # Implicit Types: YAML automatically detects types.
    example: 123 # Integer
    example: 12.3 # Float
    example: Yes # Boolean
    # YAML vs JSON
    # YAML is more human-readable.
    # Supports comments.
    # More expressive (e.g., multi-line strings).
    # JSON is more widely supported and simpler.
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment