Tembo CA certificate download using API at build time

July 26, 2024 (1mo ago)

Tembo is a postgresql database provider with some of interesting features. I am using node-postgres which is a collection of node.js modules for interfacing with my PostgreSQL tembo database. It supports full-ssl mode and certificates for connection to database.

  1. Create a tembo token before interacting with it's API here.

  2. The token is a JWT token and can be verified here

  3. Create an .env environment varible file for storing secrets like token, instance & organisation id of tembo.

// .env
TEMBO_ORG_ID="org_123...." // available in decoded jwt token
TEMBO_INST_ID="inst_123..." // find it in url bar of your instance dashboard
TEMBO_TOKEN="e..." // jwt token
  1. we need dotenv package as well, install it using npm install dotenv
// ./setup.mjs

import path from "path";
import { spawn } from "node:child_process";
import fs from "fs";
import "dotenv/config";
const syncContentFromGit = async () => {
  const syncRun = async () => {
    const ORG_ID = process.env.TEMBO_ORG_ID;
    const INST_ID = process.env.TEMBO_INST_ID;
    const token = process.env.TEMBO_TOKEN;
    const Url = `https://api.data-1.use1.tembo.io/api/v1/orgs/${ORG_ID}/instances/${INST_ID}/secrets/certificate`;
    await runBashCommand(`
      curl -s -X 'GET' \
        ${Url} \
        -H 'accept: application/json' \
        -H "Authorization: Bearer ${token}" \
        -H 'Content-Type: application/json' \
        -o ca.crt
    `);
    // console.log(token);
    let rawdata = fs.readFileSync("./ca.crt");
// You Can add some checks if ca.crt file obtained is actually a json file or not.
    let ca = JSON.parse(rawdata);
    for (var key in ca) {
      try {
        fs.writeFileSync("./ca.crt", ca[key]);
        console.log("Writing Certificate...");
      } catch (err) {
        console.error(err);
      }
    }
  };

  let wasCancelled = true; // fetch once or keep syncing
  let syncInterval;

  const syncLoop = async () => {
    console.log("Downloading CA certificate from Tembo API...");
    await syncRun();

    if (wasCancelled) return;

    syncInterval = setTimeout(syncLoop, 1000 * 60);
  };

  // Block until the first sync is done
  await syncLoop();

  return () => {
    wasCancelled = true;
    clearTimeout(syncInterval);
  };
};

const runBashCommand = (command) =>
  new Promise((resolve, reject) => {
    const child = spawn(command, [], { shell: true });

    child.stdout.setEncoding("utf8");
    child.stdout.on("data", (data) => process.stdout.write(data));

    child.stderr.setEncoding("utf8");
    child.stderr.on("data", (data) => process.stderr.write(data));

    child.on("close", function (code) {
      if (code === 0) {
        resolve(void 0);
      } else {
        reject(new Error(`Command failed with exit code ${code}`));
      }
    });
  });

(async () => {
  await syncContentFromGit();
})();

Now, run this file node ./setup.mjs or club it with build command to trigger it at each build process by editing in package.json.