add login register and create cluster api

This commit is contained in:
behrooz
2025-08-29 19:57:59 +03:30
parent e4d83eb1bf
commit 24e362cdba
4 changed files with 133 additions and 72 deletions

View File

@@ -47,7 +47,7 @@ export default function DashboardLayout() {
} }
> >
<Layers size={18} /> <Layers size={18} />
Create Cluster List Cluster
</NavLink> </NavLink>
<div className="pt-4 border-t border-gray-200"> <div className="pt-4 border-t border-gray-200">

View File

@@ -1,11 +1,15 @@
import { useState } from 'react' import { useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Download, Trash2 } from 'lucide-react' import { Download, Feather, Trash2 } from 'lucide-react'
import type { FormEvent } from 'react' import type { FormEvent } from 'react'
interface Cluster { interface Cluster {
id: string id: string
name: string Name: string
ControlPlane: string
PlatformVersion: string
Cpu: string
Memory: string
clusterId: string clusterId: string
status: string status: string
version: string version: string
@@ -15,15 +19,30 @@ interface Cluster {
export default function CreateCluster() { export default function CreateCluster() {
const [clusters, setClusters] = useState<Cluster[]>(() => { const [clusters, setClusters] = useState<Cluster[]>(() => {
fetch('http://localhost:8082/clusters', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `${localStorage.getItem('auth:token') || ''}`
},
}).then(async (res) => {
if (res.ok) {
const data = await res.json()
setClusters(data.clusters || [])
return data.clusters || []
//localStorage.setItem('clusters', JSON.stringify(data.clusters || []))
} else {
const data = await res.json()
console.error(data.message || 'Failed to fetch clusters')
}
}).catch(() => {
console.error('Failed to fetch clusters')
})
const saved = localStorage.getItem('clusters') const saved = localStorage.getItem('clusters')
if (saved) { if (saved) {
return JSON.parse(saved) return JSON.parse(saved)
} }
return [ return []
{ id: '1', name: 'dev-cluster', clusterId: '680d172c', status: 'Healthy', version: 'v1.28.0', alerts: '0', endpoint: 'https://dev-cluster.example.com' },
{ id: '2', name: 'prod-cluster', clusterId: 'd02b06fe', status: 'Healthy', version: 'v1.28.0', alerts: '0', endpoint: 'https://prod-cluster.example.com' },
{ id: '3', name: 'test-prod', clusterId: '937261be', status: 'Healthy', version: 'v1.28.0', alerts: '0', endpoint: 'https://test-prod.example.com' },
]
}) })
const [showModal, setShowModal] = useState(false) const [showModal, setShowModal] = useState(false)
@@ -32,34 +51,51 @@ export default function CreateCluster() {
namespace: '', namespace: '',
controlPlane: 'Kubernetes (k8s)', controlPlane: 'Kubernetes (k8s)',
kubernetesVersion: 'v1.31.6 (recommended)', kubernetesVersion: 'v1.31.6 (recommended)',
cpu: 1, Cpu: 1,
memory: 2048 Memory: 1024
}) })
const handleSubmit = (e: FormEvent) => { const handleSubmit = (e: FormEvent) => {
e.preventDefault() e.preventDefault()
const newCluster: Cluster = { const newCluster: Cluster = {
id: Date.now().toString(), Name: formData.clusterName,
name: formData.clusterName,
clusterId: Math.random().toString(36).substr(2, 8), clusterId: Math.random().toString(36).substr(2, 8),
status: 'Creating', status: 'Creating',
version: formData.kubernetesVersion.split(' ')[0], ControlPlane: formData.controlPlane,
alerts: '0', Cpu: formData.Cpu.toString(),
endpoint: `https://${formData.clusterName}.example.com` Memory: formData.Memory.toString(),
PlatformVersion: formData.kubernetesVersion.split(' ')[0]
} }
const updatedClusters = [...clusters, newCluster] fetch('http://localhost:8082/createcluster', {
setClusters(updatedClusters) method: 'POST',
localStorage.setItem('clusters', JSON.stringify(updatedClusters)) headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('auth:token') || '' },
setShowModal(false) body: JSON.stringify(newCluster),
setFormData({ }).then(async (res) => {
clusterName: '', if (res.ok) {
namespace: '', const data = await res.json()
controlPlane: 'Kubernetes (k8s)', console.log(data)
kubernetesVersion: 'v1.31.6 (recommended)', } else {
cpu: 1, const data = await res.json()
memory: 2048 console.log(data)
// setError(data.message || 'Login failed')
}
}).catch(() => {
//setError('Login failed')
}) })
// const updatedClusters = [...clusters, newCluster]
// setClusters(updatedClusters)
// localStorage.setItem('clusters', JSON.stringify(updatedClusters))
// setShowModal(false)
// setFormData({
// clusterName: '',
// namespace: '',
// controlPlane: 'Kubernetes (k8s)',
// kubernetesVersion: 'v1.31.6 (recommended)',
// Cpu: 1,
// Memory: 2048
// })
} }
const downloadKubeconfig = (clusterId: string) => { const downloadKubeconfig = (clusterId: string) => {
@@ -138,7 +174,7 @@ users:
to={`/app/clusters/${cluster.id}`} to={`/app/clusters/${cluster.id}`}
className="text-sm font-medium text-blue-600 hover:text-blue-900" className="text-sm font-medium text-blue-600 hover:text-blue-900"
> >
{cluster.name} {cluster.Name}
</Link> </Link>
</td> </td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{cluster.clusterId}</td> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{cluster.clusterId}</td>
@@ -202,18 +238,7 @@ users:
required required
/> />
</div> </div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Namespace
</label>
<input
type="text"
value={formData.namespace}
onChange={(e) => setFormData({...formData, namespace: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
</div> </div>
</div> </div>
@@ -256,14 +281,14 @@ users:
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
CPU (cores) Cpu (cores)
</label> </label>
<input <input
type="number" type="number"
min="1" min="1"
max="8" max="8"
value={formData.cpu} value={formData.Cpu}
onChange={(e) => setFormData({...formData, cpu: parseInt(e.target.value)})} onChange={(e) => setFormData({...formData, Cpu: parseInt(e.target.value)})}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
</div> </div>
@@ -276,8 +301,8 @@ users:
min="1024" min="1024"
max="16384" max="16384"
step="1024" step="1024"
value={formData.memory} value={formData.Memory}
onChange={(e) => setFormData({...formData, memory: parseInt(e.target.value)})} onChange={(e) => setFormData({...formData, Memory: parseInt(e.target.value)})}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
</div> </div>

View File

@@ -5,22 +5,38 @@ import Button from '../components/ui/button'
export default function Login() { export default function Login() {
const navigate = useNavigate() const navigate = useNavigate()
const [email, setEmail] = useState('') const [username, setusername] = useState('')
const [password, setPassword] = useState('') const [password, setPassword] = useState('')
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
function onSubmit(e: FormEvent) { function onSubmit(e: FormEvent) {
e.preventDefault() e.preventDefault()
setError(null) setError(null)
const usersString = localStorage.getItem('auth:users')
const users: Array<{ email: string; password: string }> = usersString ? JSON.parse(usersString) : [] fetch('http://localhost:8082/login', {
const match = users.find((u) => u.email === email && u.password === password) method: 'POST',
if (!match) { headers: { 'Content-Type': 'application/json' },
setError('Invalid credentials') body: JSON.stringify({ username, password }),
return }).then(async (res) => {
} if (res.ok) {
localStorage.setItem('auth:user', JSON.stringify({ email })) const data = await res.json()
navigate('/app') localStorage.setItem('auth:token', data.token)
navigate('/app')
} else {
const data = await res.json()
setError(data.message || 'Login failed')
}
}).catch(() => {
setError('Login failed')
})
// const usersString = localStorage.getItem('auth:users')
// const users: Array<{ username: string; password: string }> = usersString ? JSON.parse(usersString) : []
// const match = users.find((u) => u.username === username && u.password === password)
// if (!match) {
// setError('Invalid credentials')
// return
// }
// localStorage.setItem('auth:user', JSON.stringify({ username }))
} }
return ( return (
@@ -29,13 +45,13 @@ export default function Login() {
<h1 className="text-xl font-semibold mb-4">Login</h1> <h1 className="text-xl font-semibold mb-4">Login</h1>
<form className="space-y-4" onSubmit={onSubmit}> <form className="space-y-4" onSubmit={onSubmit}>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-sm font-medium" htmlFor="email">Email</label> <label className="text-sm font-medium" htmlFor="username">username</label>
<input <input
id="email" id="username"
type="email" type="username"
className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
value={email} value={username}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setusername(e.target.value)}
required required
/> />
</div> </div>

View File

@@ -6,6 +6,7 @@ import Button from '../components/ui/button'
export default function Register() { export default function Register() {
const navigate = useNavigate() const navigate = useNavigate()
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
const [username, setUsername] = useState('')
const [password, setPassword] = useState('') const [password, setPassword] = useState('')
const [confirm, setConfirm] = useState('') const [confirm, setConfirm] = useState('')
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
@@ -13,20 +14,28 @@ export default function Register() {
function onSubmit(e: FormEvent) { function onSubmit(e: FormEvent) {
e.preventDefault() e.preventDefault()
setError(null) setError(null)
if (password !== confirm) { if (password !== confirm) {
setError('Passwords do not match') setError('Passwords do not match')
return return
} }
const usersString = localStorage.getItem('auth:users')
const users: Array<{ email: string; password: string }> = usersString ? JSON.parse(usersString) : [] fetch('http://localhost:8082/register', {
if (users.some((u) => u.email === email)) { method: 'POST',
setError('Email already registered') headers: { 'Content-Type': 'application/json' },
return body: JSON.stringify({ username, email, password }),
} }).then(async (res) => {
users.push({ email, password }) if (res.ok) {
localStorage.setItem('auth:users', JSON.stringify(users)) navigate('/login')
localStorage.setItem('auth:user', JSON.stringify({ email })) } else {
navigate('/app') const data = await res.json()
// throw new Error(data.message || 'Registration failed')
setError(data.message || 'Registration failed')
}
}).catch(() => {
setError('Registration failed')
})
} }
return ( return (
@@ -45,6 +54,17 @@ export default function Register() {
required required
/> />
</div> </div>
<div className="space-y-1">
<label className="text-sm font-medium" htmlFor="username">Username</label>
<input
id="username"
type="username"
className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
</div>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-sm font-medium" htmlFor="password">Password</label> <label className="text-sm font-medium" htmlFor="password">Password</label>
<input <input