@tapflowio/relay 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/RelayServer.d.ts +10 -0
- package/dist/RelayServer.d.ts.map +1 -1
- package/dist/RelayServer.js +53 -5
- package/dist/RelayServer.js.map +1 -1
- package/dist/api/builds.d.ts.map +1 -1
- package/dist/api/builds.js +14 -13
- package/dist/api/builds.js.map +1 -1
- package/dist/api/comments.d.ts.map +1 -1
- package/dist/api/comments.js +23 -10
- package/dist/api/comments.js.map +1 -1
- package/dist/api/team.d.ts.map +1 -1
- package/dist/api/team.js +4 -2
- package/dist/api/team.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/cert/AcmeCertProvider.d.ts +55 -0
- package/dist/lib/cert/AcmeCertProvider.d.ts.map +1 -0
- package/dist/lib/cert/AcmeCertProvider.js +62 -0
- package/dist/lib/cert/AcmeCertProvider.js.map +1 -0
- package/dist/lib/cert/AcmeClientIssuer.d.ts +23 -0
- package/dist/lib/cert/AcmeClientIssuer.d.ts.map +1 -0
- package/dist/lib/cert/AcmeClientIssuer.js +54 -0
- package/dist/lib/cert/AcmeClientIssuer.js.map +1 -0
- package/dist/lib/cert/CertProvider.d.ts +30 -0
- package/dist/lib/cert/CertProvider.d.ts.map +1 -0
- package/dist/lib/cert/CertProvider.js +4 -0
- package/dist/lib/cert/CertProvider.js.map +1 -0
- package/dist/lib/cert/CloudflareDnsProvider.d.ts +40 -0
- package/dist/lib/cert/CloudflareDnsProvider.d.ts.map +1 -0
- package/dist/lib/cert/CloudflareDnsProvider.js +93 -0
- package/dist/lib/cert/CloudflareDnsProvider.js.map +1 -0
- package/dist/lib/cert/DiskCertStore.d.ts +10 -0
- package/dist/lib/cert/DiskCertStore.d.ts.map +1 -0
- package/dist/lib/cert/DiskCertStore.js +35 -0
- package/dist/lib/cert/DiskCertStore.js.map +1 -0
- package/dist/lib/cert/DnsProvider.d.ts +7 -0
- package/dist/lib/cert/DnsProvider.d.ts.map +1 -0
- package/dist/lib/cert/DnsProvider.js +2 -0
- package/dist/lib/cert/DnsProvider.js.map +1 -0
- package/dist/lib/cert/ImportCertProvider.d.ts +20 -0
- package/dist/lib/cert/ImportCertProvider.d.ts.map +1 -0
- package/dist/lib/cert/ImportCertProvider.js +24 -0
- package/dist/lib/cert/ImportCertProvider.js.map +1 -0
- package/dist/lib/cert/VercelDnsProvider.d.ts +38 -0
- package/dist/lib/cert/VercelDnsProvider.d.ts.map +1 -0
- package/dist/lib/cert/VercelDnsProvider.js +146 -0
- package/dist/lib/cert/VercelDnsProvider.js.map +1 -0
- package/dist/lib/cert/accountKey.d.ts +2 -0
- package/dist/lib/cert/accountKey.d.ts.map +1 -0
- package/dist/lib/cert/accountKey.js +23 -0
- package/dist/lib/cert/accountKey.js.map +1 -0
- package/dist/lib/cert/addressPublisher.d.ts +20 -0
- package/dist/lib/cert/addressPublisher.d.ts.map +1 -0
- package/dist/lib/cert/addressPublisher.js +103 -0
- package/dist/lib/cert/addressPublisher.js.map +1 -0
- package/dist/lib/cert/createCertProvider.d.ts +20 -0
- package/dist/lib/cert/createCertProvider.d.ts.map +1 -0
- package/dist/lib/cert/createCertProvider.js +25 -0
- package/dist/lib/cert/createCertProvider.js.map +1 -0
- package/dist/lib/cert/dnsRegistry.d.ts +24 -0
- package/dist/lib/cert/dnsRegistry.d.ts.map +1 -0
- package/dist/lib/cert/dnsRegistry.js +36 -0
- package/dist/lib/cert/dnsRegistry.js.map +1 -0
- package/dist/lib/cert/index.d.ts +23 -0
- package/dist/lib/cert/index.d.ts.map +1 -0
- package/dist/lib/cert/index.js +12 -0
- package/dist/lib/cert/index.js.map +1 -0
- package/dist/lib/cert/parseCert.d.ts +3 -0
- package/dist/lib/cert/parseCert.d.ts.map +1 -0
- package/dist/lib/cert/parseCert.js +6 -0
- package/dist/lib/cert/parseCert.js.map +1 -0
- package/dist/lib/cert/renewal.d.ts +13 -0
- package/dist/lib/cert/renewal.d.ts.map +1 -0
- package/dist/lib/cert/renewal.js +28 -0
- package/dist/lib/cert/renewal.js.map +1 -0
- package/dist/lib/config.d.ts +22 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +39 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/cors.d.ts +2 -0
- package/dist/lib/cors.d.ts.map +1 -0
- package/dist/lib/cors.js +18 -0
- package/dist/lib/cors.js.map +1 -0
- package/dist/lib/csrf.d.ts +3 -0
- package/dist/lib/csrf.d.ts.map +1 -0
- package/dist/lib/csrf.js +42 -0
- package/dist/lib/csrf.js.map +1 -0
- package/dist/lib/loadEnvFile.d.ts +2 -0
- package/dist/lib/loadEnvFile.d.ts.map +1 -0
- package/dist/lib/loadEnvFile.js +17 -0
- package/dist/lib/loadEnvFile.js.map +1 -0
- package/dist/lib/proxyConfig.d.ts +6 -0
- package/dist/lib/proxyConfig.d.ts.map +1 -0
- package/dist/lib/proxyConfig.js +27 -0
- package/dist/lib/proxyConfig.js.map +1 -0
- package/dist/lib/publicUrl.d.ts +3 -0
- package/dist/lib/publicUrl.d.ts.map +1 -0
- package/dist/lib/publicUrl.js +17 -0
- package/dist/lib/publicUrl.js.map +1 -0
- package/dist/lib/uploads.d.ts +2 -0
- package/dist/lib/uploads.d.ts.map +1 -0
- package/dist/lib/uploads.js +14 -0
- package/dist/lib/uploads.js.map +1 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +13 -1
- package/dist/router.js.map +1 -1
- package/dist/server.js +44 -5
- package/dist/server.js.map +1 -1
- package/package.json +6 -5
- package/public/assets/AppCenter-CaY73Tvf.js +16 -0
- package/public/assets/AppCenter-CaY73Tvf.js.br +0 -0
- package/public/assets/Default-DQiKj0NT.js +1 -0
- package/public/assets/Default-DQiKj0NT.js.br +0 -0
- package/public/assets/Invite-B4MMcQtw.js +1 -0
- package/public/assets/Invite-B4MMcQtw.js.br +0 -0
- package/public/assets/MacResources-BfZAJ5BP.js +5 -0
- package/public/assets/MacResources-BfZAJ5BP.js.br +0 -0
- package/public/assets/NotFound-D85RKzwv.js +1 -0
- package/public/assets/NotFound-D85RKzwv.js.br +0 -0
- package/public/assets/QASession-B9FSUtZo.js +130 -0
- package/public/assets/QASession-B9FSUtZo.js.br +0 -0
- package/public/assets/ResetPassword-Degmb6sl.js +1 -0
- package/public/assets/ResetPassword-Degmb6sl.js.br +0 -0
- package/public/assets/Setup-DKOFxDo2.js +1 -0
- package/public/assets/Setup-DKOFxDo2.js.br +0 -0
- package/public/assets/Team-Bgspan2B.js +6 -0
- package/public/assets/Team-Bgspan2B.js.br +0 -0
- package/public/assets/Tokens-C3u48FUG.js +6 -0
- package/public/assets/Tokens-C3u48FUG.js.br +0 -0
- package/public/assets/alert-dialog-Ci_cWoVo.js +7 -0
- package/public/assets/alert-dialog-Ci_cWoVo.js.br +0 -0
- package/public/assets/badge-Cktrrdvf.js +1 -0
- package/public/assets/badge-Cktrrdvf.js.br +0 -0
- package/public/assets/build-format-Blzidb1D.js +11 -0
- package/public/assets/build-format-Blzidb1D.js.br +0 -0
- package/public/assets/dialog-BuZjQWBQ.js +11 -0
- package/public/assets/dialog-BuZjQWBQ.js.br +0 -0
- package/public/assets/index-BI7E3MD-.css +1 -0
- package/public/assets/index-BI7E3MD-.css.br +0 -0
- package/public/assets/index-ClAB0eKw.js +258 -0
- package/public/assets/index-ClAB0eKw.js.br +0 -0
- package/public/assets/inter-latin-ext-standard-normal-DpK-iCPk.woff2 +0 -0
- package/public/assets/inter-latin-standard-normal-BwkfbSeq.woff2 +0 -0
- package/public/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/public/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/public/assets/pencil-BoxokH2R.js +6 -0
- package/public/assets/pencil-BoxokH2R.js.br +0 -0
- package/public/assets/plus-C0Y4Yk9P.js +6 -0
- package/public/assets/plus-C0Y4Yk9P.js.br +0 -0
- package/public/assets/table-NBRnBazr.js +1 -0
- package/public/assets/table-NBRnBazr.js.br +0 -0
- package/public/assets/tabs-CB9a8dpY.js +1 -0
- package/public/assets/tabs-CB9a8dpY.js.br +0 -0
- package/public/assets/tinyh264.worker-DiJ50Hai.js.br +0 -0
- package/public/favicon.svg.br +0 -0
- package/public/index.html +2 -2
- package/public/index.html.br +0 -0
- package/public/logo-dark.svg.br +0 -0
- package/public/logo.svg.br +0 -0
- package/public/assets/index-Bhoumejm.js +0 -520
- package/public/assets/index-DX5Pf9T6.css +0 -1
- package/public/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
- package/public/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
- package/public/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
- package/public/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
- package/public/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
- package/public/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
- package/public/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
- package/public/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
- package/public/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
- package/public/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
- package/public/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
- package/public/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
- package/public/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
- package/public/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
- package/public/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
- package/public/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
- package/public/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
- package/public/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
- package/public/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
- package/public/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
- package/public/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
- package/public/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
- package/public/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
- package/public/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
- package/public/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/public/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- package/public/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/public/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/public/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
- package/public/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
- package/public/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
- package/public/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
- package/public/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
- package/public/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
- package/public/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
- package/public/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
- package/public/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
- package/public/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
- package/public/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
- package/public/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
- package/public/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
- package/public/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
- package/public/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/public/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/public/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/public/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/public/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/public/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/public/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/public/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/public/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
package/dist/api/team.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/api/team.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/api/team.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ export { SessionManager } from './SessionManager.js';
|
|
|
3
3
|
export { initDb, getDb, closeDb } from './db.js';
|
|
4
4
|
export { config } from './lib/config.js';
|
|
5
5
|
export type { TapflowConfig } from './lib/config.js';
|
|
6
|
+
export { buildCorsOrigins, proxyWithoutPublicUrlWarning } from './lib/proxyConfig.js';
|
|
7
|
+
export { loadDataDirEnv } from './lib/loadEnvFile.js';
|
|
8
|
+
export type { CertProvider, CertMaterial, CertStrategy } from './lib/cert/index.js';
|
|
9
|
+
export type { DnsProvider } from './lib/cert/index.js';
|
|
10
|
+
export { createCertProvider, startCertRenewal, startAddressPublisher, dnsProviders } from './lib/cert/index.js';
|
|
11
|
+
export type { DnsProviderEntry } from './lib/cert/index.js';
|
|
12
|
+
export type { TlsConfig } from './lib/cert/index.js';
|
|
6
13
|
export type { Session } from './SessionManager.js';
|
|
7
14
|
export type { RelayMessage, MessageType } from './types.js';
|
|
8
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAA;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACnF,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC/G,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACpD,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,7 @@ export { RelayServer } from './RelayServer.js';
|
|
|
2
2
|
export { SessionManager } from './SessionManager.js';
|
|
3
3
|
export { initDb, getDb, closeDb } from './db.js';
|
|
4
4
|
export { config } from './lib/config.js';
|
|
5
|
+
export { buildCorsOrigins, proxyWithoutPublicUrlWarning } from './lib/proxyConfig.js';
|
|
6
|
+
export { loadDataDirEnv } from './lib/loadEnvFile.js';
|
|
7
|
+
export { createCertProvider, startCertRenewal, startAddressPublisher, dnsProviders } from './lib/cert/index.js';
|
|
5
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAExC,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAA;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAGrD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { CertProvider, CertMaterial, CertStrategy } from './CertProvider.js';
|
|
2
|
+
import type { DnsProvider } from './DnsProvider.js';
|
|
3
|
+
/** 발급된 인증서 자료(만료 포함). */
|
|
4
|
+
export interface IssuedCert {
|
|
5
|
+
cert: string;
|
|
6
|
+
key: string;
|
|
7
|
+
expiresAt: Date;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* ACME 발급 추상화. 실제 Let's Encrypt 왕복은 AcmeClientIssuer가 구현하고,
|
|
11
|
+
* 단위 테스트는 fake가 구현한다. 키는 issuer 내부(로컬)에서 생성된다.
|
|
12
|
+
*/
|
|
13
|
+
export interface AcmeIssuer {
|
|
14
|
+
issue(opts: {
|
|
15
|
+
domain: string;
|
|
16
|
+
dns: DnsProvider;
|
|
17
|
+
}): Promise<IssuedCert>;
|
|
18
|
+
}
|
|
19
|
+
/** cert 영속화 추상화. 기본은 in-memory, 디스크 구현은 갱신 스케줄러 단계에서 주입. */
|
|
20
|
+
export interface CertStore {
|
|
21
|
+
load(): Promise<CertMaterial | null>;
|
|
22
|
+
save(material: CertMaterial): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare class InMemoryCertStore implements CertStore {
|
|
25
|
+
private material;
|
|
26
|
+
load(): Promise<CertMaterial | null>;
|
|
27
|
+
save(material: CertMaterial): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export interface AcmeCertProviderOptions {
|
|
30
|
+
domain: string;
|
|
31
|
+
dns: DnsProvider;
|
|
32
|
+
issuer: AcmeIssuer;
|
|
33
|
+
store?: CertStore;
|
|
34
|
+
/** 테스트용 시계. 기본 Date.now. */
|
|
35
|
+
now?: () => number;
|
|
36
|
+
renewThresholdDays?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* BYO API 토큰 경로의 CertProvider. 로컬에서 발급/갱신하며 키를 외부로 내보내지 않는다.
|
|
40
|
+
*/
|
|
41
|
+
export declare class AcmeCertProvider implements CertProvider {
|
|
42
|
+
readonly strategy: CertStrategy;
|
|
43
|
+
private readonly domain;
|
|
44
|
+
private readonly dns;
|
|
45
|
+
private readonly issuer;
|
|
46
|
+
private readonly store;
|
|
47
|
+
private readonly now;
|
|
48
|
+
private readonly thresholdMs;
|
|
49
|
+
private inflight;
|
|
50
|
+
constructor(opts: AcmeCertProviderOptions);
|
|
51
|
+
ensureCert(): Promise<CertMaterial>;
|
|
52
|
+
renewIfNeeded(): Promise<CertMaterial | null>;
|
|
53
|
+
private issueAndStore;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=AcmeCertProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AcmeCertProvider.d.ts","sourceRoot":"","sources":["../../../src/lib/cert/AcmeCertProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAKnD,yBAAyB;AACzB,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,IAAI,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CACvE;AAED,4DAA4D;AAC5D,MAAM,WAAW,SAAS;IACxB,IAAI,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IACpC,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5C;AAED,qBAAa,iBAAkB,YAAW,SAAS;IACjD,OAAO,CAAC,QAAQ,CAA4B;IACtC,IAAI,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAGpC,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAGlD;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,WAAW,CAAA;IAChB,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;GAEG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAkB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC,OAAO,CAAC,QAAQ,CAAqC;gBAEzC,IAAI,EAAE,uBAAuB;IASnC,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC;IAMnC,aAAa,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAMnD,OAAO,CAAC,aAAa;CActB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const DAY = 24 * 60 * 60 * 1000;
|
|
2
|
+
const RENEW_THRESHOLD_DAYS = 30;
|
|
3
|
+
export class InMemoryCertStore {
|
|
4
|
+
material = null;
|
|
5
|
+
async load() {
|
|
6
|
+
return this.material;
|
|
7
|
+
}
|
|
8
|
+
async save(material) {
|
|
9
|
+
this.material = material;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* BYO API 토큰 경로의 CertProvider. 로컬에서 발급/갱신하며 키를 외부로 내보내지 않는다.
|
|
14
|
+
*/
|
|
15
|
+
export class AcmeCertProvider {
|
|
16
|
+
strategy = 'byo-api-token';
|
|
17
|
+
domain;
|
|
18
|
+
dns;
|
|
19
|
+
issuer;
|
|
20
|
+
store;
|
|
21
|
+
now;
|
|
22
|
+
thresholdMs;
|
|
23
|
+
// 진행 중 발급을 공유해 동시 호출이 중복 ACME 주문(→ CA 한도)을 내지 않게 한다(single-flight).
|
|
24
|
+
inflight = null;
|
|
25
|
+
constructor(opts) {
|
|
26
|
+
this.domain = opts.domain;
|
|
27
|
+
this.dns = opts.dns;
|
|
28
|
+
this.issuer = opts.issuer;
|
|
29
|
+
this.store = opts.store ?? new InMemoryCertStore();
|
|
30
|
+
this.now = opts.now ?? Date.now;
|
|
31
|
+
this.thresholdMs = (opts.renewThresholdDays ?? RENEW_THRESHOLD_DAYS) * DAY;
|
|
32
|
+
}
|
|
33
|
+
async ensureCert() {
|
|
34
|
+
const existing = await this.store.load();
|
|
35
|
+
if (existing && existing.expiresAt.getTime() > this.now())
|
|
36
|
+
return existing;
|
|
37
|
+
return this.issueAndStore();
|
|
38
|
+
}
|
|
39
|
+
async renewIfNeeded() {
|
|
40
|
+
const cur = await this.ensureCert();
|
|
41
|
+
if (cur.expiresAt.getTime() - this.now() > this.thresholdMs)
|
|
42
|
+
return null;
|
|
43
|
+
return this.issueAndStore();
|
|
44
|
+
}
|
|
45
|
+
issueAndStore() {
|
|
46
|
+
if (this.inflight)
|
|
47
|
+
return this.inflight;
|
|
48
|
+
this.inflight = (async () => {
|
|
49
|
+
try {
|
|
50
|
+
const issued = await this.issuer.issue({ domain: this.domain, dns: this.dns });
|
|
51
|
+
const material = { cert: issued.cert, key: issued.key, expiresAt: issued.expiresAt };
|
|
52
|
+
await this.store.save(material);
|
|
53
|
+
return material;
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
this.inflight = null;
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
return this.inflight;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=AcmeCertProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AcmeCertProvider.js","sourceRoot":"","sources":["../../../src/lib/cert/AcmeCertProvider.ts"],"names":[],"mappings":"AAGA,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAC/B,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAuB/B,MAAM,OAAO,iBAAiB;IACpB,QAAQ,GAAwB,IAAI,CAAA;IAC5C,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,QAAsB;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC1B,CAAC;CACF;AAYD;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAClB,QAAQ,GAAiB,eAAe,CAAA;IAChC,MAAM,CAAQ;IACd,GAAG,CAAa;IAChB,MAAM,CAAY;IAClB,KAAK,CAAW;IAChB,GAAG,CAAc;IACjB,WAAW,CAAQ;IACpC,oEAAoE;IAC5D,QAAQ,GAAiC,IAAI,CAAA;IAErD,YAAY,IAA6B;QACvC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QACzB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAA;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,iBAAiB,EAAE,CAAA;QAClD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;QAC/B,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,kBAAkB,IAAI,oBAAoB,CAAC,GAAG,GAAG,CAAA;IAC5E,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;QACxC,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,QAAQ,CAAA;QAC1E,OAAO,IAAI,CAAC,aAAa,EAAE,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAA;QACnC,IAAI,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAA;QACxE,OAAO,IAAI,CAAC,aAAa,EAAE,CAAA;IAC7B,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAA;QACvC,IAAI,CAAC,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE;YAC1B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;gBAC9E,MAAM,QAAQ,GAAiB,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAA;gBAClG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBAC/B,OAAO,QAAQ,CAAA;YACjB,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;YACtB,CAAC;QACH,CAAC,CAAC,EAAE,CAAA;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DnsProvider } from './DnsProvider.js';
|
|
2
|
+
import type { AcmeIssuer, IssuedCert } from './AcmeCertProvider.js';
|
|
3
|
+
export interface AcmeClientIssuerOptions {
|
|
4
|
+
/** ACME 계정 이메일. 빈 값이면 생략. */
|
|
5
|
+
email: string;
|
|
6
|
+
/** true면 LE 스테이징(테스트), 기본은 프로덕션. */
|
|
7
|
+
staging?: boolean;
|
|
8
|
+
/** 영속화된 계정 키(PEM) 직접 주입. */
|
|
9
|
+
accountKey?: Buffer | string;
|
|
10
|
+
/** 계정 키를 캐시할 파일 경로 — 없으면 읽고, 없으면 생성·저장(매 발급마다 새 LE 계정 방지). */
|
|
11
|
+
accountKeyPath?: string;
|
|
12
|
+
/** TXT 설정 후 LE 검증 전 고정 전파 대기(ms). 기본 60초. */
|
|
13
|
+
dnsPropagationMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class AcmeClientIssuer implements AcmeIssuer {
|
|
16
|
+
private readonly opts;
|
|
17
|
+
constructor(opts: AcmeClientIssuerOptions);
|
|
18
|
+
issue({ domain, dns }: {
|
|
19
|
+
domain: string;
|
|
20
|
+
dns: DnsProvider;
|
|
21
|
+
}): Promise<IssuedCert>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=AcmeClientIssuer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AcmeClientIssuer.d.ts","sourceRoot":"","sources":["../../../src/lib/cert/AcmeClientIssuer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AASnE,MAAM,WAAW,uBAAuB;IACtC,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC5B,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AASD,qBAAa,gBAAiB,YAAW,UAAU;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,uBAAuB;IAEpD,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;CA0CxF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as acme from 'acme-client';
|
|
2
|
+
import { createLogger } from '@tapflowio/agent-core';
|
|
3
|
+
import { parseCertNotAfter } from './parseCert.js';
|
|
4
|
+
import { loadOrCreateAccountKey } from './accountKey.js';
|
|
5
|
+
const logger = createLogger('relay:acme');
|
|
6
|
+
// DNS-01 TXT 전파를 위한 고정 대기. 폴링/권위조회는 재귀 리졸버 음성캐시에 취약해 단순 고정 대기를 쓴다.
|
|
7
|
+
const DEFAULT_DNS_PROPAGATION_MS = 60_000;
|
|
8
|
+
function sleep(ms) {
|
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
}
|
|
11
|
+
export class AcmeClientIssuer {
|
|
12
|
+
opts;
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
this.opts = opts;
|
|
15
|
+
}
|
|
16
|
+
async issue({ domain, dns }) {
|
|
17
|
+
const accountKey = this.opts.accountKey ??
|
|
18
|
+
(this.opts.accountKeyPath
|
|
19
|
+
? await loadOrCreateAccountKey(this.opts.accountKeyPath, async () => (await acme.crypto.createPrivateKey()).toString())
|
|
20
|
+
: await acme.crypto.createPrivateKey());
|
|
21
|
+
const client = new acme.Client({
|
|
22
|
+
directoryUrl: this.opts.staging
|
|
23
|
+
? acme.directory.letsencrypt.staging
|
|
24
|
+
: acme.directory.letsencrypt.production,
|
|
25
|
+
accountKey,
|
|
26
|
+
});
|
|
27
|
+
const [key, csr] = await acme.crypto.createCsr({ commonName: domain });
|
|
28
|
+
const propagationMs = this.opts.dnsPropagationMs ?? DEFAULT_DNS_PROPAGATION_MS;
|
|
29
|
+
logger.info(`requesting ${this.opts.staging ? 'STAGING' : 'production'} cert for ${domain} via ${dns.name} (dns-01)`);
|
|
30
|
+
const cert = await client.auto({
|
|
31
|
+
csr,
|
|
32
|
+
email: this.opts.email || undefined,
|
|
33
|
+
termsOfServiceAgreed: true,
|
|
34
|
+
challengePriority: ['dns-01'],
|
|
35
|
+
// 우리가 고정 전파 대기를 하므로 acme-client 내부 DNS 재확인은 생략한다(재귀 음성캐시로 정체될 수 있음).
|
|
36
|
+
// 검증은 LE에 맡긴다(LE는 권위 NS를 직접 조회).
|
|
37
|
+
skipChallengeVerification: true,
|
|
38
|
+
challengeCreateFn: async (_authz, _challenge, keyAuthorization) => {
|
|
39
|
+
logger.info(`setting _acme-challenge.${domain} TXT`);
|
|
40
|
+
await dns.setTxtRecord(domain, keyAuthorization);
|
|
41
|
+
logger.info(`TXT set; waiting ${propagationMs}ms for propagation before LE validation`);
|
|
42
|
+
if (propagationMs > 0)
|
|
43
|
+
await sleep(propagationMs);
|
|
44
|
+
logger.info('handing challenge to ACME for validation');
|
|
45
|
+
},
|
|
46
|
+
challengeRemoveFn: async (_authz, _challenge, keyAuthorization) => {
|
|
47
|
+
await dns.removeTxtRecord(domain, keyAuthorization);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
logger.info(`cert issued for ${domain}`);
|
|
51
|
+
return { cert, key: key.toString(), expiresAt: parseCertNotAfter(cert) };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=AcmeClientIssuer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AcmeClientIssuer.js","sourceRoot":"","sources":["../../../src/lib/cert/AcmeClientIssuer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** relay가 https 종단에 쓰는 PEM 자료. key는 이 프로세스 밖으로 나가지 않는다. */
|
|
2
|
+
export interface CertMaterial {
|
|
3
|
+
/** PEM 인증서 체인 (leaf + intermediates). */
|
|
4
|
+
cert: string;
|
|
5
|
+
/** PEM 개인키. */
|
|
6
|
+
key: string;
|
|
7
|
+
/** 만료 시각 — 갱신 판단에 사용. */
|
|
8
|
+
expiresAt: Date;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* cert 발급 전략 — 도달성 모드별로 다른 경로를 추상화한다.
|
|
12
|
+
* v1: 'byo-api-token'(자기 DNS 계정 DNS-01, 주력) | 'import-cert'(기존 cert 로드).
|
|
13
|
+
* 후속: 'tailscale'(Tailscale 자체 cert) | 'free-subdomain' | 'byo-cname' | 'local-ca'.
|
|
14
|
+
*/
|
|
15
|
+
export type CertStrategy = 'byo-api-token' | 'import-cert';
|
|
16
|
+
/**
|
|
17
|
+
* 인증서 공급 계약. relay가 시작 시 ensureCert()로 자료를 확보하고,
|
|
18
|
+
* 스케줄러가 renewIfNeeded()를 주기 호출한다.
|
|
19
|
+
*/
|
|
20
|
+
export interface CertProvider {
|
|
21
|
+
readonly strategy: CertStrategy;
|
|
22
|
+
/** 유효한 cert 확보(없으면 발급/로드). 멱등 — 이미 유효하면 기존 자료를 반환. */
|
|
23
|
+
ensureCert(): Promise<CertMaterial>;
|
|
24
|
+
/**
|
|
25
|
+
* 만료 임박(잔여 ~30일 이하)일 때만 갱신하고 새 자료를 반환.
|
|
26
|
+
* 충분히 남았으면 갱신하지 않고 null(중복 발급 한도 방어 — 부팅마다 재발급 금지).
|
|
27
|
+
*/
|
|
28
|
+
renewIfNeeded(): Promise<CertMaterial | null>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=CertProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CertProvider.d.ts","sourceRoot":"","sources":["../../../src/lib/cert/CertProvider.ts"],"names":[],"mappings":"AAGA,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe;IACf,GAAG,EAAE,MAAM,CAAA;IACX,yBAAyB;IACzB,SAAS,EAAE,IAAI,CAAA;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,aAAa,CAAA;AAE1D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,sDAAsD;IACtD,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC,CAAA;IACnC;;;OAGG;IACH,aAAa,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;CAC9C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CertProvider.js","sourceRoot":"","sources":["../../../src/lib/cert/CertProvider.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,mDAAmD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { DnsProvider } from './DnsProvider.js';
|
|
2
|
+
/** 의존성 주입용 최소 fetch 계약 — global fetch 타입에 묶이지 않게 자체 정의. */
|
|
3
|
+
export type FetchLike = (url: string, init?: {
|
|
4
|
+
method?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
body?: string;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}) => Promise<{
|
|
9
|
+
ok: boolean;
|
|
10
|
+
status: number;
|
|
11
|
+
json(): Promise<unknown>;
|
|
12
|
+
}>;
|
|
13
|
+
export declare const DNS_API_TIMEOUT_MS = 30000;
|
|
14
|
+
export interface CloudflareDnsProviderOptions {
|
|
15
|
+
token: string;
|
|
16
|
+
/** 명시하면 zone 디스커버리를 생략하고 이 이름의 zone만 쓴다. */
|
|
17
|
+
zoneName?: string;
|
|
18
|
+
/** 테스트 주입용. 기본은 global fetch. */
|
|
19
|
+
fetchFn?: FetchLike;
|
|
20
|
+
/** 기본 https://api.cloudflare.com/client/v4 */
|
|
21
|
+
apiBase?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class CloudflareDnsProvider implements DnsProvider {
|
|
24
|
+
readonly name = "cloudflare";
|
|
25
|
+
private readonly token;
|
|
26
|
+
private readonly fetchFn;
|
|
27
|
+
private readonly apiBase;
|
|
28
|
+
private readonly fixedZoneName?;
|
|
29
|
+
private readonly zoneIdCache;
|
|
30
|
+
constructor(opts: CloudflareDnsProviderOptions);
|
|
31
|
+
setTxtRecord(fqdn: string, value: string): Promise<void>;
|
|
32
|
+
removeTxtRecord(fqdn: string, value: string): Promise<void>;
|
|
33
|
+
upsertAddressRecord(fqdn: string, ip: string): Promise<void>;
|
|
34
|
+
private zoneIdFor;
|
|
35
|
+
private findRecords;
|
|
36
|
+
private request;
|
|
37
|
+
}
|
|
38
|
+
/** env(TAPFLOW_CLOUDFLARE_TOKEN)에서 토큰을 읽어 provider를 만든다. 토큰 미설정 시 throw. */
|
|
39
|
+
export declare function cloudflareDnsFromEnv(zoneName?: string): CloudflareDnsProvider;
|
|
40
|
+
//# sourceMappingURL=CloudflareDnsProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudflareDnsProvider.d.ts","sourceRoot":"","sources":["../../../src/lib/cert/CloudflareDnsProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAQnD,2DAA2D;AAC3D,MAAM,MAAM,SAAS,GAAG,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAC9F,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAA;AAGvE,eAAO,MAAM,kBAAkB,QAAS,CAAA;AAExC,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAA;IACb,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAoBD,qBAAa,qBAAsB,YAAW,WAAW;IACvD,QAAQ,CAAC,IAAI,gBAAe;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;gBAE5C,IAAI,EAAE,4BAA4B;IAQxC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxD,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3D,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAYpD,SAAS;YAiBT,WAAW;YAKX,OAAO;CActB;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAI7E"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Cloudflare DNS-01 solver (issue #232). 사용자 자기 Cloudflare 계정 토큰으로 동작한다(BYO).
|
|
2
|
+
// 토큰은 config가 아니라 env(TAPFLOW_CLOUDFLARE_TOKEN)에서 읽는다 — cloudflareDnsFromEnv 참고.
|
|
3
|
+
const DEFAULT_API_BASE = 'https://api.cloudflare.com/client/v4';
|
|
4
|
+
const TTL_SECONDS = 60;
|
|
5
|
+
// DNS 공급자 API 호출이 무한 대기하지 않도록 하는 요청 타임아웃.
|
|
6
|
+
export const DNS_API_TIMEOUT_MS = 30_000;
|
|
7
|
+
export class CloudflareDnsProvider {
|
|
8
|
+
name = 'cloudflare';
|
|
9
|
+
token;
|
|
10
|
+
fetchFn;
|
|
11
|
+
apiBase;
|
|
12
|
+
fixedZoneName;
|
|
13
|
+
zoneIdCache = new Map();
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
if (!opts.token)
|
|
16
|
+
throw new Error('Cloudflare API token is required (set TAPFLOW_CLOUDFLARE_TOKEN)');
|
|
17
|
+
this.token = opts.token;
|
|
18
|
+
this.fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
19
|
+
this.apiBase = opts.apiBase ?? DEFAULT_API_BASE;
|
|
20
|
+
this.fixedZoneName = opts.zoneName;
|
|
21
|
+
}
|
|
22
|
+
async setTxtRecord(fqdn, value) {
|
|
23
|
+
const zoneId = await this.zoneIdFor(fqdn);
|
|
24
|
+
const name = `_acme-challenge.${fqdn}`;
|
|
25
|
+
const existing = await this.findRecords(zoneId, 'TXT', name);
|
|
26
|
+
if (existing.some((r) => r.content === value))
|
|
27
|
+
return;
|
|
28
|
+
await this.request(`/zones/${zoneId}/dns_records`, 'POST', { type: 'TXT', name, content: value, ttl: TTL_SECONDS });
|
|
29
|
+
}
|
|
30
|
+
async removeTxtRecord(fqdn, value) {
|
|
31
|
+
const zoneId = await this.zoneIdFor(fqdn);
|
|
32
|
+
const name = `_acme-challenge.${fqdn}`;
|
|
33
|
+
const matches = (await this.findRecords(zoneId, 'TXT', name)).filter((r) => r.content === value);
|
|
34
|
+
for (const r of matches) {
|
|
35
|
+
await this.request(`/zones/${zoneId}/dns_records/${r.id}`, 'DELETE');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async upsertAddressRecord(fqdn, ip) {
|
|
39
|
+
const zoneId = await this.zoneIdFor(fqdn);
|
|
40
|
+
const type = ip.includes(':') ? 'AAAA' : 'A';
|
|
41
|
+
const existing = await this.findRecords(zoneId, type, fqdn);
|
|
42
|
+
const payload = { type, name: fqdn, content: ip, ttl: TTL_SECONDS };
|
|
43
|
+
if (existing.length > 0) {
|
|
44
|
+
await this.request(`/zones/${zoneId}/dns_records/${existing[0].id}`, 'PATCH', payload);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
await this.request(`/zones/${zoneId}/dns_records`, 'POST', payload);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async zoneIdFor(fqdn) {
|
|
51
|
+
const cacheKey = this.fixedZoneName ?? fqdn;
|
|
52
|
+
const cached = this.zoneIdCache.get(cacheKey);
|
|
53
|
+
if (cached)
|
|
54
|
+
return cached;
|
|
55
|
+
const zones = await this.request('/zones', 'GET');
|
|
56
|
+
const candidates = this.fixedZoneName
|
|
57
|
+
? zones.filter((z) => z.name === this.fixedZoneName)
|
|
58
|
+
: zones.filter((z) => fqdn === z.name || fqdn.endsWith(`.${z.name}`));
|
|
59
|
+
// 가장 구체적인(최장 suffix) zone 우선
|
|
60
|
+
candidates.sort((a, b) => b.name.length - a.name.length);
|
|
61
|
+
const zone = candidates[0];
|
|
62
|
+
if (!zone)
|
|
63
|
+
throw new Error(`No Cloudflare zone found for ${fqdn}`);
|
|
64
|
+
this.zoneIdCache.set(cacheKey, zone.id);
|
|
65
|
+
return zone.id;
|
|
66
|
+
}
|
|
67
|
+
async findRecords(zoneId, type, name) {
|
|
68
|
+
const qs = new URLSearchParams({ type, name }).toString();
|
|
69
|
+
return this.request(`/zones/${zoneId}/dns_records?${qs}`, 'GET');
|
|
70
|
+
}
|
|
71
|
+
async request(path, method, body) {
|
|
72
|
+
const res = await this.fetchFn(`${this.apiBase}${path}`, {
|
|
73
|
+
method,
|
|
74
|
+
headers: { Authorization: `Bearer ${this.token}`, 'content-type': 'application/json' },
|
|
75
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
76
|
+
signal: AbortSignal.timeout(DNS_API_TIMEOUT_MS),
|
|
77
|
+
});
|
|
78
|
+
const data = (await res.json());
|
|
79
|
+
if (!res.ok || !data.success) {
|
|
80
|
+
const msg = data.errors?.map((e) => e.message).join('; ') || `HTTP ${res.status}`;
|
|
81
|
+
throw new Error(`Cloudflare API error: ${msg}`);
|
|
82
|
+
}
|
|
83
|
+
return data.result;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** env(TAPFLOW_CLOUDFLARE_TOKEN)에서 토큰을 읽어 provider를 만든다. 토큰 미설정 시 throw. */
|
|
87
|
+
export function cloudflareDnsFromEnv(zoneName) {
|
|
88
|
+
const token = process.env.TAPFLOW_CLOUDFLARE_TOKEN;
|
|
89
|
+
if (!token)
|
|
90
|
+
throw new Error('TAPFLOW_CLOUDFLARE_TOKEN is not set');
|
|
91
|
+
return new CloudflareDnsProvider({ token, zoneName });
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=CloudflareDnsProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudflareDnsProvider.js","sourceRoot":"","sources":["../../../src/lib/cert/CloudflareDnsProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CertStore } from './AcmeCertProvider.js';
|
|
2
|
+
import type { CertMaterial } from './CertProvider.js';
|
|
3
|
+
export declare class DiskCertStore implements CertStore {
|
|
4
|
+
private readonly certPath;
|
|
5
|
+
private readonly keyPath;
|
|
6
|
+
constructor(dir: string);
|
|
7
|
+
load(): Promise<CertMaterial | null>;
|
|
8
|
+
save(material: CertMaterial): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=DiskCertStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DiskCertStore.d.ts","sourceRoot":"","sources":["../../../src/lib/cert/DiskCertStore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,qBAAa,aAAc,YAAW,SAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;gBAEpB,GAAG,EAAE,MAAM;IAKjB,IAAI,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAUpC,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { parseCertNotAfter } from './parseCert.js';
|
|
4
|
+
// cert/key를 디스크에 영속화한다. 만료는 저장값이 아니라 cert에서 파싱(드리프트 방지).
|
|
5
|
+
// 키 파일은 0600(JWT secret과 동일 패턴) — 개인키는 소유자만 읽는다.
|
|
6
|
+
export class DiskCertStore {
|
|
7
|
+
certPath;
|
|
8
|
+
keyPath;
|
|
9
|
+
constructor(dir) {
|
|
10
|
+
this.certPath = path.join(dir, 'cert.pem');
|
|
11
|
+
this.keyPath = path.join(dir, 'key.pem');
|
|
12
|
+
}
|
|
13
|
+
async load() {
|
|
14
|
+
try {
|
|
15
|
+
const cert = fs.readFileSync(this.certPath, 'utf-8');
|
|
16
|
+
const key = fs.readFileSync(this.keyPath, 'utf-8');
|
|
17
|
+
return { cert, key, expiresAt: parseCertNotAfter(cert) };
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async save(material) {
|
|
24
|
+
fs.mkdirSync(path.dirname(this.certPath), { recursive: true });
|
|
25
|
+
fs.writeFileSync(this.certPath, material.cert, { mode: 0o644 });
|
|
26
|
+
fs.writeFileSync(this.keyPath, material.key, { mode: 0o600 });
|
|
27
|
+
try {
|
|
28
|
+
fs.chmodSync(this.keyPath, 0o600);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// best-effort on platforms without POSIX permissions
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=DiskCertStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DiskCertStore.js","sourceRoot":"","sources":["../../../src/lib/cert/DiskCertStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AAGvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,yDAAyD;AACzD,iDAAiD;AACjD,MAAM,OAAO,aAAa;IACP,QAAQ,CAAQ;IAChB,OAAO,CAAQ;IAEhC,YAAY,GAAW;QACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAA;QAC1C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;IAC1C,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YACpD,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAClD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAA;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAsB;QAC/B,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC9D,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC/D,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC7D,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface DnsProvider {
|
|
2
|
+
readonly name: string;
|
|
3
|
+
setTxtRecord(fqdn: string, value: string): Promise<void>;
|
|
4
|
+
removeTxtRecord(fqdn: string, value: string): Promise<void>;
|
|
5
|
+
upsertAddressRecord?(fqdn: string, ip: string): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=DnsProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DnsProvider.d.ts","sourceRoot":"","sources":["../../../src/lib/cert/DnsProvider.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxD,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3D,mBAAmB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DnsProvider.js","sourceRoot":"","sources":["../../../src/lib/cert/DnsProvider.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CertProvider, CertMaterial, CertStrategy } from './CertProvider.js';
|
|
2
|
+
export interface ImportCertProviderOptions {
|
|
3
|
+
certPath: string;
|
|
4
|
+
keyPath: string;
|
|
5
|
+
/** 테스트 주입용. 기본 fs.readFileSync(utf-8). */
|
|
6
|
+
readFileFn?: (filePath: string) => string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 사용자가 직접 마련한 cert/key 파일을 로드하는 CertProvider.
|
|
10
|
+
* ACME를 돌리지 않으므로 갱신은 사용자 책임 — renewIfNeeded는 항상 null.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ImportCertProvider implements CertProvider {
|
|
13
|
+
private readonly opts;
|
|
14
|
+
readonly strategy: CertStrategy;
|
|
15
|
+
private readonly readFile;
|
|
16
|
+
constructor(opts: ImportCertProviderOptions);
|
|
17
|
+
ensureCert(): Promise<CertMaterial>;
|
|
18
|
+
renewIfNeeded(): Promise<CertMaterial | null>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=ImportCertProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImportCertProvider.d.ts","sourceRoot":"","sources":["../../../src/lib/cert/ImportCertProvider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGjF,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAA;CAC1C;AAED;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,YAAY;IAIzC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;gBAE1B,IAAI,EAAE,yBAAyB;IAItD,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC;IAMnC,aAAa,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAGpD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { parseCertNotAfter } from './parseCert.js';
|
|
3
|
+
/**
|
|
4
|
+
* 사용자가 직접 마련한 cert/key 파일을 로드하는 CertProvider.
|
|
5
|
+
* ACME를 돌리지 않으므로 갱신은 사용자 책임 — renewIfNeeded는 항상 null.
|
|
6
|
+
*/
|
|
7
|
+
export class ImportCertProvider {
|
|
8
|
+
opts;
|
|
9
|
+
strategy = 'import-cert';
|
|
10
|
+
readFile;
|
|
11
|
+
constructor(opts) {
|
|
12
|
+
this.opts = opts;
|
|
13
|
+
this.readFile = opts.readFileFn ?? ((filePath) => fs.readFileSync(filePath, 'utf-8'));
|
|
14
|
+
}
|
|
15
|
+
async ensureCert() {
|
|
16
|
+
const cert = this.readFile(this.opts.certPath);
|
|
17
|
+
const key = this.readFile(this.opts.keyPath);
|
|
18
|
+
return { cert, key, expiresAt: parseCertNotAfter(cert) };
|
|
19
|
+
}
|
|
20
|
+
async renewIfNeeded() {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=ImportCertProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImportCertProvider.js","sourceRoot":"","sources":["../../../src/lib/cert/ImportCertProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AASlD;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IAIA;IAHpB,QAAQ,GAAiB,aAAa,CAAA;IAC9B,QAAQ,CAA8B;IAEvD,YAA6B,IAA+B;QAA/B,SAAI,GAAJ,IAAI,CAA2B;QAC1D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,QAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/F,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAA;IAC1D,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
|