gigadrive 1.1.1 → 2.0.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/index.cjs +619 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +637 -186
- package/dist/index.mjs.map +1 -1
- package/package.json +19 -11
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,45 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
+
// src/util/log.ts
|
|
7
|
+
import { createConsola } from "consola";
|
|
8
|
+
var verbose = false;
|
|
9
|
+
var consola = createConsola({
|
|
10
|
+
level: verbose ? 4 : 3,
|
|
11
|
+
// 4 includes debug messages, 3 is the default (info and above)
|
|
12
|
+
formatOptions: {
|
|
13
|
+
colors: true
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
var updateConsola = () => {
|
|
17
|
+
consola.level = verbose ? 4 : 3;
|
|
18
|
+
};
|
|
19
|
+
var setVerbose = (value) => {
|
|
20
|
+
verbose = value;
|
|
21
|
+
updateConsola();
|
|
22
|
+
};
|
|
23
|
+
var log = (message) => {
|
|
24
|
+
consola.info(message);
|
|
25
|
+
};
|
|
26
|
+
var error = (message) => {
|
|
27
|
+
consola.error(message);
|
|
28
|
+
};
|
|
29
|
+
var success = (message) => {
|
|
30
|
+
consola.success(message);
|
|
31
|
+
};
|
|
32
|
+
var warn = (message) => {
|
|
33
|
+
consola.warn(message);
|
|
34
|
+
};
|
|
35
|
+
var debug = (message, ...optionalParams) => {
|
|
36
|
+
if (!verbose) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
consola.debug("[DEBUG]", message, ...optionalParams);
|
|
40
|
+
};
|
|
41
|
+
var spinner = (message) => {
|
|
42
|
+
log(message);
|
|
43
|
+
};
|
|
44
|
+
|
|
6
45
|
// src/commands/build/index.ts
|
|
7
46
|
import { exec as exec2 } from "@gigadrive/build-utils";
|
|
8
47
|
import fs2 from "fs";
|
|
@@ -17,7 +56,7 @@ import { spawn } from "child_process";
|
|
|
17
56
|
async function exec({
|
|
18
57
|
command,
|
|
19
58
|
cwd = process.cwd(),
|
|
20
|
-
env,
|
|
59
|
+
env: env2,
|
|
21
60
|
onOutput = (data) => console.log(data),
|
|
22
61
|
onError = (data) => console.error(data)
|
|
23
62
|
}) {
|
|
@@ -35,7 +74,7 @@ async function exec({
|
|
|
35
74
|
const childProcess = spawn(spawnCommand, spawnArgs, {
|
|
36
75
|
cwd,
|
|
37
76
|
shell: true,
|
|
38
|
-
env
|
|
77
|
+
env: env2
|
|
39
78
|
});
|
|
40
79
|
childProcess.stdout.on("data", (data) => {
|
|
41
80
|
onOutput(data.toString().trim());
|
|
@@ -77,7 +116,7 @@ var getPackageManager = async ({ cwd }) => {
|
|
|
77
116
|
try {
|
|
78
117
|
await exec({ command: "bun --version", cwd });
|
|
79
118
|
return "bun";
|
|
80
|
-
} catch
|
|
119
|
+
} catch {
|
|
81
120
|
return "npm";
|
|
82
121
|
}
|
|
83
122
|
};
|
|
@@ -101,7 +140,7 @@ var buildCommand = (pm) => {
|
|
|
101
140
|
// src/commands/build/index.ts
|
|
102
141
|
var build = (parent) => {
|
|
103
142
|
parent.command("build").description("Build the current project").action(async () => {
|
|
104
|
-
const
|
|
143
|
+
const env2 = {
|
|
105
144
|
...process.env,
|
|
106
145
|
NODE_ENV: "production",
|
|
107
146
|
NEBULA: "1",
|
|
@@ -112,34 +151,34 @@ var build = (parent) => {
|
|
|
112
151
|
const cwd = process.cwd();
|
|
113
152
|
const packageJsonPath = path2.join(cwd, "package.json");
|
|
114
153
|
if (!fs2.existsSync(packageJsonPath)) {
|
|
115
|
-
|
|
154
|
+
error("No package.json found");
|
|
116
155
|
return;
|
|
117
156
|
}
|
|
118
157
|
const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
|
|
119
158
|
const buildScript = packageJson.scripts?.build;
|
|
120
159
|
if (!buildScript) {
|
|
121
|
-
|
|
160
|
+
error("No build script found in package.json");
|
|
122
161
|
return;
|
|
123
162
|
}
|
|
124
163
|
const packageManager = await getPackageManager({ cwd });
|
|
125
164
|
if (!packageManager) {
|
|
126
|
-
|
|
165
|
+
error("No package manager found");
|
|
127
166
|
return;
|
|
128
167
|
}
|
|
129
168
|
await exec2({
|
|
130
169
|
command: installCommand(packageManager),
|
|
131
170
|
cwd,
|
|
132
|
-
env,
|
|
171
|
+
env: env2,
|
|
133
172
|
onOutput: (chunk) => {
|
|
134
|
-
|
|
173
|
+
log(chunk);
|
|
135
174
|
}
|
|
136
175
|
});
|
|
137
176
|
await exec2({
|
|
138
177
|
command: buildCommand(packageManager),
|
|
139
178
|
cwd,
|
|
140
|
-
env,
|
|
179
|
+
env: env2,
|
|
141
180
|
onOutput: (chunk) => {
|
|
142
|
-
|
|
181
|
+
log(chunk);
|
|
143
182
|
}
|
|
144
183
|
});
|
|
145
184
|
});
|
|
@@ -148,47 +187,6 @@ var build = (parent) => {
|
|
|
148
187
|
// src/commands/debug/config/index.ts
|
|
149
188
|
import { findConfig, parseConfig } from "@gigadrive/network-config";
|
|
150
189
|
import { log as log2 } from "console";
|
|
151
|
-
|
|
152
|
-
// src/util/log.ts
|
|
153
|
-
import { createConsola } from "consola";
|
|
154
|
-
var verbose = false;
|
|
155
|
-
var consola = createConsola({
|
|
156
|
-
level: verbose ? 4 : 3,
|
|
157
|
-
// 4 includes debug messages, 3 is the default (info and above)
|
|
158
|
-
formatOptions: {
|
|
159
|
-
colors: true
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
var updateConsola = () => {
|
|
163
|
-
consola.level = verbose ? 4 : 3;
|
|
164
|
-
};
|
|
165
|
-
var setVerbose = (value) => {
|
|
166
|
-
verbose = value;
|
|
167
|
-
updateConsola();
|
|
168
|
-
};
|
|
169
|
-
var log = (message) => {
|
|
170
|
-
consola.info(message);
|
|
171
|
-
};
|
|
172
|
-
var error = (message) => {
|
|
173
|
-
consola.error(message);
|
|
174
|
-
};
|
|
175
|
-
var success = (message) => {
|
|
176
|
-
consola.success(message);
|
|
177
|
-
};
|
|
178
|
-
var warn = (message) => {
|
|
179
|
-
consola.warn(message);
|
|
180
|
-
};
|
|
181
|
-
var debug = (message, ...optionalParams) => {
|
|
182
|
-
if (!verbose) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
consola.debug("[DEBUG]", message, ...optionalParams);
|
|
186
|
-
};
|
|
187
|
-
var spinner = (message) => {
|
|
188
|
-
log(message);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// src/commands/debug/config/index.ts
|
|
192
190
|
var config = (parent) => {
|
|
193
191
|
parent.command("config").description("Provide debug info about the project configuration in the current directory").action(async () => {
|
|
194
192
|
const configPath = findConfig(process.cwd());
|
|
@@ -208,19 +206,441 @@ var debug2 = (program) => {
|
|
|
208
206
|
config(debug3);
|
|
209
207
|
};
|
|
210
208
|
|
|
209
|
+
// src/env.ts
|
|
210
|
+
import { createEnv } from "@t3-oss/env-core";
|
|
211
|
+
import { config as loadEnv } from "dotenv";
|
|
212
|
+
import fs3 from "fs";
|
|
213
|
+
import path3 from "path";
|
|
214
|
+
import { z } from "zod";
|
|
215
|
+
(() => {
|
|
216
|
+
const cwd = process.cwd();
|
|
217
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
218
|
+
const candidates = [
|
|
219
|
+
nodeEnv ? `.env.${nodeEnv}.local` : null,
|
|
220
|
+
`.env.local`,
|
|
221
|
+
nodeEnv ? `.env.${nodeEnv}` : null,
|
|
222
|
+
`.env`
|
|
223
|
+
].filter(Boolean);
|
|
224
|
+
for (const filename of candidates) {
|
|
225
|
+
const fullPath = path3.join(cwd, filename);
|
|
226
|
+
if (fs3.existsSync(fullPath)) {
|
|
227
|
+
console.debug(`Loading environment variables from ${fullPath}`);
|
|
228
|
+
loadEnv({ path: fullPath, override: false });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
})();
|
|
232
|
+
var env = createEnv({
|
|
233
|
+
server: {
|
|
234
|
+
GIGADRIVE_NETWORK_OAUTH_ISSUER_URL: z.url().min(1).default("https://idp.gigadrive.de"),
|
|
235
|
+
GIGADRIVE_NETWORK_OAUTH_CLIENT_ID: z.string().min(1).default("todo_add_client_id")
|
|
236
|
+
},
|
|
237
|
+
runtimeEnv: process.env,
|
|
238
|
+
emptyStringAsUndefined: true
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// src/config/oauth.ts
|
|
242
|
+
var cachedConfig = null;
|
|
243
|
+
async function getOAuthConfig() {
|
|
244
|
+
if (cachedConfig) return cachedConfig;
|
|
245
|
+
const issuerBase = env.GIGADRIVE_NETWORK_OAUTH_ISSUER_URL.replace(/\/$/, "");
|
|
246
|
+
const controller = new AbortController();
|
|
247
|
+
const timeout = setTimeout(() => controller.abort(), 8e3);
|
|
248
|
+
let discovery = null;
|
|
249
|
+
try {
|
|
250
|
+
const discoveryUrl = `${issuerBase}/.well-known/openid-configuration`;
|
|
251
|
+
const res = await fetch(discoveryUrl, { signal: controller.signal });
|
|
252
|
+
if (res.ok) {
|
|
253
|
+
discovery = await res.json();
|
|
254
|
+
} else {
|
|
255
|
+
discovery = null;
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
discovery = null;
|
|
259
|
+
} finally {
|
|
260
|
+
clearTimeout(timeout);
|
|
261
|
+
}
|
|
262
|
+
if (!discovery?.authorization_endpoint || !discovery?.token_endpoint || !discovery?.userinfo_endpoint) {
|
|
263
|
+
throw new Error("OIDC discovery failed or returned incomplete endpoints");
|
|
264
|
+
}
|
|
265
|
+
const DEFAULT_SCOPE = "offline_access openid profile email";
|
|
266
|
+
cachedConfig = {
|
|
267
|
+
clientId: env.GIGADRIVE_NETWORK_OAUTH_CLIENT_ID,
|
|
268
|
+
issuer: discovery.issuer ?? issuerBase,
|
|
269
|
+
authorizeUrl: discovery.authorization_endpoint,
|
|
270
|
+
tokenUrl: discovery.token_endpoint,
|
|
271
|
+
scope: DEFAULT_SCOPE,
|
|
272
|
+
userinfoUrl: discovery.userinfo_endpoint
|
|
273
|
+
};
|
|
274
|
+
return cachedConfig;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/auth/manager.ts
|
|
278
|
+
import * as crypto from "crypto";
|
|
279
|
+
import * as fs4 from "fs";
|
|
280
|
+
import * as http from "http";
|
|
281
|
+
import * as os from "os";
|
|
282
|
+
import * as path4 from "path";
|
|
283
|
+
import { URL, URLSearchParams } from "url";
|
|
284
|
+
import open from "open";
|
|
285
|
+
function getAuthStoragePaths() {
|
|
286
|
+
const homeDir = os.homedir();
|
|
287
|
+
const authDir = path4.join(homeDir, ".gigadrive");
|
|
288
|
+
const authFile = path4.join(authDir, "auth.json");
|
|
289
|
+
return { directory: authDir, file: authFile };
|
|
290
|
+
}
|
|
291
|
+
async function ensureAuthDirectory() {
|
|
292
|
+
const { directory } = getAuthStoragePaths();
|
|
293
|
+
try {
|
|
294
|
+
await fs4.promises.mkdir(directory, { recursive: true, mode: 448 });
|
|
295
|
+
await fs4.promises.chmod(directory, 448);
|
|
296
|
+
} catch (error2) {
|
|
297
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
|
|
298
|
+
throw new Error(`Failed to create auth directory: ${errorMessage}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function generatePKCE() {
|
|
302
|
+
const codeVerifier = base64URLEncode(crypto.randomBytes(64));
|
|
303
|
+
const challengeBytes = crypto.createHash("sha256").update(codeVerifier, "utf8").digest();
|
|
304
|
+
const codeChallenge = base64URLEncode(challengeBytes);
|
|
305
|
+
return { codeVerifier, codeChallenge };
|
|
306
|
+
}
|
|
307
|
+
function base64URLEncode(buffer) {
|
|
308
|
+
return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
309
|
+
}
|
|
310
|
+
var AuthManager = class {
|
|
311
|
+
accessToken = null;
|
|
312
|
+
tokenExpirationTime = 0;
|
|
313
|
+
// Unix timestamp in milliseconds
|
|
314
|
+
codeVerifier = null;
|
|
315
|
+
state = null;
|
|
316
|
+
constructor() {
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Retrieves the stored auth data from the local file.
|
|
320
|
+
* @returns {Promise<StoredAuthData | null>} The stored auth data if found, otherwise null.
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
async _loadStoredAuthData() {
|
|
324
|
+
try {
|
|
325
|
+
const { file } = getAuthStoragePaths();
|
|
326
|
+
const data = await fs4.promises.readFile(file, "utf8");
|
|
327
|
+
const authData = JSON.parse(data);
|
|
328
|
+
if (authData.accessToken && authData.tokenExpirationTime && Date.now() < authData.tokenExpirationTime) {
|
|
329
|
+
this.accessToken = authData.accessToken;
|
|
330
|
+
this.tokenExpirationTime = authData.tokenExpirationTime;
|
|
331
|
+
}
|
|
332
|
+
return authData;
|
|
333
|
+
} catch {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Stores the auth data in the local file.
|
|
339
|
+
* @param {StoredAuthData} authData The auth data to store.
|
|
340
|
+
* @private
|
|
341
|
+
*/
|
|
342
|
+
async _saveStoredAuthData(authData) {
|
|
343
|
+
try {
|
|
344
|
+
await ensureAuthDirectory();
|
|
345
|
+
const { file } = getAuthStoragePaths();
|
|
346
|
+
await fs4.promises.writeFile(file, JSON.stringify(authData, null, 2), { encoding: "utf8", mode: 384 });
|
|
347
|
+
await fs4.promises.chmod(file, 384);
|
|
348
|
+
console.debug("Auth data saved to local storage.");
|
|
349
|
+
} catch (error2) {
|
|
350
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
|
|
351
|
+
console.error("Error saving auth data to local storage:", errorMessage);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Deletes the stored auth data from the local file.
|
|
356
|
+
*/
|
|
357
|
+
async logout() {
|
|
358
|
+
this.accessToken = null;
|
|
359
|
+
this.tokenExpirationTime = 0;
|
|
360
|
+
try {
|
|
361
|
+
const { file } = getAuthStoragePaths();
|
|
362
|
+
await fs4.promises.unlink(file);
|
|
363
|
+
log("Logged out. Auth data removed from local storage.");
|
|
364
|
+
} catch {
|
|
365
|
+
log("Logged out.");
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Initiates the OAuth login flow with the IDP via a web portal.
|
|
370
|
+
* Opens a browser for user authentication and listens for the redirect.
|
|
371
|
+
* @returns {Promise<boolean>} True if login was successful, false otherwise.
|
|
372
|
+
*/
|
|
373
|
+
async login() {
|
|
374
|
+
await this.logout();
|
|
375
|
+
const { codeVerifier, codeChallenge } = generatePKCE();
|
|
376
|
+
this.codeVerifier = codeVerifier;
|
|
377
|
+
this.state = crypto.randomBytes(16).toString("hex");
|
|
378
|
+
const OAUTH_CONFIG = await getOAuthConfig();
|
|
379
|
+
const loginState = this.state;
|
|
380
|
+
if (!loginState) {
|
|
381
|
+
throw new Error("Internal error: login state not initialized");
|
|
382
|
+
}
|
|
383
|
+
const server = http.createServer();
|
|
384
|
+
let usedRedirectUri = "";
|
|
385
|
+
const serverPromise = new Promise((resolve, reject) => {
|
|
386
|
+
const redirectPath = "/callback";
|
|
387
|
+
server.listen(0, "127.0.0.1", () => {
|
|
388
|
+
const address = server.address();
|
|
389
|
+
if (!address || typeof address !== "object") {
|
|
390
|
+
reject(new Error("Failed to determine callback server address"));
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const port = address.port;
|
|
394
|
+
usedRedirectUri = `http://127.0.0.1:${port}${redirectPath}`;
|
|
395
|
+
const authUrl = new URL(OAUTH_CONFIG.authorizeUrl);
|
|
396
|
+
authUrl.searchParams.append("client_id", OAUTH_CONFIG.clientId);
|
|
397
|
+
authUrl.searchParams.append("redirect_uri", usedRedirectUri);
|
|
398
|
+
authUrl.searchParams.append("response_type", "code");
|
|
399
|
+
authUrl.searchParams.append("scope", OAUTH_CONFIG.scope);
|
|
400
|
+
authUrl.searchParams.append("code_challenge", codeChallenge);
|
|
401
|
+
authUrl.searchParams.append("code_challenge_method", "S256");
|
|
402
|
+
authUrl.searchParams.append("state", loginState);
|
|
403
|
+
log(`Please open this URL in your browser to log in:
|
|
404
|
+
${authUrl.toString()}
|
|
405
|
+
`);
|
|
406
|
+
log("Waiting for login callback...");
|
|
407
|
+
void open(authUrl.toString());
|
|
408
|
+
});
|
|
409
|
+
server.on("request", (req, res) => {
|
|
410
|
+
const base = usedRedirectUri || "http://127.0.0.1/callback";
|
|
411
|
+
const reqUrl = new URL(req.url || "", base);
|
|
412
|
+
if (reqUrl.pathname === "/callback") {
|
|
413
|
+
const authCode = reqUrl.searchParams.get("code");
|
|
414
|
+
const receivedState = reqUrl.searchParams.get("state");
|
|
415
|
+
const error2 = reqUrl.searchParams.get("error");
|
|
416
|
+
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
417
|
+
if (error2) {
|
|
418
|
+
res.end("<h1>Login Failed!</h1><p>Error: " + error2 + "</p><p>You can close this tab.</p>");
|
|
419
|
+
reject(new Error(`OAuth error: ${error2}`));
|
|
420
|
+
server.close();
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (receivedState !== this.state) {
|
|
424
|
+
res.end("<h1>Login Failed!</h1><p>State mismatch. Possible CSRF attack.</p><p>You can close this tab.</p>");
|
|
425
|
+
reject(new Error("State mismatch"));
|
|
426
|
+
server.close();
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (authCode) {
|
|
430
|
+
res.end("<h1>Login Successful!</h1><p>You can close this tab.</p>");
|
|
431
|
+
resolve(authCode);
|
|
432
|
+
server.close();
|
|
433
|
+
} else {
|
|
434
|
+
res.end("<h1>Login Failed!</h1><p>No authorization code received.</p><p>You can close this tab.</p>");
|
|
435
|
+
reject(new Error("No authorization code received"));
|
|
436
|
+
server.close();
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
res.writeHead(404).end("Not Found");
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
server.on("error", (err) => {
|
|
443
|
+
console.error("Local server error:", err);
|
|
444
|
+
reject(err);
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
try {
|
|
448
|
+
const authCode = await serverPromise;
|
|
449
|
+
const tokenResponse = await fetch(OAUTH_CONFIG.tokenUrl, {
|
|
450
|
+
method: "POST",
|
|
451
|
+
headers: {
|
|
452
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
453
|
+
},
|
|
454
|
+
body: new URLSearchParams({
|
|
455
|
+
grant_type: "authorization_code",
|
|
456
|
+
client_id: OAUTH_CONFIG.clientId,
|
|
457
|
+
code: authCode,
|
|
458
|
+
...usedRedirectUri ? { redirect_uri: usedRedirectUri } : {},
|
|
459
|
+
code_verifier: this.codeVerifier || ""
|
|
460
|
+
// Use the stored codeVerifier
|
|
461
|
+
}).toString()
|
|
462
|
+
});
|
|
463
|
+
if (!tokenResponse.ok) {
|
|
464
|
+
const errorData = await tokenResponse.json().catch(() => null);
|
|
465
|
+
throw new Error(
|
|
466
|
+
`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText} - ${errorData ? JSON.stringify(errorData) : "Unknown error"}`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
const tokens = await tokenResponse.json();
|
|
470
|
+
this.accessToken = tokens.access_token;
|
|
471
|
+
this.tokenExpirationTime = Date.now() + (tokens.expires_in || 3600) * 1e3;
|
|
472
|
+
if (tokens.refresh_token) {
|
|
473
|
+
await this._saveStoredAuthData({
|
|
474
|
+
refreshToken: tokens.refresh_token,
|
|
475
|
+
accessToken: this.accessToken,
|
|
476
|
+
tokenExpirationTime: this.tokenExpirationTime
|
|
477
|
+
});
|
|
478
|
+
success("Login successful!");
|
|
479
|
+
return true;
|
|
480
|
+
} else {
|
|
481
|
+
console.error("Error: No refresh token received from Gigadrive IDP.");
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
} catch (error2) {
|
|
485
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
|
|
486
|
+
console.error("Login process failed:", errorMessage);
|
|
487
|
+
this.accessToken = null;
|
|
488
|
+
this.tokenExpirationTime = 0;
|
|
489
|
+
return false;
|
|
490
|
+
} finally {
|
|
491
|
+
this.codeVerifier = null;
|
|
492
|
+
this.state = null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Refreshes the access token using the stored refresh token.
|
|
497
|
+
* This method implicitly loads the refresh token from local storage.
|
|
498
|
+
* @returns {Promise<boolean>} True if refresh was successful, false otherwise.
|
|
499
|
+
*/
|
|
500
|
+
async refreshAccessToken() {
|
|
501
|
+
const storedData = await this._loadStoredAuthData();
|
|
502
|
+
const OAUTH_CONFIG = await getOAuthConfig();
|
|
503
|
+
if (!storedData?.refreshToken) {
|
|
504
|
+
error("No refresh token available for refresh. Please log in.");
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
const tokenResponse = await fetch(OAUTH_CONFIG.tokenUrl, {
|
|
509
|
+
method: "POST",
|
|
510
|
+
headers: {
|
|
511
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
512
|
+
},
|
|
513
|
+
body: new URLSearchParams({
|
|
514
|
+
grant_type: "refresh_token",
|
|
515
|
+
client_id: OAUTH_CONFIG.clientId,
|
|
516
|
+
refresh_token: storedData.refreshToken
|
|
517
|
+
}).toString()
|
|
518
|
+
});
|
|
519
|
+
if (!tokenResponse.ok) {
|
|
520
|
+
const errorData = await tokenResponse.json().catch(() => null);
|
|
521
|
+
if (tokenResponse.status === 400 && errorData && (errorData.error === "oauth_token_not_found" || errorData.errors?.[0]?.code === "token_invalid" || errorData.errors?.[0]?.code === "token_expired" || errorData.errors?.[0]?.code === "token_revoked")) {
|
|
522
|
+
console.error("Refresh token is invalid or expired. Please log in again.");
|
|
523
|
+
await this.logout();
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Token refresh failed: ${tokenResponse.status} ${tokenResponse.statusText} - ${errorData ? JSON.stringify(errorData) : "Unknown error"}`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
const tokens = await tokenResponse.json();
|
|
531
|
+
this.accessToken = tokens.access_token;
|
|
532
|
+
this.tokenExpirationTime = Date.now() + (tokens.expires_in || 3600) * 1e3;
|
|
533
|
+
const newRefreshToken = tokens.refresh_token || storedData.refreshToken;
|
|
534
|
+
if (tokens.refresh_token && tokens.refresh_token !== storedData.refreshToken) {
|
|
535
|
+
console.debug("New refresh token received and updated.");
|
|
536
|
+
}
|
|
537
|
+
await this._saveStoredAuthData({
|
|
538
|
+
refreshToken: newRefreshToken,
|
|
539
|
+
accessToken: this.accessToken,
|
|
540
|
+
tokenExpirationTime: this.tokenExpirationTime
|
|
541
|
+
});
|
|
542
|
+
console.debug("Access token refreshed successfully!");
|
|
543
|
+
return true;
|
|
544
|
+
} catch (error2) {
|
|
545
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
|
|
546
|
+
console.error("Error refreshing access token:", errorMessage);
|
|
547
|
+
this.accessToken = null;
|
|
548
|
+
this.tokenExpirationTime = 0;
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Provides the current valid access token. If the in-memory token is expired,
|
|
554
|
+
* it attempts to refresh it using the stored refresh token.
|
|
555
|
+
* @returns {Promise<string | null>} The valid access token or null if no session.
|
|
556
|
+
*/
|
|
557
|
+
async getAccessToken() {
|
|
558
|
+
if (this.accessToken && Date.now() < this.tokenExpirationTime) {
|
|
559
|
+
return this.accessToken;
|
|
560
|
+
}
|
|
561
|
+
console.debug("Access token expired or not available. Attempting to refresh...");
|
|
562
|
+
if (await this.refreshAccessToken()) {
|
|
563
|
+
return this.accessToken;
|
|
564
|
+
}
|
|
565
|
+
error("No valid access token available. Please log in first.");
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Fetches the current user's information from the configured userinfo endpoint.
|
|
570
|
+
* Attempts a single token refresh and retry on 401/403 responses.
|
|
571
|
+
*/
|
|
572
|
+
async getUserInfo() {
|
|
573
|
+
const OAUTH_CONFIG = await getOAuthConfig();
|
|
574
|
+
const currentToken = await this.getAccessToken();
|
|
575
|
+
if (!currentToken) {
|
|
576
|
+
error("Not authenticated. Please log in.");
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
const fetchUserInfo = async (token) => fetch(OAUTH_CONFIG.userinfoUrl, {
|
|
580
|
+
headers: {
|
|
581
|
+
Authorization: `Bearer ${token}`,
|
|
582
|
+
Accept: "application/json"
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
let response = await fetchUserInfo(currentToken);
|
|
586
|
+
if (response.status === 401 || response.status === 403) {
|
|
587
|
+
const refreshed = await this.refreshAccessToken();
|
|
588
|
+
if (!refreshed || !this.accessToken) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
response = await fetchUserInfo(this.accessToken);
|
|
592
|
+
}
|
|
593
|
+
if (!response.ok) {
|
|
594
|
+
const body = await response.text().catch(() => "");
|
|
595
|
+
throw new Error(
|
|
596
|
+
`Failed to fetch user info: ${response.status} ${response.statusText}${body ? ` - ${body}` : ""}`
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
const data = await response.json();
|
|
600
|
+
return data;
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
var authManager = new AuthManager();
|
|
604
|
+
|
|
605
|
+
// src/commands/login/index.ts
|
|
606
|
+
var login = (program) => {
|
|
607
|
+
program.command("login").description("Login to the platform").action(async () => {
|
|
608
|
+
log("Initiating login...");
|
|
609
|
+
const result = await authManager.login();
|
|
610
|
+
if (result) {
|
|
611
|
+
try {
|
|
612
|
+
const info = await authManager.getUserInfo() ?? {};
|
|
613
|
+
const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
|
|
614
|
+
const nameField = info.name;
|
|
615
|
+
const givenNameField = info.given_name;
|
|
616
|
+
const familyNameField = info.family_name;
|
|
617
|
+
const preferredUsernameField = info.preferred_username;
|
|
618
|
+
const emailField = info.email;
|
|
619
|
+
const inferredName = isNonEmptyString(nameField) && nameField || (isNonEmptyString(givenNameField) && isNonEmptyString(familyNameField) ? `${givenNameField} ${familyNameField}` : void 0) || isNonEmptyString(preferredUsernameField) && preferredUsernameField || isNonEmptyString(emailField) && emailField || "your account";
|
|
620
|
+
success(`You are now logged in as ${inferredName}.`);
|
|
621
|
+
} catch {
|
|
622
|
+
success("You are now logged in.");
|
|
623
|
+
}
|
|
624
|
+
} else {
|
|
625
|
+
error("Login failed. Please try again.");
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
};
|
|
629
|
+
|
|
211
630
|
// src/commands/platform/deploy/index.ts
|
|
212
631
|
import { formatFileSize, objectToQueryString } from "@gigadrive/commons";
|
|
213
632
|
import { findConfig as findConfig2, parseConfig as parseConfig2 } from "@gigadrive/network-config";
|
|
214
|
-
import
|
|
215
|
-
import
|
|
216
|
-
import
|
|
633
|
+
import fs6 from "fs";
|
|
634
|
+
import os2 from "os";
|
|
635
|
+
import path6 from "path";
|
|
217
636
|
|
|
218
637
|
// src/util/zip.ts
|
|
219
638
|
import { getFilesForPattern } from "@gigadrive/build-utils";
|
|
220
639
|
import archiver from "archiver";
|
|
221
|
-
import
|
|
640
|
+
import fs5 from "fs";
|
|
641
|
+
import fsPromises from "fs/promises";
|
|
222
642
|
import ignore from "ignore";
|
|
223
|
-
import
|
|
643
|
+
import path5 from "path";
|
|
224
644
|
var ignoreFileNames = [
|
|
225
645
|
/*'.gitignore', */
|
|
226
646
|
".gigadriveignore",
|
|
@@ -228,89 +648,95 @@ var ignoreFileNames = [
|
|
|
228
648
|
".dockerignore",
|
|
229
649
|
".nowignore"
|
|
230
650
|
];
|
|
231
|
-
function readIgnoreFile(ignorePath) {
|
|
232
|
-
|
|
233
|
-
|
|
651
|
+
async function readIgnoreFile(ignorePath) {
|
|
652
|
+
try {
|
|
653
|
+
await fsPromises.access(ignorePath);
|
|
654
|
+
const content = await fsPromises.readFile(ignorePath, "utf8");
|
|
655
|
+
return content.split("\n").filter((line) => line.trim() !== "");
|
|
656
|
+
} catch {
|
|
657
|
+
return [];
|
|
234
658
|
}
|
|
235
|
-
return [];
|
|
236
659
|
}
|
|
237
|
-
function collectIgnorePatterns(dir, baseDir, ignoreRules) {
|
|
238
|
-
const items =
|
|
239
|
-
|
|
240
|
-
const fullPath =
|
|
241
|
-
const relativePath =
|
|
242
|
-
const stat =
|
|
660
|
+
async function collectIgnorePatterns(dir, baseDir, ignoreRules) {
|
|
661
|
+
const items = await fsPromises.readdir(dir);
|
|
662
|
+
for (const item of items) {
|
|
663
|
+
const fullPath = path5.join(dir, item);
|
|
664
|
+
const relativePath = path5.relative(baseDir, fullPath);
|
|
665
|
+
const stat = await fsPromises.lstat(fullPath);
|
|
243
666
|
if (stat.isDirectory()) {
|
|
244
667
|
if (ignoreRules.ignores(`${relativePath}/`)) {
|
|
245
|
-
|
|
668
|
+
continue;
|
|
246
669
|
}
|
|
247
|
-
|
|
248
|
-
const ignoreFilePath =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
670
|
+
for (const ignoreFileName of ignoreFileNames) {
|
|
671
|
+
const ignoreFilePath = path5.join(fullPath, ignoreFileName);
|
|
672
|
+
try {
|
|
673
|
+
await fsPromises.access(ignoreFilePath);
|
|
674
|
+
const patterns = (await readIgnoreFile(ignoreFilePath)).map((pattern) => {
|
|
675
|
+
return pattern.startsWith("!") ? `!${path5.join(relativePath, pattern.slice(1))}` : path5.join(relativePath, pattern);
|
|
252
676
|
});
|
|
253
677
|
ignoreRules.add(patterns);
|
|
678
|
+
} catch {
|
|
254
679
|
}
|
|
255
|
-
}
|
|
256
|
-
collectIgnorePatterns(fullPath, baseDir, ignoreRules);
|
|
680
|
+
}
|
|
681
|
+
await collectIgnorePatterns(fullPath, baseDir, ignoreRules);
|
|
257
682
|
}
|
|
258
|
-
}
|
|
683
|
+
}
|
|
259
684
|
}
|
|
260
|
-
function initializeIgnoreRules(baseDir) {
|
|
685
|
+
async function initializeIgnoreRules(baseDir) {
|
|
261
686
|
const ignoreRules = ignore();
|
|
262
687
|
ignoreRules.add([".git/", "**/.DS_Store", "**/Thumbs.db"]);
|
|
263
|
-
|
|
264
|
-
const ignoreFilePath =
|
|
265
|
-
|
|
266
|
-
|
|
688
|
+
for (const ignoreFileName of ignoreFileNames) {
|
|
689
|
+
const ignoreFilePath = path5.join(baseDir, ignoreFileName);
|
|
690
|
+
try {
|
|
691
|
+
await fsPromises.access(ignoreFilePath);
|
|
692
|
+
const patterns = (await readIgnoreFile(ignoreFilePath)).map((pattern) => {
|
|
267
693
|
return pattern.startsWith("!") ? `!${pattern.slice(1)}` : pattern;
|
|
268
694
|
});
|
|
269
695
|
ignoreRules.add(patterns);
|
|
696
|
+
} catch {
|
|
270
697
|
}
|
|
271
|
-
}
|
|
272
|
-
collectIgnorePatterns(baseDir, baseDir, ignoreRules);
|
|
698
|
+
}
|
|
699
|
+
await collectIgnorePatterns(baseDir, baseDir, ignoreRules);
|
|
273
700
|
return ignoreRules;
|
|
274
701
|
}
|
|
275
702
|
async function getFilesToInclude(dir, ignoreRules, whitelist) {
|
|
276
703
|
const filesToInclude = [];
|
|
277
|
-
function
|
|
278
|
-
const items =
|
|
279
|
-
|
|
280
|
-
const fullPath =
|
|
281
|
-
const relativePath =
|
|
282
|
-
const stat =
|
|
704
|
+
async function walkDir(currentDir) {
|
|
705
|
+
const items = await fsPromises.readdir(currentDir);
|
|
706
|
+
for (const item of items) {
|
|
707
|
+
const fullPath = path5.join(currentDir, item);
|
|
708
|
+
const relativePath = path5.relative(dir, fullPath);
|
|
709
|
+
const stat = await fsPromises.lstat(fullPath);
|
|
283
710
|
if (stat.isDirectory()) {
|
|
284
711
|
if (ignoreRules.ignores(`${relativePath}/`)) {
|
|
285
|
-
|
|
712
|
+
continue;
|
|
286
713
|
}
|
|
287
|
-
|
|
714
|
+
await walkDir(fullPath);
|
|
288
715
|
} else {
|
|
289
716
|
if (!ignoreRules.ignores(relativePath)) {
|
|
290
717
|
filesToInclude.push(fullPath);
|
|
291
718
|
} else if (ignoreRules.ignores(relativePath) && ignoreRules.test(relativePath).unignored) {
|
|
292
719
|
filesToInclude.push(fullPath);
|
|
293
|
-
} else {
|
|
294
720
|
}
|
|
295
721
|
}
|
|
296
|
-
}
|
|
722
|
+
}
|
|
297
723
|
}
|
|
298
|
-
|
|
724
|
+
await walkDir(dir);
|
|
299
725
|
return filesToInclude;
|
|
300
726
|
}
|
|
301
727
|
async function createZipArchive(inputDir, outputFile, options = {}) {
|
|
302
|
-
const ignoreRules = options.useIgnoreFiles !== false ? initializeIgnoreRules(inputDir) : ignore();
|
|
728
|
+
const ignoreRules = options.useIgnoreFiles !== false ? await initializeIgnoreRules(inputDir) : ignore();
|
|
303
729
|
const filesToInclude = options.whitelist ? await Promise.all(options.whitelist.map((file) => getFilesForPattern(inputDir, file))) : await getFilesToInclude(inputDir, ignoreRules);
|
|
304
|
-
const output =
|
|
730
|
+
const output = fs5.createWriteStream(outputFile);
|
|
305
731
|
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
306
732
|
return new Promise((resolve, reject) => {
|
|
307
733
|
output.on("close", () => resolve());
|
|
308
734
|
archive.on("error", reject);
|
|
309
735
|
archive.pipe(output);
|
|
310
|
-
filesToInclude.
|
|
311
|
-
archive.file(file, { name:
|
|
312
|
-
}
|
|
313
|
-
archive.finalize();
|
|
736
|
+
for (const file of filesToInclude.flat()) {
|
|
737
|
+
archive.file(file, { name: path5.relative(inputDir, file) });
|
|
738
|
+
}
|
|
739
|
+
void archive.finalize();
|
|
314
740
|
});
|
|
315
741
|
}
|
|
316
742
|
|
|
@@ -367,53 +793,57 @@ var handleDeployment = async (deploymentId, config2) => {
|
|
|
367
793
|
error("The deployment failed. Please check the logs for more information.");
|
|
368
794
|
process.exit(1);
|
|
369
795
|
}
|
|
370
|
-
const statusInterval = setInterval(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
796
|
+
const statusInterval = setInterval(() => {
|
|
797
|
+
void (async () => {
|
|
798
|
+
try {
|
|
799
|
+
debug(`Fetching deployment status for ${deploymentId}`);
|
|
800
|
+
const newStatus = await getDeploymentStatus(deploymentId);
|
|
801
|
+
debug(`Current status: ${status}, New status: ${newStatus}`);
|
|
802
|
+
if (newStatus === "ACTIVE" || newStatus === "FAILED") {
|
|
803
|
+
debug(`Final status reached: ${newStatus}, clearing interval`);
|
|
804
|
+
clearInterval(statusInterval);
|
|
805
|
+
clearInterval(logsInterval);
|
|
806
|
+
if (newStatus === "ACTIVE") {
|
|
807
|
+
success(`Deployed to https://${deploymentId}.gigadrivedev.com`);
|
|
808
|
+
} else {
|
|
809
|
+
error("The deployment failed. Please check the logs for more information.");
|
|
810
|
+
}
|
|
811
|
+
return;
|
|
383
812
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
813
|
+
if (newStatus !== status) {
|
|
814
|
+
debug(`Updating status from ${status} to ${newStatus}`);
|
|
815
|
+
status = newStatus;
|
|
816
|
+
spinner(`Status: ${status.toLowerCase().replace(/^\w/, (c) => c.toUpperCase())}`);
|
|
817
|
+
}
|
|
818
|
+
} catch (err) {
|
|
819
|
+
debug(`Error fetching status: ${err.message}`);
|
|
390
820
|
}
|
|
391
|
-
}
|
|
392
|
-
debug(`Error fetching status: ${err.message}`);
|
|
393
|
-
}
|
|
821
|
+
})();
|
|
394
822
|
}, 1e3);
|
|
395
|
-
const logsInterval = setInterval(
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
823
|
+
const logsInterval = setInterval(() => {
|
|
824
|
+
void (async () => {
|
|
825
|
+
try {
|
|
826
|
+
debug(`Fetching logs for ${deploymentId}, current log count: ${logs.length}`);
|
|
827
|
+
const newLogs = await getLogs(deploymentId, {
|
|
828
|
+
offset: logs.length,
|
|
829
|
+
limit: 100,
|
|
830
|
+
"createdAt[gt]": logs[logs.length - 1]?.createdAt
|
|
831
|
+
});
|
|
832
|
+
debug(`Fetched ${newLogs.items.length} new logs`);
|
|
833
|
+
for (const logEntry of newLogs.items) {
|
|
834
|
+
logs.push(logEntry);
|
|
835
|
+
if (logEntry.type === "ERROR") {
|
|
836
|
+
error(logEntry.message);
|
|
837
|
+
} else if (logEntry.type === "WARN") {
|
|
838
|
+
warn(logEntry.message);
|
|
839
|
+
} else {
|
|
840
|
+
log(logEntry.message);
|
|
841
|
+
}
|
|
412
842
|
}
|
|
843
|
+
} catch (err) {
|
|
844
|
+
debug(`Error fetching logs: ${err.message}`);
|
|
413
845
|
}
|
|
414
|
-
}
|
|
415
|
-
debug(`Error fetching logs: ${err.message}`);
|
|
416
|
-
}
|
|
846
|
+
})();
|
|
417
847
|
}, 1e3);
|
|
418
848
|
spinner(`Status: ${status.toLowerCase().replace(/^\w/, (c) => c.toUpperCase())}`);
|
|
419
849
|
};
|
|
@@ -436,11 +866,11 @@ var addLog = (message, type, logs) => {
|
|
|
436
866
|
var uploadArchive = async (deploymentId, logs, config2) => {
|
|
437
867
|
debug("Starting uploadArchive");
|
|
438
868
|
addLog("Creating archive...", "INFO", logs);
|
|
439
|
-
const archivePath =
|
|
869
|
+
const archivePath = path6.join(process.env.TEMP || os2.tmpdir(), `project-${Date.now()}.zip`);
|
|
440
870
|
debug(`Archive path: ${archivePath}`);
|
|
441
|
-
if (
|
|
871
|
+
if (fs6.existsSync(archivePath)) {
|
|
442
872
|
debug(`Archive already exists, removing: ${archivePath}`);
|
|
443
|
-
|
|
873
|
+
fs6.unlinkSync(archivePath);
|
|
444
874
|
}
|
|
445
875
|
debug("Creating zip archive");
|
|
446
876
|
await createZipArchive(config2.userArchive?.rootOverwrite || process.cwd(), archivePath, {
|
|
@@ -448,7 +878,7 @@ var uploadArchive = async (deploymentId, logs, config2) => {
|
|
|
448
878
|
useIgnoreFiles: false,
|
|
449
879
|
useManagedIgnore: false
|
|
450
880
|
});
|
|
451
|
-
const fileStats =
|
|
881
|
+
const fileStats = fs6.statSync(archivePath);
|
|
452
882
|
const fileSize = fileStats.size;
|
|
453
883
|
debug(`Archive created, size: ${fileSize} bytes (${formatFileSize(fileSize)})`);
|
|
454
884
|
addLog(`Archive created (${formatFileSize(fileSize)})`, "INFO", logs);
|
|
@@ -460,7 +890,7 @@ var uploadArchive = async (deploymentId, logs, config2) => {
|
|
|
460
890
|
const fileChunkSize = 10 * 1024 * 1024;
|
|
461
891
|
const numChunks = Math.ceil(fileSize / fileChunkSize);
|
|
462
892
|
debug(`File size: ${fileSize}, chunk size: ${fileChunkSize}, number of chunks: ${numChunks}`);
|
|
463
|
-
const
|
|
893
|
+
const promises2 = [];
|
|
464
894
|
let start, end;
|
|
465
895
|
for (let index = 1; index < numChunks + 1; index++) {
|
|
466
896
|
start = (index - 1) * fileChunkSize;
|
|
@@ -468,42 +898,44 @@ var uploadArchive = async (deploymentId, logs, config2) => {
|
|
|
468
898
|
debug(`Processing chunk ${index}/${numChunks}, start: ${start}, end: ${end}`);
|
|
469
899
|
const presignedUrl = await getPresignedUrl(deploymentId, uploadId, index);
|
|
470
900
|
debug("Presigned URL for part", index, presignedUrl);
|
|
471
|
-
const promise = new Promise(
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
901
|
+
const promise = new Promise((resolve, reject) => {
|
|
902
|
+
void (async () => {
|
|
903
|
+
try {
|
|
904
|
+
debug(`Reading chunk ${index} data`);
|
|
905
|
+
const readStream = fs6.createReadStream(archivePath, {
|
|
906
|
+
start,
|
|
907
|
+
end: index < numChunks ? end - 1 : void 0
|
|
908
|
+
});
|
|
909
|
+
const chunks = [];
|
|
910
|
+
readStream.on("data", (chunk) => {
|
|
911
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
912
|
+
});
|
|
913
|
+
await new Promise((resolveRead) => {
|
|
914
|
+
readStream.on("end", () => resolveRead());
|
|
915
|
+
readStream.on("error", reject);
|
|
916
|
+
});
|
|
917
|
+
const buffer = Buffer.concat(chunks);
|
|
918
|
+
debug(`Uploading chunk ${index} to presigned URL (${buffer.length} bytes)`);
|
|
919
|
+
const response = await fetch(presignedUrl, {
|
|
920
|
+
method: "PUT",
|
|
921
|
+
headers: {
|
|
922
|
+
"Content-Type": "application/zip",
|
|
923
|
+
"Content-Length": String(buffer.length)
|
|
924
|
+
},
|
|
925
|
+
body: buffer
|
|
926
|
+
});
|
|
927
|
+
resolve(response);
|
|
928
|
+
} catch (err) {
|
|
929
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
930
|
+
}
|
|
931
|
+
})();
|
|
500
932
|
});
|
|
501
|
-
|
|
933
|
+
promises2.push(promise);
|
|
502
934
|
}
|
|
503
935
|
const finishedParts = [];
|
|
504
936
|
try {
|
|
505
|
-
debug(`Waiting for all ${
|
|
506
|
-
const responses = await Promise.all(
|
|
937
|
+
debug(`Waiting for all ${promises2.length} upload promises to complete`);
|
|
938
|
+
const responses = await Promise.all(promises2);
|
|
507
939
|
debug("All upload promises completed");
|
|
508
940
|
for (let index = 0; index < responses.length; index++) {
|
|
509
941
|
const res = responses[index];
|
|
@@ -627,7 +1059,7 @@ var getDeploymentStatus = async (deploymentId) => {
|
|
|
627
1059
|
return deployment.status;
|
|
628
1060
|
};
|
|
629
1061
|
var getLogs = async (deploymentId, options) => {
|
|
630
|
-
debug(`Getting logs for deployment: ${deploymentId}, options:`, options);
|
|
1062
|
+
debug(`Getting logs for deployment: ${deploymentId}, options:`, options ?? "No options");
|
|
631
1063
|
const res = await fetch(`http://localhost:3000/deployments/${deploymentId}/logs${objectToQueryString(options)}`, {
|
|
632
1064
|
method: "GET",
|
|
633
1065
|
headers: {
|
|
@@ -650,6 +1082,23 @@ var platform = (program) => {
|
|
|
650
1082
|
deploy(platform2);
|
|
651
1083
|
};
|
|
652
1084
|
|
|
1085
|
+
// src/commands/whoami/index.ts
|
|
1086
|
+
var whoami = (program) => {
|
|
1087
|
+
program.command("whoami").description("Show the currently authenticated user information").action(async () => {
|
|
1088
|
+
try {
|
|
1089
|
+
const info = await authManager.getUserInfo();
|
|
1090
|
+
if (!info) {
|
|
1091
|
+
console.log('You are not logged in. Run "gigadrive login" to authenticate.');
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
console.log(JSON.stringify(info, null, 2));
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1097
|
+
console.error(`Failed to retrieve user info: ${msg}`);
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
};
|
|
1101
|
+
|
|
653
1102
|
// src/index.ts
|
|
654
1103
|
var { isTTY } = process.stdout;
|
|
655
1104
|
var main = async () => {
|
|
@@ -661,7 +1110,7 @@ var main = async () => {
|
|
|
661
1110
|
let exitCode = 0;
|
|
662
1111
|
const program = new Command();
|
|
663
1112
|
program.option("-v, --verbose", "Enable verbose logging", false);
|
|
664
|
-
program.hook("preSubcommand", (thisCommand
|
|
1113
|
+
program.hook("preSubcommand", (thisCommand) => {
|
|
665
1114
|
setVerbose(thisCommand.opts().verbose === true);
|
|
666
1115
|
if (thisCommand.opts().verbose) {
|
|
667
1116
|
log("Using verbose mode");
|
|
@@ -673,6 +1122,8 @@ var main = async () => {
|
|
|
673
1122
|
platform(program);
|
|
674
1123
|
debug2(program);
|
|
675
1124
|
build(program);
|
|
1125
|
+
login(program);
|
|
1126
|
+
whoami(program);
|
|
676
1127
|
try {
|
|
677
1128
|
await program.parseAsync(process.argv);
|
|
678
1129
|
} catch (err) {
|
|
@@ -681,13 +1132,13 @@ var main = async () => {
|
|
|
681
1132
|
}
|
|
682
1133
|
return exitCode;
|
|
683
1134
|
};
|
|
684
|
-
var handleRejection =
|
|
1135
|
+
var handleRejection = (err) => {
|
|
685
1136
|
if (err) {
|
|
686
1137
|
if (err instanceof Error) {
|
|
687
|
-
|
|
1138
|
+
handleUnexpected(err);
|
|
688
1139
|
} else {
|
|
689
1140
|
console.error(`An unexpected rejection occurred
|
|
690
|
-
${err}`);
|
|
1141
|
+
${JSON.stringify(err)}`);
|
|
691
1142
|
}
|
|
692
1143
|
} else {
|
|
693
1144
|
console.error("An unexpected empty rejection occurred");
|
|
@@ -706,7 +1157,7 @@ process.on("exit", () => {
|
|
|
706
1157
|
const terminal = process.stderr.isTTY ? process.stderr : process.stdout.isTTY ? process.stdout : void 0;
|
|
707
1158
|
terminal?.write("\x1B[?25h");
|
|
708
1159
|
});
|
|
709
|
-
main().then(
|
|
710
|
-
process.exitCode
|
|
1160
|
+
main().then((exitCode) => {
|
|
1161
|
+
process.exit(exitCode);
|
|
711
1162
|
}).catch(handleUnexpected);
|
|
712
1163
|
//# sourceMappingURL=index.mjs.map
|