mirror of
https://github.com/dathere/ckan-devstaller.git
synced 2025-11-09 05:39:49 +00:00
feat(docs): update quick start page and add reference section
This commit is contained in:
parent
cbd0a6dfd9
commit
b984c3b5e0
12 changed files with 435 additions and 4 deletions
|
|
@ -2,7 +2,6 @@
|
|||
/** biome-ignore-all lint/suspicious/noArrayIndexKey: <explanation> */
|
||||
"use client";
|
||||
|
||||
import { cva } from "class-variance-authority";
|
||||
import { cn } from "fumadocs-ui/utils/cn";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
"": {
|
||||
"name": "docs",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"fumadocs-core": "15.8.1",
|
||||
"fumadocs-mdx": "12.0.1",
|
||||
"fumadocs-ui": "15.8.1",
|
||||
|
|
@ -11,6 +14,7 @@
|
|||
"next": "15.5.4",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.2.4",
|
||||
|
|
|
|||
11
docs/cli.json
Normal file
11
docs/cli.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"aliases": {
|
||||
"uiDir": "./components/ui",
|
||||
"componentsDir": "./components",
|
||||
"blockDir": "./components",
|
||||
"cssDir": "./styles",
|
||||
"libDir": "./lib"
|
||||
},
|
||||
"baseDir": "",
|
||||
"commands": {}
|
||||
}
|
||||
136
docs/components/accordion.tsx
Normal file
136
docs/components/accordion.tsx
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
'use client';
|
||||
|
||||
import type {
|
||||
AccordionMultipleProps,
|
||||
AccordionSingleProps,
|
||||
} from '@radix-ui/react-accordion';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import { Check, ChevronRight, Link as LinkIcon } from 'lucide-react';
|
||||
import {
|
||||
type ComponentPropsWithoutRef,
|
||||
forwardRef,
|
||||
type ReactNode,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { cn } from '../lib/cn';
|
||||
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
|
||||
import { buttonVariants } from './ui/button';
|
||||
import { mergeRefs } from '../lib/merge-refs';
|
||||
|
||||
export const Accordions = forwardRef<
|
||||
HTMLDivElement,
|
||||
| Omit<AccordionSingleProps, 'value' | 'onValueChange'>
|
||||
| Omit<AccordionMultipleProps, 'value' | 'onValueChange'>
|
||||
>(({ type = 'single', className, defaultValue, ...props }, ref) => {
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const composedRef = mergeRefs(ref, rootRef);
|
||||
const [value, setValue] = useState<string | string[]>(() =>
|
||||
type === 'single' ? (defaultValue ?? '') : (defaultValue ?? []),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const id = window.location.hash.substring(1);
|
||||
const element = rootRef.current;
|
||||
if (!element || id.length === 0) return;
|
||||
|
||||
const selected = document.getElementById(id);
|
||||
if (!selected || !element.contains(selected)) return;
|
||||
const value = selected.getAttribute('data-accordion-value');
|
||||
|
||||
if (value)
|
||||
setValue((prev) => (typeof prev === 'string' ? value : [value, ...prev]));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
// @ts-expect-error -- Multiple types
|
||||
<AccordionPrimitive.Root
|
||||
type={type}
|
||||
ref={composedRef}
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
collapsible={type === 'single' ? true : undefined}
|
||||
className={cn(
|
||||
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Accordions.displayName = 'Accordions';
|
||||
|
||||
export const Accordion = forwardRef<
|
||||
HTMLDivElement,
|
||||
Omit<
|
||||
ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>,
|
||||
'value' | 'title'
|
||||
> & {
|
||||
title: string | ReactNode;
|
||||
value?: string;
|
||||
}
|
||||
>(
|
||||
(
|
||||
{ title, className, id, value = String(title), children, ...props },
|
||||
ref,
|
||||
) => {
|
||||
return (
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
value={value}
|
||||
className={cn('scroll-m-24', className)}
|
||||
{...props}
|
||||
>
|
||||
<AccordionPrimitive.Header
|
||||
id={id}
|
||||
data-accordion-value={value}
|
||||
className="not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent"
|
||||
>
|
||||
<AccordionPrimitive.Trigger className="group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none">
|
||||
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-90" />
|
||||
{title}
|
||||
</AccordionPrimitive.Trigger>
|
||||
{id ? <CopyButton id={id} /> : null}
|
||||
</AccordionPrimitive.Header>
|
||||
<AccordionPrimitive.Content className="overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down">
|
||||
<div className="px-4 pb-2 text-[15px] prose-no-margin">
|
||||
{children}
|
||||
</div>
|
||||
</AccordionPrimitive.Content>
|
||||
</AccordionPrimitive.Item>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
function CopyButton({ id }: { id: string }) {
|
||||
const [checked, onClick] = useCopyButton(() => {
|
||||
const url = new URL(window.location.href);
|
||||
url.hash = id;
|
||||
|
||||
return navigator.clipboard.writeText(url.toString());
|
||||
});
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Copy Link"
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
color: 'ghost',
|
||||
className: 'text-fd-muted-foreground me-2',
|
||||
}),
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{checked ? (
|
||||
<Check className="size-3.5" />
|
||||
) : (
|
||||
<LinkIcon className="size-3.5" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
Accordion.displayName = 'Accordion';
|
||||
85
docs/components/files.tsx
Normal file
85
docs/components/files.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
'use client';
|
||||
|
||||
import { cva } from 'class-variance-authority';
|
||||
import {
|
||||
File as FileIcon,
|
||||
Folder as FolderIcon,
|
||||
FolderOpen,
|
||||
} from 'lucide-react';
|
||||
import { type HTMLAttributes, type ReactNode, useState } from 'react';
|
||||
import { cn } from '../lib/cn';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from './ui/collapsible';
|
||||
|
||||
const itemVariants = cva(
|
||||
'flex flex-row items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground [&_svg]:size-4',
|
||||
);
|
||||
|
||||
export function Files({
|
||||
className,
|
||||
...props
|
||||
}: HTMLAttributes<HTMLDivElement>): React.ReactElement {
|
||||
return (
|
||||
<div
|
||||
className={cn('not-prose rounded-md border bg-fd-card p-2', className)}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export interface FileProps extends HTMLAttributes<HTMLDivElement> {
|
||||
name: string;
|
||||
icon?: ReactNode;
|
||||
}
|
||||
|
||||
export interface FolderProps extends HTMLAttributes<HTMLDivElement> {
|
||||
name: string;
|
||||
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Open folder by default
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
defaultOpen?: boolean;
|
||||
}
|
||||
|
||||
export function File({
|
||||
name,
|
||||
icon = <FileIcon />,
|
||||
className,
|
||||
...rest
|
||||
}: FileProps): React.ReactElement {
|
||||
return (
|
||||
<div className={cn(itemVariants({ className }))} {...rest}>
|
||||
{icon}
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Folder({
|
||||
name,
|
||||
defaultOpen = false,
|
||||
...props
|
||||
}: FolderProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
|
||||
return (
|
||||
<Collapsible open={open} onOpenChange={setOpen} {...props}>
|
||||
<CollapsibleTrigger className={cn(itemVariants({ className: 'w-full' }))}>
|
||||
{open ? <FolderOpen /> : <FolderIcon />}
|
||||
{name}
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="ms-2 flex flex-col border-l ps-2">{props.children}</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
39
docs/components/ui/collapsible.tsx
Normal file
39
docs/components/ui/collapsible.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
'use client';
|
||||
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
||||
import { forwardRef, useEffect, useState } from 'react';
|
||||
import { cn } from '../../lib/cn';
|
||||
|
||||
const Collapsible = CollapsiblePrimitive.Root;
|
||||
|
||||
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
|
||||
|
||||
const CollapsibleContent = forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleContent>
|
||||
>(({ children, ...props }, ref) => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CollapsiblePrimitive.CollapsibleContent
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
'overflow-hidden',
|
||||
mounted &&
|
||||
'data-[state=closed]:animate-fd-collapsible-up data-[state=open]:animate-fd-collapsible-down',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</CollapsiblePrimitive.CollapsibleContent>
|
||||
);
|
||||
});
|
||||
|
||||
CollapsibleContent.displayName =
|
||||
CollapsiblePrimitive.CollapsibleContent.displayName;
|
||||
|
||||
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
|
||||
|
|
@ -3,4 +3,64 @@ title: Quick Start
|
|||
description: Getting Started with ckan-devstaller
|
||||
---
|
||||
|
||||
This site is a work in progress. You may [view the ckan-devstaller README and source code](https://github.com/dathere/ckan-devstaller) in the meantime.
|
||||
ckan-devstaller attempts to install a CKAN instance from source along with [ckan-compose](https://github.com/tino097/ckan-compose) and other optional features, intended for development use in a new Ubuntu 22.04 instance.
|
||||
|
||||
<Callout title="Please run ckan-devstaller in a new Ubuntu 22.04 instance only" type="error">Make sure `ckan-devstaller` is run in a **new** Ubuntu 22.04 instanceof. Do NOT run `ckan-devstaller` in an existing instance that is important for your usage.</Callout>
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
|
||||
<Accordions type="single">
|
||||
<Accordion title="Note for VirtualBox users">If you are using Ubuntu 22.04 on VirtualBox, you may need to add your user to the sudoers file before running the ckan-devstaller install script. Open a terminal in your virtual machine (VM), run `su -` and log in as the root user with the password you used to set up the VM, then type `sudo adduser <username> sudo` where `<username>` is your username then restart your VM and run the ckan-devstaller installer script.</Accordion>
|
||||
<Accordion title="Note for ARM64 users">Currently `ckan-devstaller` supports `x86_64` architecture. `ARM64` support is planned.</Accordion>
|
||||
</Accordions>
|
||||
|
||||
Currently you have two options to choose from for installation. Paste one of the following scripts into your new Ubuntu 22.04 instance's terminal:
|
||||
|
||||
## (Option 1/2) Install with interactive mode
|
||||
|
||||
```bash
|
||||
wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.2.1/install.bash | bash -s default
|
||||
```
|
||||
|
||||
## (Option 2/2) Install with non-interactive mode with a specific config
|
||||
|
||||
The following script will install the following:
|
||||
|
||||
- CKAN 2.11.3
|
||||
- [ckan-compose](https://github.com/tino097/ckan-compose/tree/ckan-devstaller)
|
||||
- [DataStore extension](https://docs.ckan.org/en/2.11/maintaining/datastore.html)
|
||||
- [ckanext-scheming extension](https://github.com/ckan/ckanext-scheming)
|
||||
- [DataPusher+ extension](https://github.com/dathere/datapusher-plus)
|
||||
|
||||
[DRUF mode](https://github.com/dathere/datapusher-plus?tab=readme-ov-file#druf-dataset-resource-upload-first-workflow) for DataPusher+ is available but disabled by default.
|
||||
|
||||
```bash
|
||||
wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.2.1/install.bash | bash -s default
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
import { BlocksIcon, GitMergeIcon, Trash2Icon } from 'lucide-react';
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
icon={<GitMergeIcon />}
|
||||
href="https://github.com/dathere/ckan-devstaller"
|
||||
title="Source code">
|
||||
View the source code of ckan-devstaller on GitHub
|
||||
</Card>
|
||||
<Card
|
||||
icon={<BlocksIcon />}
|
||||
href="/docs/reference/installation-architecture"
|
||||
title="Installation architecture"
|
||||
>
|
||||
Learn about where files are installed after running ckan-devstaller
|
||||
</Card>
|
||||
<Card
|
||||
icon={<Trash2Icon />}
|
||||
href="/docs/tutorials/uninstall-ckan"
|
||||
title="Uninstall CKAN"
|
||||
>
|
||||
Learn how to uninstall CKAN after running ckan-devstaller
|
||||
</Card>
|
||||
</Cards>
|
||||
|
|
|
|||
76
docs/content/docs/reference/installation-architecture.mdx
Normal file
76
docs/content/docs/reference/installation-architecture.mdx
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
title: Installation architecture
|
||||
description: View a brief overview of what the installation from ckan-devstaller looks like
|
||||
---
|
||||
|
||||
import { File, Folder, Files } from 'fumadocs-ui/components/files';
|
||||
|
||||
## CKAN and extensions
|
||||
|
||||
The CKAN repository selected from ckan-devstaller is installed to `/usr/lib/ckan/default/src/ckan`. Extensions are also installed as sibling folders. For example if `ckanext-scheming` is also installed:
|
||||
|
||||
```files
|
||||
/usr/lib/ckan/default/src
|
||||
├── ckan
|
||||
│ ├── ...
|
||||
├── ckanext-scheming
|
||||
│ ├── ...
|
||||
```
|
||||
|
||||
The configuration file for CKAN is installed at `/etc/ckan/default/ckan.ini`:
|
||||
|
||||
```files
|
||||
/etc/ckan/default
|
||||
├── ckan.ini
|
||||
├── who.ini
|
||||
```
|
||||
|
||||
## ckan-compose
|
||||
|
||||
We install certain first-time install files and `ckan-compose` as a directory in the user's home (`~`) directory. For example for the user `adam`:
|
||||
|
||||
```files
|
||||
/home/adam
|
||||
├── ahoy
|
||||
├── dpp_default_config.ini
|
||||
├── get-docker.sh
|
||||
├── permissions.sql
|
||||
├── ckan-compose
|
||||
│ ├── ...
|
||||
```
|
||||
|
||||
After running ckan-devstaller you may also see many files starting with `qsv` and `README`. There are various files you can remove after running ckan-devstaller including:
|
||||
|
||||
- `dpp_default_config.ini`
|
||||
- `get-docker.sh`
|
||||
- `permissions.sql`
|
||||
- `README`
|
||||
- The various `qsv` files
|
||||
|
||||
Here's a script you can run for cleanup after running ckan-devstaller:
|
||||
|
||||
```bash
|
||||
cd ~/
|
||||
rm -rf dpp_default_config.ini get-docker.sh permissions.sql README qsv*
|
||||
```
|
||||
|
||||
## DataPusher+
|
||||
|
||||
We install a compatible version of qsv with the DataPusher+ variant named `qsvdp` and move it to `/usr/local/bin`:
|
||||
|
||||
```files
|
||||
/usr/local/bin
|
||||
├── qsvdp
|
||||
```
|
||||
|
||||
The ckanext-scheming and DataPusher+ extensions are installed in the same location as other CKAN extensions:
|
||||
|
||||
```files
|
||||
/usr/lib/ckan/default/src
|
||||
├── ckan
|
||||
| ├── ...
|
||||
├── ckanext-scheming
|
||||
| ├── ...
|
||||
├── datapusher_plus
|
||||
| ├── ...
|
||||
```
|
||||
1
docs/lib/cn.ts
Normal file
1
docs/lib/cn.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { twMerge as cn } from 'tailwind-merge';
|
||||
15
docs/lib/merge-refs.ts
Normal file
15
docs/lib/merge-refs.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import type * as React from 'react';
|
||||
|
||||
export function mergeRefs<T>(
|
||||
...refs: (React.Ref<T> | undefined)[]
|
||||
): React.RefCallback<T> {
|
||||
return (value) => {
|
||||
refs.forEach((ref) => {
|
||||
if (typeof ref === 'function') {
|
||||
ref(value);
|
||||
} else if (ref) {
|
||||
ref.current = value;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -11,13 +11,17 @@
|
|||
"format": "biome format --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"fumadocs-core": "15.8.1",
|
||||
"fumadocs-mdx": "12.0.1",
|
||||
"fumadocs-ui": "15.8.1",
|
||||
"lucide-react": "^0.544.0",
|
||||
"next": "15.5.4",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1"
|
||||
"react-dom": "^19.1.1",
|
||||
"tailwind-merge": "^3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "24.5.2",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { remarkMdxFiles } from 'fumadocs-core/mdx-plugins';
|
||||
import {
|
||||
defineConfig,
|
||||
defineDocs,
|
||||
|
|
@ -21,6 +22,6 @@ export const docs = defineDocs({
|
|||
|
||||
export default defineConfig({
|
||||
mdxOptions: {
|
||||
// MDX options
|
||||
remarkPlugins: [remarkMdxFiles],
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue