PM2

Process manager for Node.js

IMPORTANT COMMANDS

pm2 start –name server_name –watch

server name = Your prefer server name
watch flag to reflect code update

pm2 status

show the server status

pm2 restart server_name or ID

restart a single process, check the status for name or ID.

pm2 restart all

restart all process

pm2 logs server_name or ID –line 20

display log 20 lines to check any error occurs

pm2 show server_name or ID

display process details. check exec cwd, scrpit path

I use PM2 for handling my website contact form email server. 

I got 500 error after restarted all PM2 process. 

To solve,

  • updated node version 8 > 20 (!!), 
  • reinstall PM2 and other node modules (actually I deleted all modules once and reinstall all)
  • use .env for credential information such as SMTP user and key, so make sure .env info is reflected to PM2 through server code.
  • And since update node and PM2, flush all old setting to start all the  PM2 processes.

Google reCAPTCHA v3 Enterprise

Google reCAPTCHA V3: Enterprise key generation, config and implementation.

Some issues with my website’s contact form,  a robot sent a series of spam emails, the regular V3 only protects the frontend submission,  not direct attack from the backend.

So I have to update to the Enterprise version. I think so far works well and I recommended to update if you haven’t yet.

The process

All Google services have moved to under Google Cloud, so you have to use Google Cloud console to add, config, set up your reCAPTCHA key to frontend and backend.

If you are familiar with Google Cloud, I guess it’s not difficult. But if not, it’s hard to find where to start and where to look for, how to generate keys and others.

Especially the process instruction in the reCAPTCHA key generation page is not all you need to deal with like before. More than you think, more to read doc. more to understand their structure etc.. .

So please follow how I did.

Create a project

Anything you do there, first create a project. If you already done something and have a project, you can use the existing project but I recommend to create a new project for “G-reCAPTCHA”.

https://console.cloud.google.com/

Where to start

Google reCAPTCHA is located in the “SECURITY” section, under “Fraud Defense”. click that from the top left hamburger menu.

In the Dashboard, if you’ve used some “old” reCAPTCHA, you can find the keys there too in the list below.

I decide to start from creating a new key for my website.

NOTE: When you create a new key, it will automatically create a  “Essential” plan. And t’s “Free up to 10,000 assessments/month”. It’s good for my kind of business since I don’t expect so many access. But if you run an eCommerce site, maybe consider to update. You can check and update the plans in the “Attacks” tab.

+ Create Key

Click to + Create key.
Fill:

  1. [Display name (like a site name)],
  2. select Application  type [Web|Android|iOS]
  3. and [Add domain](that’s the site to use the reCAPTCHA)
  4. then [Create Key]


Follow the ① Frontend and ② Backend setups in the “Integration” tab
Easy!

NO!!!! It’s not that simple.
More steps are hidden.

But for now, you created a reCAPTCHA key successfully anyway.

Frontend


Following the “integration”
instruction.
At the top of your web site such as index.html, inside < head> </head> tag,
add the line:

This is simple.
Don’t forget to replace the Site KEY, in the line after:
enterprise.js?render=

<script> 
    src="https://www.google.com/recaptcha/enterprise.js?render=your-site-key">
</script>

Then, for the form submission, add:

<script> 
    function onClick(e) {
        e.preventDefault();
        grecaptcha.enterprise.ready(async () => {
        const token = await grecaptcha.enterprise.execute('your-site-key (ID)', {action: 'LOGIN'});
});
}
</script>

My example here, vuejs composition-API

try {
    const token = await new Promise((resolve, reject) => {
      if (typeof grecaptcha === 'undefined' || !grecaptcha.enterprise) {
        reject(new Error('reCAPTCHA library not loaded. Check if script is blocked.'));
        return;
      }

      grecaptcha.enterprise.ready(function() {
        grecaptcha.enterprise.execute('your-key', { 
          action: 'contact' 
        })
        .then(resolve)
        .catch(reject);
      });
    });

    const payload = {
      email: user.value.email,
      name: user.value.name,

      recaptchaToken: token
    };

    const response = await axios.post(variables.DBP, payload);
    console.log('Success:', response.data);
    
    initForm();
    thankYou.value = true;
  } catch (err) {
    console.error('Submit error:', err);
    alert(`Submission failed: ${err.message}`);
  } finally {
    loading.value = false;
  }
};
e;
  }
};

