m/cup
1
0
mirror of https://github.com/sergi0g/cup.git synced 2025-11-15 08:33:49 -05:00

Create basic homepage and format docs

This commit is contained in:
Sergio
2024-12-20 19:24:22 +02:00
parent 0a4e302322
commit 359147770f
47 changed files with 3365 additions and 1306 deletions

3
docs/.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}

View File

@@ -0,0 +1,15 @@
import React from "react";
export function GitHubIcon({ className }: { className?: string | undefined }) {
return (
<svg
width="24"
height="24"
fill="currentColor"
viewBox="3 3 18 18"
className={className}
>
<path d="M12 3C7.0275 3 3 7.12937 3 12.2276C3 16.3109 5.57625 19.7597 9.15374 20.9824C9.60374 21.0631 9.77249 20.7863 9.77249 20.5441C9.77249 20.3249 9.76125 19.5982 9.76125 18.8254C7.5 19.2522 6.915 18.2602 6.735 17.7412C6.63375 17.4759 6.19499 16.6569 5.8125 16.4378C5.4975 16.2647 5.0475 15.838 5.80124 15.8264C6.51 15.8149 7.01625 16.4954 7.18499 16.7723C7.99499 18.1679 9.28875 17.7758 9.80625 17.5335C9.885 16.9337 10.1212 16.53 10.38 16.2993C8.3775 16.0687 6.285 15.2728 6.285 11.7432C6.285 10.7397 6.63375 9.9092 7.20749 9.26326C7.1175 9.03257 6.8025 8.08674 7.2975 6.81794C7.2975 6.81794 8.05125 6.57571 9.77249 7.76377C10.4925 7.55615 11.2575 7.45234 12.0225 7.45234C12.7875 7.45234 13.5525 7.55615 14.2725 7.76377C15.9937 6.56418 16.7475 6.81794 16.7475 6.81794C17.2424 8.08674 16.9275 9.03257 16.8375 9.26326C17.4113 9.9092 17.76 10.7281 17.76 11.7432C17.76 15.2843 15.6563 16.0687 13.6537 16.2993C13.98 16.5877 14.2613 17.1414 14.2613 18.0065C14.2613 19.2407 14.25 20.2326 14.25 20.5441C14.25 20.7863 14.4188 21.0746 14.8688 20.9824C16.6554 20.364 18.2079 19.1866 19.3078 17.6162C20.4077 16.0457 20.9995 14.1611 21 12.2276C21 7.12937 16.9725 3 12 3Z"></path>
</svg>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

26
docs/components/Card.tsx Normal file
View File

@@ -0,0 +1,26 @@
import { ReactNode, createElement } from "react";
export function Card({
name,
icon,
description,
}: {
name: string;
icon: ReactNode;
description: string;
}) {
const iconElement = createElement(icon, {
className: "text-black size-7 dark:text-white inline mr-2",
});
return (
<div>
{iconElement}
<span className="align-middle text-2xl font-bold text-black dark:text-white">
{name}
</span>
<p className="text-2xl font-semibold text-neutral-500 dark:text-neutral-500">
{description}
</p>
</div>
);
}

View File

@@ -0,0 +1,31 @@
import React from "react";
import { clsx } from "clsx";
export function GradientText({
text,
innerClassName,
className,
blur,
}: {
text: string;
innerClassName: string;
className?: string;
blur: number;
}) {
return (
<div className={clsx("relative", className)}>
<p className={clsx("bg-clip-text text-transparent", innerClassName)}>
{text}
</p>
<p
className={clsx(
"pointer-events-none absolute top-0 hidden select-none bg-clip-text text-transparent dark:block",
innerClassName,
)}
style={{ filter: `blur(${blur}px)` }}
>
{text}
</p>
</div>
);
}

View File

@@ -0,0 +1,32 @@
import { useId } from "react";
const SIZE = 36;
export function GridPattern() {
const id = useId();
return (
<svg
aria-hidden="true"
className="pointer-events-none absolute inset-0 bottom-0 left-0 right-0 top-0 -z-10 h-full w-full stroke-neutral-200 dark:stroke-neutral-600/30"
>
<defs>
<pattern
id={id}
width={SIZE}
height={SIZE}
patternUnits="userSpaceOnUse"
x={0}
y={0}
>
<path
d={`M.5 ${SIZE}V.5H${SIZE}`}
fill="none"
strokeDasharray={"4 2"}
/>
</pattern>
</defs>
<rect width="100%" height="100%" strokeWidth={0} fill={`url(#${id})`} />
</svg>
);
}

View File

@@ -0,0 +1,28 @@
import { ReactNode } from "react";
import { GradientText } from "./GradientText";
export function Section({
title,
className,
children,
}: {
title: string;
className: string;
children: ReactNode;
}) {
return (
<div className="border-t bg-neutral-50 py-32 dark:border-t-neutral-600/30 dark:bg-neutral-950">
<div className="mx-auto w-full max-w-screen-xl">
<GradientText
text={title}
className="mx-auto mb-20 w-fit text-center text-4xl font-bold tracking-tighter"
innerClassName={className}
blur={12}
/>
<div className="m-2 grid w-full auto-cols-fr gap-20 lg:grid-cols-3">
{children}
</div>
</div>
</div>
);
}

23
docs/components/Step.tsx Normal file
View File

@@ -0,0 +1,23 @@
import React, { ReactNode } from "react";
export function Step({
children,
title,
number,
}: {
children: ReactNode;
title: string;
number: number;
}) {
return (
<div className="mb-2 flex grow-0 items-baseline">
<p className="m-2 flex size-10 items-center justify-center rounded-full bg-neutral-100 dark:bg-neutral-900">
{number}
</p>
<div>
<p className="text-xl font-bold">{title}</p>
<div className="my-6">{children}</div>
</div>
</div>
);
}

26
docs/components/Thing.tsx Normal file
View File

@@ -0,0 +1,26 @@
import React, { ReactNode, createElement } from "react";
export function Thing({
title,
icon,
content,
}: {
title: string;
icon: ReactNode;
content: string;
}) {
const iconElement = createElement(icon, {
className: "text-black size-7 dark:text-white inline mr-2",
});
return (
<div>
{iconElement}
<span className="align-middle text-2xl font-bold text-black dark:text-white">
{title}
</span>
<p className="text-2xl font-semibold text-neutral-500 dark:text-neutral-500">
{content}
</p>
</div>
);
}

View File

@@ -0,0 +1,176 @@
import React, { useState, useEffect } from "react";
import {
IconAdjustments,
IconArrowRight,
IconBolt,
IconBraces,
IconCheck,
IconClipboard,
IconDevices,
IconFeather,
IconLockCheck,
} from "@tabler/icons-react";
import { GitHubIcon } from "../../assets/GitHubIcon";
import { GridPattern } from "../GridPattern";
import { Section } from "../Section";
import { GradientText } from "../GradientText";
import Image from "next/image";
import screenshot_light from "../../assets/screenshot_light.png";
import screenshot_dark from "../../assets/screenshot_dark.png";
import { Step } from "../Step";
import { Card } from "../Card";
export function Home() {
const [copySuccess, setCopySuccess] = useState(false);
const [isBrowser, setIsBrowser] = useState(false); // To prevent hydration mismatch
useEffect(() => setIsBrowser(true));
const handleCopy = (text: string) => {
return () => {
navigator.clipboard.writeText(text).then(() => {
setCopySuccess(true);
setTimeout(() => {
setCopySuccess(false);
}, 3000);
});
};
};
return (
<div className="home-animation" style={{ opacity: 0 }}>
<div className="relative h-full overflow-x-hidden p-4 pt-10 lg:pt-20">
<GridPattern />
<div className="mx-auto h-full min-h-svh w-full max-w-screen-xl md:h-[46rem] md:min-h-0">
<div className="grid gap-8 lg:grid-cols-2">
<div className="flex max-w-3xl flex-col gap-8">
<div className="text-6xl font-extrabold leading-none tracking-tighter sm:text-7xl">
The easiest way to manage your
<GradientText
text="container updates."
innerClassName="bg-gradient-to-r from-blue-500 to-green-500"
blur={30}
/>
</div>
<h3 className="text-xl text-neutral-600 dark:text-neutral-400">
Cup is a small utility with a big impact. Simplify your
container management workflow with fast and efficient update
checking, a full-featured CLI and web inteface, and more.
</h3>
<div className="*:-0 mt-auto grid w-fit grid-cols-2 gap-4 *:flex *:items-center *:gap-2 *:rounded-lg *:px-3 *:py-2">
<a
href="/docs"
target="_blank"
className="hide-focus group h-full bg-black text-white dark:bg-white dark:text-black"
>
Get started
<IconArrowRight className="ml-auto mr-1 transition-transform duration-300 ease-out group-hover:translate-x-1 group-focus:translate-x-1 dark:!text-black" />
</a>
<a
href="https://github.com/sergi0g/cup"
target="_blank"
className="hide-focus h-full text-nowrap border border-neutral-400 transition-colors duration-200 ease-in-out hover:border-neutral-600 focus:border-neutral-600 dark:border-neutral-600 hover:dark:border-neutral-400 hover:dark:shadow-sm hover:dark:shadow-neutral-600 focus:dark:border-neutral-400"
>
Star on GitHub
<GitHubIcon className="ml-auto size-4 md:size-5" />
</a>
</div>
</div>
<div className="h-full">
<div className="max-h-[33.75rem] max-w-full overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-800">
<Image
src={screenshot_light}
alt="Screenshot of Cup's web interface"
className="dark:hidden"
/>
<Image
src={screenshot_dark}
alt="Screenshot of Cup's web interface"
className="hidden dark:block"
/>
</div>
</div>
</div>
</div>
</div>
<Section
title="Powerful at its core."
className="bg-gradient-to-r from-red-500 to-amber-500"
>
<Card
name="100% Safe Code"
icon={IconLockCheck}
description="Built with safe Rust and Typescript to ensure security and reliability."
/>
<Card
name="Lightning Fast Performance"
icon={IconBolt}
description="Heavily optimized to squeeze out every last drop of performance. Each release is extensively benchmarked and profiled so that you'll never have to stare at a loading spinner for long."
/>
<Card
name="Lightweight"
icon={IconFeather}
description="No runtimes or libraries are needed. All you need is the 5.3 MB static binary that works out of the box on any system."
/>
</Section>
<Section
title="Efficient, yet flexible."
className="bg-gradient-to-r from-blue-500 to-indigo-500"
>
<Card
name="JSON output"
description="Connect Cup to your favorite intergrations with JSON output for the CLI and an API for the server. Now go make that cool dashboard you've been dreaming of!"
icon={IconBraces}
/>
<Card
name="Both CLI and web interface"
description="Whether you prefer the command line, or the web, Cup runs wherever you choose."
icon={IconDevices}
/>
<Card
name="Configurable"
description="The simple configuration file provides you with all the tools you need to specify a custom Docker socket, manage registry connection options, choose a theme for the web interface and more."
icon={IconAdjustments}
/>
</Section>
<div className="relative py-24">
<GridPattern />
<div className="mx-auto flex w-full max-w-screen-xl flex-col items-center">
<p className="mb-8 text-center text-3xl font-bold">
Still not convinced? Try it out now!
</p>
<div>
<Step title="Open a terminal and run" number={1}>
<div className="group relative mx-auto flex max-w-screen-xl items-center rounded-lg bg-neutral-100 px-3 py-2 font-mono text-neutral-700 dark:bg-neutral-950 dark:text-neutral-300">
<p className="overflow-scroll">
docker run -v /var/run/docker.sock:/var/run/docker.sock -p
8000:8000 -t ghcr.io/sergi0g/cup serve
</p>
{isBrowser &&
navigator.clipboard &&
(copySuccess ? (
<IconCheck className="absolute right-3 size-7 bg-neutral-100 pl-2 dark:bg-neutral-950" />
) : (
<button
className="duration-50 absolute right-3 bg-neutral-100 pl-2 opacity-0 transition-opacity group-hover:opacity-100 dark:bg-neutral-950"
onClick={handleCopy(
"docker run --rm -t -v /var/run/docker.sock:/var/run/docker.sock -p 8000:8000 ghcr.io/sergi0g/cup serve",
)}
>
<IconClipboard className="size-5" />
</button>
))}
</div>
</Step>
<Step number={2} title="Open the dashboard in your browser">
<p>
Visit{" "}
<a href="http://localhost:8000" className="underline">
http://localhost:8000
</a>{" "}
in your browser to try it out!
</p>
</Step>
</div>
</div>
</div>
</div>
);
}

5
docs/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

View File

@@ -3,15 +3,14 @@ const withNextra = require("nextra")({
themeConfig: "./theme.config.jsx", themeConfig: "./theme.config.jsx",
}); });
module.exports = withNextra( module.exports = withNextra({
{
output: "export", output: "export",
images: { images: {
unoptimized: true unoptimized: true,
}, },
basePath: process.env.NODE_ENV == 'production' ? '/cup' : '' transpilePackages: ["geist", "framer-motion"],
} basePath: process.env.NODE_ENV == "production" ? "/cup" : "",
); });
// If you have other Next.js configurations, you can pass them as the parameter: // If you have other Next.js configurations, you can pass them as the parameter:
// module.exports = withNextra({ /* other next.js config */ }) // module.exports = withNextra({ /* other next.js config */ })

