@hasna/shortlinks 0.1.1 → 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 CHANGED
@@ -96,7 +96,7 @@ shortlinks cloudflare worker \
96
96
  --origin https://shortlinks.hasna.xyz
97
97
  ```
98
98
 
99
- Upsert DNS when `CLOUDFLARE_API_TOKEN` is available:
99
+ Upsert DNS when `CLOUDFLARE_API_TOKEN` is available. Global API key auth is also supported with `CLOUDFLARE_API_KEY` plus `CLOUDFLARE_EMAIL`.
100
100
 
101
101
  ```bash
102
102
  shortlinks cloudflare dns has.na --target shortlinks.hasna.xyz
package/dist/cli/index.js CHANGED
@@ -3224,11 +3224,25 @@ SHORTLINKS_ORIGIN = "${options.origin || "https://shortlinks.example.com"}"
3224
3224
  `);
3225
3225
  return { workerPath, wranglerPath };
3226
3226
  }
3227
+ function cloudflareAuthHeaders(token) {
3228
+ const apiToken = token || process.env.CLOUDFLARE_API_TOKEN;
3229
+ if (apiToken)
3230
+ return { authorization: `Bearer ${apiToken}` };
3231
+ const apiKey = process.env.CLOUDFLARE_API_KEY;
3232
+ const email = process.env.CLOUDFLARE_EMAIL;
3233
+ if (apiKey && email) {
3234
+ return {
3235
+ "x-auth-key": apiKey,
3236
+ "x-auth-email": email
3237
+ };
3238
+ }
3239
+ throw new Error("Cloudflare auth is required: set CLOUDFLARE_API_TOKEN, or CLOUDFLARE_API_KEY plus CLOUDFLARE_EMAIL.");
3240
+ }
3227
3241
  async function cloudflareRequest(token, path, init = {}) {
3228
3242
  const response = await fetch(`https://api.cloudflare.com/client/v4${path}`, {
3229
3243
  ...init,
3230
3244
  headers: {
3231
- authorization: `Bearer ${token}`,
3245
+ ...cloudflareAuthHeaders(token),
3232
3246
  "content-type": "application/json",
3233
3247
  ...init.headers || {}
3234
3248
  }
@@ -3266,8 +3280,6 @@ async function upsertCloudflareDnsRecord(options) {
3266
3280
  });
3267
3281
  if (options.dryRun)
3268
3282
  return plan;
3269
- if (!token)
3270
- throw new Error("CLOUDFLARE_API_TOKEN is required unless --dry-run is used.");
3271
3283
  const zoneId = options.zoneId || await findCloudflareZoneId(plan.hostname, token);
3272
3284
  const existing = await cloudflareRequest(token, `/zones/${zoneId}/dns_records?type=CNAME&name=${encodeURIComponent(plan.hostname)}`);
3273
3285
  const payload = JSON.stringify(plan.dnsRecord);
@@ -3832,6 +3844,8 @@ program2.command("doctor").description("Check local shortlinks tooling and integ
3832
3844
  },
3833
3845
  environment: {
3834
3846
  cloudflare_api_token_present: Boolean(process.env.CLOUDFLARE_API_TOKEN),
3847
+ cloudflare_api_key_present: Boolean(process.env.CLOUDFLARE_API_KEY),
3848
+ cloudflare_email_present: Boolean(process.env.CLOUDFLARE_EMAIL),
3835
3849
  shortlinks_origin_present: Boolean(process.env.SHORTLINKS_ORIGIN)
3836
3850
  }
3837
3851
  };
