-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
171 lines (160 loc) · 4.48 KB
/
index.js
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
const {
DynamoDBClient,
GetItemCommand,
PutItemCommand,
} = require("@aws-sdk/client-dynamodb");
const { urlAlphabet, customAlphabet } = require("nanoid");
const dynamodb_region = "us-east-1";
const dynamodb_table = "snipps";
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
const handleRequest = async (request) => {
const url = new URL(request.url);
if (request.method === "OPTIONS") {
return handleOptions(request);
} else if (request.method === "GET") {
return handleFetch(request);
} else if (request.method == "POST" && url.pathname === "/") {
return handleCreate(request);
} else {
return new Response("Not Found", {
headers: { "content-type": "text/plain" },
status: 404,
});
}
};
function handleOptions(request) {
if (
request.headers.get("Origin") !== null &&
request.headers.get("Access-Control-Request-Method") !== null &&
request.headers.get("Access-Control-Request-Headers") !== null
) {
return new Response(null, {
headers: corsHeaders,
});
} else {
return new Response(null, {
headers: {
Allow: "GET, HEAD, POST, PUT, OPTIONS",
},
});
}
}
const handleFetch = async (request) => {
const url = new URL(request.url);
const path = url.pathname.slice(1).match("^[a-zA-Z0-9-_]+$");
if (!path) return fetch(request);
const key = path[0];
const value = await get(key);
if (!value)
return new Response("Not Found", {
headers: { "content-type": "text/plain" },
status: 404,
});
return Response.redirect(value, 301);
};
const handleCreate = async (request) => {
let data = await request.json();
if (!data.value)
return new Response(
JSON.stringify({ error: "Field 'value' is required" }),
{
headers: { "content-type": "application/json" },
status: 400,
}
);
const value = data.value;
if (!isValidURI(value))
return new Response(
JSON.stringify({ error: "Field 'value' is not a valid URI" }),
{
headers: { "content-type": "application/json" },
status: 400,
}
);
if (data.key && !/^[a-zA-Z0-9-_]+$/.test(data.key)) {
return new Response(
JSON.stringify({
error:
"Invalid characters in field 'key'. Valid characters: A-Z,a-z,0-9,-,_",
}),
{
headers: { "content-type": "application/json" },
status: 400,
}
);
}
const key = data.key ? data.key : customAlphabet(urlAlphabet, 12)();
let ttl = 157680000;
if (data.ttl && !!parseInt(data.ttl)) ttl = Math.min(parseInt(data.ttl), ttl);
try {
await put(key, value, ttl);
return new Response(JSON.stringify({ key: key, value: value }), {
headers: { "content-type": "application/json" },
status: 201,
});
} catch (error) {
if (error.message === "The conditional request failed") {
return new Response(JSON.stringify({ error: "Key Already Exists" }), {
headers: { "content-type": "application/json" },
status: 409,
});
}
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
headers: { "content-type": "application/json" },
status: 500,
});
}
};
const put = async (key, value, ttl) => {
const client = new DynamoDBClient({
region: dynamodb_region,
credentialDefaultProvider: credentials,
});
const record = new PutItemCommand({
TableName: dynamodb_table,
Item: {
key: { S: key },
value: { S: value },
ttl: { N: Math.floor(new Date().valueOf() / 1000 + ttl).toString() },
},
ConditionExpression: "attribute_not_exists(#k)",
ExpressionAttributeNames: { "#k": "key" },
});
await client.send(record);
};
const get = async (key) => {
const client = new DynamoDBClient({
region: dynamodb_region,
credentialDefaultProvider: credentials,
});
const record = new GetItemCommand({
TableName: dynamodb_table,
Key: { key: { S: key } },
});
const results = await client.send(record);
if (results.Item) {
return results.Item["value"]["S"];
}
return undefined;
};
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, HEAD, POST, PUT, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
};
const isValidURI = (value) => {
try {
new URL(value);
return true;
} catch (err) {
return false;
}
};
const credentials = () => {
return {
accessKeyId: AWS_ACCESS_KEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
};
};