node.js웹사이트 만들기

웹개발

프린_ 2023. 10. 23. 19:15
728x90
const express = require('express');
const app = express();
const port = 3030;

const mysql = require('mysql');
const session = require('express-session');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const crypto = require('crypto');
const moment = require('moment-timezone');
const winston = require('winston');
const bodyParser = require('body-parser');
const SitemapGenerator = require('sitemap-generator');
const multer = require('multer');
require('dotenv').config();
const bcrypt = require('bcrypt');
const path = require('path');
const fs = require('fs');

// MySQL 연결 설정
const mysqldb = mysql.createConnection({
    host: process.env.MYSQL_HOST,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    database: process.env.MYSQL_DATABASE,
    charset: 'utf8'
});

mysqldb.connect((err) => {
    if (err) {
        console.error('MySQL 연결 오류:', err);
        throw err;
    }
    console.log('MySQL 연결 성공');
});

// 로그 설정
const logOptions = {
    level: 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.printf(({ timestamp, level, message }) => {
            return `${timestamp} [${level}] ${message}`;
        })
    ),
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'app.log' })
    ]
};

const logger = winston.createLogger(logOptions);

console.log = (...args) => {
    logger.info(args.join(' '));
};

// Express 설정
app.use(session({
    secret: crypto.randomBytes(32).toString('hex'),
    resave: false,
    saveUninitialized: false,
}));

app.use(passport.initialize());
app.use(passport.session());