Backend - (tricky)


To adding code the backend server.
I use Node.js Express server.

actually it took me long…
It wasn’t clear, easy to find out this

Before proceeding, you must authenticate with reCAPTCHA. API requests to reCAPTCHA will fail until authentication steps are complete.

Learn More

click [Learn More] to get how to “authenticate”, but it’s pretty complex, at least to me….
but Anyway, First you need to generate a json file for the backend.

Generate a json file for the backend

  1. go to the left top, hamburger menu [IAM & Admin > Service Accounts]
  2. on the top, click [+ Create]
  3. service account
  4. Create service account
  5. service account
    • name = recaptcha-service
    • service account ID will automatically created
    • sercice account description = google recaptcha
  6. Once created it, go to [Actions] (three vertical dots)
    [Add key] > create new key > JSON
    it will generate a key and download to your computer automatically.
  7. You can find the file something like “recpatcha26-xxxxx-xxxxx.json” in your download folder


This process ② Permission (option) is important. Don’t forget to select “reCAPTCHA Enterprise Agent”

I didn’t first time because it says (option)… and when tested my server I got 500 error.
So it’s better to config when create a service account.


Once you’ve done creating the account, you can find in the list with newly created Email.

Select Manage Keys from Actions ⠇

Then [Add key] > [Create new ley]

Select JSON

Once you are successfully get and download the json file. Then upload it to the same directory where the server.js (node server app) located. (Actually you can place anywhere in the server although it’s easier to configure the path)

Configure the server script (Node.js)


After uploading the json file, yourProjectName-xxxxxxx-xxxxx.json

You need to install @google-cloud/recaptcha-enterprise package.


In your IPS cloud server where your express server (server.js) is, (mine is Linux Ubuntu) type:

npm i @google-cloud/recaptcha-enterprise

and to configure the server.js adding lines below:

const {RecaptchaEnterpriseServiceClient} = require('@google-cloud/recaptcha-enterprise');
const recaptchaClient = new RecaptchaEnterpriseServiceClient({
  keyFilename: "/path/To/your/projectName-xxxxx-xxxx.json"
});

My sample code for app.post:

app.post('/contact', async (req, res) => {
  const token = req.body.recaptchaToken; // send this from the client
  if (!token) {
    return res.status(400).send
    ({ error: 'Missing recaptcha token' })
  };

  try {
    const request = {
      parent: `projects/${'yourProjectName'}`,
      assessment: {
        event: {
          token,
          siteKey: 'YourReCAPTCHAkey-same-as-theFrontend'
        },
      },
    };
    
    const [assessment] = 
      await recaptchaClient.createAssessment(request);

    if (!assessment.tokenProperties || !assessment.tokenProperties.valid) {
      return res.status(403).send({ error: 'Invalid reCAPTCHA token' });
    }

    const score = assessment.riskAnalysis?.score ?? 0;
    if (score < 0.5) return res.status(403).send({ error: 'Low reCAPTCHA score', score });

    const transporter = nodemailer.createTransport({ 
      host: "smtp.yourMailService.com",
      port: 587,
      auth: {
        user: "yourUserName",
        pass: "YourPassword"
      },
     });

    const mailOptions = {
      from: 'info@koo-ds.com',
      subject: `Message from ${req.body.email}`,
      html: `
        <h1>Message from ${req.body.name}</h1>

        Email:${req.body.email}
      `, 
    };

    await transporter.sendMail(mailOptions);
    return res.send({ message: 'Success' });

  } catch (err) {
    console.error('reCAPTCHA verification error:', err);
    return res.status(500).send({ error: 'reCAPTCHA verification failed' });
  }
});

Please refer https://koo-ds.com/contactHow for Node Express Server development.

TODO:  

in console.cloud.googe.com

  1. Create a new project 
  2. get reCAPTCHA key
  3. Add it to the frontend <head>
  4. configure your sending script
  5. back to google cloud to create Service Account and get .json file
  6. upload the json to the production server
  7. configure server.js adding lines for reCAPTCHA
  8. make sure the path to .json file is correct
  9. test if now error, means you get inquire email from the site form. Back to Google Cloud to check the reCAPTCHA is “Protected”. Frontend and backend integrated.

Then you will see the activities.

GOOD LUCK!!

WordPress update

