From e10f0f469a7109755d73851db86e632ebcdac57d Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 24 Mar 2026 00:02:31 +0000 Subject: [PATCH 1/6] feat: open source main app --- .gitignore | 2 + apps/icbe/README.md | 0 apps/icbe/astro.config.ts | 36 + apps/icbe/package.json | 17 +- apps/icbe/public/.assetsignore | 2 + apps/icbe/src/actions/index.ts | 8 + apps/icbe/src/actions/submit-elements.ts | 65 + apps/icbe/src/components/AdminDashboard.tsx | 11 + apps/icbe/src/components/UserDashboard.tsx | 40 + .../SubmitDisprovenElementsSection.tsx | 97 + .../dashboard/SubmitProvenElementsSection.tsx | 159 + apps/icbe/src/components/ui/Button.tsx | 50 + apps/icbe/src/components/ui/Divider.tsx | 5 + apps/icbe/src/components/ui/Link.tsx | 48 + apps/icbe/src/layouts/Layout.astro | 39 + apps/icbe/src/pages/admin/dashboard.astro | 16 + apps/icbe/src/pages/api/get-elements.ts | 20 + apps/icbe/src/pages/dashboard/index.astro | 18 + apps/icbe/src/pages/index.astro | 22 + apps/icbe/src/schemas/data/disproven.ts | 6 + apps/icbe/src/schemas/data/history.ts | 22 + apps/icbe/src/schemas/data/index.ts | 16 + apps/icbe/src/schemas/data/metadata.ts | 13 + apps/icbe/src/schemas/data/proven.ts | 25 + apps/icbe/src/schemas/index.ts | 1 + apps/icbe/src/styles/global.css | 1 + apps/icbe/src/utils/gitlab.ts | 82 + apps/icbe/src/utils/merge-data.ts | 42 + apps/icbe/src/utils/respond-json.ts | 11 + apps/icbe/src/utils/submit-elements.ts | 93 + apps/icbe/src/worker-configuration.d.ts | 13623 ++++++++++++++++ apps/icbe/tailwind.config.ts | 4 + apps/icbe/tests/index.test.ts | 18 + apps/icbe/tsconfig.json | 9 + apps/icbe/wrangler.jsonc | 11 + bun.lock | 92 + package.json | 4 + 37 files changed, 14727 insertions(+), 1 deletion(-) create mode 100644 apps/icbe/README.md create mode 100644 apps/icbe/astro.config.ts create mode 100644 apps/icbe/public/.assetsignore create mode 100644 apps/icbe/src/actions/index.ts create mode 100644 apps/icbe/src/actions/submit-elements.ts create mode 100644 apps/icbe/src/components/AdminDashboard.tsx create mode 100644 apps/icbe/src/components/UserDashboard.tsx create mode 100644 apps/icbe/src/components/dashboard/SubmitDisprovenElementsSection.tsx create mode 100644 apps/icbe/src/components/dashboard/SubmitProvenElementsSection.tsx create mode 100644 apps/icbe/src/components/ui/Button.tsx create mode 100644 apps/icbe/src/components/ui/Divider.tsx create mode 100644 apps/icbe/src/components/ui/Link.tsx create mode 100644 apps/icbe/src/layouts/Layout.astro create mode 100644 apps/icbe/src/pages/admin/dashboard.astro create mode 100644 apps/icbe/src/pages/api/get-elements.ts create mode 100644 apps/icbe/src/pages/dashboard/index.astro create mode 100644 apps/icbe/src/pages/index.astro create mode 100644 apps/icbe/src/schemas/data/disproven.ts create mode 100644 apps/icbe/src/schemas/data/history.ts create mode 100644 apps/icbe/src/schemas/data/index.ts create mode 100644 apps/icbe/src/schemas/data/metadata.ts create mode 100644 apps/icbe/src/schemas/data/proven.ts create mode 100644 apps/icbe/src/schemas/index.ts create mode 100644 apps/icbe/src/styles/global.css create mode 100644 apps/icbe/src/utils/gitlab.ts create mode 100644 apps/icbe/src/utils/merge-data.ts create mode 100644 apps/icbe/src/utils/respond-json.ts create mode 100644 apps/icbe/src/utils/submit-elements.ts create mode 100644 apps/icbe/src/worker-configuration.d.ts create mode 100644 apps/icbe/tailwind.config.ts create mode 100644 apps/icbe/tests/index.test.ts create mode 100644 apps/icbe/tsconfig.json create mode 100644 apps/icbe/wrangler.jsonc diff --git a/.gitignore b/.gitignore index 443b409..896a6ec 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ dist/ .astro/ .wrangler/ + +.env diff --git a/apps/icbe/README.md b/apps/icbe/README.md new file mode 100644 index 0000000..e69de29 diff --git a/apps/icbe/astro.config.ts b/apps/icbe/astro.config.ts new file mode 100644 index 0000000..e8ec6b7 --- /dev/null +++ b/apps/icbe/astro.config.ts @@ -0,0 +1,36 @@ +import cloudflare from "@astrojs/cloudflare"; +import solid from "@astrojs/solid-js"; +import tailwindcss from "@tailwindcss/vite"; +import { defineConfig, envField } from "astro/config"; + +type EnvSchema = NonNullable< + Parameters[0]["env"] +>["schema"]; + +const envSchema: EnvSchema = { + GITLAB_ACCESS_TOKEN: envField.string({ + context: "server", + access: "secret", + startsWith: "glpat", + length: 26, + }), + GITLAB_PROJECT_ID: envField.number({ + context: "server", + access: "secret", + int: true, + gt: 1, + }), +}; + +export default defineConfig({ + output: "server", + adapter: cloudflare(), + integrations: [solid()], + vite: { plugins: [tailwindcss()] }, + site: "https://icbe.rman.dev", + + env: { + schema: envSchema, + validateSecrets: true, + }, +}); diff --git a/apps/icbe/package.json b/apps/icbe/package.json index 46e648f..4993a07 100644 --- a/apps/icbe/package.json +++ b/apps/icbe/package.json @@ -2,13 +2,28 @@ "name": "web-app", "type": "module", "private": true, + "imports": { + "#styles": "./src/styles/global.css", + "#layout": "./src/layouts/Layout.astro", + "#schemas": "./src/schemas/index.ts", + "#utils/*": "./src/utils/*.ts", + "#components/*": "./src/components/*.tsx" + }, "scripts": { + "typegen": "wrangler types ./src/worker-configuration.d.ts", + "test": "bun run test", "build": "astro build", "deploy": "wrangler deploy", "dev": "astro dev" }, "dependencies": { - "astro": "catalog:" + "@astrojs/cloudflare": "^13.1.3", + "@astrojs/solid-js": "^6.0.1", + "@gitbeaker/rest": "^43.8.0", + "@tailwindcss/vite": "catalog:", + "astro": "catalog:", + "solid-js": "^1.9.11", + "tailwindcss": "catalog:" }, "devDependencies": { "wrangler": "catalog:" diff --git a/apps/icbe/public/.assetsignore b/apps/icbe/public/.assetsignore new file mode 100644 index 0000000..b7ccb6c --- /dev/null +++ b/apps/icbe/public/.assetsignore @@ -0,0 +1,2 @@ +_worker.js +_routes.json diff --git a/apps/icbe/src/actions/index.ts b/apps/icbe/src/actions/index.ts new file mode 100644 index 0000000..18801f5 --- /dev/null +++ b/apps/icbe/src/actions/index.ts @@ -0,0 +1,8 @@ +import { + submitDisprovenElements, + submitProvenElements, +} from "./submit-elements"; + +export const server = { + elements: { submitDisprovenElements, submitProvenElements }, +}; diff --git a/apps/icbe/src/actions/submit-elements.ts b/apps/icbe/src/actions/submit-elements.ts new file mode 100644 index 0000000..81c0a83 --- /dev/null +++ b/apps/icbe/src/actions/submit-elements.ts @@ -0,0 +1,65 @@ +import { ActionError, defineAction } from "astro:actions"; +import * as z from "astro/zod"; +import { DataProvenSchema } from "#schemas"; +import { saveToCloud } from "#utils/gitlab"; +import { getDisprovenToSave, getProvenToSave } from "#utils/submit-elements"; + +const submitDisprovenElements = defineAction({ + input: z.object({ elements: z.array(z.object({ name: z.string() })) }), + async handler(input, context) { + const role = null; + + if (!role) { + throw new ActionError({ + code: "UNAUTHORIZED", + message: "Please login to use this feature.", + }); + } + + const canSubmitElements = false; + + if (!canSubmitElements) { + throw new ActionError({ + code: "FORBIDDEN", + message: "You don't have permissions to use this feature.", + }); + } + + const elements = input.elements.map((element) => element.name); + + const data = await getDisprovenToSave(elements); + await saveToCloud(data); + + return { success: true } as const; + }, +}); + +const submitProvenElements = defineAction({ + input: z.object({ elements: DataProvenSchema }), + handler: async (input, context) => { + const role = null; + + if (!role) { + throw new ActionError({ + code: "UNAUTHORIZED", + message: "Please login to use this feature.", + }); + } + + const canSubmitElements = false; + + if (!canSubmitElements) { + throw new ActionError({ + code: "FORBIDDEN", + message: "You don't have permissions to use this feature.", + }); + } + + const data = await getProvenToSave(input.elements); + await saveToCloud(data); + + return { success: true } as const; + }, +}); + +export { submitDisprovenElements, submitProvenElements }; diff --git a/apps/icbe/src/components/AdminDashboard.tsx b/apps/icbe/src/components/AdminDashboard.tsx new file mode 100644 index 0000000..ac2a318 --- /dev/null +++ b/apps/icbe/src/components/AdminDashboard.tsx @@ -0,0 +1,11 @@ +import type { JSX } from "solid-js"; + +export default function AdminDashboard(): JSX.Element { + return ( +
+

+ Admin Controls +

+
+ ); +} diff --git a/apps/icbe/src/components/UserDashboard.tsx b/apps/icbe/src/components/UserDashboard.tsx new file mode 100644 index 0000000..946a347 --- /dev/null +++ b/apps/icbe/src/components/UserDashboard.tsx @@ -0,0 +1,40 @@ +import { type JSX, Show } from "solid-js"; + +import SubmitDisprovenElementsSection from "./dashboard/SubmitDisprovenElementsSection"; +import SubmitProvenElementsSection from "./dashboard/SubmitProvenElementsSection"; +import Divider from "./ui/Divider"; +import Link from "./ui/Link"; + +const TopBar = (): JSX.Element => ( +
+

+ User Dashboard +

+
+); + +export default function UserDashboard(props: { + canSubmitElements: boolean; +}): JSX.Element { + const isAdmin = false; + + return ( +
+ + + + + + + + + + + +
+ ); +} diff --git a/apps/icbe/src/components/dashboard/SubmitDisprovenElementsSection.tsx b/apps/icbe/src/components/dashboard/SubmitDisprovenElementsSection.tsx new file mode 100644 index 0000000..52dd14e --- /dev/null +++ b/apps/icbe/src/components/dashboard/SubmitDisprovenElementsSection.tsx @@ -0,0 +1,97 @@ +import { actions } from "astro:actions"; +import type { JSX } from "solid-js"; +import { createSignal, Show } from "solid-js"; +import Button from "#components/ui/Button"; + +type SubmitState = + | { status: "not-started" } + | { status: "submitting" } + | { status: "complete"; success: boolean } + | { status: "error"; error?: string }; + +const SubmitDisprovenElementsResultContainer = (props: { + state: SubmitState; +}) => ( + + {(state) => ( +

+ {state.success + ? "Elements submitted successfully!" + : "Elements submission failed."} +

+ )} +
+); + +function SubmitDisprovenElementsSection(): JSX.Element { + const [elementsToBeSubmitted, setElementsToBeSubmitted] = createSignal< + string[] + >([]); + const [submitState, setSubmitState] = createSignal({ + status: "not-started", + }); + + const handleSubmit = async () => { + try { + setSubmitState({ status: "submitting" }); + const disprovenElements = elementsToBeSubmitted().map((name) => ({ + name, + })); + const result = await actions.elements.submitDisprovenElements({ + elements: disprovenElements, + }); + if (result.error) { + console.error("Submit failed:", result.error); + setSubmitState({ status: "error", error: result.error.message }); + } else { + setSubmitState({ status: "complete", success: result.data.success }); + } + } catch (err) { + console.error("Submit error:", err); + setSubmitState({ status: "error" }); + } + }; + + return ( +
+