Skip to content

Commit 09e23f0

Browse files
committed
use cloudfront KV store to handle redirect mechanism instead of dynamo
1 parent e043830 commit 09e23f0

File tree

10 files changed

+1536
-282
lines changed

10 files changed

+1536
-282
lines changed

cloudformation/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ Resources:
164164
Variables:
165165
RunEnvironment: !Ref RunEnvironment
166166
EntraRoleArn: !GetAtt AppSecurityRoles.Outputs.EntraFunctionRoleArn
167+
LinkryKvArn: !GetAtt LinkryRecordsCloudfrontStore.Arn
167168
VpcConfig:
168169
Ipv6AllowedForDualStack: !If [ShouldAttachVpc, True, !Ref AWS::NoValue]
169170
SecurityGroupIds:
@@ -898,7 +899,7 @@ Resources:
898899
async function handler(event) {
899900
const request = event.request;
900901
const path = request.uri.replace(/^\/+/, '');
901-
let redirectUrl = "https://acm.illinois.edu";
902+
let redirectUrl = "https://acm.illinois.edu/404";
902903
try {
903904
const value = await kvs.get(path);
904905
if (value) {
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import {
2+
CloudFrontKeyValueStoreClient,
3+
ConflictException,
4+
DeleteKeyCommand,
5+
DescribeKeyValueStoreCommand,
6+
GetKeyCommand,
7+
PutKeyCommand,
8+
} from "@aws-sdk/client-cloudfront-keyvaluestore";
9+
import { environmentConfig } from "common/config.js";
10+
import {
11+
DatabaseDeleteError,
12+
DatabaseFetchError,
13+
DatabaseInsertError,
14+
} from "common/errors/index.js";
15+
import { RunEnvironment } from "common/roles.js";
16+
import "@aws-sdk/signature-v4-crt";
17+
18+
const INITIAL_CONFLICT_WAIT_PERIOD = 150;
19+
const CONFLICT_NUM_RETRIES = 3;
20+
21+
function sleep(ms: number) {
22+
return new Promise((resolve) => setTimeout(resolve, ms));
23+
}
24+
25+
export const setKey = async ({
26+
key,
27+
value,
28+
arn,
29+
kvsClient,
30+
}: {
31+
key: string;
32+
value: string;
33+
arn: string;
34+
kvsClient: CloudFrontKeyValueStoreClient;
35+
}) => {
36+
let numRetries = 0;
37+
let currentWaitPeriod = INITIAL_CONFLICT_WAIT_PERIOD;
38+
while (numRetries < CONFLICT_NUM_RETRIES) {
39+
const command = new DescribeKeyValueStoreCommand({ KvsARN: arn });
40+
const response = await kvsClient.send(command);
41+
const etag = response.ETag;
42+
const putCommand = new PutKeyCommand({
43+
IfMatch: etag,
44+
Key: key,
45+
Value: value,
46+
KvsARN: arn,
47+
});
48+
try {
49+
await kvsClient.send(putCommand);
50+
return;
51+
} catch (e) {
52+
if (e instanceof ConflictException) {
53+
numRetries++;
54+
await sleep(currentWaitPeriod);
55+
currentWaitPeriod *= 2;
56+
continue;
57+
} else {
58+
throw e;
59+
}
60+
}
61+
}
62+
throw new DatabaseInsertError({
63+
message: "Failed to save redirect to Cloudfront KV store.",
64+
});
65+
};
66+
67+
export const deleteKey = async ({
68+
key,
69+
arn,
70+
kvsClient,
71+
}: {
72+
key: string;
73+
arn: string;
74+
kvsClient: CloudFrontKeyValueStoreClient;
75+
}) => {
76+
let numRetries = 0;
77+
let currentWaitPeriod = INITIAL_CONFLICT_WAIT_PERIOD;
78+
while (numRetries < CONFLICT_NUM_RETRIES) {
79+
const command = new DescribeKeyValueStoreCommand({ KvsARN: arn });
80+
const response = await kvsClient.send(command);
81+
const etag = response.ETag;
82+
const putCommand = new DeleteKeyCommand({
83+
IfMatch: etag,
84+
Key: key,
85+
KvsARN: arn,
86+
});
87+
try {
88+
await kvsClient.send(putCommand);
89+
return;
90+
} catch (e) {
91+
if (e instanceof ConflictException) {
92+
numRetries++;
93+
await sleep(currentWaitPeriod);
94+
currentWaitPeriod *= 2;
95+
continue;
96+
} else {
97+
throw e;
98+
}
99+
}
100+
}
101+
throw new DatabaseDeleteError({
102+
message: "Failed to save delete to Cloudfront KV store.",
103+
});
104+
};
105+
106+
export const getKey = async ({
107+
key,
108+
arn,
109+
kvsClient,
110+
}: {
111+
key: string;
112+
arn: string;
113+
kvsClient: CloudFrontKeyValueStoreClient;
114+
}) => {
115+
let numRetries = 0;
116+
let currentWaitPeriod = INITIAL_CONFLICT_WAIT_PERIOD;
117+
while (numRetries < CONFLICT_NUM_RETRIES) {
118+
const getCommand = new GetKeyCommand({
119+
Key: key,
120+
KvsARN: arn,
121+
});
122+
try {
123+
const response = await kvsClient.send(getCommand);
124+
return response.Value;
125+
} catch (e) {
126+
if (e instanceof ConflictException) {
127+
numRetries++;
128+
await sleep(currentWaitPeriod);
129+
currentWaitPeriod *= 2;
130+
continue;
131+
} else {
132+
throw e;
133+
}
134+
}
135+
}
136+
throw new DatabaseFetchError({
137+
message: "Failed to retrieve value from Cloudfront KV store.",
138+
});
139+
};
140+
141+
export const getLinkryKvArn = async (runEnvironment: RunEnvironment) => {
142+
if (process.env.LinkryKvArn) {
143+
return process.env.LinkryKvArn;
144+
}
145+
if (environmentConfig[runEnvironment].LinkryCloudfrontKvArn) {
146+
return environmentConfig[runEnvironment].LinkryCloudfrontKvArn;
147+
}
148+
throw new DatabaseInsertError({
149+
message: "Could not find the Cloudfront Key-Value store ARN",
150+
});
151+
};

src/api/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ async function init(prettyPrint: boolean = false) {
6363
const customDomainBaseMappers: Record<string, string> = {
6464
"ical.acm.illinois.edu": `/api/v1/ical${url}`,
6565
"ical.aws.qa.acmuiuc.org": `/api/v1/ical${url}`,
66-
"go.acm.illinois.edu": `/api/v1/linkry/redir${url}`,
67-
"go.aws.qa.acmuiuc.org": `/api/v1/linkry/redir${url}`,
6866
};
6967
if (hostname in customDomainBaseMappers) {
7068
return customDomainBaseMappers[hostname];

src/api/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
"prettier:write": "prettier --write *.ts **/*.ts"
1616
},
1717
"dependencies": {
18+
"@aws-sdk/client-cloudfront-keyvaluestore": "^3.787.0",
1819
"@aws-sdk/client-dynamodb": "^3.624.0",
1920
"@aws-sdk/client-secrets-manager": "^3.624.0",
2021
"@aws-sdk/client-ses": "^3.734.0",
2122
"@aws-sdk/client-sqs": "^3.738.0",
2223
"@aws-sdk/client-sts": "^3.758.0",
24+
"@aws-sdk/signature-v4-crt": "^3.787.0",
2325
"@aws-sdk/util-dynamodb": "^3.624.0",
2426
"@azure/msal-node": "^2.16.1",
2527
"@fastify/auth": "^5.0.1",

0 commit comments

Comments
 (0)