for migrating Ubuntu22, php8.1, I’ve updated my blog (wordpress) too.

  1. Simply download the latest WordPress (6.3.1) and place in a server designated area (ie. /var/www/wordpress) and chown www-data:www-data -R /var/www/wordpress
  2. in the mysql database, create a database named ‘wordpress‘
  3. adjust php-fpm and nginx max_upload_file_size, if you want to upload .sql file using phpmyadmin. then create a database named ‘wordpress-yourdata‘
  4. import the SQL file, so all the previous data is migrated.
  5. go to ‘wordpress’ folder, edit ‘wp-config.php‘
  6. create nginx conf file for the blog (ie. blog.koodesignstudio.com.conf)
  7. restart nginx (dont’ forget “sudo nginx -t“
  8. certbot to the blog.koodesignstudio.com
  9. access with a browser
  10. initial configuration for the new wordpress
  11. start from a brand new plain wordpress,
  12. update ‘wp-config.php‘ database to ‘wordpress-yourdata‘, now all the data is coming from the previous database
  13. copy images from the previous wordpress ./wordpress/wp-content/uploads
  14. install the theme and plug-ins as same as the previous
  15. done!

!IMPORTANT even to migrate from another server, frist create a default ‘wordpress’ dabase, then change ‘wp-config.php’

Vue 2 add gtag for GA4

This is for vue 2

First: install ‘vue-gtag’ . this should be Ver 1. And it works with GA4 too.

IMPORTANT: add @legacy

npm install vue-gtag@legacy

Second: Update main.js

NOTE: use “vue-router” to automatically log “pages”

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

// vue-gtag
import VueGtag from 'vue-gtag'
Vue.use(VueGtag, {
  config: {
    id: 'G-xxxxxxxxx'
  }
}, router)

new Vue({
  router,
  render: (h) => h(App),
}).$mount("#app");

DONE!

Certbot

Generate a certificate for nginx

$ sudo certbot --nginx
$ sudo certbot --nginx -d example.com

List all the certificates

$ sudo certbot certificates

NOTE: check each nginx conf files updated by Certbot

Delete a specific certificate

$ sudo certbot delete --cert-name example.com

EXPAND, add a new domain to existing certificate

$ sudo certbot certonly --expand --cert-name existingdomain -d existingdomain.com,www.existingdomain.com,new.existingdomain.com

ADDITIONAL info added 10/09/2024

In case, accidentally deleted a certificate, here how to fix.

sudo certbot certonly --standalone -d deletedDomainname.com

Since still nginx.conf contain information about the deleted certificate, it need reissue certificate ONLY = certonly flag to avoide nginx error such as:

nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/domainname.com/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/letsencrypt/live/domainname.com/fullchain.pem, r) error:10000080:BIO routines::no such file)
nginx: configuration file /etc/nginx/nginx.conf test failed

mysql | memo

case: remove a row with FOREIGN_KEY

SET FOREIGN_KEY_CHECK = 0;
DELETE FROM `tableName` WHERE (id = `idNumber`);
SET FOREIGN_KEY_CHECK = 1;

case: reset AUTO_INCREMENT id MAX

ALTER TABLE `tableName` AUTO_INCREMENT = `value`;

The ‘value’ is where the AUTO_INCREMENT starts next time.

SELECT @max := MAX(ID) + 1 FROM `tableName`;
PREPARE stmt FROM 'ALTER TABLE `tableName` AUTO_INCREMENT = ?';
EXECUTE stmt USING @max;
DEALLOCATE PREPARE stmt;

case: unable to enter `emptyValue` ” to a NULL space. (once rebooted mySQL, run the command)

SET @@global.sql_mode = '';

Vue3 w/pinia, bootstrap5 and axios | my memo

Finally updated my mentality to Vue 3. I’ve updated my Mac OS from Catalina to Monterey a while ago and started to thinking about software update accordingly. The big one was Node, I’ve been using the version 12 for such a long time, so decided to update to v16. Thanks to nvm, I can switch versions when I needed. And started to try Vue3.

Vue3, Vite server, it’s just amazingly FAST!!! I’m loving it.

> npm init vue@latest
> npm run dev

So far, trying to get familiar with update and I’ve encounter an axios issue. I’ve install axios and vue-axios. According to https://stackoverflow.com/questions/68233550/what-is-the-different-between-axios-vue-axios. “vue-axios is just a wrapper, exposing axios to components as this.axios, this.$http, or Vue.axios.”

