@dytsou/shorten-url 2.4.4 → 2.4.6

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.
@@ -0,0 +1,122 @@
1
+ import { fetchFrontendAsset, isFrontendAssetPath } from "./assets.js";
2
+ import { createFlagRoutes } from "./flag-routes.js";
3
+ import { createFlagshipAdapter } from "./flagship.js";
4
+ import { endpointsFromFrontend } from "./endpoints.js";
5
+ import { createShortener } from "./shortener.js";
6
+ import { createRuntimeConfig } from "./runtime-config.js";
7
+
8
+ const API_DOC_PATHS = new Set(["/api/", "/api/index.html", "/api/openapi.yaml"]);
9
+ const ABOUT_DOC_PATHS = new Set(["/about", "/about.html"]);
10
+
11
+ async function handleEarlyRequest({ request, requestURL, env, shortener, flagRoutes }) {
12
+ if (request.method === "OPTIONS") {
13
+ return new Response(null, { status: 204, headers: shortener.htmlHeaders() });
14
+ }
15
+
16
+ const settingsResponse = flagRoutes.handleSettings(request, requestURL.pathname);
17
+ if (settingsResponse) return settingsResponse;
18
+
19
+ if (request.method === "POST") {
20
+ return shortener.handleShorten(request, requestURL, {
21
+ apiPath: requestURL.pathname,
22
+ allowedApiPaths: ["/", "/shorten"],
23
+ });
24
+ }
25
+
26
+ if (request.method !== "GET" && request.method !== "HEAD") return null;
27
+ if (requestURL.pathname === "/api") {
28
+ return Response.redirect(new URL(`/api/${requestURL.search}`, requestURL), 308);
29
+ }
30
+ if (requestURL.pathname === "/about/") {
31
+ return Response.redirect(new URL(`/about${requestURL.search}`, requestURL), 308);
32
+ }
33
+ if (ABOUT_DOC_PATHS.has(requestURL.pathname)) {
34
+ return fetchFrontendAsset(env, request, "/about");
35
+ }
36
+ if (API_DOC_PATHS.has(requestURL.pathname)) {
37
+ return fetchFrontendAsset(env, request, requestURL.pathname);
38
+ }
39
+ return null;
40
+ }
41
+
42
+ async function handlePageRequest({
43
+ request,
44
+ requestURL,
45
+ env,
46
+ shortener,
47
+ flagRoutes,
48
+ homePath = "/",
49
+ rootResponse,
50
+ }) {
51
+ const path = requestURL.pathname.split("/")[1] || "";
52
+ const variantResponse = await flagRoutes.evaluateShortening(request, requestURL.pathname, () =>
53
+ fetchFrontendAsset(env, request, "/")
54
+ );
55
+ if (variantResponse) return variantResponse;
56
+ if (requestURL.pathname === homePath) return fetchFrontendAsset(env, request, "/");
57
+ if (requestURL.pathname === "/" && homePath !== "/" && rootResponse) {
58
+ return rootResponse();
59
+ }
60
+ if (requestURL.pathname === "/favicon.ico") {
61
+ return Response.redirect(new URL("/favicon.svg", request.url), 302);
62
+ }
63
+ if (isFrontendAssetPath(requestURL.pathname)) {
64
+ return fetchFrontendAsset(env, request, requestURL.pathname);
65
+ }
66
+ if (
67
+ requestURL.pathname === "/api" ||
68
+ requestURL.pathname.startsWith("/api/") ||
69
+ requestURL.pathname === "/shorten" ||
70
+ requestURL.pathname.startsWith("/shorten/")
71
+ ) {
72
+ return shortener.jsonResponse({ status: 404, message: "API route not found" }, 404);
73
+ }
74
+ return shortener.handleShortUrlRedirect(path, requestURL.search);
75
+ }
76
+
77
+ /**
78
+ * Compose the shared Worker request handler.
79
+ *
80
+ * Entrypoints provide only configuration overrides; route behavior lives here
81
+ * so the production and example Workers cannot silently diverge.
82
+ */
83
+ export function createWorkerHandler({
84
+ config: fixedConfig,
85
+ configOverrides = {},
86
+ flagshipAdapter,
87
+ verifyToken,
88
+ } = {}) {
89
+ return async function workerHandler(request, env = {}) {
90
+ const config = fixedConfig || createRuntimeConfig(env, configOverrides);
91
+ const requestURL = new URL(request.url);
92
+ const endpoints = endpointsFromFrontend(config.frontend);
93
+ const shortener = createShortener({
94
+ worker: {
95
+ ...config.worker,
96
+ safe_browsing_api_key: env.SAFE_BROWSING_API_KEY ?? config.worker.safe_browsing_api_key,
97
+ },
98
+ endpoints,
99
+ kv: env.LINKS,
100
+ env,
101
+ });
102
+ const flagRoutes = createFlagRoutes({
103
+ prefix: "/settings",
104
+ shorteningPath: "/",
105
+ pageUrl: endpoints.shortenPage,
106
+ shortener,
107
+ env,
108
+ adapter: flagshipAdapter || createFlagshipAdapter(env),
109
+ verifyToken,
110
+ });
111
+
112
+ const earlyResponse = await handleEarlyRequest({
113
+ request,
114
+ requestURL,
115
+ env,
116
+ shortener,
117
+ flagRoutes,
118
+ });
119
+ if (earlyResponse) return earlyResponse;
120
+ return handlePageRequest({ request, requestURL, env, shortener, flagRoutes });
121
+ };
122
+ }
package/src/worker.js CHANGED
@@ -1,69 +1,6 @@
1
- /**
2
- * URL Shortener - Cloudflare Worker
3
- *
4
- * Configuration is imported from `config/config.js`.
5
- * Requires a KV namespace bound as `LINKS`.
6
- *
7
- * Routes:
8
- * OPTIONS * -> CORS preflight
9
- * POST / -> create short URL (protected by Cloudflare Access / WARP)
10
- * GET / -> serve the shortener frontend
11
- * GET /<key> -> redirect a stored short link
12
- */
13
-
14
- import { createShortener } from "./lib/shortener.js";
15
- import { endpointsFromFrontend } from "./lib/endpoints.js";
16
1
  import { withFetchObservability } from "./lib/observability.js";