app.set('view engine', 'ejs');
app.use('/public', express.static('public/css'));
app.use('/yu', express.static('views/public/css'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use((req, res, next) => {
    const clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    const logMessage = `사용자가 웹 사이트에 접속함 - IP 주소: ${clientIp}`;
    logger.info(logMessage);
    createAndStoreLog(logMessage, clientIp);
    next();
});


function createAndStoreLog(message, clientIPAddress) {
    const currentTime = moment().tz('Asia/Seoul').format('YYYY-MM-DD HH:mm:ss');
    const logEntry = { timestamp: currentTime, message: message, ip_address: clientIPAddress };
    mysqldb.query('INSERT INTO logs SET ?', logEntry, (err, results) => {
        if (err) {
            console.error('로그 저장 중 오류 발생:', err);
        } else {
            console.log('로그가 성공적으로 저장되었습니다.');
        }
    });
}

const generator = SitemapGenerator('https://fg-uonqq.run.goorm.site', {
    stripQuerystring: false, // 쿼리 문자열을 포함할지 여부
});

generator.on('done', () => {
    console.log('사이트 맵 생성이 완료되었습니다.');
});

const generateSitemapInterval = 24 * 60 * 60 * 1000; // 24시간(하루)마다

const generateSitemap = () => {
    generator.start();
};

setInterval(generateSitemap, generateSitemapInterval);

app.get('/sitemap.xml', (req, res) => {
    res.sendFile(__dirname + '/sitemap.xml');
});

app.get('/', (req, res) => {
    if (req.session.user) {
        if (req.session.role === 'admin') {
            
			res.render('index_a.ejs');
            console.log('관리자 계정으로 로그인 후 홈');
        } else {
            res.render('index_u.ejs');
            console.log('일반 사용자 계정으로 홈에 접속');
        }
    } else {
        res.render('index.ejs');
        console.log('로그인 안한 상태로 접속');
    }
});

app.get('/server/error', (req, res) => {
    res.render('server_error.ejs');
});

app.get('/shop', (req, res) => {
    const products = [
        { name: '상품1', price: 10000 },
        { name: '상품2', price: 20000 },
        { name: '상품3', price: 30000 }
    ];

    res.render('shop', { products });
});

// 회원가입 페이지 렌더링
app.get('/join/membership', (req, res) => {
    res.sendFile(__dirname + '/public/html/u.html');
});
app.get('/r', (req, res) => {
    res.sendFile(__dirname + '/public/html/r.txt');
});


// 회원가입 POST 요청 처리
app.post('/join/membership', (req, res) => {
    // POST 요청으로부터 입력 데이터 추출
    const { email, phoneNumber, address, name, id, password } = req.body;

    // 빈 칸 검사
    if (!email || !phoneNumber || !address || !name || !id || !password) {
        return res.status(400).send('필수 입력 정보를 모두 제공해야 합니다.');
    }

    // 중복 정보 검사 (예: ID가 중복되지 않는지 확인)
    mysqldb.query('SELECT * FROM users WHERE id = ?', [id], (err, results) => {
        if (err) {
            console.error('중복 정보 검사 중 오류 발생:', err);
            return res.status(500).send('중복 정보 검사 중 오류 발생');
        }
        if (results.length > 0) {
            return res.status(400).send('이미 사용 중인 ID입니다.');
        }

        // 암호 해시 생성
        bcrypt.hash(password, 10, (err, hash) => {
            if (err) {
                console.error('암호 해시 생성 중 오류 발생:', err);
                return res.status(500).send('암호 해시 생성 중 오류 발생');
            }

            // 새 사용자 생성 및 데이터베이스에 저장
            const newUser = {
                email,
                phoneNumber,
                address,
                name,
                id,
                password: hash,
                role: 'user',
            };

            mysqldb.query('INSERT INTO users SET ?', newUser, (err) => {
                if (err) {
                    console.error('회원가입 중 오류 발생:', err);
                    return res.status(500).send('회원가입 중 오류 발생');
                }

                // 가입 완료 페이지로 이동
                res.redirect('/Membership/registration/completed');
            });
        });
    });
});

app.get('/Membership/registration/completed', (req, res) => {
    res.send('회원가입이 완료되었습니다.');
});

// getLogsFromDatabase 함수 수정
function getLogsFromDatabase(db, callback) {
    db.query('SELECT * FROM logs', (err, results) => {
        if (err) {
            callback(err, null);
        } else {
            callback(null, results);
        }
    });
}

// '/logs' 경로에 대한 라우트 핸들러
app.get('/logs', (req, res) => {
    if (req.session.user && req.session.role === 'admin') {
        // 'admin' 권한이 있는 사용자만 액세스 가능

        // 여기에서 데이터베이스에서 로그 목록을 가져오는 코드를 추가
        mysqldb.query('SELECT * FROM logs', (err, results) => {
            if (err) {
                console.error('로그 목록을 가져오는 중 오류:', err);
                res.status(500).send('로그 목록을 가져오는 중 오류 발생');
            } else {
                const logs = results; // 로그 데이터를 변수에 저장

                // 로그 데이터를 'logs.ejs' 페이지에 전달
                res.render('logs.ejs', { logs });
            }
        });
    } else {
        // 'admin' 권한이 아닌 경우 접근 불가능
        res.status(403).send('접근이 거부되었습니다.');
    }
});

app.get('/googleb1672fc63397d758', (req, res) => {
    res.sendFile(__dirname + "/public/html/googleb1672fc63397d758.html");
});
app.get('/robots.txt', (req, res) => {
    res.sendFile(__dirname + "/robots.txt");
});

// 로그인 페이지 렌더링
app.get('/login', (req, res) => {
    res.render('login.ejs');
});

app.post('/login', (req, res) => {
    const { id, password } = req.body;

    // 데이터베이스에서 사용자 확인
    mysqldb.query('SELECT * FROM users WHERE id = ?', [id], (err, results) => {
        if (err) {
            console.error('로그인 중 오류 발생:', err);
            return res.status(500).send('로그인 중 오류 발생');
        }

        if (results.length === 1) {
            const user = results[0];

            // 비밀번호 비교
            bcrypt.compare(password, user.password, (bcryptErr, result) => {
                if (bcryptErr) {
                    console.error('비밀번호 확인 중 오류 발생:', bcryptErr);
                    return res.status(500).send('비밀번호 확인 중 오류 발생');
                }

                if (result) {
                    // 비밀번호가 일치하는 경우, 로그인 성공
                    // 사용자 이름 가져오기
                    const sqlGetName = 'SELECT name FROM users WHERE id = ?';
                    mysqldb.query(sqlGetName, [id], (nameErr, nameResults) => {
                        if (nameErr) {
                            console.error('사용자 이름 가져오기 오류:', nameErr);
                            return res.status(500).send('사용자 이름 가져오기 오류');
                        }

                        if (nameResults.length === 1) {
                            const userName = nameResults[0].name;

                            if (user.role === 'admin') {
                                // 관리자 계정으로 로그인 성공
                                req.session.user = user; // 세션에 사용자 정보 저장
                                req.session.role = 'admin'; // 세션에 권한 저장
                                req.session.userName = userName; // 사용자 이름을 세션에 저장
                                console.log('관리자 계정 로그인 성공: ' + user.id);
                                res.redirect('/');
                            } else if (user.role === 'user') {
                                // 일반 사용자 계정으로 로그인 성공
                                req.session.user = user; // 세션에 사용자 정보 저장
                                req.session.role = 'user'; // 세션에 권한 저장
                                req.session.userName = userName; // 사용자 이름을 세션에 저장
                                console.log('일반 사용자 계정 로그인 성공: ' + user.id);
                                res.redirect('/');
                            } else {
                                // 권한이 없는 경우
                                console.log('로그인 실패: 권한이 없는 사용자입니다.');
                                res.render('login.ejs', { error: '권한이 없는 사용자입니다.' });
                            }
                        } else {
                            // 사용자 이름을 찾을 수 없음
                            console.log('사용자 이름을 찾을 수 없습니다.');
                            res.status(500).send('사용자 이름을 찾을 수 없습니다.');
                        }
                    });
                } else {
                    // 비밀번호가 일치하지 않는 경우, 로그인 실패
                    console.log('로그인 실패: 비밀번호가 일치하지 않습니다.');
                    res.render('login.ejs', { error: '비밀번호가 일치하지 않습니다.' });
                }
            });
        } else {
            // 아이디가 존재하지 않음
            console.log('로그인 실패: 아이디가 존재하지 않습니다.');
            res.render('login.ejs', { error: '아이디가 존재하지 않습니다.' });
        }
    });
});

app.get('/members', (req, res) => {
    // 권한 확인: 사용자의 권한이 'admin'인 경우에만 액세스 허용
    if (req.session.user && req.session.role === 'admin') {
        const sql = 'SELECT id, password, role FROM users';

        mysqldb.query(sql, (err, results) => {
            if (err) {
                console.error('회원 정보 가져오기 오류:', err);
                return res.status(500).send('오류 발생');
            }

            // EJS 템플릿 렌더링하여 회원 정보 및 권한 정보 표시
            res.render('members.ejs', { members: results });
        });
    } else {
        // 'admin' 권한이 아닌 경우 접근 거부
        res.status(403).send('액세스가 거부되었습니다.');
    }
});

app.get('/admin-dashboard', (req, res) => {
    if (req.session.user && req.session.role === 'admin') {
        // 'admin' 권한이 있는 사용자만 액세스 가능
        res.render('admin-dashboard.ejs', { userName: req.session.userName });
    } else {
        // 권한이 없는 경우 또는 로그인하지 않은 경우
        res.redirect('/login');
    }
});

app.get('/dashboard', (req, res) => {
    if (req.session.user && req.session.role === 'user') {
        // 'user' 권한이 있는 사용자만 액세스 가능
        console.log(req.session.userName)
        res.render('dashboard.ejs', { userName: req.session.userName });
    } else {
        // 권한이 없는 경우 또는 로그인하지 않은 경우
        res.redirect('/login');
    }
});

app.use('/favicon.ico', express.static('public/favicon.ico'));

app.get('/detail/:id', (req, res) => {
    const postId = req.params.id;
    const currentUser = req.session.user; // 현재 로그인한 사용자 정보

    // 글 조회 쿼리를 수행 (post 테이블에서 가져오도록 수정)
    mysqldb.query('SELECT * FROM post WHERE id = ?', [postId], (err, results) => {
        if (err) {
            console.error('데이터베이스에서 글 조회 중 오류:', err);
            return res.status(500).send('오류가 발생했습니다.');
        }

        if (results.length === 0) {
            return res.status(404).send('해당 글을 찾을 수 없습니다.');
        }

        // 글을 조회한 결과를 가져오고 현재 사용자와 글 작성자를 비교
        const post = results[0];

        // 사용자 역할을 확인하여 관리자인 경우 항상 삭제할 수 있도록 설정
        // 또는 글 작성자와 현재 사용자가 동일한 경우에만 삭제할 수 있도록 설정
        let canDelete = false;

        if (currentUser) {
            // 현재 사용자가 로그인한 경우에만
            if (currentUser.id === post.user_id || currentUser.role === 'admin') {
                canDelete = true;
            }
        }

        // 파일 URL을 파일 이름을 사용하여 생성
        const filePath = `/f/${post.file_path}`;

        // 파일 유형에 따라 이미지와 비디오를 구별
        const isImage = filePath.match(/\.(jpg|jpeg|png|gif)$/i);
        const isVideo = filePath.match(/\.(mp4|avi|mkv)$/i);

        res.render('detail.ejs', { post, canDelete, filePath, isImage, isVideo });
    });
});

app.post('/delete/:id', (req, res) => {
    const postId = req.params.id;

    if (req.session.user) {
        // 글 삭제 권한 확인: 관리자 또는 글 작성자만 삭제할 수 있음
        if (req.session.user.role === 'admin') {
            // 관리자 권한을 가진 사용자는 언제나 글을 삭제할 수 있음
            mysqldb.query('DELETE FROM post WHERE id = ?', [postId], (err) => {
                if (err) {
                    console.error('글 삭제 중 오류:', err);
                    return res.status(500).send('글 삭제 중 오류 발생');
                }

                console.log(`글(ID: ${postId})이 삭제되었습니다.`);
                res.redirect('/');
            });
        } else {
            // 글 작성자 확인: 로그인한 사용자와 글 작성자가 동일한지 확인
            mysqldb.query('SELECT user_id FROM post WHERE id = ?', [postId], (userErr, userResults) => {
                if (userErr) {
                    console.error('글 작성자 확인 중 오류 발생:', userErr);
                    return res.status(500).send('글 작성자 확인 중 오류 발생');
                }

                if (userResults && userResults.length > 0) {
                    const postUserId = userResults[0].user_id;
                    if (postUserId === req.session.user.id) {
                        // 글 작성자와 로그인한 사용자가 동일한 경우 글을 삭제할 수 있음
                        mysqldb.query('DELETE FROM post WHERE id = ?', [postId], (deleteErr) => {
                            if (deleteErr) {
                                console.error('글 삭제 중 오류:', deleteErr);
                                return res.status(500).send('글 삭제 중 오류 발생');
                            }

                            console.log(`글(ID: ${postId})이 삭제되었습니다.`);
                            res.redirect('/');
                        });
                    } else {
                        // 글 작성자와 로그인한 사용자가 동일하지 않은 경우 접근 거부
                        console.log('글 삭제 권한 없음: 글 작성자와 로그인한 사용자가 다름');
                        res.status(403).send('글 삭제 권한이 없습니다.');
                    }
                } else {
                    // 글 작성자를 찾을 수 없음
                    console.error('글 작성자를 찾을 수 없음.');
                    res.status(500).send('글 작성자를 찾을 수 없음');
                }
            });
        }
    } else {
        // 로그인하지 않은 경우 글 삭제 권한이 없음
        console.log('글 삭제 권한 없음: 로그인하지 않음');
        res.status(403).send('글 삭제 권한이 없습니다.');
    }
});

// 게시글 작성 페이지 렌더링
app.get('/write', (req, res) => {
    res.render('write.ejs');
});
app.get('/1', (req, res) => {
    res.render('1.ejs');
});


app.get('/yt', (req, res) => {
    res.render('yt.ejs');
});

// 로그아웃
app.get('/logout', (req, res) => {
    req.session.destroy((err) => {
        if (err) {
            console.error('로그아웃 중 오류 발생:', err);
            return res.status(500).send('로그아웃 중 오류 발생');
        }
        res.redirect('/');
    });
});
app.get('/list', (req, res) => {
    // 데이터베이스에서 "post" 테이블의 글 목록을 가져오는 쿼리
    mysqldb.query('SELECT id, title, content FROM post', (err, results) => {
        if (err) {
            console.error('데이터베이스에서 글 목록을 가져오는 중 오류 발생:', err);
            return res.status(500).send('서버 오류');
        }

        // 글 목록을 EJS 템플릿으로 렌더링하여 클라이언트에 전달
        res.render('list.ejs', { 글목록: results });
    });
});
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/'); // 파일 저장 경로 (uploads 디렉토리에 저장)
  },
  filename: (req, file, cb) => {
    const extname = path.extname(file.originalname);
    cb(null, Date.now() + extname); // 파일 이름을 고유하게 설정
  },
});

