This commit is contained in:
@ -1,53 +1,40 @@
|
||||
// ✅ EditForm.js
|
||||
import React from 'react';
|
||||
|
||||
function EditForm({ form, onChange, onSubmit }) {
|
||||
return (
|
||||
<form onSubmit={onSubmit} className="bg-white p-8 rounded shadow-md w-full max-w-md mx-auto">
|
||||
<h2 className="text-2xl font-bold mb-6 text-center text-[#3B82F6]">회원정보 수정</h2>
|
||||
<form onSubmit={onSubmit} className="space-y-4">
|
||||
<input
|
||||
name="name"
|
||||
value={form.name}
|
||||
onChange={onChange}
|
||||
className="w-full px-4 py-2 border rounded"
|
||||
placeholder="이름"
|
||||
/>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">이름</label>
|
||||
<input
|
||||
name="name"
|
||||
value={form.name}
|
||||
onChange={onChange}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded"
|
||||
placeholder="이름을 입력하세요"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
name="email"
|
||||
type="email"
|
||||
value={form.email}
|
||||
onChange={onChange}
|
||||
className="w-full px-4 py-2 border rounded"
|
||||
placeholder="이메일"
|
||||
/>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">이메일</label>
|
||||
<input
|
||||
name="email"
|
||||
type="email"
|
||||
value={form.email}
|
||||
onChange={onChange}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded"
|
||||
placeholder="이메일을 입력하세요"
|
||||
/>
|
||||
</div>
|
||||
<textarea
|
||||
name="desc"
|
||||
value={form.desc}
|
||||
onChange={onChange}
|
||||
className="w-full px-4 py-2 border rounded"
|
||||
placeholder="자기소개"
|
||||
rows={4}
|
||||
/>
|
||||
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">설명</label>
|
||||
<textarea
|
||||
name="desc"
|
||||
value={form.desc}
|
||||
onChange={onChange}
|
||||
rows="4"
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded"
|
||||
placeholder="자기소개 또는 간단한 설명을 입력하세요"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-[#3B82F6] text-white py-2 rounded hover:bg-blue-700 transition"
|
||||
>
|
||||
수정
|
||||
<button type="submit" className="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">
|
||||
저장
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditForm;
|
||||
export default EditForm;
|
79
src/pages/Profile/SSHKeyForm.js
Normal file
79
src/pages/Profile/SSHKeyForm.js
Normal file
@ -0,0 +1,79 @@
|
||||
// ✅ SSHKeyForm.js
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import authApi from '../../api/authApi';
|
||||
|
||||
function SSHKeyForm({ form, onChange, onSubmit }) {
|
||||
const [keyInfo, setKeyInfo] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchKeyInfo = async () => {
|
||||
try {
|
||||
const res = await authApi.get('/api/auth/ssh-key/info/');
|
||||
setKeyInfo(res.data);
|
||||
} catch (err) {
|
||||
console.error("SSH 키 정보 조회 실패", err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchKeyInfo();
|
||||
}, []);
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (!window.confirm("등록된 SSH 키를 삭제하시겠습니까?")) return;
|
||||
|
||||
try {
|
||||
await authApi.delete('/api/auth/ssh-key/');
|
||||
alert("SSH 키가 삭제되었습니다.");
|
||||
setKeyInfo(null);
|
||||
} catch (err) {
|
||||
console.error("삭제 실패", err);
|
||||
alert("SSH 키 삭제에 실패했습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit} className="space-y-4">
|
||||
{keyInfo?.has_key && (
|
||||
<div className="p-4 bg-gray-100 rounded border border-gray-300">
|
||||
<p className="text-sm text-gray-700 mb-1">
|
||||
<strong>등록된 키 이름:</strong> {keyInfo.encrypted_private_key_name || '이름 없음'}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
<strong>마지막 사용 시각:</strong>{' '}
|
||||
{keyInfo.last_used_at ? new Date(keyInfo.last_used_at).toLocaleString() : '없음'}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDelete}
|
||||
className="mt-2 px-4 py-1 text-sm bg-red-500 text-white rounded hover:bg-red-600"
|
||||
>
|
||||
SSH 키 삭제
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<input
|
||||
name="encrypted_private_key_name"
|
||||
value={form.encrypted_private_key_name || ''}
|
||||
onChange={onChange}
|
||||
className="w-full px-4 py-2 border rounded"
|
||||
placeholder="SSH 키 이름"
|
||||
/>
|
||||
|
||||
<textarea
|
||||
name="private_key"
|
||||
value={form.private_key || ''}
|
||||
onChange={onChange}
|
||||
className="w-full px-4 py-2 border rounded font-mono"
|
||||
placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
|
||||
rows={6}
|
||||
/>
|
||||
|
||||
<button type="submit" className="w-full bg-green-600 text-white py-2 rounded hover:bg-green-700">
|
||||
키 등록
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default SSHKeyForm;
|
@ -1,15 +1,18 @@
|
||||
// ✅ index.js
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import EditForm from './EditForm';
|
||||
import authApi from '../../api/authApi';
|
||||
import EditForm from './EditForm';
|
||||
import SSHKeyForm from './SSHKeyForm';
|
||||
|
||||
function ProfilePage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
desc: '' // ✅ desc 상태 추가
|
||||
desc: '',
|
||||
encrypted_private_key_name: '',
|
||||
private_key: ''
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@ -22,8 +25,8 @@ function ProfilePage() {
|
||||
const fetchUser = async () => {
|
||||
try {
|
||||
const res = await authApi.get('/api/auth/me/');
|
||||
const { name, email, desc } = res.data;
|
||||
setForm({ name, email, desc }); // ✅ desc 상태 반영
|
||||
const { name, email, desc, encrypted_private_key_name } = res.data;
|
||||
setForm((prev) => ({ ...prev, name, email, desc, encrypted_private_key_name }));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
navigate('/login');
|
||||
@ -34,13 +37,19 @@ function ProfilePage() {
|
||||
}, [navigate]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
setForm({ ...form, [e.target.name]: e.target.value });
|
||||
const { name, value } = e.target;
|
||||
setForm((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
const handleProfileSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await authApi.put('/api/auth/me/', form); // ✅ desc 포함해서 PUT
|
||||
await authApi.put('/api/auth/me/', {
|
||||
name: form.name,
|
||||
email: form.email,
|
||||
desc: form.desc,
|
||||
encrypted_private_key_name: form.encrypted_private_key_name
|
||||
});
|
||||
alert('회원정보가 수정되었습니다.');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
@ -48,11 +57,43 @@ function ProfilePage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeySubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!form.private_key) {
|
||||
alert('키 내용을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await authApi.post('/api/auth/ssh-key/', {
|
||||
private_key: form.private_key,
|
||||
key_name: form.encrypted_private_key_name
|
||||
});
|
||||
alert('SSH 키가 등록되었습니다.');
|
||||
setForm((prev) => ({ ...prev, private_key: '' }));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('SSH 키 등록 실패');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mt-5">
|
||||
<EditForm form={form} onChange={handleChange} onSubmit={handleSubmit} />
|
||||
<div className="min-h-screen bg-gray-100 py-16 px-4 flex justify-center items-start">
|
||||
<div className="w-full max-w-5xl bg-white shadow-lg rounded-xl p-8 space-y-12">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-center text-blue-700 mb-6">👤 회원정보 수정</h2>
|
||||
<EditForm form={form} onChange={handleChange} onSubmit={handleProfileSubmit} />
|
||||
</div>
|
||||
|
||||
<hr className="border-gray-300" />
|
||||
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-center text-green-700 mb-6">🔐 SSH 키 등록</h2>
|
||||
<SSHKeyForm form={form} onChange={handleChange} onSubmit={handleKeySubmit} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProfilePage;
|
||||
export default ProfilePage;
|
Reference in New Issue
Block a user