Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,61 @@ var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");

var cors = require("cors");
/* --------------------------------------- */
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var balanceRouter = require("./routes/real");

var authRouter = require("./routes/auth");
/* --------------------------------------- */
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const jwt = require("jsonwebtoken");
const SECRET_KEY = "MyJWT";
/* --------------------------------------- */
dotenv.config();
pw = process.env.PW;
const DB_URL = `mongodb+srv://pius0316:${pw}@upanddown.n3ptkyf.mongodb.net/?retryWrites=true&w=majority&appName=UpAndDown`;
mongoose
.connect(DB_URL, {
retryWrites: true,
w: "majority",
appName: "express-mongodb",
})
.then(() => {
console.log("Connected Successful");
})
.catch((err) => {
console.log(err);
});
/* --------------------------------------- */
var app = express();
const cors = require("cors");

/* CORS */
const allowedOrigins = ["http://localhost:3000"];

app.use(
cors({
origin: allowedOrigins,
origin: ["http://localhost:3000"], // TODO: 클라이언트 주소 배포하면 추가해주기
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
credentials: true,
})
);

/* --------------------------------------- */
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

/* --------------------------------------- */
app.use("/", indexRouter);
app.use("/auth", authRouter);
app.use("/users", usersRouter);
var balanceRouter = require("./routes/real");
app.use("/api/real", balanceRouter);
/* --------------------------------------- */
const port = process.env.PORT || 3001;

// 서버 시작
app.listen(port, () => {
console.log(`▶️ Server is listening on http://localhost:${port}`);
});

// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
Expand All @@ -48,10 +74,4 @@ app.use(function (err, req, res, next) {
res.render("error");
});

const PORT = process.env.PORT || 3001;

app.listen(PORT, () => {
console.log(`🚀 서버가 실행 중: http://localhost:${PORT}`);
});

module.exports = app;
86 changes: 86 additions & 0 deletions bin/www
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env node

/**
* Module dependencies.
*/

var app = require("../app");
var debug = require("debug")("neukkim-server:server");
var http = require("http");

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || "3001");
app.set("port", port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on("error", onError);
server.on("listening", onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}

return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
if (error.syscall !== "listen") {
throw error;
}

var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case "EACCES":
console.error(bind + " requires elevated privileges");
process.exit(1);
break;
case "EADDRINUSE":
console.error(bind + " is already in use");
process.exit(1);
break;
default:
throw error;
}
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
debug("Listening on " + bind);
}
32 changes: 32 additions & 0 deletions middleware/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { verifyToken } = require("../utils/auth");

function authenticate(req, res, next) {
try {
let token;
if (req.headers.authorization) {
token = req.headers.authorization.split(" ")[1];
} else if (req.cookies.authToken) {
token = req.cookies.authToken;
}
if (token) {
console.log("서버가 받은 토큰 (앞 30자):", token.slice(0, 30) + "...");
} else {
console.log("서버가 받은 토큰: 없음");
}

const userPayload = verifyToken(token);

if (!userPayload) {
throw new Error("유효하지 않거나 존재하지 않는 토큰입니다.");
}

req.user = userPayload;
next();
} catch (error) {
const authError = new Error("Authorization Failed: 인증에 실패했습니다.");
authError.status = 401;
next(authError);
}
}

module.exports = { authenticate };
94 changes: 94 additions & 0 deletions models/Auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const mongoose = require("mongoose");
const { isEmail } = require("validator");
const bcrypt = require("bcrypt");

const authSchema = new mongoose.Schema({
email: {
type: String,
required: [true, "이메일을 입력해 주세요."],
unique: true,
lowercase: true,
validate: [isEmail, "올바른 이메일 형식이 아닙니다."],
},
password: {
type: String,
required: [true, "비밀번호를 입력해 주세요."],
},
nickname: {
type: String,
required: [true, "닉네임을 입력해 주세요."],
},
});

// 로그인
authSchema.statics.login = async function (email, password) {
const auth = await this.findOne({ email });
if (!auth) {
// 이메일이 없을 때
const error = new Error("이메일을 다시 확인해 주세요.");
error.field = "email";
throw error;
}

const isMatch = await bcrypt.compare(password, auth.password);
if (!isMatch) {
// 비밀번호가 틀렸을 때
const error = new Error("비밀번호를 다시 확인해 주세요.");
error.field = "password";
throw error;
}

// 성공 시
return auth.visibleUser;
};

// 노출할 필드만 반환하는 가상 프로퍼티
authSchema.virtual("visibleUser").get(function () {
return {
_id: this._id,
email: this.email,
nickname: this.nickname,
};
});

// 회원가입
authSchema.statics.signUp = async function (email, password, nickname) {
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(password, salt);

try {
const auth = await this.create({
email,
password: hashedPassword,
nickname,
});
return {
_id: auth._id,
nickname: auth.nickname,
};
} catch (err) {
// 중복된 이메일 처리
if (err.code === 11000 && err.keyPattern && err.keyPattern.email) {
const error = new Error(
"이미 사용 중인 이메일입니다. 다른 이메일을 입력해 주세요."
);
error.field = "email";
throw error;
}

// mongoose validation error 처리 (예: 이메일 형식이 잘못되었거나, 필수 값 누락 등)
if (err.name === "ValidationError") {
const firstErrorField = Object.keys(err.errors)[0];
const errorMessage = err.errors[firstErrorField].message;
const error = new Error(errorMessage);
error.field = firstErrorField;
throw error;
}

// 그 외 에러는 그대로 던짐
throw err;
}
};

const Auth = mongoose.model("Auth", authSchema, "Auth");
module.exports = Auth;
Loading