import axios from "axios";
import VueAxios from 'vue-axios'
app.use(VueAxios, axios);

app.mount("#app");

// with in a component add this
add(payload) {
const path = "URL";
this.axios.post(path, payload)
.then(() => {

I’ll update when I noticed something converting from vue2 to VUE3

SVG (scalable vector graphic) animation

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
  <rect x=0 y=0 width=100 height=100 fill="black" />
  <circle cx="20" cy="20" r="20" fill="red" />
</svg>

SVG animation with SMIL

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="100" height="100" fill="black"></rect>
  <circle cx="20" cy="30" r="20" fill="red">
    <animate attributeName="cx" from="0" to="100" dur="2s" repeatCount="indefinite"></animate>
    <animate attributeName="cy" from="30" to="100" dur="2s" repeatCount="indefinite"></animate>
</circle>
</svg>

You can use Javascript to manipulate element

var svg = document.getElementById('IDNAME');

and/or CSS

@keyframes

<style>
.wrapper{
height:400px;
width: 400px;
background:#111;
text-align: center;
overflow: hidden;
}
.svg-wrapper {
  height: 60px;
  margin: 0 auto;
  position: relative;
  top: 50%;
  transform: translateY(-50%);
  width: 320px;
}

.shape {
  fill: transparent;
  stroke-dasharray: 140 540;
  stroke-dashoffset: -474;
  stroke-width: 8px;
  stroke: #19f6e8;
}

.text {
  color: #fff;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 22px;
  letter-spacing: 8px;
  line-height: 32px;
  position: relative;
  top: -48px;
}

@keyframes draw {
  0% {
    stroke-dasharray: 140 540;
    stroke-dashoffset: -474;
    stroke-width: 8px;
  }
  100% {
    stroke-dasharray: 760;
    stroke-dashoffset: 0;
    stroke-width: 8px;
  }
}

.svg-wrapper:hover .shape {
  -webkit-animation: 0.5s draw linear forwards;
  animation: 0.5s draw linear forwards;
}
</style>
<div class="wrapper">
<div class="svg-wrapper">
  <svg height="50" width="320" xmlns="http://www.w3.org/2000/svg">
    <rect class="shape" height="60" width="320" />
  </svg>
   <div class="text">HOVER</div>
</div>
</div>
HOVER

git cli update

My previous post was to introduce git, this one is for CLI update/note:

REMOTE

$ git init ProjectName.git --bare (create remote empty git repo as origin master to push)

$ git push --set-upstream origin BranchName. (To push the current branch and set the remote as upstream)

$ git push -u origin myBranchName

$ git remote -v (List remote repo)

$ git remote add <name> <url> (add remote repo <name> = “origin”, url = server.com:/where/the/project.git)

FILE

$ git checkout --filename.ext (remove the file filename.ext from git)

$ git status (show current status of git, I execute it before git add .)

$ git restore filename.ext (before commit, restore the file to previous status, even file is saved, the content is back to the before)

CLEAN UP GIT

$ git clean -df projectName (clean up git)

$ git log --oneline --graph (show list of commit.)

$ git reset --hard HEAD (go to current HEAD.)

$ git reset --hard HEAD^ (go back to the 1 commit from current HEAD.)

$ git reset --hard HEAD~2 (go back to the 2 commits from current HEAD.)

$ git reset --hard index (go back to the commit where the index is)

UPDATED 09/16/2021

Just encounter an accident, so I’m updating the post.

Accidentally overwritten a file!! OMG. A awesome thing about git is you can retrieve a file or files later. Usually right after you save a file and notice a mistake and Ooops, I shouldn’t save the file, in stead save as different file name. Anyway if you saved and noticed, means you have not git commit the change. So simply:

$ git status (show status what has been modified)

$ git restore fileName (restore the file)

IMPORTANT: Once you notice, you need to change the file name before do git restore, so you can have both.

If you’ve already git add . + committed, you should use

git reset --hard HEAD^ (go back to the 1 commit from current HEAD.)

IMPORTANT: before git reset, always nice to have a current backup somewhere else in local or remote.

UPDATED 06/09/2022

find remote branch names

$ git remote -v show origin

pull the remote branch

$ git pull branch-name