vite-plugin-caddy-multiple-tls 0.0.1
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/README.md +28 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +460 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# vite-plugin-caddy-multiple-tls
|
|
2
|
+
|
|
3
|
+
Vite plugin that runs Caddy to proxy local development traffic over HTTPS with
|
|
4
|
+
derived domains like `<repo>.<branch>.localhost`.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install -D vite-plugin-caddy-multiple-tls
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { defineConfig } from 'vite';
|
|
16
|
+
import caddyTls from 'vite-plugin-caddy-multiple-tls';
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
plugins: [caddyTls()],
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Options
|
|
24
|
+
|
|
25
|
+
- `domain`: explicit domain to proxy without repo/branch derivation
|
|
26
|
+
- `baseDomain`: base domain to build `<repo>.<branch>.<baseDomain>` (defaults to `localhost`)
|
|
27
|
+
- `repo`, `branch`: override repo/branch names used for derived domains
|
|
28
|
+
- `internalTls`: use Caddy internal CA for provided domains
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PluginOption } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface ViteCaddyTlsPluginOptions {
|
|
4
|
+
/** Explicit domain to proxy without repo/branch derivation */
|
|
5
|
+
domain?: string;
|
|
6
|
+
/** Base domain to build <repo>.<branch>.<baseDomain> (defaults to localhost) */
|
|
7
|
+
baseDomain?: string;
|
|
8
|
+
/** Override repo name used in derived domains */
|
|
9
|
+
repo?: string;
|
|
10
|
+
/** Override branch name used in derived domains */
|
|
11
|
+
branch?: string;
|
|
12
|
+
cors?: string;
|
|
13
|
+
/** Override the default Caddy server name (srv0) */
|
|
14
|
+
serverName?: string;
|
|
15
|
+
/** Use Caddy's internal CA for the provided domains (defaults to true when baseDomain or domain is set) */
|
|
16
|
+
internalTls?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Vite plugin to run Caddy server to proxy traffic on https for local development
|
|
20
|
+
*
|
|
21
|
+
* @param {@link ViteCaddyTlsPluginOptions} config - the config to pass to the plugin
|
|
22
|
+
* @example
|
|
23
|
+
* ```
|
|
24
|
+
* caddyTls({
|
|
25
|
+
* domain: "app.localhost",
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
* @returns {Plugin} - a Vite plugin
|
|
29
|
+
*/
|
|
30
|
+
declare function viteCaddyTlsPlugin({ domain, baseDomain, repo, branch, cors, serverName, internalTls, }?: ViteCaddyTlsPluginOptions): PluginOption;
|
|
31
|
+
|
|
32
|
+
export { type ViteCaddyTlsPluginOptions, viteCaddyTlsPlugin as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { execSync as execSync2 } from "child_process";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
// src/utils.ts
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
var CADDY_API = "http://localhost:2019";
|
|
9
|
+
var DEFAULT_SERVER_NAME = "srv0";
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function parseConfig(text) {
|
|
14
|
+
if (!text.trim()) return {};
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(text);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function isTlsPolicyOverlapError(text) {
|
|
22
|
+
return text.includes("cannot apply more than one automation policy to host");
|
|
23
|
+
}
|
|
24
|
+
function validateCaddyIsInstalled() {
|
|
25
|
+
try {
|
|
26
|
+
execSync("caddy version");
|
|
27
|
+
return true;
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.error("caddy cli is not installed");
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function isCaddyRunning() {
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch(`${CADDY_API}/config/`);
|
|
36
|
+
return res.ok;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function startCaddy() {
|
|
42
|
+
try {
|
|
43
|
+
execSync("caddy start", { stdio: "ignore" });
|
|
44
|
+
for (let i = 0; i < 10; i++) {
|
|
45
|
+
if (await isCaddyRunning()) return true;
|
|
46
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error("Failed to start Caddy:", e);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function ensureBaseConfig(serverName = DEFAULT_SERVER_NAME) {
|
|
55
|
+
const serverUrl = `${CADDY_API}/config/apps/http/servers/${serverName}`;
|
|
56
|
+
const res = await fetch(serverUrl);
|
|
57
|
+
if (res.ok) return;
|
|
58
|
+
const baseConfig = {
|
|
59
|
+
listen: [":443"],
|
|
60
|
+
routes: []
|
|
61
|
+
};
|
|
62
|
+
const httpAppConfig = {
|
|
63
|
+
servers: {
|
|
64
|
+
[serverName]: baseConfig
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const configRes = await fetch(`${CADDY_API}/config/`);
|
|
68
|
+
if (!configRes.ok) {
|
|
69
|
+
const text = await configRes.text();
|
|
70
|
+
throw new Error(`Failed to read Caddy config: ${text}`);
|
|
71
|
+
}
|
|
72
|
+
const configText = await configRes.text();
|
|
73
|
+
const config = parseConfig(configText);
|
|
74
|
+
if (config === void 0) {
|
|
75
|
+
throw new Error("Failed to parse Caddy config response.");
|
|
76
|
+
}
|
|
77
|
+
const isEmptyConfig = configText.trim() === "" || config === null || isRecord(config) && Object.keys(config).length === 0;
|
|
78
|
+
if (isEmptyConfig) {
|
|
79
|
+
const loadRes = await fetch(`${CADDY_API}/load`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "Content-Type": "application/json" },
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
apps: {
|
|
84
|
+
http: httpAppConfig
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
});
|
|
88
|
+
if (!loadRes.ok) {
|
|
89
|
+
const text = await loadRes.text();
|
|
90
|
+
throw new Error(`Failed to initialize Caddy base configuration: ${text}`);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const apps = isRecord(config) ? config.apps : void 0;
|
|
95
|
+
const http = isRecord(apps) ? apps.http : void 0;
|
|
96
|
+
const servers = isRecord(http) ? http.servers : void 0;
|
|
97
|
+
let hasApps = isRecord(apps);
|
|
98
|
+
let hasHttp = isRecord(http);
|
|
99
|
+
let hasServers = isRecord(servers);
|
|
100
|
+
if (!hasApps) {
|
|
101
|
+
const createAppsRes = await fetch(`${CADDY_API}/config/apps`, {
|
|
102
|
+
method: "PUT",
|
|
103
|
+
headers: { "Content-Type": "application/json" },
|
|
104
|
+
body: JSON.stringify({})
|
|
105
|
+
});
|
|
106
|
+
if (!createAppsRes.ok && createAppsRes.status !== 409) {
|
|
107
|
+
const text = await createAppsRes.text();
|
|
108
|
+
throw new Error(`Failed to initialize Caddy base configuration: ${text}`);
|
|
109
|
+
}
|
|
110
|
+
hasApps = true;
|
|
111
|
+
}
|
|
112
|
+
if (!hasHttp) {
|
|
113
|
+
const createHttpRes = await fetch(`${CADDY_API}/config/apps/http`, {
|
|
114
|
+
method: "PUT",
|
|
115
|
+
headers: { "Content-Type": "application/json" },
|
|
116
|
+
body: JSON.stringify({ servers: {} })
|
|
117
|
+
});
|
|
118
|
+
if (!createHttpRes.ok && createHttpRes.status !== 409) {
|
|
119
|
+
const text = await createHttpRes.text();
|
|
120
|
+
throw new Error(`Failed to initialize Caddy base configuration: ${text}`);
|
|
121
|
+
}
|
|
122
|
+
hasHttp = true;
|
|
123
|
+
hasServers = true;
|
|
124
|
+
}
|
|
125
|
+
if (!hasServers) {
|
|
126
|
+
const createServersRes = await fetch(`${CADDY_API}/config/apps/http/servers`, {
|
|
127
|
+
method: "PUT",
|
|
128
|
+
headers: { "Content-Type": "application/json" },
|
|
129
|
+
body: JSON.stringify({})
|
|
130
|
+
});
|
|
131
|
+
if (!createServersRes.ok && createServersRes.status !== 409) {
|
|
132
|
+
const text = await createServersRes.text();
|
|
133
|
+
throw new Error(`Failed to initialize Caddy base configuration: ${text}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const createServerRes = await fetch(serverUrl, {
|
|
137
|
+
method: "PUT",
|
|
138
|
+
headers: { "Content-Type": "application/json" },
|
|
139
|
+
body: JSON.stringify(baseConfig)
|
|
140
|
+
});
|
|
141
|
+
if (!createServerRes.ok && createServerRes.status !== 409) {
|
|
142
|
+
const text = await createServerRes.text();
|
|
143
|
+
throw new Error(`Failed to initialize Caddy base configuration: ${text}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function ensureTlsAutomation() {
|
|
147
|
+
const policiesUrl = `${CADDY_API}/config/apps/tls/automation/policies`;
|
|
148
|
+
const policiesRes = await fetch(policiesUrl);
|
|
149
|
+
if (policiesRes.ok) return;
|
|
150
|
+
const policiesText = await policiesRes.text();
|
|
151
|
+
if (policiesRes.status !== 404 && !policiesText.includes("invalid traversal path")) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Failed to initialize Caddy TLS automation: ${policiesText}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
const automationRes = await fetch(`${CADDY_API}/config/apps/tls/automation`, {
|
|
157
|
+
method: "PUT",
|
|
158
|
+
headers: { "Content-Type": "application/json" },
|
|
159
|
+
body: JSON.stringify({ policies: [] })
|
|
160
|
+
});
|
|
161
|
+
if (automationRes.ok || automationRes.status === 409) return;
|
|
162
|
+
const automationText = await automationRes.text();
|
|
163
|
+
if (!automationText.includes("invalid traversal path")) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Failed to initialize Caddy TLS automation: ${automationText}`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
const tlsRes = await fetch(`${CADDY_API}/config/apps/tls`, {
|
|
169
|
+
method: "PUT",
|
|
170
|
+
headers: { "Content-Type": "application/json" },
|
|
171
|
+
body: JSON.stringify({ automation: { policies: [] } })
|
|
172
|
+
});
|
|
173
|
+
if (!tlsRes.ok && tlsRes.status !== 409) {
|
|
174
|
+
const text = await tlsRes.text();
|
|
175
|
+
throw new Error(`Failed to initialize Caddy TLS automation: ${text}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function addRoute(id, domains, port, cors, serverName = DEFAULT_SERVER_NAME) {
|
|
179
|
+
const handlers = [];
|
|
180
|
+
if (cors) {
|
|
181
|
+
handlers.push({
|
|
182
|
+
handler: "headers",
|
|
183
|
+
response: {
|
|
184
|
+
set: {
|
|
185
|
+
"Access-Control-Allow-Origin": [cors],
|
|
186
|
+
"Access-Control-Allow-Methods": [
|
|
187
|
+
"GET",
|
|
188
|
+
"POST",
|
|
189
|
+
"PUT",
|
|
190
|
+
"PATCH",
|
|
191
|
+
"DELETE",
|
|
192
|
+
"OPTIONS"
|
|
193
|
+
],
|
|
194
|
+
"Access-Control-Allow-Headers": ["*"]
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
handlers.push({
|
|
200
|
+
handler: "reverse_proxy",
|
|
201
|
+
upstreams: [{ dial: `localhost:${port}` }]
|
|
202
|
+
});
|
|
203
|
+
const route = {
|
|
204
|
+
"@id": id,
|
|
205
|
+
match: [{ host: domains }],
|
|
206
|
+
handle: [
|
|
207
|
+
{
|
|
208
|
+
handler: "subroute",
|
|
209
|
+
routes: [
|
|
210
|
+
{
|
|
211
|
+
handle: handlers
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
terminal: true
|
|
217
|
+
};
|
|
218
|
+
const res = await fetch(
|
|
219
|
+
`${CADDY_API}/config/apps/http/servers/${serverName}/routes`,
|
|
220
|
+
{
|
|
221
|
+
method: "POST",
|
|
222
|
+
// Append to routes list
|
|
223
|
+
headers: { "Content-Type": "application/json" },
|
|
224
|
+
body: JSON.stringify(route)
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
if (!res.ok) {
|
|
228
|
+
const text = await res.text();
|
|
229
|
+
throw new Error(`Failed to add route: ${text}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function addTlsPolicy(id, domains) {
|
|
233
|
+
await ensureTlsAutomation();
|
|
234
|
+
const policy = {
|
|
235
|
+
"@id": id,
|
|
236
|
+
subjects: domains,
|
|
237
|
+
issuers: [
|
|
238
|
+
{
|
|
239
|
+
module: "internal"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
};
|
|
243
|
+
const res = await fetch(`${CADDY_API}/config/apps/tls/automation/policies`, {
|
|
244
|
+
method: "POST",
|
|
245
|
+
headers: { "Content-Type": "application/json" },
|
|
246
|
+
body: JSON.stringify(policy)
|
|
247
|
+
});
|
|
248
|
+
if (!res.ok) {
|
|
249
|
+
const text = await res.text();
|
|
250
|
+
if (isTlsPolicyOverlapError(text)) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
throw new Error(`Failed to add TLS policy: ${text}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function removeRoute(id) {
|
|
257
|
+
const res = await fetch(`${CADDY_API}/id/${id}`, {
|
|
258
|
+
method: "DELETE"
|
|
259
|
+
});
|
|
260
|
+
if (!res.ok && res.status !== 404) {
|
|
261
|
+
console.error(`Failed to remove route ${id}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async function removeTlsPolicy(id) {
|
|
265
|
+
const res = await fetch(`${CADDY_API}/id/${id}`, {
|
|
266
|
+
method: "DELETE"
|
|
267
|
+
});
|
|
268
|
+
if (!res.ok && res.status !== 404) {
|
|
269
|
+
console.error(`Failed to remove TLS policy ${id}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/index.ts
|
|
274
|
+
function execGit(command) {
|
|
275
|
+
return execSync2(command, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
276
|
+
}
|
|
277
|
+
function getGitRepoInfo() {
|
|
278
|
+
const info = {};
|
|
279
|
+
try {
|
|
280
|
+
const repoRoot = execGit("git rev-parse --show-toplevel");
|
|
281
|
+
if (repoRoot) {
|
|
282
|
+
info.repo = path.basename(repoRoot);
|
|
283
|
+
}
|
|
284
|
+
} catch (e) {
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
let branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
288
|
+
if (branch === "HEAD") {
|
|
289
|
+
branch = execGit("git rev-parse --short HEAD");
|
|
290
|
+
}
|
|
291
|
+
if (branch) {
|
|
292
|
+
info.branch = branch;
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
}
|
|
296
|
+
return info;
|
|
297
|
+
}
|
|
298
|
+
function normalizeBaseDomain(baseDomain) {
|
|
299
|
+
return baseDomain.trim().replace(/^\.+|\.+$/g, "").toLowerCase();
|
|
300
|
+
}
|
|
301
|
+
function sanitizeDomainLabel(value) {
|
|
302
|
+
return value.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
303
|
+
}
|
|
304
|
+
function buildDerivedDomain(options) {
|
|
305
|
+
const baseDomain = normalizeBaseDomain(options.baseDomain ?? "localhost");
|
|
306
|
+
if (!baseDomain) return null;
|
|
307
|
+
let repo = options.repo;
|
|
308
|
+
let branch = options.branch;
|
|
309
|
+
if (!repo || !branch) {
|
|
310
|
+
const info = getGitRepoInfo();
|
|
311
|
+
if (!repo) repo = info.repo;
|
|
312
|
+
if (!branch) branch = info.branch;
|
|
313
|
+
}
|
|
314
|
+
if (!repo || !branch) return null;
|
|
315
|
+
const repoLabel = sanitizeDomainLabel(repo);
|
|
316
|
+
const branchLabel = sanitizeDomainLabel(branch);
|
|
317
|
+
if (!repoLabel || !branchLabel) return null;
|
|
318
|
+
return `${repoLabel}.${branchLabel}.${baseDomain}`;
|
|
319
|
+
}
|
|
320
|
+
function normalizeDomain(domain) {
|
|
321
|
+
const trimmed = domain.trim().toLowerCase();
|
|
322
|
+
if (!trimmed) return null;
|
|
323
|
+
return trimmed;
|
|
324
|
+
}
|
|
325
|
+
function resolveDomain(options) {
|
|
326
|
+
if (options.domain) {
|
|
327
|
+
return normalizeDomain(options.domain);
|
|
328
|
+
}
|
|
329
|
+
return buildDerivedDomain(options);
|
|
330
|
+
}
|
|
331
|
+
function viteCaddyTlsPlugin({
|
|
332
|
+
domain,
|
|
333
|
+
baseDomain,
|
|
334
|
+
repo,
|
|
335
|
+
branch,
|
|
336
|
+
cors,
|
|
337
|
+
serverName,
|
|
338
|
+
internalTls
|
|
339
|
+
} = {}) {
|
|
340
|
+
return {
|
|
341
|
+
name: "vite:caddy-tls",
|
|
342
|
+
configureServer({ httpServer, config }) {
|
|
343
|
+
const fallbackPort = config.server.port || 5173;
|
|
344
|
+
const resolvedDomain = resolveDomain({ domain, baseDomain, repo, branch });
|
|
345
|
+
const domainArray = resolvedDomain ? [resolvedDomain] : [];
|
|
346
|
+
const routeId = `vite-proxy-${Date.now()}-${Math.floor(Math.random() * 1e3)}`;
|
|
347
|
+
const shouldUseInternalTls = internalTls ?? (baseDomain !== void 0 || domain !== void 0);
|
|
348
|
+
const tlsPolicyId = shouldUseInternalTls ? `${routeId}-tls` : null;
|
|
349
|
+
let cleanupStarted = false;
|
|
350
|
+
if (domainArray.length === 0) {
|
|
351
|
+
console.error(
|
|
352
|
+
chalk.red(
|
|
353
|
+
"No domain resolved. Provide domain, or run inside a git repo, or pass repo/branch."
|
|
354
|
+
)
|
|
355
|
+
);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
let tlsPolicyAdded = false;
|
|
359
|
+
function getServerPort() {
|
|
360
|
+
if (!httpServer) return fallbackPort;
|
|
361
|
+
const address = httpServer.address();
|
|
362
|
+
if (address && typeof address === "object" && "port" in address) {
|
|
363
|
+
return address.port;
|
|
364
|
+
}
|
|
365
|
+
return fallbackPort;
|
|
366
|
+
}
|
|
367
|
+
async function cleanupRoute() {
|
|
368
|
+
if (cleanupStarted) return;
|
|
369
|
+
cleanupStarted = true;
|
|
370
|
+
if (tlsPolicyId) {
|
|
371
|
+
await removeTlsPolicy(tlsPolicyId);
|
|
372
|
+
}
|
|
373
|
+
await removeRoute(routeId);
|
|
374
|
+
}
|
|
375
|
+
function onServerClose() {
|
|
376
|
+
void cleanupRoute();
|
|
377
|
+
}
|
|
378
|
+
function handleSignal(signal) {
|
|
379
|
+
process.off("SIGINT", onSigint);
|
|
380
|
+
process.off("SIGTERM", onSigterm);
|
|
381
|
+
void cleanupRoute().finally(() => {
|
|
382
|
+
process.kill(process.pid, signal);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
function onSigint() {
|
|
386
|
+
handleSignal("SIGINT");
|
|
387
|
+
}
|
|
388
|
+
function onSigterm() {
|
|
389
|
+
handleSignal("SIGTERM");
|
|
390
|
+
}
|
|
391
|
+
function registerProcessCleanup() {
|
|
392
|
+
process.once("SIGINT", onSigint);
|
|
393
|
+
process.once("SIGTERM", onSigterm);
|
|
394
|
+
}
|
|
395
|
+
async function setupRoute() {
|
|
396
|
+
if (!validateCaddyIsInstalled()) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
let running = await isCaddyRunning();
|
|
400
|
+
if (!running) {
|
|
401
|
+
running = await startCaddy();
|
|
402
|
+
if (!running) {
|
|
403
|
+
console.error(chalk.red("Failed to start Caddy server."));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
await ensureBaseConfig(serverName);
|
|
409
|
+
} catch (e) {
|
|
410
|
+
console.error(chalk.red("Failed to configure Caddy base settings."), e);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const port = getServerPort();
|
|
414
|
+
if (tlsPolicyId) {
|
|
415
|
+
try {
|
|
416
|
+
await addTlsPolicy(tlsPolicyId, domainArray);
|
|
417
|
+
tlsPolicyAdded = true;
|
|
418
|
+
} catch (e) {
|
|
419
|
+
console.error(chalk.red("Failed to add TLS policy to Caddy."), e);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
await addRoute(routeId, domainArray, port, cors, serverName);
|
|
425
|
+
} catch (e) {
|
|
426
|
+
if (tlsPolicyAdded && tlsPolicyId) {
|
|
427
|
+
await removeTlsPolicy(tlsPolicyId);
|
|
428
|
+
}
|
|
429
|
+
console.error(chalk.red("Failed to add route to Caddy."), e);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
console.log();
|
|
433
|
+
console.log(chalk.green("\u{1F512} Caddy is proxying your traffic on https"));
|
|
434
|
+
console.log();
|
|
435
|
+
console.log(
|
|
436
|
+
`\u{1F517} Access your local ${domainArray.length > 1 ? "servers" : "server"} `
|
|
437
|
+
);
|
|
438
|
+
domainArray.forEach((domain2) => {
|
|
439
|
+
console.log(chalk.blue(`\u{1F30D} https://${domain2}`));
|
|
440
|
+
});
|
|
441
|
+
console.log();
|
|
442
|
+
registerProcessCleanup();
|
|
443
|
+
httpServer?.once("close", onServerClose);
|
|
444
|
+
}
|
|
445
|
+
function onListening() {
|
|
446
|
+
void setupRoute();
|
|
447
|
+
}
|
|
448
|
+
if (httpServer?.listening) {
|
|
449
|
+
void setupRoute();
|
|
450
|
+
} else if (httpServer) {
|
|
451
|
+
httpServer.once("listening", onListening);
|
|
452
|
+
} else {
|
|
453
|
+
void setupRoute();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
export {
|
|
459
|
+
viteCaddyTlsPlugin as default
|
|
460
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-caddy-multiple-tls",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Vite plugin that uses Caddy to provide local HTTPS with derived domains.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"vite",
|
|
7
|
+
"plugin",
|
|
8
|
+
"caddy",
|
|
9
|
+
"https",
|
|
10
|
+
"tls",
|
|
11
|
+
"local-development"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "vampaz",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"module": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "tsup --watch src/**/* src/index.ts --format esm --dts-resolve",
|
|
32
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
33
|
+
"prepublishOnly": "npm run build",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:watch": "vitest"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22.0.0"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/vampaz/vite-plugin-caddy-multiple-tls.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/vampaz/vite-plugin-caddy-multiple-tls/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/vampaz/vite-plugin-caddy-multiple-tls/#readme",
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/fs-extra": "^11.0.4",
|
|
53
|
+
"@types/node": "^25.0.3",
|
|
54
|
+
"fs-extra": "^11.3.3",
|
|
55
|
+
"tsup": "^8.5.1",
|
|
56
|
+
"vite": "^7.3.0",
|
|
57
|
+
"vitest": "^4.0.16"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"chalk": "^5.6.2"
|
|
61
|
+
}
|
|
62
|
+
}
|