@studiometa/trafic-agent 0.1.37 → 0.1.39
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 +46 -23
- package/dist/cli.js +27 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{server-CLrB1zpR.js → server-BAjHxgqh.js} +198 -195
- package/dist/server-BAjHxgqh.js.map +1 -0
- package/dist/server.d.ts +48 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/setup/docker-firewall.d.ts +35 -0
- package/dist/setup/docker-firewall.d.ts.map +1 -0
- package/dist/setup/index.d.ts.map +1 -1
- package/dist/setup/migrations/0012__docker_user_tool_ports.d.ts +26 -0
- package/dist/setup/migrations/0012__docker_user_tool_ports.d.ts.map +1 -0
- package/dist/setup/migrations/index.d.ts.map +1 -1
- package/dist/setup/upgrade.d.ts +23 -1
- package/dist/setup/upgrade.d.ts.map +1 -1
- package/dist/{setup-DHhQF7wW.js → setup-DxoFhMfT.js} +163 -4
- package/dist/setup-DxoFhMfT.js.map +1 -0
- package/dist/utils/db.d.ts +13 -4
- package/dist/utils/db.d.ts.map +1 -1
- package/dist/utils/ddev.d.ts +24 -3
- package/dist/utils/ddev.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/server-CLrB1zpR.js.map +0 -1
- package/dist/setup-DHhQF7wW.js.map +0 -1
|
@@ -294,42 +294,31 @@ function watchProjectList(projectListPath, onChange) {
|
|
|
294
294
|
if (eventType === "change") onChange();
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
|
-
/**
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
* the same thread.
|
|
306
|
-
*/
|
|
307
|
-
function ddev(args, timeoutMs) {
|
|
308
|
-
return new Promise((resolve) => {
|
|
309
|
-
execFile("ddev", args, {
|
|
310
|
-
encoding: "utf-8",
|
|
311
|
-
timeout: timeoutMs
|
|
312
|
-
}, (error, stdout) => {
|
|
313
|
-
if (error) {
|
|
314
|
-
console.error(`ddev ${args.join(" ")} failed:`, error.message);
|
|
315
|
-
resolve({
|
|
316
|
-
ok: false,
|
|
317
|
-
stdout: stdout ?? ""
|
|
318
|
-
});
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
297
|
+
/** The real runner, used unless a caller passes its own. */
|
|
298
|
+
var nodeDdevRunner = (args, timeoutMs) => new Promise((resolve) => {
|
|
299
|
+
execFile("ddev", args, {
|
|
300
|
+
encoding: "utf-8",
|
|
301
|
+
timeout: timeoutMs
|
|
302
|
+
}, (error, stdout) => {
|
|
303
|
+
if (error) {
|
|
304
|
+
console.error(`ddev ${args.join(" ")} failed:`, error.message);
|
|
321
305
|
resolve({
|
|
322
|
-
ok:
|
|
306
|
+
ok: false,
|
|
323
307
|
stdout: stdout ?? ""
|
|
324
308
|
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
resolve({
|
|
312
|
+
ok: true,
|
|
313
|
+
stdout: stdout ?? ""
|
|
325
314
|
});
|
|
326
315
|
});
|
|
327
|
-
}
|
|
316
|
+
});
|
|
328
317
|
/**
|
|
329
318
|
* Get detailed project info using ddev describe
|
|
330
319
|
*/
|
|
331
|
-
async function getProjectInfo(name) {
|
|
332
|
-
const { ok, stdout } = await
|
|
320
|
+
async function getProjectInfo(name, run = nodeDdevRunner) {
|
|
321
|
+
const { ok, stdout } = await run([
|
|
333
322
|
"describe",
|
|
334
323
|
name,
|
|
335
324
|
"-j"
|
|
@@ -359,15 +348,15 @@ async function getProjectInfo(name) {
|
|
|
359
348
|
* Resolves when the project is up. Callers in the request path must not await
|
|
360
349
|
* it — respond first, then let this settle and record the outcome.
|
|
361
350
|
*/
|
|
362
|
-
async function startProject(name) {
|
|
363
|
-
const { ok } = await
|
|
351
|
+
async function startProject(name, run = nodeDdevRunner) {
|
|
352
|
+
const { ok } = await run(["start", name], 12e4);
|
|
364
353
|
return ok;
|
|
365
354
|
}
|
|
366
355
|
/**
|
|
367
356
|
* Stop a DDEV project
|
|
368
357
|
*/
|
|
369
|
-
async function stopProject(name) {
|
|
370
|
-
const { ok } = await
|
|
358
|
+
async function stopProject(name, run = nodeDdevRunner) {
|
|
359
|
+
const { ok } = await run(["stop", name], 6e4);
|
|
371
360
|
return ok;
|
|
372
361
|
}
|
|
373
362
|
/**
|
|
@@ -394,7 +383,134 @@ function listProjects() {
|
|
|
394
383
|
}
|
|
395
384
|
}
|
|
396
385
|
//#endregion
|
|
386
|
+
//#region src/setup/steps.ts
|
|
387
|
+
var stepNumber = 0;
|
|
388
|
+
var dryRun = false;
|
|
389
|
+
/**
|
|
390
|
+
* Set dry-run mode
|
|
391
|
+
*/
|
|
392
|
+
function setDryRun(value) {
|
|
393
|
+
dryRun = value;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Reset step counter
|
|
397
|
+
*/
|
|
398
|
+
function resetSteps() {
|
|
399
|
+
stepNumber = 0;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Log a step
|
|
403
|
+
*/
|
|
404
|
+
function step(message) {
|
|
405
|
+
stepNumber++;
|
|
406
|
+
console.log(`\n\x1b[36m[${stepNumber}]\x1b[0m ${message}`);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Log success
|
|
410
|
+
*/
|
|
411
|
+
function success(message) {
|
|
412
|
+
console.log(` \x1b[32m✓\x1b[0m ${message}`);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Log warning
|
|
416
|
+
*/
|
|
417
|
+
function warn(message) {
|
|
418
|
+
console.log(` \x1b[33m⚠\x1b[0m ${message}`);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Log error
|
|
422
|
+
*/
|
|
423
|
+
function error(message) {
|
|
424
|
+
console.log(` \x1b[31m✗\x1b[0m ${message}`);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Log info
|
|
428
|
+
*/
|
|
429
|
+
function info(message) {
|
|
430
|
+
console.log(` ${message}`);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Execute a command (or show in dry-run mode)
|
|
434
|
+
*/
|
|
435
|
+
function exec(command, options) {
|
|
436
|
+
if (dryRun) {
|
|
437
|
+
if (!options?.silent) console.log(` \x1b[90m$ ${command}\x1b[0m`);
|
|
438
|
+
return "";
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
return execSync(command, {
|
|
442
|
+
encoding: "utf-8",
|
|
443
|
+
stdio: options?.silent ? "pipe" : "inherit"
|
|
444
|
+
});
|
|
445
|
+
} catch (err) {
|
|
446
|
+
throw new Error(`Command failed: ${command}\n${err.stderr ?? ""}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Execute a command and return output (silent)
|
|
451
|
+
*/
|
|
452
|
+
function execSilent(command) {
|
|
453
|
+
if (dryRun) return "";
|
|
454
|
+
try {
|
|
455
|
+
return execSync(command, {
|
|
456
|
+
encoding: "utf-8",
|
|
457
|
+
stdio: "pipe"
|
|
458
|
+
}).trim();
|
|
459
|
+
} catch {
|
|
460
|
+
return "";
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Check if a command exists
|
|
465
|
+
*/
|
|
466
|
+
function commandExists(command) {
|
|
467
|
+
try {
|
|
468
|
+
execSync(`which ${command}`, { stdio: "pipe" });
|
|
469
|
+
return true;
|
|
470
|
+
} catch {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Check if running as root
|
|
476
|
+
*/
|
|
477
|
+
function isRoot() {
|
|
478
|
+
return process.getuid?.() === 0;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Write a file (or show in dry-run mode)
|
|
482
|
+
*/
|
|
483
|
+
function writeFile(path, content) {
|
|
484
|
+
if (dryRun) {
|
|
485
|
+
console.log(` \x1b[90mWrite ${path}:\x1b[0m`);
|
|
486
|
+
const lines = content.split("\n").slice(0, 5);
|
|
487
|
+
for (const line of lines) console.log(` \x1b[90m ${line}\x1b[0m`);
|
|
488
|
+
if (content.split("\n").length > 5) console.log(` \x1b[90m ...\x1b[0m`);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
writeFileSync(path, content);
|
|
492
|
+
}
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/setup/io.ts
|
|
495
|
+
/**
|
|
496
|
+
* The real implementation, used when no io is injected.
|
|
497
|
+
*
|
|
498
|
+
* `writeFile` comes from steps.js rather than `node:fs` so writes are
|
|
499
|
+
* skipped in dry-run mode. Reads stay direct: they change nothing.
|
|
500
|
+
*/
|
|
501
|
+
var nodeIo = {
|
|
502
|
+
exec,
|
|
503
|
+
execSilent,
|
|
504
|
+
commandExists,
|
|
505
|
+
writeFile,
|
|
506
|
+
fileExists: existsSync,
|
|
507
|
+
readFile: (path) => readFileSync(path, "utf-8"),
|
|
508
|
+
env: process.env
|
|
509
|
+
};
|
|
510
|
+
//#endregion
|
|
397
511
|
//#region src/utils/db.ts
|
|
512
|
+
/** The real clock, used unless a caller passes its own. */
|
|
513
|
+
var systemClock = () => Date.now();
|
|
398
514
|
var db = null;
|
|
399
515
|
/**
|
|
400
516
|
* Initialize the database connection and schema
|
|
@@ -442,12 +558,12 @@ function closeDb() {
|
|
|
442
558
|
/**
|
|
443
559
|
* Update project last access time
|
|
444
560
|
*/
|
|
445
|
-
function updateProjectAccess(name) {
|
|
561
|
+
function updateProjectAccess(name, now = systemClock) {
|
|
446
562
|
getDb().prepare(`
|
|
447
563
|
INSERT INTO projects (name, last_access, status)
|
|
448
564
|
VALUES (?, ?, 'running')
|
|
449
565
|
ON CONFLICT(name) DO UPDATE SET last_access = excluded.last_access
|
|
450
|
-
`).run(name,
|
|
566
|
+
`).run(name, now());
|
|
451
567
|
}
|
|
452
568
|
/**
|
|
453
569
|
* Get project record
|
|
@@ -458,19 +574,19 @@ function getProject(name) {
|
|
|
458
574
|
/**
|
|
459
575
|
* Set project status
|
|
460
576
|
*/
|
|
461
|
-
function setProjectStatus(name, status) {
|
|
577
|
+
function setProjectStatus(name, status, now = systemClock) {
|
|
462
578
|
getDb().prepare(`
|
|
463
579
|
INSERT INTO projects (name, last_access, status)
|
|
464
580
|
VALUES (?, ?, ?)
|
|
465
581
|
ON CONFLICT(name) DO UPDATE SET status = excluded.status
|
|
466
|
-
`).run(name,
|
|
582
|
+
`).run(name, now(), status);
|
|
467
583
|
}
|
|
468
584
|
/**
|
|
469
585
|
* Get idle projects (last access older than threshold)
|
|
470
586
|
*/
|
|
471
|
-
function getIdleProjects(thresholdMs) {
|
|
587
|
+
function getIdleProjects(thresholdMs, now = systemClock) {
|
|
472
588
|
const database = getDb();
|
|
473
|
-
const cutoff =
|
|
589
|
+
const cutoff = now() - thresholdMs;
|
|
474
590
|
return database.prepare(`
|
|
475
591
|
SELECT name, last_access as lastAccess, status
|
|
476
592
|
FROM projects
|
|
@@ -501,9 +617,9 @@ function getAccessLogs(project, limit = 100) {
|
|
|
501
617
|
/**
|
|
502
618
|
* Clean old access logs (older than given days)
|
|
503
619
|
*/
|
|
504
|
-
function cleanOldLogs(days) {
|
|
620
|
+
function cleanOldLogs(days, now = systemClock) {
|
|
505
621
|
const database = getDb();
|
|
506
|
-
const cutoff =
|
|
622
|
+
const cutoff = now() - days * 24 * 60 * 60 * 1e3;
|
|
507
623
|
const result = database.prepare("DELETE FROM access_logs WHERE timestamp < ?").run(cutoff);
|
|
508
624
|
return Number(result.changes);
|
|
509
625
|
}
|
|
@@ -574,131 +690,6 @@ function getIdleTimeoutMs(config) {
|
|
|
574
690
|
return ms > 0 ? ms : void 0;
|
|
575
691
|
}
|
|
576
692
|
//#endregion
|
|
577
|
-
//#region src/setup/steps.ts
|
|
578
|
-
var stepNumber = 0;
|
|
579
|
-
var dryRun = false;
|
|
580
|
-
/**
|
|
581
|
-
* Set dry-run mode
|
|
582
|
-
*/
|
|
583
|
-
function setDryRun(value) {
|
|
584
|
-
dryRun = value;
|
|
585
|
-
}
|
|
586
|
-
/**
|
|
587
|
-
* Reset step counter
|
|
588
|
-
*/
|
|
589
|
-
function resetSteps() {
|
|
590
|
-
stepNumber = 0;
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Log a step
|
|
594
|
-
*/
|
|
595
|
-
function step(message) {
|
|
596
|
-
stepNumber++;
|
|
597
|
-
console.log(`\n\x1b[36m[${stepNumber}]\x1b[0m ${message}`);
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Log success
|
|
601
|
-
*/
|
|
602
|
-
function success(message) {
|
|
603
|
-
console.log(` \x1b[32m✓\x1b[0m ${message}`);
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Log warning
|
|
607
|
-
*/
|
|
608
|
-
function warn(message) {
|
|
609
|
-
console.log(` \x1b[33m⚠\x1b[0m ${message}`);
|
|
610
|
-
}
|
|
611
|
-
/**
|
|
612
|
-
* Log error
|
|
613
|
-
*/
|
|
614
|
-
function error(message) {
|
|
615
|
-
console.log(` \x1b[31m✗\x1b[0m ${message}`);
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* Log info
|
|
619
|
-
*/
|
|
620
|
-
function info(message) {
|
|
621
|
-
console.log(` ${message}`);
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Execute a command (or show in dry-run mode)
|
|
625
|
-
*/
|
|
626
|
-
function exec(command, options) {
|
|
627
|
-
if (dryRun) {
|
|
628
|
-
if (!options?.silent) console.log(` \x1b[90m$ ${command}\x1b[0m`);
|
|
629
|
-
return "";
|
|
630
|
-
}
|
|
631
|
-
try {
|
|
632
|
-
return execSync(command, {
|
|
633
|
-
encoding: "utf-8",
|
|
634
|
-
stdio: options?.silent ? "pipe" : "inherit"
|
|
635
|
-
});
|
|
636
|
-
} catch (err) {
|
|
637
|
-
throw new Error(`Command failed: ${command}\n${err.stderr ?? ""}`);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Execute a command and return output (silent)
|
|
642
|
-
*/
|
|
643
|
-
function execSilent(command) {
|
|
644
|
-
if (dryRun) return "";
|
|
645
|
-
try {
|
|
646
|
-
return execSync(command, {
|
|
647
|
-
encoding: "utf-8",
|
|
648
|
-
stdio: "pipe"
|
|
649
|
-
}).trim();
|
|
650
|
-
} catch {
|
|
651
|
-
return "";
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
|
-
* Check if a command exists
|
|
656
|
-
*/
|
|
657
|
-
function commandExists(command) {
|
|
658
|
-
try {
|
|
659
|
-
execSync(`which ${command}`, { stdio: "pipe" });
|
|
660
|
-
return true;
|
|
661
|
-
} catch {
|
|
662
|
-
return false;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Check if running as root
|
|
667
|
-
*/
|
|
668
|
-
function isRoot() {
|
|
669
|
-
return process.getuid?.() === 0;
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Write a file (or show in dry-run mode)
|
|
673
|
-
*/
|
|
674
|
-
function writeFile(path, content) {
|
|
675
|
-
if (dryRun) {
|
|
676
|
-
console.log(` \x1b[90mWrite ${path}:\x1b[0m`);
|
|
677
|
-
const lines = content.split("\n").slice(0, 5);
|
|
678
|
-
for (const line of lines) console.log(` \x1b[90m ${line}\x1b[0m`);
|
|
679
|
-
if (content.split("\n").length > 5) console.log(` \x1b[90m ...\x1b[0m`);
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
writeFileSync(path, content);
|
|
683
|
-
}
|
|
684
|
-
//#endregion
|
|
685
|
-
//#region src/setup/io.ts
|
|
686
|
-
/**
|
|
687
|
-
* The real implementation, used when no io is injected.
|
|
688
|
-
*
|
|
689
|
-
* `writeFile` comes from steps.js rather than `node:fs` so writes are
|
|
690
|
-
* skipped in dry-run mode. Reads stay direct: they change nothing.
|
|
691
|
-
*/
|
|
692
|
-
var nodeIo = {
|
|
693
|
-
exec,
|
|
694
|
-
execSilent,
|
|
695
|
-
commandExists,
|
|
696
|
-
writeFile,
|
|
697
|
-
fileExists: existsSync,
|
|
698
|
-
readFile: (path) => readFileSync(path, "utf-8"),
|
|
699
|
-
env: process.env
|
|
700
|
-
};
|
|
701
|
-
//#endregion
|
|
702
693
|
//#region src/setup/ddev.ts
|
|
703
694
|
/**
|
|
704
695
|
* Install system dependencies required by Trafic
|
|
@@ -1028,12 +1019,12 @@ function loadTemplate(name) {
|
|
|
1028
1019
|
/**
|
|
1029
1020
|
* Get effective auth config for a project (merges global + per-project)
|
|
1030
1021
|
*/
|
|
1031
|
-
function getEffectiveAuthConfig(projectName) {
|
|
1032
|
-
if (!projectName) return
|
|
1033
|
-
const projectConfig = projectConfigs.get(projectName);
|
|
1034
|
-
if (!projectConfig?.auth_policy) return
|
|
1022
|
+
function getEffectiveAuthConfig(projectName, deps) {
|
|
1023
|
+
if (!projectName) return deps.auth;
|
|
1024
|
+
const projectConfig = deps.projectConfigs.get(projectName);
|
|
1025
|
+
if (!projectConfig?.auth_policy) return deps.auth;
|
|
1035
1026
|
return {
|
|
1036
|
-
...
|
|
1027
|
+
...deps.auth,
|
|
1037
1028
|
defaultPolicy: projectConfig.auth_policy
|
|
1038
1029
|
};
|
|
1039
1030
|
}
|
|
@@ -1041,14 +1032,14 @@ function getEffectiveAuthConfig(projectName) {
|
|
|
1041
1032
|
* Handle forward auth requests from Traefik
|
|
1042
1033
|
* Traefik sends the original request headers, we return 200 (allow) or 401 (deny)
|
|
1043
1034
|
*/
|
|
1044
|
-
function handleAuth(req, res) {
|
|
1035
|
+
function handleAuth(req, res, deps) {
|
|
1045
1036
|
const hostname = req.headers["x-forwarded-host"] ?? "";
|
|
1046
1037
|
const socketIp = req.socket.remoteAddress ?? "";
|
|
1047
1038
|
const forwardedFor = req.headers["x-forwarded-for"];
|
|
1048
1039
|
const authorization = req.headers["authorization"];
|
|
1049
1040
|
const path = req.headers["x-forwarded-uri"] ?? "/";
|
|
1050
|
-
const projectName = hostnameIndex.get(hostname);
|
|
1051
|
-
const authConfig = getEffectiveAuthConfig(projectName);
|
|
1041
|
+
const projectName = deps.hostnameIndex.get(hostname);
|
|
1042
|
+
const authConfig = getEffectiveAuthConfig(projectName, deps);
|
|
1052
1043
|
const clientIp = resolveClientIp(socketIp, forwardedFor, authConfig.trustedProxyHops);
|
|
1053
1044
|
if (checkAuth({
|
|
1054
1045
|
hostname,
|
|
@@ -1057,8 +1048,8 @@ function handleAuth(req, res) {
|
|
|
1057
1048
|
forwardedFor
|
|
1058
1049
|
}, authConfig).allowed) {
|
|
1059
1050
|
if (projectName) {
|
|
1060
|
-
updateProjectAccess(projectName);
|
|
1061
|
-
logAccess({
|
|
1051
|
+
deps.updateProjectAccess(projectName);
|
|
1052
|
+
deps.logAccess({
|
|
1062
1053
|
project: projectName,
|
|
1063
1054
|
timestamp: Date.now(),
|
|
1064
1055
|
ip: clientIp,
|
|
@@ -1077,17 +1068,17 @@ function handleAuth(req, res) {
|
|
|
1077
1068
|
* Handle errors middleware requests (502 from Traefik)
|
|
1078
1069
|
* When a project is stopped, Traefik returns 502. We show a waiting page and start the project.
|
|
1079
1070
|
*/
|
|
1080
|
-
async function handleErrors(req, res) {
|
|
1071
|
+
async function handleErrors(req, res, deps) {
|
|
1081
1072
|
const hostname = req.headers["x-forwarded-host"] ?? req.headers.host ?? "";
|
|
1082
|
-
const projectName = hostnameIndex.get(hostname);
|
|
1073
|
+
const projectName = deps.hostnameIndex.get(hostname);
|
|
1083
1074
|
if (!projectName) {
|
|
1084
|
-
const template = loadTemplate("error");
|
|
1075
|
+
const template = deps.loadTemplate("error");
|
|
1085
1076
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
1086
1077
|
res.end(template.replace("{{message}}", "Project not found"));
|
|
1087
1078
|
return;
|
|
1088
1079
|
}
|
|
1089
|
-
if (getProject(projectName)?.status === "starting") {
|
|
1090
|
-
const template = loadTemplate("wait");
|
|
1080
|
+
if (deps.getProject(projectName)?.status === "starting") {
|
|
1081
|
+
const template = deps.loadTemplate("wait");
|
|
1091
1082
|
res.writeHead(503, {
|
|
1092
1083
|
"Content-Type": "text/html",
|
|
1093
1084
|
"Retry-After": "5"
|
|
@@ -1095,32 +1086,32 @@ async function handleErrors(req, res) {
|
|
|
1095
1086
|
res.end(template.replace(/\{\{project\}\}/g, projectName).replace(/\{\{hostname\}\}/g, hostname));
|
|
1096
1087
|
return;
|
|
1097
1088
|
}
|
|
1098
|
-
setProjectStatus(projectName, "starting");
|
|
1099
|
-
const template = loadTemplate("wait");
|
|
1089
|
+
deps.setProjectStatus(projectName, "starting");
|
|
1090
|
+
const template = deps.loadTemplate("wait");
|
|
1100
1091
|
res.writeHead(503, {
|
|
1101
1092
|
"Content-Type": "text/html",
|
|
1102
1093
|
"Retry-After": "5"
|
|
1103
1094
|
});
|
|
1104
1095
|
res.end(template.replace(/\{\{project\}\}/g, projectName).replace(/\{\{hostname\}\}/g, hostname));
|
|
1105
|
-
startProject(projectName).then((success) => {
|
|
1106
|
-
setProjectStatus(projectName, success ? "running" : "stopped");
|
|
1096
|
+
deps.startProject(projectName).then((success) => {
|
|
1097
|
+
deps.setProjectStatus(projectName, success ? "running" : "stopped");
|
|
1107
1098
|
}).catch((error) => {
|
|
1108
|
-
setProjectStatus(projectName, "stopped");
|
|
1099
|
+
deps.setProjectStatus(projectName, "stopped");
|
|
1109
1100
|
console.error(`Could not start ${projectName}:`, error);
|
|
1110
1101
|
});
|
|
1111
1102
|
}
|
|
1112
1103
|
/**
|
|
1113
1104
|
* Handle status polling requests
|
|
1114
1105
|
*/
|
|
1115
|
-
async function handleStatus(req, res) {
|
|
1106
|
+
async function handleStatus(req, res, deps) {
|
|
1116
1107
|
const projectName = new URL(req.url ?? "/", `http://${req.headers.host}`).searchParams.get("project");
|
|
1117
1108
|
if (!projectName) {
|
|
1118
1109
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1119
1110
|
res.end(JSON.stringify({ error: "Missing project parameter" }));
|
|
1120
1111
|
return;
|
|
1121
1112
|
}
|
|
1122
|
-
const info = await getProjectInfo(projectName);
|
|
1123
|
-
const record = getProject(projectName);
|
|
1113
|
+
const info = await deps.getProjectInfo(projectName);
|
|
1114
|
+
const record = deps.getProject(projectName);
|
|
1124
1115
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1125
1116
|
res.end(JSON.stringify({
|
|
1126
1117
|
name: projectName,
|
|
@@ -1148,18 +1139,18 @@ function routePath(target) {
|
|
|
1148
1139
|
/**
|
|
1149
1140
|
* Request handler
|
|
1150
1141
|
*/
|
|
1151
|
-
async function handleRequest(req, res) {
|
|
1142
|
+
async function handleRequest(req, res, deps) {
|
|
1152
1143
|
const path = routePath(req.url);
|
|
1153
1144
|
try {
|
|
1154
|
-
if (path === "/__auth__" || path.startsWith("/__auth__/")) handleAuth(req, res);
|
|
1155
|
-
else if (path === "/__status__" || path.startsWith("/__status__/")) await handleStatus(req, res);
|
|
1145
|
+
if (path === "/__auth__" || path.startsWith("/__auth__/")) handleAuth(req, res, deps);
|
|
1146
|
+
else if (path === "/__status__" || path.startsWith("/__status__/")) await handleStatus(req, res, deps);
|
|
1156
1147
|
else if (path === "/__health__") {
|
|
1157
1148
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1158
1149
|
res.end(JSON.stringify({
|
|
1159
1150
|
status: "ok",
|
|
1160
1151
|
version: "__VERSION__"
|
|
1161
1152
|
}));
|
|
1162
|
-
} else await handleErrors(req, res);
|
|
1153
|
+
} else await handleErrors(req, res, deps);
|
|
1163
1154
|
} catch (error) {
|
|
1164
1155
|
console.error("Request error:", error);
|
|
1165
1156
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
@@ -1194,8 +1185,20 @@ function startServer(agentConfig) {
|
|
|
1194
1185
|
console.log("Project list changed, reloading...");
|
|
1195
1186
|
reloadProjects();
|
|
1196
1187
|
});
|
|
1188
|
+
const deps = () => ({
|
|
1189
|
+
auth: config.auth,
|
|
1190
|
+
hostnameIndex,
|
|
1191
|
+
projectConfigs,
|
|
1192
|
+
loadTemplate,
|
|
1193
|
+
updateProjectAccess,
|
|
1194
|
+
logAccess,
|
|
1195
|
+
getProject,
|
|
1196
|
+
setProjectStatus,
|
|
1197
|
+
startProject,
|
|
1198
|
+
getProjectInfo
|
|
1199
|
+
});
|
|
1197
1200
|
createServer((req, res) => {
|
|
1198
|
-
handleRequest(req, res).catch((error) => {
|
|
1201
|
+
handleRequest(req, res, deps()).catch((error) => {
|
|
1199
1202
|
console.error("Unhandled error:", error);
|
|
1200
1203
|
if (!res.headersSent) {
|
|
1201
1204
|
res.writeHead(500);
|
|
@@ -1209,6 +1212,6 @@ function startServer(agentConfig) {
|
|
|
1209
1212
|
});
|
|
1210
1213
|
}
|
|
1211
1214
|
//#endregion
|
|
1212
|
-
export {
|
|
1215
|
+
export { error as A, buildHostnameIndex as B, getIdleProjects as C, setProjectStatus as D, logAccess as E, resetSteps as F, startProject as G, getProjectInfo as H, setDryRun as I, checkAuth as J, stopProject as K, step as L, execSilent as M, info as N, updateProjectAccess as O, isRoot as P, parseBearerToken as Q, success as R, getDb as S, initDb as T, listProjects as U, getProjectFromHostname as V, loadProjectList as W, matchIp as X, matchHostname as Y, parseBasicAuth as Z, loadProjectConfig as _, handleStatus as a, closeDb as b, addDdevAptRepo as c, getDockerGatewayIp as d, installDdev as f, getIdleTimeoutMs as g, readToolPorts as h, handleRequest as i, exec as j, nodeIo as k, configureDdev as l, purgeTraefikAcmeStorage as m, handleAuth as n, routePath as o, installSystemDeps as p, watchProjectList as q, handleErrors as r, startServer as s, getEffectiveAuthConfig as t, configureTraefik as u, shouldNeverStop as v, getProject as w, getAccessLogs as x, cleanOldLogs as y, warn as z };
|
|
1213
1216
|
|
|
1214
|
-
//# sourceMappingURL=server-
|
|
1217
|
+
//# sourceMappingURL=server-BAjHxgqh.js.map
|