@20syldev/api 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ github: [20syldev]
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Sylvain L.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ <div align="center">
2
+ <a href="https://api.sylvain.pro"><img src="https://api.sylvain.pro/favicon.ico" alt="Logo" width="25%" height="auto"/></a>
3
+
4
+ # API Personnelle
5
+ [![Version](https://custom-icon-badges.demolab.com/badge/Version%20:-v2.6.0-6479ee?logo=api.sylvain.pro&labelColor=23272A)](https://github.com/20syldev/api/releases/latest)
6
+ </div>
7
+
8
+ ---
9
+
10
+ ## À propos de l'API
11
+ Voici mon API personnelle, disponible sur le domaine [api.sylvain.pro](https://api.sylvain.pro).
12
+ L'API est développée avec Node.js et hébergée **24h/7j**. Elle est **simple d'utilisation** et a une **documentation** disponible sur [docs.sylvain.pro](https://docs.sylvain.pro) !
13
+ > *Une limite de **1000** requêtes maximum chaque **10 secondes** est fixée.*
14
+
15
+ ## Tester l'API localement
16
+ ```console
17
+ $ npm run build
18
+ ```
19
+ ```console
20
+ > @20syldev/api@2.6.0 build
21
+ > npm install && node app.js
22
+
23
+ [...]
24
+
25
+ found 0 vulnerabilities
26
+ API is running on
27
+ - http://127.0.0.1:3000
28
+ - http://localhost:3000
29
+ ```
30
+
31
+ *Visitez la [documentation](https://docs.sylvain.pro) dédiée, vous y retrouverez des exemples de requêtes et des codes simples pour tester l'[API](https://api.sylvain.pro) !*
package/app.js ADDED
@@ -0,0 +1,861 @@
1
+ require('dotenv').config();
2
+
3
+ // Built-in module
4
+ const crypto = require('crypto');
5
+
6
+ // Imported module
7
+ const { createCanvas } = require('canvas');
8
+ const cors = require('cors');
9
+ const express = require('express');
10
+ const fetch = require('node-fetch');
11
+ const math = require('mathjs');
12
+ const path = require('path');
13
+ const qrcode = require('qrcode');
14
+ const random = require('random');
15
+ const uuid = require('uuid');
16
+ const app = express();
17
+
18
+ // Define allowed versions & endpoints
19
+ const versions = ['v1'];
20
+ const endpoints = ['algorithms', 'captcha', 'chat', 'color', 'convert', 'domain', 'hash', 'infos', 'personal', 'qrcode', 'tic-tac-toe', 'token', 'username', 'website'];
21
+
22
+ // Store data
23
+ const logs = [], chat = [], privateChats = {}, sessions = {}, rateLimits = {}, games = {};
24
+
25
+ // Define global variables
26
+ let lastFetch = 0, requests = 0, resetTime = Date.now() + 10000;
27
+
28
+ // ----------- ----------- MIDDLEWARES SETUP ----------- ----------- //
29
+
30
+ // CORS & Express setup
31
+ app.use(cors({ methods: ['GET', 'POST'] }));
32
+ app.use(express.urlencoded({ extended: true }));
33
+ app.use(express.json());
34
+
35
+ // Set favicon for API
36
+ app.use('/favicon.ico', express.static(path.join(__dirname, 'src', 'favicon.ico')));
37
+
38
+ // Display robots.txt
39
+ app.use('/robots.txt', express.static(path.join(__dirname, 'robots.txt')));
40
+
41
+ // Return formatted JSON
42
+ app.use((req, res, next) => {
43
+ res.setHeader('Content-Type', 'application/json');
44
+ res.jsonResponse = (data) => {
45
+ res.send(JSON.stringify(data, null, 2));
46
+ };
47
+ next();
48
+ });
49
+
50
+ // Too many requests
51
+ app.use((req, res, next) => {
52
+ if (Date.now() > resetTime) requests = 0, resetTime = Date.now() + 10000;
53
+ if (++requests > 1000) return res.status(429).jsonResponse({ message: 'Too Many Requests' });
54
+ next();
55
+ });
56
+
57
+ // Save and send logs
58
+ app.use((req, res, next) => {
59
+ if (req.method === 'HEAD') return next();
60
+ if (req.originalUrl === '/logs') return next();
61
+
62
+ const startTime = Date.now();
63
+
64
+ res.on('finish', () => {
65
+ logs.push({
66
+ timestamp: new Date().toISOString(),
67
+ method: req.method,
68
+ url: req.originalUrl,
69
+ status: res.statusCode === 304 ? 200 : res.statusCode,
70
+ duration: `${Date.now() - startTime}ms`,
71
+ platform: req.headers['sec-ch-ua-platform']?.replace(/"/g, ''),
72
+ });
73
+ if (logs.length > 1000) logs.shift();
74
+ console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl} ${res.statusCode} - ${Date.now() - startTime}ms`);
75
+ });
76
+ next();
77
+ });
78
+
79
+ // Internal Server Error
80
+ app.use((err, req, res, next) => {
81
+ console.error(err.stack);
82
+ res.status(500).json({
83
+ message: 'Internal Server Error',
84
+ error: err.message,
85
+ documentation: 'https://docs.sylvain.pro',
86
+ status: '500'
87
+ });
88
+ });
89
+
90
+ // Check if version exists
91
+ app.use('/:version', (req, res, next) => {
92
+ const { version } = req.params;
93
+ const latest = versions[versions.length - 1];
94
+ const endpoint = req.originalUrl.split('/').slice(2).join('/');
95
+
96
+ if (['latest', 'fr', 'en'].includes(version)) {
97
+ return res.redirect(endpoint ? `/${latest}/${endpoint}` : `/${latest}`);
98
+ }
99
+
100
+ if (!versions.includes(version) && version !== 'logs') {
101
+ return res.status(404).jsonResponse({
102
+ message: 'Not Found',
103
+ error: `Invalid API version (${version}).`,
104
+ documentation: 'https://docs.sylvain.pro',
105
+ status: '404'
106
+ });
107
+ }
108
+ next();
109
+ });
110
+
111
+ // Check if endpoint exists
112
+ app.use('/:version/:endpoint', (req, res, next) => {
113
+ const { version, endpoint } = req.params;
114
+
115
+ if (!versions.includes(version) || !endpoints.includes(endpoint)) {
116
+ return res.status(404).jsonResponse({
117
+ message: 'Not Found',
118
+ error: `Endpoint '${endpoint}' does not exist in ${version}.`,
119
+ documentation: 'https://docs.sylvain.pro',
120
+ status: '404'
121
+ });
122
+ }
123
+ next();
124
+ });
125
+
126
+ // ----------- ----------- MAIN ENDPOINTS ----------- ----------- //
127
+
128
+ // Main route
129
+ app.get('/', (req, res) => {
130
+ res.setHeader('Content-Type', 'application/json');
131
+ res.jsonResponse({
132
+ documentation: 'https://docs.sylvain.pro',
133
+ latest: 'https://api.sylvain.pro/latest',
134
+ logs: 'https://api.sylvain.pro/logs',
135
+ versions: {
136
+ v1: 'https://api.sylvain.pro/v1'
137
+ }
138
+ });
139
+ });
140
+
141
+ // Display v1 endpoints
142
+ app.get('/v1', (req, res) => {
143
+ res.jsonResponse({
144
+ version: 'v1',
145
+ endpoints: {
146
+ get: {
147
+ algorithm: '/v1/algorithms?method={algorithm}&value={value}(&value2={value2})',
148
+ captcha: '/v1/captcha?text={text}',
149
+ chat: '/v1/chat',
150
+ color: '/v1/color',
151
+ convert: '/v1/convert?value={value}&from={unit}&to={unit}',
152
+ domain: '/v1/domain',
153
+ infos: '/v1/infos',
154
+ personal: '/v1/personal',
155
+ qrcode: '/v1/qrcode?url={URL}',
156
+ username: '/v1/username'
157
+ },
158
+ post: {
159
+ chat: '/v1/chat',
160
+ hash: '/v1/hash',
161
+ token: '/v1/token'
162
+ }
163
+ }
164
+ });
165
+ });
166
+
167
+ // Display logs
168
+ app.get('/logs', (req, res) => res.jsonResponse(logs));
169
+
170
+ // ----------- ----------- GET ENDPOINTS ----------- ----------- //
171
+
172
+ // Algorithms
173
+ app.get('/:version/algorithms', (req, res) => {
174
+ const { method, value, value2 } = req.query;
175
+
176
+ if (!['anagram', 'bubblesort', 'factorial', 'fibonacci', 'gcd', 'isprime', 'palindrome', 'primefactors', 'primelist', 'reverse'].includes(method)) {
177
+ return res.jsonResponse({
178
+ error: 'Please provide a valid algorithm (?method={algorithm})',
179
+ documentation: 'https://docs.sylvain.pro/v1/algorithms'
180
+ });
181
+ }
182
+ if (!value) return res.jsonResponse({ error: 'Please provide a valid value (&value={value})' });
183
+
184
+ if (method === 'anagram') {
185
+ if (!value2) return res.jsonResponse({ error: 'Please provide a second value (&value2={value})' });
186
+ return res.jsonResponse({ answer: value.split('').sort().join('') === value2.split('').sort().join('') });
187
+ }
188
+
189
+ if (method === 'bubblesort') {
190
+ const arr = value.split(',').map(Number);
191
+ const n = arr.length;
192
+ for (let i = 0; i < n-1; i++) {
193
+ for (let j = 0; j < n-i-1; j++) {
194
+ if (arr[j] > arr[j + 1]) [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
195
+ }
196
+ }
197
+ return res.jsonResponse({ answer: arr });
198
+ }
199
+
200
+ if (method === 'factorial') {
201
+ if (isNaN(value) || value < 0 || value > 170) return res.jsonResponse({ error: 'Please provide a valid number between 0 and 170.' });
202
+ return res.jsonResponse({ answer: math.factorial(value) });
203
+ }
204
+
205
+ if (method === 'fibonacci') {
206
+ let fib = [0, 1];
207
+ for (let i = 2; i < parseInt(value); i++) fib.push(fib[i - 1] + fib[i - 2]);
208
+ return res.jsonResponse({ answer: fib.slice(0, parseInt(value)) });
209
+ }
210
+
211
+ if (method === 'gcd') {
212
+ const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
213
+ if (!value2) return res.jsonResponse({ error: 'Please provide a second value (&value2={value})' });
214
+ if (isNaN(value) || isNaN(value2)) return res.jsonResponse({ error: 'Invalid numbers.' });
215
+ return res.jsonResponse({ answer: gcd(value, value2) });
216
+ }
217
+
218
+ if (method === 'isprime') {
219
+ let isPrime = true;
220
+ if (isNaN(value) || value < 1) return res.jsonResponse({ error: 'Please provide a valid number greater than or equal to 1.' });
221
+ for (let i = 2; i <= Math.sqrt(value); i++) {
222
+ if (value % i === 0) {
223
+ isPrime = false;
224
+ break;
225
+ }
226
+ }
227
+ return res.jsonResponse({ answer: isPrime });
228
+ }
229
+
230
+ if (method === 'palindrome') return res.jsonResponse({ answer: value === value.split('').reverse().join('') });
231
+
232
+ if (method === 'primefactors') {
233
+ let num = value;
234
+ let factors = [];
235
+ if (isNaN(num) || num < 2 || num > 100000) return res.jsonResponse({ error: 'Please provide a valid number between 2 and 100 000.' });
236
+ for (let i = 2; i <= num; i++) {
237
+ while (num % i === 0) {
238
+ factors.push(i);
239
+ num /= i;
240
+ }
241
+ }
242
+ return res.jsonResponse({ answer: factors });
243
+ }
244
+
245
+ if (method === 'primelist') {
246
+ const primes = [];
247
+ if (isNaN(value) || value < 2 || value > 10000) return res.jsonResponse({ error: 'Please provide a valid number between 2 and 10 000.' });
248
+ for (let i = 2; i <= value; i++) {
249
+ let isPrime = true;
250
+ for (let j = 2; j <= Math.sqrt(i); j++) {
251
+ if (i % j === 0) {
252
+ isPrime = false;
253
+ break;
254
+ }
255
+ }
256
+ if (isPrime) primes.push(i);
257
+ }
258
+ return res.jsonResponse({ answer: primes });
259
+ }
260
+
261
+ if (method === 'reverse') return res.jsonResponse({ answer: value.split('').reverse().join('') });
262
+ });
263
+
264
+ // Generate captcha
265
+ app.get('/:version/captcha', (req, res) => {
266
+ const captcha = req.query.text;
267
+
268
+ if (!captcha) return res.jsonResponse({ error: 'Please provide a valid argument (?text={text})' });
269
+
270
+ const size = 60, font = '60px Comic Sans Ms', width = captcha.length * size, height = 120;
271
+ const canvas = createCanvas(width, height), ctx = canvas.getContext('2d');
272
+
273
+ ctx.fillStyle = 'white';
274
+ ctx.fillRect(0, 0, width, height);
275
+
276
+ for (let i = 0; i < 20; i++) {
277
+ ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
278
+ ctx.beginPath();
279
+ ctx.moveTo(Math.random() * width, Math.random() * height);
280
+ ctx.lineTo(Math.random() * width, Math.random() * height);
281
+ ctx.lineWidth = Math.random() * 2;
282
+ ctx.stroke();
283
+ }
284
+
285
+ let x = (canvas.width + 20 - width) / 2;
286
+ for (let i = 0; i < captcha.length; i++) {
287
+ const offsetX = Math.cos(i * 0.3) * 10, y = height / 2.5 + Math.floor(Math.random() * (height / 2));
288
+
289
+ ctx.font = font;
290
+ ctx.fillStyle = `rgb(${Math.floor(Math.random() * 192)}, ${Math.floor(Math.random() * 192)}, ${Math.floor(Math.random() * 192)})`;
291
+
292
+ ctx.save();
293
+ ctx.translate(x + size / 2, y);
294
+ ctx.rotate((Math.random() - 0.5) * 0.5);
295
+ ctx.fillText(captcha[i], -size / 2 + offsetX, 0);
296
+ ctx.restore();
297
+
298
+ x += size;
299
+ }
300
+
301
+ for (let i = 0; i < 200; i++) {
302
+ ctx.fillStyle = 'black';
303
+ ctx.fillRect(Math.floor(Math.random() * width), Math.floor(Math.random() * height), 1.2, 1.2);
304
+ }
305
+
306
+ res.set('Content-Type', 'image/png');
307
+ res.send(canvas.toBuffer('image/png'));
308
+ });
309
+
310
+ // Display stored data
311
+ app.get('/:version/chat', (req, res) => {
312
+ if (chat.length > 0) res.jsonResponse(chat);
313
+ else res.jsonResponse({ error: 'No messages stored.' });
314
+ });
315
+
316
+ // GET private chat error
317
+ app.get('/:version/chat/private', (req, res) => {
318
+ res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
319
+ });
320
+
321
+ // Generate color
322
+ app.get('/:version/color', (req, res) => {
323
+ const r = random.int(0, 255), g = random.int(0, 255), b = random.int(0, 255);
324
+ const hsl = (() => {
325
+ const r1 = r / 255, g1 = g / 255, b1 = b / 255, max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1), l = (max + min) / 2;
326
+ if (max === min) return [0, 0, l * 100];
327
+ const d = max - min, s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
328
+ let h = { [r1]: (g1 - b1) / d + (g1 < b1 ? 6 : 0), [g1]: (b1 - r1) / d + 2, [b1]: (r1 - g1) / d + 4 }[max];
329
+ return [h * 60 % 360, s * 100, l * 100];
330
+ })();
331
+ const hsv = (() => {
332
+ const max = Math.max(r, g, b), min = Math.min(r, g, b), v = max / 255, s = max ? (max - min) / max : 0;
333
+ let h = max === min ? 0 : { [r]: (g - b) / (max - min), [g]: 2 + (b - r) / (max - min), [b]: 4 + (r - g) / (max - min) }[max];
334
+ return [h * 60 % 360, s * 100, v * 100];
335
+ })();
336
+ const hwb = (() => {
337
+ const [h] = hsv, whiteness = Math.min(r, g, b) / 255, blackness = 1 - Math.max(r, g, b) / 255;
338
+ return [h, whiteness * 100, blackness * 100];
339
+ })();
340
+ const cmyk = (() => {
341
+ const k = 1 - Math.max(r, g, b) / 255, c = (1 - r / 255 - k) / (1 - k) || 0, m = (1 - g / 255 - k) / (1 - k) || 0, y = (1 - b / 255 - k) / (1 - k) || 0;
342
+ return [c, m, y, k].map(x => x * 100);
343
+ })();
344
+ res.jsonResponse({
345
+ hex: `#${[r, g, b].map(x => x.toString(16).padStart(2, '0')).join('')}`,
346
+ rgb: `rgb(${r}, ${g}, ${b})`,
347
+ hsl: `hsl(${hsl[0].toFixed(1)}, ${hsl[1].toFixed(1)}%, ${hsl[2].toFixed(1)}%)`,
348
+ hsv: `hsv(${hsv[0].toFixed(1)}, ${hsv[1].toFixed(1)}%, ${hsv[2].toFixed(1)}%)`,
349
+ hwb: `hwb(${hwb[0].toFixed(1)}, ${hwb[1].toFixed(1)}%, ${hwb[2].toFixed(1)}%)`,
350
+ cmyk: `cmyk(${cmyk.map(x => x.toFixed(1)).join('%, ')}%)`
351
+ });
352
+ });
353
+
354
+ // Convert units
355
+ app.get('/:version/convert', (req, res) => {
356
+ const { value, from, to } = req.query;
357
+
358
+ if (!value || isNaN(value)) return res.jsonResponse({ error: 'Please provide a valid value (?value={value})' });
359
+ if (!from) return res.jsonResponse({ error: 'Please provide a valid source unit (&from={unit})' });
360
+ if (!to) return res.jsonResponse({ error: 'Please provide a valid target unit (&to={unit})' });
361
+
362
+ const conversions = {
363
+ celsius: { fahrenheit: (val) => (val * 9) / 5 + 32, kelvin: (val) => val + 273.15 },
364
+ fahrenheit: { celsius: (val) => ((val - 32) * 5) / 9, kelvin: (val) => ((val - 32) * 5) / 9 + 273.15 },
365
+ kelvin: { celsius: (val) => val - 273.15, fahrenheit: (val) => ((val - 273.15) * 9) / 5 + 32 },
366
+ };
367
+
368
+ const convert = conversions[from.toLowerCase()]?.[to.toLowerCase()];
369
+ if (!convert) return res.jsonResponse({ error: 'Invalid conversion units.' });
370
+
371
+ res.jsonResponse({ from, to, value: parseFloat(value), result: convert(parseFloat(value)) });
372
+ });
373
+
374
+ // Generate domain informations
375
+ app.get('/:version/domain', (req, res) => {
376
+ const random = (arr) => arr[Math.floor(Math.random() * arr.length)];
377
+ const subdomains = ['fr.', 'en.', 'docs.', 'api.', 'projects.', 'app.', 'web.', 'info.', 'dev.', 'shop.', 'blog.', 'support.', 'mail.', 'forum.'];
378
+ const domains = ['example', 'site', 'test', 'demo', 'page', 'store', 'portfolio', 'platform', 'hub', 'network', 'service', 'cloud', 'solutions', 'company'];
379
+ const tlds = ['.com', '.fr', '.eu', '.dev', '.net', '.org', '.io', '.tech', '.biz', '.info', '.co', '.app', '.store', '.online', '.shop', '.tv'];
380
+
381
+ const domain = `${random(domains)}${random(tlds)}`;
382
+ const fulldomain = `${random(subdomains)}${domain}`;
383
+
384
+ const getRandomIp = () => `${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}`;
385
+ const ips = Array.from({ length: Math.floor(Math.random() * 3) + 1 }, getRandomIp);
386
+ const dns = Array.from({ length: Math.floor(Math.random() * 5) + 1 }, getRandomIp);
387
+
388
+ res.jsonResponse({
389
+ domain,
390
+ full_domain: fulldomain,
391
+ ip_address: ips,
392
+ ssl_certified: Math.random() > 0.5,
393
+ hosting_provider: random(['AWS', 'Bluehost', 'DigitalOcean', 'GitHub', 'HostGator', 'Render', 'SiteGround']),
394
+ dns_servers: dns,
395
+ dns_provider: random(['AWS Route 53', 'Cloudflare', 'GoDaddy', 'Google DNS', 'Namecheap']),
396
+ traffic: `${Math.floor(Math.random() * 10000)} visits/day`,
397
+ seo_score: Math.floor(Math.random() * 100),
398
+ page_rank: Math.floor(Math.random() * 10),
399
+ country: random(['Australia', 'Canada', 'France', 'Germany', 'India', 'Japan', 'UK', 'USA']),
400
+ website_type: random(['Blog', 'Community', 'Corporate', 'Educational', 'E-commerce', 'Personal', 'Portfolio']),
401
+ random_name: domain.split('.')[0],
402
+ random_subdomain: fulldomain.split('.')[0],
403
+ random_tld: domain.split('.').pop(),
404
+ backlinks_count: Math.floor(Math.random() * 1000),
405
+ creation_date: new Date(Date.now() - Math.floor(Math.random() * 10000000000)).toISOString(),
406
+ expiration_date: new Date(Date.now() + Math.floor(Math.random() * 10000000000)).toISOString(),
407
+ });
408
+ });
409
+
410
+ // GET hash error
411
+ app.get('/:version/hash', (req, res) => {
412
+ res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
413
+ });
414
+
415
+ // Display API informations
416
+ app.get('/:version/infos', (req, res) => {
417
+ res.jsonResponse({
418
+ endpoints: endpoints.length,
419
+ last_version: versions.at(-1),
420
+ documentation: 'https://docs.sylvain.pro',
421
+ github: 'https://github.com/20syldev/api',
422
+ creation: 'November 25th 2024',
423
+ });
424
+ });
425
+
426
+ // Generate personal data
427
+ app.get('/:version/personal', (req, res) => {
428
+ const random = (arr) => arr[Math.floor(Math.random() * arr.length)];
429
+
430
+ const people = [
431
+ { name: 'John Doe', social: 'john_doe', email: 'john@example.com', country: 'US' },
432
+ { name: 'Jane Martin', social: 'jane_martin', email: 'jane@example.com', country: 'FR' },
433
+ { name: 'Michael Johnson', social: 'mike_johnson', email: 'michael@example.com', country: 'UK' },
434
+ { name: 'Emily Davis', social: 'emily_davis', email: 'emily@example.com', country: 'ES' },
435
+ { name: 'Alexis Barbos', social: 'alexis_barbos', email: 'alexis@example.com', country: 'DE' },
436
+ { name: 'Sarah Williams', social: 'sarah_williams', email: 'sarah@example.com', country: 'IT' },
437
+ { name: 'Daniel Brown', social: 'daniel_brown', email: 'daniel@example.com', country: 'JP' },
438
+ { name: 'Sophia Wilson', social: 'sophia_wilson', email: 'sophia@example.com', country: 'BR' },
439
+ { name: 'James Taylor', social: 'james_taylor', email: 'james@example.com', country: 'CA' },
440
+ { name: 'Olivia Thomas', social: 'olivia_thomas', email: 'olivia@example.com', country: 'AU' }
441
+ ];
442
+
443
+ const countries = {
444
+ US: { tel: '123-456-7890', code: '1', lang: 'English' },
445
+ FR: { tel: '06 78 90 12 34', code: '33', lang: 'French' },
446
+ UK: { tel: '7911 123456', code: '44', lang: 'English' },
447
+ ES: { tel: '678 901 234', code: '34', lang: 'Spanish' },
448
+ DE: { tel: '163 555 1584', code: '49', lang: 'German' },
449
+ IT: { tel: '345 678 9012', code: '39', lang: 'Italian' },
450
+ JP: { tel: '080-1234-5678', code: '81', lang: 'Japanese' },
451
+ BR: { tel: '(11) 98765-4321', code: '55', lang: 'Portuguese' },
452
+ CA: { tel: '416-123-4567', code: '1', lang: 'English' },
453
+ AU: { tel: '0412 345 678', code: '61', lang: 'English' }
454
+ };
455
+
456
+ const jobs = ['Writer', 'Artist', 'Musician', 'Explorer', 'Scientist', 'Engineer', 'Athlete', 'Doctor'];
457
+ const hobbies = ['Reading', 'Traveling', 'Gaming', 'Cooking', 'Fitness', 'Music', 'Photography', 'Writing'];
458
+ const cities = ['New York', 'Paris', 'London', 'Madrid', 'Berlin', 'Rome', 'Tokyo', 'Los Angeles', 'Sydney', 'São Paulo', 'Toronto'];
459
+ const streets = ['Main St', '2nd Ave', 'Broadway', 'Park Lane', 'Elm St', 'Sunset Blvd', 'Maple St', 'Highland Rd'];
460
+
461
+ const card = Array.from({ length: 4 }, () => Math.floor(Math.random() * 9000) + 1000).join(' ');
462
+ const cvc = Math.floor(Math.random() * 900) + 100;
463
+ const expiration = `${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}/${(new Date().getFullYear() + Math.floor(Math.random() * 3)).toString().slice(-2)}`;
464
+
465
+ const person = random(people);
466
+ const social = person.social;
467
+ const country = person.country;
468
+ const phone = countries[country].tel;
469
+ const lang = countries[country].lang;
470
+
471
+ const age = Math.floor(Math.random() * 50) + 18;
472
+ const birthday = new Date(Date.now() - Math.floor((Math.random() * 50 + 18) * 365.25 * 24 * 60 * 60 * 1000)).toISOString();
473
+
474
+ let emergencyContacts = [], yearIncome = Math.floor(Math.random() * 100000), subscriptions = [], pets = [], vehicles = [];
475
+ let civilStatus = 'Single';
476
+ let children = 0;
477
+
478
+ if (age >= 21 && Math.random() > 0.7) civilStatus = 'Married';
479
+
480
+ if (civilStatus === 'Married' && age >= 25) children = Math.floor(Math.random() * 4);
481
+
482
+ while (emergencyContacts.length < Math.floor(Math.random() * 3) + 1) {
483
+ let emergencyContact = random(people);
484
+ while (emergencyContact.email === person.email || emergencyContacts.some(e => e.email === emergencyContact.email)) {
485
+ emergencyContact = random(people);
486
+ }
487
+ emergencyContacts.push({
488
+ name: emergencyContact.name,
489
+ relationship: random(['Spouse', 'Parent', 'Sibling', 'Friend']),
490
+ phone: `+${countries[country].code} ${countries[country].tel}`
491
+ });
492
+ }
493
+
494
+ while (subscriptions.length < Math.floor(Math.random() * 3) + 1) {
495
+ let subscription = random(['Netflix', 'Spotify', 'Amazon Prime', 'Disney+', 'Hulu']);
496
+ if (!subscriptions.includes(subscription)) subscriptions.push(subscription);
497
+ }
498
+
499
+ while (pets.length < Math.floor(Math.random() * 3) + 1) {
500
+ let pet = random(['Dog', 'Cat', 'Fish', 'Bird', 'None']);
501
+ if (!pets.includes(pet)) pets.push(pet);
502
+ }
503
+
504
+ while (vehicles.length < Math.floor(Math.random() * 3) + 1) {
505
+ let vehicle = random(['Car', 'Bike', 'Motorcycle', 'Bus', 'None']);
506
+ if (!vehicles.includes(vehicle)) vehicles.push(vehicle);
507
+ }
508
+
509
+ res.jsonResponse({
510
+ name: person.name,
511
+ email: person.email,
512
+ localisation: country,
513
+ phone: `+${countries[country].code} ${phone}`,
514
+ job: random(jobs),
515
+ hobbies: random(hobbies),
516
+ language: lang,
517
+ card,
518
+ cvc,
519
+ expiration,
520
+ address: `${Math.floor(Math.random() * 9999)} ${random(streets)}, ${random(cities)}`,
521
+ birthday,
522
+ civil_status: civilStatus,
523
+ children,
524
+ vehicle: vehicles,
525
+ social_profiles: {
526
+ twitter: `@${social}`,
527
+ facebook: `facebook.com/${social}`,
528
+ linkedin: `linkedin.com/in/${social}`,
529
+ instagram: `instagram.com/${social}`
530
+ },
531
+ year_income: `${yearIncome} USD/year`,
532
+ month_income: `${(yearIncome / 12).toFixed(2)} USD/month`,
533
+ education: random(['High School', 'Bachelor\'s', 'Master\'s', 'PhD']),
534
+ work_experience: `${Math.floor(Math.random() * 20)} years`,
535
+ health_status: random(['Healthy', 'Minor Issues', 'Chronic Conditions']),
536
+ emergency_contacts: emergencyContacts,
537
+ subscriptions,
538
+ pets,
539
+ });
540
+ });
541
+
542
+ // Generate QR Code
543
+ app.get('/:version/qrcode', async (req, res) => {
544
+ const { url } = req.query;
545
+
546
+ if (!url) return res.jsonResponse({ error: 'Please provide a valid url (?url={URL})' });
547
+
548
+ try { res.jsonResponse({ qr: await qrcode.toDataURL(url) }); }
549
+ catch { res.jsonResponse({ error: 'Error generating QR code.' }); }
550
+ });
551
+
552
+ // GET tic-tac-toe game error
553
+ app.get('/:version/tic-tac-toe', (req, res) => {
554
+ res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
555
+ });
556
+
557
+ // GET tic-tac-toe fetch error
558
+ app.get('/:version/tic-tac-toe/fetch', (req, res) => {
559
+ res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
560
+ });
561
+
562
+ // GET token error
563
+ app.get('/:version/token', (req, res) => {
564
+ res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
565
+ });
566
+
567
+ // Generate username
568
+ app.get('/:version/username', (req, res) => {
569
+ const adj = ['Happy', 'Silly', 'Clever', 'Creative', 'Brave', 'Gentle', 'Kind', 'Funny', 'Wise', 'Charming', 'Sincere', 'Resourceful', 'Patient', 'Energetic', 'Adventurous', 'Ambitious', 'Courageous', 'Courteous', 'Determined'];
570
+ const ani = ['Cat', 'Dog', 'Tiger', 'Elephant', 'Monkey', 'Penguin', 'Dolphin', 'Lion', 'Bear', 'Fox', 'Owl', 'Giraffe', 'Zebra', 'Koala', 'Rabbit', 'Squirrel', 'Panda', 'Horse', 'Wolf', 'Eagle'];
571
+ const job = ['Writer', 'Artist', 'Musician', 'Explorer', 'Scientist', 'Engineer', 'Athlete', 'Chef', 'Doctor', 'Teacher', 'Lawyer', 'Entrepreneur', 'Actor', 'Dancer', 'Photographer', 'Architect', 'Pilot', 'Designer', 'Journalist', 'Veterinarian'];
572
+
573
+ const random = (arr) => arr[Math.floor(Math.random() * arr.length)];
574
+ const nombre = Math.floor(Math.random() * 100);
575
+ const choix = {
576
+ adj_num: () => random(adj) + nombre,
577
+ ani_num: () => random(ani) + nombre,
578
+ pro_num: () => random(job) + nombre,
579
+ adj_ani: () => random(adj) + random(ani),
580
+ adj_ani_num: () => random(adj) + random(ani) + nombre,
581
+ adj_pro: () => random(adj) + random(job),
582
+ pro_ani: () => random(job) + random(ani),
583
+ pro_ani_num: () => random(job) + random(ani) + nombre
584
+ };
585
+
586
+ const username = choix[random(Object.keys(choix))]();
587
+ res.jsonResponse({ adjective: adj, animal: ani, job, number: nombre, username });
588
+ });
589
+
590
+ // Display informations for owner's website
591
+ app.get('/:version/website', async (req, res) => {
592
+ const currentTime = Date.now();
593
+
594
+ if (currentTime - lastFetch >= 10 * 60 * 1000) {
595
+ try {
596
+ const username = '20syldev';
597
+ const token = process.env.STATS5;
598
+ const today = new Date().toISOString().split('T')[0];
599
+ const monthFirst = new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString().split('T')[0];
600
+ const lastYear = new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString().split('T')[0];
601
+
602
+ const query = `
603
+ {
604
+ user(login: "${username}") {
605
+ contributionsCollection(from: "${today}T00:00:00Z") {
606
+ contributionCalendar {
607
+ totalContributions
608
+ }
609
+ }
610
+ contributions_month: contributionsCollection(from: "${monthFirst}T00:00:00Z") {
611
+ contributionCalendar {
612
+ totalContributions
613
+ }
614
+ }
615
+ contributions_year: contributionsCollection(from: "${lastYear}T00:00:00Z") {
616
+ contributionCalendar {
617
+ totalContributions
618
+ }
619
+ }
620
+ }
621
+ }`;
622
+
623
+ const apiResponse = await fetch('https://api.github.com/graphql', {
624
+ method: 'POST',
625
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
626
+ body: JSON.stringify({ query })
627
+ });
628
+
629
+ if (!apiResponse.ok) throw new Error('Error fetching data.');
630
+
631
+ const data = await apiResponse.json();
632
+ const user = data?.data?.user;
633
+
634
+ contributions = {
635
+ today: user?.contributionsCollection?.contributionCalendar?.totalContributions || 0,
636
+ month: user?.contributions_month?.contributionCalendar?.totalContributions || 0,
637
+ year: user?.contributions_year?.contributionCalendar?.totalContributions || 0,
638
+ };
639
+
640
+ lastFetch = currentTime;
641
+ } catch { contributions = { today: 0, month: 0, year: 0 }; }
642
+ }
643
+
644
+ res.jsonResponse({
645
+ versions: {
646
+ api: process.env.API,
647
+ coop_api: process.env.COOP_API,
648
+ coop_status: process.env.COOP_STATUS,
649
+ chat: process.env.CHAT,
650
+ digit: process.env.DIGIT,
651
+ doc_coopbot: process.env.DOC_COOPBOT,
652
+ docs: process.env.DOCS,
653
+ donut: process.env.DONUT,
654
+ flowers: process.env.FLOWERS,
655
+ gemsync: process.env.GEMSYNC,
656
+ gitsite: process.env.GITSITE,
657
+ logs: process.env.LOGS,
658
+ nitrogen: process.env.NITROGEN,
659
+ old_database: process.env.OLD_DATABASE,
660
+ php: process.env.PHP,
661
+ portfolio: process.env.PORTFOLIO,
662
+ python_api: process.env.PYTHON_API,
663
+ readme: process.env.README,
664
+ terminal: process.env.TERMINAL,
665
+ wrkit: process.env.WRKIT,
666
+ zpki: process.env.ZPKI
667
+ },
668
+ updated_projects: process.env.RECENT.split(' '),
669
+ new_projects: process.env.NEW.split(' '),
670
+ stats: {
671
+ os: process.env.STATS1,
672
+ front: process.env.STATS2,
673
+ back: process.env.STATS3,
674
+ projects: process.env.STATS4,
675
+ today: contributions.today.toString(),
676
+ this_month: contributions.month.toString(),
677
+ last_year: contributions.year.toString(),
678
+ },
679
+ notif_tag: process.env.TAG,
680
+ active: process.env.ACTIVE
681
+ });
682
+ });
683
+
684
+ // ----------- ----------- POST ENDPOINTS ----------- ----------- //
685
+
686
+ // Store chat messages
687
+ app.post('/:version/chat', (req, res) => {
688
+ const { username, message, timestamp, session, token } = req.body;
689
+
690
+ if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
691
+ if (!message) return res.jsonResponse({ error: 'Please provide a message (&message={message})' });
692
+ if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
693
+
694
+ const u = username.toLowerCase(), now = Date.now();
695
+ const msg = { username, message, timestamp: timestamp || new Date().toISOString() };
696
+
697
+ rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
698
+ if (rateLimits[u].length > 50) {
699
+ const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
700
+ return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
701
+ }
702
+ rateLimits[u].push(now);
703
+
704
+ if (sessions[u] && sessions[u].user !== session) return res.jsonResponse({ error: 'Session ID mismatch' });
705
+
706
+ if (token) {
707
+ privateChats[token] = privateChats[token] || [];
708
+ privateChats[token].push(msg);
709
+ setTimeout(() => { delete privateChats[token]; }, 3600000);
710
+ } else {
711
+ chat.push(msg);
712
+ setTimeout(() => chat.splice(chat.indexOf(msg), 1), 3600000);
713
+ }
714
+
715
+ sessions[u] = sessions[u] || { user: session, last: now };
716
+ sessions[u].last = now;
717
+
718
+ setTimeout(() => { if (now - sessions[u].last >= 3600000) delete sessions[u]; }, 3600000);
719
+
720
+ res.jsonResponse({ message: 'Message sent successfully' });
721
+ });
722
+
723
+ // Display a private chat with a token
724
+ app.post('/:version/chat/private', (req, res) => {
725
+ const { username, token } = req.body;
726
+
727
+ if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
728
+ if (!token) return res.jsonResponse({ error: 'Please provide a valid token (&token={key}).' });
729
+
730
+ const u = username.toLowerCase(), now = Date.now();
731
+
732
+ rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
733
+ if (rateLimits[u].length > 50) {
734
+ const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
735
+ return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
736
+ }
737
+ rateLimits[u].push(now);
738
+
739
+ if (privateChats[token]) return res.jsonResponse(privateChats[token]);
740
+
741
+ return res.jsonResponse({ error: 'Invalid or expired token.' });
742
+ });
743
+
744
+ // Store tic tac toe games
745
+ app.post('/:version/tic-tac-toe', (req, res) => {
746
+ const { username, move, session, game } = req.body;
747
+
748
+ if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
749
+ if (!move) return res.jsonResponse({ error: 'Please provide a valid move (&move={move})' });
750
+ if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
751
+ if (!game) return res.jsonResponse({ error: 'Please provide a valid game ID (&game={ID})' });
752
+
753
+ const u = username.toLowerCase(), now = Date.now();
754
+ const play = { username, move, session };
755
+ const validMoves = ['1-1', '1-2', '1-3', '2-1', '2-2', '2-3', '3-1', '3-2', '3-3'];
756
+
757
+ if (!validMoves.includes(move)) return res.jsonResponse({ error: 'Invalid move. Please provide a valid move (e.g., 1-1, 2-2, 3-3).' });
758
+
759
+ rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
760
+ if (rateLimits[u].length > 50) {
761
+ const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
762
+ return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
763
+ }
764
+ rateLimits[u].push(now);
765
+
766
+ if (sessions[u] && sessions[u].user !== session) return res.jsonResponse({ error: 'Session ID mismatch' });
767
+
768
+ games[game] = games[game] || [];
769
+
770
+ const players = [...new Set(games[game].map(play => play.username))];
771
+ if (players.length >= 2 && !players.includes(username)) {
772
+ return res.jsonResponse({ error: 'Game is full, you can only watch.' });
773
+ }
774
+
775
+ if (games[game].length > 0 && games[game][games[game].length - 1].username === username) return res.jsonResponse({ error: 'Please wait for the other player to make a move.' });
776
+ if (games[game].some(play => play.move === move)) return res.jsonResponse({ error: 'Move already made. Please choose a different move.' });
777
+
778
+ games[game].push(play);
779
+ setTimeout(() => { delete games[game]; }, 3600000);
780
+
781
+ sessions[u] = sessions[u] || { user: session, last: now };
782
+ sessions[u].last = now;
783
+
784
+ setTimeout(() => { if (now - sessions[u].last >= 3600000) delete sessions[u]; }, 3600000);
785
+
786
+ res.jsonResponse({ message: 'Move sent successfully' });
787
+ });
788
+
789
+ // Display a tic tac toe game with a token
790
+ app.post('/:version/tic-tac-toe/fetch', (req, res) => {
791
+ const { username, game } = req.body;
792
+
793
+ if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
794
+
795
+ const generateId = () => {
796
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
797
+ return Array.from(crypto.randomBytes(5)).map(b => chars[b % chars.length]).join('');
798
+ };
799
+
800
+ const ID = game || generateId();
801
+ const u = username.toLowerCase(), now = Date.now();
802
+
803
+ rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
804
+ if (rateLimits[u].length > 50) {
805
+ const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
806
+ return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
807
+ }
808
+ rateLimits[u].push(now);
809
+
810
+ if (!games[ID]) games[ID] = [];
811
+
812
+ const data = games[ID], last = data.length ? data[data.length - 1].username : null;
813
+ const players = [...new Set(data.map(p => p.username))], turn = players.find(p => p !== last);
814
+
815
+ res.jsonResponse({ game: data, turn, ID });
816
+ });
817
+
818
+ // Generate hash
819
+ app.post('/:version/hash', (req, res) => {
820
+ const { text, method } = req.body;
821
+
822
+ if (!text) return res.jsonResponse({ error: 'Please provide a text (?text={text})' });
823
+ if (!method) return res.jsonResponse({
824
+ error: 'Please provide a valid hash algorithm (?method={algorithm})',
825
+ documentation: 'https://docs.sylvain.pro/v1/hash'
826
+ });
827
+
828
+ const methods = crypto.getHashes();
829
+ if (!methods.includes(method)) return res.jsonResponse({ error: `Unsupported method. Use one of: ${methods.join(', ')}` });
830
+
831
+ const hash = crypto.createHash(method).update(text).digest('hex');
832
+ res.jsonResponse({ method, hash });
833
+ });
834
+
835
+ // Generate Token
836
+ app.post('/:version/token', (req, res) => {
837
+ const length = parseInt(req.body.len || 24, 10);
838
+ const type = req.body.type || 'alpha';
839
+
840
+ if (isNaN(length) || length < 0) return res.jsonResponse({ error: 'Invalid number.' });
841
+ if (length > 4096) return res.jsonResponse({ error: 'Length cannot exceed 4096.' });
842
+ if (length < 12) return res.jsonResponse({ error: 'Length cannot be less than 12.' });
843
+
844
+ const generateToken = (chars) => Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
845
+ const token = {
846
+ alpha: generateToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
847
+ alphanum: generateToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'),
848
+ base64: crypto.randomBytes(length).toString('base64').slice(0, length),
849
+ hex: crypto.randomBytes(length).toString('hex').slice(0, length),
850
+ num: generateToken('0123456789'),
851
+ punct: generateToken('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'),
852
+ urlsafe: generateToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_'),
853
+ uuid: uuid.v4().replace(/-/g, '').slice(0, length)
854
+ }[type] || generateToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
855
+
856
+ res.jsonResponse({ token });
857
+ });
858
+
859
+ // ----------- ----------- SERVER SETUP ----------- ----------- //
860
+
861
+ app.listen(3000, () => console.log('API is running on\n - http://127.0.0.1:3000\n - http://localhost:3000'));
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "version": "2.6.0",
3
+ "name": "@20syldev/api",
4
+ "description": "Personnal API",
5
+ "main": "app.js",
6
+ "scripts": {
7
+ "start": "node app.js",
8
+ "build": "npm install && node app.js",
9
+ "upgrade:minor": "npm upgrade",
10
+ "upgrade:major": "npx npm-check-updates -u && npm install",
11
+ "upgrade:build": "npm upgrade && npm install && node app.js"
12
+ },
13
+ "dependencies": {
14
+ "canvas": "^3.1.0",
15
+ "cors": "2.8.5",
16
+ "dotenv": "^16.4.7",
17
+ "express": "^4.21.2",
18
+ "mathjs": "^14.2.0",
19
+ "node-fetch": "^2.7.0",
20
+ "prettier": "^3.4.2",
21
+ "qrcode": "^1.5.4",
22
+ "random": "^5.1.1",
23
+ "uuid": "^11.0.5"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/20syldev/api.git"
28
+ },
29
+ "keywords": [
30
+ "api",
31
+ "express",
32
+ "utility",
33
+ "math"
34
+ ],
35
+ "author": "Sylvain L.",
36
+ "license": "BSD 3-Clause",
37
+ "bugs": {
38
+ "url": "https://github.com/20syldev/api/issues"
39
+ },
40
+ "homepage": "https://api.sylvain.pro"
41
+ }
package/robots.txt ADDED
@@ -0,0 +1,74 @@
1
+ User-agent: 360Spider
2
+ User-agent: 360Spider-Image
3
+ User-agent: 360Spider-Video
4
+ User-agent: AdsBot-Google
5
+ User-agent: AdsBot-Google-Mobile
6
+ User-agent: AdsBot-Google-Mobile-Apps
7
+ User-agent: adidxbot
8
+ User-agent: Applebot
9
+ User-agent: AppleNewsBot
10
+ User-agent: Baiduspider
11
+ User-agent: Baiduspider-image
12
+ User-agent: Baiduspider-news
13
+ User-agent: Baiduspider-video
14
+ User-agent: bingbot
15
+ User-agent: BingPreview
16
+ User-agent: BublupBot
17
+ User-agent: CCBot
18
+ User-agent: Cliqzbot
19
+ User-agent: coccoc
20
+ User-agent: coccocbot-image
21
+ User-agent: coccocbot-web
22
+ User-agent: Daumoa
23
+ User-agent: Dazoobot
24
+ User-agent: DeuSu
25
+ User-agent: DuckDuckBot
26
+ User-agent: DuckDuckGo-Favicons-Bot
27
+ User-agent: EuripBot
28
+ User-agent: Exploratodo
29
+ User-agent: Facebot
30
+ User-agent: Feedly
31
+ User-agent: Findxbot
32
+ User-agent: Googlebot
33
+ User-agent: Googlebot-Image
34
+ User-agent: Googlebot-Mobile
35
+ User-agent: Googlebot-News
36
+ User-agent: Googlebot-Video
37
+ User-agent: HaoSouSpider
38
+ User-agent: ichiro
39
+ User-agent: istellabot
40
+ User-agent: JikeSpider
41
+ User-agent: Lycos
42
+ User-agent: Mail.Ru
43
+ User-agent: Mediapartners-Google
44
+ User-agent: MojeekBot
45
+ User-agent: msnbot
46
+ User-agent: msnbot-media
47
+ User-agent: OrangeBot
48
+ User-agent: Pinterest
49
+ User-agent: Plukkie
50
+ User-agent: Qwantify
51
+ User-agent: Rambler
52
+ User-agent: SeznamBot
53
+ User-agent: Sosospider
54
+ User-agent: Slurp
55
+ User-agent: Sogou blog
56
+ User-agent: Sogou inst spider
57
+ User-agent: Sogou News Spider
58
+ User-agent: Sogou Orion spider
59
+ User-agent: Sogou spider2
60
+ User-agent: Sogou web spider
61
+ User-agent: SputnikBot
62
+ User-agent: Teoma
63
+ User-agent: Twitterbot
64
+ User-agent: wotbox
65
+ User-agent: yacybot
66
+ User-agent: Yandex
67
+ User-agent: YandexMobileBot
68
+ User-agent: Yeti
69
+ User-agent: YioopBot
70
+ User-agent: yoozBot
71
+ User-agent: YoudaoBot
72
+ Disallow:
73
+ User-agent: *
74
+ Disallow: /
Binary file