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: , - errorElement: , - children: [ - { - index: true, - element: , - }, - { - path: '/test', - element:

Testing 1 2 3

, - }, - ], - }, -]); - -// Render your React component instead -const root = createRoot(document.getElementById('root')); -root.render( - -); +import { jsx as _jsx } from "react/jsx-runtime"; +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: _jsx(App, {}), + errorElement: _jsx(Error, {}), + children: [ + { + index: true, + element: _jsx(Home, {}), + }, + { + path: '/test', + element: _jsx("h1", { children: "Testing 1 2 3" }), + }, + ], + }, +]); +// Render your React component instead +const root = createRoot(document.getElementById('root')); +root.render(_jsx(RouterProvider, { router: router })); diff --git a/client/src/main.tsx b/client/src/main.tsx new file mode 100644 index 0000000..c87d6d0 --- /dev/null +++ b/client/src/main.tsx @@ -0,0 +1,31 @@ +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: , + errorElement: , + children: [ + { + index: true, + element: , + }, + { + path: '/test', + element:

Testing 1 2 3

, + }, + ], + }, +]); + +// Render your React component instead +// @ts-ignore +const root = createRoot(document.getElementById('root')); +root.render( + +); diff --git a/client/src/pages/Error.js b/client/src/pages/Error.js new file mode 100644 index 0000000..a47af01 --- /dev/null +++ b/client/src/pages/Error.js @@ -0,0 +1,7 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { Header } from "@/components/Header/Header"; +import { Footer } from "@/components/Footer/Footer"; +const Error = () => { + return (_jsxs("div", { className: "container", children: [_jsx(Header, {}), _jsx("div", { className: "row", children: _jsx("div", { className: "col-12", children: _jsx("h1", { children: "Error!" }) }) }), _jsx(Footer, {})] })); +}; +export default Error; diff --git a/client/src/pages/Error.jsx b/client/src/pages/Error.tsx similarity index 100% rename from client/src/pages/Error.jsx rename to client/src/pages/Error.tsx diff --git a/client/src/pages/Home.js b/client/src/pages/Home.js new file mode 100644 index 0000000..d972324 --- /dev/null +++ b/client/src/pages/Home.js @@ -0,0 +1,9 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState } from 'react'; +import { Button } from "@nextui-org/react"; +const Home = () => { + const [count, setCount] = useState(0); + const tester = "Victor E"; + return (_jsx("div", { className: "row", children: _jsxs("div", { className: "col-12", children: [_jsx("h1", { children: "Hello, Cloudflare Workers!" }), _jsx("br", {}), _jsx("h3", { children: "This is a basic React page deployed on Cloudflare Workers." }), _jsx("br", {}), _jsxs("pre", { children: [_jsx("strong", { children: "Your name:" }), " ", tester] }), _jsxs("p", { children: ["Count: ", count] }), _jsx("br", {}), _jsx(Button, { color: "primary", type: "button", onClick: () => setCount(count + 1), children: "Increase" })] }) })); +}; +export default Home; diff --git a/client/src/pages/Home.jsx b/client/src/pages/Home.tsx similarity index 100% rename from client/src/pages/Home.jsx rename to client/src/pages/Home.tsx diff --git a/client/src/routers/encoding/base64/router.js b/client/src/routers/encoding/base64/router.js index ba6949c..e410d34 100644 --- a/client/src/routers/encoding/base64/router.js +++ b/client/src/routers/encoding/base64/router.js @@ -1,26 +1,30 @@ -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 +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 { Buffer } from 'buffer'; +export const base64Handler = (_a) => __awaiter(void 0, [_a], void 0, function* ({ 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 = yield fetch(backendApi); + const data = yield 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 +`, { + 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 -H "Content-Type: application/json" -d '{"abc": "def"}' -*/ -export const postHandler = async request => { - // Create a base object with some fields. - let fields = { - "asn": request.cf.asn, - "colo": request.cf.colo - } - - // If the POST data is JSON then attach it to our response. - if (request.headers.get("Content-Type") === "application/json") { - fields["json"] = await request.json() - } - - // Serialise the JSON to a string. - const returnData = JSON.stringify(fields, null, 2); - - return new Response(returnData, { - headers: { - "Content-Type": "application/json" - } - }) -}; \ 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()); + }); +}; +/* +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 -H "Content-Type: application/json" -d '{"abc": "def"}' +*/ +export const postHandler = (request) => __awaiter(void 0, void 0, void 0, function* () { + // Create a base object with some fields. + let fields = { + "asn": request.cf.asn, + "colo": request.cf.colo + }; + // If the POST data is JSON then attach it to our response. + if (request.headers.get("Content-Type") === "application/json") { + fields["json"] = yield request.json(); + } + // Serialise the JSON to a string. + const returnData = JSON.stringify(fields, null, 2); + return new Response(returnData, { + headers: { + "Content-Type": "application/json" + } + }); +}); diff --git a/client/src/routers/post/router.ts b/client/src/routers/post/router.ts new file mode 100644 index 0000000..52cc4bd --- /dev/null +++ b/client/src/routers/post/router.ts @@ -0,0 +1,30 @@ +/* +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 -H "Content-Type: application/json" -d '{"abc": "def"}' +*/ +export const postHandler = async request => { + // Create a base object with some fields. + let fields = { + "asn": request.cf.asn, + "colo": request.cf.colo + } + + // If the POST data is JSON then attach it to our response. + if (request.headers.get("Content-Type") === "application/json") { + fields["json"] = await request.json() + } + + // Serialise the JSON to a string. + const returnData = JSON.stringify(fields, null, 2); + + return new Response(returnData, { + headers: { + "Content-Type": "application/json" + } + }) +}; \ No newline at end of file diff --git a/client/src/routers/root/router.js b/client/src/routers/root/router.js index bddfb5e..01ac1af 100644 --- a/client/src/routers/root/router.js +++ b/client/src/routers/root/router.js @@ -1,13 +1,21 @@ -import indexHTML from '../../../dist/index.html'; - -/** - * Handles requests to serve the index.html file. - * Uses the indexHTML imported from the public directory. - */ -export const rootHandler = async (request) => { - return new Response(indexHTML, { - headers: { - 'Content-Type': 'text/html', - }, - }); -}; +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 indexHTML from '../../../dist/index.html'; +/** + * Handles requests to serve the index.html file. + * Uses the indexHTML imported from the public directory. + */ +export const rootHandler = (request) => __awaiter(void 0, void 0, void 0, function* () { + return new Response(indexHTML, { + headers: { + 'Content-Type': 'text/html', + }, + }); +}); diff --git a/client/src/routers/root/router.ts b/client/src/routers/root/router.ts new file mode 100644 index 0000000..bddfb5e --- /dev/null +++ b/client/src/routers/root/router.ts @@ -0,0 +1,13 @@ +import indexHTML from '../../../dist/index.html'; + +/** + * Handles requests to serve the index.html file. + * Uses the indexHTML imported from the public directory. + */ +export const rootHandler = async (request) => { + return new Response(indexHTML, { + headers: { + 'Content-Type': 'text/html', + }, + }); +}; diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..4c546dd --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,45 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noImplicitAny": false, + "noEmit": false, + "outDir": "dist", + "esModuleInterop": true, + "module": "commonjs", + "target": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./src/*" + ], + "@public/*": [ + "./public/*" + ] + } + }, + "include": [ + "**/*.ts", + "**/*.tsx", + "src/@types/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/client/webpack.config.js b/client/webpack.config.js index 3bb6296..9534300 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -7,7 +7,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { mode: 'production', - entry: './src/main.js', + entry: './src/main.tsx', output: { filename: 'bundle.[contenthash:8].js', // Use content hash for cache busting path: path.resolve(__dirname, 'dist'), @@ -17,7 +17,7 @@ module.exports = { module: { rules: [ { - test: /\.(js|jsx)$/, + test: /\.(ts|tsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', @@ -59,7 +59,7 @@ module.exports = { new CompressionPlugin() ], resolve: { - extensions: ['.js', '.jsx'], + extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { '@': path.resolve(__dirname, 'src'), // Alias for src folder '@public': path.resolve(__dirname, 'public'), // Alias for public folder