Skip to content

Commit 337b1a1

Browse files
authored
Merge pull request #77 from daeisbae/76-use-link-to-download-the-db-certificate
Change DB certificate fetching using s3 link (#76)
2 parents 0173d2e + d778813 commit 337b1a1

File tree

6 files changed

+154
-82
lines changed

6 files changed

+154
-82
lines changed

.env.example

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ DB_NAME=
55
DB_USER=
66
DB_PASSWORD=
77
# if you do not require any certificate, you can ignore this
8-
# if you want to need ssl connection with the DB, place the certificate inside the certificates folder then give the filename of the certificate
9-
DB_CERTIFICATE=
8+
# if you want to need ssl connection with the DB, upload the certificate to amazon s3 and provide link of the certificate
9+
DB_CERTIFICATE_LINK=
10+
DB_CERTIFICATE_REGION=
11+
DB_CERTIFICATE_BUCKET_NAME=
12+
DB_CERTIFICATE_FILE=
13+
DB_CERTIFICATE_ACCESS_KEY_ID=
14+
DB_CERTIFICATE_SECRET_ACCESS_KEY=
1015

1116
# Github Token for increasing rate limit of reading the repository
1217
GITHUB_TOKEN=

certificates/ADD_YOUR_CERTIFICATE_HERE

Whitespace-only changes.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"db:delete": "node --experimental-specifier-resolution=node src/db/scripts/drop-db.js"
1414
},
1515
"dependencies": {
16+
"@aws-sdk/client-s3": "^3.717.0",
1617
"@google/generative-ai": "^0.21.0",
1718
"@langchain/core": "^0.3.26",
1819
"@radix-ui/react-navigation-menu": "^1.2.3",

src/db/config/config.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import dotenv from 'dotenv'
2-
dotenv.config()
1+
import dotenv from "dotenv";
2+
dotenv.config();
33

44
export const DBConfig = {
5-
host: process.env.DB_HOST,
6-
port: parseInt(process.env.DB_PORT),
7-
database: process.env.DB_NAME,
8-
user: process.env.DB_USER,
9-
password: process.env.DB_PASSWORD,
10-
certificate: process.env.DB_CERTIFICATE,
11-
}
5+
host: process.env.DB_HOST,
6+
port: parseInt(process.env.DB_PORT),
7+
database: process.env.DB_NAME,
8+
user: process.env.DB_USER,
9+
password: process.env.DB_PASSWORD,
10+
certificateLink: process.env.DB_CERTIFICATE_LINK,
11+
certificateRegion: process.env.DB_CERTIFICATE_REGION,
12+
certificateBucket: process.env.DB_CERTIFICATE_BUCKET_NAME,
13+
certificateFile: process.env.DB_CERTIFICATE_FILE,
14+
certificateAccessKeyID: process.env.DB_CERTIFICATE_ACCESS_KEY_ID,
15+
certificateSecretAccessKey: process.env.DB_CERTIFICATE_SECRET_ACCESS_KEY,
16+
};

src/db/scripts/init-db.js

Lines changed: 83 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,96 @@
1-
import { createRequire } from 'module'
2-
const require = createRequire(import.meta.url)
3-
const { Client } = require('pg')
4-
const { readFile } = require('fs/promises')
5-
import { fileURLToPath } from 'url'
6-
import { dirname, join } from 'path'
7-
import { DBConfig } from '../config/config.js'
8-
import { promises as fs } from 'fs'
1+
import { createRequire } from "module";
2+
const require = createRequire(import.meta.url);
3+
const { Client } = require("pg");
4+
const { readFile } = require("fs/promises");
5+
import { fileURLToPath } from "url";
6+
import { dirname, join } from "path";
7+
import { DBConfig } from "../config/config.js";
8+
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
99

10-
const __dirname = dirname(fileURLToPath(import.meta.url))
10+
const __dirname = dirname(fileURLToPath(import.meta.url));
11+
12+
async function downloadCertificate() {
13+
const s3Client = new S3Client({
14+
region: DBConfig.certificateRegion,
15+
endpoint: DBConfig.certificateLink,
16+
credentials: {
17+
accessKeyId: DBConfig.certificateAccessKeyID,
18+
secretAccessKey: DBConfig.certificateSecretAccessKey,
19+
},
20+
});
21+
22+
try {
23+
const command = new GetObjectCommand({
24+
Bucket: DBConfig.certificateBucket,
25+
Key: DBConfig.certificateFile,
26+
});
27+
28+
const response = await s3Client.send(command);
29+
const certificateData = await response.Body.transformToString();
30+
return certificateData;
31+
} catch (error) {
32+
console.error("Failed to download certificate:", error);
33+
throw error;
34+
}
35+
}
1136

