connect tailscale node status through platform gateway
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -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,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>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -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,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>
|
||||||
|
|||||||
Reference in New Issue
Block a user