View File

@@ -2,10 +2,13 @@
"scripts": { "scripts": {
"dev": "next", "dev": "next",
"build": "next build", "build": "next build",
"start": "next start" "start": "next start",
"fmt": "prettier --ignore-path ../.gitignore --write ."
}, },
"dependencies": { "dependencies": {
"@tabler/icons-react": "^3.11.0", "@tabler/icons-react": "^3.11.0",
"clsx": "^2.1.1",
"geist": "^1.3.1",
"next": "^14.2.10", "next": "^14.2.10",
"nextra": "^2.13.4", "nextra": "^2.13.4",
"nextra-theme-docs": "^2.13.4", "nextra-theme-docs": "^2.13.4",
@@ -13,9 +16,13 @@
"react-dom": "^18.3.1" "react-dom": "^18.3.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "22.10.2",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"postcss": "^8.4.39", "postcss": "^8.4.39",
"tailwindcss": "^3.4.5" "prettier": "^3.4.2",
"prettier-plugin-tailwindcss": "^0.6.9",
"tailwindcss": "^3.4.5",
"typescript": "5.7.2"
}, },
"packageManager": "pnpm@9.10.0+sha512.73a29afa36a0d092ece5271de5177ecbf8318d454ecd701343131b8ebc0c1a91c487da46ab77c8e596d6acf1461e3594ced4becedf8921b074fbd8653ed7051c" "packageManager": "pnpm@9.10.0+sha512.73a29afa36a0d092ece5271de5177ecbf8318d454ecd701343131b8ebc0c1a91c487da46ab77c8e596d6acf1461e3594ced4becedf8921b074fbd8653ed7051c"
} }

