small changes regarding my proxmox maintenance checks
This commit is contained in:
parent
953fa603ea
commit
cadbe8caa9
15 changed files with 149 additions and 483 deletions
|
|
@ -9,7 +9,11 @@ export async function GET(request: Request) {
|
|||
if (url.startsWith('minecraft://')) {
|
||||
const address = url.replace('minecraft://', '');
|
||||
try {
|
||||
const res = await fetch(`https://api.mcsrvstat.us/3/${address}`);
|
||||
// mcsrvstat.us is unreliable at resolving some hostnames, so use mcstatus.io.
|
||||
const res = await fetch(`https://api.mcstatus.io/v2/status/java/${address}`, {
|
||||
signal: AbortSignal.timeout(5000),
|
||||
cache: 'no-store',
|
||||
});
|
||||
const data = await res.json();
|
||||
return NextResponse.json({ status: data.online ? 'up' : 'down' });
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,16 @@
|
|||
|
||||
import React, { useEffect, useState, ReactNode } from 'react';
|
||||
|
||||
export default function ServiceButton({ url, checkUrl, className, icon, name, desc, access }: { url: string, checkUrl?: string, className: string, icon: ReactNode, name: string, desc: string, access?: 'public' | 'private' | 'on_request' }) {
|
||||
const [status, setStatus] = useState<'pending' | 'up' | 'down'>('pending');
|
||||
export default function ServiceButton({ url, checkUrl, className, icon, name, desc, access, disabled }: { url: string, checkUrl?: string, className: string, icon: ReactNode, name: string, desc: string, access?: 'public' | 'private' | 'on_request', disabled?: boolean }) {
|
||||
const [status, setStatus] = useState<'pending' | 'up' | 'down'>(disabled ? 'up' : 'pending');
|
||||
|
||||
useEffect(() => {
|
||||
// Disabled buttons are decorative: skip the health check and stay green.
|
||||
if (disabled) {
|
||||
setStatus('up');
|
||||
return;
|
||||
}
|
||||
|
||||
const checkStatus = async () => {
|
||||
try {
|
||||
const targetUrl = checkUrl || url;
|
||||
|
|
@ -17,22 +23,15 @@ export default function ServiceButton({ url, checkUrl, className, icon, name, de
|
|||
}
|
||||
};
|
||||
checkStatus();
|
||||
}, [url, checkUrl]);
|
||||
}, [url, checkUrl, disabled]);
|
||||
|
||||
const statusColor = status === 'up' ? '#4ade80' : status === 'down' ? '#ef4444' : '#9ca3af';
|
||||
const statusGlow = status === 'up' ? 'rgba(74, 222, 128, 0.9)' : status === 'down' ? 'rgba(239, 68, 68, 0.9)' : 'transparent';
|
||||
const statusLabel = status === 'up' ? 'Active' : status === 'down' ? 'Dead' : 'Checking...';
|
||||
const isFriend = className === 'friend';
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`service-button ${className}`}
|
||||
style={{ position: 'relative', '--status-color': statusColor, '--status-glow': statusGlow } as React.CSSProperties}
|
||||
title={statusLabel}
|
||||
>
|
||||
const content = (
|
||||
<>
|
||||
<span className="service-icon-wrap">
|
||||
{isFriend && <span className="service-status-dot" />}
|
||||
<span className="service-icon">{icon}</span>
|
||||
|
|
@ -46,6 +45,39 @@ export default function ServiceButton({ url, checkUrl, className, icon, name, de
|
|||
</span>
|
||||
<span className="service-desc">{desc}</span>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
|
||||
const sharedStyle = {
|
||||
position: 'relative',
|
||||
'--status-color': statusColor,
|
||||
'--status-glow': statusGlow,
|
||||
...(disabled ? { cursor: 'default' } : {}),
|
||||
} as React.CSSProperties;
|
||||
|
||||
if (disabled) {
|
||||
return (
|
||||
<span
|
||||
className={`service-button ${className} disabled`}
|
||||
style={sharedStyle}
|
||||
title={statusLabel}
|
||||
aria-disabled="true"
|
||||
>
|
||||
{content}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`service-button ${className}`}
|
||||
style={sharedStyle}
|
||||
title={statusLabel}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ body {
|
|||
}
|
||||
|
||||
.services-buttons.services-main {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -480,8 +480,6 @@ body {
|
|||
.service-button.jellyfin:hover .service-name { color: var(--purple); }
|
||||
.service-button.pterodactyl:hover .service-name { color: var(--red); }
|
||||
.service-button.funkwhale:hover .service-name { color: var(--darkpink); }
|
||||
.service-button.searxng:hover .service-name { color: var(--aqua); }
|
||||
.service-button.convertx:hover .service-name { color: var(--gray); }
|
||||
.service-button.gitdxd:hover .service-name { color: var(--purple); }
|
||||
|
||||
.service-button.proxmox { border-color: rgba(168, 153, 132, 0.1); }
|
||||
|
|
@ -489,8 +487,6 @@ body {
|
|||
.service-button.jellyfin { border-color: rgba(168, 153, 132, 0.1); }
|
||||
.service-button.pterodactyl { border-color: rgba(168, 153, 132, 0.1); }
|
||||
.service-button.funkwhale { border-color: rgba(255, 0, 67, 0.1); }
|
||||
.service-button.searxng { border-color: rgba(168, 153, 132, 0.1); }
|
||||
.service-button.convertx { border-color: rgba(168, 153, 132, 0.1); }
|
||||
.service-button.gitdxd { border-color: rgba(255, 0, 67, 0.1); }
|
||||
|
||||
.service-desc {
|
||||
|
|
|
|||
|
|
@ -107,8 +107,8 @@ const codingProjects = [
|
|||
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/sas2py",
|
||||
},
|
||||
{
|
||||
title: "Ollama server",
|
||||
desc: "my ollama vm running on my proxmox with a gpu passthroughed RTX 3070, dm me if u wanna test it lol.",
|
||||
title: "Llama.cpp server",
|
||||
desc: "my llama.cpp vm running on my proxmox with a gpu passthroughed RTX 3070, dm me if u wanna test it lol.",
|
||||
img: "/coding-14.webp",
|
||||
url: "https://ai.dvorakmortonxd.dev/",
|
||||
},
|
||||
|
|
@ -162,7 +162,7 @@ export default function Home() {
|
|||
},
|
||||
{
|
||||
title: "Proxmox Server",
|
||||
desc: "my personal proxmox server (dm me on ig if u need a VPS/hosting), specs: Xeon E5 2680 v4 (14c/28t), RTX 3070 (ollama), 68gb ddr4 rdimm ram (2*16gb, 1*32gb, 1*4gb), 256gb of sata m2 space + 1tb hdd + 750gb hdd.",
|
||||
desc: "my personal proxmox server (dm me on ig if u need a VPS/hosting), specs: Xeon E5 2680 v4 (14c/28t), RTX 3070 (llama.cpp), 68gb ddr4 rdimm ram (2*16gb, 1*32gb, 1*4gb), 256gb of sata m2 space + 1tb hdd + 750gb hdd.",
|
||||
image: "/proxmox-server.webp",
|
||||
},
|
||||
{
|
||||
|
|
@ -388,12 +388,13 @@ export default function Home() {
|
|||
name="Proxmox"
|
||||
desc="Server Management"
|
||||
access="private"
|
||||
disabled
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://ai.dvorakmortonxd.dev/"
|
||||
className="ai"
|
||||
icon="🦾"
|
||||
name="Ollama"
|
||||
name="Llama.cpp"
|
||||
desc="Local AI Server"
|
||||
access="on_request"
|
||||
/>
|
||||
|
|
@ -422,22 +423,6 @@ export default function Home() {
|
|||
desc="Music Streaming"
|
||||
access="private"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://search.dvorakmortonxd.dev/"
|
||||
className="searxng"
|
||||
icon="🔦"
|
||||
name="SearXNG"
|
||||
desc="Private Search"
|
||||
access="private"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://convert.dvorakmortonxd.dev/"
|
||||
className="convertx"
|
||||
icon="📽️"
|
||||
name="ConvertX"
|
||||
desc="File Converter"
|
||||
access="on_request"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://git.dvorakmortonxd.dev"
|
||||
className="gitdxd"
|
||||
|
|
@ -612,7 +597,7 @@ export default function Home() {
|
|||
</section>
|
||||
|
||||
<footer className="footer">
|
||||
<span className="footer-label">v1.2.0</span>
|
||||
<span className="footer-label">v1.2.1</span>
|
||||
© 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just follow the GPL3.0 License)
|
||||
</footer>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,235 +0,0 @@
|
|||
import Image from "next/image";
|
||||
import "../page.css";
|
||||
import { Metadata } from 'next';
|
||||
import type { CSSProperties } from 'react';
|
||||
import JokerLogo from "../components/JokerLogo";
|
||||
import ServiceButton from "../components/ServiceButton";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'dvorakmortonxd.dev/softcraft',
|
||||
description: 'Welcome to the Softcraft Minecraft Server info page!',
|
||||
openGraph: {
|
||||
title: 'dvorakmortonxd.dev/softcraft',
|
||||
description: 'Welcome to the Softcraft Minecraft Server info page!',
|
||||
url: 'https://dvorakmortonxd.dev/softcraft',
|
||||
siteName: 'dvorakmortonxd.dev',
|
||||
},
|
||||
};
|
||||
|
||||
type PanelStyle = CSSProperties & { '--panel-color'?: string };
|
||||
|
||||
const panelStyle = (color: string, style: CSSProperties = {}): PanelStyle => ({
|
||||
...style,
|
||||
'--panel-color': color,
|
||||
});
|
||||
|
||||
export default function Softcraft() {
|
||||
return <>
|
||||
<style>{`
|
||||
@keyframes mc-panorama-pan {
|
||||
0% { background-position: 0% center; }
|
||||
100% { background-position: 100% center; }
|
||||
}
|
||||
|
||||
.header, .hero-panel, .tui-panel, .footer, .service-button, .social-button, .video-container, .hero-panel-label, .tui-panel-label, .footer-label {
|
||||
background-image: linear-gradient(rgba(10, 10, 10, 0.85), rgba(10, 10, 10, 0.85)), url(/dirt.webp) !important;
|
||||
background-repeat: repeat !important;
|
||||
background-size: 64px !important;
|
||||
image-rendering: pixelated !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.terminal-box {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.95), rgba(0, 0, 0, 0.95)), url(/dirt.webp) !important;
|
||||
background-repeat: repeat !important;
|
||||
background-size: 64px !important;
|
||||
image-rendering: pixelated !important;
|
||||
}
|
||||
`}</style>
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
zIndex: -1,
|
||||
backgroundImage: 'url(/minecrafttest2.webp)',
|
||||
backgroundSize: 'cover',
|
||||
animation: 'mc-panorama-pan 120s linear infinite alternate',
|
||||
filter: 'brightness(0.6)'
|
||||
}} />
|
||||
<div className="container">
|
||||
|
||||
{/* Header - TUI top bar */}
|
||||
<header className="header">
|
||||
<div className="header-center">
|
||||
<JokerLogo />
|
||||
<h1 className="site-name">dvorakmortonxd.dev/softcraft</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero - TUI welcome panel */}
|
||||
<main className="hero">
|
||||
<div className="hero-panel" style={panelStyle('var(--accent)')}>
|
||||
<span className="hero-panel-label">~ ABOUT_SOFTCRAFT.MD</span>
|
||||
<h2 className="hero-title">$ ./start_softcraft.sh</h2>
|
||||
<p className="hero-subtitle">
|
||||
Welcome to the Softcraft info page! {'>'}:)<span className="cursor-blink"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Server Info Panel */}
|
||||
<div className="about-section">
|
||||
<div className="tui-panel" style={panelStyle('var(--accent)')}>
|
||||
<span className="tui-panel-label label-yellow">├─ neofetch</span>
|
||||
<div className="about-content">
|
||||
<p>
|
||||
<span className="text-green">martin@dvorakmortonxd</span>:<span className="text-blue">~/softcraft</span>$ neofetch
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: '2rem', marginTop: '1.5rem', alignItems: 'center', flexWrap: 'wrap', color: '#a3a3a3' }}>
|
||||
|
||||
<div style={{ flexShrink: 0 }}>
|
||||
<Image
|
||||
src="/softcraft.webp"
|
||||
alt="Softcraft Logo"
|
||||
width={128}
|
||||
height={128}
|
||||
style={{ imageRendering: 'pixelated', borderRadius: '8px' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, minWidth: '250px', lineHeight: '1.6' }}>
|
||||
<div className="terminal-box" style={{ padding: '15px', borderRadius: '8px', border: '1px dashed #444', marginTop: '-10px' }}>
|
||||
<p style={{ margin: '0 0 5px 0', color: '#22c55e', fontWeight: 'bold' }}>martin<span style={{ color: '#fff' }}>@</span>softcraft</p>
|
||||
<p style={{ margin: '0 0 10px 0' }}>----------------</p>
|
||||
<p style={{ margin: '0' }}><strong>OS</strong>: Purpur 1.21.11 or newer on Debian 13 Trixie</p>
|
||||
<p style={{ margin: '0' }}><strong>Host/IP Adress</strong>: <span style={{ color: '#fff' }}>mc.dvorakmortonxd.dev</span></p>
|
||||
<p style={{ margin: '0' }}><strong>Uptime</strong>: 24/7</p>
|
||||
<p style={{ margin: '0' }}><strong>Rules</strong>: Little to no chat control, just enjoy the game bro</p>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: '0.5rem', marginTop: '15px' }}>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#000000', display: 'inline-block' }}></span>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#ef4444', display: 'inline-block' }}></span>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#22c55e', display: 'inline-block' }}></span>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#eab308', display: 'inline-block' }}></span>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#3b82f6', display: 'inline-block' }}></span>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#d946ef', display: 'inline-block' }}></span>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#06b6d4', display: 'inline-block' }}></span>
|
||||
<span style={{ width: '16px', height: '16px', backgroundColor: '#ffffff', display: 'inline-block' }}></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Links Panel */}
|
||||
<div className="services-section" style={{ marginTop: '20px' }}>
|
||||
<div className="tui-panel" style={panelStyle('var(--blue)')}>
|
||||
<span className="tui-panel-label label-blue">├─ LINKS.SH</span>
|
||||
<div className="services-buttons" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem' }}>
|
||||
<ServiceButton
|
||||
url="https://bluemap.dvorakmortonxd.dev"
|
||||
className="bluemap"
|
||||
icon="🗺️"
|
||||
name="Live Server Map"
|
||||
desc="View the world in 3D"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://discord-sc.dvorakmortonxd.dev"
|
||||
className="discord-sc"
|
||||
icon={<Image src="/discord.svg" alt="Discord" width={28} height={28} />}
|
||||
name="Community Discord"
|
||||
desc="Chat with other players"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://dvorakmortonxd.dev"
|
||||
className="back-home"
|
||||
icon="🏠"
|
||||
name="Main Site"
|
||||
desc="cd .."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Vote Panel */}
|
||||
<div className="services-section" style={{ marginTop: '20px' }}>
|
||||
<div className="tui-panel" style={panelStyle('var(--accent)')}>
|
||||
<span className="tui-panel-label label-yellow">├─ VOTE_HERE.JS</span>
|
||||
<div className="services-buttons" style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '1rem' }}>
|
||||
<ServiceButton
|
||||
url="https://czech-craft.eu/server/softcraft-refreshed/"
|
||||
className="vote"
|
||||
icon="⭐"
|
||||
name="Czech-Craft"
|
||||
desc="Vote for us!"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://www.minecraft-list.cz/server/softcraft-refreshed"
|
||||
className="vote"
|
||||
icon="⭐"
|
||||
name="Minecraft-List.cz"
|
||||
desc="Vote for us!"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://minecraftservery.eu/server/softcraft-refreshed"
|
||||
className="vote"
|
||||
icon="⭐"
|
||||
name="MinecraftServery.eu"
|
||||
desc="Vote for us!"
|
||||
/>
|
||||
<ServiceButton
|
||||
url="https://craftlist.org/softcraft"
|
||||
className="vote"
|
||||
icon="⭐"
|
||||
name="CraftList.org"
|
||||
desc="Vote for us!"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Video Panel */}
|
||||
<div className="video-section" style={{ marginTop: '20px' }}>
|
||||
<div className="tui-panel" style={panelStyle('#ec4899')}>
|
||||
<span className="tui-panel-label" style={{ color: '#ec4899', borderColor: '#ec4899' }}>└─ BASH 'LS ./VIDEOS/'</span>
|
||||
<div className="video-grid" style={{ marginTop: '10px' }}>
|
||||
{[
|
||||
"ifBnNS8X3O8",
|
||||
"fQ3T374cpZk",
|
||||
"yYrzQkhp8CQ",
|
||||
"gIl01RaQjcI",
|
||||
"e3gjtyPNZjg",
|
||||
"P97CC3BWvg4",
|
||||
"uPNN6LppOsg",
|
||||
"NCy-h29lfzM"
|
||||
].map(id => (
|
||||
<div key={id} className="video-container" style={{ width: '100%', aspectRatio: '16/9', borderRadius: '8px', overflow: 'hidden', transform: 'translateZ(0)' }}>
|
||||
<iframe
|
||||
width="100%"
|
||||
height="100%"
|
||||
src={`https://www.youtube.com/embed/${id}`}
|
||||
title="YouTube video player"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowFullScreen
|
||||
loading="lazy"
|
||||
style={{ border: 'none' }}
|
||||
></iframe>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer className="footer">
|
||||
<span className="footer-label">v1.6.7</span>
|
||||
© 2025-2026 Martin Dvořák. All rights reserved
|
||||
</footer>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue