Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ff1b013
Redesign layout; add build explorer and project pages; refactor cooki…
vLuckyyy Dec 10, 2025
f2853a6
Fix build.
vLuckyyy Dec 10, 2025
ff4fe2b
Redesing OG-Image generation.
vLuckyyy Dec 10, 2025
c9f543d
Redesing OG-Image generation.
vLuckyyy Dec 10, 2025
39f0e0e
Switch to Ultracite biome config.
vLuckyyy Dec 10, 2025
0e92d57
massive codebase update. 300+ linter errors and correct UI rendering …
vLuckyyy Dec 10, 2025
a58ac0e
fix: resolve build issues and add Vercel Blob storage
vLuckyyy Dec 12, 2025
890c2cd
update: bump Payload CMS and related dependencies to 3.68.3
vLuckyyy Dec 12, 2025
7777812
fix: resolve peer dependency conflict for nextjs
vLuckyyy Dec 12, 2025
ab58d2b
fix: correct component import casing for linux compatibility
vLuckyyy Dec 12, 2025
b4a647f
fix: rename sound-contant.ts to sound-content.ts and fix case sensiti…
vLuckyyy Dec 12, 2025
f906a03
chore: temporary rename for case sensitivity reset
vLuckyyy Dec 12, 2025
58bd77b
chore: finish renaming files to kebab-case
vLuckyyy Dec 12, 2025
9b8b4b8
chore: temporary rename for steps component case fix
vLuckyyy Dec 12, 2025
7642d49
chore: finish renaming steps component to kebab-case
vLuckyyy Dec 12, 2025
caeb925
remove unsed files
vLuckyyy Dec 12, 2025
2c93e02
fix: add `overflow-hidden` to background elements for clipping effects
vLuckyyy Dec 12, 2025
0e74331
fix: add override for Next.js to 16.1.0-canary.19 in bun.lock
vLuckyyy Dec 12, 2025
51c29a4
feat: implement Zod schemas for BuildArtifact and BuildRun, enhance d…
vLuckyyy Dec 12, 2025
8d36bf5
fix.
vLuckyyy Dec 12, 2025
90d40ca
refactor(mdx): remove unused @next/mdx loader and unify usage around …
vLuckyyy Dec 13, 2025
11c5603
refactor: replace prismjs with rehype-prism-plus & fix code blocks re…
vLuckyyy Dec 13, 2025
dc957ad
build: remove critters and disable optimizeCss
vLuckyyy Dec 13, 2025
041077a
chore: remove redundant ts-node dependency
vLuckyyy Dec 13, 2025
5333f1d
chore: remove unnecessary caniuse-lite dependency
vLuckyyy Dec 13, 2025
7aeb0ee
chore: remove redundant @types/mdx dependency
vLuckyyy Dec 13, 2025
5a24271
refactor(ui): reimplement AlertBox with class-variance-authority
vLuckyyy Dec 13, 2025
fb894c2
refactor(ui): migrate CodeTabs from HeadlessUI to Radix UI
vLuckyyy Dec 13, 2025
fd48120
feat(app): add SmoothScrolling component for enhanced user experience
vLuckyyy Dec 13, 2025
b4e3f45
chore: remove bun.lock file to clean up the repository
vLuckyyy Dec 13, 2025
aad810e
feat(builds): add Build Explorer components for managing builds and p…
vLuckyyy Dec 14, 2025
f66e502
feat(blog & infrastructure): add multiple components for enhanced blo…
vLuckyyy Dec 24, 2025
0c9b6ab
refactor(importMap): streamline import statements for better readability
vLuckyyy Dec 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
ETERNALCODE_STRAPI_URL=https://localhost:1337 # Replace with your Strapi URL
ETERNALCODE_STRAPI_KEY=YOUR_STRAPI_KEY # Replace with your Strapi key
# Server URL (required for PayloadCMS to generate correct media URLs)
NEXT_PUBLIC_SERVER_URL=http://localhost:3000

# PayloadCMS Configuration
PAYLOAD_SECRET=your-secret-key-that-is-very-long-and-secure
DATABASE_URI=file:./payload.db
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ next-env.d.ts

# ignore idea files
.idea

