connect tailscale node status through platform gateway
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-08 18:08:04 +09:00
parent 6dd1166c7a
commit c71c25016e
4 changed files with 19 additions and 37 deletions
-7
View File
@@ -6,7 +6,6 @@ import {
ChevronRight, ChevronRight,
CircleHelp, CircleHelp,
ExternalLink, ExternalLink,
GitBranch,
LogOut, LogOut,
Search, Search,
SlidersHorizontal SlidersHorizontal
@@ -1050,16 +1049,10 @@ function CommonAppsPanel({ pipelines, loading }: { pipelines: CommonAppPipeline[
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="min-w-0"> <div className="min-w-0">
<div className="flex min-w-0 items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
<GitBranch className="size-3.5 shrink-0 text-muted-foreground" />
<p className="truncate text-sm font-semibold">{pipeline.repository}</p> <p className="truncate text-sm font-semibold">{pipeline.repository}</p>
</div> </div>
<p className="mt-1 truncate text-sm text-muted-foreground">{pipeline.message}</p> <p className="mt-1 truncate text-sm text-muted-foreground">{pipeline.message}</p>
</div> </div>
{pipeline.url ? (
<a href={pipeline.url} target="_blank" rel="noreferrer" className="shrink-0 text-muted-foreground hover:text-foreground" aria-label="Woodpecker 저장소 열기">
<ExternalLink className="size-4" />
</a>
) : null}
</div> </div>
<div className="mt-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground"> <div className="mt-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
<Badge tone={getTone(pipeline.status)}>{pipeline.status}</Badge> <Badge tone={getTone(pipeline.status)}>{pipeline.status}</Badge>
+1 -7
View File
@@ -1,4 +1,4 @@
import { ExternalLink, GitBranch, Loader2, RotateCcw } from 'lucide-react'; import { Loader2, RotateCcw } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Badge, Button, Card, CardHeader } from '../../components/ui'; import { Badge, Button, Card, CardHeader } from '../../components/ui';
import { useToast } from '../../components/toast'; import { useToast } from '../../components/toast';
@@ -82,7 +82,6 @@ export function CommonAppBuildPage() {
<div className="min-w-0"> <div className="min-w-0">
<div className="flex min-w-0 items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
<span className={cn('size-2.5 shrink-0 rounded-full', pipelineStatusDotClass(pipeline.status))} /> <span className={cn('size-2.5 shrink-0 rounded-full', pipelineStatusDotClass(pipeline.status))} />
<GitBranch className="size-3.5 shrink-0 text-muted-foreground" />
<p className="truncate text-sm font-semibold">{formatRepositoryName(pipeline.repository)}</p> <p className="truncate text-sm font-semibold">{formatRepositoryName(pipeline.repository)}</p>
{isActivePipelineStatus(pipeline.status) ? ( {isActivePipelineStatus(pipeline.status) ? (
<Loader2 className="size-3.5 shrink-0 animate-spin text-sky-300" /> <Loader2 className="size-3.5 shrink-0 animate-spin text-sky-300" />
@@ -100,11 +99,6 @@ export function CommonAppBuildPage() {
<span>{formatPipelineRelativeTime(pipeline.createdAt)}</span> <span>{formatPipelineRelativeTime(pipeline.createdAt)}</span>
</div> </div>
</div> </div>
{pipeline.url ? (
<a href={pipeline.url} target="_blank" rel="noreferrer" className="shrink-0 rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground" aria-label="저장소 열기">
<ExternalLink className="size-4" />
</a>
) : null}
</div> </div>
</div> </div>
))} ))}
+16 -15
View File
@@ -1,4 +1,4 @@
import { ExternalLink, FileText, GitBranch, RotateCcw } from 'lucide-react'; import { FileText, RotateCcw } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui'; import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui';
import { useToast } from '../../components/toast'; import { useToast } from '../../components/toast';
@@ -25,6 +25,7 @@ const commitRangeLabels: Record<CommitRange, string> = {
const commitPageSize = 5; const commitPageSize = 5;
const branchOptions = ['develop', 'release', 'main']; const branchOptions = ['develop', 'release', 'main'];
const defaultCicdBaseUrl = 'http://192.168.0.20:8010';
function repoFullName(repo: GiteaRepo) { function repoFullName(repo: GiteaRepo) {
return (repo as GiteaRepo & { fullName?: string }).fullName ?? repo.full_name ?? repo.name; return (repo as GiteaRepo & { fullName?: string }).fullName ?? repo.full_name ?? repo.name;
@@ -36,6 +37,17 @@ function splitRepository(repository: string) {
return owner && repo ? { owner, repo } : null; return owner && repo ? { owner, repo } : null;
} }
function cicdLabel(pipeline?: CommonAppPipeline) {
if (!pipeline) return '-';
try {
const origin = pipeline.url ? new URL(pipeline.url).origin : '';
const baseUrl = origin && !origin.includes(':3000') ? origin : defaultCicdBaseUrl;
return `Woodpecker / ${baseUrl}`;
} catch {
return `Woodpecker / ${defaultCicdBaseUrl}`;
}
}
function commitAuthor(commit: GiteaCommit) { function commitAuthor(commit: GiteaCommit) {
return commit.author?.login return commit.author?.login
|| commit.author?.username || commit.author?.username
@@ -136,6 +148,7 @@ export function CommonAppSourcePage() {
}); });
}, [knownCommonRepos, repos]); }, [knownCommonRepos, repos]);
const selectedRepo = useMemo(() => repos.find((repo) => repoFullName(repo) === selectedRepository), [repos, selectedRepository]); const selectedRepo = useMemo(() => repos.find((repo) => repoFullName(repo) === selectedRepository), [repos, selectedRepository]);
const selectedPipeline = pipelineByRepo.get(selectedRepository);
const repositoryParts = useMemo(() => splitRepository(selectedRepository), [selectedRepository]); const repositoryParts = useMemo(() => splitRepository(selectedRepository), [selectedRepository]);
const totalCommitPages = totalCommits === null ? null : Math.max(1, Math.ceil(totalCommits / commitPageSize)); const totalCommitPages = totalCommits === null ? null : Math.max(1, Math.ceil(totalCommits / commitPageSize));
@@ -240,7 +253,6 @@ export function CommonAppSourcePage() {
<div className="divide-y divide-border"> <div className="divide-y divide-border">
{sortedRepos.map((repo) => { {sortedRepos.map((repo) => {
const fullName = repoFullName(repo); const fullName = repoFullName(repo);
const pipeline = pipelineByRepo.get(fullName);
const selected = fullName === selectedRepository; const selected = fullName === selectedRepository;
return ( return (
<button <button
@@ -251,13 +263,9 @@ export function CommonAppSourcePage() {
> >
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="min-w-0"> <div className="min-w-0">
<div className="flex min-w-0 items-center gap-2">
<GitBranch className="size-3.5 shrink-0 text-muted-foreground" />
<p className="truncate text-sm font-semibold">{formatRepositoryName(fullName)}</p> <p className="truncate text-sm font-semibold">{formatRepositoryName(fullName)}</p>
</div>
<p className="mt-1 truncate text-xs text-muted-foreground">{repo.private ? 'Private' : 'Public'}</p> <p className="mt-1 truncate text-xs text-muted-foreground">{repo.private ? 'Private' : 'Public'}</p>
</div> </div>
<Badge tone={pipeline ? 'green' : 'neutral'}>{pipeline ? 'Woodpecker 연결됨' : '미연결'}</Badge>
</div> </div>
</button> </button>
); );
@@ -274,10 +282,8 @@ export function CommonAppSourcePage() {
<p className="mt-2 truncate text-sm font-semibold">{selectedRepository ? formatRepositoryName(selectedRepository) : '-'}</p> <p className="mt-2 truncate text-sm font-semibold">{selectedRepository ? formatRepositoryName(selectedRepository) : '-'}</p>
</div> </div>
<div className="rounded-md border border-border bg-muted/20 p-4"> <div className="rounded-md border border-border bg-muted/20 p-4">
<p className="text-xs text-muted-foreground">Woodpecker</p> <p className="text-xs text-muted-foreground">CI/CD</p>
<div className="mt-2"> <p className="mt-2 truncate text-sm font-semibold">{cicdLabel(selectedPipeline)}</p>
<Badge tone={pipelineByRepo.has(selectedRepository) ? 'green' : 'neutral'}>{pipelineByRepo.has(selectedRepository) ? '연결됨' : '미연결'}</Badge>
</div>
</div> </div>
<div> <div>
<p className="text-xs text-muted-foreground">조회 브랜치</p> <p className="text-xs text-muted-foreground">조회 브랜치</p>
@@ -295,11 +301,6 @@ export function CommonAppSourcePage() {
<p className="text-xs text-muted-foreground">Gitea URL</p> <p className="text-xs text-muted-foreground">Gitea URL</p>
<div className="mt-2 flex h-9 items-center gap-2 rounded-md border border-border bg-background px-3"> <div className="mt-2 flex h-9 items-center gap-2 rounded-md border border-border bg-background px-3">
<p className="min-w-0 flex-1 truncate text-xs text-muted-foreground">{selectedRepo?.html_url ?? '-'}</p> <p className="min-w-0 flex-1 truncate text-xs text-muted-foreground">{selectedRepo?.html_url ?? '-'}</p>
{selectedRepo?.html_url ? (
<Button variant="ghost" className="size-8 px-0" onClick={() => window.open(selectedRepo.html_url, '_blank', 'noopener,noreferrer')} aria-label="Gitea 저장소 열기">
<ExternalLink className="size-4" />
</Button>
) : null}
</div> </div>
</div> </div>
</div> </div>
+1 -7
View File
@@ -1,4 +1,4 @@
import { Activity, Boxes, ExternalLink, FileKey, GitBranch, RefreshCw } from 'lucide-react'; import { Activity, Boxes, FileKey, RefreshCw } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Badge, Button, Card, CardHeader } from '../../components/ui'; import { Badge, Button, Card, CardHeader } from '../../components/ui';
import { PageTitle } from '../../layout/AppLayout'; import { PageTitle } from '../../layout/AppLayout';
@@ -132,16 +132,10 @@ export function DashboardPage() {
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="min-w-0"> <div className="min-w-0">
<div className="flex min-w-0 items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
<GitBranch className="size-3.5 shrink-0 text-muted-foreground" />
<p className="truncate text-sm font-semibold">{pipeline.repository}</p> <p className="truncate text-sm font-semibold">{pipeline.repository}</p>
</div> </div>
<p className="mt-1 truncate text-sm text-muted-foreground">{pipeline.message}</p> <p className="mt-1 truncate text-sm text-muted-foreground">{pipeline.message}</p>
</div> </div>
{pipeline.url ? (
<a href={pipeline.url} target="_blank" rel="noreferrer" className="shrink-0 text-muted-foreground hover:text-foreground" aria-label="Woodpecker 저장소 열기">
<ExternalLink className="size-4" />
</a>
) : null}
</div> </div>
<div className="mt-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground"> <div className="mt-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
<Badge tone={pipelineTone(pipeline.status)}>{pipeline.status}</Badge> <Badge tone={pipelineTone(pipeline.status)}>{pipeline.status}</Badge>