-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
233 lines (201 loc) · 6.98 KB
/
index.js
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require('body-parser')
const app = express();
const chalk = require("chalk");
const config = require("./Config/config.js");
const RL = require("./Functions/ratelimit.js");
require("dotenv").config();
app.use(bodyParser.json());
const creationRL = new RL({
limit: config.ratelimits.creation.limit,
reset: config.ratelimits.creation.reset,
});
const viewingRL = new RL({
limit: config.ratelimits.viewing.limit,
reset: config.ratelimits.viewing.reset,
});
(async () => {
await mongoose.connect(process.env.MONGO_URI, {
autoIndex: false
});
})();
const URLs = mongoose.model("Urls", require("./Config/schemas").urlSchema, "urls");
app.get("/", async (req, res) => {
res.redirect("https://github.com/Airbus-A330/url-shortener");
});
app.get("/:code", async (req, res) => {
if (!viewingRL.canUse(req.headers['cf-connecting-ip'] ?? req.headers['x-forwarded-for'])) {
res.status(429).send({
status: 429,
message: "You are accessing this endpoint way too quickly!"
});
return;
}
if (!req.params.code) {
res.status(400).send({
message: "You are missing the shortened code."
})
return;
}
let url = await URLs.findById(req.params.code).lean().exec();
if (!url) {
res.status(404).send({
status: 404,
message: "This shortend URL couldn't be found."
});
return;
}
if (url.flagged) {
res.status(403).send({
status: 403,
message: "This shortend URL has been flagged by the system for the following reason: " + url.comments
});
await console.log(
chalk.bold.blue('[API]:') +
' Flagged URL visited: ' + chalk.gray(url.redirect_uri)
);
return;
}
if (config.url.checkLink) {
let domainCheck = await require("./Functions/checkHarmful")(url.redirect_uri);
if (domainCheck.matches) {
res.status(403).send({
status: 403,
message: `This URL has been flagged as an unsafe URL. As a result it cannot be visited using this service and should not be visited in general.`
});
await console.log(
chalk.bold.red('[SafeBrowsing]:') +
' Harmful URL detected and flagged: ' + chalk.gray(url.redirect_uri)
);
await URLs.findOneAndUpdate({
_id: url._id
}, {
$set: {
flagged: true,
comments: `Automatically flagged at ${new Date().toLocaleString()} by Google SafeBrowsing API.`
}
}, {
new: true,
upsert: false
}).exec();
return;
}
}
res.redirect(url.redirect_uri);
await console.log(
chalk.bold.blue('[API]:') +
' Redirect URL requested: ' + chalk.gray(url.redirect_uri)
);
viewingRL.increment(req.headers['cf-connecting-ip'] ?? req.headers['x-forwarded-for']);
});
app.post("/urls", async (req, res) => {
if (!creationRL.canUse(req.headers['cf-connecting-ip'] ?? req.headers['x-forwarded-for'])) {
res.status(429).send({
status: 429,
message: "You are accessing this endpoint way too quickly!"
});
return;
}
if (!req.body.url) {
res.status(400).send({
status: 404,
message: "You are missing the 'url' body parameter."
});
return;
}
if (!/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/.test(req.body.url)) {
res.status(400).send({
status: 400,
message: "The URL must be in URL form (include http:// or https://)."
});
return;
}
if (config.url.checkLink) {
let domainCheck = await require("./Functions/checkHarmful")(req.body.url);
if (domainCheck.matches) {
res.status(400).send({
status: 400,
message: "This URL has been flagged as a potentially unsafe URL and cannot be submitted."
});
await console.log(
chalk.bold.red('[SafeBrowsing]:') +
' Harmful URL submission attempt: ' + chalk.gray(url.redirect_uri)
);
return;
}
}
if (config.url.maximumRedirects !== 0) {
let count = require("./Functions/checkRedirect.js")(req.body.url);
if (count > config.url.maximumRedirects) {
res.status(400).send({
status: 400,
message: `Your URL redirects more than the allotted maximum of ${config.url.maximumRedirects} redirects.`
});
await console.log(
chalk.bold.yellow('[Validator]:') +
' URL that redirected ' + count + 'times detected: ' + chalk.gray(url.redirect_uri)
);
return;
}
}
if (!config.url.allowMultipleEntries) {
let url = await URLs.findOne({
redirect_uri: req.body.url
}).lean().exec();
if (url) {
res.status(400).send({
status: 400,
message: `This URL already exists! The shortened URL for this link is: ${req.originalUrl.replace(/\/urls\/\w+/g, "").replace("/urls", "")}${url._id}.`
});
await console.log(
chalk.bold.yellow('[Validator]:') +
' Pre-existing URL detected: ' + chalk.gray(url.redirect_uri)
);
return;
}
}
let id = await require("./Functions/idGen.js")(config.id.length);
const url = new URLs({
_id: id,
redirect_uri: req.body.url,
ip: req.headers['cf-connecting-ip'] ?? req.headers['x-forwarded-for'],
flagged: false,
comments: "",
created_at: Date.now()
});
url.save();
res.status(200).send({
status: 200,
message: "The shortened URL path has been successfully created!",
code: id,
retain: config.retain
});
await console.log(
chalk.bold.blue('[API]:') +
' URL created: ' + chalk.gray(url.redirect_uri)
);
creationRL.increment(req.headers['cf-connecting-ip'] ?? req.headers['x-forwarded-for']);
});
if (config.retain > 0) {
setTimeout(async () => {
let urls = await URLs.find({}).lean().exec();
for (const url of urls) {
if ((url.created_at + config.retain) < Date.now()) {
await URLs.findOneAndDelete({
_id: url._id
}).lean().exec();
await console.log(
chalk.bold.pink('[Worker]:') +
' URL purged due to retention rules: ' + chalk.gray(url._id)
);
}
}
}, 5 * 60 * 1000);
}
app.listen(8080, () => {
console.log(
chalk.bold.blue('[API]:') +
' API successfully deployed!'
);
});