# Shannon
.shannon-tool
shannon_repos
shannon_results
24 changes: 24 additions & 0 deletions app/(payload)/admin/[[...segments]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */

import { generatePageMetadata, RootPage } from "@payloadcms/next/views";
import type { Metadata } from "next";
import config from "@/payload.config";
import { importMap } from "../importMap";

type Args = {
params: Promise<{
segments: string[];
}>;
searchParams: Promise<{
[key: string]: string | string[];
}>;
};

export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams });

const Page = ({ params, searchParams }: Args) =>
RootPage({ config, params, searchParams, importMap });

export default Page;
55 changes: 55 additions & 0 deletions app/(payload)/admin/importMap.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions app/(payload)/api/[...slug]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from "@/payload.config";
import "@payloadcms/next/css";
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
REST_PUT,
} from "@payloadcms/next/routes";

export const GET = REST_GET(config);
export const POST = REST_POST(config);
export const DELETE = REST_DELETE(config);
export const PATCH = REST_PATCH(config);
export const PUT = REST_PUT(config);
export const OPTIONS = REST_OPTIONS(config);
5 changes: 5 additions & 0 deletions app/(payload)/api/graphql/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import config from "@payload-config";
import { GRAPHQL_PLAYGROUND_GET, GRAPHQL_POST } from "@payloadcms/next/routes";

export const GET = GRAPHQL_PLAYGROUND_GET(config);
export const POST = GRAPHQL_POST(config);
32 changes: 32 additions & 0 deletions app/(payload)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { ServerRuntime } from "next";
import type { ServerFunctionClient } from "payload";

import config from "@/payload.config";
import "@payloadcms/next/css";
import { handleServerFunctions, RootLayout } from "@payloadcms/next/layouts";
import type React from "react";

import { importMap } from "./admin/importMap";

export const runtime: ServerRuntime = "nodejs";

type Args = {
children: React.ReactNode;
};

const serverFunction: ServerFunctionClient = async (args) => {
"use server";
return handleServerFunctions({
...args,
config,
importMap,
});
};

export default function Layout({ children }: Args) {
return (
<RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
{children}
</RootLayout>
);
}
5 changes: 5 additions & 0 deletions app/(website)/[...not-found]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { notFound } from "next/navigation";

export default function NotFoundCatchAll() {
notFound();
}
160 changes: 160 additions & 0 deletions app/(website)/api/builds/builds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { z } from "zod";

export const BuildArtifactSchema = z.object({
id: z.number(),
node_id: z.string(),
name: z.string(),
size_in_bytes: z.number(),
url: z.string(),
archive_download_url: z.string(),
expired: z.boolean(),
created_at: z.string(),
expires_at: z.string(),
updated_at: z.string(),
});

export type BuildArtifact = z.infer<typeof BuildArtifactSchema>;

export const BuildRunSchema = z.object({
id: z.number(),
name: z.string().nullable(),
status: z.string(),
conclusion: z.string().nullable(),
head_branch: z.string(),
head_sha: z.string(),
created_at: z.string(),
html_url: z.string(),
artifacts_url: z.string(),
display_title: z.string().optional(),
});

export type BuildRun = z.infer<typeof BuildRunSchema> & {
found_artifact?: BuildArtifact;
};

const GithubRunsResponseSchema = z.object({
workflow_runs: z.array(BuildRunSchema),
});

const BuildArtifactsResponseSchema = z.object({
total_count: z.number(),
artifacts: z.array(BuildArtifactSchema),
});

export const ModrinthFileSchema = z.object({
url: z.string(),
filename: z.string(),
primary: z.boolean(),
});

export const ModrinthVersionSchema = z.object({
id: z.string(),
name: z.string(),
version_number: z.string(),
date_published: z.string(),
files: z.array(ModrinthFileSchema),
});

export type ModrinthVersion = z.infer<typeof ModrinthVersionSchema>;

export type Project = {
id: string;
name: string;
githubRepo: string;
modrinthId?: string;
};

export const PROJECTS: Project[] = [
{
id: "eternalcore",
name: "EternalCore",
githubRepo: "EternalCodeTeam/EternalCore",
modrinthId: "eternalcore",
},
{
id: "eternalcombat",
name: "EternalCombat",
githubRepo: "EternalCodeTeam/EternalCombat",
modrinthId: "eternalcombat",
},
];

