From 53960e51c770aa823e4d65974e80adb151da6db2 Mon Sep 17 00:00:00 2001 From: Jose Valerio Date: Fri, 5 Jul 2024 18:55:26 -0400 Subject: [PATCH 1/7] stuff --- services/web/app/components/pricing.tsx | 173 +++++++++++++----------- services/web/app/root.tsx | 97 +++++++++++++ services/web/app/routes/_index.tsx | 24 ++-- services/web/package-lock.json | 105 ++++++++++++++ services/web/package.json | 3 + 5 files changed, 316 insertions(+), 86 deletions(-) diff --git a/services/web/app/components/pricing.tsx b/services/web/app/components/pricing.tsx index 6074dfb..92648a5 100644 --- a/services/web/app/components/pricing.tsx +++ b/services/web/app/components/pricing.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import { Radio, RadioGroup } from "@headlessui/react"; import { CheckIcon } from "@heroicons/react/20/solid"; +import { Form, useNavigation } from "@remix-run/react"; const frequencies = [ { value: "monthly", label: "Monthly", priceSuffix: "/month" }, @@ -27,7 +28,7 @@ const tiers = [ "Complimentary block on Twitter by the AverageDB CEO", ], featured: false, - cta: "Get API key (coming soon)", + cta: "Get API key", }, { name: ( @@ -49,7 +50,7 @@ const tiers = [ "ACID compliance available as a paid addon", ], featured: false, - cta: "Get API key (coming soon)", + cta: "Get API key", }, { name: "Enterprise", @@ -67,7 +68,7 @@ const tiers = [ "Backups", ], featured: true, - cta: "Get API key (coming soon)", + cta: "Get API key", }, ]; @@ -77,6 +78,7 @@ function classNames(...classes) { export const Pricing = () => { const [frequency, setFrequency] = useState(frequencies[0]); + const navigation = useNavigation(); return (
{
- {tiers.map((tier) => ( -
-

- {tier.name} -

-

{ + const loading = + navigation.state === "submitting" && + navigation?.formData?.get("gibs") === tier.id; + + return ( +

- {tier.description} -

-

- - {typeof tier.price === "string" - ? tier.price - : tier.price[frequency.value]} - - - {tier?.hideFrequency ? null : typeof tier.price !== "string" ? ( + {tier.name} + +

+ {tier.description} +

+

- {frequency.priceSuffix} + {typeof tier.price === "string" + ? tier.price + : tier.price[frequency.value]} + + + {tier?.hideFrequency ? null : typeof tier.price !== + "string" ? ( + + {frequency.priceSuffix} + + ) : null} +

+ {tier.priceSubtext ? ( + + {tier.priceSubtext} ) : null} -

- {tier.priceSubtext ? ( - - {tier.priceSubtext} - - ) : null} - e.preventDefault()} - aria-describedby={tier.id} - className={classNames( - tier.featured - ? "bg-white/10 text-white hover:bg-white/20 focus-visible:outline-white" - : "bg-indigo-600 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline-indigo-600", - "mt-6 hover:cursor-not-allowed block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" - )} - > - {tier.cta} - -
    - {tier.features.map((feature) => ( -
  • - +
    + +
  • - ))} -
-
- ))} + > + {loading ? "Creating... (fake delay)" : tier.cta} + + +
+ +
+ ); + })} {/*
diff --git a/services/web/app/root.tsx b/services/web/app/root.tsx index b83417d..9891c9f 100644 --- a/services/web/app/root.tsx +++ b/services/web/app/root.tsx @@ -5,6 +5,7 @@ import { Outlet, Scripts, ScrollRestoration, + useActionData, useNavigate, useRouteError, } from "@remix-run/react"; @@ -13,12 +14,108 @@ import { import stylesheet from "~/tailwind.css?url"; import { LinksFunction } from "@remix-run/node"; import { ArrowLeftCircleIcon } from "@heroicons/react/20/solid"; +import { useEffect } from "react"; +import toast from "react-hot-toast"; +import axios from "axios"; +import { useClipboard } from "@mantine/hooks"; export const links: LinksFunction = () => [ { rel: "stylesheet", href: stylesheet }, ]; +export const action = async ({ request }) => { + const body = await request.formData(); + console.log("body", body); + console.log("body.entries", Object.fromEntries(body)); + const { _action, ...values } = Object.fromEntries(body); + + console.log("values", values); + console.log("action", _action); + try { + // Random number between 200 and 2000 + const random = Math.floor(Math.random() * 1800) + 200; + + await new Promise((resolve) => setTimeout(resolve, random)); + const key = await axios.post("http://0.0.0.0:80/gibs-key", {}); + console.log("success", key.data); + // console.log("key", key); + return key.data; + } catch (error) { + console.error("error", error); + return null; + } +}; + +const ToastContent = ({ apiKey, sponsorMessage }) => { + const clipboard = useClipboard({ timeout: 3000 }); + + return ( +
+
+
+ Your API key has been created! +
+

+ {clipboard.copied ? `Copied! ✅` : "Click to copy ➡️"} + + + + View Docs + +

+
+
+

Sponsor:

+

{sponsorMessage}

+
+
+ ); +}; + export function Layout({ children }: { children: React.ReactNode }) { + const data = useActionData(); + + useEffect(() => { + if (data?.api_key) { + toast.custom( + (t) => ( + + ), + { + duration: 5000, // Adjust as needed + } + ); + } + }, [data]); + + // useEffect(() => { + // if (data?.api_key) { + // console.log("api_key", data.api_key); + // } + // }, [data?.api_key]); + + // if (data?.api_key && !toastRef.current) { + // toastRef.current = true; + // // console.log(navigation.formData.get("gibs")); + // toast.success(`Successfully toasted! -`); + // toastRef.current = false; + // } + return ( diff --git a/services/web/app/routes/_index.tsx b/services/web/app/routes/_index.tsx index 14aa0e7..e8657e7 100644 --- a/services/web/app/routes/_index.tsx +++ b/services/web/app/routes/_index.tsx @@ -1,4 +1,6 @@ -import type { MetaFunction } from "@remix-run/node"; +import { json, type MetaFunction } from "@remix-run/node"; +import axios from "axios"; +import { Toaster } from "react-hot-toast"; import { BarCharts } from "~/components/benchmarks"; import { Features } from "~/components/features"; import Footer from "~/components/footer"; @@ -18,13 +20,17 @@ export const meta: MetaFunction = () => { export default function Index() { return ( -
- - - - - -
-
+ <> + + +
+ + + + + +
+
+ ); } diff --git a/services/web/package-lock.json b/services/web/package-lock.json index 9f1d0fb..903f2ce 100644 --- a/services/web/package-lock.json +++ b/services/web/package-lock.json @@ -8,16 +8,19 @@ "@headlessui/react": "^2.0.4", "@heroicons/react": "^2.1.3", "@hono/node-server": "^1.11.2", + "@mantine/hooks": "^7.11.1", "@material-tailwind/react": "^2.1.9", "@remix-run/node": "^2.9.2", "@remix-run/react": "^2.9.2", "@remix-run/serve": "^2.9.2", + "axios": "^1.7.2", "cross-env": "^7.0.3", "dotenv": "^16.4.5", "isbot": "^4.1.0", "react": "^18.2.0", "react-apexcharts": "^1.4.1", "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", "recharts": "^2.12.7", "remix-hono": "^0.0.16", "tw-elements-react": "^1.0.0-alpha-end" @@ -1417,6 +1420,14 @@ "integrity": "sha512-Lg3PnLp0QXpxwLIAuuJboLeRaIhrgJjeuh797QADg3xz8wGLugQOS5DpsE8A6i6Adgzf+bacllkKZG3J0tGfDw==", "dev": true }, + "node_modules/@mantine/hooks": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.11.1.tgz", + "integrity": "sha512-28WS/U6QL4jaIHf1uFpny5Tglu9MoyyM4bWLmIcAQHtOD3YHpuNvs9OTWLqKAQs6VN+kydlxvjvT+w1LBWEpQg==", + "peerDependencies": { + "react": "^18.2.0" + } + }, "node_modules/@material-tailwind/react": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/@material-tailwind/react/-/react-2.1.9.tgz", @@ -3128,6 +3139,11 @@ "astring": "bin/astring" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.19", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", @@ -3188,6 +3204,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -3689,6 +3715,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -4194,6 +4231,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -5547,6 +5592,25 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -5583,6 +5647,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -5907,6 +5984,14 @@ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", "dev": true }, + "node_modules/goober": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz", + "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -9263,6 +9348,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -9397,6 +9487,21 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/services/web/package.json b/services/web/package.json index 3ea47a2..eae13ed 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -14,16 +14,19 @@ "@headlessui/react": "^2.0.4", "@heroicons/react": "^2.1.3", "@hono/node-server": "^1.11.2", + "@mantine/hooks": "^7.11.1", "@material-tailwind/react": "^2.1.9", "@remix-run/node": "^2.9.2", "@remix-run/react": "^2.9.2", "@remix-run/serve": "^2.9.2", + "axios": "^1.7.2", "cross-env": "^7.0.3", "dotenv": "^16.4.5", "isbot": "^4.1.0", "react": "^18.2.0", "react-apexcharts": "^1.4.1", "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", "recharts": "^2.12.7", "remix-hono": "^0.0.16", "tw-elements-react": "^1.0.0-alpha-end" From 439ec937aa43e256aa8df29854aee504ee982802 Mon Sep 17 00:00:00 2001 From: Jose Valerio Date: Fri, 5 Jul 2024 19:24:06 -0400 Subject: [PATCH 2/7] tests --- services/web/app/routes/docs._index.tsx | 68 +++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/services/web/app/routes/docs._index.tsx b/services/web/app/routes/docs._index.tsx index bb270c5..f39932f 100644 --- a/services/web/app/routes/docs._index.tsx +++ b/services/web/app/routes/docs._index.tsx @@ -1,8 +1,68 @@ -export default function Docs() { +const faqs = [ + { + id: 1, + endpoint: ( +
+

POST

+

+ /SECRET_INTERNAL_ENDPOINT_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_add_item +

+
+ ), + text: ( +

+ Send a JSON body like {`{"data": "your-crap-here"}`} +

+ ), + }, + { + id: 2, + endpoint: ( +
+

POST

+

/gibs-item

+
+ ), + text: ( +

+ Pass a query param of ?key=YOUR_ITEM_KEY +

+ ), + }, +]; + +export default function Example() { return ( -
-

TODO have the community fill this out

- TODO +
+
+
+

+ How to use the API +

+

+ Send a POST request to the following base URL +

+ + https://averagedatabase.com/api + + with the header{" "} + x-averagedb-api-key and + the value being your API key. Endpoints are to the right (or below on + mobile sorry idk how to do that yet) +
+
+ {faqs.map((faq) => ( +
+

+ {faq.endpoint} +

+

+ {faq.text} +

+
+ ))} +
+
); } From dc71a8f80e9104ffd471c9b11680a561c1931b49 Mon Sep 17 00:00:00 2001 From: Jose Valerio Date: Fri, 5 Jul 2024 19:48:57 -0400 Subject: [PATCH 3/7] yuge --- services/web/app/components/pricing.tsx | 12 ++-- services/web/app/root.tsx | 27 ++----- services/web/app/routes/docs._index.tsx | 93 +++++++++++++++---------- 3 files changed, 70 insertions(+), 62 deletions(-) diff --git a/services/web/app/components/pricing.tsx b/services/web/app/components/pricing.tsx index 92648a5..117998f 100644 --- a/services/web/app/components/pricing.tsx +++ b/services/web/app/components/pricing.tsx @@ -191,12 +191,12 @@ export const Pricing = () => { className={classNames( tier.featured ? "bg-white/10 text-white hover:bg-white/20 focus-visible:outline-white" - : "bg-indigo-600 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline-indigo-600", - `mt-6 block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ${ - loading - ? "cursor-progress bg-indigo-200 hover:bg-indigo-200" - : "" - }` + : `${ + loading + ? "bg-indigo-200 hover:bg-indigo-200 cursor-progress" + : "hover:bg-indigo-500 bg-indigo-600" + } text-white shadow-sm focus-visible:outline-indigo-600`, + `mt-6 block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 }` )} > {loading ? "Creating... (fake delay)" : tier.cta} diff --git a/services/web/app/root.tsx b/services/web/app/root.tsx index 9891c9f..fe90fb9 100644 --- a/services/web/app/root.tsx +++ b/services/web/app/root.tsx @@ -25,23 +25,17 @@ export const links: LinksFunction = () => [ export const action = async ({ request }) => { const body = await request.formData(); - console.log("body", body); - console.log("body.entries", Object.fromEntries(body)); + const { _action, ...values } = Object.fromEntries(body); - console.log("values", values); - console.log("action", _action); try { // Random number between 200 and 2000 const random = Math.floor(Math.random() * 1800) + 200; await new Promise((resolve) => setTimeout(resolve, random)); - const key = await axios.post("http://0.0.0.0:80/gibs-key", {}); - console.log("success", key.data); - // console.log("key", key); + const key = await axios.post("http://0.0.0.0:80/api/gibs-key", {}); return key.data; } catch (error) { - console.error("error", error); return null; } }; @@ -100,22 +94,13 @@ export function Layout({ children }: { children: React.ReactNode }) { duration: 5000, // Adjust as needed } ); + } else { + toast.error( + "Sorry bruh we failed to get the key. Ping us on twitter idk whats wrong we don't have logs yet" + ); } }, [data]); - // useEffect(() => { - // if (data?.api_key) { - // console.log("api_key", data.api_key); - // } - // }, [data?.api_key]); - - // if (data?.api_key && !toastRef.current) { - // toastRef.current = true; - // // console.log(navigation.formData.get("gibs")); - // toast.success(`Successfully toasted! -`); - // toastRef.current = false; - // } - return ( diff --git a/services/web/app/routes/docs._index.tsx b/services/web/app/routes/docs._index.tsx index f39932f..70fb376 100644 --- a/services/web/app/routes/docs._index.tsx +++ b/services/web/app/routes/docs._index.tsx @@ -1,64 +1,87 @@ const faqs = [ { id: 1, - endpoint: ( -
-

POST

-

- /SECRET_INTERNAL_ENDPOINT_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_add_item -

-
- ), + method: "POST", + color: "bg-orange-500", + endpoint: + "/SECRET_INTERNAL_ENDPOINT_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_add_item", text: (

- Send a JSON body like {`{"data": "your-crap-here"}`} + Send a JSON body like{" "} + + {`{"data": "add your data here"}`} + {" "} + which will give you a response of:

), + response: `{ + "message": "Great success!", + "key": "442104:m0OSzCNyCifpj3mAHGUd", + "brought_to_you_by": "Baskin-Robbins: Satisfy your sweet tooth with Baskin-Robbins' new ice cream flavors." +}`, }, { id: 2, - endpoint: ( -
-

POST

-

/gibs-item

-
- ), + method: "POST", + color: "bg-green-500", + endpoint: "/gibs-item", text: (

- Pass a query param of ?key=YOUR_ITEM_KEY + Pass a query param of{" "} + + ?key=YOUR_ITEM_KEY + + which will give you a response of:

), + response: `{ + "value": "add your data here", + "brought_to_you_by": "KFC: Satisfy your hunger with KFC's new chicken sandwich." +}`, }, ]; export default function Example() { return ( -
-
-
-

+
+
+
+

How to use the API

-

- Send a POST request to the following base URL +

+ Send a POST{" "} + request to the following base URL

- + https://averagedatabase.com/api - with the header{" "} - x-averagedb-api-key and - the value being your API key. Endpoints are to the right (or below on - mobile sorry idk how to do that yet) +

+ with the header{" "} + + x-averagedb-api-key + {" "} + and the value being your API key. Endpoints are to the right (or + below on mobile). +

-
+
{faqs.map((faq) => ( -
-

- {faq.endpoint} -

-

- {faq.text} -

+
+
+ + {faq.method} + + + {faq.endpoint} + +
+

{faq.text}

+ + {faq.response} +
))}
From 6467423c202695287f78e6fda1ab5929789ac5e8 Mon Sep 17 00:00:00 2001 From: Jose Valerio Date: Fri, 5 Jul 2024 19:49:05 -0400 Subject: [PATCH 4/7] yuge --- services/api/Cargo.lock | 77 +++++++++++++++++++++++++++++-------- services/api/Cargo.toml | 1 + services/api/src/main.rs | 82 +++++++++++++++++++++------------------- 3 files changed, 106 insertions(+), 54 deletions(-) diff --git a/services/api/Cargo.lock b/services/api/Cargo.lock index ed72ae2..e3837d6 100644 --- a/services/api/Cargo.lock +++ b/services/api/Cargo.lock @@ -64,6 +64,7 @@ dependencies = [ "serde_json", "tokio", "tower", + "tower-http", "tracing-subscriber", ] @@ -94,8 +95,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", "hyper", "hyper-util", @@ -127,8 +128,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -156,9 +157,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" @@ -285,6 +286,17 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -296,6 +308,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -303,7 +326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -314,11 +337,17 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + [[package]] name = "httparse" version = "1.9.4" @@ -340,8 +369,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -358,8 +387,8 @@ checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "hyper", "pin-project-lite", "tokio", @@ -645,9 +674,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags", ] @@ -842,6 +871,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" diff --git a/services/api/Cargo.toml b/services/api/Cargo.toml index 2a7e26f..89806de 100644 --- a/services/api/Cargo.toml +++ b/services/api/Cargo.toml @@ -16,4 +16,5 @@ serde_derive = "1.0.203" serde_json = "1.0.120" tokio = { version = "1.38.0", features = ["full"] } tower = "0.4.13" +tower-http = "0.4.4" tracing-subscriber = "0.3.18" diff --git a/services/api/src/main.rs b/services/api/src/main.rs index 4e48dbb..2762b7f 100644 --- a/services/api/src/main.rs +++ b/services/api/src/main.rs @@ -6,21 +6,19 @@ use serde_derive::{Deserialize, Serialize}; use tokio::sync::Mutex; use tracing_subscriber; extern crate lru; -use chrono::Utc; - -use lru::LruCache; -use std::{num::NonZeroUsize, sync::Arc}; -use tower::ServiceBuilder; use axum::{ extract::{DefaultBodyLimit, Extension, Query, Request}, - http::StatusCode, + http::{header::CONTENT_TYPE, StatusCode}, middleware::{self, Next}, response::{IntoResponse, Json, Response}, routing::{get, post}, Router, }; +use lru::LruCache; use serde_json::Value; +use std::{num::NonZeroUsize, sync::Arc}; +use tower::ServiceBuilder; const ADS: &[&str; 100] = &[ "Tempur-Pedic: Experience the ultimate comfort with Tempur-Pedic mattresses.", @@ -132,18 +130,18 @@ async fn main() { let cache: Arc>> = Arc::new(Mutex::new(LruCache::new(NonZeroUsize::new(10000).unwrap()))); - let with_auth = Router::new() + let app = Router::new() + .route("/api", get(root)) + .route("/api/", get(root)) .route( - "/SECRET_INTERNAL_ENDPOINT_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_add_item", - post(add_item), + "/api/SECRET_INTERNAL_ENDPOINT_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_add_item", + post(add_item).layer(ServiceBuilder::new().layer(middleware::from_fn(check_for_key))), ) - .route("/gibs-item", get(gibs_item)) - .layer(middleware::from_fn(check_for_key)); - - let app = Router::new() - .route("/", get(root)) - .merge(with_auth) - .route("/gibs-key", get(gibs_key)) + .route( + "/api/gibs-item", + get(gibs_item).layer(ServiceBuilder::new().layer(middleware::from_fn(check_for_key))), + ) + .route("/api/gibs-key", post(gibs_key)) .layer( ServiceBuilder::new() .layer(Extension(cache)) @@ -169,41 +167,47 @@ async fn check_for_key( Extension(cache): Extension>>>, mut req: Request, next: Next, -) -> Response { - let api_key = req - .headers() - .get("averagedb-api-key") - .and_then(|value| value.to_str().ok()) - .map(|s| s.to_string()); - - if api_key.is_none() { - return ( +) -> Result { + let header_value = req.headers().get("x-averagedb-api-key").ok_or_else(|| { + ( StatusCode::UNAUTHORIZED, - "You must provide an API key in the 'averagedb-api-key' header".to_string(), + "You must provide an API key in the 'x-averagedb-api-key' header", ) - .into_response(); - } - - let api_key = api_key.unwrap(); + .into_response() + })?; + + let api_key = header_value + .to_str() + .map_err(|_| { + ( + StatusCode::BAD_REQUEST, + "The 'x-averagedb-api-key header is not a valid string", + ) + }) + .map(ToString::to_string) + .map_err(|_| { + ( + StatusCode::BAD_REQUEST, + "The 'x-averagedb-api-key' header contains invalid characters", + ) + .into_response() + })?; if api_key.is_empty() { - return ( + return Err(( StatusCode::BAD_REQUEST, - "The 'averagedb-api key' header must not be empty".to_string(), + "The 'x-averagedb-api-key' header must not be empty", ) - .into_response(); + .into_response()); } if !cache.lock().await.contains(&api_key) { - return ( - StatusCode::UNAUTHORIZED, - "Get a valid key first dummy".to_string(), - ) - .into_response(); + return Err((StatusCode::UNAUTHORIZED, "Get a valid key first dummy").into_response()); } req.extensions_mut().insert(api_key); - return next.run(req).await; + + Ok(next.run(req).await) } fn get_random_string() -> String { From 02307ed043e647cf29908b51e14347b534fc68d7 Mon Sep 17 00:00:00 2001 From: Jose Valerio Date: Fri, 5 Jul 2024 19:55:27 -0400 Subject: [PATCH 5/7] url --- k8s/templates/service.yaml | 1 + k8s/values/deployments/web.yaml | 4 ++++ services/web/app/root.tsx | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/k8s/templates/service.yaml b/k8s/templates/service.yaml index e189575..d64d864 100644 --- a/k8s/templates/service.yaml +++ b/k8s/templates/service.yaml @@ -14,5 +14,6 @@ spec: port: {{ .defaultContainerPort }} # Port that the service is exposed on targetPort: {{ .port }} + {{- end }} {{- end }} diff --git a/k8s/values/deployments/web.yaml b/k8s/values/deployments/web.yaml index d8a976a..31781e4 100644 --- a/k8s/values/deployments/web.yaml +++ b/k8s/values/deployments/web.yaml @@ -6,3 +6,7 @@ deployments: - name: web port: 3000 image: joswayski/averagedatabase:latest + # Add environment + env: + - name: BASE_API_URL + value: http://api:80 diff --git a/services/web/app/root.tsx b/services/web/app/root.tsx index fe90fb9..c1d80fa 100644 --- a/services/web/app/root.tsx +++ b/services/web/app/root.tsx @@ -33,7 +33,10 @@ export const action = async ({ request }) => { const random = Math.floor(Math.random() * 1800) + 200; await new Promise((resolve) => setTimeout(resolve, random)); - const key = await axios.post("http://0.0.0.0:80/api/gibs-key", {}); + const key = await axios.post( + `${process.env.BASE_API_URL || "http://localhost:80"} /api/gibs-key`, + {} + ); return key.data; } catch (error) { return null; From 6adc04748b3dbd31b2fa648fe44cfc7f083b06d9 Mon Sep 17 00:00:00 2001 From: Jose Valerio Date: Fri, 5 Jul 2024 21:58:17 -0400 Subject: [PATCH 6/7] stuff --- .gitignore | 1 + k8s/Chart.yaml | 2 +- k8s/README.md | 3 + k8s/templates/deployment.yaml | 73 ++++++++++------ k8s/templates/ingress.yaml | 14 +++- k8s/templates/service.yaml | 19 ++--- k8s/values/api.yaml | 39 +++++++++ k8s/values/deployments/api.yaml | 22 ----- k8s/values/deployments/shared.yaml | 11 --- k8s/values/deployments/web.yaml | 12 --- k8s/values/ingress.yaml | 106 ++++++++++++------------ k8s/values/production.yaml | 6 ++ k8s/values/services/api.yaml | 4 - k8s/values/services/shared.yaml | 2 - k8s/values/services/web.yaml | 4 - k8s/values/shared.yaml | 9 -- k8s/values/values.yaml | 7 ++ k8s/values/web.yaml | 38 +++++++++ services/api/Dockerfile | 42 ++++++---- services/api/src/main.rs | 36 +++++--- services/web/app/root.tsx | 15 ++-- services/web/app/routes/docs._index.tsx | 14 +++- services/web/server.mjs | 5 +- 23 files changed, 289 insertions(+), 195 deletions(-) create mode 100644 .gitignore create mode 100644 k8s/values/api.yaml delete mode 100644 k8s/values/deployments/api.yaml delete mode 100644 k8s/values/deployments/shared.yaml delete mode 100644 k8s/values/deployments/web.yaml create mode 100644 k8s/values/production.yaml delete mode 100644 k8s/values/services/api.yaml delete mode 100644 k8s/values/services/shared.yaml delete mode 100644 k8s/values/services/web.yaml delete mode 100644 k8s/values/shared.yaml create mode 100644 k8s/values/values.yaml create mode 100644 k8s/values/web.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6e7f4b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +root* diff --git a/k8s/Chart.yaml b/k8s/Chart.yaml index 4c9345f..66634f6 100644 --- a/k8s/Chart.yaml +++ b/k8s/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: averagedatabase +name: plutomi description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. diff --git a/k8s/README.md b/k8s/README.md index e69de29..acd3cbc 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -0,0 +1,3 @@ +### Kubernetes Files + +This directory contains the Kubernetes files used to deploy the project. We are using K3S as our Kubernetes distribution, and we are using SealedSecrets to store our secrets in the Git repository. Everything else is pretty standard with templates for deployments, services, and statefulsets, and our values which is the only file that should be modified directly. diff --git a/k8s/templates/deployment.yaml b/k8s/templates/deployment.yaml index 9840f94..a4aff96 100644 --- a/k8s/templates/deployment.yaml +++ b/k8s/templates/deployment.yaml @@ -1,44 +1,73 @@ -{{- if .Values.deployments }} +{{- if .Values.deployment }} +{{- with .Values.deployment }} apiVersion: apps/v1 kind: Deployment metadata: - name: {{ .Values.deployments.name }} + name: {{ .metadata.name }} labels: - app: {{ .Values.deployments.name }} + app: {{ .metadata.name }} spec: - replicas: {{ .Values.deployments.replicas }} + replicas: {{ .spec.replicas }} selector: matchLabels: - app: {{ .Values.deployments.name }} + app: {{ .metadata.name }} strategy: type: RollingUpdate rollingUpdate: - maxUnavailable: {{ .Values.deployments.maxUnavailable }} - maxSurge: {{ .Values.deployments.maxSurge }} + maxUnavailable: {{ .spec.strategy.rollingUpdate.maxUnavailable }} + maxSurge: {{ .spec.strategy.rollingUpdate.maxSurge }} template: metadata: + # # Disabled for now. Was using too many resources for my little cluster :3 + # annotations: + # prometheus.io/scrape: "true" + # prometheus.io/port: "9100" labels: - app: {{ .Values.deployments.name }} + app: {{ .metadata.name }} spec: - terminationGracePeriodSeconds: {{ $.Values.common.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .spec.template.spec.terminationGracePeriodSeconds }} containers: - {{- range .Values.deployments.containers }} + {{- range .spec.template.spec.containers }} - name: {{ .name }} image: {{ .image }} + imagePullPolicy: Always ports: - - containerPort: {{ .port }} + # - name: metrics + # containerPort: {{ .port }} + - name: http + containerPort: {{ .port }} env: + # Add the pod name for logging - TODO - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - {{- range $key, $value := .env }} + # Add any shared secrets + {{- range $key, $value := $.Values.env }} - name: {{ $key }} + {{- if $value.value }} + value: {{ $value.value | quote }} + {{- else if $value.secretKeyRef }} valueFrom: secretKeyRef: - name: {{ $value.secretKeyRef.name }} - key: {{ $value.secretKeyRef.key }} + name: {{ $value.secretKeyRef.name | quote }} + key: {{ $value.secretKeyRef.key | quote }} + {{- end }} {{- end }} + # Add application specific secrets + {{- range $key, $value := .env }} + - name: {{ $key }} + {{- if $value.value }} + value: {{ $value.value | quote }} + {{- else if $value.secretKeyRef }} + valueFrom: + secretKeyRef: + name: {{ $value.secretKeyRef.name | quote }} + key: {{ $value.secretKeyRef.key | quote }} + {{- end }} + {{- end }} + + volumeMounts: {{- range .volumeMounts }} - name: {{ .name }} @@ -48,23 +77,18 @@ spec: lifecycle: preStop: exec: - command: - [ - "sh", - "-c", - "sleep {{ $.Values.deployments.preStopSleepSeconds }}", - ] + command: {{ .lifecycle.preStop.exec.command }} readinessProbe: httpGet: - path: {{ $.Values.deployments.healthCheckPath }} + path: {{ .readinessProbe.httpGet.path }} port: {{ .port }} - initialDelaySeconds: {{ $.Values.deployments.initialDelaySeconds }} - periodSeconds: {{ $.Values.deployments.periodSeconds }} + initialDelaySeconds: {{ .readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .readinessProbe.periodSeconds }} timeoutSeconds: 5 failureThreshold: 3 {{- end }} volumes: - {{- range .Values.deployments.volumes }} + {{- range .spec.template.spec.volumes }} - name: {{ .name }} {{- if .secret }} secret: @@ -77,3 +101,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/k8s/templates/ingress.yaml b/k8s/templates/ingress.yaml index a441946..ecff676 100644 --- a/k8s/templates/ingress.yaml +++ b/k8s/templates/ingress.yaml @@ -3,14 +3,14 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ .name }} + name: {{ .metadata.name }} annotations: - {{- range $key, $value := .annotations }} + {{- range $key, $value := .metadata.annotations }} {{ $key }}: {{ $value | quote }} {{- end }} spec: rules: - {{- range .rules }} + {{- range .spec.rules }} - host: "{{ .host }}" http: paths: @@ -24,5 +24,13 @@ spec: number: {{ .backend.service.port.number }} {{- end }} {{- end }} + tls: + {{- range .spec.tls }} + - hosts: + {{- range .hosts }} + - "{{ . }}" + {{- end }} + secretName: {{ .secretName }} + {{- end }} {{- end }} {{- end }} diff --git a/k8s/templates/service.yaml b/k8s/templates/service.yaml index d64d864..c5e73e9 100644 --- a/k8s/templates/service.yaml +++ b/k8s/templates/service.yaml @@ -1,19 +1,18 @@ -{{- if .Values.services }} -{{- with .Values.services }} +{{- if .Values.service }} +{{- with .Values.service }} apiVersion: v1 kind: Service metadata: - name: {{ .name }} + name: {{ .metadata.name }} spec: - clusterIP: {{ .clusterIP }} + clusterIP: {{ .spec.clusterIP }} selector: - app: {{ .name }} + app: {{ .spec.selector.app }} ports: + {{- range .spec.ports }} - protocol: TCP - # Port the pod listens on - global value from common.yaml - port: {{ .defaultContainerPort }} - # Port that the service is exposed on - targetPort: {{ .port }} - + port: {{ .port }} + targetPort: {{ .targetPort }} + {{- end }} {{- end }} {{- end }} diff --git a/k8s/values/api.yaml b/k8s/values/api.yaml new file mode 100644 index 0000000..e55a243 --- /dev/null +++ b/k8s/values/api.yaml @@ -0,0 +1,39 @@ +service: + metadata: + name: api + spec: + # clusterIP: None + selector: + app: api + ports: + - port: 80 + targetPort: 8080 + + +deployment: + metadata: + name: api + + spec: + replicas: 1 # :D + template: + spec: + terminationGracePeriodSeconds: 10 + containers: + - name: api + image: joswayski/averagedatabase-api:latest + port: 8080 + + readinessProbe: + periodSeconds: 10 + initialDelaySeconds: 10 + httpGet: + path: /health + lifecycle: + preStop: + exec: + command: ["sh", "-c", "sleep 10"] + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/k8s/values/deployments/api.yaml b/k8s/values/deployments/api.yaml deleted file mode 100644 index 87a9b0d..0000000 --- a/k8s/values/deployments/api.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# # Must start with 'deployments' key -# deployments: -# replicas: 3 -# name: api -# # TODO This will be changed soon -# containers: -# - name: api -# port: 8080 -# image: plutomi/api:latestx86 # TODO update -# env: -# DATABASE_URL: -# secretKeyRef: -# name: api-config-secret -# key: DATABASE_URL -# BASE_WEB_URL: -# secretKeyRef: -# name: api-config-secret -# key: BASE_WEB_URL -# ENVIRONMENT: -# secretKeyRef: -# name: api-config-secret -# key: ENVIRONMENT diff --git a/k8s/values/deployments/shared.yaml b/k8s/values/deployments/shared.yaml deleted file mode 100644 index dd0da19..0000000 --- a/k8s/values/deployments/shared.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Must start with 'deployments' key -deployments: - ## Readiness probe - healthCheckPath: /health - initialDelaySeconds: 10 - periodSeconds: 10 - - ## Rolling update strategy - maxUnavailable: 0 - maxSurge: 1 - preStopSleepSeconds: 10 diff --git a/k8s/values/deployments/web.yaml b/k8s/values/deployments/web.yaml deleted file mode 100644 index 31781e4..0000000 --- a/k8s/values/deployments/web.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# Must start with 'deployments' key -deployments: - replicas: 3 - name: web - containers: - - name: web - port: 3000 - image: joswayski/averagedatabase:latest - # Add environment - env: - - name: BASE_API_URL - value: http://api:80 diff --git a/k8s/values/ingress.yaml b/k8s/values/ingress.yaml index ddc814b..8c42dec 100644 --- a/k8s/values/ingress.yaml +++ b/k8s/values/ingress.yaml @@ -1,55 +1,53 @@ -# Must start with 'ingress' key ingress: - name: ingress - annotations: - cert-manager.io/cluster-issuer: letsencrypt - rules: - - host: "averagedatabase.com" - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: web - port: - number: 80 - # - path: /api/ - # pathType: Prefix - # backend: - # service: - # name: api - # port: - # number: 80 - - host: "averagedatabase.com/api/" - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: api - port: - number: 80 - - host: "*.averagedatabase.com" - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: web - port: - number: 80 - # Temporarily disabled while i get unbanned from lets-encrypt - # tls: - # - hosts: - # - "plutomi.com" - # # HAVE TO USE DNS FOR WILDCARD CERT - # # CURRENTLY BANNED :D - # # https://stackoverflow.com/questions/68219076/cert-manager-no-configured-challenge-solvers-can-be-used-for-this-challenge - # # https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/ - # - "services.plutomi.com" # not being used just needed a new cert - # secretName: plutomi-tls-secret - - + metadata: + name: ingress + annotations: + cert-manager.io/cluster-issuer: letsencrypt + spec: + rules: + - host: "averagedatabase.com" + http: + paths: + # Redirect to the API + - path: /api/ + pathType: Prefix + backend: + service: + name: api + port: + number: 80 + - path: /api + pathType: Prefix + backend: + service: + name: api + port: + number: 80 + # Everything else goes to web + - path: / + pathType: Prefix + backend: + service: + name: web + port: + number: 80 + - host: "*.averagedatabase.com" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: web + port: + number: 80 + # tls: + # - hosts: + # - "plutomi.com" + # # YOU HAVE TO USE DNS FOR WILDCARD CERT + # # https://stackoverflow.com/questions/68219076/cert-manager-no-configured-challenge-solvers-can-be-used-for-this-challenge + # # https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/ + # # If you get banned for too many cert attempts, just add a new domain + # # and you can create a new cert. Wildcard, ideally, is best. + # - "*.plutomi.com" + # secretName: plutomi-tls-secret diff --git a/k8s/values/production.yaml b/k8s/values/production.yaml new file mode 100644 index 0000000..9e95079 --- /dev/null +++ b/k8s/values/production.yaml @@ -0,0 +1,6 @@ + +env: + ENVIRONMENT: + value: production + BASE_API_URL: + value: http://api.default.svc.cluster.local:80 diff --git a/k8s/values/services/api.yaml b/k8s/values/services/api.yaml deleted file mode 100644 index 99be7d7..0000000 --- a/k8s/values/services/api.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Must start with 'services' key -services: - name: api - port: 8080 diff --git a/k8s/values/services/shared.yaml b/k8s/values/services/shared.yaml deleted file mode 100644 index 0fa284d..0000000 --- a/k8s/values/services/shared.yaml +++ /dev/null @@ -1,2 +0,0 @@ -services: - defaultContainerPort: 80 diff --git a/k8s/values/services/web.yaml b/k8s/values/services/web.yaml deleted file mode 100644 index 459ae1c..0000000 --- a/k8s/values/services/web.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Must start with 'services' key -services: - name: web - port: 3000 diff --git a/k8s/values/shared.yaml b/k8s/values/shared.yaml deleted file mode 100644 index 5afbf18..0000000 --- a/k8s/values/shared.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Global values that are shared across all 1 or more components -common: - terminationGracePeriodSeconds: 10 - -databases: {} - -deployments: {} - -services: {} diff --git a/k8s/values/values.yaml b/k8s/values/values.yaml new file mode 100644 index 0000000..6814e77 --- /dev/null +++ b/k8s/values/values.yaml @@ -0,0 +1,7 @@ +# Default values +env: + ENVIRONMENT: + value: development + + BASE_API_URL: + value: http://localhost:80 diff --git a/k8s/values/web.yaml b/k8s/values/web.yaml new file mode 100644 index 0000000..d4c20a7 --- /dev/null +++ b/k8s/values/web.yaml @@ -0,0 +1,38 @@ +service: + metadata: + name: web + spec: + # clusterIP: None + selector: + app: web + ports: + - port: 80 + targetPort: 3000 + +deployment: + metadata: + name: web + + spec: + replicas: 3 + template: + spec: + terminationGracePeriodSeconds: 10 + containers: + - name: web + image: joswayski/averagedatabase-web:latest + port: 3000 + env: + readinessProbe: + periodSeconds: 10 + initialDelaySeconds: 10 + httpGet: + path: /health + lifecycle: + preStop: + exec: + command: ["sh", "-c", "sleep 10"] + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/services/api/Dockerfile b/services/api/Dockerfile index 4a7a0b1..f63d239 100644 --- a/services/api/Dockerfile +++ b/services/api/Dockerfile @@ -1,29 +1,35 @@ +# ########## ARM BUILD ########### - TODO add caching like the below one +# Builder stage +FROM arm64v8/rust:1.76.0-bullseye AS builder -# Builder stage using an official Rust image for AMD64 -FROM --platform=linux/amd64 rust:1.76.0-bullseye AS builder +# Add the aarch64-unknown-linux-musl target +RUN rustup target add aarch64-unknown-linux-musl +# Install musl-tools, musl-dev, wget, xz-utils, and ca-certificates +RUN apt update && apt install -y musl-tools musl-dev wget ca-certificates -WORKDIR /app - -# Cache dependencies by pre-building the application with an empty main file -COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && \ - echo "fn main() {}" > src/main.rs && \ - cargo build --release --target x86_64-unknown-linux-gnu --locked +# # Download and install UPX +RUN wget https://github.com/upx/upx/releases/download/v4.2.1/upx-4.2.1-arm64_linux.tar.xz \ + && tar -xf upx-4.2.1-arm64_linux.tar.xz \ + && mv upx-4.2.1-arm64_linux/upx /usr/bin/ \ + && rm -rf upx-4.2.1-arm64_linux upx-4.2.1-arm64_linux.tar.xz +WORKDIR /app -# Build the actual application COPY ./ . -RUN touch src/main.rs && \ - cargo build --release --target x86_64-unknown-linux-gnu --locked +RUN cargo build --target aarch64-unknown-linux-musl --release -# Compress the executable using UPX - NOT WORKING ON x86_64 -# RUN upx --best --lzma target/x86_64-unknown-linux-gnu/release/api +# Compress the executable using UPX +RUN upx --best --lzma /app/target/aarch64-unknown-linux-musl/release/api -# Final stage uses distroless for minimal size -FROM --platform=linux/amd64 debian:bullseye-slim as final -COPY --from=builder /app/target/x86_64-unknown-linux-gnu/release/api /app/api +# Final stage +FROM scratch + +# Copy CA certificates from the builder stage +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ EXPOSE 8080 +WORKDIR /app +COPY --from=builder /app/target/aarch64-unknown-linux-musl/release/api ./api +CMD ["/app/api"] -ENTRYPOINT ["/app/api"] diff --git a/services/api/src/main.rs b/services/api/src/main.rs index 2762b7f..77b1df3 100644 --- a/services/api/src/main.rs +++ b/services/api/src/main.rs @@ -9,14 +9,13 @@ extern crate lru; use axum::{ extract::{DefaultBodyLimit, Extension, Query, Request}, - http::{header::CONTENT_TYPE, StatusCode}, + http::StatusCode, middleware::{self, Next}, response::{IntoResponse, Json, Response}, routing::{get, post}, Router, }; use lru::LruCache; -use serde_json::Value; use std::{num::NonZeroUsize, sync::Arc}; use tower::ServiceBuilder; @@ -131,8 +130,13 @@ async fn main() { Arc::new(Mutex::new(LruCache::new(NonZeroUsize::new(10000).unwrap()))); let app = Router::new() + // k8s check + .route("/health", get(health)) .route("/api", get(root)) .route("/api/", get(root)) + .route("/api/u-up", get(health2)) + // For the people that can't read + .route("/api/health", get(health2)) .route( "/api/SECRET_INTERNAL_ENDPOINT_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_add_item", post(add_item).layer(ServiceBuilder::new().layer(middleware::from_fn(check_for_key))), @@ -148,7 +152,7 @@ async fn main() { .layer(DefaultBodyLimit::max(1024)), ); - let listener = tokio::net::TcpListener::bind("0.0.0.0:80").await.unwrap(); + let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap(); axum::serve(listener, app).await.unwrap(); } @@ -156,6 +160,14 @@ async fn root() -> &'static str { "Are you an idiot? Did you forget to look at the docs?" } +// k8s +async fn health() -> &'static str { + "Yeah" +} + +async fn health2() -> Response { + (StatusCode::OK, "yea").into_response() +} #[derive(Serialize)] struct InsertItemResponse { message: String, @@ -244,6 +256,11 @@ struct CreateApiKeyResponse { brought_to_you_by: String, } +#[derive(Serialize)] +struct CreateApiKeyError { + message: String, +} + fn get_random_ad() -> String { let mut rng: rand::prelude::ThreadRng = rand::thread_rng(); let index = rng.gen_range(0..ADS.len()); @@ -255,9 +272,7 @@ fn get_api_key() -> String { rng.gen_range(1..=1000000).to_string() } -async fn gibs_key( - Extension(cache): Extension>>>, -) -> (StatusCode, Json) { +async fn gibs_key(Extension(cache): Extension>>>) -> Response { let mut cache = cache.lock().await; for _ in 0..10 { @@ -269,18 +284,17 @@ async fn gibs_key( api_key, brought_to_you_by: get_random_ad(), }; - return (StatusCode::CREATED, Json(res)); + return (StatusCode::CREATED, Json(res)).into_response(); } } ( StatusCode::INTERNAL_SERVER_ERROR, - Json(CreateApiKeyResponse { - api_key: "".to_string(), - brought_to_you_by: "Failed to generate a unique API key sorry bud we're not experts" - .to_string(), + Json(CreateApiKeyError { + message: "Failed to generate a unique API key sorry bud we're not experts".to_string(), }), ) + .into_response() } #[derive(Serialize, Deserialize)] diff --git a/services/web/app/root.tsx b/services/web/app/root.tsx index c1d80fa..04def92 100644 --- a/services/web/app/root.tsx +++ b/services/web/app/root.tsx @@ -34,12 +34,14 @@ export const action = async ({ request }) => { await new Promise((resolve) => setTimeout(resolve, random)); const key = await axios.post( - `${process.env.BASE_API_URL || "http://localhost:80"} /api/gibs-key`, + `${process.env.BASE_API_URL || "http://localhost:80"}/api/gibs-key`, {} ); return key.data; } catch (error) { - return null; + console.error(`error man :/`); + console.error(error); + return { error: "sorry bruh we messed up :/" }; } }; @@ -97,10 +99,11 @@ export function Layout({ children }: { children: React.ReactNode }) { duration: 5000, // Adjust as needed } ); - } else { - toast.error( - "Sorry bruh we failed to get the key. Ping us on twitter idk whats wrong we don't have logs yet" - ); + return; + } + + if (data?.error) { + toast.error(data.error); } }, [data]); diff --git a/services/web/app/routes/docs._index.tsx b/services/web/app/routes/docs._index.tsx index 70fb376..f67e7d8 100644 --- a/services/web/app/routes/docs._index.tsx +++ b/services/web/app/routes/docs._index.tsx @@ -1,6 +1,14 @@ const faqs = [ { id: 1, + method: "GET", + color: "bg-sky-500", + endpoint: "/u-up", + text:

Will give you a response of:

, + response: `"yea"`, + }, + { + id: 2, method: "POST", color: "bg-orange-500", endpoint: @@ -21,9 +29,9 @@ const faqs = [ }`, }, { - id: 2, - method: "POST", - color: "bg-green-500", + id: 3, + method: "GET", + color: "bg-emerald-500", endpoint: "/gibs-item", text: (

diff --git a/services/web/server.mjs b/services/web/server.mjs index d0154f8..0ce259b 100644 --- a/services/web/server.mjs +++ b/services/web/server.mjs @@ -95,4 +95,7 @@ app.all( ); const port = 3000; -app.listen(port, () => console.log("http://localhost:" + port)); +app.listen(port, () => { + console.log("http://localhost:" + port); + console.log(`API URL: ${process.env.BASE_API_URL}`); +}); From 86891a2e3912af7efad03225ed4e8cca6b586171 Mon Sep 17 00:00:00 2001 From: Jose Valerio Date: Fri, 5 Jul 2024 21:59:49 -0400 Subject: [PATCH 7/7] more --- k8s/Chart.yaml | 2 +- k8s/README.md | 3 --- k8s/templates/deployment.yaml | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 k8s/README.md diff --git a/k8s/Chart.yaml b/k8s/Chart.yaml index 66634f6..4c9345f 100644 --- a/k8s/Chart.yaml +++ b/k8s/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: plutomi +name: averagedatabase description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. diff --git a/k8s/README.md b/k8s/README.md deleted file mode 100644 index acd3cbc..0000000 --- a/k8s/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### Kubernetes Files - -This directory contains the Kubernetes files used to deploy the project. We are using K3S as our Kubernetes distribution, and we are using SealedSecrets to store our secrets in the Git repository. Everything else is pretty standard with templates for deployments, services, and statefulsets, and our values which is the only file that should be modified directly. diff --git a/k8s/templates/deployment.yaml b/k8s/templates/deployment.yaml index a4aff96..b2538de 100644 --- a/k8s/templates/deployment.yaml +++ b/k8s/templates/deployment.yaml @@ -18,10 +18,6 @@ spec: maxSurge: {{ .spec.strategy.rollingUpdate.maxSurge }} template: metadata: - # # Disabled for now. Was using too many resources for my little cluster :3 - # annotations: - # prometheus.io/scrape: "true" - # prometheus.io/port: "9100" labels: app: {{ .metadata.name }} spec: