diff --git a/README.md b/README.md index 8f7ccfb..a382c18 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ export default { name: 'test', /** custom trust domains */ domains: ['*.custom.com'], + /** optional, days before certificate expires */ + ttlDays: 30, /** custom certification directory */ certDir: '/Users/.../.devServer/cert', }), diff --git a/src/certificate.ts b/src/certificate.ts index 22dbb53..74b9751 100644 --- a/src/certificate.ts +++ b/src/certificate.ts @@ -55,8 +55,8 @@ function toPositiveHex(hexString: string) { export function createCertificate( name: string = 'example.org', domains?: string[], + ttlDays = 30, ): string { - const days = 30 const keySize = 2048 const appendDomains = domains @@ -146,7 +146,7 @@ export function createCertificate( cert.validity.notBefore = new Date() cert.validity.notAfter = new Date() - cert.validity.notAfter.setDate(cert.validity.notBefore.getDate() + days) + cert.validity.notAfter.setDate(cert.validity.notBefore.getDate() + ttlDays) cert.setSubject(attrs) cert.setIssuer(attrs) diff --git a/src/index.ts b/src/index.ts index 2b44c0c..36f667d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import { X509Certificate } from 'node:crypto' import { promises as fsp } from 'node:fs' import type { Plugin } from 'vite' @@ -8,6 +9,7 @@ interface Options { certDir: string domains: string[] name: string + ttlDays: number } function viteBasicSslPlugin(options?: Partial): Plugin { @@ -18,6 +20,7 @@ function viteBasicSslPlugin(options?: Partial): Plugin { options?.certDir ?? (config.cacheDir ?? defaultCacheDir) + '/basic-ssl', options?.name, options?.domains, + options?.ttlDays, ) const https = () => ({ cert: certificate, key: certificate }) if (config.server.https === undefined || !!config.server.https) { @@ -34,17 +37,19 @@ export async function getCertificate( cacheDir: string, name?: string, domains?: string[], + ttlDays?: number, ) { const cachePath = path.join(cacheDir, '_cert.pem') try { - const [stat, content] = await Promise.all([ - fsp.stat(cachePath), - fsp.readFile(cachePath, 'utf8'), - ]) + const content = await fsp.readFile(cachePath, 'utf8') + const cert = new X509Certificate(content); - if (Date.now() - stat.ctime.valueOf() > 30 * 24 * 60 * 60 * 1000) { - throw new Error('cache is outdated.') + // validTo is a nonstandard format, but it successfully parses. validToDate + // is not available until node 22 + // https://github.com/nodejs/node/issues/52931 + if (Date.now() > Date.parse(cert.validTo)) { + throw new Error('cache is outdated.'); } return content @@ -52,6 +57,7 @@ export async function getCertificate( const content = (await import('./certificate')).createCertificate( name, domains, + ttlDays, ) fsp .mkdir(cacheDir, { recursive: true })