magic-pocket-cli 0.2.0__py3-none-any.whl
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.
- magic_pocket_cli-0.2.0.dist-info/METADATA +14 -0
- magic_pocket_cli-0.2.0.dist-info/RECORD +65 -0
- magic_pocket_cli-0.2.0.dist-info/WHEEL +4 -0
- magic_pocket_cli-0.2.0.dist-info/entry_points.txt +2 -0
- pocket_cli/__init__.py +0 -0
- pocket_cli/cli/__init__.py +0 -0
- pocket_cli/cli/aws_auth.py +48 -0
- pocket_cli/cli/awscontainer_cli.py +328 -0
- pocket_cli/cli/cloudfront_cli.py +116 -0
- pocket_cli/cli/cloudfront_keys_cli.py +68 -0
- pocket_cli/cli/cloudfront_waf_cli.py +68 -0
- pocket_cli/cli/deploy_cli.py +274 -0
- pocket_cli/cli/destroy_cli.py +358 -0
- pocket_cli/cli/dsql_cli.py +60 -0
- pocket_cli/cli/main_cli.py +91 -0
- pocket_cli/cli/migrate_cli.py +148 -0
- pocket_cli/cli/neon_cli.py +97 -0
- pocket_cli/cli/permissions_cli.py +46 -0
- pocket_cli/cli/rds_cli.py +63 -0
- pocket_cli/cli/runtime_config_cli.py +185 -0
- pocket_cli/cli/s3_cli.py +69 -0
- pocket_cli/cli/status_cli.py +56 -0
- pocket_cli/cli/tidb_cli.py +73 -0
- pocket_cli/cli/vpc_cli.py +92 -0
- pocket_cli/cli/waf_cli.py +182 -0
- pocket_cli/django_cli.py +412 -0
- pocket_cli/mediator.py +220 -0
- pocket_cli/resources/__init__.py +0 -0
- pocket_cli/resources/aws/__init__.py +0 -0
- pocket_cli/resources/aws/builders/__init__.py +57 -0
- pocket_cli/resources/aws/builders/codebuild.py +363 -0
- pocket_cli/resources/aws/builders/depot.py +84 -0
- pocket_cli/resources/aws/builders/docker.py +34 -0
- pocket_cli/resources/aws/builders/dockerignore.py +44 -0
- pocket_cli/resources/aws/cloudformation.py +790 -0
- pocket_cli/resources/aws/ecr.py +145 -0
- pocket_cli/resources/aws/efs.py +138 -0
- pocket_cli/resources/aws/lambdahandler.py +182 -0
- pocket_cli/resources/aws/s3_utils.py +58 -0
- pocket_cli/resources/aws/state.py +74 -0
- pocket_cli/resources/awscontainer.py +265 -0
- pocket_cli/resources/cloudfront.py +491 -0
- pocket_cli/resources/cloudfront_acm.py +55 -0
- pocket_cli/resources/cloudfront_keys.py +81 -0
- pocket_cli/resources/cloudfront_waf.py +67 -0
- pocket_cli/resources/dsql.py +142 -0
- pocket_cli/resources/neon.py +353 -0
- pocket_cli/resources/rds.py +680 -0
- pocket_cli/resources/s3.py +307 -0
- pocket_cli/resources/tidb.py +298 -0
- pocket_cli/resources/upstash.py +152 -0
- pocket_cli/resources/vpc.py +67 -0
- pocket_cli/templates/cloudformation/awscontainer.yaml +516 -0
- pocket_cli/templates/cloudformation/cf_function_api_host.js +5 -0
- pocket_cli/templates/cloudformation/cf_function_spa_auth.js +28 -0
- pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +8 -0
- pocket_cli/templates/cloudformation/cloudfront.yaml +309 -0
- pocket_cli/templates/cloudformation/cloudfront_acm.yaml +43 -0
- pocket_cli/templates/cloudformation/cloudfront_keys.yaml +32 -0
- pocket_cli/templates/cloudformation/cloudfront_waf.yaml +97 -0
- pocket_cli/templates/cloudformation/vpc.yaml +213 -0
- pocket_cli/templates/init/django-dotenv.env +3 -0
- pocket_cli/templates/init/django-settings.py +140 -0
- pocket_cli/templates/init/pocket.Dockerfile +26 -0
- pocket_cli/templates/init/pocket_simple.toml +31 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
AWSTemplateFormatVersion: "2010-09-09"
|
|
2
|
+
Description: lambda configuration for webapp
|
|
3
|
+
|
|
4
|
+
Resources:
|
|
5
|
+
LambdaRole:
|
|
6
|
+
# This role must be created before the lambda function.
|
|
7
|
+
# https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html
|
|
8
|
+
# When you change Role, you must delete function and role.
|
|
9
|
+
# https://www.lastweekinaws.com/blog/the-sneaky-weakness-behind-aws-managed-kms-keys/
|
|
10
|
+
Type: AWS::IAM::Role
|
|
11
|
+
Properties:
|
|
12
|
+
AssumeRolePolicyDocument:
|
|
13
|
+
Statement:
|
|
14
|
+
- Effect: Allow
|
|
15
|
+
Principal:
|
|
16
|
+
Service: lambda.amazonaws.com
|
|
17
|
+
Action: "sts:AssumeRole"
|
|
18
|
+
ManagedPolicyArns:
|
|
19
|
+
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
|
|
20
|
+
# {% if vpc %}
|
|
21
|
+
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
|
|
22
|
+
# {% endif %}
|
|
23
|
+
# {% if use_ses %}
|
|
24
|
+
- arn:aws:iam::aws:policy/AmazonSESFullAccess
|
|
25
|
+
# {% endif %}
|
|
26
|
+
# {% if use_s3 %}
|
|
27
|
+
- arn:aws:iam::aws:policy/AmazonS3FullAccess
|
|
28
|
+
# {% endif %}
|
|
29
|
+
# {% if use_route53 %}
|
|
30
|
+
- arn:aws:iam::aws:policy/AmazonRoute53FullAccess
|
|
31
|
+
# {% endif %}
|
|
32
|
+
# {% if use_sqs %}
|
|
33
|
+
- arn:aws:iam::aws:policy/AmazonSQSFullAccess
|
|
34
|
+
# {% endif %}
|
|
35
|
+
# {% if use_efs %}
|
|
36
|
+
- arn:aws:iam::aws:policy/AmazonElasticFileSystemFullAccess
|
|
37
|
+
# {% endif %}
|
|
38
|
+
# {% for arn in iam.managed_policy_arns %}
|
|
39
|
+
- "{{ arn }}"
|
|
40
|
+
# {% endfor %}
|
|
41
|
+
Policies:
|
|
42
|
+
# {% if secrets and secrets.allowed_sm_resources %}
|
|
43
|
+
- PolicyName: "{{ resource_prefix }}access-secretsmanager"
|
|
44
|
+
PolicyDocument:
|
|
45
|
+
Version: "2012-10-17"
|
|
46
|
+
Statement:
|
|
47
|
+
- Effect: "Allow"
|
|
48
|
+
Action:
|
|
49
|
+
- "secretsmanager:GetSecretValue"
|
|
50
|
+
Resource:
|
|
51
|
+
# {% for value in secrets.allowed_sm_resources %}
|
|
52
|
+
- Fn::Sub: "{{ value }}"
|
|
53
|
+
# {% endfor %}
|
|
54
|
+
# {% if secrets.require_list_secrets %}
|
|
55
|
+
- Effect: "Allow"
|
|
56
|
+
Action:
|
|
57
|
+
- "secretsmanager:ListSecrets"
|
|
58
|
+
Resource:
|
|
59
|
+
- "*"
|
|
60
|
+
# {% endif %}
|
|
61
|
+
# {% endif %}
|
|
62
|
+
# {% if secrets and secrets.allowed_ssm_resources %}
|
|
63
|
+
- PolicyName: "{{ resource_prefix }}access-ssm"
|
|
64
|
+
PolicyDocument:
|
|
65
|
+
Version: "2012-10-17"
|
|
66
|
+
Statement:
|
|
67
|
+
- Effect: "Allow"
|
|
68
|
+
Action:
|
|
69
|
+
- "ssm:GetParameter"
|
|
70
|
+
- "ssm:GetParameters"
|
|
71
|
+
- "ssm:GetParametersByPath"
|
|
72
|
+
Resource:
|
|
73
|
+
# {% for value in secrets.allowed_ssm_resources %}
|
|
74
|
+
- Fn::Sub: "{{ value }}"
|
|
75
|
+
# {% endfor %}
|
|
76
|
+
# {% endif %}
|
|
77
|
+
# {% if rds_secret_arn %}
|
|
78
|
+
- PolicyName: "{{ resource_prefix }}access-rds-secret"
|
|
79
|
+
PolicyDocument:
|
|
80
|
+
Version: "2012-10-17"
|
|
81
|
+
Statement:
|
|
82
|
+
- Effect: "Allow"
|
|
83
|
+
Action:
|
|
84
|
+
- "secretsmanager:GetSecretValue"
|
|
85
|
+
Resource:
|
|
86
|
+
- "{{ rds_secret_arn }}"
|
|
87
|
+
# {% if rds_kms_key_id %}
|
|
88
|
+
- Effect: "Allow"
|
|
89
|
+
Action:
|
|
90
|
+
- "kms:Decrypt"
|
|
91
|
+
Resource:
|
|
92
|
+
- "{{ rds_kms_key_id }}"
|
|
93
|
+
# {% endif %}
|
|
94
|
+
# {% endif %}
|
|
95
|
+
# {% if rds_ssm_param_arn %}
|
|
96
|
+
- PolicyName: "{{ resource_prefix }}access-rds-ssm"
|
|
97
|
+
PolicyDocument:
|
|
98
|
+
Version: "2012-10-17"
|
|
99
|
+
Statement:
|
|
100
|
+
- Effect: "Allow"
|
|
101
|
+
Action:
|
|
102
|
+
- "ssm:GetParameter"
|
|
103
|
+
Resource:
|
|
104
|
+
- Fn::Sub: "{{ rds_ssm_param_arn }}"
|
|
105
|
+
# {% endif %}
|
|
106
|
+
# {% if use_dsql %}
|
|
107
|
+
- PolicyName: "{{ resource_prefix }}access-dsql"
|
|
108
|
+
PolicyDocument:
|
|
109
|
+
Version: "2012-10-17"
|
|
110
|
+
Statement:
|
|
111
|
+
- Effect: "Allow"
|
|
112
|
+
Action:
|
|
113
|
+
- "dsql:DbConnectAdmin"
|
|
114
|
+
Resource:
|
|
115
|
+
- "{{ dsql_cluster_arn }}"
|
|
116
|
+
# {% endif %}
|
|
117
|
+
- PolicyName:
|
|
118
|
+
Fn::Sub: "{{ resource_prefix }}access-cloudformation"
|
|
119
|
+
PolicyDocument:
|
|
120
|
+
Version: "2012-10-17"
|
|
121
|
+
Statement:
|
|
122
|
+
- Effect: "Allow"
|
|
123
|
+
Action:
|
|
124
|
+
- "cloudformation:DescribeStacks"
|
|
125
|
+
Resource:
|
|
126
|
+
- Fn::Sub: "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/{{ slug }}-*"
|
|
127
|
+
# {% for name, doc in iam.inline_policies.items() %}
|
|
128
|
+
- PolicyName: "{{ resource_prefix }}{{ name }}"
|
|
129
|
+
PolicyDocument: {{ doc | tojson }}
|
|
130
|
+
# {% endfor %}
|
|
131
|
+
RoleName: "lambda-{{ slug }}-{{ namespace }}"
|
|
132
|
+
# {% if permissions_boundary %}
|
|
133
|
+
PermissionsBoundary: "{{ permissions_boundary }}"
|
|
134
|
+
# {% endif %}
|
|
135
|
+
|
|
136
|
+
# {% if vpc %}
|
|
137
|
+
LambdaSecurityGroup:
|
|
138
|
+
Type: AWS::EC2::SecurityGroup
|
|
139
|
+
Properties:
|
|
140
|
+
GroupDescription: "{{ resource_prefix }}lambda"
|
|
141
|
+
GroupName: "{{ resource_prefix }}lambda"
|
|
142
|
+
SecurityGroupEgress:
|
|
143
|
+
- CidrIp: 0.0.0.0/0
|
|
144
|
+
Description: Allow all outbound traffic by default
|
|
145
|
+
IpProtocol: "-1"
|
|
146
|
+
Tags:
|
|
147
|
+
- Key: Name
|
|
148
|
+
Value: "{{ resource_prefix }}lambda"
|
|
149
|
+
VpcId:
|
|
150
|
+
Fn::ImportValue: "{{ export.vpc_id }}"
|
|
151
|
+
# {% endif %}
|
|
152
|
+
|
|
153
|
+
# {% for handler in handlers.values() %}
|
|
154
|
+
"{{ handler.key|capitalize }}LambdaFunction":
|
|
155
|
+
Type: AWS::Lambda::Function
|
|
156
|
+
Properties:
|
|
157
|
+
FunctionName: "{{ handler.function_name }}"
|
|
158
|
+
PackageType: Image
|
|
159
|
+
MemorySize: "{{ handler.memory_size }}"
|
|
160
|
+
Timeout: "{{ handler.timeout }}"
|
|
161
|
+
# {% if handler.reserved_concurrency %}
|
|
162
|
+
ReservedConcurrentExecutions: "{{ handler.reserved_concurrency }}"
|
|
163
|
+
# {% endif %}
|
|
164
|
+
Role:
|
|
165
|
+
Fn::GetAtt: LambdaRole.Arn
|
|
166
|
+
Code:
|
|
167
|
+
ImageUri:
|
|
168
|
+
Fn::Sub: "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/{{ ecr_name }}:{{ stage }}"
|
|
169
|
+
ImageConfig:
|
|
170
|
+
Command:
|
|
171
|
+
- "{{ handler.command }}"
|
|
172
|
+
Environment:
|
|
173
|
+
Variables:
|
|
174
|
+
"POCKET_STAGE": "{{ stage }}"
|
|
175
|
+
# {% for env_key, value in envs.items() %}
|
|
176
|
+
"{{ env_key }}": "{{ value }}"
|
|
177
|
+
# {% endfor %}
|
|
178
|
+
# {% for env_key, import_name in signing_key_imports.items() %}
|
|
179
|
+
"{{ env_key }}":
|
|
180
|
+
Fn::ImportValue: "{{ import_name }}"
|
|
181
|
+
# {% endfor %}
|
|
182
|
+
# {% if rds_secret_arn %}
|
|
183
|
+
"POCKET_RDS_SECRET_ARN": "{{ rds_secret_arn }}"
|
|
184
|
+
"POCKET_RDS_ENDPOINT": "{{ rds_endpoint }}"
|
|
185
|
+
"POCKET_RDS_PORT": "{{ rds_port }}"
|
|
186
|
+
"POCKET_RDS_DBNAME": "{{ rds_dbname }}"
|
|
187
|
+
# {% endif %}
|
|
188
|
+
# {% if rds_ssm_param_name %}
|
|
189
|
+
"POCKET_RDS_SECRET_STORE": "ssm"
|
|
190
|
+
"POCKET_RDS_SSM_PARAM": "{{ rds_ssm_param_name }}"
|
|
191
|
+
"POCKET_RDS_ENDPOINT": "{{ rds_endpoint }}"
|
|
192
|
+
"POCKET_RDS_PORT": "{{ rds_port }}"
|
|
193
|
+
"POCKET_RDS_DBNAME": "{{ rds_dbname }}"
|
|
194
|
+
# {% endif %}
|
|
195
|
+
# {% if use_dsql %}
|
|
196
|
+
"POCKET_DSQL_ENDPOINT": "{{ dsql_endpoint }}"
|
|
197
|
+
"POCKET_DSQL_REGION": "{{ dsql_region }}"
|
|
198
|
+
# {% endif %}
|
|
199
|
+
# {% if vpc %}
|
|
200
|
+
VpcConfig:
|
|
201
|
+
SecurityGroupIds:
|
|
202
|
+
- Ref: LambdaSecurityGroup
|
|
203
|
+
SubnetIds:
|
|
204
|
+
# {% for zone in vpc.zones %}
|
|
205
|
+
- Fn::ImportValue: "{{ export.private_subnet_ }}{{ loop.index }}"
|
|
206
|
+
# {% endfor %}
|
|
207
|
+
# {% endif %}
|
|
208
|
+
# {% if use_efs %}
|
|
209
|
+
FileSystemConfigs:
|
|
210
|
+
- Arn:
|
|
211
|
+
Fn::ImportValue: "{{ export.efs_access_point_arn }}"
|
|
212
|
+
LocalMountPath: "{{ efs_local_mount_path }}"
|
|
213
|
+
# {% endif %}
|
|
214
|
+
# {% endfor %}
|
|
215
|
+
|
|
216
|
+
# {% for handler in handlers.values() %}
|
|
217
|
+
# {% if handler.apigateway %}
|
|
218
|
+
"{{ handler.key|capitalize }}ApigatewayCloudWatchLogsGroup":
|
|
219
|
+
Type: AWS::Logs::LogGroup
|
|
220
|
+
Properties:
|
|
221
|
+
LogGroupName: "{{ slug }}-{{ handler.key }}-apigateway"
|
|
222
|
+
RetentionInDays: 365
|
|
223
|
+
|
|
224
|
+
"{{ handler.key|capitalize }}Api":
|
|
225
|
+
Type: AWS::ApiGatewayV2::Api
|
|
226
|
+
Properties:
|
|
227
|
+
Name: "{{ slug }}-{{ handler.key }}"
|
|
228
|
+
ProtocolType: HTTP
|
|
229
|
+
Target:
|
|
230
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}LambdaFunction.Arn"
|
|
231
|
+
DisableExecuteApiEndpoint: "{{ handler.apigateway.disable_execute_api_endpoint }}"
|
|
232
|
+
|
|
233
|
+
"{{ handler.key|capitalize }}ApiGatewayManagedOverrides":
|
|
234
|
+
Type: AWS::ApiGatewayV2::ApiGatewayManagedOverrides
|
|
235
|
+
Properties:
|
|
236
|
+
ApiId:
|
|
237
|
+
Ref: "{{ handler.key|capitalize }}Api"
|
|
238
|
+
Stage:
|
|
239
|
+
AutoDeploy: true
|
|
240
|
+
AccessLogSettings:
|
|
241
|
+
DestinationArn:
|
|
242
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}ApigatewayCloudWatchLogsGroup.Arn"
|
|
243
|
+
Format: >-
|
|
244
|
+
{"requestTime": "$context.requestTime",
|
|
245
|
+
"requestId": "$context.requestId",
|
|
246
|
+
"httpMethod": "$context.httpMethod",
|
|
247
|
+
"path": "$context.path",
|
|
248
|
+
"routeKey": "$context.routeKey",
|
|
249
|
+
"status": $context.status,
|
|
250
|
+
"responseLatency": $context.responseLatency,
|
|
251
|
+
"integrationRequestId": "$context.integration.requestId",
|
|
252
|
+
"functionResponseStatus": "$context.integration.status",
|
|
253
|
+
"integrationLatency": "$context.integration.latency",
|
|
254
|
+
"integrationServiceStatus": "$context.integration.integrationStatus",
|
|
255
|
+
"integrationErrorMessage": "$context.integrationErrorMessage",
|
|
256
|
+
"ip": "$context.identity.sourceIp",
|
|
257
|
+
"userAgent": "$context.identity.userAgent"}
|
|
258
|
+
|
|
259
|
+
"{{ handler.key|capitalize }}Route":
|
|
260
|
+
Type: AWS::ApiGatewayV2::Route
|
|
261
|
+
DependsOn:
|
|
262
|
+
- "{{ handler.key|capitalize }}Integration"
|
|
263
|
+
Properties:
|
|
264
|
+
ApiId:
|
|
265
|
+
Ref: "{{ handler.key|capitalize }}Api"
|
|
266
|
+
RouteKey: "ANY /{proxy+}"
|
|
267
|
+
Target:
|
|
268
|
+
Fn::Join:
|
|
269
|
+
- /
|
|
270
|
+
- - integrations
|
|
271
|
+
- Ref: "{{ handler.key|capitalize }}Integration"
|
|
272
|
+
|
|
273
|
+
"{{ handler.key|capitalize }}Integration":
|
|
274
|
+
Type: AWS::ApiGatewayV2::Integration
|
|
275
|
+
Properties:
|
|
276
|
+
ApiId:
|
|
277
|
+
Ref: "{{ handler.key|capitalize }}Api"
|
|
278
|
+
IntegrationType: AWS_PROXY
|
|
279
|
+
IntegrationUri:
|
|
280
|
+
Fn::Join:
|
|
281
|
+
- ""
|
|
282
|
+
- - "arn:"
|
|
283
|
+
- Ref: "AWS::Partition"
|
|
284
|
+
- ":apigateway:"
|
|
285
|
+
- Ref: "AWS::Region"
|
|
286
|
+
- ":lambda:path/2015-03-31/functions/"
|
|
287
|
+
- Fn::GetAtt: "{{ handler.key|capitalize }}LambdaFunction.Arn"
|
|
288
|
+
- /invocations
|
|
289
|
+
PayloadFormatVersion: "2.0"
|
|
290
|
+
|
|
291
|
+
"{{ handler.key|capitalize }}Permission":
|
|
292
|
+
Type: AWS::Lambda::Permission
|
|
293
|
+
Properties:
|
|
294
|
+
FunctionName:
|
|
295
|
+
Ref: "{{ handler.key|capitalize }}LambdaFunction"
|
|
296
|
+
Action: lambda:InvokeFunction
|
|
297
|
+
Principal: apigateway.amazonaws.com
|
|
298
|
+
SourceArn:
|
|
299
|
+
Fn::Join:
|
|
300
|
+
- ""
|
|
301
|
+
- - "arn:"
|
|
302
|
+
- Ref: "AWS::Partition"
|
|
303
|
+
- ":execute-api:"
|
|
304
|
+
- Ref: "AWS::Region"
|
|
305
|
+
- ":"
|
|
306
|
+
- Ref: "AWS::AccountId"
|
|
307
|
+
- ":"
|
|
308
|
+
- Ref: "{{ handler.key|capitalize }}Api"
|
|
309
|
+
- "/*/*"
|
|
310
|
+
# {% endif %}
|
|
311
|
+
|
|
312
|
+
# {% if handler.apigateway.domain %}
|
|
313
|
+
"{{ handler.cloudformation_cert_ref_name }}":
|
|
314
|
+
Type: AWS::CertificateManager::Certificate
|
|
315
|
+
Properties:
|
|
316
|
+
DomainName: "{{ handler.apigateway.domain }}"
|
|
317
|
+
# {% if handler.apigateway.create_records %}
|
|
318
|
+
DomainValidationOptions:
|
|
319
|
+
- DomainName: "{{ handler.apigateway.domain }}"
|
|
320
|
+
HostedZoneId: "{{ handler.apigateway.hosted_zone_id }}"
|
|
321
|
+
# {% endif %}
|
|
322
|
+
Tags:
|
|
323
|
+
- Key: Name
|
|
324
|
+
Value: "{{ slug }}-{{ handler.key }}-cert"
|
|
325
|
+
ValidationMethod: DNS
|
|
326
|
+
|
|
327
|
+
##### Warning #####
|
|
328
|
+
# Api Gateway does not support http, so you must use http.
|
|
329
|
+
# Moreover you can not just redirect http to https.
|
|
330
|
+
# - https://stackoverflow.com/a/58683733
|
|
331
|
+
# - https://stackoverflow.com/q/47311081
|
|
332
|
+
###################
|
|
333
|
+
"{{ handler.key|capitalize }}ApiGatewayDomainName":
|
|
334
|
+
Type: AWS::ApiGatewayV2::DomainName
|
|
335
|
+
Properties:
|
|
336
|
+
DomainName: "{{ handler.apigateway.domain }}"
|
|
337
|
+
DomainNameConfigurations:
|
|
338
|
+
- CertificateArn:
|
|
339
|
+
Ref: "{{ handler.cloudformation_cert_ref_name }}"
|
|
340
|
+
|
|
341
|
+
# {% if handler.apigateway.create_records %}
|
|
342
|
+
"{{ handler.key|capitalize }}DNSRecord":
|
|
343
|
+
Type: AWS::Route53::RecordSet
|
|
344
|
+
Properties:
|
|
345
|
+
HostedZoneId: "{{ handler.apigateway.hosted_zone_id }}"
|
|
346
|
+
Name: "{{ handler.apigateway.domain }}"
|
|
347
|
+
Type: A
|
|
348
|
+
AliasTarget:
|
|
349
|
+
HostedZoneId:
|
|
350
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}ApiGatewayDomainName.RegionalHostedZoneId"
|
|
351
|
+
DNSName:
|
|
352
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}ApiGatewayDomainName.RegionalDomainName"
|
|
353
|
+
# {% endif %}
|
|
354
|
+
|
|
355
|
+
"{{ handler.key|capitalize }}ApiGatewayApiMapping":
|
|
356
|
+
DependsOn:
|
|
357
|
+
- "{{ handler.key|capitalize }}ApiGatewayDomainName"
|
|
358
|
+
Type: "AWS::ApiGatewayV2::ApiMapping"
|
|
359
|
+
Properties:
|
|
360
|
+
DomainName: "{{ handler.apigateway.domain }}"
|
|
361
|
+
ApiId:
|
|
362
|
+
Ref: "{{ handler.key|capitalize }}Api"
|
|
363
|
+
Stage: "$default"
|
|
364
|
+
# {% endif %}
|
|
365
|
+
# {% endfor %}
|
|
366
|
+
|
|
367
|
+
# {% for handler in handlers.values() %}
|
|
368
|
+
# {% if handler.sqs %}
|
|
369
|
+
"{{ handler.key|capitalize }}SqsQueue":
|
|
370
|
+
Type: AWS::SQS::Queue
|
|
371
|
+
Properties:
|
|
372
|
+
QueueName:
|
|
373
|
+
Fn::Sub: "{{ handler.sqs.name }}"
|
|
374
|
+
RedrivePolicy:
|
|
375
|
+
deadLetterTargetArn:
|
|
376
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}DeadLetterQueue.Arn"
|
|
377
|
+
maxReceiveCount: "{{ handler.sqs.dead_letter_max_receive_count }}"
|
|
378
|
+
VisibilityTimeout: "{{ handler.sqs.visibility_timeout }}"
|
|
379
|
+
# default 4 days
|
|
380
|
+
MessageRetentionPeriod: "{{ handler.sqs.message_retention_period }}"
|
|
381
|
+
|
|
382
|
+
"{{ handler.key|capitalize }}DeadLetterQueue":
|
|
383
|
+
Type: AWS::SQS::Queue
|
|
384
|
+
Properties:
|
|
385
|
+
QueueName:
|
|
386
|
+
Fn::Sub: "{{ handler.sqs.name }}-dead-letter"
|
|
387
|
+
# max 14 days
|
|
388
|
+
MessageRetentionPeriod: "{{ handler.sqs.dead_letter_message_retention_period }}"
|
|
389
|
+
|
|
390
|
+
"{{ handler.key|capitalize }}SqsEventSourceMapping":
|
|
391
|
+
DependsOn:
|
|
392
|
+
- "{{ handler.key|capitalize }}SqsQueue"
|
|
393
|
+
- "{{ handler.key|capitalize }}LambdaFunction"
|
|
394
|
+
Type: AWS::Lambda::EventSourceMapping
|
|
395
|
+
Properties:
|
|
396
|
+
EventSourceArn:
|
|
397
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}SqsQueue.Arn"
|
|
398
|
+
FunctionName:
|
|
399
|
+
Ref: "{{ handler.key|capitalize }}LambdaFunction"
|
|
400
|
+
BatchSize: "{{ handler.sqs.batch_size }}"
|
|
401
|
+
ScalingConfig:
|
|
402
|
+
MaximumConcurrency: "{{ handler.sqs.maximum_concurrency }}"
|
|
403
|
+
# {% if handler.sqs.report_batch_item_failures %}
|
|
404
|
+
FunctionResponseTypes:
|
|
405
|
+
- "ReportBatchItemFailures"
|
|
406
|
+
# {% endif %}
|
|
407
|
+
# {% endif %}
|
|
408
|
+
# {% endfor %}
|
|
409
|
+
|
|
410
|
+
# {% if scheduler and scheduler.has_schedules %}
|
|
411
|
+
SchedulerExecutionRole:
|
|
412
|
+
Type: AWS::IAM::Role
|
|
413
|
+
Properties:
|
|
414
|
+
RoleName: "{{ scheduler.role_name }}"
|
|
415
|
+
AssumeRolePolicyDocument:
|
|
416
|
+
Version: "2012-10-17"
|
|
417
|
+
Statement:
|
|
418
|
+
- Effect: Allow
|
|
419
|
+
Principal:
|
|
420
|
+
Service: scheduler.amazonaws.com
|
|
421
|
+
Action: "sts:AssumeRole"
|
|
422
|
+
Policies:
|
|
423
|
+
- PolicyName: invoke-handlers
|
|
424
|
+
PolicyDocument:
|
|
425
|
+
Version: "2012-10-17"
|
|
426
|
+
Statement:
|
|
427
|
+
- Effect: Allow
|
|
428
|
+
Action:
|
|
429
|
+
- "lambda:InvokeFunction"
|
|
430
|
+
Resource:
|
|
431
|
+
# {% for arn in scheduler.invoked_function_arns %}
|
|
432
|
+
- Fn::Sub: "{{ arn }}"
|
|
433
|
+
# {% endfor %}
|
|
434
|
+
|
|
435
|
+
# {% for entry in scheduler.schedules %}
|
|
436
|
+
"{{ entry.yaml_key }}Schedule":
|
|
437
|
+
Type: AWS::Scheduler::Schedule
|
|
438
|
+
DependsOn:
|
|
439
|
+
- "{{ entry.handler|capitalize }}LambdaFunction"
|
|
440
|
+
- SchedulerExecutionRole
|
|
441
|
+
Properties:
|
|
442
|
+
Name: "{{ entry.name }}"
|
|
443
|
+
ScheduleExpression: "{{ entry.schedule_expression }}"
|
|
444
|
+
FlexibleTimeWindow:
|
|
445
|
+
Mode: "OFF"
|
|
446
|
+
Target:
|
|
447
|
+
Arn:
|
|
448
|
+
Fn::GetAtt: "{{ entry.handler|capitalize }}LambdaFunction.Arn"
|
|
449
|
+
RoleArn:
|
|
450
|
+
Fn::GetAtt: SchedulerExecutionRole.Arn
|
|
451
|
+
Input: {{ entry.input_json|tojson }}
|
|
452
|
+
# {% endfor %}
|
|
453
|
+
# {% endif %}
|
|
454
|
+
|
|
455
|
+
# {% if use_efs %}
|
|
456
|
+
LambdaEFSAccess:
|
|
457
|
+
Type: AWS::EC2::SecurityGroupIngress
|
|
458
|
+
Properties:
|
|
459
|
+
GroupId:
|
|
460
|
+
Fn::ImportValue: "{{ export.efs_security_group }}"
|
|
461
|
+
SourceSecurityGroupId:
|
|
462
|
+
Ref: LambdaSecurityGroup
|
|
463
|
+
IpProtocol: tcp
|
|
464
|
+
FromPort: 2049
|
|
465
|
+
ToPort: 2049
|
|
466
|
+
# {% endif %}
|
|
467
|
+
|
|
468
|
+
# {% if use_rds %}
|
|
469
|
+
LambdaRDSAccess:
|
|
470
|
+
Type: AWS::EC2::SecurityGroupIngress
|
|
471
|
+
Properties:
|
|
472
|
+
GroupId: "{{ rds_security_group_id }}"
|
|
473
|
+
SourceSecurityGroupId:
|
|
474
|
+
Ref: LambdaSecurityGroup
|
|
475
|
+
IpProtocol: tcp
|
|
476
|
+
FromPort: 5432
|
|
477
|
+
ToPort: 5432
|
|
478
|
+
# {% endif %}
|
|
479
|
+
|
|
480
|
+
# {% if handlers %}
|
|
481
|
+
Outputs:
|
|
482
|
+
# {% for handler in handlers.values() %}
|
|
483
|
+
"{{ handler.key|capitalize }}LambdaFunction":
|
|
484
|
+
Value:
|
|
485
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}LambdaFunction.Arn"
|
|
486
|
+
# {% if handler.export_api_domain %}
|
|
487
|
+
"{{ handler.key|capitalize }}ApiDomain":
|
|
488
|
+
Value:
|
|
489
|
+
Fn::Sub:
|
|
490
|
+
- "${ApiId}.execute-api.${AWS::Region}.amazonaws.com"
|
|
491
|
+
- ApiId:
|
|
492
|
+
Ref: "{{ handler.key|capitalize }}Api"
|
|
493
|
+
Export:
|
|
494
|
+
Name: "{{ handler.export_api_domain }}"
|
|
495
|
+
# {% endif %}
|
|
496
|
+
# {% if handler.apigateway %}
|
|
497
|
+
"{{ handler.key|capitalize }}ApiEndpoint":
|
|
498
|
+
Value:
|
|
499
|
+
# {% if handler.apigateway.domain %}
|
|
500
|
+
Fn::Join:
|
|
501
|
+
- ""
|
|
502
|
+
- - "https://{{ handler.apigateway.domain }}/"
|
|
503
|
+
# {% else %}
|
|
504
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}Api.ApiEndpoint"
|
|
505
|
+
# {% endif %}
|
|
506
|
+
# {% if handler.apigateway.domain %}
|
|
507
|
+
"{{ handler.key|capitalize }}RegionalDomainName":
|
|
508
|
+
Value:
|
|
509
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}ApiGatewayDomainName.RegionalDomainName"
|
|
510
|
+
"{{ handler.key|capitalize }}RegionalHostedZoneId":
|
|
511
|
+
Value:
|
|
512
|
+
Fn::GetAtt: "{{ handler.key|capitalize }}ApiGatewayDomainName.RegionalHostedZoneId"
|
|
513
|
+
# {% endif %}
|
|
514
|
+
# {% endif %}
|
|
515
|
+
# {% endfor %}
|
|
516
|
+
# {% endif %}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import cf from 'cloudfront';
|
|
2
|
+
var crypto = require('crypto');
|
|
3
|
+
const kvsHandle = cf.kvs('${TokenKvs}');
|
|
4
|
+
async function handler(event) {
|
|
5
|
+
var request = event.request;
|
|
6
|
+
var originalUri = request.uri;
|
|
7
|
+
var lastItem = request.uri.split('/').pop();
|
|
8
|
+
if (!lastItem.includes('.')) { request.uri = '{{ fallback_uri }}'; }
|
|
9
|
+
var cookie = request.cookies['pocket-spa-token'];
|
|
10
|
+
if (!cookie) { return _redirect(originalUri); }
|
|
11
|
+
var parts = cookie.value.split(':');
|
|
12
|
+
if (parts.length !== 3) { return _redirect(originalUri); }
|
|
13
|
+
var expiry = parseInt(parts[1], 10);
|
|
14
|
+
if (Math.floor(Date.now() / 1000) > expiry) { return _redirect(originalUri); }
|
|
15
|
+
var secret;
|
|
16
|
+
try { secret = await kvsHandle.get('token_secret'); }
|
|
17
|
+
catch (e) { return _redirect(originalUri); }
|
|
18
|
+
var msg = parts[0] + ':' + parts[1];
|
|
19
|
+
var hmac = crypto.createHmac('sha256', Buffer.from(secret, 'hex'));
|
|
20
|
+
var sig = hmac.update(msg).digest('hex');
|
|
21
|
+
if (sig !== parts[2]) { return _redirect(originalUri); }
|
|
22
|
+
return request;
|
|
23
|
+
}
|
|
24
|
+
function _redirect(uri) {
|
|
25
|
+
var next = encodeURIComponent(uri);
|
|
26
|
+
return { statusCode: 302, statusDescription: 'Found',
|
|
27
|
+
headers: { location: { value: '{{ login_path }}?next=' + next } } };
|
|
28
|
+
}
|