@hasna/shortlinks 0.1.2 → 0.1.3
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 +11 -0
- package/dist/cli/index.js +88 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +49 -0
- package/dist/local.d.ts +27 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,6 +67,17 @@ shortlinks serve --port 8787
|
|
|
67
67
|
shortlinks doctor
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
## Local Domain Setup
|
|
71
|
+
|
|
72
|
+
Record a local mapping with the `machines` CLI and print the remaining hosts/proxy setup:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
shortlinks local setup has.na --port 8787
|
|
76
|
+
shortlinks local plan has.na --port 8787
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The command emits the `/etc/hosts` line, a Caddy reverse-proxy snippet, and certificate paths. Writing `/etc/hosts` still requires sudo on macOS.
|
|
80
|
+
|
|
70
81
|
## Custom Domains
|
|
71
82
|
|
|
72
83
|
Add as many domains as you need:
|
package/dist/cli/index.js
CHANGED
|
@@ -2560,9 +2560,9 @@ var source_default = chalk;
|
|
|
2560
2560
|
|
|
2561
2561
|
// src/cli/index.ts
|
|
2562
2562
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
2563
|
-
import { dirname as dirname3, join as
|
|
2563
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
2564
2564
|
import { fileURLToPath } from "url";
|
|
2565
|
-
import { spawnSync as
|
|
2565
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
2566
2566
|
|
|
2567
2567
|
// src/store.ts
|
|
2568
2568
|
import { createHash } from "crypto";
|
|
@@ -3324,6 +3324,54 @@ function runDomains(action, domain, options = {}) {
|
|
|
3324
3324
|
};
|
|
3325
3325
|
}
|
|
3326
3326
|
|
|
3327
|
+
// src/local.ts
|
|
3328
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
3329
|
+
import { homedir as homedir2 } from "os";
|
|
3330
|
+
import { join as join4 } from "path";
|
|
3331
|
+
function createLocalSetupPlan(input) {
|
|
3332
|
+
const domain = normalizeHostname(input.domain);
|
|
3333
|
+
const targetHost = input.targetHost || "127.0.0.1";
|
|
3334
|
+
const port = input.port || 8787;
|
|
3335
|
+
const certDir = input.certDir || join4(homedir2(), ".hasna", "machines", "certs");
|
|
3336
|
+
const certPath = join4(certDir, `${domain}.pem`);
|
|
3337
|
+
const keyPath = join4(certDir, `${domain}-key.pem`);
|
|
3338
|
+
return {
|
|
3339
|
+
domain,
|
|
3340
|
+
targetHost,
|
|
3341
|
+
port,
|
|
3342
|
+
hostsEntry: `${targetHost} ${domain}`,
|
|
3343
|
+
caddySnippet: `${domain} {
|
|
3344
|
+
reverse_proxy ${targetHost}:${port}
|
|
3345
|
+
tls ${certPath} ${keyPath}
|
|
3346
|
+
}`,
|
|
3347
|
+
certPath,
|
|
3348
|
+
keyPath,
|
|
3349
|
+
machinesCommand: `machines dns add --domain ${domain} --target-host ${targetHost} --port ${port} --json`,
|
|
3350
|
+
sudoRequired: true
|
|
3351
|
+
};
|
|
3352
|
+
}
|
|
3353
|
+
function registerMachinesDns(input) {
|
|
3354
|
+
const plan = createLocalSetupPlan(input);
|
|
3355
|
+
const args = [
|
|
3356
|
+
"dns",
|
|
3357
|
+
"add",
|
|
3358
|
+
"--domain",
|
|
3359
|
+
plan.domain,
|
|
3360
|
+
"--target-host",
|
|
3361
|
+
plan.targetHost,
|
|
3362
|
+
"--port",
|
|
3363
|
+
String(plan.port),
|
|
3364
|
+
"--json"
|
|
3365
|
+
];
|
|
3366
|
+
const result = spawnSync2("machines", args, { encoding: "utf-8" });
|
|
3367
|
+
return {
|
|
3368
|
+
command: `machines ${args.join(" ")}`,
|
|
3369
|
+
status: result.status,
|
|
3370
|
+
stdout: result.stdout || "",
|
|
3371
|
+
stderr: result.stderr || ""
|
|
3372
|
+
};
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3327
3375
|
// src/pg-migrations.ts
|
|
3328
3376
|
var PG_MIGRATIONS = [
|
|
3329
3377
|
`
|
|
@@ -3400,7 +3448,7 @@ var PG_MIGRATIONS = [
|
|
|
3400
3448
|
// src/cli/index.ts
|
|
3401
3449
|
function getPackageVersion() {
|
|
3402
3450
|
try {
|
|
3403
|
-
const pkgPath =
|
|
3451
|
+
const pkgPath = join5(dirname3(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
3404
3452
|
return JSON.parse(readFileSync3(pkgPath, "utf-8")).version || "0.0.0";
|
|
3405
3453
|
} catch {
|
|
3406
3454
|
return "0.0.0";
|
|
@@ -3447,7 +3495,7 @@ function formatLink(link) {
|
|
|
3447
3495
|
return `${source_default.green(link.short_url || `${link.hostname}/${link.slug}`)} ${source_default.dim("->")} ${link.destination_url}`;
|
|
3448
3496
|
}
|
|
3449
3497
|
function commandExists(command) {
|
|
3450
|
-
const result =
|
|
3498
|
+
const result = spawnSync3("which", [command], { encoding: "utf-8" });
|
|
3451
3499
|
return result.status === 0;
|
|
3452
3500
|
}
|
|
3453
3501
|
program2.name("shortlinks").description("CLI-only shortlink manager with custom domains, click tracking, Cloudflare helpers, and cloud sync").version(getPackageVersion()).option("--db <path>", "SQLite database path").option("-j, --json", "Output JSON for agents and scripts");
|
|
@@ -3826,6 +3874,42 @@ cloudCmd.command("status").description("Show local and cloud configuration healt
|
|
|
3826
3874
|
handleError(error);
|
|
3827
3875
|
}
|
|
3828
3876
|
});
|
|
3877
|
+
var localCmd = program2.command("local").description("Local domain setup helpers");
|
|
3878
|
+
localCmd.command("plan <domain>").description("Render hosts and reverse-proxy setup for a local shortlink domain").option("--port <port>", "Local redirect server port", "8787").option("--target-host <host>", "Local target host", "127.0.0.1").option("-j, --json", "Output JSON").action((domain, opts) => {
|
|
3879
|
+
try {
|
|
3880
|
+
const plan = createLocalSetupPlan({
|
|
3881
|
+
domain,
|
|
3882
|
+
port: Number(opts.port),
|
|
3883
|
+
targetHost: opts.targetHost
|
|
3884
|
+
});
|
|
3885
|
+
print(plan, opts, () => console.log(JSON.stringify(plan, null, 2)));
|
|
3886
|
+
} catch (error) {
|
|
3887
|
+
handleError(error);
|
|
3888
|
+
}
|
|
3889
|
+
});
|
|
3890
|
+
localCmd.command("setup <domain>").description("Record local domain mapping with machines and print remaining sudo-only setup").option("--port <port>", "Local redirect server port", "8787").option("--target-host <host>", "Local target host", "127.0.0.1").option("--skip-machines", "Do not call machines dns add").option("-j, --json", "Output JSON").action((domain, opts) => {
|
|
3891
|
+
try {
|
|
3892
|
+
const plan = createLocalSetupPlan({
|
|
3893
|
+
domain,
|
|
3894
|
+
port: Number(opts.port),
|
|
3895
|
+
targetHost: opts.targetHost
|
|
3896
|
+
});
|
|
3897
|
+
const machines = opts.skipMachines ? null : registerMachinesDns({
|
|
3898
|
+
domain,
|
|
3899
|
+
port: Number(opts.port),
|
|
3900
|
+
targetHost: opts.targetHost
|
|
3901
|
+
});
|
|
3902
|
+
const result = { plan, machines };
|
|
3903
|
+
print(result, opts, () => {
|
|
3904
|
+
if (machines && machines.status !== 0) {
|
|
3905
|
+
console.error(source_default.yellow(machines.stderr.trim() || "machines dns add failed"));
|
|
3906
|
+
}
|
|
3907
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3908
|
+
});
|
|
3909
|
+
} catch (error) {
|
|
3910
|
+
handleError(error);
|
|
3911
|
+
}
|
|
3912
|
+
});
|
|
3829
3913
|
program2.command("doctor").description("Check local shortlinks tooling and integration readiness").option("-j, --json", "Output JSON").action((opts) => {
|
|
3830
3914
|
try {
|
|
3831
3915
|
const stats = withStore((store) => store.totalStats());
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { ShortlinksDatabase, SQLITE_MIGRATIONS, makeId, now } from "./database.j
|
|
|
2
2
|
export { ShortlinksStore } from "./store.js";
|
|
3
3
|
export { createShortlinksHandler, serveShortlinks } from "./server.js";
|
|
4
4
|
export { createCloudflarePlan, generateWorkerScript, writeWorkerFiles, upsertCloudflareDnsRecord } from "./cloudflare.js";
|
|
5
|
+
export { createLocalSetupPlan, registerMachinesDns } from "./local.js";
|
|
5
6
|
export { PG_MIGRATIONS } from "./pg-migrations.js";
|
|
6
7
|
export { formatShortUrl, getConfigPath, getDataDir, getDatabasePath, loadConfig, normalizeHostname, saveConfig } from "./config.js";
|
|
7
8
|
export { normalizeSlug, randomToken } from "./slug.js";
|
package/dist/index.js
CHANGED
|
@@ -728,6 +728,53 @@ async function upsertCloudflareDnsRecord(options) {
|
|
|
728
728
|
});
|
|
729
729
|
return { id: created.id, action: "created" };
|
|
730
730
|
}
|
|
731
|
+
// src/local.ts
|
|
732
|
+
import { spawnSync } from "child_process";
|
|
733
|
+
import { homedir as homedir2 } from "os";
|
|
734
|
+
import { join as join4 } from "path";
|
|
735
|
+
function createLocalSetupPlan(input) {
|
|
736
|
+
const domain = normalizeHostname(input.domain);
|
|
737
|
+
const targetHost = input.targetHost || "127.0.0.1";
|
|
738
|
+
const port = input.port || 8787;
|
|
739
|
+
const certDir = input.certDir || join4(homedir2(), ".hasna", "machines", "certs");
|
|
740
|
+
const certPath = join4(certDir, `${domain}.pem`);
|
|
741
|
+
const keyPath = join4(certDir, `${domain}-key.pem`);
|
|
742
|
+
return {
|
|
743
|
+
domain,
|
|
744
|
+
targetHost,
|
|
745
|
+
port,
|
|
746
|
+
hostsEntry: `${targetHost} ${domain}`,
|
|
747
|
+
caddySnippet: `${domain} {
|
|
748
|
+
reverse_proxy ${targetHost}:${port}
|
|
749
|
+
tls ${certPath} ${keyPath}
|
|
750
|
+
}`,
|
|
751
|
+
certPath,
|
|
752
|
+
keyPath,
|
|
753
|
+
machinesCommand: `machines dns add --domain ${domain} --target-host ${targetHost} --port ${port} --json`,
|
|
754
|
+
sudoRequired: true
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function registerMachinesDns(input) {
|
|
758
|
+
const plan = createLocalSetupPlan(input);
|
|
759
|
+
const args = [
|
|
760
|
+
"dns",
|
|
761
|
+
"add",
|
|
762
|
+
"--domain",
|
|
763
|
+
plan.domain,
|
|
764
|
+
"--target-host",
|
|
765
|
+
plan.targetHost,
|
|
766
|
+
"--port",
|
|
767
|
+
String(plan.port),
|
|
768
|
+
"--json"
|
|
769
|
+
];
|
|
770
|
+
const result = spawnSync("machines", args, { encoding: "utf-8" });
|
|
771
|
+
return {
|
|
772
|
+
command: `machines ${args.join(" ")}`,
|
|
773
|
+
status: result.status,
|
|
774
|
+
stdout: result.stdout || "",
|
|
775
|
+
stderr: result.stderr || ""
|
|
776
|
+
};
|
|
777
|
+
}
|
|
731
778
|
// src/pg-migrations.ts
|
|
732
779
|
var PG_MIGRATIONS = [
|
|
733
780
|
`
|
|
@@ -805,6 +852,7 @@ export {
|
|
|
805
852
|
upsertCloudflareDnsRecord,
|
|
806
853
|
serveShortlinks,
|
|
807
854
|
saveConfig,
|
|
855
|
+
registerMachinesDns,
|
|
808
856
|
randomToken,
|
|
809
857
|
now,
|
|
810
858
|
normalizeSlug,
|
|
@@ -817,6 +865,7 @@ export {
|
|
|
817
865
|
generateWorkerScript,
|
|
818
866
|
formatShortUrl,
|
|
819
867
|
createShortlinksHandler,
|
|
868
|
+
createLocalSetupPlan,
|
|
820
869
|
createCloudflarePlan,
|
|
821
870
|
ShortlinksStore,
|
|
822
871
|
ShortlinksDatabase,
|
package/dist/local.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface LocalSetupPlan {
|
|
2
|
+
domain: string;
|
|
3
|
+
targetHost: string;
|
|
4
|
+
port: number;
|
|
5
|
+
hostsEntry: string;
|
|
6
|
+
caddySnippet: string;
|
|
7
|
+
certPath: string;
|
|
8
|
+
keyPath: string;
|
|
9
|
+
machinesCommand: string;
|
|
10
|
+
sudoRequired: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function createLocalSetupPlan(input: {
|
|
13
|
+
domain: string;
|
|
14
|
+
port?: number;
|
|
15
|
+
targetHost?: string;
|
|
16
|
+
certDir?: string;
|
|
17
|
+
}): LocalSetupPlan;
|
|
18
|
+
export declare function registerMachinesDns(input: {
|
|
19
|
+
domain: string;
|
|
20
|
+
port?: number;
|
|
21
|
+
targetHost?: string;
|
|
22
|
+
}): {
|
|
23
|
+
command: string;
|
|
24
|
+
status: number | null;
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
};
|
package/package.json
CHANGED