Moved to next js ssr
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import '@/src/index.css';
|
||||
import '@/src/App.css';
|
||||
import '@/src/css/Unit.css';
|
||||
import '@/src/css/Building.css';
|
||||
import { ThemeProvider } from '@/src/context/ThemeContext';
|
||||
import AppShell from '@/components/AppShell';
|
||||
|
||||
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://dow-wiki.example.com';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(siteUrl),
|
||||
title: {
|
||||
default: 'Dawn of War Wiki — юниты, расы и здания всех модов',
|
||||
template: '%s — Dawn of War Wiki',
|
||||
},
|
||||
description:
|
||||
'Dawn of War wiki. Unification mod wiki. Unification mod unit stats. Supported all popular mods.',
|
||||
manifest: '/manifest.json',
|
||||
applicationName: 'Dawn of War Wiki',
|
||||
verification: {
|
||||
google: 'Q4XMn2UDpL2xmK7iBfaWQpVN8EPy8oGxN-7HqSKapb0',
|
||||
},
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
siteName: 'Dawn of War Wiki',
|
||||
title: 'Dawn of War Wiki — юниты, расы и здания всех модов',
|
||||
description:
|
||||
'Dawn of War wiki. Unification mod wiki. Unification mod unit stats. Supported all popular mods.',
|
||||
images: ['/logo512.png'],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const viewport: Viewport = {
|
||||
themeColor: '#000000',
|
||||
width: 'device-width',
|
||||
initialScale: 1,
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
{/* Yandex.Metrika */}
|
||||
<script
|
||||
type="text/javascript"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
"(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};m[i].l=1*new Date();for(var j=0;j<document.scripts.length;j++){if(document.scripts[j].src===r){return;}}k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})(window,document,\"script\",\"https://mc.yandex.ru/metrika/tag.js\",\"ym\");ym(99502794,\"init\",{clickmap:true,trackLinks:true,accurateTrackBounce:true,webvisor:true});",
|
||||
}}
|
||||
/>
|
||||
<noscript>
|
||||
<div>
|
||||
<img
|
||||
src="https://mc.yandex.ru/watch/99502794"
|
||||
style={{ position: 'absolute', left: -9999 }}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</noscript>
|
||||
<ThemeProvider>
|
||||
<AppShell>{children}</AppShell>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { Metadata } from 'next';
|
||||
import ModPageClient from '@/components/pages/ModPageClient';
|
||||
import { getMod } from '@/lib/api-server';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
interface Props {
|
||||
params: { modId: string };
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const mod = await getMod(params.modId);
|
||||
if (!mod) {
|
||||
return { title: 'Mod' };
|
||||
}
|
||||
const title = `${mod.name} (${mod.version})`;
|
||||
const description = `Dawn of War wiki. ${mod.name} mod. Units, races, buildings and stats.`;
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: `/mod/${params.modId}`,
|
||||
},
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function ModPage() {
|
||||
return <ModPageClient />;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { Metadata } from 'next';
|
||||
import BuildingPageClient from '@/components/pages/BuildingPageClient';
|
||||
import { getBuilding, getMod } from '@/lib/api-server';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
interface Props {
|
||||
params: { modId: string; raceId: string; buildingId: string };
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const [mod, building] = await Promise.all([getMod(params.modId), getBuilding(params.buildingId)]);
|
||||
if (!mod || !building) {
|
||||
return { title: 'Building' };
|
||||
}
|
||||
let buildingName = building.name;
|
||||
if (!buildingName) {
|
||||
buildingName = building.filename.replaceAll('_', ' ').replace('.rgd', '');
|
||||
}
|
||||
const title = `${buildingName} — ${mod.name}`;
|
||||
const description = `${buildingName} building in ${mod.name} mod. Stats, production, researches.`;
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: `/mod/${params.modId}/race/${params.raceId}/building/${params.buildingId}`,
|
||||
},
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function BuildingPage() {
|
||||
return <BuildingPageClient />;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { Metadata } from 'next';
|
||||
import RacePageClient from '@/components/pages/RacePageClient';
|
||||
import { getMod, getRace } from '@/lib/api-server';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
interface Props {
|
||||
params: { modId: string; raceId: string };
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const [mod, race] = await Promise.all([getMod(params.modId), getRace(params.raceId)]);
|
||||
if (!mod || !race) {
|
||||
return { title: 'Race' };
|
||||
}
|
||||
const title = `${race.name} — ${mod.name}`;
|
||||
const description = `${race.name} race in ${mod.name} mod. Units, buildings and stats.`;
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: `/mod/${params.modId}/race/${params.raceId}`,
|
||||
},
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function RacePage() {
|
||||
return <RacePageClient />;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { Metadata } from 'next';
|
||||
import UnitPageClient from '@/components/pages/UnitPageClient';
|
||||
import { getMod, getUnit } from '@/lib/api-server';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
interface Props {
|
||||
params: { modId: string; raceId: string; unitId: string };
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const [mod, unit] = await Promise.all([getMod(params.modId), getUnit(params.unitId)]);
|
||||
if (!mod || !unit) {
|
||||
return { title: 'Unit' };
|
||||
}
|
||||
const title = `${unit.name} — ${mod.name}`;
|
||||
const description = unit.description
|
||||
? `${unit.name} unit in ${mod.name} mod. ${unit.description}`
|
||||
: `${unit.name} unit in ${mod.name} mod. Stats, weapons, abilities.`;
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: `/mod/${params.modId}/race/${params.raceId}/unit/${params.unitId}`,
|
||||
},
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function UnitPage() {
|
||||
return <UnitPageClient />;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Metadata } from 'next';
|
||||
import ModsPageClient from '@/components/pages/ModsPageClient';
|
||||
import { getMods } from '@/lib/api-server';
|
||||
import { IMod } from '@/src/types/Imod';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Dawn of War Wiki — юниты, расы и здания всех модов',
|
||||
description:
|
||||
'Dawn of War wiki. Unification mod wiki. Unification mod unit stats. Supported all popular mods.',
|
||||
alternates: {
|
||||
canonical: '/',
|
||||
},
|
||||
};
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default async function HomePage() {
|
||||
const mods = (await getMods()) ?? [];
|
||||
const visibleMods = mods.filter(mod => !mod.isHide);
|
||||
return <ModsPageClient initialMods={visibleMods} />;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { MetadataRoute } from 'next';
|
||||
import { SITE_URL } from '@/lib/api-server';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
},
|
||||
sitemap: `${SITE_URL}/sitemap.xml`,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import type { MetadataRoute } from 'next';
|
||||
import {
|
||||
getBuildingsForMod,
|
||||
getMods,
|
||||
getUnitsForMod,
|
||||
SITE_URL,
|
||||
} from '@/lib/api-server';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const mods = (await getMods()) ?? [];
|
||||
|
||||
const modEntries: MetadataRoute.Sitemap = mods.map((mod) => ({
|
||||
url: `${SITE_URL}/mod/${mod.id}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.8,
|
||||
}));
|
||||
|
||||
const entries: MetadataRoute.Sitemap = [...modEntries];
|
||||
|
||||
// Gather races/units/buildings per mod (limit parallelism)
|
||||
const details = await Promise.all(
|
||||
mods.slice(0, 20).map(async (mod) => {
|
||||
const [units, buildings] = await Promise.all([
|
||||
getUnitsForMod(mod.id),
|
||||
getBuildingsForMod(mod.id),
|
||||
]);
|
||||
return { mod, units, buildings };
|
||||
})
|
||||
);
|
||||
|
||||
for (const { mod, units, buildings } of details) {
|
||||
const raceIds = new Set<string>();
|
||||
for (const unitGroup of units ?? []) raceIds.add(unitGroup.race.id);
|
||||
for (const buildingGroup of buildings ?? []) raceIds.add(buildingGroup.race.id);
|
||||
|
||||
for (const raceId of raceIds) {
|
||||
entries.push({
|
||||
url: `${SITE_URL}/mod/${mod.id}/race/${raceId}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.7,
|
||||
});
|
||||
}
|
||||
for (const unitGroup of units ?? []) {
|
||||
for (const unit of [...unitGroup.infantry, ...unitGroup.support, ...unitGroup.tech]) {
|
||||
entries.push({
|
||||
url: `${SITE_URL}/mod/${mod.id}/race/${unitGroup.race.id}/unit/${unit.id}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.5,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const buildingGroup of buildings ?? []) {
|
||||
for (const building of [...buildingGroup.buildings, ...buildingGroup.buildingsAdvanced]) {
|
||||
entries.push({
|
||||
url: `${SITE_URL}/mod/${mod.id}/race/${buildingGroup.race.id}/building/${building.id}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.5,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
url: SITE_URL,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'daily',
|
||||
priority: 1,
|
||||
},
|
||||
...entries,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user