vercel-vm-factory 0.1.0 → 0.1.2
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 +36 -1
- package/deploy-vm.mjs +90 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,16 @@ Create a tiny Vercel Container deployment: copy `wsterm` from `ghcr.io/v1xingyue
|
|
|
6
6
|
npx vercel-vm-factory create \
|
|
7
7
|
--base ubuntu \
|
|
8
8
|
--project ws-shell-ubuntu \
|
|
9
|
+
--auth-mode basic \
|
|
10
|
+
--auth-user admin \
|
|
11
|
+
--auth-password change-me
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
GitHub OAuth is optional:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx vercel-vm-factory create \
|
|
18
|
+
--auth-mode github \
|
|
9
19
|
--client-id YOUR_GITHUB_CLIENT_ID \
|
|
10
20
|
--client-secret YOUR_GITHUB_CLIENT_SECRET \
|
|
11
21
|
--github-userid 12345678
|
|
@@ -17,6 +27,8 @@ Run without flags for prompts:
|
|
|
17
27
|
npx vercel-vm-factory create
|
|
18
28
|
```
|
|
19
29
|
|
|
30
|
+
The prompt asks for authentication first: `basic`, `github`, `both`, or `none`, then only asks for the fields that mode needs.
|
|
31
|
+
|
|
20
32
|
Check local setup:
|
|
21
33
|
|
|
22
34
|
```bash
|
|
@@ -27,7 +39,7 @@ The script checks `vercel --version` and `vercel whoami`; if you are not logged
|
|
|
27
39
|
|
|
28
40
|
Use `--help` to show all flags.
|
|
29
41
|
|
|
30
|
-
Entered
|
|
42
|
+
Entered auth values are reused from `~/.vercel-vm-factory/defaults.json`; press Enter to keep the placeholder value or skip an empty one.
|
|
31
43
|
|
|
32
44
|
The generated project contains only `Dockerfile.vercel`.
|
|
33
45
|
|
|
@@ -56,4 +68,27 @@ Before deploying, set the GitHub OAuth callback URL to:
|
|
|
56
68
|
https://PROJECT.vercel.app/auth/github/callback
|
|
57
69
|
```
|
|
58
70
|
|
|
71
|
+
For example, if `--project x-shell`, set:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
https://x-shell.vercel.app/auth/github/callback
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
GitHub OAuth fields:
|
|
78
|
+
|
|
79
|
+
- Auth mode -> `--auth-mode basic|github|both|none`
|
|
80
|
+
- Username -> `--auth-user`
|
|
81
|
+
- Password -> `--auth-password`
|
|
82
|
+
- Client ID -> `--client-id`
|
|
83
|
+
- Client Secret -> `--client-secret`
|
|
84
|
+
- Numeric GitHub user ID -> `--github-userid`
|
|
85
|
+
|
|
86
|
+
Get your numeric user ID:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
https://api.github.com/users/YOUR_LOGIN
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The callback URL must match exactly. If you deploy under another project name or custom domain, update the GitHub OAuth App or pass `--redirect-url`.
|
|
93
|
+
|
|
59
94
|
Use `--dry-run` to generate files without deploying.
|
package/deploy-vm.mjs
CHANGED
|
@@ -64,6 +64,7 @@ async function main() {
|
|
|
64
64
|
const prod = args.prod !== "false";
|
|
65
65
|
const dryRun = Boolean(args["dry-run"]);
|
|
66
66
|
const skipLink = Boolean(args["skip-link"]);
|
|
67
|
+
const authMode = await chooseAuthMode(defaultAuthMode());
|
|
67
68
|
|
|
68
69
|
const oauthRedirectUrl =
|
|
69
70
|
args["redirect-url"] ??
|
|
@@ -83,7 +84,8 @@ async function main() {
|
|
|
83
84
|
project,
|
|
84
85
|
scope: scope || "default",
|
|
85
86
|
source: wsShellImage,
|
|
86
|
-
|
|
87
|
+
auth: authMode,
|
|
88
|
+
...(usesGitHubAuth(authMode) ? { callback: oauthRedirectUrl } : {}),
|
|
87
89
|
dockerfile: path.join(appDir, "Dockerfile.vercel"),
|
|
88
90
|
target: prod ? "production" : "preview",
|
|
89
91
|
});
|
|
@@ -94,14 +96,23 @@ async function main() {
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
await ensureVercelReady();
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
if (usesGitHubAuth(authMode)) printOAuthGuide(oauthRedirectUrl);
|
|
100
|
+
|
|
101
|
+
const authUsername = usesBasicAuth(authMode)
|
|
102
|
+
? await secret("auth-user", "Basic auth username", process.env.AUTH_USERNAME ?? defaults["auth-user"])
|
|
103
|
+
: "";
|
|
104
|
+
const authPassword = usesBasicAuth(authMode)
|
|
105
|
+
? await secret("auth-password", "Basic auth password", process.env.AUTH_PASSWORD ?? defaults["auth-password"])
|
|
106
|
+
: "";
|
|
107
|
+
const githubClientId = usesGitHubAuth(authMode)
|
|
108
|
+
? await secret("client-id", "GitHub client id", process.env.GITHUB_CLIENT_ID ?? defaults["client-id"])
|
|
109
|
+
: "";
|
|
110
|
+
const githubClientSecret = usesGitHubAuth(authMode)
|
|
111
|
+
? await secret("client-secret", "GitHub client secret", process.env.GITHUB_CLIENT_SECRET ?? defaults["client-secret"])
|
|
112
|
+
: "";
|
|
113
|
+
const allowedUserIds = usesGitHubAuth(authMode)
|
|
114
|
+
? await secret("github-userid", "Allowed GitHub numeric user id(s)", process.env.ALLOWED_USER_IDS ?? defaults["github-userid"])
|
|
115
|
+
: "";
|
|
105
116
|
|
|
106
117
|
await writeDefaults(defaultsPath, {
|
|
107
118
|
...defaults,
|
|
@@ -109,6 +120,9 @@ async function main() {
|
|
|
109
120
|
scope: scope || undefined,
|
|
110
121
|
project,
|
|
111
122
|
from: wsShellImage,
|
|
123
|
+
"auth-mode": authMode,
|
|
124
|
+
"auth-user": authUsername,
|
|
125
|
+
"auth-password": authPassword,
|
|
112
126
|
"client-id": githubClientId,
|
|
113
127
|
"client-secret": githubClientSecret,
|
|
114
128
|
"github-userid": allowedUserIds,
|
|
@@ -133,10 +147,11 @@ async function main() {
|
|
|
133
147
|
appDir,
|
|
134
148
|
"--yes",
|
|
135
149
|
"--logs",
|
|
136
|
-
"--env",
|
|
137
|
-
`OAUTH_REDIRECT_URL=${oauthRedirectUrl}`,
|
|
138
150
|
];
|
|
139
151
|
|
|
152
|
+
if (authUsername) vercelArgs.push("--env", `AUTH_USERNAME=${authUsername}`);
|
|
153
|
+
if (authPassword) vercelArgs.push("--env", `AUTH_PASSWORD=${authPassword}`);
|
|
154
|
+
if (usesGitHubAuth(authMode)) vercelArgs.push("--env", `OAUTH_REDIRECT_URL=${oauthRedirectUrl}`);
|
|
140
155
|
if (githubClientId) vercelArgs.push("--env", `GITHUB_CLIENT_ID=${githubClientId}`);
|
|
141
156
|
if (githubClientSecret) vercelArgs.push("--env", `GITHUB_CLIENT_SECRET=${githubClientSecret}`);
|
|
142
157
|
if (allowedUserIds) vercelArgs.push("--env", `ALLOWED_USER_IDS=${allowedUserIds}`);
|
|
@@ -192,6 +207,9 @@ async function doctor() {
|
|
|
192
207
|
printKeyValue("project", defaults.project || "not set");
|
|
193
208
|
printKeyValue("scope", defaults.scope || "not set");
|
|
194
209
|
printKeyValue("source image", defaults.from || "ghcr.io/v1xingyue/ws-shell:v1.1.alpine");
|
|
210
|
+
printKeyValue("auth mode", defaults["auth-mode"] || "not set");
|
|
211
|
+
printKeyValue("auth user", defaults["auth-user"] ? mask(defaults["auth-user"]) : "not set");
|
|
212
|
+
printKeyValue("auth password", defaults["auth-password"] ? mask(defaults["auth-password"]) : "not set");
|
|
195
213
|
printKeyValue("client id", defaults["client-id"] ? mask(defaults["client-id"]) : "not set");
|
|
196
214
|
printKeyValue("client secret", defaults["client-secret"] ? mask(defaults["client-secret"]) : "not set");
|
|
197
215
|
printKeyValue("github userid", defaults["github-userid"] ? mask(defaults["github-userid"]) : "not set");
|
|
@@ -247,6 +265,51 @@ async function optionalValue(name, question, fallback) {
|
|
|
247
265
|
return answer;
|
|
248
266
|
}
|
|
249
267
|
|
|
268
|
+
function defaultAuthMode() {
|
|
269
|
+
if (args["auth-mode"]) return args["auth-mode"];
|
|
270
|
+
const hasBasic = Boolean(args["auth-user"] || args["auth-password"] || process.env.AUTH_USERNAME || process.env.AUTH_PASSWORD);
|
|
271
|
+
const hasGitHub = Boolean(args["client-id"] || args["client-secret"] || args["github-userid"] || process.env.GITHUB_CLIENT_ID || process.env.GITHUB_CLIENT_SECRET);
|
|
272
|
+
if (hasBasic && hasGitHub) return "both";
|
|
273
|
+
if (hasBasic) return "basic";
|
|
274
|
+
if (hasGitHub) return "github";
|
|
275
|
+
return defaults["auth-mode"] || (input.isTTY ? "basic" : "none");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function chooseAuthMode(fallback) {
|
|
279
|
+
const modes = new Set(["basic", "github", "both", "none"]);
|
|
280
|
+
if (args["auth-mode"]) {
|
|
281
|
+
if (!modes.has(args["auth-mode"])) throw new Error("--auth-mode must be basic, github, both, or none");
|
|
282
|
+
return args["auth-mode"];
|
|
283
|
+
}
|
|
284
|
+
if (!input.isTTY) return modes.has(fallback) ? fallback : "basic";
|
|
285
|
+
|
|
286
|
+
console.log(color.bold("Choose authentication"));
|
|
287
|
+
console.log(` ${color.cyan("1")}. basic username/password`);
|
|
288
|
+
console.log(` ${color.cyan("2")}. GitHub OAuth`);
|
|
289
|
+
console.log(` ${color.cyan("3")}. both`);
|
|
290
|
+
console.log(` ${color.cyan("4")}. none`);
|
|
291
|
+
|
|
292
|
+
const rl = createInterface({ input, output });
|
|
293
|
+
const answer = (await rl.question(`Authentication [${fallback}]: `)).trim();
|
|
294
|
+
rl.close();
|
|
295
|
+
|
|
296
|
+
if (!answer) return modes.has(fallback) ? fallback : "basic";
|
|
297
|
+
if (modes.has(answer)) return answer;
|
|
298
|
+
if (answer === "1") return "basic";
|
|
299
|
+
if (answer === "2") return "github";
|
|
300
|
+
if (answer === "3") return "both";
|
|
301
|
+
if (answer === "4") return "none";
|
|
302
|
+
throw new Error("Authentication must be basic, github, both, or none");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function usesBasicAuth(mode) {
|
|
306
|
+
return mode === "basic" || mode === "both";
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function usesGitHubAuth(mode) {
|
|
310
|
+
return mode === "github" || mode === "both";
|
|
311
|
+
}
|
|
312
|
+
|
|
250
313
|
async function chooseBase(fallback) {
|
|
251
314
|
if (args.base) return args.base;
|
|
252
315
|
if (!input.isTTY) return fallback;
|
|
@@ -381,6 +444,15 @@ function printSummary(items) {
|
|
|
381
444
|
console.log("");
|
|
382
445
|
}
|
|
383
446
|
|
|
447
|
+
function printOAuthGuide(callbackUrl) {
|
|
448
|
+
console.log(color.bold("GitHub OAuth"));
|
|
449
|
+
console.log(`${color.yellow("!")} Set this callback URL in your GitHub OAuth App before deploying:`);
|
|
450
|
+
console.log(` ${color.bold(callbackUrl)}`);
|
|
451
|
+
console.log(color.dim(" GitHub: Settings -> Developer settings -> OAuth Apps -> Authorization callback URL"));
|
|
452
|
+
console.log(color.dim(" User ID: open https://api.github.com/users/YOUR_LOGIN and copy the numeric id"));
|
|
453
|
+
console.log("");
|
|
454
|
+
}
|
|
455
|
+
|
|
384
456
|
function printKeyValue(key, value) {
|
|
385
457
|
console.log(`${color.dim(`${key.padEnd(14)} `)}${value}`);
|
|
386
458
|
}
|
|
@@ -414,6 +486,9 @@ Options:
|
|
|
414
486
|
--project NAME Vercel project name
|
|
415
487
|
--scope SLUG Optional Vercel team/user scope slug
|
|
416
488
|
--from IMAGE Source image for /app/bin/wsterm
|
|
489
|
+
--auth-mode MODE basic, github, both, or none
|
|
490
|
+
--auth-user VALUE Username/password auth user
|
|
491
|
+
--auth-password VAL Username/password auth password
|
|
417
492
|
--client-id VALUE GitHub OAuth client id
|
|
418
493
|
--client-secret VAL GitHub OAuth client secret
|
|
419
494
|
--github-userid VAL Allowed GitHub numeric user id(s)
|
|
@@ -423,5 +498,9 @@ Options:
|
|
|
423
498
|
--dry-run Generate Dockerfile.vercel only
|
|
424
499
|
--doctor Check Vercel CLI/login and saved defaults
|
|
425
500
|
--help Show this help
|
|
501
|
+
|
|
502
|
+
GitHub OAuth:
|
|
503
|
+
Callback URL must match the deployed project:
|
|
504
|
+
https://PROJECT.vercel.app/auth/github/callback
|
|
426
505
|
`);
|
|
427
506
|
}
|