Compare commits
2 Commits
c7d0f31852
...
21f31c0f1d
Author | SHA1 | Date | |
---|---|---|---|
21f31c0f1d | |||
0216662767 |
30
.gitea/workflows/deploy-frontend.yml
Normal file
30
.gitea/workflows/deploy-frontend.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Deploy Frontend
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["master"]
|
||||||
|
paths: [".gitea/workflows/deploy-frontend.yml", "Frontend/**"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
java-version: ["17"]
|
||||||
|
maven-version: ["3.8.5"]
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: "./Frontend"
|
||||||
|
|
||||||
|
# Steps to run
|
||||||
|
steps:
|
||||||
|
# Checkout the repo
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Deploy to Dokku
|
||||||
|
- name: Deploy to Dokku
|
||||||
|
uses: dokku/github-action@master
|
||||||
|
with:
|
||||||
|
git_remote_url: "ssh://dokku@10.10.3.28:22/restfulmc-web"
|
||||||
|
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
|
26
Frontend/.dockerignore
Normal file
26
Frontend/.dockerignore
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
**/.gitea
|
||||||
|
**/.next
|
||||||
|
**/.env
|
||||||
|
LICENSE
|
||||||
|
README.md
|
45
Frontend/Dockerfile
Normal file
45
Frontend/Dockerfile
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
FROM fascinated/docker-images:nodejs_20_with_yarn AS base
|
||||||
|
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
FROM base AS depends
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
COPY package.json* yarn.lock* ./
|
||||||
|
RUN yarn install --frozen-lockfile --quiet
|
||||||
|
|
||||||
|
|
||||||
|
# Build the app
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
COPY --from=depends /usr/src/app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
RUN yarn run build
|
||||||
|
|
||||||
|
|
||||||
|
# Run the app
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nextjs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
RUN mkdir .next
|
||||||
|
RUN chown nextjs:nextjs .next
|
||||||
|
|
||||||
|
COPY --from=builder --chown=nextjs:nextjs /usr/src/app/node_modules ./node_modules
|
||||||
|
COPY --from=builder --chown=nextjs:nextjs /usr/src/app/.next ./.next
|
||||||
|
COPY --from=builder --chown=nextjs:nextjs /usr/src/app/public ./public
|
||||||
|
COPY --from=builder --chown=nextjs:nextjs /usr/src/app/next.config.mjs ./next.config.mjs
|
||||||
|
COPY --from=builder --chown=nextjs:nextjs /usr/src/app/package.json ./package.json
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
# Exposting on port 80 so we can
|
||||||
|
# access via a reverse proxy for Dokku
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
|
EXPOSE 80
|
||||||
|
ENV PORT 80
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
CMD ["yarn", "start"]
|
@ -1,3 +1,5 @@
|
|||||||
|
import Embed from "@/components/embed";
|
||||||
|
import PlayerResult from "@/components/player/player-result";
|
||||||
import PlayerSearch from "@/components/player/player-search";
|
import PlayerSearch from "@/components/player/player-search";
|
||||||
import { minecrafter } from "@/font/fonts";
|
import { minecrafter } from "@/font/fonts";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@ -5,7 +7,6 @@ import { PageProps } from "@/types/page";
|
|||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { CachedPlayer, getPlayer, type RestfulMCAPIError } from "restfulmc-lib";
|
import { CachedPlayer, getPlayer, type RestfulMCAPIError } from "restfulmc-lib";
|
||||||
import PlayerResult from "../../../components/player/player-result";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The page to lookup a player.
|
* The page to lookup a player.
|
||||||
@ -15,16 +16,11 @@ import PlayerResult from "../../../components/player/player-result";
|
|||||||
const PlayerPage = async ({ params }: PageProps): Promise<JSX.Element> => {
|
const PlayerPage = async ({ params }: PageProps): Promise<JSX.Element> => {
|
||||||
let error: string | undefined = undefined; // The error to display
|
let error: string | undefined = undefined; // The error to display
|
||||||
let result: CachedPlayer | undefined = undefined; // The player to display
|
let result: CachedPlayer | undefined = undefined; // The player to display
|
||||||
let query: string | undefined = params.slug?.[0]; // The query to search for
|
const query: string | undefined = trimQuery(params.slug?.[0]); // The query to search for
|
||||||
|
|
||||||
// Limit the query to 36 chars
|
|
||||||
if (query && query.length > 36) {
|
|
||||||
query = query.substr(0, 36);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try and get the player to display
|
// Try and get the player to display
|
||||||
try {
|
try {
|
||||||
result = params.slug ? await getPlayer(query) : undefined;
|
result = query ? await getPlayer(query) : undefined;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = (err as RestfulMCAPIError).message; // Set the error message
|
error = (err as RestfulMCAPIError).message; // Set the error message
|
||||||
}
|
}
|
||||||
@ -73,11 +69,50 @@ const PlayerPage = async ({ params }: PageProps): Promise<JSX.Element> => {
|
|||||||
* @param searchParams the search params
|
* @param searchParams the search params
|
||||||
* @returns the generated metadata
|
* @returns the generated metadata
|
||||||
*/
|
*/
|
||||||
const generateMetadata = async ({ params }: PageProps): Promise<Metadata> => {
|
export const generateMetadata = async ({
|
||||||
console.log("params", params);
|
params,
|
||||||
return {
|
}: PageProps): Promise<Metadata> => {
|
||||||
title: "bob ross",
|
const query: string | undefined = trimQuery(params.slug?.[0]); // The query to embed for
|
||||||
};
|
|
||||||
|
// Try and get the player to display
|
||||||
|
if (query) {
|
||||||
|
try {
|
||||||
|
const player: CachedPlayer = await getPlayer(query); // Get the player to embed
|
||||||
|
return Embed({
|
||||||
|
title: `${player.username}'s Player`,
|
||||||
|
description: "Click to view data about this player.",
|
||||||
|
thumbnail: player.skin.parts.HEAD,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const code: number = (err as RestfulMCAPIError).code; // Get the error status code
|
||||||
|
if (code === 400) {
|
||||||
|
return Embed({
|
||||||
|
title: "Invalid Player",
|
||||||
|
description: "The player you searched for is invalid.",
|
||||||
|
});
|
||||||
|
} else if (code === 404) {
|
||||||
|
return Embed({
|
||||||
|
title: "Player Not Found",
|
||||||
|
description: "The player you searched for was not found.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trim the given query.
|
||||||
|
*
|
||||||
|
* @param query the query to trim
|
||||||
|
* @returns the trimmed query
|
||||||
|
*/
|
||||||
|
const trimQuery = (query: string | undefined): string | undefined => {
|
||||||
|
// Limit the query to 36 chars
|
||||||
|
if (query && query.length > 36) {
|
||||||
|
query = query.substr(0, 36);
|
||||||
|
}
|
||||||
|
return query;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PlayerPage;
|
export default PlayerPage;
|
||||||
|
47
Frontend/src/app/components/embed.tsx
Normal file
47
Frontend/src/app/components/embed.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
type EmbedProps = {
|
||||||
|
/**
|
||||||
|
* The title of the embed.
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description of the embed.
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The optional thumbnail image of the embed.
|
||||||
|
*/
|
||||||
|
thumbnail?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An embed for a page.
|
||||||
|
*
|
||||||
|
* @param props the embed props
|
||||||
|
* @returns the embed jsx
|
||||||
|
*/
|
||||||
|
const Embed = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
thumbnail = "",
|
||||||
|
}: EmbedProps): Metadata => {
|
||||||
|
return {
|
||||||
|
title: title,
|
||||||
|
openGraph: {
|
||||||
|
title: `${title}`,
|
||||||
|
description: description,
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: thumbnail,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export default Embed;
|
@ -14,6 +14,7 @@ const PlayerResult = ({
|
|||||||
uniqueId,
|
uniqueId,
|
||||||
username,
|
username,
|
||||||
skin: { parts },
|
skin: { parts },
|
||||||
|
legacy,
|
||||||
},
|
},
|
||||||
}: {
|
}: {
|
||||||
player: CachedPlayer;
|
player: CachedPlayer;
|
||||||
@ -23,16 +24,19 @@ const PlayerResult = ({
|
|||||||
<div className="flex gap-5 items-center">
|
<div className="flex gap-5 items-center">
|
||||||
{/* Player Head */}
|
{/* Player Head */}
|
||||||
<Image
|
<Image
|
||||||
src={parts[SkinPart.HEAD]}
|
src={parts.HEAD}
|
||||||
alt={`${username}'s Head`}
|
alt={`${username}'s Head`}
|
||||||
width={128}
|
width={128}
|
||||||
height={128}
|
height={128}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Name & Unique ID */}
|
{/* Name, Unique ID, and Badges */}
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<h1 className="text-xl font-bold text-minecraft-green-3">{username}</h1>
|
<h1 className="text-xl font-bold text-minecraft-green-3">{username}</h1>
|
||||||
<code className="text-zinc-300">{uniqueId}</code>
|
<code className="text-zinc-300">{uniqueId}</code>
|
||||||
|
|
||||||
|
{/* Legacy Badge */}
|
||||||
|
{legacy && <p className="text-sm font-semibold uppercase">Legacy</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user