날씨 랑 todo/긴급위젯이랑 정비일정 위젯 합치기 완료

This commit is contained in:
leeheejin
2025-10-23 15:11:10 +09:00
parent ec1669d9ca
commit aa3cd95a36
11 changed files with 3591 additions and 67 deletions

View File

@@ -11,5 +11,70 @@
"updatedAt": "2025-10-20T09:00:26.948Z",
"isUrgent": false,
"order": 3
},
{
"id": "c8292b4d-bb45-487c-aa29-55b78580b837",
"title": "오늘의 힐일",
"description": "이거 데이터베이스랑 연결하기",
"priority": "normal",
"status": "pending",
"assignedTo": "",
"dueDate": "2025-10-23T14:04",
"createdAt": "2025-10-23T05:04:50.249Z",
"updatedAt": "2025-10-23T05:04:50.249Z",
"isUrgent": false,
"order": 4
},
{
"id": "2c7f90a3-947c-4693-8525-7a2a707172c0",
"title": "테스트용 일정",
"description": "ㅁㄴㅇㄹ",
"priority": "low",
"status": "pending",
"assignedTo": "",
"dueDate": "2025-10-16T18:16",
"createdAt": "2025-10-23T05:13:14.076Z",
"updatedAt": "2025-10-23T05:13:14.076Z",
"isUrgent": false,
"order": 5
},
{
"id": "499feff6-92c7-45a9-91fa-ca727edf90f2",
"title": "ㅁSdf",
"description": "asdfsdfs",
"priority": "normal",
"status": "pending",
"assignedTo": "",
"dueDate": "",
"createdAt": "2025-10-23T05:15:38.430Z",
"updatedAt": "2025-10-23T05:15:38.430Z",
"isUrgent": false,
"order": 6
},
{
"id": "166c3910-9908-457f-8c72-8d0183f12e2f",
"title": "ㅎㄹㅇㄴ",
"description": "ㅎㄹㅇㄴ",
"priority": "normal",
"status": "pending",
"assignedTo": "",
"dueDate": "",
"createdAt": "2025-10-23T05:21:01.515Z",
"updatedAt": "2025-10-23T05:21:01.515Z",
"isUrgent": false,
"order": 7
},
{
"id": "bfa9d476-bb98-41d5-9d74-b016be011bba",
"title": "ㅁㄴㅇㄹㅁㄴㅇㄹㅁㄴㅇㄹ",
"description": "ㅁㄴㅇㄹㄴㅇㄹ",
"priority": "normal",
"status": "pending",
"assignedTo": "",
"dueDate": "",
"createdAt": "2025-10-23T05:21:25.781Z",
"updatedAt": "2025-10-23T05:21:25.781Z",
"isUrgent": false,
"order": 8
}
]

View File

@@ -441,7 +441,7 @@ export class DashboardController {
}
/**
* 쿼리 실행
* 쿼리 실행 (SELECT만)
* POST /api/dashboards/execute-query
*/
async executeQuery(req: AuthenticatedRequest, res: Response): Promise<void> {
@@ -506,6 +506,79 @@ export class DashboardController {
}
}
/**
* DML 쿼리 실행 (INSERT, UPDATE, DELETE)
* POST /api/dashboards/execute-dml
*/
async executeDML(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const { query } = req.body;
// 유효성 검증
if (!query || typeof query !== "string" || query.trim().length === 0) {
res.status(400).json({
success: false,
message: "쿼리가 필요합니다.",
});
return;
}
// SQL 인젝션 방지를 위한 기본적인 검증
const trimmedQuery = query.trim().toLowerCase();
const allowedCommands = ["insert", "update", "delete"];
const isAllowed = allowedCommands.some((cmd) =>
trimmedQuery.startsWith(cmd)
);
if (!isAllowed) {
res.status(400).json({
success: false,
message: "INSERT, UPDATE, DELETE 쿼리만 허용됩니다.",
});
return;
}
// 위험한 명령어 차단
const dangerousPatterns = [
/drop\s+table/i,
/drop\s+database/i,
/truncate/i,
/alter\s+table/i,
/create\s+table/i,
];
if (dangerousPatterns.some((pattern) => pattern.test(query))) {
res.status(403).json({
success: false,
message: "허용되지 않는 쿼리입니다.",
});
return;
}
// 쿼리 실행
const result = await PostgreSQLService.query(query.trim());
res.status(200).json({
success: true,
data: {
rowCount: result.rowCount || 0,
command: result.command,
},
message: "쿼리가 성공적으로 실행되었습니다.",
});
} catch (error) {
console.error("DML execution error:", error);
res.status(500).json({
success: false,
message: "쿼리 실행 중 오류가 발생했습니다.",
error:
process.env.NODE_ENV === "development"
? (error as Error).message
: "쿼리 실행 오류",
});
}
}
/**
* 외부 API 프록시 (CORS 우회용)
* POST /api/dashboards/fetch-external-api

View File

@@ -24,12 +24,18 @@ router.get(
dashboardController.getDashboard.bind(dashboardController)
);
// 쿼리 실행 (인증 불필요 - 개발용)
// 쿼리 실행 (SELECT만, 인증 불필요 - 개발용)
router.post(
"/execute-query",
dashboardController.executeQuery.bind(dashboardController)
);
// DML 쿼리 실행 (INSERT/UPDATE/DELETE, 인증 불필요 - 개발용)
router.post(
"/execute-dml",
dashboardController.executeDML.bind(dashboardController)
);
// 외부 API 프록시 (CORS 우회)
router.post(
"/fetch-external-api",