|
|
// server.js
|
|
|
|
|
|
// 1. 모듈 불러오기
|
|
|
const express = require('express');
|
|
|
const path = require('path');
|
|
|
const { spawn } = require('child_process');
|
|
|
const multer = require('multer'); // [추가] 파일 업로드 처리를 위한 multer
|
|
|
const fs = require('fs'); // [추가] 파일 시스템 접근을 위한 fs
|
|
|
|
|
|
// 2. Express 앱 생성
|
|
|
const app = express();
|
|
|
const port = 3000;
|
|
|
|
|
|
// [추가] 업로드 경로 설정
|
|
|
const uploadPath = '/mnt/user_data/applications/misc/networks/cuuva';
|
|
|
|
|
|
// [추가] 업로드 경로가 없으면 생성
|
|
|
// (실제 운영 환경에서는 권한 문제가 없는지 확인 필요)
|
|
|
fs.mkdirSync(uploadPath, { recursive: true });
|
|
|
|
|
|
// [추가] Multer 저장소 설정
|
|
|
const storage = multer.diskStorage({
|
|
|
// 파일 저장 위치 지정
|
|
|
destination: (req, file, cb) => {
|
|
|
cb(null, uploadPath);
|
|
|
},
|
|
|
// 파일 이름 지정 (원본 파일 이름 사용)
|
|
|
filename: (req, file, cb) => {
|
|
|
cb(null, file.originalname);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// [추가] Multer 파일 필터 설정 (.aiwbin 확장자만 허용)
|
|
|
const fileFilter = (req, file, cb) => {
|
|
|
if (!file.originalname.endsWith('.aiwbin')) {
|
|
|
// 허용되지 않는 파일 형식
|
|
|
return cb(new Error('Invalid file type: Only .aiwbin files are allowed'), false);
|
|
|
}
|
|
|
// 허용되는 파일 형식
|
|
|
cb(null, true);
|
|
|
};
|
|
|
|
|
|
// [추가] Multer 업로드 인스턴스 생성
|
|
|
const upload = multer({
|
|
|
storage: storage,
|
|
|
fileFilter: fileFilter
|
|
|
});
|
|
|
|
|
|
|
|
|
// 3. 'public' 폴더의 파일들을 정적 파일로 제공하도록 설정합니다.
|
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
|
|
// [추가] POST 요청의 JSON 본문(body)을 파싱하기 위한 미들웨어
|
|
|
app.use(express.json());
|
|
|
|
|
|
// 4. 루트 경로('/')로 접속하면 'index.html'(로그인 페이지)를 보냅니다.
|
|
|
app.get('/', (req, res) => {
|
|
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
|
|
});
|
|
|
|
|
|
// 5. '/dashboard' 경로로 접속하면 'dashboard.html'(메인 페이지)를 보냅니다.
|
|
|
app.get('/dashboard', (req, res) => {
|
|
|
res.sendFile(path.join(__dirname, 'public', 'dashboard.html'));
|
|
|
});
|
|
|
|
|
|
// [수정] 6. 모델 변경 API 엔드포인트
|
|
|
app.post('/set-model', (req, res) => {
|
|
|
const { model } = req.body; // app.js에서 보낸 { model: "..." }
|
|
|
|
|
|
if (!model) {
|
|
|
return res.status(400).json({ status: 'error', message: '모델 값이 없습니다.' });
|
|
|
}
|
|
|
|
|
|
console.log(`[서버] 모델 변경 명령 수신: ${model}`);
|
|
|
|
|
|
|
|
|
// --- 요청하신 파이썬 스크립트 실행 ---
|
|
|
const pythonCommand = 'python3';
|
|
|
const scriptPath = '/mnt/user_data/ctrl_cli.py';
|
|
|
|
|
|
// [수정] 인수를 "ON|OFF [선택값]" 형태의 단일 문자열로 만듭니다.
|
|
|
const combinedArg = `"ON ${model}"`;
|
|
|
|
|
|
// [수정] 수정된 인수를 배열에 담아 전달합니다.
|
|
|
const args = [scriptPath, combinedArg];
|
|
|
|
|
|
console.log(`[서버] 실행: ${pythonCommand} ${args.join(' ')}`); // 실행될 명령어 로그
|
|
|
|
|
|
const py = spawn(pythonCommand, args);
|
|
|
|
|
|
let stdoutData = '';
|
|
|
let stderrData = '';
|
|
|
|
|
|
// 파이썬 스크립트의 표준 출력
|
|
|
py.stdout.on('data', (data) => {
|
|
|
console.log(`Python stdout: ${data}`);
|
|
|
stdoutData += data.toString();
|
|
|
});
|
|
|
|
|
|
// 파이썬 스크립트의 표준 에러
|
|
|
py.stderr.on('data', (data) => {
|
|
|
console.error(`Python stderr: ${data}`);
|
|
|
stderrData += data.toString();
|
|
|
});
|
|
|
|
|
|
// 파이썬 프로세스 종료
|
|
|
py.on('close', (code) => {
|
|
|
console.log(`Python process exited with code ${code}`);
|
|
|
if (code === 0) {
|
|
|
// 성공 시 클라이언트에 응답
|
|
|
res.json({ status: 'success', message: `모델이 ${model}(으)로 변경됨`, output: stdoutData });
|
|
|
} else {
|
|
|
// 실패 시 클라이언트에 에러 응답
|
|
|
res.status(500).json({ status: 'error', message: '파이썬 스크립트 실행 실패', error: stderrData });
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 스폰 자체의 에러 (예: python3 명령어를 찾을 수 없음)
|
|
|
py.on('error', (err) => {
|
|
|
console.error('[서버] 파이썬 프로세스 시작 실패:', err);
|
|
|
res.status(500).json({ status: 'error', message: '프로세스 시작 실패', error: err.message });
|
|
|
});
|
|
|
// --- 스크립트 실행 끝 ---
|
|
|
});
|
|
|
|
|
|
// [수정됨] 7. 모델 파일 업로드 엔드포인트
|
|
|
// 'modelFile'은 app.js의 FormData.append('modelFile', ...)와 일치해야 합니다.
|
|
|
app.post('/upload-model', (req, res) => {
|
|
|
|
|
|
// upload.single() 미들웨어를 수동으로 호출하여 오류를 상세히 처리
|
|
|
upload.single('modelFile')(req, res, function (err) {
|
|
|
|
|
|
// Multer 관련 오류 처리 (예: 파일 크기 제한, 저장소 오류 등)
|
|
|
if (err instanceof multer.MulterError) {
|
|
|
console.error('[Multer Error]:', err.message);
|
|
|
return res.status(500).json({ status: 'error', message: `업로드 오류: ${err.message}` });
|
|
|
}
|
|
|
// 파일 필터에서 발생한 오류 처리 (예: .aiwbin이 아닌 경우)
|
|
|
else if (err) {
|
|
|
console.error('[File Filter Error]:', err.message);
|
|
|
return res.status(400).json({ status: 'error', message: err.message });
|
|
|
}
|
|
|
|
|
|
// 파일이 제대로 업로드되었는지 확인
|
|
|
if (!req.file) {
|
|
|
return res.status(400).json({ status: 'error', message: '업로드할 파일을 찾을 수 없습니다.' });
|
|
|
}
|
|
|
|
|
|
// [제거됨] 폼 데이터로 함께 전송된 모델 정보 (modelId, modelRole)
|
|
|
// const modelId = req.body.modelId;
|
|
|
// const modelRole = req.body.modelRole;
|
|
|
|
|
|
console.log(`[서버] 파일 업로드 성공:`);
|
|
|
// [제거됨] console.log(` - 모델 ID: ${modelId} (${modelRole})`);
|
|
|
console.log(` - 원본 파일명: ${req.file.originalname}`);
|
|
|
console.log(` - 저장 경로: ${req.file.path}`);
|
|
|
|
|
|
// 클라이언트에 성공 응답 전송
|
|
|
res.json({
|
|
|
status: 'success',
|
|
|
message: `파일 '${req.file.originalname}'이(가) 성공적으로 업로드되었습니다.`,
|
|
|
filePath: req.file.path
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
|
|
|
|
|
|
// 8. (번호 수정) 설정한 3000번 포트에서 서버가 요청을 기다리도록 실행합니다.
|
|
|
app.listen(port, () => {
|
|
|
console.log(`Server running on http://localhost:${port}`);
|
|
|
});
|
|
|
<!--2025.11.12 15:56--> |