1237
async function initDatabase() {
13-
console.log('Initializing database with config:', {
14-
host: DBConfig.host,
15-
port: DBConfig.port,
16-
database: DBConfig.database,
17-
user: DBConfig.user,
18-
})
38+
console.log("Initializing database with config:", {
39+
host: DBConfig.host,
40+
port: DBConfig.port,
41+
database: DBConfig.database,
42+
user: DBConfig.user,
43+
password: DBConfig.password,
44+
certificateLink: DBConfig.certificateLink,
45+
});
1946

20-
const { certificate, config } = DBConfig
47+
const { config, certificateLink } = DBConfig;
2148

22-
let pgClient = undefined
49+
let pgClient = undefined;
2350

24-
if (!certificate) {
25-
pgClient = new Client({
26-
...config,
27-
})
28-
} else {
29-
pgClient = new Client({
30-
...config,
31-
ssl: {
32-
rejectUnauthorized: false,
33-
ca: fs
34-
.readFile(process.cwd() + '/certificates/' + certificate)
35-
.toString(),
36-
sslmode: 'require',
37-
},
38-
})
39-
}
51+
if (!certificateLink) {
52+
pgClient = new Client({
53+
...config,
54+
});
55+
} else {
56+
const certificateData = await downloadCertificate();
57+
pgClient = new Client({
58+
...config,
59+
ssl: {
60+
rejectUnauthorized: false,
61+
ca: certificateData,
62+
sslmode: "require",
63+
},
64+
});
65+
}
4066

41-
pgClient.connect()
67+
await pgClient.connect();
4268

43-
await pgClient
44-
.query(`CREATE DATABASE ${DBConfig.database};`)
45-
.catch((error) => {
46-
console.log('❌ Database is already created')
47-
})
48-
.then(() => {
49-
console.log('✅ Database created successfully')
50-
})
69+
await pgClient
70+
.query(`CREATE DATABASE ${DBConfig.database};`)
71+
.catch((error) => {
72+
console.log("❌ Database is already created");
73+
})
74+
.then(() => {
75+
console.log("✅ Database created successfully");
76+
});
5177

52-
const schemaPath = join(__dirname, '../migrations/create_tables.sql')
53-
const sql = await readFile(schemaPath, 'utf8')
78+
const schemaPath = join(__dirname, "../migrations/create_tables.sql");
79+
const sql = await readFile(schemaPath, "utf8");
5480

55-
await pgClient.query(sql).catch((error) => {
56-
console.error('❌ Schema loading failed:', error)
57-
})
58-
console.log('✅ Schema loaded successfully')
59-
pgClient.end()
81+
await pgClient.query(sql).catch((error) => {
82+
console.error("❌ Schema loading failed:", error);
83+
});
84+
console.log("✅ Schema loaded successfully");
85+
pgClient.end();
6086
}
6187

6288
initDatabase()
63-
.then(() => {
64-
console.log('✅ Database initialization complete')
65-
process.exit(0)
66-
})
67-
.catch((error) => {
68-
console.error('❌ Fatal error:', error)
69-
process.exit(1)
70-
})
89+
.then(() => {
90+
console.log("✅ Database initialization complete");
91+
process.exit(0);
92+
})
93+
.catch((error) => {
94+
console.error("❌ Fatal error:", error);
95+
process.exit(1);
96+
});

src/db/utils/connector.ts

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
import pg, { QueryResult, QueryResultRow } from 'pg'
22
import { DBConfig } from '@/db/config/config'
3-
import { promises as fs } from 'fs'
3+
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
4+
5+
async function downloadCertificate() {
6+
const s3Client = new S3Client({
7+
region: DBConfig.certificateRegion,
8+
endpoint: DBConfig.certificateLink,
9+
credentials: {
10+
accessKeyId: DBConfig.certificateAccessKeyID || '',
11+
secretAccessKey: DBConfig.certificateSecretAccessKey || '',
12+
},
13+
});
14+
15+
try {
16+
const command = new GetObjectCommand({
17+
Bucket: DBConfig.certificateBucket,
18+
Key: DBConfig.certificateFile,
19+
});
20+
21+
const response = await s3Client.send(command);
22+
const certificateData = await response.Body!.transformToString();
23+
return certificateData;
24+
} catch (error) {
25+
console.error("Failed to download certificate:", error);
26+
throw error;
27+
}
28+
}
429

530
/**
631
* Database connection handler (Instance pattern - Although not recommended due to thread issue, this is currently the best solution)
@@ -18,21 +43,31 @@ class DBConnector {
1843
private constructor() {
1944
const { Pool } = pg
2045
this.conn = false
21-
const { certificate, ...config } = DBConfig
22-
if (!certificate) {
46+
const { ...config } = DBConfig
47+
if (!DBConfig.certificateLink) {
2348
this.pool = new Pool(config)
2449
return
2550
}
26-
this.pool = new Pool({
27-
...config,
28-
ssl: {
29-
rejectUnauthorized: false,
30-
ca: fs
31-
.readFile(process.cwd() + '/certificates/' + certificate)
32-
.toString(),
33-
sslmode: 'require',
34-
},
35-
})
51+
52+
try {
53+
let certificate: string | undefined = undefined
54+
downloadCertificate().then((cert) => {
55+
certificate = cert
56+
});
57+
this.pool = new Pool({
58+
...config,
59+
ssl: {
60+
rejectUnauthorized: false,
61+
ca: certificate,
62+
},
63+
})
64+
} catch (error) {
65+
console.log(
66+
`Database connection failed: ${
67+
error instanceof Error ? error.message : 'Unknown error'
68+
}`
69+
)
70+
}
3671
}
3772

3873
/**
@@ -66,7 +101,7 @@ class DBConnector {
66101
this.conn = false
67102
return result
68103
} catch (error) {
69-
this.conn && client.release()
104+
this.conn && client!.release()
70105
console.log(
71106
`Database query failed: ${
72107
error instanceof Error ? error.message : 'Unknown error'

0 commit comments

Comments
 (0)