upload.controller.js
// upload.controller.js
const UploadModel = require('../models/upload.js');
const jwt = require("jsonwebtoken");
const multer = require('multer');
const path = require('path');
const AdmZip = require('adm-zip');
const fs = require('fs');
const { spawn } = require('child_process');
const SECRET_key = process.env.SECRET_key;
const upload = multer({
storage: multer.diskStorage({
destination(req, file, done) {
done(null, "../upload");
},
filename(req, file, done) {
const ext = path.extname(file.originalname);
if (ext !== '.zip') {
return done(new Error('Only zip files are allowed.'));
}
done(null, path.basename(file.originalname, ext) + ext);
},
}),
limits: {
fileSize: 10000 * 1024 * 1024, // 10GB
},
});
// 압축 해제 함수
async function extractZipFile(zipPath, outputDir) {
return new Promise((resolve, reject) => {
try {
const zip = new AdmZip(zipPath); // 압축 파일 열기
zip.extractAllTo(outputDir, true); // 지정된 디렉토리에 압축 해제
console.log(`Extraction complete. Files are extracted to ${outputDir}`);
resolve(); // 압축 해제 완료 후 resolve
} catch (error) {
reject(error); // 에러 발생 시 reject
}
});
}
// 도커 빌드 함수
async function runDocker(serverFilePath) {
const dockerComposePath = path.join(serverFilePath, 'docker-compose.yml');
return new Promise((resolve, reject) => {
const dockerCompose = spawn('docker-compose', ['-f', dockerComposePath, 'up', '--build']);
dockerCompose.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
resolve();
});
dockerCompose.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
});
}
exports.renderUploadPage = (req, res) => {
try {
const token = req.cookies.session;
const verified = jwt.verify(token, SECRET_key);
console.log(verified.class);
if (verified.class === 1){
res.render("upload");
} else {
return res.send(`<script>alert("Warning: Invalid Token"); window.location.href = '/';</script>`);
}
} catch (error) {
console.error("JWT verification failed:", error);
return res.send(`<script>alert("Warning: Invalid Token"); window.location.href = '/';</script>`);
}
};
exports.handleUpload = async (req, res) => {
const { title, text, category, flag, port, score } = req.body;
const releaseFileName = req.files.releaseFile[0].originalname;
console.log(req.body);
console.log(req.files);
try {
// JWT 인증
const token = req.cookies.session;
const verified = jwt.verify(token, SECRET_key);
const usrIdx = await UploadModel.whatUsrIdx(verified.id);
await UploadModel.uploadProblem({ usrIdx, title, text, category, flag, score, port, releaseFileName });
if (req.body.category == "Web" || req.body.category == "Pwn") {
const serverFile = req.files.serverFile[0];
// serverFile zip 파일 압축 해제
const outputDir = path.join(__dirname, '../workspace', serverFile.originalname);
await extractZipFile(serverFile.path, outputDir); //압축해제
console.log("success unzip");
// docker-compose 파일 빌드
await runDocker(outputDir);
console.log("success build");
}
res.send(`<script>alert("upload success"); window.location.href = '/wargame';</script>`);
} catch (error) {
console.error("Problem Upload Fail:", error);
return res.send(`<script>alert("Problem upload Fail. Please try again."); window.location.href = '/';</script>`);
}
};
exports.uploadMiddleware = upload.fields([
{ name: 'releaseFile', maxCount: 1 },
{ name: 'serverFile', maxCount: 1 }
]);
업로드 컨트롤러 파일 전문이다. 이 코드의 중점이 되는 handleUpload 함수의 역할은 다음과 같다.
handleUpload
- 관리자가 배포용 파일, 서버 빌드용 .zip 파일을 각각 업로드한다.
- 두 파일을 업로드 디렉터리에 업로드한다.
- 서버 빌드용 파일은 압축 해제 하여 workspace 디렉터리에 이동한다.
- docker-compose 파일을 빌드한다.
- 빌드한 url 링크를 반환한다.
기능 구현
1. 압축 해제 구현
// 압축 해제 함수
async function extractZipFile(zipPath, outputDir) {
return new Promise((resolve, reject) => {
try {
const zip = new AdmZip(zipPath); // 압축 파일 열기
zip.extractAllTo(outputDir, true); // 지정된 디렉터리에 압축 해제
console.log(`Extraction complete. Files are extracted to ${outputDir}`);
resolve(); // 압축 해제 완료 후 resolve
} catch (error) {
reject(error); // 에러 발생 시 reject
}
});
}
입력된 서버파일을 지정된 디렉터리에 압축 해제하는 함수이다.
new AdmZip 함수를 통해 .zip 파일을 다루는 인스턴스를 생성하고 , zip.extractAllTo 함수를 통해 지정된 함수로 추출한다.
2. 도커 빌드 구현
// 도커 빌드 함수
async function runDocker(serverFilePath) {
const dockerComposePath = path.join(serverFilePath, 'docker-compose.yml');
return new Promise((resolve) => {
const dockerCompose = spawn('docker-compose', ['-f', dockerComposePath, 'up', '--build']);
dockerCompose.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
resolve();
});
dockerCompose.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
});
}
압축 해제된 파일을 찾아 도커 컴포즈 파일을 빌드하는 함수이다.
spawn 함수를 사용해 도커 컴포즈 파일을 빌드하며 빌드될 시 resolve()를 통해 빠져나온다.
이러한 과정을 통해 웹 사이트에서 정상적으로 도커파일을 빌드해 해당 주소로 접근하는 것이 가능하다.
https://github.com/f10vv3r/wargame-docker/tree/main/wargame_git/wargame-gcp-master
wargame-docker/wargame_git/wargame-gcp-master at main · f10vv3r/wargame-docker
Contribute to f10vv3r/wargame-docker development by creating an account on GitHub.
github.com
이렇게 최종적으로 프로젝트를 마무리할 수 있었다.
'Dev' 카테고리의 다른 글
[JS] 자바스크립트 기본 개념 / 변수 선언 방식 (0) | 2025.03.26 |
---|---|
[JS] 자바스크립트 기본 개념 / 동기, 비동기 (2) | 2025.03.25 |
pm2 사용하여 Node.js 백그라운드 구동하기 (0) | 2025.02.27 |
docker-compose.yml 파일 생성하기 / nginx.conf 파일 정의하기 (0) | 2025.02.27 |
nginx 리버스 프록시 설정하기 (0) | 2025.02.27 |