17
- import config from "../config/config.js";
18
-
19
- function getEndpoints() {
20
- return endpointsFromFrontend(config.frontend);
21
- }
22
-
23
- async function fetchHandler(request, env) {
24
- const workerConfig = {
25
- ...config.worker,
26
- safe_browsing_api_key:
27
- env.SAFE_BROWSING_API_KEY ?? config.worker.safe_browsing_api_key,
28
- };
29
- const shortener = createShortener({
30
- worker: workerConfig,
31
- endpoints: getEndpoints(),
32
- kv: env.LINKS,
33
- });
34
-
35
- const requestURL = new URL(request.url);
36
- const [, path] = requestURL.pathname.split("/");
37
- const params = requestURL.search;
38
-
39
- if (request.method === "OPTIONS") {
40
- return new Response(null, { status: 204, headers: shortener.htmlHeaders() });
41
- }
42
- if (
43
- (request.method === "GET" || request.method === "HEAD") &&
44
- (requestURL.pathname === "/api" || requestURL.pathname.startsWith("/api/"))
45
- ) {
46
- if (
47
- requestURL.pathname !== "/api/" &&
48
- /^\/api(?:\/api)*\/?$/.test(requestURL.pathname)
49
- ) {
50
- return Response.redirect(new URL("/api/", requestURL), 308);
51
- }
2
+ import { createWorkerHandler } from "./lib/worker-handler.js";
52
3
 
53
- const assetURL = new URL(
54
- `${requestURL.pathname.slice(1)}${requestURL.search}`,
55
- config.frontend.url,
56
- );
57
- return fetch(new Request(assetURL, { method: request.method }));
58
- }
59
- if (request.method === "POST") {
60
- return shortener.handleShorten(request, requestURL, {
61
- apiPath: requestURL.pathname,
62
- allowedApiPaths: ["/"],
63
- });
64
- }
65
- if (!path) return shortener.fetchHostedPage(getEndpoints().shortenPage);
66
- return shortener.handleShortUrlRedirect(path, params);
67
- }
4
+ export const workerFetchHandler = createWorkerHandler();
68
5
 
69
- export default withFetchObservability(fetchHandler);
6
+ export default withFetchObservability(workerFetchHandler);