export async function fetchDevBuilds(project: Project): Promise<BuildRun[]> {
try {
const res = await fetch(
`https://api.github.com/repos/${project.githubRepo}/actions/runs?per_page=20&status=success&branch=master`
);
if (!res.ok) {
console.error(`Failed to fetch Github Actions for ${project.name}`, await res.text());
return [];
}

const json = await res.json();
const parsed = GithubRunsResponseSchema.safeParse(json);

if (!parsed.success) {
console.error(`Invalid Github Actions response for ${project.name}`, parsed.error);
return [];
}

const runs = parsed.data.workflow_runs;

// Fetch artifacts for each run to get the correct artifact name
return await Promise.all(
runs.map(async (run) => {
try {
const artRes = await fetch(run.artifacts_url);
if (!artRes.ok) {
return run;
}

const artJson = await artRes.json();
const artParsed = BuildArtifactsResponseSchema.safeParse(artJson);

if (artParsed.success && artParsed.data.artifacts.length > 0) {
// We take the first artifact as the primary one
return { ...run, found_artifact: artParsed.data.artifacts[0] };
}
return run;
} catch (e) {
console.error(`Error fetching artifacts for run ${run.id}`, e);
return run;
}
})
);
} catch (error) {
console.error(`Error fetching dev builds for ${project.name}`, error);
return [];
}
}

export async function fetchStableBuilds(project: Project): Promise<ModrinthVersion[]> {
if (!project.modrinthId) {
return [];
}

try {
const res = await fetch(`https://api.modrinth.com/v2/project/${project.modrinthId}/version`);
if (!res.ok) {
if (res.status === 404) {
return []; // Project might not exist yet
}
console.error(`Failed to fetch Modrinth versions for ${project.name}`, await res.text());
return [];
}

const json = await res.json();
// Validate response is an array of ModrinthVersion
const parsed = z.array(ModrinthVersionSchema).safeParse(json);

if (!parsed.success) {
console.error(`Invalid Modrinth versions response for ${project.name}`, parsed.error);
return [];
}

return parsed.data;
} catch (error) {
console.error(`Error fetching stable builds for ${project.name}`, error);
return [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import path from "node:path";
import matter from "gray-matter";
import { NextResponse } from "next/server";

const MDX_EXTENSION_REGEX = /\.mdx$/;

type SearchIndexItem = {
title: string;
path: string;
excerpt: string;
};

function findMarkdownFiles(dir: string): string[] {
const files: string[] = [];
const entries = fs.readdirSync(dir, { withFileTypes: true });
Expand All @@ -23,13 +31,13 @@ function findMarkdownFiles(dir: string): string[] {
function generateSearchIndex() {
const docsDir = path.join(process.cwd(), "content/docs");
const files = findMarkdownFiles(docsDir);
const searchIndex = [];
const searchIndex: SearchIndexItem[] = [];

for (const file of files) {
const content = fs.readFileSync(file, "utf8");
const { data, content: markdownContent } = matter(content);
const relativePath = path.relative(docsDir, file);
const urlPath = `/docs/${relativePath.replace(/\.mdx$/, "")}`;
const urlPath = `/docs/${relativePath.replace(MDX_EXTENSION_REGEX, "")}`;

const excerpt = markdownContent
.replace(/[#*`_~]/g, "")
Expand All @@ -47,7 +55,7 @@ function generateSearchIndex() {
return searchIndex;
}

export async function GET() {
export function GET() {
try {
const searchIndex = generateSearchIndex();
return NextResponse.json(searchIndex);
Expand Down
29 changes: 29 additions & 0 deletions app/(website)/api/og/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { OgTemplate, loadFonts } from "@/components/og/og-template";
import { ImageResponse } from "@vercel/og";
import type { NextRequest } from "next/server";

export const runtime = "edge";

export async function GET(req: NextRequest) {
try {
const { searchParams } = new URL(req.url);

const title = searchParams.get("title") || "EternalCode.pl";
const subtitle = searchParams.get("subtitle") || "Open Source Solutions";
const image = searchParams.get("image") || "https://eternalcode.pl/logo.svg";

const fonts = await loadFonts();

return new ImageResponse(<OgTemplate title={title} subtitle={subtitle} image={image} />, {
width: 1200,
height: 630,
fonts,
headers: {
"Cache-Control": "public, max-age=0, must-revalidate",
},
});
} catch (e) {
console.error("OG Image Generation Error:", e);
return new Response("Failed to generate OG image", { status: 500 });
}
}
Loading