const upload = multer({ storage });

app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

app.post('/write', upload.single('file'), (req, res) => {
    // 로그인하지 않은 경우 글 작성 권한이 없음
    if (!req.session.user) {
        console.log('글 작성 권한 없음: 로그인하지 않음');
        return res.status(403).send('글 작성 권한이 없습니다.');
    }

    // POST 요청으로부터 입력 데이터 추출
    const { title, content } = req.body;
    const user_id = req.session.user.id;

    // 빈 칸 검사
    if (!title || !content) {
        console.log('필수 정보를 모두 입력해야 함');
        return res.status(400).send('필수 정보를 모두 입력해야 합니다.');
    }

    // 파일을 서버에 저장하고 제목과 내용을 데이터베이스에 저장
    savePostToDatabase(req, title, content, user_id, req.file)
        .then((result) => {
            console.log('새로운 글(ID: ' + result.insertId + ')이 작성되었습니다.');
            res.redirect('/');
        })
        .catch((err) => {
            console.error('글 작성 중 오류 발생:', err);
            return res.status(500).send('글 작성 중 오류 발생');
        });
});

// 데이터베이스에 제목, 내용 및 파일 저장하는 함수
function savePostToDatabase(req, title, content, user_id, file) {
    return new Promise((resolve, reject) => {
        const query = 'INSERT INTO post (title, content, user_id, file_path) VALUES (?, ?, ?, ?)';
        const filePath = file ? 'uploads/' + file.filename : null;
        mysqldb.query(query, [title, content, user_id, filePath], (err, result) => {
            if (err) {
                reject(err);
            } else {
                resolve(result); // 새로 생성된 게시물 정보 반환
            }
        });
    });
}

