Update README.md
[KisSync.git] / src / utilities.js
blob7561f897f2326619e5c439e5810fdf789e272312
1 (function () {
2 const root = module.exports;
3 const net = require("net");
4 const crypto = require("crypto");
6 root.isValidChannelName = function (name) {
7 return name.match(/^[\w-]{1,30}$/);
8 },
10 root.isValidUserName = function (name) {
11 return name.match(/^[\w-]{1,20}$/);
14 root.isValidEmail = function (email) {
15 if (typeof email !== "string") {
16 return false;
19 if (email.length > 255) {
20 return false;
23 if (!email.match(/^[^@]+?@[^@]+$/)) {
24 return false;
27 if (email.match(/^[^@]+?@(localhost|127\.0\.0\.1)$/)) {
28 return false;
31 return true;
34 root.randomSalt = function (length) {
35 var chars = "abcdefgihjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
36 + "0123456789!@#$%^&*_+=~";
37 var salt = [];
38 for(var i = 0; i < length; i++) {
39 salt.push(chars[parseInt(Math.random()*chars.length)]);
41 return salt.join('');
44 root.getIPRange = function (ip) {
45 if (net.isIPv6(ip)) {
46 return root.expandIPv6(ip)
47 .replace(/((?:[0-9a-f]{4}:){3}[0-9a-f]{4}):(?:[0-9a-f]{4}:){3}[0-9a-f]{4}/, "$1");
48 } else {
49 return ip.replace(/((?:[0-9]+\.){2}[0-9]+)\.[0-9]+/, "$1");
53 root.getWideIPRange = function (ip) {
54 if (net.isIPv6(ip)) {
55 return root.expandIPv6(ip)
56 .replace(/((?:[0-9a-f]{4}:){2}[0-9a-f]{4}):(?:[0-9a-f]{4}:){4}[0-9a-f]{4}/, "$1");
57 } else {
58 return ip.replace(/([0-9]+\.[0-9]+)\.[0-9]+\.[0-9]+/, "$1");
62 root.expandIPv6 = function (ip) {
63 var result = "0000:0000:0000:0000:0000:0000:0000:0000".split(":");
64 var parts = ip.split("::");
65 var left = parts[0].split(":");
66 var i = 0;
67 left.forEach(function (block) {
68 while (block.length < 4) {
69 block = "0" + block;
71 result[i++] = block;
72 });
74 if (parts.length > 1) {
75 var right = parts[1].split(":");
76 i = 7;
77 right.forEach(function (block) {
78 while (block.length < 4) {
79 block = "0" + block;
81 result[i--] = block;
82 });
85 return result.join(":");
88 root.formatTime = function (sec) {
89 if(sec === "--:--")
90 return sec;
92 sec = Math.floor(+sec);
93 var h = "", m = "", s = "";
95 if(sec >= 3600) {
96 h = "" + Math.floor(sec / 3600);
97 if(h.length < 2)
98 h = "0" + h;
99 sec %= 3600;
102 m = "" + Math.floor(sec / 60);
103 if(m.length < 2)
104 m = "0" + m;
106 s = "" + (sec % 60);
107 if(s.length < 2)
108 s = "0" + s;
110 if(h === "")
111 return [m, s].join(":");
113 return [h, m, s].join(":");
116 root.parseTime = function (time) {
117 var parts = time.split(":").reverse();
118 var seconds = 0;
119 // TODO: consider refactoring to remove this suppression
120 /* eslint no-fallthrough: off */
121 switch (parts.length) {
122 case 3:
123 seconds += parseInt(parts[2]) * 3600;
124 case 2:
125 seconds += parseInt(parts[1]) * 60;
126 case 1:
127 seconds += parseInt(parts[0]);
128 break;
129 default:
130 break;
132 return seconds;
135 root.newRateLimiter = function () {
136 return {
137 count: 0,
138 lastTime: 0,
139 throttle: function (opts) {
140 if (typeof opts === "undefined")
141 opts = {};
143 var burst = +opts.burst,
144 sustained = +opts.sustained,
145 cooldown = +opts.cooldown;
147 if (isNaN(burst))
148 burst = 10;
150 if (isNaN(sustained))
151 sustained = 2;
153 if (isNaN(cooldown))
154 cooldown = burst / sustained;
156 // Cooled down, allow and clear buffer
157 if (this.lastTime < Date.now() - cooldown*1000) {
158 this.count = 1;
159 this.lastTime = Date.now();
160 return false;
163 // Haven't reached burst cap yet, allow
164 if (this.count < burst) {
165 this.count++;
166 this.lastTime = Date.now();
167 return false;
170 var diff = Date.now() - this.lastTime;
171 if (diff < 1000/sustained)
172 return true;
174 this.lastTime = Date.now();
175 return false;
180 root.formatLink = function (id, type, _meta) {
181 switch (type) {
182 case "yt":
183 return "https://youtu.be/" + id;
184 case "vi":
185 return "https://vimeo.com/" + id;
186 case "dm":
187 return "https://dailymotion.com/video/" + id;
188 case "sc":
189 return id;
190 case "li":
191 return "https://livestream.com/" + id;
192 case "tw":
193 return "https://twitch.tv/" + id;
194 case "rt":
195 return id;
196 case "us":
197 return "https://ustream.tv/channel/" + id;
198 case "gd":
199 return "https://docs.google.com/file/d/" + id;
200 case "fi":
201 return id;
202 case "hb":
203 return "https://www.smashcast.tv/" + id;
204 case "hl":
205 return id;
206 case "sb":
207 return "https://streamable.com/" + id;
208 case "tc":
209 return "https://clips.twitch.tv/" + id;
210 case "cm":
211 return id;
212 default:
213 return "";
217 root.isLive = function (type) {
218 switch (type) {
219 case "li":
220 case "tw":
221 case "us":
222 case "rt":
223 case "cu":
224 case "hb":
225 case "hl":
226 return true;
227 default:
228 return false;
232 root.sha1 = function (data) {
233 if (!crypto) {
234 return "";
236 var shasum = crypto.createHash("sha1");
237 shasum.update(data);
238 return shasum.digest("hex");
241 root.cloakIP = function (ip) {
242 if (ip.match(/\d+\.\d+(\.\d+)?(\.\d+)?/)) {
243 return cloakIPv4(ip);
244 } else if (ip.match(/([0-9a-f]{1,4}:){1,7}[0-9a-f]{1,4}/)) {
245 return cloakIPv6(ip);
246 } else {
247 return ip;
250 function iphash(data, len) {
251 var md5 = crypto.createHash("md5");
252 md5.update(data);
253 return md5.digest("base64").substring(0, len);
256 function cloakIPv4(ip) {
257 var parts = ip.split(".");
258 var accumulator = "";
260 parts = parts.map(function (segment, i) {
261 var part = iphash(accumulator + segment + i, 3);
262 accumulator += segment;
263 return part;
266 while (parts.length < 4) parts.push("*");
267 return parts.join(".");
270 function cloakIPv6(ip) {
271 var parts = ip.split(":");
272 parts.splice(4, 4);
273 var accumulator = "";
275 parts = parts.map(function (segment, i) {
276 var part = iphash(accumulator + segment + i, 4);
277 accumulator += segment;
278 return part;
281 while (parts.length < 4) parts.push("*");
282 return parts.join(":");
285 })();