Skip to content

Commit eca65e4

Browse files
committed
chore: several updates
1 parent 659a638 commit eca65e4

File tree

3 files changed

+99
-61
lines changed

3 files changed

+99
-61
lines changed

src/keys.ts

Lines changed: 94 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,65 @@ import path from 'node:path'
44
import { log, runCommand } from '@stacksjs/cli'
55
import forge, { pki, tls } from 'node-forge'
66
import { config, resolveConfig } from './config'
7-
import type { GenerateCertOptions } from './types'
7+
import type { AddCertOptions, GenerateCertOptions } from './types'
88

9-
const makeNumberPositive = (hexString: string) => {
10-
let mostSignificativeHexDigitAsInt = Number.parseInt(hexString[0], 16)
11-
12-
if (mostSignificativeHexDigitAsInt < 8) return hexString
13-
14-
mostSignificativeHexDigitAsInt -= 8
15-
return mostSignificativeHexDigitAsInt.toString() + hexString.substring(1)
16-
}
17-
18-
// Generate a random serial number for the Certificate
19-
const randomSerialNumber = () => {
9+
/**
10+
* Generate a random serial number for the Certificate
11+
* @returns The serial number for the Certificate
12+
*/
13+
export const randomSerialNumber = (): string => {
2014
return makeNumberPositive(forge.util.bytesToHex(forge.random.getBytesSync(20)))
2115
}
2216

23-
// Get the Not Before Date for a Certificate (will be valid from 2 days ago)
24-
const getCertNotBefore = () => {
17+
/**
18+
* Get the Not Before Date for a Certificate (will be valid from 2 days ago)
19+
* @returns The Not Before Date for the Certificate
20+
*/
21+
export const getCertNotBefore = (): Date => {
2522
const twoDaysAgo = new Date(Date.now() - 60 * 60 * 24 * 2 * 1000)
2623
const year = twoDaysAgo.getFullYear()
2724
const month = (twoDaysAgo.getMonth() + 1).toString().padStart(2, '0')
2825
const day = twoDaysAgo.getDate()
2926
return new Date(`${year}-${month}-${day} 00:00:00Z`)
3027
}
3128

32-
// Get Certificate Expiration Date (Valid for 90 Days)
33-
const getCertNotAfter = (notBefore: any) => {
29+
/**
30+
* Get the Not After Date for a Certificate (Valid for 90 Days)
31+
* @param notBefore - The Not Before Date for the Certificate
32+
* @returns The Not After Date for the Certificate
33+
*/
34+
export const getCertNotAfter = (notBefore: Date): Date => {
3435
const ninetyDaysLater = new Date(notBefore.getTime() + 60 * 60 * 24 * 90 * 1000)
3536
const year = ninetyDaysLater.getFullYear()
3637
const month = (ninetyDaysLater.getMonth() + 1).toString().padStart(2, '0')
3738
const day = ninetyDaysLater.getDate()
39+
3840
return new Date(`${year}-${month}-${day} 23:59:59Z`)
3941
}
4042

41-
// Get CA Expiration Date (Valid for 100 Years)
42-
const getCANotAfter = (notBefore: any) => {
43+
/**
44+
* Get the CA Not After Date (Valid for 100 Years)
45+
* @param notBefore - The Not Before Date for the CA
46+
* @returns The Not After Date for the CA
47+
*/
48+
export const getCANotAfter = (notBefore: Date): Date => {
4349
const year = notBefore.getFullYear() + 100
4450
const month = (notBefore.getMonth() + 1).toString().padStart(2, '0')
4551
const day = notBefore.getDate()
52+
4653
return new Date(`${year}-${month}-${day} 23:59:59Z`)
4754
}
4855

49-
const DEFAULT_C = 'US'
50-
const DEFAULT_ST = 'California'
51-
const DEFAULT_L = 'Playa Vista'
52-
const DEFAULT_O = config?.ssl?.organizationName ?? 'Stacks.js'
56+
export const DEFAULT_C = 'US'
57+
export const DEFAULT_ST = 'California'
58+
export const DEFAULT_L = 'Playa Vista'
59+
export const DEFAULT_O: string = config?.ssl?.organizationName ?? 'Stacks.js'
5360

54-
// Generate a new Root CA Certificate
55-
export async function createRootCA() {
61+
/**
62+
* Create a new Root CA Certificate
63+
* @returns The Root CA Certificate
64+
*/
65+
export async function createRootCA(): Promise<GenerateCertReturn> {
5666
// Create a new Keypair for the Root CA
5767
const { privateKey, publicKey } = pki.rsa.generateKeyPair(2048)
5868

@@ -119,7 +129,19 @@ export async function createRootCA() {
119129
}
120130
}
121131

122-
export async function generateCert(options?: GenerateCertOptions) {
132+
type GenerateCertReturn = {
133+
certificate: string
134+
privateKey: string
135+
notBefore: Date
136+
notAfter: Date
137+
}
138+
139+
/**
140+
* Generate a new Host Certificate
141+
* @param options - The options for generating the certificate
142+
* @returns The generated certificate
143+
*/
144+
export async function generateCert(options?: GenerateCertOptions): Promise<GenerateCertReturn> {
123145
log.debug('generateCert', options)
124146

125147
if (!options?.hostCertCN.toString().trim()) throw new Error('"hostCertCN" must be a String')
@@ -210,18 +232,16 @@ export async function generateCert(options?: GenerateCertOptions) {
210232
return {
211233
certificate: pemHostCert,
212234
privateKey: pemHostKey,
235+
notBefore: newHostCert.validity.notBefore,
236+
notAfter: newHostCert.validity.notAfter,
213237
}
214238
}
215239

216-
export interface AddCertOptions {
217-
customCertPath?: string
218-
}
219-
220240
export async function addCertToSystemTrustStoreAndSaveCerts(
221241
cert: { certificate: string; privateKey: string },
222242
CAcert: string,
223243
options?: AddCertOptions,
224-
) {
244+
): Promise<string> {
225245
const certPath = storeCert(cert, options)
226246
const CAcertPath = storeCACert(CAcert, options)
227247

@@ -240,35 +260,6 @@ export async function addCertToSystemTrustStoreAndSaveCerts(
240260
// Linux (This might vary based on the distro)
241261
// for Ubuntu/Debian based systems
242262

243-
// return all directories that contain cert9.db file using fs.readdirSync
244-
function findFoldersWithFile(rootDir: string, fileName: string): string[] {
245-
const result: string[] = []
246-
247-
function search(dir: string) {
248-
try {
249-
const files = fs.readdirSync(dir)
250-
251-
for (const file of files) {
252-
const filePath = path.join(dir, file)
253-
const stats = fs.lstatSync(filePath) // Use fs.lstatSync instead
254-
255-
if (stats.isDirectory()) {
256-
search(filePath)
257-
} else if (file === fileName) {
258-
result.push(dir)
259-
}
260-
}
261-
} catch (error) {
262-
// Handle any errors (e.g., broken links, permission issues)
263-
console.warn(`Error reading directory ${dir}: ${error}`)
264-
}
265-
}
266-
267-
search(rootDir)
268-
return result
269-
}
270-
271-
//
272263
const rootDirectory = `${os.homedir()}`
273264
const targetFileName = 'cert9.db'
274265
const foldersWithFile = findFoldersWithFile(rootDirectory, targetFileName)
@@ -306,7 +297,7 @@ export async function addCertToSystemTrustStoreAndSaveCerts(
306297
return certPath
307298
}
308299

309-
export function storeCert(cert: { certificate: string; privateKey: string }, options?: AddCertOptions) {
300+
export function storeCert(cert: { certificate: string; privateKey: string }, options?: AddCertOptions): string {
310301
// Construct the path using os.homedir() and path.join()
311302
const certPath = options?.customCertPath || path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.crt`)
312303
const certKeyPath = options?.customCertPath || path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.crt.key`)
@@ -325,7 +316,13 @@ export function storeCert(cert: { certificate: string; privateKey: string }, opt
325316
return certPath
326317
}
327318

328-
export function storeCACert(CAcert: string, options?: AddCertOptions) {
319+
/**
320+
* Store the CA Certificate
321+
* @param CAcert - The CA Certificate
322+
* @param options - The options for storing the CA Certificate
323+
* @returns The path to the CA Certificate
324+
*/
325+
export function storeCACert(CAcert: string, options?: AddCertOptions): string {
329326
// Construct the path using os.homedir() and path.join()
330327
const CAcertPath = options?.customCertPath || path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.ca.crt`)
331328

@@ -338,4 +335,40 @@ export function storeCACert(CAcert: string, options?: AddCertOptions) {
338335
return CAcertPath
339336
}
340337

338+
function findFoldersWithFile(rootDir: string, fileName: string): string[] {
339+
const result: string[] = []
340+
341+
function search(dir: string) {
342+
try {
343+
const files = fs.readdirSync(dir)
344+
345+
for (const file of files) {
346+
const filePath = path.join(dir, file)
347+
const stats = fs.lstatSync(filePath) // Use fs.lstatSync instead
348+
349+
if (stats.isDirectory()) {
350+
search(filePath)
351+
} else if (file === fileName) {
352+
result.push(dir)
353+
}
354+
}
355+
} catch (error) {
356+
// Handle any errors (e.g., broken links, permission issues)
357+
console.warn(`Error reading directory ${dir}: ${error}`)
358+
}
359+
}
360+
361+
search(rootDir)
362+
return result
363+
}
364+
365+
const makeNumberPositive = (hexString: string) => {
366+
let mostSignificativeHexDigitAsInt = Number.parseInt(hexString[0], 16)
367+
368+
if (mostSignificativeHexDigitAsInt < 8) return hexString
369+
370+
mostSignificativeHexDigitAsInt -= 8
371+
return mostSignificativeHexDigitAsInt.toString() + hexString.substring(1)
372+
}
373+
341374
export { tls, pki, forge }

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,8 @@ export type CertDetails = {
3737
serialNumber: string
3838
}
3939

40+
export interface AddCertOptions {
41+
customCertPath?: string
42+
}
43+
4044
export type TlsConfig = DeepPartial<TlsOptions>

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"declaration": true,
1414
"noEmit": true,
1515
"outDir": "./dist",
16+
"isolatedDeclarations": true,
1617
"esModuleInterop": true,
1718
"forceConsistentCasingInFileNames": true,
1819
"verbatimModuleSyntax": true,

0 commit comments

Comments
 (0)