@hasna/shortlinks 0.1.13 → 0.1.14
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/cloudflare/shortlinks.js +18 -11
- package/cloudflare/wrangler.example.toml +1 -0
- package/dist/cli/index.js +26 -13
- package/dist/cloudflare.d.ts +1 -0
- package/dist/cloudflare.js +19 -11
- package/dist/index.js +19 -11
- package/infra/aws-ec2-user-data.sh +21 -0
- package/package.json +1 -1
package/cloudflare/shortlinks.js
CHANGED
|
@@ -6,25 +6,32 @@ export default {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
const incoming = new URL(request.url);
|
|
9
|
+
const proxyTo = (targetOrigin, marker) => {
|
|
10
|
+
const upstream = new URL(incoming.pathname + incoming.search, targetOrigin);
|
|
11
|
+
const headers = new Headers(request.headers);
|
|
12
|
+
headers.set("x-forwarded-host", incoming.host);
|
|
13
|
+
headers.set("x-shortlinks-worker", marker);
|
|
14
|
+
|
|
15
|
+
return fetch(upstream.toString(), {
|
|
16
|
+
method: request.method,
|
|
17
|
+
headers,
|
|
18
|
+
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
19
|
+
redirect: "manual"
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
9
23
|
const reserved = (env.SHORTLINKS_RESERVED_PATH_PREFIXES || "a")
|
|
10
24
|
.split(",")
|
|
11
25
|
.map((value) => value.trim().toLowerCase())
|
|
12
26
|
.filter(Boolean);
|
|
13
27
|
const firstSegment = decodeURIComponent(incoming.pathname.replace(/^\/+/, "").split("/")[0] || "").toLowerCase();
|
|
14
28
|
if (firstSegment && reserved.includes(firstSegment)) {
|
|
29
|
+
if (env.ATTACHMENTS_ORIGIN) {
|
|
30
|
+
return proxyTo(env.ATTACHMENTS_ORIGIN, "attachments");
|
|
31
|
+
}
|
|
15
32
|
return new Response("Reserved path prefix", { status: 404 });
|
|
16
33
|
}
|
|
17
34
|
|
|
18
|
-
|
|
19
|
-
const headers = new Headers(request.headers);
|
|
20
|
-
headers.set("x-forwarded-host", incoming.host);
|
|
21
|
-
headers.set("x-shortlinks-worker", "cloudflare");
|
|
22
|
-
|
|
23
|
-
return fetch(upstream.toString(), {
|
|
24
|
-
method: request.method,
|
|
25
|
-
headers,
|
|
26
|
-
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
27
|
-
redirect: "manual"
|
|
28
|
-
});
|
|
35
|
+
return proxyTo(origin, "cloudflare");
|
|
29
36
|
}
|
|
30
37
|
};
|
package/dist/cli/index.js
CHANGED
|
@@ -9585,26 +9585,33 @@ function generateWorkerScript() {
|
|
|
9585
9585
|
}
|
|
9586
9586
|
|
|
9587
9587
|
const incoming = new URL(request.url);
|
|
9588
|
+
const proxyTo = (targetOrigin, marker) => {
|
|
9589
|
+
const upstream = new URL(incoming.pathname + incoming.search, targetOrigin);
|
|
9590
|
+
const headers = new Headers(request.headers);
|
|
9591
|
+
headers.set("x-forwarded-host", incoming.host);
|
|
9592
|
+
headers.set("x-shortlinks-worker", marker);
|
|
9593
|
+
|
|
9594
|
+
return fetch(upstream.toString(), {
|
|
9595
|
+
method: request.method,
|
|
9596
|
+
headers,
|
|
9597
|
+
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
9598
|
+
redirect: "manual"
|
|
9599
|
+
});
|
|
9600
|
+
};
|
|
9601
|
+
|
|
9588
9602
|
const reserved = (env.SHORTLINKS_RESERVED_PATH_PREFIXES || "a")
|
|
9589
9603
|
.split(",")
|
|
9590
9604
|
.map((value) => value.trim().toLowerCase())
|
|
9591
9605
|
.filter(Boolean);
|
|
9592
9606
|
const firstSegment = decodeURIComponent(incoming.pathname.replace(/^\\/+/, "").split("/")[0] || "").toLowerCase();
|
|
9593
9607
|
if (firstSegment && reserved.includes(firstSegment)) {
|
|
9608
|
+
if (env.ATTACHMENTS_ORIGIN) {
|
|
9609
|
+
return proxyTo(env.ATTACHMENTS_ORIGIN, "attachments");
|
|
9610
|
+
}
|
|
9594
9611
|
return new Response("Reserved path prefix", { status: 404 });
|
|
9595
9612
|
}
|
|
9596
9613
|
|
|
9597
|
-
|
|
9598
|
-
const headers = new Headers(request.headers);
|
|
9599
|
-
headers.set("x-forwarded-host", incoming.host);
|
|
9600
|
-
headers.set("x-shortlinks-worker", "cloudflare");
|
|
9601
|
-
|
|
9602
|
-
return fetch(upstream.toString(), {
|
|
9603
|
-
method: request.method,
|
|
9604
|
-
headers,
|
|
9605
|
-
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
9606
|
-
redirect: "manual"
|
|
9607
|
-
});
|
|
9614
|
+
return proxyTo(origin, "cloudflare");
|
|
9608
9615
|
}
|
|
9609
9616
|
};
|
|
9610
9617
|
`;
|
|
@@ -9622,6 +9629,7 @@ compatibility_date = "2026-05-01"
|
|
|
9622
9629
|
|
|
9623
9630
|
[vars]
|
|
9624
9631
|
SHORTLINKS_ORIGIN = "${options.origin || "https://shortlinks.example.com"}"
|
|
9632
|
+
ATTACHMENTS_ORIGIN = "${options.attachmentsOrigin || ""}"
|
|
9625
9633
|
SHORTLINKS_RESERVED_PATH_PREFIXES = "a"
|
|
9626
9634
|
`);
|
|
9627
9635
|
return { workerPath, wranglerPath };
|
|
@@ -10134,9 +10142,14 @@ cfCmd.command("plan <hostname>").description("Print the Cloudflare setup plan").
|
|
|
10134
10142
|
handleError(error);
|
|
10135
10143
|
}
|
|
10136
10144
|
});
|
|
10137
|
-
cfCmd.command("worker").description("Write Cloudflare Worker files").option("--out-dir <dir>", "Output directory", "cloudflare").option("--worker <name>", "Worker name", "shortlinks").option("--origin <url>", "Origin redirect server URL", process.env.SHORTLINKS_ORIGIN || "https://shortlinks.example.com").option("-j, --json", "Output JSON").action((opts) => {
|
|
10145
|
+
cfCmd.command("worker").description("Write Cloudflare Worker files").option("--out-dir <dir>", "Output directory", "cloudflare").option("--worker <name>", "Worker name", "shortlinks").option("--origin <url>", "Origin redirect server URL", process.env.SHORTLINKS_ORIGIN || "https://shortlinks.example.com").option("--attachments-origin <url>", "Origin URL for reserved attachment paths", process.env.ATTACHMENTS_ORIGIN).option("-j, --json", "Output JSON").action((opts) => {
|
|
10138
10146
|
try {
|
|
10139
|
-
const result = writeWorkerFiles({
|
|
10147
|
+
const result = writeWorkerFiles({
|
|
10148
|
+
outDir: opts.outDir,
|
|
10149
|
+
workerName: opts.worker,
|
|
10150
|
+
origin: opts.origin,
|
|
10151
|
+
attachmentsOrigin: opts.attachmentsOrigin
|
|
10152
|
+
});
|
|
10140
10153
|
print2(result, opts, () => {
|
|
10141
10154
|
console.log(source_default.green(`Wrote ${result.workerPath}`));
|
|
10142
10155
|
console.log(source_default.green(`Wrote ${result.wranglerPath}`));
|
package/dist/cloudflare.d.ts
CHANGED
package/dist/cloudflare.js
CHANGED
|
@@ -56,26 +56,33 @@ function generateWorkerScript() {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const incoming = new URL(request.url);
|
|
59
|
+
const proxyTo = (targetOrigin, marker) => {
|
|
60
|
+
const upstream = new URL(incoming.pathname + incoming.search, targetOrigin);
|
|
61
|
+
const headers = new Headers(request.headers);
|
|
62
|
+
headers.set("x-forwarded-host", incoming.host);
|
|
63
|
+
headers.set("x-shortlinks-worker", marker);
|
|
64
|
+
|
|
65
|
+
return fetch(upstream.toString(), {
|
|
66
|
+
method: request.method,
|
|
67
|
+
headers,
|
|
68
|
+
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
69
|
+
redirect: "manual"
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
59
73
|
const reserved = (env.SHORTLINKS_RESERVED_PATH_PREFIXES || "a")
|
|
60
74
|
.split(",")
|
|
61
75
|
.map((value) => value.trim().toLowerCase())
|
|
62
76
|
.filter(Boolean);
|
|
63
77
|
const firstSegment = decodeURIComponent(incoming.pathname.replace(/^\\/+/, "").split("/")[0] || "").toLowerCase();
|
|
64
78
|
if (firstSegment && reserved.includes(firstSegment)) {
|
|
79
|
+
if (env.ATTACHMENTS_ORIGIN) {
|
|
80
|
+
return proxyTo(env.ATTACHMENTS_ORIGIN, "attachments");
|
|
81
|
+
}
|
|
65
82
|
return new Response("Reserved path prefix", { status: 404 });
|
|
66
83
|
}
|
|
67
84
|
|
|
68
|
-
|
|
69
|
-
const headers = new Headers(request.headers);
|
|
70
|
-
headers.set("x-forwarded-host", incoming.host);
|
|
71
|
-
headers.set("x-shortlinks-worker", "cloudflare");
|
|
72
|
-
|
|
73
|
-
return fetch(upstream.toString(), {
|
|
74
|
-
method: request.method,
|
|
75
|
-
headers,
|
|
76
|
-
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
77
|
-
redirect: "manual"
|
|
78
|
-
});
|
|
85
|
+
return proxyTo(origin, "cloudflare");
|
|
79
86
|
}
|
|
80
87
|
};
|
|
81
88
|
`;
|
|
@@ -93,6 +100,7 @@ compatibility_date = "2026-05-01"
|
|
|
93
100
|
|
|
94
101
|
[vars]
|
|
95
102
|
SHORTLINKS_ORIGIN = "${options.origin || "https://shortlinks.example.com"}"
|
|
103
|
+
ATTACHMENTS_ORIGIN = "${options.attachmentsOrigin || ""}"
|
|
96
104
|
SHORTLINKS_RESERVED_PATH_PREFIXES = "a"
|
|
97
105
|
`);
|
|
98
106
|
return { workerPath, wranglerPath };
|
package/dist/index.js
CHANGED
|
@@ -6315,26 +6315,33 @@ function generateWorkerScript() {
|
|
|
6315
6315
|
}
|
|
6316
6316
|
|
|
6317
6317
|
const incoming = new URL(request.url);
|
|
6318
|
+
const proxyTo = (targetOrigin, marker) => {
|
|
6319
|
+
const upstream = new URL(incoming.pathname + incoming.search, targetOrigin);
|
|
6320
|
+
const headers = new Headers(request.headers);
|
|
6321
|
+
headers.set("x-forwarded-host", incoming.host);
|
|
6322
|
+
headers.set("x-shortlinks-worker", marker);
|
|
6323
|
+
|
|
6324
|
+
return fetch(upstream.toString(), {
|
|
6325
|
+
method: request.method,
|
|
6326
|
+
headers,
|
|
6327
|
+
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
6328
|
+
redirect: "manual"
|
|
6329
|
+
});
|
|
6330
|
+
};
|
|
6331
|
+
|
|
6318
6332
|
const reserved = (env.SHORTLINKS_RESERVED_PATH_PREFIXES || "a")
|
|
6319
6333
|
.split(",")
|
|
6320
6334
|
.map((value) => value.trim().toLowerCase())
|
|
6321
6335
|
.filter(Boolean);
|
|
6322
6336
|
const firstSegment = decodeURIComponent(incoming.pathname.replace(/^\\/+/, "").split("/")[0] || "").toLowerCase();
|
|
6323
6337
|
if (firstSegment && reserved.includes(firstSegment)) {
|
|
6338
|
+
if (env.ATTACHMENTS_ORIGIN) {
|
|
6339
|
+
return proxyTo(env.ATTACHMENTS_ORIGIN, "attachments");
|
|
6340
|
+
}
|
|
6324
6341
|
return new Response("Reserved path prefix", { status: 404 });
|
|
6325
6342
|
}
|
|
6326
6343
|
|
|
6327
|
-
|
|
6328
|
-
const headers = new Headers(request.headers);
|
|
6329
|
-
headers.set("x-forwarded-host", incoming.host);
|
|
6330
|
-
headers.set("x-shortlinks-worker", "cloudflare");
|
|
6331
|
-
|
|
6332
|
-
return fetch(upstream.toString(), {
|
|
6333
|
-
method: request.method,
|
|
6334
|
-
headers,
|
|
6335
|
-
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
6336
|
-
redirect: "manual"
|
|
6337
|
-
});
|
|
6344
|
+
return proxyTo(origin, "cloudflare");
|
|
6338
6345
|
}
|
|
6339
6346
|
};
|
|
6340
6347
|
`;
|
|
@@ -6352,6 +6359,7 @@ compatibility_date = "2026-05-01"
|
|
|
6352
6359
|
|
|
6353
6360
|
[vars]
|
|
6354
6361
|
SHORTLINKS_ORIGIN = "${options.origin || "https://shortlinks.example.com"}"
|
|
6362
|
+
ATTACHMENTS_ORIGIN = "${options.attachmentsOrigin || ""}"
|
|
6355
6363
|
SHORTLINKS_RESERVED_PATH_PREFIXES = "a"
|
|
6356
6364
|
`);
|
|
6357
6365
|
return { workerPath, wranglerPath };
|
|
@@ -8,6 +8,7 @@ export RDS_SECRET_ID="${RDS_SECRET_ID:-}"
|
|
|
8
8
|
export RDS_HOST="${RDS_HOST:-}"
|
|
9
9
|
export RDS_USERNAME="${RDS_USERNAME:-}"
|
|
10
10
|
export SHORTLINKS_DOMAIN="${SHORTLINKS_DOMAIN:-}"
|
|
11
|
+
export ATTACHMENTS_ORIGIN="${ATTACHMENTS_ORIGIN:-}"
|
|
11
12
|
|
|
12
13
|
: "${RDS_SECRET_ID:?Set RDS_SECRET_ID to the AWS Secrets Manager secret for the PostgreSQL database_url}"
|
|
13
14
|
: "${SHORTLINKS_DOMAIN:?Set SHORTLINKS_DOMAIN to the public host served by Caddy}"
|
|
@@ -145,12 +146,32 @@ WantedBy=multi-user.target
|
|
|
145
146
|
SERVICE
|
|
146
147
|
|
|
147
148
|
install -d /etc/caddy
|
|
149
|
+
if [ -n "${ATTACHMENTS_ORIGIN}" ]; then
|
|
150
|
+
cat > /etc/caddy/Caddyfile <<CADDY
|
|
151
|
+
${SHORTLINKS_DOMAIN} {
|
|
152
|
+
encode zstd gzip
|
|
153
|
+
|
|
154
|
+
handle /a/* {
|
|
155
|
+
reverse_proxy ${ATTACHMENTS_ORIGIN}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
handle /api/* {
|
|
159
|
+
reverse_proxy ${ATTACHMENTS_ORIGIN}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
handle {
|
|
163
|
+
reverse_proxy 127.0.0.1:8787
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
CADDY
|
|
167
|
+
else
|
|
148
168
|
cat > /etc/caddy/Caddyfile <<CADDY
|
|
149
169
|
${SHORTLINKS_DOMAIN} {
|
|
150
170
|
encode zstd gzip
|
|
151
171
|
reverse_proxy 127.0.0.1:8787
|
|
152
172
|
}
|
|
153
173
|
CADDY
|
|
174
|
+
fi
|
|
154
175
|
|
|
155
176
|
systemctl daemon-reload
|
|
156
177
|
systemctl enable shortlinks.service caddy.service
|
package/package.json
CHANGED