-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
132 lines (107 loc) · 4.08 KB
/
server.js
File metadata and controls
132 lines (107 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const { URL } = require('url');
// [수정] 우리가 만든 모델 파일 불러오기! (이 한 줄로 스키마 정의 끝)
const Url = require('./models/Url');
const app = express();
const PORT = process.env.PORT || 3000;
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/my_tinyurl';
const BASE_URL = process.env.BASE_URL || `http://localhost:${PORT}`;
// 미들웨어
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
// DB 연결
mongoose.connect(MONGO_URI)
.then(() => console.log('✅ MongoDB 연결 성공!'))
.catch(err => console.error('❌ MongoDB 연결 실패:', err));
// --- (여기 있던 스키마 정의 코드는 싹 지워졌습니다!) ---
// 랜덤 ID 생성 함수
function generateShortId(length = 5) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// 🚀 라우팅 (API)
// URL 단축 (POST)
app.post('/shorten', async (req, res) => {
try {
let { originalUrl } = req.body;
// 유효성 검사 로직들...
if (!originalUrl.match(/^https?:\/\//)) originalUrl = 'http://' + originalUrl;
try {
const parsedUrl = new URL(originalUrl);
if (parsedUrl.hostname.indexOf('.') === -1 && parsedUrl.hostname !== 'localhost') {
throw new Error("Invalid Domain");
}
const myDomain = new URL(BASE_URL).hostname;
if (myDomain === parsedUrl.hostname) {
return res.status(400).json({ error: '이미 단축된 URL입니다.' });
}
} catch (err) {
return res.status(400).json({ error: '유효하지 않은 URL입니다.' });
}
// DB 조회 및 저장
let urlData = await Url.findOne({ originalUrl }); // 여기서 models/Url.js를 사용함
if (urlData) {
return res.json({ shortUrl: `${BASE_URL}/${urlData.shortId}` });
}
let shortId = generateShortId();
while (await Url.findOne({ shortId })) {
shortId = generateShortId();
}
urlData = new Url({ originalUrl, shortId });
await urlData.save();
res.json({ shortUrl: `${BASE_URL}/${urlData.shortId}` });
} catch (err) {
console.error(err);
res.status(500).json({ error: '서버 에러' });
}
});
// 리다이렉트 (GET)
app.get('/:shortId', async (req, res) => {
try {
const { shortId } = req.params;
const urlData = await Url.findOne({ shortId });
if (urlData) {
urlData.clicks++;
await urlData.save();
return res.redirect(urlData.originalUrl);
} else {
return res.status(404).send('<h1>존재하지 않는 URL입니다.</h1>');
}
} catch (err) {
console.error(err);
res.status(500).send('서버 에러');
}
});
app.listen(PORT, () => {
console.log(`🚀 서버 실행 중: ${PORT}`);
});
// server.js의 리다이렉트 부분 찾아서 수정
app.get('/:shortId', async (req, res) => {
try {
const { shortId } = req.params;
const urlData = await Url.findOne({ shortId });
if (urlData) {
// [추가] 진짜 IP 알아내기 (Cloudflare 헤더 우선, 없으면 일반 IP)
const userIp = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.socket.remoteAddress;
// [추가] DB에 IP와 시간 기록 (배열에 추가)
urlData.visitHistory.push({ ip: userIp });
urlData.clicks++;
await urlData.save();
return res.redirect(urlData.originalUrl);
} else {
// ... (에러 처리)
}
} catch (err) {
// ...
}
});