@@ -36,7 +36,7 @@ export declare function writeWorkerFiles(options?: {
36
36
  workerPath: string;
37
37
  wranglerPath: string;
38
38
  };
39
- export declare function findCloudflareZoneId(hostname: string, token: string): Promise<string>;
39
+ export declare function findCloudflareZoneId(hostname: string, token?: string): Promise<string>;
40
40
  export declare function upsertCloudflareDnsRecord(options: CloudflareDnsOptions): Promise<CloudflareSetupPlan | {
41
41
  id: string;
42
42
  action: "created" | "updated";
@@ -87,11 +87,25 @@ SHORTLINKS_ORIGIN = "${options.origin || "https://shortlinks.example.com"}"
87
87
  `);
88
88
  return { workerPath, wranglerPath };
89
89
  }
90
+ function cloudflareAuthHeaders(token) {
91
+ const apiToken = token || process.env.CLOUDFLARE_API_TOKEN;
92
+ if (apiToken)
93
+ return { authorization: `Bearer ${apiToken}` };
94
+ const apiKey = process.env.CLOUDFLARE_API_KEY;
95
+ const email = process.env.CLOUDFLARE_EMAIL;
96
+ if (apiKey && email) {
97
+ return {
98
+ "x-auth-key": apiKey,
99
+ "x-auth-email": email
100
+ };
101
+ }
102
+ throw new Error("Cloudflare auth is required: set CLOUDFLARE_API_TOKEN, or CLOUDFLARE_API_KEY plus CLOUDFLARE_EMAIL.");
103
+ }
90
104
  async function cloudflareRequest(token, path, init = {}) {
91
105
  const response = await fetch(`https://api.cloudflare.com/client/v4${path}`, {
92
106
  ...init,
93
107
  headers: {
94
- authorization: `Bearer ${token}`,
108
+ ...cloudflareAuthHeaders(token),
95
109
  "content-type": "application/json",
96
110
  ...init.headers || {}
97
111
  }
@@ -129,8 +143,6 @@ async function upsertCloudflareDnsRecord(options) {
129
143
  });
130
144
  if (options.dryRun)
131
145
  return plan;
132
- if (!token)
133
- throw new Error("CLOUDFLARE_API_TOKEN is required unless --dry-run is used.");
134
146
  const zoneId = options.zoneId || await findCloudflareZoneId(plan.hostname, token);
135
147
  const existing = await cloudflareRequest(token, `/zones/${zoneId}/dns_records?type=CNAME&name=${encodeURIComponent(plan.hostname)}`);
136
148
  const payload = JSON.stringify(plan.dnsRecord);
package/dist/index.js CHANGED
@@ -656,11 +656,25 @@ SHORTLINKS_ORIGIN = "${options.origin || "https://shortlinks.example.com"}"
656
656
  `);
657
657
  return { workerPath, wranglerPath };
658
658
  }
659
+ function cloudflareAuthHeaders(token) {
660
+ const apiToken = token || process.env.CLOUDFLARE_API_TOKEN;
661
+ if (apiToken)
662
+ return { authorization: `Bearer ${apiToken}` };
663
+ const apiKey = process.env.CLOUDFLARE_API_KEY;
664
+ const email = process.env.CLOUDFLARE_EMAIL;
665
+ if (apiKey && email) {
666
+ return {
667
+ "x-auth-key": apiKey,
668
+ "x-auth-email": email
669
+ };
670
+ }
671
+ throw new Error("Cloudflare auth is required: set CLOUDFLARE_API_TOKEN, or CLOUDFLARE_API_KEY plus CLOUDFLARE_EMAIL.");
672
+ }
659
673
  async function cloudflareRequest(token, path, init = {}) {
660
674
  const response = await fetch(`https://api.cloudflare.com/client/v4${path}`, {
661
675
  ...init,
662
676
  headers: {
663
- authorization: `Bearer ${token}`,
677
+ ...cloudflareAuthHeaders(token),
664
678
  "content-type": "application/json",
665
679
  ...init.headers || {}
666
680
  }
@@ -698,8 +712,6 @@ async function upsertCloudflareDnsRecord(options) {
698
712
  });
699
713
  if (options.dryRun)
700
714
  return plan;
701
- if (!token)
702
- throw new Error("CLOUDFLARE_API_TOKEN is required unless --dry-run is used.");
703
715
  const zoneId = options.zoneId || await findCloudflareZoneId(plan.hostname, token);
704
716
  const existing = await cloudflareRequest(token, `/zones/${zoneId}/dns_records?type=CNAME&name=${encodeURIComponent(plan.hostname)}`);
705
717
  const payload = JSON.stringify(plan.dnsRecord);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/shortlinks",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "CLI-only shortlink manager for custom domains, click tracking, Cloudflare setup, and @hasna cloud sync",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",