@20syldev/api 4.7.0 → 4.9.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.
- package/README.md +84 -47
- package/dist/app.js +6 -6
- package/dist/app.js.map +1 -1
- package/dist/config/env.js.map +1 -1
- package/dist/config/versions.js +22 -3
- package/dist/config/versions.js.map +1 -1
- package/dist/constants.js +27 -12
- package/dist/constants.js.map +1 -1
- package/dist/middleware/error.js +2 -2
- package/dist/middleware/error.js.map +1 -1
- package/dist/middleware/logger.js.map +1 -1
- package/dist/middleware/ratelimit.js +2 -2
- package/dist/middleware/ratelimit.js.map +1 -1
- package/dist/middleware/version.js.map +1 -1
- package/dist/modules/v3.js +1 -1
- package/dist/modules/v3.js.map +1 -1
- package/{src/modules/v4/address.ts → dist/modules/v4/address.js} +14 -46
- package/dist/modules/v4/address.js.map +1 -0
- package/{src/modules/v4/agent.ts → dist/modules/v4/agent.js} +26 -38
- package/dist/modules/v4/agent.js.map +1 -0
- package/dist/modules/v4/algorithms.js +79 -0
- package/dist/modules/v4/algorithms.js.map +1 -1
- package/dist/modules/v4/avatar.js +136 -0
- package/dist/modules/v4/avatar.js.map +1 -0
- package/dist/modules/v4/barcode.js +440 -0
- package/dist/modules/v4/barcode.js.map +1 -0
- package/dist/modules/v4/captcha.js +7 -0
- package/dist/modules/v4/captcha.js.map +1 -1
- package/dist/modules/v4/chat.js +9 -1
- package/dist/modules/v4/chat.js.map +1 -1
- package/dist/modules/v4/color.js +8 -1
- package/dist/modules/v4/color.js.map +1 -1
- package/dist/modules/v4/convert.js +9 -0
- package/dist/modules/v4/convert.js.map +1 -1
- package/dist/modules/v4/credit.js +104 -0
- package/dist/modules/v4/credit.js.map +1 -0
- package/dist/modules/v4/cron.js +202 -0
- package/dist/modules/v4/cron.js.map +1 -0
- package/dist/modules/v4/dice.js +7 -0
- package/dist/modules/v4/dice.js.map +1 -1
- package/dist/modules/v4/domain.js +6 -1
- package/dist/modules/v4/domain.js.map +1 -1
- package/dist/modules/v4/encode.js +71 -0
- package/dist/modules/v4/encode.js.map +1 -1
- package/dist/modules/v4/geo.js +11 -1
- package/dist/modules/v4/geo.js.map +1 -1
- package/dist/modules/v4/hash.js +10 -1
- package/dist/modules/v4/hash.js.map +1 -1
- package/dist/modules/v4/hyperplanning.js +9 -1
- package/dist/modules/v4/hyperplanning.js.map +1 -1
- package/{src/modules/v4/ip.ts → dist/modules/v4/ip.js} +58 -60
- package/dist/modules/v4/ip.js.map +1 -0
- package/dist/modules/v4/levenshtein.js +8 -0
- package/dist/modules/v4/levenshtein.js.map +1 -1
- package/dist/modules/v4/palette.js +9 -1
- package/dist/modules/v4/palette.js.map +1 -1
- package/{src/modules/v4/password.ts → dist/modules/v4/password.js} +25 -63
- package/dist/modules/v4/password.js.map +1 -0
- package/dist/modules/v4/personal.js +14 -18
- package/dist/modules/v4/personal.js.map +1 -1
- package/dist/modules/v4/placeholder.js +8 -0
- package/dist/modules/v4/placeholder.js.map +1 -1
- package/dist/modules/v4/qrcode.js +10 -3
- package/dist/modules/v4/qrcode.js.map +1 -1
- package/dist/modules/v4/regex.js +48 -0
- package/dist/modules/v4/regex.js.map +1 -0
- package/dist/modules/v4/statistics.js +7 -0
- package/dist/modules/v4/statistics.js.map +1 -1
- package/dist/modules/v4/text.js +30 -0
- package/dist/modules/v4/text.js.map +1 -1
- package/dist/modules/v4/tic_tac_toe.js +9 -1
- package/dist/modules/v4/tic_tac_toe.js.map +1 -1
- package/dist/modules/v4/time.js +62 -11
- package/dist/modules/v4/time.js.map +1 -1
- package/dist/modules/v4/token.js +17 -8
- package/dist/modules/v4/token.js.map +1 -1
- package/dist/modules/v4/username.js +5 -0
- package/dist/modules/v4/username.js.map +1 -1
- package/dist/modules/v4/validate.js +21 -0
- package/dist/modules/v4/validate.js.map +1 -1
- package/dist/modules/v4.js +10 -1
- package/dist/modules/v4.js.map +1 -1
- package/dist/routes/delete.js.map +1 -1
- package/dist/routes/get.js +208 -48
- package/dist/routes/get.js.map +1 -1
- package/dist/routes/index.js +2 -2
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/patch.js.map +1 -1
- package/dist/routes/post.js +2 -2
- package/dist/routes/post.js.map +1 -1
- package/dist/utils/response.js.map +1 -1
- package/package.json +2 -1
- package/.github/FUNDING.yml +0 -1
- package/.prettierrc +0 -7
- package/eslint.config.js +0 -24
- package/robots.txt +0 -74
- package/src/app.ts +0 -53
- package/src/config/env.ts +0 -87
- package/src/config/plans.ts +0 -61
- package/src/config/versions.ts +0 -119
- package/src/constants.ts +0 -46
- package/src/favicon.ico +0 -0
- package/src/middleware/cors.ts +0 -23
- package/src/middleware/error.ts +0 -16
- package/src/middleware/json.ts +0 -9
- package/src/middleware/logger.ts +0 -32
- package/src/middleware/ratelimit.ts +0 -89
- package/src/middleware/version.ts +0 -40
- package/src/modules/v3/algorithms.js +0 -218
- package/src/modules/v3/captcha.js +0 -60
- package/src/modules/v3/chat.js +0 -111
- package/src/modules/v3/color.js +0 -60
- package/src/modules/v3/convert.js +0 -39
- package/src/modules/v3/domain.js +0 -86
- package/src/modules/v3/hash.js +0 -16
- package/src/modules/v3/hyperplanning.js +0 -56
- package/src/modules/v3/levenshtein.js +0 -46
- package/src/modules/v3/personal.js +0 -139
- package/src/modules/v3/qrcode.js +0 -20
- package/src/modules/v3/tic_tac_toe.js +0 -242
- package/src/modules/v3/time.js +0 -98
- package/src/modules/v3/token.js +0 -54
- package/src/modules/v3/username.js +0 -94
- package/src/modules/v3/utils.js +0 -77
- package/src/modules/v3.js +0 -15
- package/src/modules/v4/algorithms.ts +0 -276
- package/src/modules/v4/captcha.ts +0 -114
- package/src/modules/v4/chat.ts +0 -130
- package/src/modules/v4/color.ts +0 -75
- package/src/modules/v4/convert.ts +0 -127
- package/src/modules/v4/dice.ts +0 -46
- package/src/modules/v4/domain.ts +0 -86
- package/src/modules/v4/encode.ts +0 -241
- package/src/modules/v4/geo.ts +0 -63
- package/src/modules/v4/hash.ts +0 -32
- package/src/modules/v4/hyperplanning.ts +0 -93
- package/src/modules/v4/levenshtein.ts +0 -46
- package/src/modules/v4/palette.ts +0 -71
- package/src/modules/v4/personal.ts +0 -140
- package/src/modules/v4/placeholder.ts +0 -197
- package/src/modules/v4/qrcode.ts +0 -121
- package/src/modules/v4/statistics.ts +0 -62
- package/src/modules/v4/text.ts +0 -330
- package/src/modules/v4/tic_tac_toe.ts +0 -264
- package/src/modules/v4/time.ts +0 -103
- package/src/modules/v4/token.ts +0 -53
- package/src/modules/v4/username.ts +0 -94
- package/src/modules/v4/validate.ts +0 -76
- package/src/modules/v4.ts +0 -27
- package/src/routes/delete.ts +0 -72
- package/src/routes/get.ts +0 -835
- package/src/routes/index.ts +0 -59
- package/src/routes/patch.ts +0 -45
- package/src/routes/post.ts +0 -209
- package/src/storage/index.ts +0 -16
- package/src/types/express.d.ts +0 -17
- package/src/types/storage.ts +0 -48
- package/src/utils/colors.ts +0 -91
- package/src/utils/helpers.ts +0 -90
- package/src/utils/response.ts +0 -28
- package/tests/integration/api.test.ts +0 -716
- package/tests/tsconfig.json +0 -3
- package/tests/unit/address.test.ts +0 -84
- package/tests/unit/agent.test.ts +0 -113
- package/tests/unit/algorithms.test.ts +0 -121
- package/tests/unit/captcha.test.ts +0 -62
- package/tests/unit/chat.test.ts +0 -54
- package/tests/unit/color.test.ts +0 -54
- package/tests/unit/convert.test.ts +0 -68
- package/tests/unit/dice.test.ts +0 -58
- package/tests/unit/domain.test.ts +0 -48
- package/tests/unit/encode.test.ts +0 -109
- package/tests/unit/geo.test.ts +0 -46
- package/tests/unit/hash.test.ts +0 -45
- package/tests/unit/hyperplanning.test.ts +0 -97
- package/tests/unit/ip.test.ts +0 -140
- package/tests/unit/levenshtein.test.ts +0 -35
- package/tests/unit/palette.test.ts +0 -54
- package/tests/unit/password.test.ts +0 -86
- package/tests/unit/personal.test.ts +0 -66
- package/tests/unit/placeholder.test.ts +0 -58
- package/tests/unit/qrcode.test.ts +0 -83
- package/tests/unit/statistics.test.ts +0 -58
- package/tests/unit/text.test.ts +0 -108
- package/tests/unit/tic_tac_toe.test.ts +0 -56
- package/tests/unit/time.test.ts +0 -68
- package/tests/unit/token.test.ts +0 -62
- package/tests/unit/username.test.ts +0 -35
- package/tests/unit/validate.test.ts +0 -72
- package/tsconfig.json +0 -17
- package/tsconfig.test.json +0 -9
package/src/routes/index.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { type Request, type Response, Router } from 'express';
|
|
2
|
-
|
|
3
|
-
import { versions } from '../config/versions.js';
|
|
4
|
-
import { APP_VERSION, DOCS_URL, START_TIME } from '../constants.js';
|
|
5
|
-
import { logger } from '../middleware/logger.js';
|
|
6
|
-
import { ipLimits } from '../storage/index.js';
|
|
7
|
-
|
|
8
|
-
const router = Router();
|
|
9
|
-
|
|
10
|
-
router.get('/', (req: Request, res: Response) => {
|
|
11
|
-
const base = `${req.protocol}://${req.get('host')}`;
|
|
12
|
-
const links = Object.keys(versions).reduce<Record<string, string>>((link, version) => {
|
|
13
|
-
link[version] = `${base}/${version}`;
|
|
14
|
-
return link;
|
|
15
|
-
}, {});
|
|
16
|
-
|
|
17
|
-
res.jsonResponse({
|
|
18
|
-
documentation: DOCS_URL,
|
|
19
|
-
latest: `${base}/latest`,
|
|
20
|
-
health: `${base}/health`,
|
|
21
|
-
logs: `${base}/logs`,
|
|
22
|
-
versions: links,
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
router.get('/health', (_req: Request, res: Response) => {
|
|
27
|
-
const mem = process.memoryUsage();
|
|
28
|
-
|
|
29
|
-
res.jsonResponse({
|
|
30
|
-
status: 'ok',
|
|
31
|
-
uptime: Math.floor((Date.now() - START_TIME) / 1000),
|
|
32
|
-
version: APP_VERSION,
|
|
33
|
-
node: process.version,
|
|
34
|
-
memory: {
|
|
35
|
-
rss: `${(mem.rss / 1024 / 1024).toFixed(1)} MB`,
|
|
36
|
-
heap_used: `${(mem.heapUsed / 1024 / 1024).toFixed(1)} MB`,
|
|
37
|
-
heap_total: `${(mem.heapTotal / 1024 / 1024).toFixed(1)} MB`,
|
|
38
|
-
},
|
|
39
|
-
connections: Object.keys(ipLimits).length,
|
|
40
|
-
logs: logger.entries().length,
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
router.get('/logs', (_req: Request, res: Response) => {
|
|
45
|
-
res.jsonResponse(logger.entries());
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
router.get('/latest', (_req: Request, res: Response) => {
|
|
49
|
-
const latest = Object.keys(versions).pop()!;
|
|
50
|
-
res.redirect(`/${latest}`);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
router.get('/latest/{*rest}', (req: Request, res: Response) => {
|
|
54
|
-
const latest = Object.keys(versions).pop()!;
|
|
55
|
-
const rest = (req.params as Record<string, string>).rest;
|
|
56
|
-
res.redirect(`/${latest}/${rest}`);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export default router;
|
package/src/routes/patch.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { type Request, type Response, Router } from 'express';
|
|
2
|
-
|
|
3
|
-
import { ticTacToeStorage } from '../storage/index.js';
|
|
4
|
-
import { error } from '../utils/response.js';
|
|
5
|
-
|
|
6
|
-
const router = Router();
|
|
7
|
-
|
|
8
|
-
// Play a tic-tac-toe move
|
|
9
|
-
router.patch('/:version/tic-tac-toe/:game', (req: Request, res: Response) => {
|
|
10
|
-
if (req.version !== 'v4') {
|
|
11
|
-
error(res, 405, 'PATCH is only supported in v4+.', `${req.version}/tic-tac-toe`);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const game = req.params.game as string;
|
|
16
|
-
const { username, move, session } = (req.body as Record<string, string>) || {};
|
|
17
|
-
|
|
18
|
-
if (!username) {
|
|
19
|
-
error(res, 400, 'Please provide a username (?username={username})');
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
if (!move) {
|
|
23
|
-
error(res, 400, 'Please provide a valid move (&move={move})');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (!session) {
|
|
27
|
-
error(res, 400, 'Please provide a valid session ID (&session={ID})');
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const result = req.module.tic_tac_toe('play', {
|
|
33
|
-
username,
|
|
34
|
-
move,
|
|
35
|
-
session,
|
|
36
|
-
game,
|
|
37
|
-
storage: ticTacToeStorage,
|
|
38
|
-
});
|
|
39
|
-
res.jsonResponse(result);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
error(res, 400, (err as Error).message);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
export default router;
|
package/src/routes/post.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { type Request, type Response, Router } from 'express';
|
|
2
|
-
|
|
3
|
-
import { MAX_TOKEN_LENGTH, MIN_TOKEN_LENGTH } from '../constants.js';
|
|
4
|
-
import type { HashResult } from '../modules/v4/hash.js';
|
|
5
|
-
import { chatStorage, ticTacToeStorage } from '../storage/index.js';
|
|
6
|
-
import { since } from '../utils/helpers.js';
|
|
7
|
-
import { error } from '../utils/response.js';
|
|
8
|
-
|
|
9
|
-
const router = Router();
|
|
10
|
-
|
|
11
|
-
// Store chat messages
|
|
12
|
-
router.post('/:version/chat', (req: Request, res: Response) => {
|
|
13
|
-
const { username, message, timestamp, session, token } = (req.body as Record<string, string>) || {};
|
|
14
|
-
|
|
15
|
-
if (!username) {
|
|
16
|
-
error(res, 400, 'Please provide a username (?username={username})');
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
if (!message) {
|
|
20
|
-
error(res, 400, 'Please provide a message (&message={message})');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
if (!session) {
|
|
24
|
-
error(res, 400, 'Please provide a valid session ID (&session={ID})');
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const result = req.module.chat('message', {
|
|
30
|
-
username,
|
|
31
|
-
message,
|
|
32
|
-
timestamp,
|
|
33
|
-
session,
|
|
34
|
-
token,
|
|
35
|
-
storage: chatStorage,
|
|
36
|
-
});
|
|
37
|
-
res.jsonResponse(result);
|
|
38
|
-
} catch (err) {
|
|
39
|
-
error(res, 400, (err as Error).message);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Display a private chat with a token
|
|
44
|
-
router.post('/:version/chat/private', (req: Request, res: Response) => {
|
|
45
|
-
const { username, token } = (req.body as Record<string, string>) || {};
|
|
46
|
-
|
|
47
|
-
if (!username) {
|
|
48
|
-
error(res, 400, 'Please provide a username (?username={username})');
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (!token) {
|
|
52
|
-
error(res, 400, 'Please provide a valid token (&token={key}).');
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
const messages = req.module.chat('private', {
|
|
58
|
-
username,
|
|
59
|
-
token,
|
|
60
|
-
storage: chatStorage,
|
|
61
|
-
});
|
|
62
|
-
res.jsonResponse(messages);
|
|
63
|
-
} catch (err) {
|
|
64
|
-
error(res, 400, (err as Error).message);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// Generate hash
|
|
69
|
-
router.post('/:version/hash', (req: Request, res: Response) => {
|
|
70
|
-
const { text, method, encoding } = (req.body as Record<string, string>) || {};
|
|
71
|
-
|
|
72
|
-
if (!text) {
|
|
73
|
-
error(res, 400, 'Please provide a text (?text={text})', `${req.version}/hash`);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
if (!method) {
|
|
77
|
-
error(res, 400, 'Please provide a valid hash algorithm (&method={algorithm})', `${req.version}/hash`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
if (since(req.version, 4)) {
|
|
83
|
-
const hashFn = req.module.hash as (t: string, m: string, e?: string) => HashResult;
|
|
84
|
-
const result = hashFn(text, method, encoding);
|
|
85
|
-
res.jsonResponse(result);
|
|
86
|
-
} else {
|
|
87
|
-
const result = (req.module.hash as (t: string, m: string) => Record<string, string>)(text, method);
|
|
88
|
-
res.jsonResponse(result);
|
|
89
|
-
}
|
|
90
|
-
} catch (err) {
|
|
91
|
-
error(res, 400, (err as Error).message, `${req.version}/hash`);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Display a planning from an ICS file
|
|
96
|
-
router.post('/:version/hyperplanning', async (req: Request, res: Response) => {
|
|
97
|
-
const { url, detail } = (req.body as Record<string, string>) || {};
|
|
98
|
-
|
|
99
|
-
if (!url) {
|
|
100
|
-
error(res, 400, 'Please provide a valid ICS file URL (?url={URL})', `${req.version}/hyperplanning`);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
const hyperplanning = await req.module.hyperplanning(url, detail);
|
|
106
|
-
res.jsonResponse(hyperplanning);
|
|
107
|
-
} catch (err) {
|
|
108
|
-
error(res, 400, (err as Error).message, `${req.version}/hyperplanning`);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Store tic tac toe games
|
|
113
|
-
router.post('/:version/tic-tac-toe', (req: Request, res: Response) => {
|
|
114
|
-
const { username, move, session, game } = (req.body as Record<string, string>) || {};
|
|
115
|
-
|
|
116
|
-
if (!username) {
|
|
117
|
-
error(res, 400, 'Please provide a username (?username={username})');
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if (!move) {
|
|
121
|
-
error(res, 400, 'Please provide a valid move (&move={move})');
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (!session) {
|
|
125
|
-
error(res, 400, 'Please provide a valid session ID (&session={ID})');
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (!game) {
|
|
129
|
-
error(res, 400, 'Please provide a valid game ID (&game={ID})');
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
const result = req.module.tic_tac_toe('play', {
|
|
135
|
-
username,
|
|
136
|
-
move,
|
|
137
|
-
session,
|
|
138
|
-
game,
|
|
139
|
-
storage: ticTacToeStorage,
|
|
140
|
-
});
|
|
141
|
-
res.jsonResponse(result);
|
|
142
|
-
} catch (err) {
|
|
143
|
-
error(res, 400, (err as Error).message);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Display a tic tac toe game with a token
|
|
148
|
-
router.post('/:version/tic-tac-toe/fetch', (req: Request, res: Response) => {
|
|
149
|
-
const { username, game } = (req.body as Record<string, string>) || {};
|
|
150
|
-
const privateGame = ((req.body as Record<string, unknown>) || {}).private as boolean | undefined;
|
|
151
|
-
|
|
152
|
-
if (!username) {
|
|
153
|
-
error(res, 400, 'Please provide a username (?username={username})');
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const result = req.module.tic_tac_toe('fetch', {
|
|
159
|
-
username,
|
|
160
|
-
game,
|
|
161
|
-
private: privateGame,
|
|
162
|
-
storage: ticTacToeStorage,
|
|
163
|
-
});
|
|
164
|
-
res.jsonResponse(result);
|
|
165
|
-
} catch (err) {
|
|
166
|
-
error(res, 400, (err as Error).message);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// List public tic tac toe games
|
|
171
|
-
router.post('/:version/tic-tac-toe/list', (req: Request, res: Response) => {
|
|
172
|
-
try {
|
|
173
|
-
const result = req.module.tic_tac_toe('list', {
|
|
174
|
-
storage: ticTacToeStorage,
|
|
175
|
-
});
|
|
176
|
-
res.jsonResponse(result);
|
|
177
|
-
} catch (err) {
|
|
178
|
-
error(res, 400, (err as Error).message);
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// Generate Token
|
|
183
|
-
router.post('/:version/token', (req: Request, res: Response) => {
|
|
184
|
-
const body = (req.body as Record<string, unknown>) || {};
|
|
185
|
-
const len = parseInt(String(body.len || 24), 10);
|
|
186
|
-
const type = body.type ? String(body.type).toLowerCase() : 'alpha';
|
|
187
|
-
|
|
188
|
-
if (isNaN(len) || len < 0) {
|
|
189
|
-
error(res, 400, 'Invalid number.', `${req.version}/token`);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
if (len > MAX_TOKEN_LENGTH) {
|
|
193
|
-
error(res, 400, 'Length cannot exceed 4096.', `${req.version}/token`);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
if (len < MIN_TOKEN_LENGTH) {
|
|
197
|
-
error(res, 400, 'Length cannot be less than 12.', `${req.version}/token`);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
const token = req.module.token(len, type);
|
|
203
|
-
res.jsonResponse({ token });
|
|
204
|
-
} catch (err) {
|
|
205
|
-
error(res, 400, (err as Error).message, `${req.version}/token`);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
export default router;
|
package/src/storage/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ChatStorage, IpLimits, TicTacToeStorage } from '../types/storage.js';
|
|
2
|
-
|
|
3
|
-
export const ipLimits: IpLimits = {};
|
|
4
|
-
|
|
5
|
-
export const chatStorage: ChatStorage = {
|
|
6
|
-
messages: [],
|
|
7
|
-
privateChats: {},
|
|
8
|
-
sessions: {},
|
|
9
|
-
rateLimits: {},
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const ticTacToeStorage: TicTacToeStorage = {
|
|
13
|
-
games: {},
|
|
14
|
-
sessions: {},
|
|
15
|
-
rateLimits: {},
|
|
16
|
-
};
|
package/src/types/express.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type * as apiv3 from '../modules/v3.js';
|
|
2
|
-
import type * as apiv4 from '../modules/v4.js';
|
|
3
|
-
|
|
4
|
-
declare global {
|
|
5
|
-
namespace Express {
|
|
6
|
-
interface Request {
|
|
7
|
-
version: string;
|
|
8
|
-
latest: string;
|
|
9
|
-
endpoint: string;
|
|
10
|
-
module: typeof apiv3 | typeof apiv4;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface Response {
|
|
14
|
-
jsonResponse: (data: unknown) => void;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
package/src/types/storage.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
export interface LogEntry {
|
|
2
|
-
timestamp: string;
|
|
3
|
-
method: string;
|
|
4
|
-
url: string;
|
|
5
|
-
status: number;
|
|
6
|
-
duration: string;
|
|
7
|
-
platform: string | undefined;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ChatMessage {
|
|
11
|
-
username: string;
|
|
12
|
-
message: string;
|
|
13
|
-
timestamp: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ChatStorage {
|
|
17
|
-
messages: ChatMessage[];
|
|
18
|
-
privateChats: Record<string, ChatMessage[]>;
|
|
19
|
-
sessions: Record<string, { user: string; last: number }>;
|
|
20
|
-
rateLimits: Record<string, number[]>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface TicTacToeMove {
|
|
24
|
-
username: string;
|
|
25
|
-
move: string;
|
|
26
|
-
session: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface TicTacToeGame {
|
|
30
|
-
moves: TicTacToeMove[];
|
|
31
|
-
players: string[];
|
|
32
|
-
private: boolean;
|
|
33
|
-
creation: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface TicTacToeStorage {
|
|
37
|
-
games: Record<string, TicTacToeGame>;
|
|
38
|
-
sessions: Record<string, { user: string; last: number }>;
|
|
39
|
-
rateLimits: Record<string, number[]>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface IpLimits {
|
|
43
|
-
[ip: string]: {
|
|
44
|
-
[hour: string]: {
|
|
45
|
-
[minute: string]: number;
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
}
|
package/src/utils/colors.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parses a hex color string into an RGB tuple.
|
|
3
|
-
*
|
|
4
|
-
* @param hex - Hex color string (#RRGGBB or RRGGBB)
|
|
5
|
-
* @returns RGB tuple [r, g, b] with values 0-255
|
|
6
|
-
* @throws Error if the hex string is invalid
|
|
7
|
-
*/
|
|
8
|
-
export function hexToRgb(hex: string): [number, number, number] {
|
|
9
|
-
const clean = hex.replace('#', '');
|
|
10
|
-
if (!/^[0-9a-fA-F]{6}$/.test(clean)) throw new Error('Invalid HEX color (use #RRGGBB)');
|
|
11
|
-
return [parseInt(clean.slice(0, 2), 16), parseInt(clean.slice(2, 4), 16), parseInt(clean.slice(4, 6), 16)];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Converts HSL values to an RGB tuple.
|
|
16
|
-
*
|
|
17
|
-
* @param h - Hue in degrees (0-360)
|
|
18
|
-
* @param s - Saturation (0-1)
|
|
19
|
-
* @param l - Lightness (0-1)
|
|
20
|
-
* @returns RGB tuple [r, g, b] with values 0-255
|
|
21
|
-
*/
|
|
22
|
-
export function hslToRgb(h: number, s: number, l: number): [number, number, number] {
|
|
23
|
-
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
24
|
-
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
25
|
-
const m = l - c / 2;
|
|
26
|
-
let r1 = 0,
|
|
27
|
-
g1 = 0,
|
|
28
|
-
b1 = 0;
|
|
29
|
-
if (h < 60) [r1, g1, b1] = [c, x, 0];
|
|
30
|
-
else if (h < 120) [r1, g1, b1] = [x, c, 0];
|
|
31
|
-
else if (h < 180) [r1, g1, b1] = [0, c, x];
|
|
32
|
-
else if (h < 240) [r1, g1, b1] = [0, x, c];
|
|
33
|
-
else if (h < 300) [r1, g1, b1] = [x, 0, c];
|
|
34
|
-
else [r1, g1, b1] = [c, 0, x];
|
|
35
|
-
return [Math.round((r1 + m) * 255), Math.round((g1 + m) * 255), Math.round((b1 + m) * 255)];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Validates and normalizes a hex color string, adding # if missing.
|
|
40
|
-
*
|
|
41
|
-
* @param value - Raw color value (with or without #)
|
|
42
|
-
* @param def - Default color if value is empty
|
|
43
|
-
* @returns Normalized hex color string with #
|
|
44
|
-
* @throws Error if the color format is invalid
|
|
45
|
-
*/
|
|
46
|
-
export function normalizeColor(value: string | undefined, def: string): string {
|
|
47
|
-
if (!value) return def;
|
|
48
|
-
const clean = value.startsWith('#') ? value : `#${value}`;
|
|
49
|
-
if (!/^#([0-9a-fA-F]{3}){1,2}$/.test(clean)) throw new Error('Invalid color (use hex like ff6600)');
|
|
50
|
-
return clean;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Formats an RGB tuple as a hex color string.
|
|
55
|
-
*
|
|
56
|
-
* @param r - Red channel (0-255)
|
|
57
|
-
* @param g - Green channel (0-255)
|
|
58
|
-
* @param b - Blue channel (0-255)
|
|
59
|
-
* @returns Hex color string (#rrggbb)
|
|
60
|
-
*/
|
|
61
|
-
export function rgbToHex(r: number, g: number, b: number): string {
|
|
62
|
-
return `#${[r, g, b].map((x) => x.toString(16).padStart(2, '0')).join('')}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Converts RGB values to an HSL tuple.
|
|
67
|
-
*
|
|
68
|
-
* @param r - Red channel (0-255)
|
|
69
|
-
* @param g - Green channel (0-255)
|
|
70
|
-
* @param b - Blue channel (0-255)
|
|
71
|
-
* @returns HSL tuple [h (0-360), s (0-1), l (0-1)]
|
|
72
|
-
*/
|
|
73
|
-
export function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
|
|
74
|
-
const r1 = r / 255,
|
|
75
|
-
g1 = g / 255,
|
|
76
|
-
b1 = b / 255;
|
|
77
|
-
const max = Math.max(r1, g1, b1),
|
|
78
|
-
min = Math.min(r1, g1, b1);
|
|
79
|
-
const l = (max + min) / 2;
|
|
80
|
-
|
|
81
|
-
if (max === min) return [0, 0, l];
|
|
82
|
-
|
|
83
|
-
const d = max - min;
|
|
84
|
-
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
85
|
-
let h = 0;
|
|
86
|
-
if (max === r1) h = (g1 - b1) / d + (g1 < b1 ? 6 : 0);
|
|
87
|
-
else if (max === g1) h = (b1 - r1) / d + 2;
|
|
88
|
-
else h = (r1 - g1) / d + 4;
|
|
89
|
-
|
|
90
|
-
return [(h * 60 + 360) % 360, s, l];
|
|
91
|
-
}
|
package/src/utils/helpers.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tracks requests per user and throws if the rate limit is exceeded.
|
|
3
|
-
*
|
|
4
|
-
* @param rateLimits - Store of timestamps per user
|
|
5
|
-
* @param userId - Identifier of the requesting user
|
|
6
|
-
* @param timestamp - Current timestamp in milliseconds
|
|
7
|
-
* @param window - Time window in milliseconds
|
|
8
|
-
* @param limit - Maximum requests allowed in the window
|
|
9
|
-
* @returns false if within limits
|
|
10
|
-
* @throws Error if rate limit is exceeded
|
|
11
|
-
*/
|
|
12
|
-
export function checkRateLimit(
|
|
13
|
-
rateLimits: Record<string, number[]>,
|
|
14
|
-
userId: string,
|
|
15
|
-
timestamp: number,
|
|
16
|
-
window = 10000,
|
|
17
|
-
limit = 50,
|
|
18
|
-
): boolean {
|
|
19
|
-
rateLimits[userId] = (rateLimits[userId] ?? []).filter((ts) => timestamp - ts < window);
|
|
20
|
-
|
|
21
|
-
if (rateLimits[userId]!.length > limit) {
|
|
22
|
-
const remainingTime = Math.ceil((rateLimits[userId]![0]! + window - timestamp) / 1000);
|
|
23
|
-
throw new Error(`Rate limit exceeded. Try again in ${remainingTime} seconds.`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
rateLimits[userId]!.push(timestamp);
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Parses a space-separated environment variable into an array.
|
|
32
|
-
*
|
|
33
|
-
* @param key - The environment variable name
|
|
34
|
-
* @returns The parsed array, or null if not set
|
|
35
|
-
*/
|
|
36
|
-
export function envList(key: string): string[] | null {
|
|
37
|
-
const v = process.env[key];
|
|
38
|
-
return v && v !== 'undefined' ? v.split(' ') : null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Formats a Date to ISO string without timezone suffix.
|
|
43
|
-
*
|
|
44
|
-
* @param date - The date to format
|
|
45
|
-
* @returns ISO-formatted string without trailing Z
|
|
46
|
-
*/
|
|
47
|
-
export function formatDate(date: Date): string {
|
|
48
|
-
return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().replace('Z', '');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Generates a random IPv4 address.
|
|
53
|
-
*
|
|
54
|
-
* @returns A string in the format "X.X.X.X"
|
|
55
|
-
*/
|
|
56
|
-
export function genIP(): string {
|
|
57
|
-
return `${randomNumber(0, 255)}.${randomNumber(0, 255)}.${randomNumber(0, 255)}.${randomNumber(0, 255)}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Returns a random element from an array.
|
|
62
|
-
*
|
|
63
|
-
* @param arr - The source array
|
|
64
|
-
* @returns A random element
|
|
65
|
-
*/
|
|
66
|
-
export function random<T>(arr: T[]): T {
|
|
67
|
-
return arr[Math.floor(Math.random() * arr.length)]!;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Returns a random integer between min and max (inclusive).
|
|
72
|
-
*
|
|
73
|
-
* @param min - Lower bound
|
|
74
|
-
* @param max - Upper bound
|
|
75
|
-
* @returns A random integer in [min, max]
|
|
76
|
-
*/
|
|
77
|
-
export function randomNumber(min: number, max: number): number {
|
|
78
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Checks if a version string is at least the given minimum.
|
|
83
|
-
*
|
|
84
|
-
* @param version - Version string (e.g. "v4")
|
|
85
|
-
* @param min - Minimum version number
|
|
86
|
-
* @returns true if the version is >= min
|
|
87
|
-
*/
|
|
88
|
-
export function since(version: string, min: number): boolean {
|
|
89
|
-
return parseInt(version.replace('v', '')) >= min;
|
|
90
|
-
}
|
package/src/utils/response.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { Response } from 'express';
|
|
2
|
-
|
|
3
|
-
import { DOCS_URL, STATUS_MESSAGES } from '../constants.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Sends a standardized JSON error response.
|
|
7
|
-
*
|
|
8
|
-
* @param res - Express response object
|
|
9
|
-
* @param status - HTTP status code
|
|
10
|
-
* @param message - Error description
|
|
11
|
-
* @param docPath - Optional documentation path appended to the base URL
|
|
12
|
-
*/
|
|
13
|
-
export function error(res: Response, status: number, message: string, docPath?: string): void {
|
|
14
|
-
const body = {
|
|
15
|
-
message: STATUS_MESSAGES[status] ?? 'Error',
|
|
16
|
-
error: message,
|
|
17
|
-
documentation: docPath ? `${DOCS_URL}/${docPath}` : DOCS_URL,
|
|
18
|
-
status: String(status),
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
if (typeof res.jsonResponse === 'function') {
|
|
22
|
-
res.status(status).jsonResponse(body);
|
|
23
|
-
} else {
|
|
24
|
-
res.status(status)
|
|
25
|
-
.setHeader('Content-Type', 'application/json')
|
|
26
|
-
.send(JSON.stringify(body, null, 2));
|
|
27
|
-
}
|
|
28
|
-
}
|