@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/app.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
|
|
3
|
-
import { env } from './config/env.js';
|
|
4
|
-
import { setupCors } from './middleware/cors.js';
|
|
5
|
-
import { errorHandler } from './middleware/error.js';
|
|
6
|
-
import { jsonResponseMiddleware } from './middleware/json.js';
|
|
7
|
-
import { loggerMiddleware } from './middleware/logger.js';
|
|
8
|
-
import { rateLimitMiddleware } from './middleware/ratelimit.js';
|
|
9
|
-
import { endpointCheckMiddleware, versionCheckMiddleware } from './middleware/version.js';
|
|
10
|
-
import deleteRoutes from './routes/delete.js';
|
|
11
|
-
import getRoutes from './routes/get.js';
|
|
12
|
-
import indexRoutes from './routes/index.js';
|
|
13
|
-
import patchRoutes from './routes/patch.js';
|
|
14
|
-
import postRoutes from './routes/post.js';
|
|
15
|
-
|
|
16
|
-
const app = express();
|
|
17
|
-
|
|
18
|
-
// Setup CORS, body parsing, static files
|
|
19
|
-
setupCors(app);
|
|
20
|
-
|
|
21
|
-
// Custom JSON response
|
|
22
|
-
app.use(jsonResponseMiddleware);
|
|
23
|
-
|
|
24
|
-
// Rate limiting
|
|
25
|
-
app.use(rateLimitMiddleware);
|
|
26
|
-
|
|
27
|
-
// Request logging
|
|
28
|
-
app.use(loggerMiddleware);
|
|
29
|
-
|
|
30
|
-
// Error handling
|
|
31
|
-
app.use(errorHandler);
|
|
32
|
-
|
|
33
|
-
// Root routes (before version check)
|
|
34
|
-
app.use(indexRoutes);
|
|
35
|
-
|
|
36
|
-
// Version & endpoint validation
|
|
37
|
-
app.use('/:version', versionCheckMiddleware);
|
|
38
|
-
app.use('/:version/:endpoint', endpointCheckMiddleware);
|
|
39
|
-
|
|
40
|
-
// Versioned routes
|
|
41
|
-
app.use(getRoutes);
|
|
42
|
-
app.use(postRoutes);
|
|
43
|
-
app.use(patchRoutes);
|
|
44
|
-
app.use(deleteRoutes);
|
|
45
|
-
|
|
46
|
-
// Start server
|
|
47
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
48
|
-
app.listen(env.PORT, () =>
|
|
49
|
-
console.log(`API is running on\n - http://127.0.0.1:${env.PORT}\n - http://localhost:${env.PORT}`),
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default app;
|
package/src/config/env.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import dotenv from 'dotenv';
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_PORT } from '../constants.js';
|
|
4
|
-
|
|
5
|
-
dotenv.config();
|
|
6
|
-
|
|
7
|
-
function envList(key: string): string[] | null {
|
|
8
|
-
const v = process.env[key];
|
|
9
|
-
return v && v !== 'undefined' ? v.split(' ') : null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function envNumber(key: string, fallback: number): number {
|
|
13
|
-
const v = parseInt(process.env[key] ?? '', 10);
|
|
14
|
-
return isNaN(v) ? fallback : v;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const env = {
|
|
18
|
-
PORT: envNumber('PORT', DEFAULT_PORT),
|
|
19
|
-
GITHUB_TOKEN: process.env.GITHUB_TOKEN ?? '',
|
|
20
|
-
|
|
21
|
-
DEFAULT_LIMIT: envNumber('DEFAULT_LIMIT', 2000),
|
|
22
|
-
ADVANCED_LIMIT: envNumber('ADVANCED_LIMIT', 3500),
|
|
23
|
-
PRO_LIMIT: envNumber('PRO_LIMIT', 6000),
|
|
24
|
-
BUSINESS_LIMIT: envNumber('BUSINESS_LIMIT', 10000),
|
|
25
|
-
GLOBAL_LIMIT: envNumber('GLOBAL_LIMIT', 50000),
|
|
26
|
-
|
|
27
|
-
DEFAULT_BURST: envNumber('DEFAULT_BURST', 50),
|
|
28
|
-
ADVANCED_BURST: envNumber('ADVANCED_BURST', 80),
|
|
29
|
-
PRO_BURST: envNumber('PRO_BURST', 120),
|
|
30
|
-
BUSINESS_BURST: envNumber('BUSINESS_BURST', 200),
|
|
31
|
-
|
|
32
|
-
BUSINESS_TOKEN_LIST: envList('BUSINESS_TOKEN_LIST') ?? [],
|
|
33
|
-
PRO_TOKEN_LIST: envList('PRO_TOKEN_LIST') ?? [],
|
|
34
|
-
ADVANCED_TOKEN_LIST: envList('ADVANCED_TOKEN_LIST') ?? [],
|
|
35
|
-
|
|
36
|
-
// Website endpoint env vars
|
|
37
|
-
G_2048: process.env.G_2048,
|
|
38
|
-
API: process.env.API,
|
|
39
|
-
CDN: process.env.CDN,
|
|
40
|
-
COOP_API: process.env.COOP_API,
|
|
41
|
-
COOP_STATUS: process.env.COOP_STATUS,
|
|
42
|
-
CHAT: process.env.CHAT,
|
|
43
|
-
DIGIT: process.env.DIGIT,
|
|
44
|
-
DOC_COOPBOT: process.env.DOC_COOPBOT,
|
|
45
|
-
DOCS: process.env.DOCS,
|
|
46
|
-
DONUT: process.env.DONUT,
|
|
47
|
-
DRAWIO_PLUGIN: process.env.DRAWIO_PLUGIN,
|
|
48
|
-
FLOWERS: process.env.FLOWERS,
|
|
49
|
-
GEMSYNC: process.env.GEMSYNC,
|
|
50
|
-
GFT: process.env.GFT,
|
|
51
|
-
GITSITE: process.env.GITSITE,
|
|
52
|
-
LEBONCHAR: process.env.LEBONCHAR,
|
|
53
|
-
LOGGER: process.env.LOGGER,
|
|
54
|
-
LOGS: process.env.LOGS,
|
|
55
|
-
LOGVAULT: process.env.LOGVAULT,
|
|
56
|
-
LYAH: process.env.LYAH,
|
|
57
|
-
MINIFY: process.env.MINIFY,
|
|
58
|
-
MN: process.env.MN,
|
|
59
|
-
MONITORING: process.env.MONITORING,
|
|
60
|
-
MORPION: process.env.MORPION,
|
|
61
|
-
NITROGEN: process.env.NITROGEN,
|
|
62
|
-
OLD_DATABASE: process.env.OLD_DATABASE,
|
|
63
|
-
PASSWORD: process.env.PASSWORD,
|
|
64
|
-
PHP: process.env.PHP,
|
|
65
|
-
PLANNING: process.env.PLANNING,
|
|
66
|
-
PING: process.env.PING,
|
|
67
|
-
PORTFOLIO: process.env.PORTFOLIO,
|
|
68
|
-
PYTHON_API: process.env.PYTHON_API,
|
|
69
|
-
README: process.env.README,
|
|
70
|
-
TIMESTAMP: process.env.TIMESTAMP,
|
|
71
|
-
TERMINAL: process.env.TERMINAL,
|
|
72
|
-
VALENTINE: process.env.VALENTINE,
|
|
73
|
-
WRKIT: process.env.WRKIT,
|
|
74
|
-
ZPKI: process.env.ZPKI,
|
|
75
|
-
|
|
76
|
-
PATCH: envList('PATCH'),
|
|
77
|
-
RECENT: envList('RECENT'),
|
|
78
|
-
NEW: envList('NEW'),
|
|
79
|
-
DOMAINS: envList('DOMAINS'),
|
|
80
|
-
|
|
81
|
-
STATS1: process.env.STATS1,
|
|
82
|
-
STATS2: process.env.STATS2,
|
|
83
|
-
STATS3: process.env.STATS3,
|
|
84
|
-
STATS4: process.env.STATS4,
|
|
85
|
-
TAG: process.env.TAG,
|
|
86
|
-
ACTIVE: process.env.ACTIVE === 'true',
|
|
87
|
-
};
|
package/src/config/plans.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { env } from './env.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Rate limit plans configuration.
|
|
5
|
-
*
|
|
6
|
-
* hourly — max requests per hour (matches pricing page)
|
|
7
|
-
* burst — max requests per 10-second window
|
|
8
|
-
* tokens — list of bearer tokens for this tier
|
|
9
|
-
*
|
|
10
|
-
* To adjust limits, edit the values below or override via .env:
|
|
11
|
-
* DEFAULT_LIMIT, ADVANCED_LIMIT, PRO_LIMIT, BUSINESS_LIMIT
|
|
12
|
-
* DEFAULT_BURST, ADVANCED_BURST, PRO_BURST, BUSINESS_BURST
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export interface Plan {
|
|
16
|
-
hourly: number;
|
|
17
|
-
burst: number;
|
|
18
|
-
tokens: string[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const plans: Record<string, Plan> = {
|
|
22
|
-
business: {
|
|
23
|
-
hourly: env.BUSINESS_LIMIT,
|
|
24
|
-
burst: env.BUSINESS_BURST,
|
|
25
|
-
tokens: env.BUSINESS_TOKEN_LIST,
|
|
26
|
-
},
|
|
27
|
-
pro: {
|
|
28
|
-
hourly: env.PRO_LIMIT,
|
|
29
|
-
burst: env.PRO_BURST,
|
|
30
|
-
tokens: env.PRO_TOKEN_LIST,
|
|
31
|
-
},
|
|
32
|
-
advanced: {
|
|
33
|
-
hourly: env.ADVANCED_LIMIT,
|
|
34
|
-
burst: env.ADVANCED_BURST,
|
|
35
|
-
tokens: env.ADVANCED_TOKEN_LIST,
|
|
36
|
-
},
|
|
37
|
-
default: {
|
|
38
|
-
hourly: env.DEFAULT_LIMIT,
|
|
39
|
-
burst: env.DEFAULT_BURST,
|
|
40
|
-
tokens: [],
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export const globalLimit = env.GLOBAL_LIMIT; // 50 000/h
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Returns the plan matching a bearer token, or 'default'.
|
|
48
|
-
* Returns null if the token is provided but invalid.
|
|
49
|
-
*/
|
|
50
|
-
export function getPlan(token: string): { name: string; plan: Plan } | null {
|
|
51
|
-
if (!token) return { name: 'default', plan: plans.default! };
|
|
52
|
-
if (token === 'undefined') return null;
|
|
53
|
-
|
|
54
|
-
for (const [name, plan] of Object.entries(plans)) {
|
|
55
|
-
if (name === 'default') continue;
|
|
56
|
-
if (plan.tokens.includes('undefined')) continue;
|
|
57
|
-
if (plan.tokens.includes(token)) return { name, plan };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return null;
|
|
61
|
-
}
|
package/src/config/versions.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import * as apiv3 from '../modules/v3.js';
|
|
2
|
-
import * as apiv4 from '../modules/v4.js';
|
|
3
|
-
|
|
4
|
-
export interface Endpoint {
|
|
5
|
-
name: string;
|
|
6
|
-
path?: string;
|
|
7
|
-
children?: Record<string, string>;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface VersionConfig {
|
|
11
|
-
endpoints: {
|
|
12
|
-
get: Endpoint[];
|
|
13
|
-
post: Endpoint[];
|
|
14
|
-
patch?: Endpoint[];
|
|
15
|
-
delete?: Endpoint[];
|
|
16
|
-
};
|
|
17
|
-
modules: typeof apiv3 | typeof apiv4;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Merges a base list of endpoints with additions, deduplicating by `name`.
|
|
22
|
-
* If an addition has the same name as a base entry, it overrides it.
|
|
23
|
-
*/
|
|
24
|
-
const merge = (base: Endpoint[], additions: Endpoint[]): Endpoint[] => {
|
|
25
|
-
const map = new Map(base.map((e) => [e.name, e]));
|
|
26
|
-
for (const e of additions) map.set(e.name, e);
|
|
27
|
-
return [...map.values()];
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const v1 = {
|
|
31
|
-
get: [
|
|
32
|
-
{ name: 'algorithms', path: '/algorithms?method={algorithm}&value={value}(&value2={value2})' },
|
|
33
|
-
{ name: 'captcha', path: '/captcha?text={text}' },
|
|
34
|
-
{ name: 'color', path: '/color' },
|
|
35
|
-
{ name: 'convert', path: '/convert?value={value}&from={unit}&to={unit}' },
|
|
36
|
-
{ name: 'domain', path: '/domain' },
|
|
37
|
-
{ name: 'infos', path: '/infos' },
|
|
38
|
-
{ name: 'personal', path: '/personal' },
|
|
39
|
-
{ name: 'qrcode', path: '/qrcode?url={URL}' },
|
|
40
|
-
{ name: 'username', path: '/username' },
|
|
41
|
-
{ name: 'website', path: '/website' },
|
|
42
|
-
],
|
|
43
|
-
post: [{ name: 'token', path: '/token' }],
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const v2 = {
|
|
47
|
-
get: merge(v1.get, [{ name: 'chat', path: '/chat' }]),
|
|
48
|
-
post: merge(v1.post, [
|
|
49
|
-
{
|
|
50
|
-
name: 'chat',
|
|
51
|
-
children: {
|
|
52
|
-
chat: '/chat',
|
|
53
|
-
private: '/chat/private',
|
|
54
|
-
} as Record<string, string>,
|
|
55
|
-
},
|
|
56
|
-
{ name: 'hash', path: '/hash' },
|
|
57
|
-
{
|
|
58
|
-
name: 'tic_tac_toe',
|
|
59
|
-
children: {
|
|
60
|
-
tic_tac_toe: '/tic-tac-toe',
|
|
61
|
-
fetch: '/tic-tac-toe/fetch',
|
|
62
|
-
list: '/tic-tac-toe/list',
|
|
63
|
-
} as Record<string, string>,
|
|
64
|
-
},
|
|
65
|
-
]),
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const v3 = {
|
|
69
|
-
get: merge(v2.get, [
|
|
70
|
-
{ name: 'levenshtein', path: '/levenshtein?str1={string}&str2={string}' },
|
|
71
|
-
{
|
|
72
|
-
name: 'time',
|
|
73
|
-
path: '/time(?type={type}&start={timestamp}&end={timestamp}&format={format}&timezone={timezone})',
|
|
74
|
-
},
|
|
75
|
-
]),
|
|
76
|
-
post: merge(v2.post, [{ name: 'hyperplanning', path: '/hyperplanning' }]),
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const v4 = {
|
|
80
|
-
get: merge(v3.get, [
|
|
81
|
-
{ name: 'address', path: '/address(&country={code}&count={n})' },
|
|
82
|
-
{ name: 'agent', path: '/agent(&ua={string})' },
|
|
83
|
-
{
|
|
84
|
-
name: 'captcha',
|
|
85
|
-
path: '/captcha(&text={text}&length={n}&width={px}&height={px}&noise={low|medium|high}&bg={hex}&color={hex})',
|
|
86
|
-
},
|
|
87
|
-
{ name: 'color', path: '/color(&hex={hex})' },
|
|
88
|
-
{ name: 'convert', path: '/convert?value={value}&from={unit}&to={unit}' },
|
|
89
|
-
{ name: 'dice', path: '/dice?roll={NdX+M}' },
|
|
90
|
-
{ name: 'encode', path: '/encode?method={method}&text={text}(&shift={shift})' },
|
|
91
|
-
{ name: 'geo', path: '/geo?lat1={lat}&lon1={lon}&lat2={lat}&lon2={lon}' },
|
|
92
|
-
{ name: 'headers', path: '/headers(&filter={header1,header2})' },
|
|
93
|
-
{ name: 'ip', path: '/ip(&address={ip})' },
|
|
94
|
-
{ name: 'palette', path: '/palette?color={#hex}&type={type}' },
|
|
95
|
-
{
|
|
96
|
-
name: 'password',
|
|
97
|
-
path: '/password(&type={random|passphrase}&length={n}&uppercase={bool}&lowercase={bool}&digits={bool}&symbols={bool}&exclude={chars}&count={n}&separator={char})',
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: 'placeholder',
|
|
101
|
-
path: '/placeholder?type={image|skeleton}&width={w}&height={h}(&bg={hex}&color={hex}&text={text}&rows={n}&avatar={bool})',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
name: 'qrcode',
|
|
105
|
-
path: '/qrcode?url={URL}(&size={px}&margin={n}&correction={L|M|Q|H}&dark={hex}&light={hex}&icon={URL}&iconSize={px}&iconPadding={px}&iconRadius={px}&format={png|base64})',
|
|
106
|
-
},
|
|
107
|
-
{ name: 'statistics', path: '/statistics?values={n1,n2,n3,...}' },
|
|
108
|
-
{ name: 'text', path: '/text?method={method}(&value={value}&type={type}&count={count}&lang={lang})' },
|
|
109
|
-
{ name: 'validate', path: '/validate?type={type}&value={value}' },
|
|
110
|
-
]),
|
|
111
|
-
post: [...v3.post],
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export const versions: Record<string, VersionConfig> = {
|
|
115
|
-
v1: { endpoints: v1, modules: apiv3 },
|
|
116
|
-
v2: { endpoints: v2, modules: apiv3 },
|
|
117
|
-
v3: { endpoints: v3, modules: apiv3 },
|
|
118
|
-
v4: { endpoints: v4, modules: apiv4 },
|
|
119
|
-
};
|
package/src/constants.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import pkg from '../package.json' with { type: 'json' };
|
|
2
|
-
|
|
3
|
-
export const APP_VERSION = pkg.version;
|
|
4
|
-
export const MAX_LOG_ENTRIES = 1000;
|
|
5
|
-
export const RATE_LIMIT_WINDOW = 10_000;
|
|
6
|
-
export const RATE_LIMIT_MAX = 50;
|
|
7
|
-
export const SESSION_TTL = 3_600_000;
|
|
8
|
-
export const GAME_CLEANUP_TTL = 600_000;
|
|
9
|
-
export const GITHUB_CACHE_TTL = 600_000;
|
|
10
|
-
export const MIN_TOKEN_LENGTH = 12;
|
|
11
|
-
export const MAX_TOKEN_LENGTH = 4096;
|
|
12
|
-
export const MAX_FACTORIAL = 170;
|
|
13
|
-
export const MAX_GCD_VALUE = 100_000;
|
|
14
|
-
export const MAX_PRIME_LIST = 10_000;
|
|
15
|
-
export const MAX_LEVENSHTEIN_LENGTH = 1000;
|
|
16
|
-
export const MAX_STRING_LENGTH = 1000;
|
|
17
|
-
export const DOCS_URL = 'https://docs.sylvain.sh';
|
|
18
|
-
export const DEFAULT_PORT = 3000;
|
|
19
|
-
|
|
20
|
-
export const STATUS_MESSAGES: Record<number, string> = {
|
|
21
|
-
400: 'Bad Request',
|
|
22
|
-
401: 'Unauthorized',
|
|
23
|
-
404: 'Not Found',
|
|
24
|
-
405: 'Method Not Allowed',
|
|
25
|
-
413: 'Payload Too Large',
|
|
26
|
-
429: 'Too Many Requests',
|
|
27
|
-
500: 'Internal Server Error',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const START_TIME = Date.now();
|
|
31
|
-
|
|
32
|
-
export const ROMAN_VALUES: [number, string][] = [
|
|
33
|
-
[1000, 'M'],
|
|
34
|
-
[900, 'CM'],
|
|
35
|
-
[500, 'D'],
|
|
36
|
-
[400, 'CD'],
|
|
37
|
-
[100, 'C'],
|
|
38
|
-
[90, 'XC'],
|
|
39
|
-
[50, 'L'],
|
|
40
|
-
[40, 'XL'],
|
|
41
|
-
[10, 'X'],
|
|
42
|
-
[9, 'IX'],
|
|
43
|
-
[5, 'V'],
|
|
44
|
-
[4, 'IV'],
|
|
45
|
-
[1, 'I'],
|
|
46
|
-
];
|
package/src/favicon.ico
DELETED
|
Binary file
|
package/src/middleware/cors.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import cors from 'cors';
|
|
2
|
-
import express, { type Express } from 'express';
|
|
3
|
-
import { dirname, join } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = dirname(__filename);
|
|
8
|
-
|
|
9
|
-
export function setupCors(app: Express): void {
|
|
10
|
-
app.set('trust proxy', 1);
|
|
11
|
-
app.use(cors({ methods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'] }));
|
|
12
|
-
app.use(express.urlencoded({ extended: true, limit: '10kb' }));
|
|
13
|
-
app.use(express.json({ limit: '10kb' }));
|
|
14
|
-
|
|
15
|
-
app.use((_req, res, next) => {
|
|
16
|
-
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
17
|
-
res.setHeader('X-Frame-Options', 'DENY');
|
|
18
|
-
next();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
app.use('/favicon.ico', express.static(join(__dirname, '..', '..', 'src', 'favicon.ico')));
|
|
22
|
-
app.use('/robots.txt', express.static(join(__dirname, '..', '..', 'robots.txt')));
|
|
23
|
-
}
|
package/src/middleware/error.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
-
|
|
3
|
-
import { error } from '../utils/response.js';
|
|
4
|
-
|
|
5
|
-
export function errorHandler(
|
|
6
|
-
err: Error & { status?: number },
|
|
7
|
-
_req: Request,
|
|
8
|
-
res: Response,
|
|
9
|
-
_next: NextFunction,
|
|
10
|
-
): void {
|
|
11
|
-
console.error(err.stack);
|
|
12
|
-
|
|
13
|
-
const status = err.status && err.status >= 400 && err.status < 600 ? err.status : 500;
|
|
14
|
-
const sensitive = status === 500 && /\/[a-z]|\\[a-z]|ECONNREFUSED|ENOENT|EPERM|at\s+\w/i.test(err.message);
|
|
15
|
-
error(res, status, sensitive ? 'An unexpected error occurred.' : err.message);
|
|
16
|
-
}
|
package/src/middleware/json.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
-
|
|
3
|
-
export function jsonResponseMiddleware(_req: Request, res: Response, next: NextFunction): void {
|
|
4
|
-
res.setHeader('Content-Type', 'application/json');
|
|
5
|
-
res.jsonResponse = (data: unknown) => {
|
|
6
|
-
res.send(JSON.stringify(data, null, 2));
|
|
7
|
-
};
|
|
8
|
-
next();
|
|
9
|
-
}
|
package/src/middleware/logger.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { createLogger } from '@20syldev/logger.ts';
|
|
2
|
-
import type { NextFunction, Request, Response } from 'express';
|
|
3
|
-
|
|
4
|
-
import { MAX_LOG_ENTRIES } from '../constants.js';
|
|
5
|
-
|
|
6
|
-
export const logger = createLogger({
|
|
7
|
-
maxEntries: MAX_LOG_ENTRIES,
|
|
8
|
-
console: true,
|
|
9
|
-
theme: 'colored',
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export function loggerMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
13
|
-
if (req.method === 'HEAD') {
|
|
14
|
-
next();
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
if (req.originalUrl === '/logs') {
|
|
18
|
-
next();
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const startTime = Date.now();
|
|
23
|
-
const platform = (req.headers['sec-ch-ua-platform'] as string)?.replace(/"/g, '');
|
|
24
|
-
|
|
25
|
-
res.on('finish', () => {
|
|
26
|
-
const status = res.statusCode === 304 ? 200 : res.statusCode;
|
|
27
|
-
const duration = `${Date.now() - startTime}ms`;
|
|
28
|
-
|
|
29
|
-
logger.log({ method: req.method, url: req.originalUrl, status, duration, platform });
|
|
30
|
-
});
|
|
31
|
-
next();
|
|
32
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
-
|
|
3
|
-
import { getPlan, globalLimit } from '../config/plans.js';
|
|
4
|
-
import { RATE_LIMIT_WINDOW, SESSION_TTL } from '../constants.js';
|
|
5
|
-
import { ipLimits } from '../storage/index.js';
|
|
6
|
-
import { error } from '../utils/response.js';
|
|
7
|
-
|
|
8
|
-
let requests = 0;
|
|
9
|
-
let resetTime = Date.now() + SESSION_TTL;
|
|
10
|
-
|
|
11
|
-
const burstTracker: Record<string, number[]> = {};
|
|
12
|
-
|
|
13
|
-
// Cleanup stale IPs every hour
|
|
14
|
-
setInterval(() => {
|
|
15
|
-
const currentHour = String(Math.floor(Date.now() / 3600000) % 24);
|
|
16
|
-
for (const ip of Object.keys(ipLimits)) {
|
|
17
|
-
const hours = Object.keys(ipLimits[ip]!);
|
|
18
|
-
if (hours.length === 0 || (hours.length === 1 && hours[0] !== currentHour)) {
|
|
19
|
-
delete ipLimits[ip];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const now = Date.now();
|
|
24
|
-
for (const ip of Object.keys(burstTracker)) {
|
|
25
|
-
burstTracker[ip] = burstTracker[ip]!.filter((t) => now - t < RATE_LIMIT_WINDOW);
|
|
26
|
-
if (burstTracker[ip]!.length === 0) delete burstTracker[ip];
|
|
27
|
-
}
|
|
28
|
-
}, SESSION_TTL);
|
|
29
|
-
|
|
30
|
-
export function rateLimitMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
31
|
-
const ip = req.ip || req.socket.remoteAddress || '';
|
|
32
|
-
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
33
|
-
|
|
34
|
-
const now = Date.now();
|
|
35
|
-
const minute = String(Math.floor(now / 60000) % 60);
|
|
36
|
-
const hour = String(Math.floor(now / 3600000) % 24);
|
|
37
|
-
|
|
38
|
-
const match = getPlan(token);
|
|
39
|
-
if (!match) {
|
|
40
|
-
error(res, 401, 'Invalid token.');
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const { plan } = match;
|
|
45
|
-
|
|
46
|
-
if (now > resetTime) {
|
|
47
|
-
requests = 0;
|
|
48
|
-
resetTime = now + SESSION_TTL;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (++requests > Math.max(globalLimit, plan.hourly)) {
|
|
52
|
-
error(res, 429, 'Global rate limit exceeded.');
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (['__proto__', 'constructor', 'prototype'].includes(ip)) {
|
|
57
|
-
error(res, 400, 'Invalid IP address.');
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Burst protection per tier
|
|
62
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
63
|
-
if (!burstTracker[ip]) burstTracker[ip] = [];
|
|
64
|
-
burstTracker[ip] = burstTracker[ip]!.filter((t) => now - t < RATE_LIMIT_WINDOW);
|
|
65
|
-
burstTracker[ip]!.push(now);
|
|
66
|
-
|
|
67
|
-
if (burstTracker[ip]!.length > plan.burst) {
|
|
68
|
-
error(res, 429, 'Too many requests, please slow down.');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Hourly rate limit per IP
|
|
74
|
-
if (!ipLimits[ip]) ipLimits[ip] = {};
|
|
75
|
-
if (!ipLimits[ip]![hour]) ipLimits[ip]![hour] = {};
|
|
76
|
-
ipLimits[ip]![hour]![minute] = (ipLimits[ip]![hour]![minute] ?? 0) + 1;
|
|
77
|
-
|
|
78
|
-
const hourTotal = Object.values(ipLimits[ip]![hour]!).reduce((sum, count) => sum + count, 0);
|
|
79
|
-
if (hourTotal > plan.hourly) {
|
|
80
|
-
error(res, 429, `You have exceeded the limit of ${plan.hourly} requests per hour.`);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
Object.keys(ipLimits[ip]!).forEach((h) => {
|
|
85
|
-
if (h != hour) delete ipLimits[ip]![h];
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
next();
|
|
89
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
-
|
|
3
|
-
import { versions } from '../config/versions.js';
|
|
4
|
-
import { error } from '../utils/response.js';
|
|
5
|
-
|
|
6
|
-
export function versionCheckMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
7
|
-
const version = req.params.version as string;
|
|
8
|
-
const latest = Object.keys(versions).pop()!;
|
|
9
|
-
|
|
10
|
-
req.version = version;
|
|
11
|
-
req.latest = latest;
|
|
12
|
-
|
|
13
|
-
if (!versions[version]) {
|
|
14
|
-
error(res, 404, `Invalid API version (${version}).`, latest);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
req.module = versions[version]!.modules;
|
|
19
|
-
|
|
20
|
-
if (!req.module) {
|
|
21
|
-
error(res, 404, `Module not found for version ${version}.`, latest);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
next();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function endpointCheckMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
29
|
-
const version = req.params.version as string;
|
|
30
|
-
const endpoint = req.params.endpoint as string;
|
|
31
|
-
|
|
32
|
-
req.version = version;
|
|
33
|
-
req.endpoint = endpoint;
|
|
34
|
-
|
|
35
|
-
if (!endpoint) {
|
|
36
|
-
error(res, 404, `Endpoint '${endpoint}' does not exist in ${req.version}.`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
next();
|
|
40
|
-
}
|