서비스 분류별로 구분

This commit is contained in:
caadiq 2025-12-21 18:14:42 +09:00
parent 406d5bf950
commit 3e91702198
4 changed files with 149 additions and 72 deletions

View file

@ -16,12 +16,14 @@ const SERVICES_URLS: &[(&str, &str)] = &[
("jenkins", "http://jenkins:8080/login"), ("jenkins", "http://jenkins:8080/login"),
("portainer", "http://portainer:9000"), ("portainer", "http://portainer:9000"),
("rustfs", "http://rustfs:9001"), ("rustfs", "http://rustfs:9001"),
("uptime-kuma", "http://uptime-kuma:3001"), ("vaultwarden", "http://vaultwarden:80"),
("dozzle", "http://dozzle:8080"), ("dozzle", "http://dozzle:8080"),
("it-tools", "http://it-tools:80"), ("it-tools", "http://it-tools:80"),
("ffmpeg-downloader", "http://ffmpeg-gui:3000"), ("ffmpeg-downloader", "http://ffmpeg-gui:3000"),
("cloudbeaver", "http://cloudbeaver:8978"), ("cloudbeaver", "http://cloudbeaver:8978"),
("forgejo", "http://forgejo:3000") ("forgejo", "http://forgejo:3000"),
("affine", "http://affine:3010"),
("hoppscotch", "http://hoppscotch:80")
]; ];
/// 시스템 상태 정보 구조체 /// 시스템 상태 정보 구조체

View file

@ -87,10 +87,6 @@ export default function App() {
</AnimatePresence> </AnimatePresence>
</div> </div>
{/* 푸터 */}
<footer className="py-6 text-center text-slate-500 text-sm mt-auto border-t border-white/5 w-full">
&copy; {new Date().getFullYear()} Caadiq Home Server. All rights reserved.
</footer>
</motion.main> </motion.main>
</div> </div>
); );

View file

@ -1,80 +1,140 @@
import { import {
Terminal, Terminal,
HeartPulse,
Wrench, Wrench,
Database, Database,
HardDrive, HardDrive,
Server, Server,
Download, Download,
GitBranch, GitBranch,
FileText,
Zap,
Monitor,
FolderCode,
Sparkles,
KeyRound,
} from "lucide-react"; } from "lucide-react";
/** /**
* 서비스 목록 설정 * 서비스 카테고리 설정
* 대시보드에 표시될 서비스들을 정의합니다. * 대시보드에 표시될 서비스들을 카테고리별로 그룹화합니다.
*/ */
export const serviceList = [ export const serviceCategories = [
{ {
id: "portainer", id: "infrastructure",
title: "Portainer", title: "인프라 관리",
desc: "Docker 컨테이너 관리", icon: Monitor,
services: [
{
id: "portainer",
title: "Portainer",
desc: "Docker 컨테이너 관리",
icon: Database,
url: "https://portainer.caadiq.co.kr",
},
{
id: "dozzle",
title: "Dozzle",
desc: "실시간 로그 뷰어",
icon: Terminal,
url: "https://dozzle.caadiq.co.kr",
},
],
},
{
id: "data-storage",
title: "데이터 & 저장소",
icon: Database, icon: Database,
url: "https://portainer.caadiq.co.kr", services: [
{
id: "cloudbeaver",
title: "CloudBeaver",
desc: "데이터베이스 관리",
icon: Database,
url: "https://cloudbeaver.caadiq.co.kr",
},
{
id: "rustfs",
title: "RustFS",
desc: "S3 호환 스토리지",
icon: HardDrive,
url: "https://rustfs.caadiq.co.kr",
},
{
id: "vaultwarden",
title: "Vaultwarden",
desc: "비밀번호 관리",
icon: KeyRound,
url: "https://vaultwarden.caadiq.co.kr",
},
],
}, },
{ {
id: "dozzle", id: "development",
title: "Dozzle", title: "개발 도구",
desc: "실시간 로그 뷰어", icon: FolderCode,
icon: Terminal, services: [
url: "https://dozzle.caadiq.co.kr", {
id: "forgejo",
title: "Forgejo",
desc: "Git 저장소 관리",
icon: GitBranch,
url: "https://forgejo.caadiq.co.kr",
},
{
id: "jenkins",
title: "Jenkins",
desc: "CI/CD 파이프라인",
icon: Server,
url: "https://jenkins.caadiq.co.kr/login",
},
{
id: "hoppscotch",
title: "Hoppscotch",
desc: "API 테스트 도구",
icon: Zap,
url: "https://hoppscotch.caadiq.co.kr",
},
],
}, },
{ {
id: "uptime-kuma", id: "productivity",
title: "Uptime Kuma", title: "생산성 & 유틸리티",
desc: "서버 상태 모니터링", icon: Sparkles,
icon: HeartPulse, services: [
url: "https://uptime-kuma.caadiq.co.kr", {
}, id: "it-tools",
{ title: "IT-Tools",
id: "cloudbeaver", desc: "개발자 도구 모음",
title: "CloudBeaver", icon: Wrench,
desc: "데이터베이스 관리", url: "https://it-tools.caadiq.co.kr",
icon: Database, },
url: "https://cloudbeaver.caadiq.co.kr", {
}, id: "ffmpeg-downloader",
{ title: "FFmpeg Downloader",
id: "forgejo", desc: "동영상 다운로드 도구",
title: "Forgejo", icon: Download,
desc: "Git 저장소 관리", url: "https://ffmpeg.caadiq.co.kr",
icon: GitBranch, },
url: "https://forgejo.caadiq.co.kr", {
}, id: "affine",
{ title: "AFFiNE",
id: "jenkins", desc: "문서 및 노트 관리",
title: "Jenkins", icon: FileText,
desc: "CI/CD 파이프라인", url: "https://affine.caadiq.co.kr",
icon: Server, },
url: "https://jenkins.caadiq.co.kr/login", ],
},
{
id: "rustfs",
title: "RustFS",
desc: "S3 호환 스토리지",
icon: HardDrive,
url: "https://rustfs.caadiq.co.kr",
},
{
id: "it-tools",
title: "IT-Tools",
desc: "개발자 도구 모음",
icon: Wrench,
url: "https://it-tools.caadiq.co.kr",
},
{
id: "ffmpeg-downloader",
title: "FFmpeg Downloader",
desc: "동영상 다운로드 도구",
icon: Download,
url: "https://ffmpeg.caadiq.co.kr",
}, },
]; ];
/**
* 기존 호환성을 위한 평면 서비스 목록
* 백엔드 상태 체크 등에서 사용됩니다.
*/
export const serviceList = serviceCategories.flatMap(
(category) => category.services
);
// 각 카테고리 내 서비스들을 title 기준 알파벳순으로 자동 정렬
serviceCategories.forEach((category) => {
category.services.sort((a, b) => a.title.localeCompare(b.title));
});

