You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
6.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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';
// [수정] 인수를 " [선택값]" 형태의 단일 문자열로 만듭니다.
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}`);
});