app.get('/f/uploads/:filename', (req, res) => {
    const fileName = req.params.filename;
    const filePath = path.join(__dirname, 'uploads', fileName);

    // 파일 확장자에 따라 Content-Type 설정
    if (fileName.endsWith('.jpg') || fileName.endsWith('.jpeg')) {
        res.setHeader('Content-Type', 'image/jpeg');
    } else if (fileName.endsWith('.png')) {
        res.setHeader('Content-Type', 'image/png');
    } else if (fileName.endsWith('.gif')) {
        res.setHeader('Content-Type', 'image/gif');
    } else if (fileName.endsWith('.mp4')) {
        res.setHeader('Content-Type', 'video/mp4');
    } else if (fileName.endsWith('.avi')) {
        res.setHeader('Content-Type', 'video/x-msvideo');
    } else if (fileName.endsWith('.mkv')) {
        res.setHeader('Content-Type', 'video/x-matroska');
    } else {
        return res.status(403).send('이미지 또는 동영상 파일이 아닙니다.');
    }

    // 파일을 클라이언트로 전송
    fs.createReadStream(filePath).pipe(res);
});

// Express 서버 실행
app.listen(port, () => {
    console.log(`서버가 http://localhost:${port} 에서 실행 중입니다.`);
});

권한 은 두개 이다 유저 또는 어드민 기본가입시에은 유저로가입되지만 mysql에 접속에서 명령어로 수동으로 어드민 권한으로 변경하고 로그가 db에저장되은 저장내용은 날짜시간 로그내용 접속ip 이다

회원가입시이제은 암호가 암호화되서 db에 저장되고 로그인할때 db에서 가져오고 암호화을 헤제시키은 코드가있다

728x90
반응형

'node.js웹사이트 만들기' 카테고리의 다른 글

node.js 만든 사이트 가비아 호스팅  (0) 2024.05.21
node.js 백엔드 구축 3강  (1) 2023.10.24
node.js 사이트 만들기  (1) 2023.10.11