날씨 랑 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

@@ -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