diff --git a/homer/assets/custom.css b/homer/assets/custom.css index 25b9df7..a3da684 100755 --- a/homer/assets/custom.css +++ b/homer/assets/custom.css @@ -139,4 +139,71 @@ body #bighead .navbar a:focus, body #bighead .navbar a:hover { /* Hide theme toggle button only */ .navbar-item[aria-label="Toggle dark mode"] { display: none !important; +} + +/* Prod/Dev 버튼 스타일 */ +.prod-dev-buttons { + display: flex; + gap: 0.5rem; + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +[data-theme="dark"] .prod-dev-buttons, +.dark .prod-dev-buttons { + border-top-color: rgba(255, 255, 255, 0.1); +} + +.env-button { + flex: 1; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + border-radius: 0.5rem; + text-decoration: none; + font-weight: 600; + font-size: 0.875rem; + transition: all 0.2s ease; + border: 2px solid transparent; +} + +.prod-button { + background-color: #48c774; + color: white; +} + +.prod-button:hover { + background-color: #3ab66d; + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(72, 199, 116, 0.3); +} + +.dev-button { + background-color: #ffdd57; + color: #363636; +} + +.dev-button:hover { + background-color: #ffd83d; + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(255, 221, 87, 0.3); +} + +.env-button i { + font-size: 0.875rem; +} + +/* Production 카드의 기존 링크 비활성화 */ +.card[data-has-buttons] > a, +.card[data-has-buttons] .card-content > a { + pointer-events: none; + cursor: default; +} + +/* 버튼이 있는 카드의 하단 여백 조정 */ +.card:has(.prod-dev-buttons) .card-content { + padding-bottom: 0.5rem; } \ No newline at end of file diff --git a/homer/assets/custom.js b/homer/assets/custom.js new file mode 100644 index 0000000..078d78b --- /dev/null +++ b/homer/assets/custom.js @@ -0,0 +1,153 @@ +// Production 애플리케이션의 Prod/Dev URL 매핑 +const productionApps = { + 'Joossam': { + prod: 'https://joossameng.com', + dev: 'https://dev.joossameng.com' + }, + 'Jaejadle': { + prod: 'https://jaejadle.kro.kr', + dev: 'https://dev.jaejadle.kro.kr' + }, + 'Jotion': { + prod: 'https://jotion.kro.kr', + dev: 'https://dev.jotion.kro.kr' + }, + 'Portfolio': { + prod: 'https://minjo0213.kro.kr', + dev: 'https://dev.minjo0213.kro.kr' + }, + 'Todo': { + prod: 'https://todo0213.kro.kr', + dev: 'https://dev.todo0213.kro.kr' + }, + 'Jovies': { + prod: 'https://jovies.kro.kr', + dev: 'https://dev.jovies.kro.kr' + } +}; + +// Production 카드에 Prod/Dev 버튼 추가 +function addProdDevButtons() { + // Production 섹션의 모든 카드 찾기 + const productionSection = document.querySelector('[data-group="Production"]') || + Array.from(document.querySelectorAll('.group-title')) + .find(el => el.textContent.trim() === 'Production')?.closest('.column'); + + if (!productionSection) { + // 다른 방법으로 Production 섹션 찾기 + const allCards = document.querySelectorAll('.card'); + allCards.forEach(card => { + const titleElement = card.querySelector('.title'); + if (titleElement && productionApps[titleElement.textContent.trim()]) { + addButtonsToCard(card, titleElement.textContent.trim()); + } + }); + return; + } + + // Production 섹션 내의 모든 카드 찾기 + const cards = productionSection.querySelectorAll('.card'); + cards.forEach(card => { + const titleElement = card.querySelector('.title'); + if (titleElement) { + const appName = titleElement.textContent.trim(); + if (productionApps[appName]) { + addButtonsToCard(card, appName); + } + } + }); +} + +// 카드에 Prod/Dev 버튼 추가 +function addButtonsToCard(card, appName) { + // 이미 버튼이 추가되었는지 확인 + if (card.querySelector('.prod-dev-buttons')) { + return; + } + + const appUrls = productionApps[appName]; + if (!appUrls) return; + + // 기존 링크 제거 또는 비활성화 + const existingLink = card.querySelector('a[href]'); + if (existingLink) { + existingLink.style.pointerEvents = 'none'; + existingLink.style.cursor = 'default'; + } + + // 버튼 컨테이너 생성 + const buttonContainer = document.createElement('div'); + buttonContainer.className = 'prod-dev-buttons'; + + // Prod 버튼 + const prodButton = document.createElement('a'); + prodButton.href = appUrls.prod; + prodButton.target = '_blank'; + prodButton.className = 'env-button prod-button'; + prodButton.innerHTML = ' Prod'; + prodButton.title = 'Production Environment'; + + // Dev 버튼 + const devButton = document.createElement('a'); + devButton.href = appUrls.dev; + devButton.target = '_blank'; + devButton.className = 'env-button dev-button'; + devButton.innerHTML = ' Dev'; + devButton.title = 'Development Environment'; + + buttonContainer.appendChild(prodButton); + buttonContainer.appendChild(devButton); + + // 카드의 content 영역에 버튼 추가 + const cardContent = card.querySelector('.card-content') || card; + cardContent.appendChild(buttonContainer); +} + +// DOM이 로드된 후 실행 +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + // 약간의 지연을 두고 실행 (Homer가 카드를 렌더링할 시간 필요) + setTimeout(addProdDevButtons, 500); + // MutationObserver로 동적 콘텐츠 감지 + observeChanges(); + }); +} else { + setTimeout(addProdDevButtons, 500); + observeChanges(); +} + +// DOM 변경 감지하여 동적으로 추가된 카드에도 버튼 추가 +function observeChanges() { + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.addedNodes.length) { + mutation.addedNodes.forEach((node) => { + if (node.nodeType === 1) { // Element node + if (node.classList && node.classList.contains('card')) { + const titleElement = node.querySelector('.title'); + if (titleElement && productionApps[titleElement.textContent.trim()]) { + addButtonsToCard(node, titleElement.textContent.trim()); + } + } + // 자식 노드에서도 카드 찾기 + const cards = node.querySelectorAll && node.querySelectorAll('.card'); + if (cards) { + cards.forEach(card => { + const titleElement = card.querySelector('.title'); + if (titleElement && productionApps[titleElement.textContent.trim()]) { + addButtonsToCard(card, titleElement.textContent.trim()); + } + }); + } + } + }); + } + }); + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); +} + diff --git a/homer/config.yml b/homer/config.yml index c73a9a9..f23a846 100644 --- a/homer/config.yml +++ b/homer/config.yml @@ -20,7 +20,7 @@ defaults: colorTheme: "light" # Set default theme to light mode # Optional columns -columns: "4" # You can change this to any number that is a factor of 12: (1, 2, 3, 4, 6, 12) +columns: "3" # You can change this to any number that is a factor of 12: (1, 2, 3, 4, 6, 12) # Optional colors colors: @@ -59,6 +59,10 @@ websocket: url: "ws://localhost:8080" reconnectInterval: 5000 +# Custom HTML (for loading JavaScript) +custom: + - '' + # Optional services services: - name: "Production" @@ -113,7 +117,7 @@ services: url: "https://jovies.kro.kr" target: "_blank" - - name: "Development" + - name: "Development Tools" icon: "fas fa-laptop-code" items: - name: "Chainlit" @@ -148,22 +152,6 @@ services: keywords: "git gitea" url: "https://gitea0213.kro.kr" target: "_blank" - - name: "Goldilocks" - logo: "/assets/icons/goldilocks.svg" - subtitle: "Resource Management" - tag: "dev" - tagstyle: "is-warning" - keywords: "goldilocks resource management" - url: "https://goldilocks0213.kro.kr" - target: "_blank" - - name: "Homer Dashboard" - logo: "/assets/icons/favicon-32x32.png" - subtitle: "Dashboard" - tag: "dev" - tagstyle: "is-warning" - keywords: "dashboard" - url: "https://mayne.kro.kr" - target: "_blank" - name: "Minecraft Server" logo: "/assets/icons/minecraft.svg" subtitle: "Coming Soon" @@ -180,14 +168,6 @@ services: keywords: "Auto Hot Reload" url: "https://tilt0213.kro.kr" target: "_blank" - - name: "Velero" - logo: "/assets/icons/velero.webp" - subtitle: "Backup & Disaster Recovery" - tag: "dev" - tagstyle: "is-warning" - keywords: "backup restore" - url: "https://velero0213.kro.kr" - target: "_blank" - name: "Infrastructure" icon: "fas fa-server" @@ -200,21 +180,13 @@ services: keywords: "argocd gitops" url: "https://argocd0213.kro.kr" target: "_blank" - - name: "Falco" - logo: "/assets/icons/falco.svg" - subtitle: "Runtime Security" + - name: "Homer Dashboard" + logo: "/assets/icons/favicon-32x32.png" + subtitle: "Dashboard" tag: "infra" tagstyle: "is-info" - keywords: "falco security runtime" - url: "https://falco0213.kro.kr" - target: "_blank" - - name: "Grafana" - logo: "/assets/icons/grafana.svg" - subtitle: "Monitoring Dashboard" - tag: "infra" - tagstyle: "is-info" - keywords: "grafana monitoring" - url: "https://grafana0213.kro.kr" + keywords: "dashboard" + url: "https://mayne.kro.kr" target: "_blank" - name: "Kubernetes Dashboard" logo: "/assets/icons/kubernetes.svg" @@ -224,38 +196,6 @@ services: keywords: "kubernetes k8s" url: "https://kubernetes0213.kro.kr" target: "_blank" - - name: "Longhorn" - logo: "/assets/icons/longhorn.webp" - subtitle: "Block Storage Management" - tag: "infra" - tagstyle: "is-info" - keywords: "longhorn storage" - url: "https://longhorn0213.kro.kr" - target: "_blank" - - name: "MinIO Console" - logo: "/assets/icons/minio.svg" - subtitle: "S3 Storage Management" - tag: "infra" - tagstyle: "is-info" - keywords: "minio s3 storage" - url: "https://minio0213.kro.kr" - target: "_blank" - - name: "PgWeb" - logo: "/assets/icons/postgresql.svg" - subtitle: "PostgreSQL Management" - tag: "infra" - tagstyle: "is-info" - keywords: "postgresql pgweb" - url: "https://pgweb0213.kro.kr" - target: "_blank" - - name: "Umami" - logo: "/assets/icons/umami.svg" - subtitle: "Website Analytics" - tag: "infra" - tagstyle: "is-warning" - keywords: "analytics umami" - url: "https://umami0213.kro.kr" - target: "_blank" - name: "Vault" logo: "/assets/icons/vault.svg" subtitle: "Secret Management" @@ -264,6 +204,78 @@ services: keywords: "vault management" url: "https://vault0213.kro.kr" target: "_blank" + - name: "Velero" + logo: "/assets/icons/velero.webp" + subtitle: "Backup & Disaster Recovery" + tag: "infra" + tagstyle: "is-info" + keywords: "backup restore" + url: "https://velero0213.kro.kr" + target: "_blank" + + - name: "Monitoring" + icon: "fas fa-chart-line" + items: + - name: "Falco" + logo: "/assets/icons/falco.svg" + subtitle: "Runtime Security" + tag: "infra" + tagstyle: "is-info" + keywords: "falco security runtime" + url: "https://falco0213.kro.kr" + target: "_blank" + - name: "Goldilocks" + logo: "/assets/icons/goldilocks.svg" + subtitle: "Resource Management" + tag: "monitoring" + tagstyle: "is-info" + keywords: "goldilocks resource management" + url: "https://goldilocks0213.kro.kr" + target: "_blank" + - name: "Grafana" + logo: "/assets/icons/grafana.svg" + subtitle: "Monitoring Dashboard" + tag: "monitoring" + tagstyle: "is-info" + keywords: "grafana monitoring" + url: "https://grafana0213.kro.kr" + target: "_blank" + - name: "Umami" + logo: "/assets/icons/umami.svg" + subtitle: "Website Analytics" + tag: "monitoring" + tagstyle: "is-info" + keywords: "analytics umami" + url: "https://umami0213.kro.kr" + target: "_blank" + + - name: "Storage & Database" + icon: "fas fa-database" + items: + - name: "Longhorn" + logo: "/assets/icons/longhorn.webp" + subtitle: "Block Storage Management" + tag: "storage" + tagstyle: "is-info" + keywords: "longhorn storage" + url: "https://longhorn0213.kro.kr" + target: "_blank" + - name: "MinIO Console" + logo: "/assets/icons/minio.svg" + subtitle: "S3 Storage Management" + tag: "storage" + tagstyle: "is-info" + keywords: "minio s3 storage" + url: "https://minio0213.kro.kr" + target: "_blank" + - name: "PgWeb" + logo: "/assets/icons/postgresql.svg" + subtitle: "PostgreSQL Management" + tag: "storage" + tagstyle: "is-info" + keywords: "postgresql pgweb" + url: "https://pgweb0213.kro.kr" + target: "_blank" - name: "Demos" icon: "fas fa-code-branch"