diff --git a/Cargo.lock b/Cargo.lock index 1288f78..62eb8e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -882,9 +882,9 @@ checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags", ] @@ -1237,9 +1237,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 46922e7..a327141 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,6 @@ bon = "3.6.3" reqwest = { version = "0.12.15", features = ["json", "multipart", "stream"] } serde = "1.0.219" serde_json = "1.0.140" -tokio = { version = "1.44.2", features = ["full"] } + +[dev-dependencies] +tokio = { version = "1.45.1", features = ["full"] } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md index 628a496..6237136 100644 --- a/README.md +++ b/README.md @@ -81,3 +81,11 @@ println!("{status_show:#?}"); ## Notes - If you use a `maybe_fn()` then if you provide `None` it will be ignored and that parameter will not be added to the JSON body. This library assumes `None` would not be provided as a value (since the cases where it is a value is often the default value that the CKAN API already has set for that parameter). + +## Tests + +To run the tests in the `tests` directory, first replace the values for `CKAN_API_TOKEN` and `CKAN_URL` then run: + +```bash +cargo test +``` diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..55a12ae --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,28 @@ +# deps +/node_modules + +# generated content +.contentlayer +.content-collections +.source + +# test & build +/coverage +/.next/ +/out/ +/build +*.tsbuildinfo + +# misc +.DS_Store +*.pem +/.pnp +.pnp.js +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# others +.env*.local +.vercel +next-env.d.ts \ No newline at end of file diff --git a/docs/.nvmrc b/docs/.nvmrc new file mode 100644 index 0000000..b009dfb --- /dev/null +++ b/docs/.nvmrc @@ -0,0 +1 @@ +lts/* diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c6e05c9 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,31 @@ +# ckanaction docs website (ckanaction.dathere.com) + +This directory includes a Next.js project built with [Fumadocs](https://github.com/fuma-nama/fumadocs) for documentation of ckanaction. The documentation can be viewed at [ckanaction.dathere.com](https://ckanaction.dathere.com). + +## Development + +Run development server: + +```bash +bun dev +``` + +Open http://localhost:3000 with your browser to see the result. + +## Explore + +In the project, you can see: + +- `lib/source.ts`: Code for content source adapter, `loader()` provides the interface to access your content. +- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep. +- `lib/openapi.yml`: The source file of the CKAN Actions API (v3) in an OpenAPI format which is then used in a script by running `bun ./scripts/generate-docs.ts` to generate the relevant docs files in `content/docs`. + +| Route | Description | +| ------------------------- | ------------------------------------------------------ | +| `app/(home)` | The route group for your landing page and other pages. | +| `app/docs` | The documentation layout and pages. | +| `app/api/search/route.ts` | The Route Handler for search. | + +## Linting + +We use [Biome](https://biomejs.dev) for linting. We recommend you install the [biome-vscode extension](https://github.com/biomejs/biome-vscode) if you are using [VSCodium](https://vscodium.com/) or VSCode for developing the docs. diff --git a/docs/app/(home)/ckanaction-demo-1.gif b/docs/app/(home)/ckanaction-demo-1.gif new file mode 100644 index 0000000..b5f52d1 Binary files /dev/null and b/docs/app/(home)/ckanaction-demo-1.gif differ diff --git a/docs/app/(home)/ckanaction-rust-demo.gif b/docs/app/(home)/ckanaction-rust-demo.gif new file mode 100644 index 0000000..2ee50cc Binary files /dev/null and b/docs/app/(home)/ckanaction-rust-demo.gif differ diff --git a/docs/app/(home)/ckanaction-web-app-interactive-demo.gif b/docs/app/(home)/ckanaction-web-app-interactive-demo.gif new file mode 100644 index 0000000..b9612bb Binary files /dev/null and b/docs/app/(home)/ckanaction-web-app-interactive-demo.gif differ diff --git a/docs/app/(home)/layout.tsx b/docs/app/(home)/layout.tsx new file mode 100644 index 0000000..77379fa --- /dev/null +++ b/docs/app/(home)/layout.tsx @@ -0,0 +1,6 @@ +import { HomeLayout } from 'fumadocs-ui/layouts/home'; +import { baseOptions } from '@/lib/layout.shared'; + +export default function Layout({ children }: LayoutProps<'/'>) { + return {children}; +} diff --git a/docs/app/(home)/page.tsx b/docs/app/(home)/page.tsx new file mode 100644 index 0000000..5ca2d38 --- /dev/null +++ b/docs/app/(home)/page.tsx @@ -0,0 +1,437 @@ +/** biome-ignore-all lint/suspicious/noArrayIndexKey: Would need to look into this trivial issue */ +"use client"; + +import { cva } from "class-variance-authority"; +import { CodeBlock } from "fumadocs-ui/components/codeblock"; +import defaultMdxComponents from "fumadocs-ui/mdx"; +import { cn } from "fumadocs-ui/utils/cn"; +import { + BlocksIcon, + GiftIcon, + GitMergeIcon, + HomeIcon, + SailboatIcon, + TerminalIcon, + Trash2Icon, + ZapIcon, +} from "lucide-react"; +import Image from "next/image"; +import Link from "next/link"; +import { type HTMLProps, type ReactNode, useState } from "react"; +import { Pre } from "@/components/codeblock"; +import { Button, buttonVariants } from "@/components/ui/button"; +import { RainbowButton } from "@/components/ui/rainbow-button"; +import CkanactionDemo1 from "./ckanaction-demo-1.gif"; +import CkanactionRustDemo from "./ckanaction-rust-demo.gif"; + +export default function HomePage() { + const gridColor = + "color-mix(in oklab, var(--color-fd-primary) 10%, transparent)"; + const { Card, Cards } = defaultMdxComponents; + return ( + <> +
+
+
+
+ + {/* */} +
+
+
+ +
+ + ); +} + +function Hero() { + const { Card, Cards } = defaultMdxComponents; + return ( +
+
+
+

ckanaction

+

+ + ckanaction + +
+ Rust crate & web GUI based on the CKAN API. +

+

+ ckanaction is a Rust library crate for interacting with the CKAN Actions + API, and this web app provides an interactive web GUI based on the + OpenAPI specification. +

+

+ Provided by{" "} + + datHere + + . +

+
+ + Get Started + + + Source Code + +
+ {/* + } href="/docs/builder" title="Quick start"> + Get started with ckan-devstaller and install CKAN within minutes + + } href="/docs/builder" title="Builder"> + Customize your installation with an interactive web GUI + + } + href="/docs/reference/installation-architecture" + title="Installation architecture" + > + Learn about where files are installed after running ckan-devstaller + + } + href="https://github.com/dathere/ckan-devstaller" + title="Source code" + > + View the source code of ckan-devstaller on GitHub + + */} + +
+ ); +} + +const previewButtonVariants = cva( + "w-48 h-8 text-sm font-medium transition-colors rounded-full", + { + variants: { + active: { + true: "text-fd-primary-foreground", + false: "text-fd-muted-foreground", + }, + }, + }, +); + +function PreviewImages() { + const [active, setActive] = useState(0); + const previews = [ + { + image: CkanactionRustDemo, + name: "Rust library crate", + }, + { + image: CkanactionDemo1, + name: "Interactive web GUI", + }, + ]; + + return ( +
+
+
+
+ {previews.map((item, i) => ( + + ))} +
+
+ {previews.map((item, i) => ( + preview + ))} +
+ ); +} + +function Why() { + return ( +
+ +
./ckan-devstaller
+ + } + codeblockUninstall={ + +
./ckan-devstaller uninstall
+
+ } + /> +
+ ); +} + +function WhyInteractive(props: { + codeblockInstall: ReactNode; + codeblockUninstall: ReactNode; +}) { + const [active, setActive] = useState(0); + const items = [ + [ + , + "Install CKAN within minutes", + ], + [ + , + "Customize your installation", + ], + [ + , + "Designed for developers", + ], + [ + , + "Uninstall with ease", + ], + ]; + + return ( +
+
+ {items.map((item, i) => ( + + ))} +
+ + +
+ {active === 0 ? ( + +

+ + Install CKAN within minutes. +

+

+ One of the primary goals of ckan-devstaller is to ease + installation of CKAN for development. Built with Rust for speed + and streamlining installation with{" "} + + ckan-compose + + , ckan-devstaller improves installation speeds{" "} + from hours/days to just minutes depending on your + download speed. +

+
+ + + +
+
+ ) : null} + {active === 1 ? ( + +

+ + Customize your installation with the Builder. +

+

+ Try out the interactive web GUI for customizing your CKAN + installation. You can select: +

+
    +
  • Presets
  • +
  • CKAN version
  • +
  • Extensions
  • +
  • Features
  • +
+

+ Then you can copy the provided ckan-devstaller command to run your + selected configuration. +

+
+ + Try out the Builder + +
+
+ ) : null} + {active === 2 ? ( + +

+ + Designed for developers. +

+

+ We've kept development use cases in mind while developing + ckan-devstaller, such as: +

+
    +
  • Trying out a new version of CKAN
  • +
  • Developing CKAN extensions and themes
  • +
+
+ + View the installation architecture + + + Source code + +
+
+ ) : null} + {active === 3 ? ( + +

+ + Uninstall CKAN with ease. +

+

+ After you've installed CKAN with ckan-devstaller, you can + uninstall CKAN with ease. This allows for quickly re-installing + CKAN for a different use case. +

+ {props.codeblockUninstall} + + Learn more about uninstalling + +
+ ) : null} +
+
+ ); +} + +function WhyPanel(props: HTMLProps) { + return ( +
+ {props.children} +
+ ); +} diff --git a/docs/app/api/search/route.ts b/docs/app/api/search/route.ts new file mode 100644 index 0000000..ec6bc8d --- /dev/null +++ b/docs/app/api/search/route.ts @@ -0,0 +1,7 @@ +import { createFromSource } from "fumadocs-core/search/server"; +import { source } from "@/lib/source"; + +export const { GET } = createFromSource(source, { + // https://docs.orama.com/docs/orama-js/supported-languages + language: "english", +}); diff --git a/docs/app/docs/[[...slug]]/page.tsx b/docs/app/docs/[[...slug]]/page.tsx new file mode 100644 index 0000000..9a9b214 --- /dev/null +++ b/docs/app/docs/[[...slug]]/page.tsx @@ -0,0 +1,54 @@ +import { createRelativeLink } from "fumadocs-ui/mdx"; +import { + DocsBody, + DocsDescription, + DocsPage, + DocsTitle, +} from "fumadocs-ui/page"; +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { getPageImage, source } from "@/lib/source"; +import { getMDXComponents } from "@/mdx-components"; + +export default async function Page(props: PageProps<"/docs/[[...slug]]">) { + const params = await props.params; + const page = source.getPage(params.slug); + if (!page) notFound(); + + const MDX = page.data.body; + + return ( + + {page.data.title} + {page.data.description} + + + + + ); +} + +export async function generateStaticParams() { + return source.generateParams(); +} + +export async function generateMetadata( + props: PageProps<"/docs/[[...slug]]">, +): Promise { + const params = await props.params; + const page = source.getPage(params.slug); + if (!page) notFound(); + + return { + title: page.data.title, + description: page.data.description, + openGraph: { + images: getPageImage(page).url, + }, + }; +} diff --git a/docs/app/docs/layout.tsx b/docs/app/docs/layout.tsx new file mode 100644 index 0000000..8e9ec72 --- /dev/null +++ b/docs/app/docs/layout.tsx @@ -0,0 +1,11 @@ +import { DocsLayout } from 'fumadocs-ui/layouts/docs'; +import { baseOptions } from '@/lib/layout.shared'; +import { source } from '@/lib/source'; + +export default function Layout({ children }: LayoutProps<'/docs'>) { + return ( + + {children} + + ); +} diff --git a/docs/app/global.css b/docs/app/global.css new file mode 100644 index 0000000..a8dba0c --- /dev/null +++ b/docs/app/global.css @@ -0,0 +1,155 @@ +@import "tailwindcss"; +@import 'fumadocs-ui/css/neutral.css'; +@import "fumadocs-ui/css/ocean.css"; +@import "fumadocs-ui/css/preset.css"; +@import 'fumadocs-openapi/css/preset.css'; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --radius-2xl: calc(var(--radius) + 8px); + --radius-3xl: calc(var(--radius) + 12px); + --radius-4xl: calc(var(--radius) + 16px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + --animate-rainbow: rainbow var(--speed, 2s) infinite linear; + --color-color-5: var(--color-5); + --color-color-4: var(--color-4); + --color-color-3: var(--color-3); + --color-color-2: var(--color-2); + --color-color-1: var(--color-1); + @keyframes rainbow { + 0% { + background-position: 0%; + } + 100% { + background-position: 200%; + } + } +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); + --color-1: oklch(66.2% 0.225 25.9); + --color-2: oklch(60.4% 0.26 302); + --color-3: oklch(69.6% 0.165 251); + --color-4: oklch(80.2% 0.134 225); + --color-5: oklch(90.7% 0.231 133); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); + --color-1: oklch(66.2% 0.225 25.9); + --color-2: oklch(60.4% 0.26 302); + --color-3: oklch(69.6% 0.165 251); + --color-4: oklch(80.2% 0.134 225); + --color-5: oklch(90.7% 0.231 133); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background dark:bg-[#101022] text-foreground; + } + button:not(:disabled), + [role="button"]:not(:disabled) { + cursor: pointer; + } +} \ No newline at end of file diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx new file mode 100644 index 0000000..bad5bbc --- /dev/null +++ b/docs/app/layout.tsx @@ -0,0 +1,25 @@ +import "@/app/global.css"; +import { RootProvider } from "fumadocs-ui/provider/next"; +import { Inter } from "next/font/google"; +import Script from "next/script"; + +const inter = Inter({ + subsets: ["latin"], +}); + +export default function Layout({ children }: LayoutProps<"/">) { + return ( + + + + {children} +