- Go backend with PostgreSQL connection - Next.js frontend with TypeScript - Docker Compose for local development - Woodpecker CI pipeline for build and push - Kubernetes manifests with Kustomize - ArgoCD application for GitOps deployment Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
119 lines
2.8 KiB
TypeScript
119 lines
2.8 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
interface ApiResponse {
|
|
message: string
|
|
db_status: string
|
|
}
|
|
|
|
export default function Home() {
|
|
const [data, setData] = useState<ApiResponse | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080'
|
|
|
|
fetch(`${apiUrl}/api/hello`)
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
setData(data)
|
|
setLoading(false)
|
|
})
|
|
.catch((err) => {
|
|
setError(err.message)
|
|
setLoading(false)
|
|
})
|
|
}, [])
|
|
|
|
return (
|
|
<main style={styles.main}>
|
|
<div style={styles.container}>
|
|
<h1 style={styles.title}>Hello World!</h1>
|
|
<p style={styles.subtitle}>Go + PostgreSQL + Next.js</p>
|
|
|
|
<div style={styles.card}>
|
|
{loading && <p>Loading...</p>}
|
|
{error && <p style={styles.error}>Error: {error}</p>}
|
|
{data && (
|
|
<>
|
|
<p style={styles.message}>{data.message}</p>
|
|
<p style={styles.status}>
|
|
Database:
|
|
<span style={{
|
|
color: data.db_status === 'connected' ? '#22c55e' : '#ef4444',
|
|
marginLeft: '8px'
|
|
}}>
|
|
{data.db_status}
|
|
</span>
|
|
</p>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
<div style={styles.techStack}>
|
|
<span style={styles.badge}>Go</span>
|
|
<span style={styles.badge}>PostgreSQL</span>
|
|
<span style={styles.badge}>Next.js</span>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
const styles: { [key: string]: React.CSSProperties } = {
|
|
main: {
|
|
minHeight: '100vh',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
},
|
|
container: {
|
|
textAlign: 'center',
|
|
color: 'white',
|
|
},
|
|
title: {
|
|
fontSize: '4rem',
|
|
marginBottom: '0.5rem',
|
|
textShadow: '2px 2px 4px rgba(0,0,0,0.2)',
|
|
},
|
|
subtitle: {
|
|
fontSize: '1.25rem',
|
|
opacity: 0.9,
|
|
marginBottom: '2rem',
|
|
},
|
|
card: {
|
|
background: 'rgba(255,255,255,0.95)',
|
|
borderRadius: '12px',
|
|
padding: '2rem',
|
|
color: '#333',
|
|
boxShadow: '0 10px 40px rgba(0,0,0,0.2)',
|
|
marginBottom: '2rem',
|
|
},
|
|
message: {
|
|
fontSize: '1.25rem',
|
|
fontWeight: 500,
|
|
},
|
|
status: {
|
|
marginTop: '1rem',
|
|
fontSize: '1rem',
|
|
},
|
|
error: {
|
|
color: '#ef4444',
|
|
},
|
|
techStack: {
|
|
display: 'flex',
|
|
gap: '1rem',
|
|
justifyContent: 'center',
|
|
},
|
|
badge: {
|
|
background: 'rgba(255,255,255,0.2)',
|
|
padding: '0.5rem 1rem',
|
|
borderRadius: '20px',
|
|
fontSize: '0.875rem',
|
|
},
|
|
}
|