View file

@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import ServiceCard from '../components/ServiceCard'; import ServiceCard from '../components/ServiceCard';
import { serviceList } from '../data/services'; import { serviceCategories } from '../data/services';
/** /**
* 페이지 컴포넌트 * 서비스 페이지 컴포넌트
* 사용 가능한 서비스 목록을 표시합니다. * 사용 가능한 서비스 목록을 카테고리별로 그룹화하여 표시합니다.
* *
* @param {Object} props * @param {Object} props
* @param {Object} props.services - 서비스의 상태 (key: service id, value: boolean) * @param {Object} props.services - 서비스의 상태 (key: service id, value: boolean)
@ -24,9 +24,28 @@ export default function Service({ services }) {
<p className="text-slate-400">운영 중인 모든 서비스를 한눈에 확인하세요.</p> <p className="text-slate-400">운영 중인 모든 서비스를 한눈에 확인하세요.</p>
</div> </div>
<div className="grid grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4 sm:gap-6"> {/* 카테고리별 서비스 그룹 */}
{serviceList.map(svc => ( <div className="space-y-10">
<ServiceCard key={svc.id} {...svc} status={services[svc.id]} /> {serviceCategories.map((category) => (
<div key={category.id}>
{/* 카테고리 헤더 */}
<div className="flex items-center gap-3 mb-5">
<div className="p-2 rounded-lg bg-white/[0.04] border border-white/[0.08]">
<category.icon size={20} className="text-slate-400" />
</div>
<h2 className="text-xl font-semibold text-slate-200">
{category.title}
</h2>
<div className="flex-1 h-px bg-gradient-to-r from-white/[0.08] to-transparent ml-2" />
</div>
{/* 서비스 카드 그리드 */}
<div className="grid grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4 sm:gap-6">
{category.services.map(svc => (
<ServiceCard key={svc.id} {...svc} status={services[svc.id]} />
))}
</div>
</div>
))} ))}
</div> </div>
</motion.section> </motion.section>