-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
102 lines (84 loc) · 3.13 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { serve } from 'https://deno.land/[email protected]/http/server.ts';
import { connect } from 'https://deno.land/x/[email protected]/mod.ts';
import { allowedCommands } from './commands.ts';
const config = {
redis: {
hostname: Deno.env.get('REDIS_HOST') || 'localhost',
port: Number(Deno.env.get('REDIS_PORT')) || 6379
},
server: {
port: Number(Deno.env.get('PORT')) || 3000,
hostname: Deno.env.get('HOST') || '0.0.0.0'
}
};
const redis = await connect(config.redis);
console.log(`Connected to redis at ${config.redis.hostname}:${config.redis.port}`);
class JSONResponse<T> extends Response {
constructor(body: T, init?: ResponseInit) {
super(JSON.stringify(body), { headers: { 'Content-Type': 'application/json' }, ...init });
}
}
class BadRequestResponse extends JSONResponse<{ error: true; result: string }> {
constructor(result: string) {
super({ error: true, result }, { status: 400 });
}
}
const responses = {
ok: new JSONResponse({ error: false, result: 'ok' }),
notFound: new JSONResponse({ error: true, result: 'Not found' }, { status: 404 }),
commands: new JSONResponse(allowedCommands)
};
async function handleRequest(request: Request): Promise<Response> {
const { pathname } = new URL(request.url);
switch (request.method) {
case 'GET':
switch (pathname) {
case '/health':
return responses.ok.clone();
case '/commands': {
return responses.commands.clone();
}
}
break;
case 'POST':
switch (pathname) {
case '/command': {
// Ensure request is json
if (!request.headers.get('Content-Type')?.includes('application/json')) {
return new BadRequestResponse('Request must be json');
}
// Ensure command is present
const body = await request.json();
if (!body.command) {
return new BadRequestResponse('Command must be present');
}
// Ensure command is a string
if (typeof body.command !== 'string') {
return new BadRequestResponse('Command must be a string');
}
const cmd = (body.command as string).trim();
// Ensure command is not empty
if (cmd.length === 0) {
return new BadRequestResponse('Command must not be empty');
}
// Ensure command is not too long
if (cmd.length > 1024) {
return new BadRequestResponse('Command must not be longer than 1024 characters');
}
const [name, ...args] = cmd.split(' ');
// Ensure command is allowed
if (!allowedCommands.includes(name.toLowerCase())) {
console.warn(`Command ${name} is not allowed`);
return new BadRequestResponse(`Command "${name}" is not allowed`);
}
return redis
.sendCommand(name, ...args)
.then((result) => new JSONResponse({ error: false, result: result.value() }))
.catch((err) => new JSONResponse({ error: true, result: err.message }));
}
}
break;
}
return responses.notFound.clone();
}
serve(handleRequest, config.server);