Skip to content

Commit c7c76c2

Browse files
committed
add: new partners stuff
1 parent cab2fe6 commit c7c76c2

15 files changed

Lines changed: 168 additions & 108 deletions

File tree

app/nodes/page.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import type { Metadata } from "next"
22
import { NodesClient } from "@/packages/ui/components/Layouts/Nodes/nodes-client"
3+
import { fetchStatusSnapshot, getNodeMonitorNames } from "@/packages/core/lib/status"
34

45
export const metadata: Metadata = {
56
title: "Our Network & Nodes",
67
description:
78
"Live status and information about NodeByte Hosting's network. See where our infrastructure is located and what powers our game server and VPS hosting.",
89
}
910

10-
export default function NodesPage() {
11-
return <NodesClient />
11+
export default async function NodesPage() {
12+
const snapshot = await fetchStatusSnapshot()
13+
const nodeNames = getNodeMonitorNames(snapshot)
14+
return <NodesClient nodeNames={nodeNames} />
1215
}

packages/core/constants/node-types.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ export interface PublicNode {
33
name: string
44
locationCode: string
55
isMaintenanceMode: boolean
6-
/** Allocated memory in MiB */
7-
memory: number
8-
/** Allocated disk in MiB */
9-
disk: number
6+
/** Allocated memory in MiB — not known for nodes without a display override. */
7+
memory?: number
8+
/** Allocated disk in MiB — not known for nodes without a display override. */
9+
disk?: number
1010
/** Number of server instances currently provisioned on this node */
1111
serverCount?: number
1212
}
Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,89 @@
11
/**
2-
* Partners & sponsors shown on /partners.
2+
* Partners, sponsors, and communities shown on /partners.
33
*
4-
* Add or remove entries here — the page groups them by `tier` automatically
5-
* and shows an empty-state invite for any tier with zero entries, so this
6-
* file can be edited freely without touching the page component.
4+
* Add or remove entries here — the page groups them by `tier` into their own
5+
* section automatically, hiding any tier with zero entries, so this file can
6+
* be edited freely without touching the page component.
77
*
8-
* There's no real partner/sponsor data yet — the entries below are clearly
9-
* marked placeholders. Replace them (or delete down to an empty array) once
10-
* real partners sign on.
8+
* - "sponsor" / "partner": companies and projects we have a business
9+
* relationship with.
10+
* - "community": servers/communities we host or sponsor for free or at a
11+
* discount (Minecraft servers, Discord communities, etc). Set `category`
12+
* to a short label like "Minecraft Server" or "Discord Community" — it
13+
* renders as a badge on the card.
1114
*/
1215

13-
export type PartnerTier = "sponsor" | "partner"
16+
export type PartnerTier = "sponsor" | "partner" | "community"
1417

1518
export interface PartnerEntry {
1619
id: string
1720
name: string
1821
tier: PartnerTier
22+
/** Short badge label, e.g. "Minecraft Server", "Discord Community". Optional — mainly useful for the community tier. */
23+
category?: string
1924
/** Path under /public, e.g. "/partners/example.svg" */
2025
logo: string
2126
url: string
2227
description: string
2328
}
2429

2530
export const PARTNERS: PartnerEntry[] = [
31+
{
32+
id: "poliberry",
33+
name: "Poliberry",
34+
tier: "partner",
35+
logo: "/partners/poliberry.png",
36+
url: "https://poliberry.com",
37+
description: "Poliberry is a technology company building tools and services to better connect people online and empowering developers to build the next big thing.",
38+
},
2639
{
2740
id: "embrly",
2841
name: "Emberly",
2942
tier: "partner",
30-
logo: "https://embrly.ca/icon.svg",
43+
logo: "/partners/emberly.svg",
3144
url: "https://embrly.ca",
3245
description: "The open-source platform for secure file sharing and team collaboration. Upload, manage, and share content with custom domains, rich embeds, and built-in talent discovery.",
3346
},
47+
{
48+
id: "clovrme",
49+
name: "Clover",
50+
tier: "partner",
51+
logo: "/partners/clovrme.svg",
52+
url: "https://clovr.me",
53+
description: "A profile page that's fully yours custom themes, music, animated backgrounds, and all your links in one place.",
54+
},
3455
{
3556
id: "octo",
3657
name: "Octoflow",
3758
tier: "partner",
38-
logo: "https://octoflow.ca/logo.png",
59+
logo: "/partners/octoflow.png",
3960
url: "https://octoflow.ca",
4061
description: "Keep your team connected to every commit, pull request, and deployment without ever leaving your Discord server.",
4162
},
4263
{
43-
id: "poliberry",
44-
name: "Poliberry",
64+
id: "antiraid",
65+
name: "AntiRaid",
4566
tier: "partner",
46-
logo: "/partners/Frame_1552.png",
47-
url: "https://poliberry.com",
48-
description: "Poliberry is a technology company building tools and services to better connect people online and empowering developers to build the next big thing.",
67+
logo: "/partners/antiraid.webp",
68+
url: "https://antiraid.xyz",
69+
description: "From basic moderation to advanced threat protection, AntiRaid handles it all so you can focus on growing your community.",
70+
},
71+
{
72+
id: "smphub",
73+
name: "SMP Hub",
74+
tier: "community",
75+
category: "Minecraft Community",
76+
logo: "/partners/smphub.webp",
77+
url: "https://discord.gg/d6sXpA7gXJ",
78+
description: "The premier directory designed to connect the Minecraft community.",
79+
},
80+
{
81+
id: "blizzardsmp",
82+
name: "Blizzard SMP",
83+
tier: "community",
84+
category: "Minecraft Community",
85+
logo: "/partners/blizzardsmp.webp",
86+
url: "https://discord.gg/mvQ9VqZ4D",
87+
description: "A warm, active community with a cool name and even cooler players.",
4988
},
5089
]

packages/core/constants/status-mapping.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
/**
2-
* Bridges the two independent naming schemes between status.nodebyte.host
3-
* (monitor names) and this site's node/location identifiers. Update these
4-
* maps whenever a node is renamed or a new Region ping monitor is added
5-
* upstream — unmapped entries simply render without live data.
2+
* The node list on /nodes is discovered live from status.nodebyte.host's
3+
* "Nodes" group (see getNodeMonitorNames in lib/status.ts) — adding a node
4+
* there is all that's needed for it to appear on the site.
5+
*
6+
* status.nodebyte.host doesn't carry location/hardware details, so those are
7+
* filled in here as optional per-node overrides, keyed by the exact monitor
8+
* name. A node with no entry here still shows up, just without these extras.
69
*/
7-
8-
/** Website node `name` (STATIC_NODES) → status.nodebyte.host monitor `name`. */
9-
export const NODE_MONITOR_MAP: Record<string, string> = {
10-
"NEWC-GAME1": "NEWC-GAME1",
11-
"NEWY-GAME1": "NEWY-GAME1",
12-
"FLUXRP-FIVEM": "FLUXRP-FIVEM",
13-
"HEL-VPS1": "HEL-VPS1",
10+
export const NODE_DISPLAY_OVERRIDES: Record<string, { locationCode?: string; cpu?: string; ramType?: string }> = {
11+
"NEWC-GAME1": { locationCode: "Newcastle, UK" },
12+
"NEWY-GAME1": { locationCode: "New York, USA" },
13+
"HEL-VPS1": { locationCode: "Helsinki, FI" },
14+
// Inferred from the "FSN" prefix (Falkenstein) — confirm/correct if wrong.
15+
"FSN-VPS1": { locationCode: "Falkenstein, DE" },
1416
}
1517

1618
/**
1719
* Website location `id` (LOCATIONS) → status.nodebyte.host "Regions" monitor
1820
* `name`. Only locations with a confirmed matching ping monitor are listed;
1921
* the Regions group also includes PoPs (e.g. Ashburn VA, Atlanta GA) that
20-
* don't correspond to an actual NodeByte data centre location.
22+
* don't correspond to an actual NodeByte data centre location. Update
23+
* whenever a new Region ping monitor is added upstream — unmapped locations
24+
* simply render without live data.
2125
*/
2226
export const LOCATION_MONITOR_MAP: Record<string, string> = {
2327
lon: "London, UK",

packages/core/lib/status.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ export function findMonitor(snapshot: StatusSnapshot | null, name: string): Stat
108108
return snapshot.monitors.find((m) => m.name.trim().toLowerCase() === target) ?? null
109109
}
110110

111+
/**
112+
* Names of individual node monitors under the "Nodes" status group — this is
113+
* the live source of truth for which nodes exist on /nodes. Excludes the
114+
* "group"-type aggregate rollups (e.g. "Game Servers", "VPS Servers") that
115+
* summarise the individual node monitors rather than representing one.
116+
* Add a node on status.nodebyte.host under the "Nodes" group and it appears
117+
* here automatically — no website code change needed.
118+
*/
119+
export function getNodeMonitorNames(snapshot: StatusSnapshot | null): string[] {
120+
if (!snapshot) return []
121+
return snapshot.monitors
122+
.filter((m) => m.group_name === "Nodes" && m.type !== "group")
123+
.map((m) => m.name)
124+
}
125+
111126
/** Compute fast/avg/slow latency (ms) from a monitor's recent heartbeats. */
112127
export function computeLatencyStats(monitor: StatusMonitor): { fast: number; avg: number; slow: number } | null {
113128
const samples = monitor.heartbeats

packages/ui/components/Layouts/Nodes/nodes-client.tsx

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
} from "@/packages/ui/components/ui/accordion"
2424
import { Alert, AlertDescription } from "@/packages/ui/components/ui/alert"
2525
import type { PublicNode } from "@/packages/core/constants/node-types"
26-
import { NODE_MONITOR_MAP, LOCATION_MONITOR_MAP } from "@/packages/core/constants/status-mapping"
26+
import { NODE_DISPLAY_OVERRIDES, LOCATION_MONITOR_MAP } from "@/packages/core/constants/status-mapping"
2727
import { useNodeStatus } from "@/packages/core/hooks/use-node-status"
2828
import type { StatusApiMonitor } from "@/app/api/status/route"
2929
import { cn } from "@/lib/utils"
@@ -36,35 +36,20 @@ interface ExtendedNode extends PublicNode {
3636
uptime?: number
3737
}
3838

39-
const STATIC_NODES: ExtendedNode[] = [
40-
{
41-
id: 1,
42-
name: "NEWC-GAME1",
43-
locationCode: "Newcastle, UK",
44-
isMaintenanceMode: false,
45-
memory: 1310089,
46-
disk: 1811000,
47-
uptime: 99.9,
48-
},
49-
{
50-
id: 2,
51-
name: "NEWY-GAME1",
52-
locationCode: "New York, USA",
53-
isMaintenanceMode: false,
54-
memory: 65104,
55-
disk: 512000,
56-
uptime: 99.8,
57-
},
58-
{
59-
id: 3,
60-
name: "HEL-VPS1",
61-
locationCode: "Helsinki, FI",
62-
isMaintenanceMode: false,
63-
memory: 65104,
64-
disk: 512000,
65-
uptime: 99.8,
66-
},
67-
]
39+
/** Build the node list from names discovered live from the status page, filling in any known display extras. */
40+
function buildNodes(nodeNames: string[]): ExtendedNode[] {
41+
return nodeNames.map((name, i) => {
42+
const overrides = NODE_DISPLAY_OVERRIDES[name] ?? {}
43+
return {
44+
id: i + 1,
45+
name,
46+
locationCode: overrides.locationCode ?? "",
47+
isMaintenanceMode: false,
48+
cpu: overrides.cpu,
49+
ramType: overrides.ramType,
50+
}
51+
})
52+
}
6853

6954
// ─── Location data ───────────────────────────────────────────────────────────
7055

@@ -314,13 +299,15 @@ const FAQS = [
314299

315300
// ─── Main export ─────────────────────────────────────────────────────────────
316301

317-
export function NodesClient() {
318-
const nodes = STATIC_NODES
302+
interface NodesClientProps {
303+
/** Node monitor names discovered live from the status page's "Nodes" group. */
304+
nodeNames: string[]
305+
}
306+
307+
export function NodesClient({ nodeNames }: NodesClientProps) {
308+
const nodes = buildNodes(nodeNames)
319309
const { findMonitor } = useNodeStatus()
320-
const nodeLiveStates = nodes.map((node) => {
321-
const monitorName = NODE_MONITOR_MAP[node.name]
322-
return monitorName ? findMonitor(monitorName) : null
323-
})
310+
const nodeLiveStates = nodes.map((node) => findMonitor(node.name))
324311
const onlineCount = nodeLiveStates.filter(
325312
(live, i) => (live ? live.status === "up" : !nodes[i].isMaintenanceMode),
326313
).length
@@ -381,11 +368,19 @@ export function NodesClient() {
381368
</p>
382369
<div className="h-px flex-1 bg-border/40" />
383370
</div>
384-
<div className="grid sm:grid-cols-2 gap-6 max-w-2xl mx-auto">
385-
{nodes.map((node, i) => (
386-
<NodeCard key={node.id} node={node} live={nodeLiveStates[i]} />
387-
))}
388-
</div>
371+
{nodes.length > 0 ? (
372+
<div className="grid sm:grid-cols-2 gap-6 max-w-2xl mx-auto">
373+
{nodes.map((node, i) => (
374+
<NodeCard key={node.id} node={node} live={nodeLiveStates[i]} />
375+
))}
376+
</div>
377+
) : (
378+
<div className="flex flex-col items-center justify-center py-16 text-center space-y-2 border border-border/40 rounded-2xl bg-card/20 max-w-2xl mx-auto">
379+
<Server className="w-8 h-8 text-muted-foreground/40" />
380+
<p className="font-medium">Node status is temporarily unavailable</p>
381+
<p className="text-sm text-muted-foreground">Check the status page directly for the latest info.</p>
382+
</div>
383+
)}
389384
</section>
390385

391386
{/* ── Locations */}

0 commit comments

Comments
 (0)