View File

@@ -1,10 +1,12 @@
import '../styles.css'; import "../styles.css";
import 'nextra-theme-docs/style.css'; import "nextra-theme-docs/style.css";
import { GeistSans } from "geist/font/sans";
export default function App({ Component, pageProps }) { export default function App({ Component, pageProps }) {
return ( return (
<main> <main className={GeistSans.className}>
<Component {...pageProps} /> <Component {...pageProps} />
</main> </main>
); );
} }

45
docs/pages/_document.tsx Normal file
View File

@@ -0,0 +1,45 @@
import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import type { DocumentInitialProps, DocumentContext } from "next/document";
class DocumentProMax extends Document {
static async getInitialProps(
ctx: DocumentContext,
): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
return initialProps;
}
render() {
return (
<Html lang="en">
<Head>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="#ffffff"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#111111"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default DocumentProMax;

View File

@@ -3,11 +3,12 @@
"title": "Documentation", "title": "Documentation",
"type": "page" "type": "page"
}, },
"about": { "index": {
"title": "About",
"type": "page", "type": "page",
"title": "Cup",
"display": "hidden",
"theme": { "theme": {
"typesetting": "article" "layout": "raw"
} }
} }
} }

View File

@@ -1,110 +0,0 @@
import Image from "next/image";
import old_cup from "../assets/old_cup.png"
import web_ui from "../assets/blue_theme.png"
# About
Cup is a small utility that checks for updates to Docker containers. The logic is simple: Cup checks the locally pulled images' digests against the latest ones in their registry. It then presents the results in a pretty interface. Here's the story:
## How it started
I got the basic idea for Cup a long time ago. I was looking at [Homepage's list of widgets](https://gethomepage.dev/latest/widgets/) when I discovered [What's Up Docker?](https://github.com/getwud/wud) (referred to as WUD from now on).
According to the docs:
> What's up Docker ( aka WUD ) gets you notified when a new version of your Docker Container is available.
It supports the most common registries, has integrations with IFTTT, Slack, Telegram and other apps/services for notifications or triggering workflows and also has the option to automatically update containers, like [Watchtower](https://github.com/containrrr/watchtower).
I was managing my homelab myself at that time and the only way to check if I had updates was log in to the server and manually try to pull the images for *every single compose file*. WUD seemed to solve the problem nicely, so I decided to give it a try. I never used automatic updates or notifications, but I configured it and let it run.
After deploying it and setting up my reverse proxy, I was greeted with this dashboard:
<Image src="https://github.com/getwud/wud/blob/master/docs/ui/ui.png?raw=true" alt="A screenshot of WUD's web UI, from the docs" />
It was working fine, but... the UI was not what I expected. It really reminds me of some really old Android app (I hope I didn't offend anyone). That was strike one. Nevertheless, I left it running. It was useful after all.
A few days later I was pulling some docker images, when I got this error message:
> You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limits.
Wait a minute. What was that? I'd never encountered a message like this before. I thought "Weird. Maybe I pulled too many images today?". So I decided to finish those updates another day.
Next time I tried, same issue. "What the heck is happening?" I thought. The only change I'd made to my homelab at that time was installing WUD. So I stopped it. And that's where the problems ended.
The problem was clearly related to WUD, so I started trying to find what was going wrong. That was when I came upon [this page from Docker's documentation](https://docs.docker.com/docker-hub/download-rate-limit/). I noticed 2 things:
> A pull request is defined as up to two `GET` requests on registry manifest URLs (`/v2/*/manifests/*`)
> `HEAD` requests aren't counted.
There were also helpful instructions on how to check the rate limit:
```
sergio@desktop:~ $ TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5429 0 5429 0 0 7431 0 --:--:-- --:--:-- --:--:-- 7426
sergio@desktop:~ $ curl --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: "sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"
date: Tue, 16 Jul 2024 12:13:17 GMT
strict-transport-security: max-age=31536000
ratelimit-limit: 100;w=21600
ratelimit-remaining: 100;w=21600
docker-ratelimit-source: <REDACTED>
```
The rate limit is there, just like in the docs, but do you see something else interesting? Look at this header: `docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020`
This is an image's digest. Can we check for updates by making `HEAD` requests to Docker Hub?
The answer is yes:
```
$ set TOKEN $(curl -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/busybox:pull" | jq -r .token)
$ curl --head -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.docker.distribution.manifest.v2.list+json" https://registry-1.docker.io/v2/library/busybox/manifests/latest
HTTP/1.1 200 OK
content-length: 6761
content-type: application/vnd.oci.image.index.v1+json
docker-content-digest: sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
docker-distribution-api-version: registry/2.0
etag: "sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7"
date: Tue, 16 Jul 2024 12:17:49 GMT
strict-transport-security: max-age=31536000
ratelimit-limit: 100;w=21600
ratelimit-remaining: 100;w=21600
docker-ratelimit-source: <REDACTED>
```
And then we can compare that with the digest of the image stored locally:
```
$ docker inspect busybox:latest | jq -r '.[0].RepoDigests[0]'
busybox@sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
```
Notice how the 2 digests are the same. We can check for image updates without using up the rate limit!
That's when I got the idea of writing a program to do this automatically.
## The birth of Cup
I initially intended to write a simple bash script but I chose not to for the following reasons:
- I wanted something more than a simple script. WUD has a web UI and support for so many integrations! I had to match that some way!
- Bash is slow and I was learning Rust at the time, so I wanted to practice (and make a proper project)
It started out as a small CLI that could either check a single image, or check all the images.
<Image src={old_cup} alt="The initial version of Cup" />
It also couldn't check for updates to images not from Docker Hub, lacked a web UI and generally had many limitations. But it proved it could be done, quickly and efficiently. The binary was just 5 MB and took about 5 seconds for ~90 images on my development machine. That's insane!
A few days later, I decided to completely rewrite it. I tried to write clean code, split it in files and fix every limitation from the previous version. I'm quite close. Here's what it looks like now:
<Image src="https://github.com/sergi0g/cup/blob/main/screenshots/cup.gif?raw=true" alt="Cup's old CLI" />
It also has a statically rendered web UI making it ideal for self hosting.
<Image src={web_ui} alt="Cup's web UI"/>
With some optimization (well ok, maybe a lot), the binary is 5 MB and that means I finally don't have to wait forever to pull the Docker image! Finally something that works nicely with my 1.5 MB/s internet connection! (Thank you powerline!)
Now go ahead and try it out!

View File

@@ -1,6 +1,6 @@
import Image from 'next/image'; import Image from "next/image";
import widget1 from '../../../assets/350767810-42eccc89-bdfd-426a-a113-653abe7483d8.png' import widget1 from "../../../assets/350767810-42eccc89-bdfd-426a-a113-653abe7483d8.png";
import widget2 from '../../../assets/358304960-e9f26767-51f7-4b5a-8b74-a5811019497b.jpeg' import widget2 from "../../../assets/358304960-e9f26767-51f7-4b5a-8b74-a5811019497b.jpeg";
# Homepage Widget # Homepage Widget
@@ -39,7 +39,9 @@ services:
homepage.widget.mappings[2].field.metrics: update_available homepage.widget.mappings[2].field.metrics: update_available
homepage.widget.mappings[2].format: number homepage.widget.mappings[2].format: number
``` ```
Preview: Preview:
<Image src={widget1} /> <Image src={widget1} />
Credit: [@agrmohit](https://github.com/agrmohit) Credit: [@agrmohit](https://github.com/agrmohit)
@@ -70,6 +72,8 @@ widget:
label: Unknown label: Unknown
format: number format: number
``` ```
Preview: Preview:
<Image src={widget2} /> <Image src={widget2} />
Credit: [@remussamoila](https://github.com/remussamoila) Credit: [@remussamoila](https://github.com/remussamoila)

View File

@@ -1,5 +1,10 @@
import { Steps, Callout, Card, Cards } from "nextra-theme-docs"; import { Steps, Callout, Card, Cards } from "nextra-theme-docs";
import { IconPaint, IconLockOpen, IconKey, IconPlug } from '@tabler/icons-react'; import {
IconPaint,
IconLockOpen,
IconKey,
IconPlug,
} from "@tabler/icons-react";
# Configuration # Configuration
@@ -14,7 +19,12 @@ $ cup -s /run/user/1000/podman/podman.sock check
``` ```
This option is also available in the configuration file and it's best to put it there. This option is also available in the configuration file and it's best to put it there.
<Card icon={<IconPlug />} title="Custom Docker socket" href="/docs/configuration/socket" />
<Card
icon={<IconPlug />}
title="Custom Docker socket"
href="/docs/configuration/socket"
/>
## Configuration file ## Configuration file
@@ -26,15 +36,25 @@ Create a `cup.json` file somewhere on your system. For binary installs, a path l
If you're running with Docker, you can create a `cup.json` in the directory you're running cup and mount it into the container. _In the next section you will need to use the path where you **mounted** the file_ If you're running with Docker, you can create a `cup.json` in the directory you're running cup and mount it into the container. _In the next section you will need to use the path where you **mounted** the file_
### Configure Cup from the configuration file ### Configure Cup from the configuration file
Follow the guides below to customize your `cup.json` Follow the guides below to customize your `cup.json`
<Cards> <Cards>
<Card icon={<IconKey />} title="Authentication" href="/docs/configuration/authentication" /> <Card
<Card icon={<IconLockOpen />} title="Insecure registries" href="/docs/configuration/insecure-registries" /> icon={<IconKey />}
title="Authentication"
href="/docs/configuration/authentication"
/>
<Card
icon={<IconLockOpen />}
title="Insecure registries"
href="/docs/configuration/insecure-registries"
/>
<Card icon={<IconPaint />} title="Theme" href="/docs/configuration/theme" /> <Card icon={<IconPaint />} title="Theme" href="/docs/configuration/theme" />
</Cards> </Cards>
Here's a full example: Here's a full example:
```json ```json
{ {
"authentication": { "authentication": {
@@ -47,6 +67,7 @@ Here's a full example:
``` ```
### Run Cup with the new configuration file ### Run Cup with the new configuration file
To let Cup know that you'd like it to use a custom configuration file, you can use the `-c` flag, followed by the _absolute_ path of the file. To let Cup know that you'd like it to use a custom configuration file, you can use the `-c` flag, followed by the _absolute_ path of the file.
```bash ```bash
@@ -56,4 +77,5 @@ $ cup -c /home/sergio/.config/cup.json check
```bash ```bash
$ docker run -tv /var/run/docker.sock:/var/run/docker.sock -v /home/sergio/.config/cup.json:/config/cup.json ghcr.io/sergi0g/cup -c /config/cup.json serve $ docker run -tv /var/run/docker.sock:/var/run/docker.sock -v /home/sergio/.config/cup.json:/config/cup.json ghcr.io/sergi0g/cup -c /config/cup.json serve
``` ```
</Steps> </Steps>

View File

@@ -1,4 +1,4 @@
import { Callout } from 'nextra-theme-docs' import { Callout } from "nextra-theme-docs";
# Authentication # Authentication
@@ -10,13 +10,11 @@ Some registries (or specific images) may require you to be authenticated. For th
"<YOUR_REGISTRY_DOMAIN_1>": "<YOUR_TOKEN_1>", "<YOUR_REGISTRY_DOMAIN_1>": "<YOUR_TOKEN_1>",
"<YOUR_REGISTRY_DOMAIN_2>": "<YOUR_TOKEN_2>" "<YOUR_REGISTRY_DOMAIN_2>": "<YOUR_TOKEN_2>"
// ... // ...
}, }
// Other options // Other options
} }
``` ```
You can use any registry, like `ghcr.io`, `quay.io`, `gcr.io`, etc. You can use any registry, like `ghcr.io`, `quay.io`, `gcr.io`, etc.
<Callout emoji="⚠️"> <Callout emoji="⚠️">For Docker Hub, use `registry-1.docker.io`</Callout>
For Docker Hub, use `registry-1.docker.io`
</Callout>

View File

@@ -1,4 +1,4 @@
import { Callout } from 'nextra-theme-docs' import { Callout } from "nextra-theme-docs";
# Insecure registries # Insecure registries
@@ -10,11 +10,13 @@ Here's what it looks like:
```json ```json
{ {
"insecure_registries": ["<INSECURE_REGISTRY_1>", "<INSECURE_REGISTRY_2>"], "insecure_registries": ["<INSECURE_REGISTRY_1>", "<INSECURE_REGISTRY_2>"]
// Other options // Other options
} }
``` ```
<Callout emoji="⚠️"> <Callout emoji="⚠️">
When configuring an insecure registry that doesn't run on port 80, don't forget to specify it (i.e. use `localhost:5000` instead of `localhost` if your registry is running on port `5000`) When configuring an insecure registry that doesn't run on port 80, don't
forget to specify it (i.e. use `localhost:5000` instead of `localhost` if your
registry is running on port `5000`)
</Callout> </Callout>

View File

@@ -2,13 +2,11 @@ import { Callout } from "nextra-theme-docs";
import Image from "next/image"; import Image from "next/image";
import blue from "../../../assets/blue_theme.png"; import blue from "../../../assets/blue_theme.png";
import gray from "../../../assets/gray_theme.png"; import neutral from "../../../assets/neutral_theme.png";
# Theme # Theme
<Callout emoji="⚠️"> <Callout emoji="⚠️">This configuration option is only for the server</Callout>
This configuration option is only for the server
</Callout>
Cup initially had a blue theme which looked like this: Cup initially had a blue theme which looked like this:
@@ -16,7 +14,7 @@ Cup initially had a blue theme which looked like this:
This was replaced by a more neutral theme which is now the default: This was replaced by a more neutral theme which is now the default:
<Image alt="Screenshot of neutral theme" src={gray} /> <Image alt="Screenshot of neutral theme" src={neutral} />
However, you can get the old theme back by adding the `theme` key to your `cup.json` However, you can get the old theme back by adding the `theme` key to your `cup.json`
Available values are `default` and `blue`. Available values are `default` and `blue`.
@@ -25,7 +23,7 @@ Here's an example:
```json ```json
{ {
"theme": "blue", "theme": "blue"
// Other options // Other options
} }
``` ```

View File

@@ -5,6 +5,7 @@ First of all, thanks for taking time to contribute to Cup! This guide will help
## Setting up a development environment ## Setting up a development environment
Requirements: Requirements:
- A computer running Linux - A computer running Linux
- Rust (usually installed from https://rustup.rs/) - Rust (usually installed from https://rustup.rs/)
- Node.js 22+ and Bun 1+ - Node.js 22+ and Bun 1+
@@ -27,8 +28,9 @@ All server specific functionality is located in `src/server.rs` and `web/`.
## Important notes ## Important notes
- When making any changes, always make sure to write optimize your code for: - When making any changes, always make sure to write optimize your code for:
+ Performance: You should always benchmark Cup before making changes and after your changes to make sure there is none (or a very small) difference in time. Profiling old and new code is also good.
+ Readability: Include comments describing any new functions you create, give descriptive names to variables and when making a design decision or a compromise, ALWAYS include a comment explaining what you did and why. - Performance: You should always benchmark Cup before making changes and after your changes to make sure there is none (or a very small) difference in time. Profiling old and new code is also good.
- Readability: Include comments describing any new functions you create, give descriptive names to variables and when making a design decision or a compromise, ALWAYS include a comment explaining what you did and why.
- If you plan on developing the frontend without making backend changes, it is highly recommended to run `cup serve` in the background and start the frontend in development mode from `web/` with `bun dev`. - If you plan on developing the frontend without making backend changes, it is highly recommended to run `cup serve` in the background and start the frontend in development mode from `web/` with `bun dev`.

View File

@@ -21,6 +21,14 @@ Cup is a lightweight alternative to [What's up Docker?](https://github.com/getwu
# Installation # Installation
<Cards> <Cards>
<Card icon={<IconBrandDocker />} title="With Docker" href="/docs/installation/docker" /> <Card
<Card icon={<IconPackage />} title="As a binary" href="/docs/installation/binary" /> icon={<IconBrandDocker />}
title="With Docker"
href="/docs/installation/docker"
/>
<Card
icon={<IconPackage />}
title="As a binary"
href="/docs/installation/binary"
/>
</Cards> </Cards>

View File

@@ -2,6 +2,7 @@ import { Callout, Card, Steps } from "nextra-theme-docs";
import { IconFileDescription } from "@tabler/icons-react"; import { IconFileDescription } from "@tabler/icons-react";
# As a binary # As a binary
## Introduction ## Introduction
This guide will help you install Cup from a binary. This guide will help you install Cup from a binary.
@@ -13,6 +14,7 @@ This guide will help you install Cup from a binary.
Go to https://github.com/sergi0g/cup/releases/latest. Go to https://github.com/sergi0g/cup/releases/latest.
Depending on your system's architecture, choose the binary for your system. For example, for an `x86_64` machine, you should download `cup-x86_64-unknown-linux-musl` Depending on your system's architecture, choose the binary for your system. For example, for an `x86_64` machine, you should download `cup-x86_64-unknown-linux-musl`
<Callout> <Callout>
You can use the command `uname -i` to find this You can use the command `uname -i` to find this
</Callout> </Callout>
@@ -21,5 +23,6 @@ Move the binary you downloaded to a directory in your path. You can usually get
</Steps> </Steps>
That's it! Cup is ready to be used. Head over to the Usage page to get started. That's it! Cup is ready to be used. Head over to the Usage page to get started.
<br /> <br />
<Card icon={<IconFileDescription />} title="Usage" href="/docs/usage" /> <Card icon={<IconFileDescription />} title="Usage" href="/docs/usage" />

View File

@@ -2,6 +2,7 @@ import { Callout, Card } from "nextra-theme-docs";
import { IconFileDescription } from "@tabler/icons-react"; import { IconFileDescription } from "@tabler/icons-react";
# With Docker # With Docker
## Introduction ## Introduction
This guide will help you install Cup as a Docker container. It is the easiest installation method and also makes updating Cup very easy. This guide will help you install Cup as a Docker container. It is the easiest installation method and also makes updating Cup very easy.
@@ -9,13 +10,18 @@ This guide will help you install Cup as a Docker container. It is the easiest in
## Installation ## Installation
To get started, open up a terminal and run the following command. To get started, open up a terminal and run the following command.
```bash ```bash
$ docker pull ghcr.io/sergi0g/cup $ docker pull ghcr.io/sergi0g/cup
``` ```
<Callout emoji="⚠️"> <Callout emoji="⚠️">
If you aren't in the `docker` group, please ensure you run all commands as a user who does. In most cases, you'll just need to prefix the `docker` commands with `sudo` If you aren't in the `docker` group, please ensure you run all commands as a
user who does. In most cases, you'll just need to prefix the `docker` commands
with `sudo`
</Callout> </Callout>
That's it! Cup is ready to be used. Head over to the Usage page to get started. That's it! Cup is ready to be used. Head over to the Usage page to get started.
<br /> <br />
<Card icon={<IconFileDescription />} title="Usage" href="/docs/usage" /> <Card icon={<IconFileDescription />} title="Usage" href="/docs/usage" />

View File

@@ -1,4 +1,4 @@
import { Callout } from "nextra-theme-docs" import { Callout } from "nextra-theme-docs";
# Using the latest version # Using the latest version
@@ -9,7 +9,11 @@ However, it is only updated when a new release is created, so if you want the la
Cup's nightly version always contains the latest changes in the main branch. Cup's nightly version always contains the latest changes in the main branch.
<Callout emoji="⚠️"> <Callout emoji="⚠️">
There is no guarantee that the nightly version will always work. There may be breaking changes or a bad commit and it may not work properly. Install nightly only if you know what you are doing. These instructions will assume you have the technical know-how to follow them. If you do not, please use the stable release There is no guarantee that the nightly version will always work. There may be
breaking changes or a bad commit and it may not work properly. Install nightly
only if you know what you are doing. These instructions will assume you have
the technical know-how to follow them. If you do not, please use the stable
release
</Callout> </Callout>
## With Docker ## With Docker

View File

@@ -8,6 +8,7 @@ Cup's CLI provides the `cup check` command.
## Basic Usage ## Basic Usage
### Check for all updates ### Check for all updates
```ansi ```ansi
$ cup check $ cup check
nginx:alpine Update available nginx:alpine Update available
@@ -24,6 +25,7 @@ rabbitmq:3.11.9-management Up to date
``` ```
### Check for updates to specific images ### Check for updates to specific images
```ansi ```ansi
$ cup check node:latest $ cup check node:latest
node:latest Update available node:latest Update available
@@ -39,11 +41,13 @@ postgres:14 Update available
``` ```
## Enable icons ## Enable icons
You can also enable icons if you have a [Nerd Font](https://nerdfonts.com) installed. You can also enable icons if you have a [Nerd Font](https://nerdfonts.com) installed.
<Image src={cup} unoptimized /> <Image src={cup} unoptimized />
## JSON output ## JSON output
When integrating Cup with other services (e.g. webhooks or a dashboard), you may find Cup's JSON output functionality useful. When integrating Cup with other services (e.g. webhooks or a dashboard), you may find Cup's JSON output functionality useful.
It provides some useful metrics (see [server](/docs/usage/server) for more information), along with a list of images and whether they have an update or not. It provides some useful metrics (see [server](/docs/usage/server) for more information), along with a list of images and whether they have an update or not.
@@ -58,14 +62,14 @@ Here is how it would look in Typescript:
```ts ```ts
interface CupData { interface CupData {
metrics: { metrics: {
monitored_images: number, monitored_images: number;
up_to_date: number, up_to_date: number;
update_available: number, update_available: number;
unknown: number unknown: number;
}, };
images: { images: {
[image: string]: boolean | null [image: string]: boolean | null;
} };
} }
``` ```
@@ -74,10 +78,13 @@ interface CupData {
If you're using the Docker image, just replace all occurences of `cup` in the examples with `docker run -tv /var/run/docker.sock:/var/run/docker.sock ghcr.io/sergi0g/cup`. If you're using the Docker image, just replace all occurences of `cup` in the examples with `docker run -tv /var/run/docker.sock:/var/run/docker.sock ghcr.io/sergi0g/cup`.
For example, this: For example, this:
```bash /check node:latest/ ```bash /check node:latest/
$ cup check node:latest $ cup check node:latest
``` ```
becomes: becomes:
```bash /check node:latest/ ```bash /check node:latest/
$ docker run -tv /var/run/docker.sock:/var/run/docker.sock ghcr.io/sergi0g/cup check node:latest $ docker run -tv /var/run/docker.sock:/var/run/docker.sock ghcr.io/sergi0g/cup check node:latest
``` ```

View File

@@ -41,13 +41,17 @@ $ cup serve -p 9000
``` ```
## Usage with Docker ## Usage with Docker
If you're using the Docker image, just replace all occurences of `cup` in the examples with `docker run -tv /var/run/docker.sock:/var/run/docker.sock -p <PORT>:<PORT> ghcr.io/sergi0g/cup`, where `<PORT>` is the port Cup will be using. If you're using the Docker image, just replace all occurences of `cup` in the examples with `docker run -tv /var/run/docker.sock:/var/run/docker.sock -p <PORT>:<PORT> ghcr.io/sergi0g/cup`, where `<PORT>` is the port Cup will be using.
For example, this: For example, this:
```bash /serve -p 9000/ ```bash /serve -p 9000/
$ cup serve -p 9000 $ cup serve -p 9000
``` ```
becomes: becomes:
```bash /serve -p 9000/ ```bash /serve -p 9000/
$ docker run -tv /var/run/docker.sock:/var/run/docker.sock -p 9000:9000 ghcr.io/sergi0g/cup serve -p 9000 $ docker run -tv /var/run/docker.sock:/var/run/docker.sock -p 9000:9000 ghcr.io/sergi0g/cup serve -p 9000
``` ```

8
docs/pages/index.mdx Normal file
View File

@@ -0,0 +1,8 @@
---
title: Cup - The easiest way to manage your container updates
description: Simple, fast, efficient Docker image update checking
---
import { Home } from "../components/pages/Home";
<Home />

3637
docs/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
} };

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
docs/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

29
docs/public/favicon.svg Normal file
View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<path style="fill:#A6CFD6;" d="M65.12,17.55c-17.6-0.53-34.75,5.6-34.83,14.36c-0.04,5.2,1.37,18.6,3.62,48.68s2.25,33.58,3.5,34.95
c1.25,1.37,10.02,8.8,25.75,8.8s25.93-6.43,26.93-8.05c0.48-0.78,1.83-17.89,3.5-37.07c1.81-20.84,3.91-43.9,3.99-45.06
C97.82,30.66,94.2,18.43,65.12,17.55z"/>
<path style="fill:#DCEDF6;" d="M41.4,45.29c-0.12,0.62,1.23,24.16,2.32,27.94c1.99,6.92,9.29,7.38,10.23,4.16
c0.9-3.07-0.38-29.29-0.38-29.29s-3.66-0.3-6.43-0.84C44,46.63,41.4,45.29,41.4,45.29z"/>
<path style="fill:#6CA4AE;" d="M33.74,32.61c-0.26,8.83,20.02,12.28,30.19,12.22c13.56-0.09,29.48-4.29,29.8-11.7
S79.53,21.1,63.35,21.1C49.6,21.1,33.96,25.19,33.74,32.61z"/>
<path style="fill:#DC0D27;" d="M84.85,13.1c-0.58,0.64-9.67,30.75-9.67,30.75s2.01-0.33,4-0.79c2.63-0.61,3.76-1.06,3.76-1.06
s7.19-22.19,7.64-23.09c0.45-0.9,21.61-7.61,22.31-7.93c0.7-0.32,1.39-0.4,1.46-0.78c0.06-0.38-2.34-6.73-3.11-6.73
C110.47,3.47,86.08,11.74,84.85,13.1z"/>
<path style="fill:#8A1F0F;" d="M110.55,7.79c1.04,2.73,2.8,3.09,3.55,2.77c0.45-0.19,1.25-1.84,0.01-4.47
c-0.99-2.09-2.17-2.74-2.93-2.61C110.42,3.6,109.69,5.53,110.55,7.79z"/>
<g>
<path style="fill:#8A1F0F;" d="M91.94,18.34c-0.22,0-0.44-0.11-0.58-0.3l-3.99-5.77c-0.22-0.32-0.14-0.75,0.18-0.97
c0.32-0.22,0.76-0.14,0.97,0.18l3.99,5.77c0.22,0.32,0.14,0.75-0.18,0.97C92.21,18.3,92.07,18.34,91.94,18.34z"/>
</g>
<g>
<path style="fill:#8A1F0F;" d="M90.28,19.43c-0.18,0-0.35-0.07-0.49-0.2l-5.26-5.12c-0.28-0.27-0.28-0.71-0.01-0.99
c0.27-0.28,0.71-0.28,0.99-0.01l5.26,5.12c0.28,0.27,0.28,0.71,0.01,0.99C90.64,19.36,90.46,19.43,90.28,19.43z"/>
</g>
<g>
<path style="fill:#8A1F0F;" d="M89.35,21.22c-0.12,0-0.25-0.03-0.36-0.1l-5.6-3.39c-0.33-0.2-0.44-0.63-0.24-0.96
c0.2-0.33,0.63-0.44,0.96-0.24l5.6,3.39c0.33,0.2,0.44,0.63,0.24,0.96C89.82,21.1,89.59,21.22,89.35,21.22z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -2,6 +2,53 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
.tabler-icon { .nextra-card .tabler-icon:hover {
color: rgb(250 250 250 / var(--tw-text-opacity)) !important color: rgb(17 24 39 / var(--tw-text-opacity));
}
.nextra-card .tabler-icon {
color: rgb(55 65 81 / var(--tw-text-opacity));
}
.nextra-card .tabler-icon:is(.dark *) {
color: rgb(229 229 229 / var(--tw-text-opacity));
}
.nextra-card .tabler-icon:is(.dark *):hover {
color: rgb(250 250 250 / var(--tw-text-opacity));
}
.home-animation {
animation-name: fade-in;
animation-delay: 300ms;
animation-duration: 500ms;
animation-fill-mode: forwards;
animation-timing-function: ease-in;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.hide-focus:focus {
outline: none;
box-shadow: none;
}
.home-pattern {
background-color: white;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%23000000' fill-opacity='0.15'%3E%3Cpath d='M0 38.59l2.83-2.83 1.41 1.41L1.41 40H0v-1.41zM0 1.4l2.83 2.83 1.41-1.41L1.41 0H0v1.41zM38.59 40l-2.83-2.83 1.41-1.41L40 38.59V40h-1.41zM40 1.41l-2.83 2.83-1.41-1.41L38.59 0H40v1.41zM20 18.6l2.83-2.83 1.41 1.41L21.41 20l2.83 2.83-1.41 1.41L20 21.41l-2.83 2.83-1.41-1.41L18.59 20l-2.83-2.83 1.41-1.41L20 18.59z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
mask-image: radial-gradient(ellipse at top, white, transparent);
translate: 0 -0.5rem;
}
.home-pattern:is(.dark *) {
background-color: #111111;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.1'%3E%3Cpath d='M0 38.59l2.83-2.83 1.41 1.41L1.41 40H0v-1.41zM0 1.4l2.83 2.83 1.41-1.41L1.41 0H0v1.41zM38.59 40l-2.83-2.83 1.41-1.41L40 38.59V40h-1.41zM40 1.41l-2.83 2.83-1.41-1.41L38.59 0H40v1.41zM20 18.6l2.83-2.83 1.41 1.41L21.41 20l2.83 2.83-1.41 1.41L20 21.41l-2.83 2.83-1.41-1.41L18.59 20l-2.83-2.83 1.41-1.41L20 18.59z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
mask-image: radial-gradient(ellipse at top, #111111, transparent);
translate: 0 -0.5rem;
} }

View File

@@ -1,11 +1,9 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: [ darkMode: "class",
"theme.config.jsx" content: ["theme.config.jsx", "components/**/*.tsx"],
],
theme: { theme: {
extend: {}, extend: {},
}, },
plugins: [], plugins: [],
} };

View File

@@ -5,38 +5,39 @@ import { useConfig } from "nextra-theme-docs";
export default { export default {
docsRepositoryBase: "https://github.com/sergi0g/cup/tree/main/docs", docsRepositoryBase: "https://github.com/sergi0g/cup/tree/main/docs",
useNextSeoProps() { useNextSeoProps() {
const { asPath } = useRouter() const { asPath } = useRouter();
if (asPath !== '/') { if (asPath !== "/") {
return { return {
titleTemplate: '%s Cup' titleTemplate: "Cup %s",
} };
} }
}, },
head: () => { head: () => {
const { asPath } = useRouter() const { asPath } = useRouter();
const { frontMatter } = useConfig() const { frontMatter } = useConfig();
const url = const url = "https://sergi0g.github.io/cup/docs/" + `/${asPath}`;
'https://sergi0g.github.io/cup/docs/' +
(`/${asPath}`);
return ( return (
<> <>
<meta property="og:url" content={url} /> <meta property="og:url" content={url} />
<meta property="og:title" content={frontMatter.title || 'Cup'} /> <meta property="og:title" content={frontMatter.title || "Cup"} />
<meta <meta
property="og:description" property="og:description"
content={frontMatter.description || 'The easiest way to manage your container updates'} content={
frontMatter.description ||
"The easiest way to manage your container updates"
}
/> />
</> </>
) );
}, },
logo: ( logo: (
<div className="flex items-center"> <div className="flex items-center">
<Logo /> <Logo />
<h1 className="font-bold ml-2">Cup</h1> <h1 className="ml-2 font-bold">Cup</h1>
</div> </div>
), ),
logoLink: "https://sergi0g.github.io/cup/docs/", logoLink: "/",
project: { project: {
link: "https://github.com/sergi0g/cup/", link: "https://github.com/sergi0g/cup/",
}, },

18
docs/tsconfig.json Normal file
View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"noEmit": true,
"incremental": true,
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}