diff --git a/client/.babelrc b/client/.babelrc
index 04ccccb..053488d 100644
--- a/client/.babelrc
+++ b/client/.babelrc
@@ -1,5 +1,8 @@
{
- "presets": ["@babel/preset-env"],
+ "presets": [
+ "@babel/preset-env",
+ "@babel/preset-react"
+ ],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
diff --git a/client/jsconfig.json b/client/jsconfig.json
deleted file mode 100644
index 59b3b19..0000000
--- a/client/jsconfig.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "compilerOptions": {
- "baseUrl": ".",
- "paths": {
- "@/*": ["src/*"],
- "@public/*": ["public/*"]
- }
- }
-}
diff --git a/client/package.json b/client/package.json
index 2b57468..117170c 100644
--- a/client/package.json
+++ b/client/package.json
@@ -5,11 +5,11 @@
"description": "tech tools hosted on cloudflare worker",
"main": "index.js",
"scripts": {
- "build": "webpack",
+ "build": "tsc && webpack",
"deploy": "wrangler deploy src/index.js",
"dev": "webpack-dev-server --config webpack-dev.config.js --open",
"prepare": "husky",
- "start": "wrangler dev src/index.js"
+ "start": "wrangler dev src/index.ts"
},
"author": "",
"license": "ISC",
@@ -28,8 +28,13 @@
"@babel/core": "^7.24.8",
"@babel/preset-env": "^7.24.8",
"@babel/preset-react": "^7.24.7",
+ "@babel/preset-typescript": "^7.24.7",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@types/react-router-dom": "^5.3.3",
"autoprefixer": "^10.4.19",
"babel-loader": "^9.1.3",
+ "babel-plugin-module-resolver": "^5.0.2",
"compression-webpack-plugin": "^11.1.0",
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.2",
@@ -43,6 +48,7 @@
"style-loader": "^4.0.0",
"tailwindcss": "^3.4.4",
"terser-webpack-plugin": "^5.3.10",
+ "typescript": "^5.5.3",
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
diff --git a/client/src/@types/html.d.ts b/client/src/@types/html.d.ts
new file mode 100644
index 0000000..ac6187a
--- /dev/null
+++ b/client/src/@types/html.d.ts
@@ -0,0 +1,4 @@
+declare module '*.html' {
+ const content: string;
+ export default content;
+}
diff --git a/client/src/@types/svg.d.ts b/client/src/@types/svg.d.ts
new file mode 100644
index 0000000..f9b7acd
--- /dev/null
+++ b/client/src/@types/svg.d.ts
@@ -0,0 +1,4 @@
+declare module '*.svg' {
+ const content: string;
+ export default content;
+}
diff --git a/client/src/App.js b/client/src/App.js
new file mode 100644
index 0000000..1433e1a
--- /dev/null
+++ b/client/src/App.js
@@ -0,0 +1,9 @@
+import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
+import { NextUIProvider } from "@nextui-org/react";
+import { Outlet } from 'react-router-dom';
+import { Header } from "@/components/Header/Header";
+import { Footer } from "@/components/Footer/Footer";
+const App = () => {
+ return (_jsx("div", { className: "container", children: _jsxs(NextUIProvider, { children: [_jsx(Header, {}), _jsx(Outlet, {}), _jsx(Footer, {})] }) }));
+};
+export default App;
diff --git a/client/src/App.jsx b/client/src/App.tsx
similarity index 100%
rename from client/src/App.jsx
rename to client/src/App.tsx
diff --git a/client/src/components/Footer/Footer.js b/client/src/components/Footer/Footer.js
new file mode 100644
index 0000000..6d17eab
--- /dev/null
+++ b/client/src/components/Footer/Footer.js
@@ -0,0 +1,2 @@
+import { jsx as _jsx } from "react/jsx-runtime";
+export const Footer = () => (_jsx("footer", { style: { marginTop: "100px", padding: "30px" }, className: 'bg-light', children: "Footer component." }));
diff --git a/client/src/components/Footer/Footer.jsx b/client/src/components/Footer/Footer.tsx
similarity index 100%
rename from client/src/components/Footer/Footer.jsx
rename to client/src/components/Footer/Footer.tsx
diff --git a/client/src/components/Header/Header.js b/client/src/components/Header/Header.js
new file mode 100644
index 0000000..4e77db9
--- /dev/null
+++ b/client/src/components/Header/Header.js
@@ -0,0 +1,7 @@
+import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
+import { memo, useMemo } from 'react';
+import logoSrc from "@public/images/cloudflare.svg";
+export const Header = memo(() => {
+ const logo = useMemo(() => logoSrc, []);
+ return (_jsxs("div", { children: [_jsx("nav", { className: "navbar navbar-light bg-light mb-1", children: _jsxs("div", { className: "container-fluid", style: { height: "50px" }, children: [_jsx("span", { className: "navbar-brand h1", children: "Cloudflare React Worker" }), _jsx("span", { style: { position: "relative", right: 0 }, children: _jsx("img", { src: logo, width: "151", height: "50", alt: "logo" }) })] }) }), _jsx("br", {})] }));
+});
diff --git a/client/src/components/Header/Header.jsx b/client/src/components/Header/Header.tsx
similarity index 100%
rename from client/src/components/Header/Header.jsx
rename to client/src/components/Header/Header.tsx
diff --git a/client/src/index.js b/client/src/index.js
index c5faf29..b80b572 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -1,31 +1,25 @@
import { Router } from 'itty-router';
import * as routes from "./routers";
-
const router = Router();
-
const { base64Handler, healthHandler, postHandler, rootHandler, routesAndAssetsHandler } = routes;
-
/**
* Health route
* This route is used to check the health status of the application.
* When a GET request is made to /health, it will be handled by the healthHandler.
*/
router.get('/health', healthHandler);
-
/**
* Test route for base64 encoding
* This route takes a text parameter and returns its base64 encoded version.
* When a GET request is made to /base64/:text, it will be handled by the base64Handler.
*/
router.get("/base64/:text", base64Handler);
-
/**
* Test POST route
* This route is used to handle POST requests for testing purposes.
* When a POST request is made to /post, it will be handled by the postHandler.
*/
router.post("/post", postHandler);
-
/**
* Catch-all route for serving the React app
* This is the last route we define. It will match any route that hasn't been
@@ -34,7 +28,6 @@ router.post("/post", postHandler);
* allowing the client-side routing of React Router to take over.
*/
router.all('*', rootHandler);
-
/**
* Event listener for incoming requests
* All incoming requests to the worker are passed to the router where your routes
@@ -42,5 +35,5 @@ router.all('*', rootHandler);
* assets and routes accordingly.
*/
addEventListener('fetch', event => {
- event.respondWith(routesAndAssetsHandler(event, router));
+ event.respondWith(routesAndAssetsHandler(event, router));
});
diff --git a/client/src/index.ts b/client/src/index.ts
new file mode 100644
index 0000000..3637421
--- /dev/null
+++ b/client/src/index.ts
@@ -0,0 +1,46 @@
+import { Router } from 'itty-router';
+import * as routes from "./routers";
+
+const router = Router();
+
+const { base64Handler, healthHandler, postHandler, rootHandler, routesAndAssetsHandler } = routes;
+
+/**
+ * Health route
+ * This route is used to check the health status of the application.
+ * When a GET request is made to /health, it will be handled by the healthHandler.
+ */
+router.get('/health', healthHandler);
+
+/**
+ * Test route for base64 encoding
+ * This route takes a text parameter and returns its base64 encoded version.
+ * When a GET request is made to /base64/:text, it will be handled by the base64Handler.
+ */
+router.get("/base64/:text", base64Handler);
+
+/**
+ * Test POST route
+ * This route is used to handle POST requests for testing purposes.
+ * When a POST request is made to /post, it will be handled by the postHandler.
+ */
+router.post("/post", postHandler);
+
+/**
+ * Catch-all route for serving the React app
+ * This is the last route we define. It will match any route that hasn't been
+ * defined above, making it useful as a catch-all route.
+ * This ensures that any route not explicitly defined will serve the React app,
+ * allowing the client-side routing of React Router to take over.
+ */
+router.all('*', rootHandler);
+
+/**
+ * Event listener for incoming requests
+ * All incoming requests to the worker are passed to the router where your routes
+ * are called, and the response is sent. The routesAndAssetsHandler will map
+ * assets and routes accordingly.
+ */
+addEventListener('fetch', (event: any) => {
+ event.respondWith(routesAndAssetsHandler(event, router));
+});
diff --git a/client/src/main.js b/client/src/main.js
index 6795e2a..55c903d 100644
--- a/client/src/main.js
+++ b/client/src/main.js
@@ -1,30 +1,27 @@
-import { createRoot } from 'react-dom/client';
-import { createBrowserRouter, RouterProvider } from 'react-router-dom'
-import App from './App';
-import Error from './pages/Error';
-import Home from '@/pages/Home';
-import './globals.css';
-
-const router = createBrowserRouter([
- {
- path: '/',
- element:
Base64 encoding: ${base64}
${JSON.stringify(data, null, 2)}
-`, { - headers: { - "Content-Type": "text/html" - } - }) -}; \ No newline at end of file +`, { + headers: { + "Content-Type": "text/html" + } + }); +}); diff --git a/client/src/routers/encoding/base64/router.ts b/client/src/routers/encoding/base64/router.ts new file mode 100644 index 0000000..ba6949c --- /dev/null +++ b/client/src/routers/encoding/base64/router.ts @@ -0,0 +1,26 @@ +import { Buffer } from 'buffer'; + +export const base64Handler = async ({ params }) => { + // Decode text like "Hello%20world" into "Hello world" + let input = decodeURIComponent(params.text) + + // Construct a buffer from our input + let buffer = Buffer.from(input, "utf8") + + // Serialise the buffer into a base64 string + let base64 = buffer.toString("base64") + + const backendApi = 'https://6wkf624dt4.execute-api.us-east-1.amazonaws.com/prod/'; + const response = await fetch(backendApi) + const data = await response.json() + + // Return the HTML with the string to the client + return new Response(` +Base64 encoding: ${base64}
${JSON.stringify(data, null, 2)}
+`, { + headers: { + "Content-Type": "text/html" + } + }) +}; \ No newline at end of file diff --git a/client/src/routers/handler.js b/client/src/routers/handler.js index 945ef03..08f65c4 100644 --- a/client/src/routers/handler.js +++ b/client/src/routers/handler.js @@ -1,42 +1,46 @@ -import { getAssetFromKV } from "@cloudflare/kv-asset-handler"; - -/** - * Handles requests for both routes and static assets. - * If the request matches certain file types (e.g., .js, .html, .css), - * it serves them directly from KV storage using getAssetFromKV. - * For other requests, it delegates handling to the provided router. - * @param {Event} event - The Cloudflare Worker event object containing the request. - * @param {Router} router - The router object responsible for handling non-static asset requests. - * @returns {Response} A Response object containing the requested asset or routed content. - */ -export const routesAndAssetsHandler = async (event, router) => { - // Extract the request from the event - const request = event.request; - - // Check if the request is for a static asset (e.g., bundle.js, .html, .ico, .svg, .jpg, .png, .css) - if ( - (request.url.includes('bundle') && request.url.endsWith('.js')) - || request.url.endsWith('.html') - || request.url.endsWith('/favicon.ico') - || request.url.endsWith('.svg') - || request.url.endsWith('.jpg') - || request.url.endsWith('.png') - || request.url.endsWith('.css') - ) { - // Serve the bundle.js file from KV storage - try { - // Pass the entire event object to getAssetFromKV - return await getAssetFromKV(event); - } catch (e) { - return new Response(`Bundle not found: ${ e.message }`, { - status: 404, - statusText: 'Not Found', - }); - } - } - - // For any other requests, handle them with the router - return router.handle(request); -}; - -// Note: If getting Uncaught SyntaxError: Unexpected token '<', this is an indicator that the asset might not be mapped properly. \ No newline at end of file +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import { getAssetFromKV } from "@cloudflare/kv-asset-handler"; +/** + * Handles requests for both routes and static assets. + * If the request matches certain file types (e.g., .js, .html, .css), + * it serves them directly from KV storage using getAssetFromKV. + * For other requests, it delegates handling to the provided router. + * @param {Event} event - The Cloudflare Worker event object containing the request. + * @param {Router} router - The router object responsible for handling non-static asset requests. + * @returns {Response} A Response object containing the requested asset or routed content. + */ +export const routesAndAssetsHandler = (event, router) => __awaiter(void 0, void 0, void 0, function* () { + // Extract the request from the event + const request = event.request; + // Check if the request is for a static asset (e.g., bundle.js, .html, .ico, .svg, .jpg, .png, .css) + if ((request.url.includes('bundle') && request.url.endsWith('.js')) + || request.url.endsWith('.html') + || request.url.endsWith('/favicon.ico') + || request.url.endsWith('.svg') + || request.url.endsWith('.jpg') + || request.url.endsWith('.png') + || request.url.endsWith('.css')) { + // Serve the bundle.js file from KV storage + try { + // Pass the entire event object to getAssetFromKV + return yield getAssetFromKV(event); + } + catch (e) { + return new Response(`Bundle not found: ${e.message}`, { + status: 404, + statusText: 'Not Found', + }); + } + } + // For any other requests, handle them with the router + return router.handle(request); +}); +// Note: If getting Uncaught SyntaxError: Unexpected token '<', this is an indicator that the asset might not be mapped properly. diff --git a/client/src/routers/handler.ts b/client/src/routers/handler.ts new file mode 100644 index 0000000..5235c42 --- /dev/null +++ b/client/src/routers/handler.ts @@ -0,0 +1,42 @@ +import { getAssetFromKV } from "@cloudflare/kv-asset-handler"; + +/** + * Handles requests for both routes and static assets. + * If the request matches certain file types (e.g., .js, .html, .css), + * it serves them directly from KV storage using getAssetFromKV. + * For other requests, it delegates handling to the provided router. + * @param {Event} event - The Cloudflare Worker event object containing the request. + * @param {Router} router - The router object responsible for handling non-static asset requests. + * @returns {Response} A Response object containing the requested asset or routed content. + */ +export const routesAndAssetsHandler = async (event, router) => { + // Extract the request from the event + const request = event.request; + + // Check if the request is for a static asset (e.g., bundle.js, .html, .ico, .svg, .jpg, .png, .css) + if ( + (request.url.includes('bundle') && request.url.endsWith('.js')) + || request.url.endsWith('.html') + || request.url.endsWith('/favicon.ico') + || request.url.endsWith('.svg') + || request.url.endsWith('.jpg') + || request.url.endsWith('.png') + || request.url.endsWith('.css') + ) { + // Serve the bundle.js file from KV storage + try { + // Pass the entire event object to getAssetFromKV + return await getAssetFromKV(event); + } catch (e: any) { + return new Response(`Bundle not found: ${e.message}`, { + status: 404, + statusText: 'Not Found', + }); + } + } + + // For any other requests, handle them with the router + return router.handle(request); +}; + +// Note: If getting Uncaught SyntaxError: Unexpected token '<', this is an indicator that the asset might not be mapped properly. \ No newline at end of file diff --git a/client/src/routers/health/router.js b/client/src/routers/health/router.js index 8874958..a52c67d 100644 --- a/client/src/routers/health/router.js +++ b/client/src/routers/health/router.js @@ -1 +1 @@ -export const healthHandler = () => new Response("success"); \ No newline at end of file +export const healthHandler = () => new Response("success"); diff --git a/client/src/routers/health/router.ts b/client/src/routers/health/router.ts new file mode 100644 index 0000000..8874958 --- /dev/null +++ b/client/src/routers/health/router.ts @@ -0,0 +1 @@ +export const healthHandler = () => new Response("success"); \ No newline at end of file diff --git a/client/src/routers/index.js b/client/src/routers/index.js index 0e64c0b..0977edc 100644 --- a/client/src/routers/index.js +++ b/client/src/routers/index.js @@ -1,13 +1,6 @@ -import { base64Handler } from './encoding/base64/router.js'; -import { healthHandler } from './health/router' -import { postHandler } from './post/router.js'; -import { rootHandler } from './root/router.js'; -import { routesAndAssetsHandler } from './handler'; - -export { - base64Handler, - healthHandler, - postHandler, - rootHandler, - routesAndAssetsHandler -} \ No newline at end of file +import { base64Handler } from './encoding/base64/router.js'; +import { healthHandler } from './health/router'; +import { postHandler } from './post/router.js'; +import { rootHandler } from './root/router.js'; +import { routesAndAssetsHandler } from './handler'; +export { base64Handler, healthHandler, postHandler, rootHandler, routesAndAssetsHandler }; diff --git a/client/src/routers/index.ts b/client/src/routers/index.ts new file mode 100644 index 0000000..0e64c0b --- /dev/null +++ b/client/src/routers/index.ts @@ -0,0 +1,13 @@ +import { base64Handler } from './encoding/base64/router.js'; +import { healthHandler } from './health/router' +import { postHandler } from './post/router.js'; +import { rootHandler } from './root/router.js'; +import { routesAndAssetsHandler } from './handler'; + +export { + base64Handler, + healthHandler, + postHandler, + rootHandler, + routesAndAssetsHandler +} \ No newline at end of file diff --git a/client/src/routers/post/router.js b/client/src/routers/post/router.js index 52cc4bd..f7b17b8 100644 --- a/client/src/routers/post/router.js +++ b/client/src/routers/post/router.js @@ -1,30 +1,36 @@ -/* -This shows a different HTTP method, a POST. - -Try send a POST request using curl or another tool. - -Try the below curl command to send JSON: - -$ curl -X POST