Files
portfolio/services/nextjs/app/api/argocd/route.ts
Mayne0213 5a48c69590 REFACTOR(app): change SSL from fetch to HTTPS
- Update SSL configuration
- Use HTTPS for API calls
2025-11-25 12:59:23 +09:00

161 lines
4.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import https from 'https';
import { URL } from 'url';
// 클러스터 내부에서 실행되는 경우 클러스터 내부 서비스 URL 사용
// 외부에서 실행되는 경우 환경 변수로 설정 가능
const ARGOCD_SERVER_URL = process.env.ARGOCD_SERVER_URL || 'https://argocd-server.argocd.svc.cluster.local';
const ARGOCD_TOKEN = process.env.ARGOCD_TOKEN || '';
const ARGOCD_CA_CERT = process.env.ARGOCD_CA_CERT || '';
// Node.js 환경에서 커스텀 fetch 함수 생성 (인증서 지원)
async function fetchWithCert(url: string, options: RequestInit = {}): Promise<Response> {
// 브라우저 환경에서는 기본 fetch 사용
if (typeof window !== 'undefined') {
return fetch(url, options);
}
// Node.js 환경에서 인증서가 있으면 https 모듈 직접 사용
if (ARGOCD_CA_CERT) {
return new Promise((resolve, reject) => {
const urlObj = new URL(url);
const requestOptions: https.RequestOptions = {
hostname: urlObj.hostname,
port: urlObj.port || 443,
path: urlObj.pathname + urlObj.search,
method: options.method || 'GET',
headers: {
...(options.headers as Record<string, string>),
},
ca: ARGOCD_CA_CERT,
rejectUnauthorized: true,
};
const req = https.request(requestOptions, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
const response = new Response(data, {
status: res.statusCode || 200,
statusText: res.statusMessage || 'OK',
headers: res.headers as HeadersInit,
});
resolve(response);
});
});
req.on('error', (error) => {
reject(error);
});
if (options.body) {
req.write(typeof options.body === 'string' ? options.body : JSON.stringify(options.body));
}
req.end();
});
}
return fetch(url, options);
}
interface ArgoCDApplication {
metadata: {
name: string;
namespace: string;
};
spec: {
source: {
repoURL: string;
path?: string;
targetRevision: string;
};
destination: {
server: string;
namespace: string;
};
};
status: {
health: {
status: string;
};
sync: {
status: string;
};
resources?: Array<{
kind: string;
name: string;
namespace: string;
status: string;
health?: {
status: string;
};
}>;
};
}
// GET /api/argocd - Get all ArgoCD applications
export async function GET(request: NextRequest) {
try {
if (!ARGOCD_TOKEN) {
return NextResponse.json(
{ error: 'ArgoCD token not configured' },
{ status: 500 }
);
}
// ArgoCD API v1 - Get applications
const response = await fetchWithCert(`${ARGOCD_SERVER_URL}/api/v1/applications`, {
headers: {
'Authorization': `Bearer ${ARGOCD_TOKEN}`,
'Content-Type': 'application/json',
},
cache: 'no-store',
});
if (!response.ok) {
const errorText = await response.text();
console.error('ArgoCD API error:', response.status, errorText);
return NextResponse.json(
{ error: `ArgoCD API error: ${response.status}` },
{ status: response.status }
);
}
const data = await response.json();
// Read-only로 필터링 (필요한 정보만 반환)
const applications = (data.items || []).map((app: ArgoCDApplication) => ({
name: app.metadata.name,
namespace: app.metadata.namespace,
repoURL: app.spec.source.repoURL,
path: app.spec.source.path || '',
targetRevision: app.spec.source.targetRevision,
destination: app.spec.destination,
health: app.status.health?.status || 'Unknown',
sync: app.status.sync?.status || 'Unknown',
resources: app.status.resources?.map((resource) => ({
kind: resource.kind,
name: resource.name,
namespace: resource.namespace,
status: resource.status,
health: resource.health?.status || 'Unknown',
})) || [],
}));
return NextResponse.json({
applications,
timestamp: new Date().toISOString(),
});
} catch (error) {
console.error('Error fetching ArgoCD applications:', error);
return NextResponse.json(
{ error: 'Failed to fetch ArgoCD applications' },
{ status: 500 }
);
}
}