Compare commits
4 Commits
31b831f97b
...
9a5cc8642d
Author | SHA1 | Date | |
---|---|---|---|
9a5cc8642d | |||
8169c08faa | |||
d0f926f330 | |||
6e2fc9e13b |
@ -47,6 +47,7 @@ RUN adduser --system --uid 1001 nextjs
|
|||||||
|
|
||||||
# Copy the public folder
|
# Copy the public folder
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/documentation ./documentation
|
||||||
|
|
||||||
# Set the correct permission for prerender cache
|
# Set the correct permission for prerender cache
|
||||||
RUN mkdir .next
|
RUN mkdir .next
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"siteName": "Minecraft Utilities",
|
"name": "Minecraft Utilities",
|
||||||
"siteDescription": "Minecraft Utilities offers you many endpoints to get information about a minecraft server or a player.",
|
"description": "Minecraft Utilities offers you many endpoints to get information about a minecraft server or a player.",
|
||||||
"siteUrl": "https://mcutils.xyz/",
|
"publicUrl": "https://mcutils.xyz/",
|
||||||
"apiUrl": "https://api.mcutils.xyz"
|
"apiEndpoint": "https://api.mcutils.xyz"
|
||||||
}
|
}
|
||||||
|
11
documentation/landing.md
Normal file
11
documentation/landing.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
title:
|
||||||
|
---
|
||||||
|
|
||||||
|
# Minecraft Utilities Documentation
|
||||||
|
|
||||||
|
Welcome to the Minecraft Utilities documentation! Here you can find information on how to use the various features of the plugin.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This is still a work in progress, so please be patient as we continue to add more documentation.
|
@ -1,3 +1,5 @@
|
|||||||
|
import createMDX from '@next/mdx'
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
@ -17,4 +19,4 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default createMDX()(nextConfig)
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
"@eslint/eslintrc": "^3.0.2",
|
"@eslint/eslintrc": "^3.0.2",
|
||||||
"@heroicons/react": "^2.1.3",
|
"@heroicons/react": "^2.1.3",
|
||||||
"@hookform/resolvers": "^3.3.4",
|
"@hookform/resolvers": "^3.3.4",
|
||||||
|
"@mdx-js/loader": "^3.0.1",
|
||||||
|
"@mdx-js/react": "^3.0.1",
|
||||||
|
"@next/mdx": "^14.2.2",
|
||||||
"@radix-ui/react-context-menu": "^2.1.5",
|
"@radix-ui/react-context-menu": "^2.1.5",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
@ -21,6 +24,7 @@
|
|||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@radix-ui/react-toast": "^1.1.5",
|
"@radix-ui/react-toast": "^1.1.5",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
|
"@types/mdx": "^2.0.13",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clipboard-copy": "^4.0.1",
|
"clipboard-copy": "^4.0.1",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
@ -35,6 +39,7 @@
|
|||||||
"react-spinners": "^0.13.8",
|
"react-spinners": "^0.13.8",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
"react-use-websocket": "4.8.1",
|
"react-use-websocket": "4.8.1",
|
||||||
|
"remote-mdx": "^0.0.4",
|
||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
|
1869
pnpm-lock.yaml
generated
1869
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
49
src/app/(pages)/documentation/[[...slug]]/page.tsx
Normal file
49
src/app/(pages)/documentation/[[...slug]]/page.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { getDocumentation } from "@/common/documentation";
|
||||||
|
import { CustomMDX } from "@/app/components/mx-components";
|
||||||
|
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
let documentationPages = getDocumentation();
|
||||||
|
|
||||||
|
return documentationPages.map(page => ({
|
||||||
|
slug: [page.slug],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocumentationPageParams = {
|
||||||
|
params: {
|
||||||
|
slug?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Page({ params: { slug } }: DocumentationPageParams) {
|
||||||
|
const documentationPages = getDocumentation();
|
||||||
|
let page = documentationPages.find(page => page.slug === slug);
|
||||||
|
|
||||||
|
// Fallback to the landing page
|
||||||
|
if (!page) {
|
||||||
|
page = documentationPages.find(page => page.slug === "landing");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to a 404 page if we still can't find the page
|
||||||
|
if (!page) {
|
||||||
|
page = {
|
||||||
|
metadata: {
|
||||||
|
title: "404 - Not Found",
|
||||||
|
},
|
||||||
|
content: "If you are seeing this, it means that the documentation page you are looking for does not exist.",
|
||||||
|
slug: "empty",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full px-4 flex flex-col gap-4">
|
||||||
|
{/* The documentation page title */}
|
||||||
|
{page.metadata.title && <h1 className="text-center">{page.metadata.title}</h1>}
|
||||||
|
|
||||||
|
{/* The content of the documentation page */}
|
||||||
|
<div className="text-left w-full">
|
||||||
|
<CustomMDX source={page.content} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -32,8 +32,8 @@ type Button = {
|
|||||||
const buttons: Button[] = [
|
const buttons: Button[] = [
|
||||||
{
|
{
|
||||||
title: "Get Started",
|
title: "Get Started",
|
||||||
tooltip: "Click to view get started!",
|
tooltip: "Click to get started!",
|
||||||
url: "/player",
|
url: "/documentation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Postman Collection",
|
title: "Postman Collection",
|
||||||
|
@ -45,7 +45,7 @@ export async function generateMetadata({ params: { id } }: Params): Promise<Meta
|
|||||||
const headPartUrl = skin.parts.head;
|
const headPartUrl = skin.parts.head;
|
||||||
|
|
||||||
return generateEmbed({
|
return generateEmbed({
|
||||||
title: `${username}`,
|
title: `${username}'s Profile`,
|
||||||
description: `UUID: ${uniqueId}\n\nClick to view more information about the player.`,
|
description: `UUID: ${uniqueId}\n\nClick to view more information about the player.`,
|
||||||
image: headPartUrl,
|
image: headPartUrl,
|
||||||
});
|
});
|
||||||
@ -95,7 +95,7 @@ export default async function Page({ params: { id } }: Params): Promise<ReactEle
|
|||||||
<ContextMenuItem>Copy Player UUID</ContextMenuItem>
|
<ContextMenuItem>Copy Player UUID</ContextMenuItem>
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
|
|
||||||
<CopyButton content={`${config.siteUrl}/player/${id}`}>
|
<CopyButton content={`${config.publicUrl}/player/${id}`}>
|
||||||
<ContextMenuItem>Copy Share URL</ContextMenuItem>
|
<ContextMenuItem>Copy Share URL</ContextMenuItem>
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
</ContextMenuContent>
|
</ContextMenuContent>
|
||||||
|
@ -44,7 +44,7 @@ function getFavicon(
|
|||||||
server: CachedJavaMinecraftServer | CachedBedrockMinecraftServer | undefined,
|
server: CachedJavaMinecraftServer | CachedBedrockMinecraftServer | undefined,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
if (server == null || platform === ServerPlatform.Bedrock) {
|
if (server == null || platform === ServerPlatform.Bedrock) {
|
||||||
return config.apiUrl + "/server/icon/fallback";
|
return config.apiEndpoint + "/server/icon/fallback";
|
||||||
}
|
}
|
||||||
server = server as CachedJavaMinecraftServer;
|
server = server as CachedJavaMinecraftServer;
|
||||||
return server.favicon && server.favicon.url;
|
return server.favicon && server.favicon.url;
|
||||||
@ -93,7 +93,8 @@ export async function generateMetadata({ params: { platform, hostname } }: Param
|
|||||||
description += "Click to view more information about the server.";
|
description += "Click to view more information about the server.";
|
||||||
|
|
||||||
return generateEmbed({
|
return generateEmbed({
|
||||||
title: `${capitalizeFirstLetter(platform)} Server: ${serverHostname}`,
|
title: `${serverHostname} ${capitalizeFirstLetter(platform)} Server`,
|
||||||
|
embedTitle: `${capitalizeFirstLetter(platform)} Server: ${serverHostname}`,
|
||||||
description: description,
|
description: description,
|
||||||
image: favicon,
|
image: favicon,
|
||||||
});
|
});
|
||||||
|
@ -24,8 +24,8 @@ export function GithubStar(): ReactElement {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<p className="text-white text-sm bg-github-green-accent py-[3px] px-[4px] rounded-lg leading-none">{starCount}</p>
|
<p className="text-white text-sm bg-github-green-accent py-[3px] px-[4px] rounded-lg leading-none">{starCount}</p>
|
||||||
<Star size={16} />
|
<Star size={18} />
|
||||||
<p className="text-white text-sm">Star us!</p>
|
<p className="text-white text-sm leading-none">Star us!</p>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
10
src/app/components/mx-components.tsx
Normal file
10
src/app/components/mx-components.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { MDXRemote } from "remote-mdx/rsc";
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
h1: (props: any) => <h1 className="text-2xl font-semibold pb-2" {...props} />,
|
||||||
|
h2: (props: any) => <h1 className="text-xl font-semibold pb-2 pt-4" {...props} />,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CustomMDX(props: any) {
|
||||||
|
return <MDXRemote {...props} components={{ ...components, ...(props.components || {}) }} />;
|
||||||
|
}
|
@ -32,6 +32,7 @@ const pages: Page[] = [
|
|||||||
{ name: "Server", url: "/server/java" },
|
{ name: "Server", url: "/server/java" },
|
||||||
{ name: "Mojang", url: "/mojang/status" },
|
{ name: "Mojang", url: "/mojang/status" },
|
||||||
{ name: "API", url: "https://api.mcutils.xyz", openInNewTab: true },
|
{ name: "API", url: "https://api.mcutils.xyz", openInNewTab: true },
|
||||||
|
{ name: "Docs", url: "/documentation", openInNewTab: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function NavBar(): ReactElement {
|
export default function NavBar(): ReactElement {
|
||||||
|
@ -31,7 +31,7 @@ export function ServerView({ server, favicon }: ServerViewProps): ReactElement {
|
|||||||
>
|
>
|
||||||
{/* Favicon */}
|
{/* Favicon */}
|
||||||
<Image
|
<Image
|
||||||
src={favicon || `${config.apiUrl}/server/icon/fallback`}
|
src={favicon || `${config.apiEndpoint}/server/icon/fallback`}
|
||||||
alt={`${server.hostname}'s Favicon`}
|
alt={`${server.hostname}'s Favicon`}
|
||||||
width={64}
|
width={64}
|
||||||
height={64}
|
height={64}
|
||||||
|
@ -15,22 +15,22 @@ export const viewport: Viewport = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
metadataBase: new URL(config.siteUrl),
|
metadataBase: new URL(config.publicUrl),
|
||||||
title: {
|
title: {
|
||||||
template: config.siteName + " - %s",
|
template: "%s - " + config.name,
|
||||||
default: config.siteName,
|
default: config.name,
|
||||||
},
|
},
|
||||||
description: config.siteDescription,
|
description: config.description,
|
||||||
keywords: "Minecraft, APIs, wrapper, utility, development",
|
keywords: "Minecraft, APIs, wrapper, utility, development",
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: config.siteName,
|
title: config.name,
|
||||||
description: config.siteDescription,
|
description: config.description,
|
||||||
url: config.siteUrl,
|
url: config.publicUrl,
|
||||||
locale: "en_US",
|
locale: "en_US",
|
||||||
type: "website",
|
type: "website",
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: `${config.siteUrl}/media/logo.png`,
|
url: `${config.publicUrl}/media/logo.png`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
52
src/common/documentation.ts
Normal file
52
src/common/documentation.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import * as fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
type Metadata = {
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseFrontmatter(fileContent: string) {
|
||||||
|
let frontmatterRegex = /---\s*([\s\S]*?)\s*---/;
|
||||||
|
let match = frontmatterRegex.exec(fileContent);
|
||||||
|
let frontMatterBlock = match![1];
|
||||||
|
let content = fileContent.replace(frontmatterRegex, "").trim();
|
||||||
|
let frontMatterLines = frontMatterBlock.trim().split("\n");
|
||||||
|
let metadata: Partial<Metadata> = {};
|
||||||
|
|
||||||
|
frontMatterLines.forEach(line => {
|
||||||
|
let [key, ...valueArr] = line.split(": ");
|
||||||
|
let value = valueArr.join(": ").trim();
|
||||||
|
value = value.replace(/^['"](.*)['"]$/, "$1"); // Remove quotes
|
||||||
|
metadata[key.trim() as keyof Metadata] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { metadata: metadata as Metadata, content };
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentationDirectory = path.join(process.cwd(), "documentation");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the files for the documentation.
|
||||||
|
*/
|
||||||
|
function getDocumentationFiles() {
|
||||||
|
return fs.readdirSync(documentationDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDocumentationFileContent(file: string) {
|
||||||
|
return fs.readFileSync(path.join(documentationDirectory, file), "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDocumentation() {
|
||||||
|
const files = getDocumentationFiles();
|
||||||
|
|
||||||
|
return files.map(file => {
|
||||||
|
const { metadata, content } = parseFrontmatter(getDocumentationFileContent(file));
|
||||||
|
let slug = path.basename(file, path.extname(file));
|
||||||
|
|
||||||
|
return {
|
||||||
|
metadata,
|
||||||
|
content,
|
||||||
|
slug,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
@ -6,6 +6,11 @@ type Embed = {
|
|||||||
*/
|
*/
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title of the embed.
|
||||||
|
*/
|
||||||
|
embedTitle?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The description of the embed.
|
* The description of the embed.
|
||||||
*/
|
*/
|
||||||
@ -21,10 +26,17 @@ type Embed = {
|
|||||||
* Generates metadata for a embed.
|
* Generates metadata for a embed.
|
||||||
*
|
*
|
||||||
* @param title the title of the embed
|
* @param title the title of the embed
|
||||||
|
* @param embedTitle the title of the embed
|
||||||
* @param description the description of the embed
|
* @param description the description of the embed
|
||||||
|
* @param image the image to show as the thumbmail
|
||||||
* @returns the metadata for the embed
|
* @returns the metadata for the embed
|
||||||
*/
|
*/
|
||||||
export function generateEmbed({ title, description, image }: Embed): Metadata {
|
export function generateEmbed({ title, embedTitle, description, image }: Embed): Metadata {
|
||||||
|
// Fall back to the title
|
||||||
|
if (!embedTitle) {
|
||||||
|
embedTitle = title;
|
||||||
|
}
|
||||||
|
|
||||||
const metadata: Metadata = {
|
const metadata: Metadata = {
|
||||||
title: `${title}`,
|
title: `${title}`,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
|
Reference in New Issue
Block a user