· 5 years ago · Aug 07, 2020, 02:12 AM
1import * as AWS from "aws-sdk";
2import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
3
4const createDOMPurify = require("dompurify");
5const { JSDOM } = require("jsdom");
6const window = new JSDOM("").window;
7const DOMPurify = createDOMPurify(window);
8
9// parseComment takes a comment submission and cleans the content so that
10// it can be used as acceptable html on a `link` page
11const parseComment = async (comment: string): Promise<string> => {
12 const config = {
13 FORBID_TAGS: ["style", "img", "video"],
14 FORBID_ATTR: ["style"],
15 };
16 const clean = DOMPurify.sanitize(comment, config);
17 // todo any links should be no follow
18 return clean;
19};
20
21exports.handler = async (
22 event: APIGatewayProxyEvent
23): Promise<APIGatewayProxyResult> => {
24 const url = event.queryStringParameters.url;
25 const comment = await parseComment(event.queryStringParameters.comment);
26 const timestamp = Date.now(); // TODO make timezone aware
27
28 AWS.config.update({ region: "REGION" });
29 var docClient = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" });
30 var params: AWS.DynamoDB.DocumentClient.UpdateItemInput = {
31 TableName: "ElotiURLComments",
32 Key: { URL: url },
33
34 // TODO UpdateExpression and ExpresionAttribureValues below should look more like this
35 // https://stackoverflow.com/questions/34951043/is-it-possible-to-combine-if-not-exists-and-list-append-in-update-item
36 // 'SET my_list2 = list_append(if_not_exists(my_list2, :empty_list), :my_value)'
37 // { ":my_value":[{"S":"test"}], ":empty_list":[] }
38 //
39 // Do you have any idea if this is subject to race-conditions? I'm
40 // looking over the AWS documentation, and they don't seem to have any
41 // locking/transactions (at least for the Python SDK). It certainly
42 // seems like this code could a: read the list from the DB, b: append
43 // the new value, and c: overwrite the old value, which could have
44 // been changed in the meantime
45 //
46 // I can't find a direct confirmation in the documentation, but I
47 // would expect single UpdateItem operation to be atomic, the closest
48 // thing I found in faq - "You can increment or decrement a numeric
49 // attribute in a row using a single API call. Similarly, you can
50 // atomically add or remove to sets, lists, or maps", see
51 // aws.amazon.com/dynamodb/faqs
52
53 UpdateExpression: "SET #c = list_append(#c, :vals)",
54 ExpressionAttributeNames: {
55 "#c": "comments",
56 },
57 ExpressionAttributeValues: {
58 ":vals": [
59 {
60 content: comment,
61 timestamp: timestamp,
62 isSpam: false, // TODO akismet
63 rawContent: event.queryStringParameters.comment,
64 },
65 ],
66 },
67 ReturnValues: "UPDATED_NEW",
68 };
69
70 try {
71 await docClient.update(params).promise();
72 return {
73 statusCode: 201,
74 body: "ok",
75 headers: {
76 Location: "https://eloti.net/link?url=" + encodeURI(url),
77 },
78 };
79 } catch (error) {
80 console.log(error);
81 return {
82 statusCode: 500,
83 body: error,
84 };
85 }
86};