aws-classify-server 0.1.0 → 0.1.2
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.
- package/README.md +57 -33
- package/lib/cjs/ClassifyResponse.d.ts +1 -0
- package/lib/cjs/ClassifyServerless.d.ts +4 -1
- package/lib/cjs/ClassifyServerless.js +32 -3
- package/lib/esm/ClassifyResponse.d.ts +1 -0
- package/lib/esm/ClassifyServerless.d.ts +4 -1
- package/lib/esm/ClassifyServerless.js +32 -3
- package/package.json +2 -2
- package/yml/apig.yml +7 -0
- package/yml/custom-cloudfront.yml +3 -0
- package/yml/custom-dynamodb.yml +9 -0
- package/yml/functions.yml +14 -0
- package/yml/provider-iam.yml +8 -0
- package/yml/resources-custom-website.yml +91 -0
- package/yml/resources-dynamodb.yml +15 -0
- package/yml/resources-website.yml +88 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A library for calling AWS lambda functions from a browser or react-native app wh
|
|
|
5
5
|
The reverse is also true in that class members implemented in the browser can be called from within a Lambda server. The latter uses Web Sockets in the AWS gateway. aws-classify also provides for a static website from which everything can be executed to comply with same-origin policy.
|
|
6
6
|
|
|
7
7
|
* Complex data with classes and cyclic structures can be passed and returned
|
|
8
|
-
*
|
|
8
|
+
* Exceptions are passed back to the caller of the request method
|
|
9
9
|
* back-end session data is simply a matter of defining fields in the response class
|
|
10
10
|
* Configuration and deployment via a simple serverless.yml file
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ These AWS resources are automatically configured and deployment is fully automat
|
|
|
17
17
|
* S3 for a static website
|
|
18
18
|
* Cloudfront with a custom domain name as a CDN
|
|
19
19
|
|
|
20
|
-
All of these resources are configured by the Serverless Framework and deployed by running a script. You only need to login to AWS in order
|
|
20
|
+
All of these resources are configured by the Serverless Framework and deployed by running a script. You only need to login to AWS in order to create credentials for the Serverless Framework and to register your domain name and create an SSL certificate for it.
|
|
21
21
|
|
|
22
22
|
### Use Cases
|
|
23
23
|
aws-classify harnesses AWS to make it easy to leverage a scalable cloud-based infrastructure with virtually no learning curve. From there you can leverage all of the other AWS resources your application may need. The primary use-case is startups and projects that want a complete "app in a box" solution.
|
|
@@ -197,18 +197,32 @@ You would then configure bisync.json to keep the shared code in sync
|
|
|
197
197
|
```
|
|
198
198
|
Implementing shared code this way avoid restrictions that packaging schemes have that effectively require code to be present in project tree
|
|
199
199
|
## Deployment
|
|
200
|
-
|
|
200
|
+
You will need a serverless.yml file for automatic deployment in your cloud folder
|
|
201
|
+
In the first two sections you need to setup these custom variables
|
|
202
|
+
* **service:** - A unique name for your project within your AWS account
|
|
203
|
+
* **domain:** - The domain name your will use(even it is not yet registered)
|
|
204
|
+
* **certificateARN:** - An AWS certificate which you can [generate on the AWS
|
|
205
|
+
console](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html)
|
|
206
|
+
* **prefixes** - The sub-domain to be used for the development stage. You may, of course, define other stages (e.g. staging, qa etc)
|
|
207
|
+
* **directories**
|
|
208
|
+
* **responseHandlers:** - A file that has the exports for the handlers aws-classify needs. This file should also register your responses. It looks like this:
|
|
209
|
+
```
|
|
210
|
+
export {responseHandler, webSocketConnect, webSocketDisconnect} from "../aws-classify-server"
|
|
211
|
+
import {classifyServerless} from "aws-classify-server";
|
|
212
|
+
|
|
213
|
+
import {ServerResponse} from "./ServerResponse"; // Your server responses
|
|
214
|
+
classifyServerless.registerResponse(ServerResponse);
|
|
201
215
|
```
|
|
202
|
-
|
|
216
|
+
* **staticWebsite:** - The directory where your static website will be built
|
|
217
|
+
* **includeFiles:** - Normally this just points to the yml directory in aws-classify
|
|
218
|
+
```
|
|
219
|
+
service: 'my-fantastic-project'
|
|
203
220
|
|
|
204
221
|
custom:
|
|
205
222
|
|
|
206
|
-
#
|
|
207
|
-
# You need to fill these out
|
|
208
|
-
#
|
|
209
|
-
|
|
210
223
|
# Specify this even if you don't have a custom domain at this time
|
|
211
224
|
domain: awsclassify.com
|
|
225
|
+
|
|
212
226
|
# Get your AWS SSL Certificate and put it here if you have a custom domain
|
|
213
227
|
certificateARN: "arn:aws:acm:us-east-1:############:certificate/########-####-####-####-############"
|
|
214
228
|
|
|
@@ -219,19 +233,21 @@ custom:
|
|
|
219
233
|
|
|
220
234
|
directories:
|
|
221
235
|
responseHandlers: "src/server-responses/index"
|
|
222
|
-
staticWebsite: "
|
|
223
|
-
includeFiles: "node_modules/aws-classify/yml"
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
236
|
+
staticWebsite: "../web/build"
|
|
237
|
+
includeFiles: "node_modules/aws-classify-serverless/yml"
|
|
238
|
+
```
|
|
239
|
+
If you are using a web site that requires building you will want to force it to be built as part of the deployment by adding the script in the **scripts** custom variable:
|
|
240
|
+
```
|
|
241
|
+
scripts:
|
|
242
|
+
hooks:
|
|
243
|
+
'before:deploy:deploy': npm run build:static
|
|
244
|
+
```
|
|
245
|
+
The remaining custom variables just need to be copied as is. They will:
|
|
246
|
+
* set up the detailed names for your domain and buckets
|
|
247
|
+
* configure the s3Sync plugin. Note that noSync: true is specified here so you will have to run ```sls s3Sync``` to push our your site. Doing this way avoids s3Sync deploying your site when you use offline mode
|
|
248
|
+
* Configures other custom variables needed
|
|
249
|
+
```
|
|
250
|
+
yml: ${self:custom.directories.includeFiles}
|
|
235
251
|
stage: ${opt:stage,'dev'}
|
|
236
252
|
hostedZoneName: ${self:custom.domain}.
|
|
237
253
|
domainName: ${self:custom.prefixes.${self:custom.stage}}{$self:custome.domain}
|
|
@@ -246,16 +262,20 @@ custom:
|
|
|
246
262
|
serverless-offline:
|
|
247
263
|
httpPort: 4000
|
|
248
264
|
dynamodb: ${file(${self:custom.yml}/custom-dynamodb.yml)}
|
|
249
|
-
|
|
250
|
-
|
|
265
|
+
```
|
|
266
|
+
The deployment is dependent on a number plugs which must be installed with npm install
|
|
267
|
+
```
|
|
251
268
|
plugins:
|
|
252
269
|
- serverless-plugin-scripts
|
|
253
270
|
- serverless-s3-sync
|
|
271
|
+
- "@serverless-aws/serverless-s3-remover"
|
|
254
272
|
- serverless-cloudfront-invalidate
|
|
255
273
|
- serverless-plugin-typescript
|
|
256
274
|
- serverless-dynamodb-local
|
|
257
275
|
- serverless-offline
|
|
258
|
-
|
|
276
|
+
```
|
|
277
|
+
The provider section details configuration the Serverless Framework handles. Many of the details are in included files to keep this simple and resiliant to change
|
|
278
|
+
```
|
|
259
279
|
provider:
|
|
260
280
|
name: aws
|
|
261
281
|
runtime: nodejs16.x
|
|
@@ -267,16 +287,19 @@ provider:
|
|
|
267
287
|
APIG_ENDPOINT: ${file(${self:custom.yml}/apig.yml)}
|
|
268
288
|
iam:
|
|
269
289
|
role:
|
|
270
|
-
|
|
290
|
+
statements:
|
|
271
291
|
- ${file(${self:custom.yml}/provider-iam.yml)}
|
|
272
|
-
|
|
292
|
+
```
|
|
293
|
+
The functions details each of the Lamda handlers. The details are in an include file
|
|
294
|
+
```
|
|
273
295
|
functions:
|
|
274
296
|
- ${file(${self:custom.yml}/functions.yml)}
|
|
275
|
-
|
|
297
|
+
```
|
|
298
|
+
Finally resources contains all of the resource definitions most of which are in include files. Important: If you don't yet have a custom domain use ```{file(${self:custom.yml}/resources-website.yml)}``` instead of ```{file(${self:custom.yml}/resources-custom-website.yml)}```
|
|
299
|
+
```
|
|
276
300
|
resources:
|
|
277
|
-
- ${file(${self:custom.yml}/resources-website.yml)}
|
|
278
|
-
|
|
279
|
-
|
|
301
|
+
- ${file(${self:custom.yml}/resources-custom-website.yml)}
|
|
302
|
+
- ${file(${self:custom.yml}/resources-dynamodb.yml)}
|
|
280
303
|
```
|
|
281
304
|
|
|
282
305
|
To deploy you need to:
|
|
@@ -291,13 +314,14 @@ serverless config credentials \
|
|
|
291
314
|
- Obtain a no-cost SSL certificate on the AWS console and put it in the serverless.yml file
|
|
292
315
|
- You can deploy a test version with
|
|
293
316
|
```
|
|
294
|
-
|
|
317
|
+
sls deploy --verbose --stage dev
|
|
318
|
+
sls s3sync
|
|
295
319
|
```
|
|
296
|
-
|
|
320
|
+
- Deploy production version with
|
|
297
321
|
```
|
|
298
322
|
serverless deploy stage prod
|
|
323
|
+
sls s3sync
|
|
299
324
|
```
|
|
300
|
-
|
|
301
325
|
## Roadmap
|
|
302
326
|
* Create some sample applications
|
|
303
327
|
* Continued shakeout with real-world applications
|
|
@@ -2,5 +2,6 @@ export declare class ClassifyResponse {
|
|
|
2
2
|
__sessionId__: string | undefined;
|
|
3
3
|
__connectionId__: string | undefined;
|
|
4
4
|
__interfaceName__: string | undefined;
|
|
5
|
+
__userId__: string | undefined;
|
|
5
6
|
constructor(interfaceName: string, sessionId: string, connectionId: string);
|
|
6
7
|
}
|
|
@@ -11,12 +11,15 @@ export declare class ClassifyServerless {
|
|
|
11
11
|
dispatch(ev: APIGatewayProxyEvent, context: Context, classes?: {}): Promise<string>;
|
|
12
12
|
registerRequest<T>(requestClass: new () => T, classes?: any): new () => T;
|
|
13
13
|
getSessionId<T>(responseObj: T): any;
|
|
14
|
+
getUserId<T>(responseObj: T): any;
|
|
15
|
+
setUserId<T>(responseObj: T, userId: string): void;
|
|
14
16
|
createRequest<T, U>(responseObj: U, requestClass: new () => T): T;
|
|
15
17
|
createRequestForSession<T>(sessionId: string, requestClass: new () => T): Promise<T>;
|
|
16
18
|
createResponse<T, R>(responseClass: new () => T, sessionId: string, callback: (_obj: T) => Promise<R>, classes?: any): Promise<R>;
|
|
19
|
+
getSessionsForUserId(userId: string): Promise<any[]>;
|
|
17
20
|
}
|
|
18
21
|
export declare function getSessionData(sessionId: string, interfaceName?: string): Promise<Record<string, any> | undefined>;
|
|
19
|
-
export declare function saveSessionData(sessionId: string, interfaceName?: string, sessionData?: string, connectionId?: string): Promise<string>;
|
|
22
|
+
export declare function saveSessionData(sessionId: string, interfaceName?: string, sessionData?: string, connectionId?: string, userId?: string): Promise<string>;
|
|
20
23
|
export declare const EndPointsLogging: {
|
|
21
24
|
create: boolean;
|
|
22
25
|
connect: boolean;
|
|
@@ -106,7 +106,7 @@ class ClassifyServerless {
|
|
|
106
106
|
}
|
|
107
107
|
const updatedSessionData = (0, js_freeze_dry_1.serialize)(obj, classes);
|
|
108
108
|
if (!result || updatedSessionData !== result.sessionData)
|
|
109
|
-
sessionId = yield saveSessionData(sessionId, request.interfaceName, updatedSessionData, undefined);
|
|
109
|
+
sessionId = yield saveSessionData(sessionId, request.interfaceName, updatedSessionData, undefined, obj.__userId__);
|
|
110
110
|
// Formulate response
|
|
111
111
|
const lambdaResponse = {
|
|
112
112
|
data: fnResponse,
|
|
@@ -189,6 +189,13 @@ class ClassifyServerless {
|
|
|
189
189
|
throw Error('ClassifyServerless.getSessionId: no session id');
|
|
190
190
|
return sessionId;
|
|
191
191
|
}
|
|
192
|
+
getUserId(responseObj) {
|
|
193
|
+
const userId = responseObj['__userId__'];
|
|
194
|
+
return userId;
|
|
195
|
+
}
|
|
196
|
+
setUserId(responseObj, userId) {
|
|
197
|
+
responseObj['__userId__'] = userId;
|
|
198
|
+
}
|
|
192
199
|
createRequest(responseObj, requestClass) {
|
|
193
200
|
const obj = new requestClass();
|
|
194
201
|
obj['__sessionId__'] = responseObj['__sessionId__'];
|
|
@@ -226,6 +233,7 @@ class ClassifyServerless {
|
|
|
226
233
|
Object.assign(obj, (0, js_freeze_dry_1.deserialize)(sessionData, classes));
|
|
227
234
|
obj.__sessionId__ = sessionId;
|
|
228
235
|
obj.__connectionId__ = result.connectionId;
|
|
236
|
+
obj.__userId__ = result.userId;
|
|
229
237
|
if (this.logLevel.calls)
|
|
230
238
|
this.log(`creating response for ${interfaceName} with sessionId=${sessionId} connectionId= ${obj.__connectionId__} sessionData=${sessionData}`);
|
|
231
239
|
const ret = yield callback(obj);
|
|
@@ -238,6 +246,23 @@ class ClassifyServerless {
|
|
|
238
246
|
return ret;
|
|
239
247
|
});
|
|
240
248
|
}
|
|
249
|
+
getSessionsForUserId(userId) {
|
|
250
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
if (userId) {
|
|
252
|
+
const data = yield ddbDocClient.query({
|
|
253
|
+
TableName: 'classifySessionStore',
|
|
254
|
+
KeyConditionExpression: 'userId = :userId',
|
|
255
|
+
IndexName: 'userId',
|
|
256
|
+
ExpressionAttributeValues: {
|
|
257
|
+
':userId': userId,
|
|
258
|
+
},
|
|
259
|
+
ProjectionExpression: `sessionId`
|
|
260
|
+
});
|
|
261
|
+
return data.Items ? data.Items.map(i => i.sessionId) : [];
|
|
262
|
+
}
|
|
263
|
+
return [];
|
|
264
|
+
});
|
|
265
|
+
}
|
|
241
266
|
}
|
|
242
267
|
exports.ClassifyServerless = ClassifyServerless;
|
|
243
268
|
function getSessionData(sessionId, interfaceName = "") {
|
|
@@ -247,7 +272,7 @@ function getSessionData(sessionId, interfaceName = "") {
|
|
|
247
272
|
TableName: 'classifySessionStore',
|
|
248
273
|
Key: { sessionId },
|
|
249
274
|
ConsistentRead: true,
|
|
250
|
-
ProjectionExpression: `interface_${interfaceName}, connectionId, updated`
|
|
275
|
+
ProjectionExpression: `interface_${interfaceName}, connectionId, userId, updated`
|
|
251
276
|
});
|
|
252
277
|
return data.Item;
|
|
253
278
|
}
|
|
@@ -255,7 +280,7 @@ function getSessionData(sessionId, interfaceName = "") {
|
|
|
255
280
|
});
|
|
256
281
|
}
|
|
257
282
|
exports.getSessionData = getSessionData;
|
|
258
|
-
function saveSessionData(sessionId, interfaceName, sessionData, connectionId) {
|
|
283
|
+
function saveSessionData(sessionId, interfaceName, sessionData, connectionId, userId) {
|
|
259
284
|
return __awaiter(this, void 0, void 0, function* () {
|
|
260
285
|
const updateExpressionComponents = ['updated = :time'];
|
|
261
286
|
const expressionAttributeValues = { ':time': new Date().getTime() };
|
|
@@ -267,6 +292,10 @@ function saveSessionData(sessionId, interfaceName, sessionData, connectionId) {
|
|
|
267
292
|
updateExpressionComponents.push('connectionId = :connectionId');
|
|
268
293
|
expressionAttributeValues[':connectionId'] = connectionId;
|
|
269
294
|
}
|
|
295
|
+
if (userId) {
|
|
296
|
+
updateExpressionComponents.push('userId = :userId');
|
|
297
|
+
expressionAttributeValues[':userId'] = userId;
|
|
298
|
+
}
|
|
270
299
|
// First try and out data only if the session id exists
|
|
271
300
|
yield ddbDocClient.update({
|
|
272
301
|
TableName: 'classifySessionStore',
|
|
@@ -2,5 +2,6 @@ export declare class ClassifyResponse {
|
|
|
2
2
|
__sessionId__: string | undefined;
|
|
3
3
|
__connectionId__: string | undefined;
|
|
4
4
|
__interfaceName__: string | undefined;
|
|
5
|
+
__userId__: string | undefined;
|
|
5
6
|
constructor(interfaceName: string, sessionId: string, connectionId: string);
|
|
6
7
|
}
|
|
@@ -11,12 +11,15 @@ export declare class ClassifyServerless {
|
|
|
11
11
|
dispatch(ev: APIGatewayProxyEvent, context: Context, classes?: {}): Promise<string>;
|
|
12
12
|
registerRequest<T>(requestClass: new () => T, classes?: any): new () => T;
|
|
13
13
|
getSessionId<T>(responseObj: T): any;
|
|
14
|
+
getUserId<T>(responseObj: T): any;
|
|
15
|
+
setUserId<T>(responseObj: T, userId: string): void;
|
|
14
16
|
createRequest<T, U>(responseObj: U, requestClass: new () => T): T;
|
|
15
17
|
createRequestForSession<T>(sessionId: string, requestClass: new () => T): Promise<T>;
|
|
16
18
|
createResponse<T, R>(responseClass: new () => T, sessionId: string, callback: (_obj: T) => Promise<R>, classes?: any): Promise<R>;
|
|
19
|
+
getSessionsForUserId(userId: string): Promise<any[]>;
|
|
17
20
|
}
|
|
18
21
|
export declare function getSessionData(sessionId: string, interfaceName?: string): Promise<Record<string, any> | undefined>;
|
|
19
|
-
export declare function saveSessionData(sessionId: string, interfaceName?: string, sessionData?: string, connectionId?: string): Promise<string>;
|
|
22
|
+
export declare function saveSessionData(sessionId: string, interfaceName?: string, sessionData?: string, connectionId?: string, userId?: string): Promise<string>;
|
|
20
23
|
export declare const EndPointsLogging: {
|
|
21
24
|
create: boolean;
|
|
22
25
|
connect: boolean;
|
|
@@ -103,7 +103,7 @@ export class ClassifyServerless {
|
|
|
103
103
|
}
|
|
104
104
|
const updatedSessionData = serialize(obj, classes);
|
|
105
105
|
if (!result || updatedSessionData !== result.sessionData)
|
|
106
|
-
sessionId = yield saveSessionData(sessionId, request.interfaceName, updatedSessionData, undefined);
|
|
106
|
+
sessionId = yield saveSessionData(sessionId, request.interfaceName, updatedSessionData, undefined, obj.__userId__);
|
|
107
107
|
// Formulate response
|
|
108
108
|
const lambdaResponse = {
|
|
109
109
|
data: fnResponse,
|
|
@@ -186,6 +186,13 @@ export class ClassifyServerless {
|
|
|
186
186
|
throw Error('ClassifyServerless.getSessionId: no session id');
|
|
187
187
|
return sessionId;
|
|
188
188
|
}
|
|
189
|
+
getUserId(responseObj) {
|
|
190
|
+
const userId = responseObj['__userId__'];
|
|
191
|
+
return userId;
|
|
192
|
+
}
|
|
193
|
+
setUserId(responseObj, userId) {
|
|
194
|
+
responseObj['__userId__'] = userId;
|
|
195
|
+
}
|
|
189
196
|
createRequest(responseObj, requestClass) {
|
|
190
197
|
const obj = new requestClass();
|
|
191
198
|
obj['__sessionId__'] = responseObj['__sessionId__'];
|
|
@@ -223,6 +230,7 @@ export class ClassifyServerless {
|
|
|
223
230
|
Object.assign(obj, deserialize(sessionData, classes));
|
|
224
231
|
obj.__sessionId__ = sessionId;
|
|
225
232
|
obj.__connectionId__ = result.connectionId;
|
|
233
|
+
obj.__userId__ = result.userId;
|
|
226
234
|
if (this.logLevel.calls)
|
|
227
235
|
this.log(`creating response for ${interfaceName} with sessionId=${sessionId} connectionId= ${obj.__connectionId__} sessionData=${sessionData}`);
|
|
228
236
|
const ret = yield callback(obj);
|
|
@@ -235,6 +243,23 @@ export class ClassifyServerless {
|
|
|
235
243
|
return ret;
|
|
236
244
|
});
|
|
237
245
|
}
|
|
246
|
+
getSessionsForUserId(userId) {
|
|
247
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
248
|
+
if (userId) {
|
|
249
|
+
const data = yield ddbDocClient.query({
|
|
250
|
+
TableName: 'classifySessionStore',
|
|
251
|
+
KeyConditionExpression: 'userId = :userId',
|
|
252
|
+
IndexName: 'userId',
|
|
253
|
+
ExpressionAttributeValues: {
|
|
254
|
+
':userId': userId,
|
|
255
|
+
},
|
|
256
|
+
ProjectionExpression: `sessionId`
|
|
257
|
+
});
|
|
258
|
+
return data.Items ? data.Items.map(i => i.sessionId) : [];
|
|
259
|
+
}
|
|
260
|
+
return [];
|
|
261
|
+
});
|
|
262
|
+
}
|
|
238
263
|
}
|
|
239
264
|
export function getSessionData(sessionId, interfaceName = "") {
|
|
240
265
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -243,14 +268,14 @@ export function getSessionData(sessionId, interfaceName = "") {
|
|
|
243
268
|
TableName: 'classifySessionStore',
|
|
244
269
|
Key: { sessionId },
|
|
245
270
|
ConsistentRead: true,
|
|
246
|
-
ProjectionExpression: `interface_${interfaceName}, connectionId, updated`
|
|
271
|
+
ProjectionExpression: `interface_${interfaceName}, connectionId, userId, updated`
|
|
247
272
|
});
|
|
248
273
|
return data.Item;
|
|
249
274
|
}
|
|
250
275
|
return undefined;
|
|
251
276
|
});
|
|
252
277
|
}
|
|
253
|
-
export function saveSessionData(sessionId, interfaceName, sessionData, connectionId) {
|
|
278
|
+
export function saveSessionData(sessionId, interfaceName, sessionData, connectionId, userId) {
|
|
254
279
|
return __awaiter(this, void 0, void 0, function* () {
|
|
255
280
|
const updateExpressionComponents = ['updated = :time'];
|
|
256
281
|
const expressionAttributeValues = { ':time': new Date().getTime() };
|
|
@@ -262,6 +287,10 @@ export function saveSessionData(sessionId, interfaceName, sessionData, connectio
|
|
|
262
287
|
updateExpressionComponents.push('connectionId = :connectionId');
|
|
263
288
|
expressionAttributeValues[':connectionId'] = connectionId;
|
|
264
289
|
}
|
|
290
|
+
if (userId) {
|
|
291
|
+
updateExpressionComponents.push('userId = :userId');
|
|
292
|
+
expressionAttributeValues[':userId'] = userId;
|
|
293
|
+
}
|
|
265
294
|
// First try and out data only if the session id exists
|
|
266
295
|
yield ddbDocClient.update({
|
|
267
296
|
TableName: 'classifySessionStore',
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aws-classify-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"main": "./lib/cjs/index.js",
|
|
5
5
|
"module": "./lib/esm/index.js",
|
|
6
6
|
"types": "./lib/esm/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
|
-
"/lib"
|
|
8
|
+
"/lib", "yml"
|
|
9
9
|
],
|
|
10
10
|
"description": "Typescript Interface to Lambda and Websockets",
|
|
11
11
|
"scripts": {
|
package/yml/apig.yml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
responseHandler:
|
|
2
|
+
handler: ${self:custom.directories.responseHandlers}.responseHandler
|
|
3
|
+
events:
|
|
4
|
+
- httpApi:
|
|
5
|
+
path: /api/dispatch
|
|
6
|
+
method: ANY
|
|
7
|
+
connectHandler:
|
|
8
|
+
handler: ${self:custom.directories.responseHandlers}.webSocketConnect
|
|
9
|
+
events:
|
|
10
|
+
- websocket: $connect
|
|
11
|
+
disconnectHandler:
|
|
12
|
+
handler: ${self:custom.directories.responseHandlers}.webSocketDisconnect
|
|
13
|
+
events:
|
|
14
|
+
- websocket: $disconnect
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Resources:
|
|
2
|
+
CloudFrontOriginAccessIdentity:
|
|
3
|
+
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
|
|
4
|
+
Properties:
|
|
5
|
+
CloudFrontOriginAccessIdentityConfig:
|
|
6
|
+
Comment: !Join [ "-", [ "access-identity", !GetAtt S3Bucket.DomainName ] ]
|
|
7
|
+
CloudFrontDistribution:
|
|
8
|
+
Type: AWS::CloudFront::Distribution
|
|
9
|
+
DependsOn:
|
|
10
|
+
- S3Bucket
|
|
11
|
+
Properties:
|
|
12
|
+
DistributionConfig:
|
|
13
|
+
PriceClass: PriceClass_All
|
|
14
|
+
ViewerCertificate:
|
|
15
|
+
AcmCertificateArn: ${self:custom.certificateARN}
|
|
16
|
+
MinimumProtocolVersion: TLSv1.1_2016
|
|
17
|
+
SslSupportMethod: sni-only
|
|
18
|
+
Aliases:
|
|
19
|
+
- ${self:custom.domainName}
|
|
20
|
+
Enabled: true
|
|
21
|
+
HttpVersion: http2
|
|
22
|
+
DefaultRootObject: index.html
|
|
23
|
+
Origins:
|
|
24
|
+
- Id: api
|
|
25
|
+
DomainName: !Join [ ".", [ !Ref HttpApi, "execute-api", !Ref AWS::Region, "amazonaws.com" ] ]
|
|
26
|
+
CustomOriginConfig:
|
|
27
|
+
OriginProtocolPolicy: https-only
|
|
28
|
+
- Id: static
|
|
29
|
+
DomainName: !GetAtt S3Bucket.RegionalDomainName
|
|
30
|
+
S3OriginConfig:
|
|
31
|
+
OriginAccessIdentity: !Join [ "", [ "origin-access-identity/cloudfront/", !Ref CloudFrontOriginAccessIdentity ] ]
|
|
32
|
+
CacheBehaviors:
|
|
33
|
+
- PathPattern: api/*
|
|
34
|
+
TargetOriginId: api
|
|
35
|
+
# recently deprecated style of setting ForwardedValues
|
|
36
|
+
# but currently there's no alternative if using CloudFormation
|
|
37
|
+
ForwardedValues:
|
|
38
|
+
QueryString: true
|
|
39
|
+
ViewerProtocolPolicy: redirect-to-https
|
|
40
|
+
AllowedMethods: [ GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE ]
|
|
41
|
+
# No caching by default but caching can be set with appropriate headers in the api responses
|
|
42
|
+
MinTTL: 0
|
|
43
|
+
DefaultTTL: 0
|
|
44
|
+
DefaultCacheBehavior:
|
|
45
|
+
TargetOriginId: static
|
|
46
|
+
# 'Managed-CachingOptimized'
|
|
47
|
+
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
|
|
48
|
+
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
|
|
49
|
+
# 'Managed-CORS-S3Origin'
|
|
50
|
+
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html
|
|
51
|
+
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
|
|
52
|
+
ViewerProtocolPolicy: redirect-to-https
|
|
53
|
+
S3Bucket:
|
|
54
|
+
Type: AWS::S3::Bucket
|
|
55
|
+
Properties:
|
|
56
|
+
BucketName: ${self:custom.bucketName}
|
|
57
|
+
S3BucketPolicy:
|
|
58
|
+
Type: AWS::S3::BucketPolicy
|
|
59
|
+
Properties:
|
|
60
|
+
Bucket: !Ref S3Bucket
|
|
61
|
+
PolicyDocument:
|
|
62
|
+
Statement:
|
|
63
|
+
- Effect: Allow
|
|
64
|
+
Action: "s3:GetObject"
|
|
65
|
+
Resource: !Join [ "", [ !GetAtt S3Bucket.Arn, "/*" ] ]
|
|
66
|
+
Principal:
|
|
67
|
+
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
|
|
68
|
+
CustomDNSName:
|
|
69
|
+
Type: AWS::Route53::RecordSetGroup
|
|
70
|
+
Properties:
|
|
71
|
+
HostedZoneName: ${self:custom.hostedZoneName}
|
|
72
|
+
RecordSets:
|
|
73
|
+
- Name: ${self:custom.domainName}
|
|
74
|
+
Type: A
|
|
75
|
+
AliasTarget:
|
|
76
|
+
HostedZoneId: Z2FDTNDATAQYW2 #cloudfront hostedzone id
|
|
77
|
+
DNSName:
|
|
78
|
+
Fn::GetAtt:
|
|
79
|
+
- CloudFrontDistribution
|
|
80
|
+
- DomainName
|
|
81
|
+
Outputs:
|
|
82
|
+
CloudFrontId:
|
|
83
|
+
Description: CloudFront Id
|
|
84
|
+
Value: !Ref CloudFrontDistribution
|
|
85
|
+
Export:
|
|
86
|
+
Name: CloudFrontId
|
|
87
|
+
WebsiteUrl:
|
|
88
|
+
Description: Cloudfront URL
|
|
89
|
+
Value: !GetAtt CloudFrontDistribution.DomainName
|
|
90
|
+
Export:
|
|
91
|
+
Name: CloudFrontURL
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Resources:
|
|
2
|
+
AWSClassifySessionTable:
|
|
3
|
+
Type: AWS::DynamoDB::Table
|
|
4
|
+
Properties:
|
|
5
|
+
TableName: classifySessionStore
|
|
6
|
+
BillingMode: PAY_PER_REQUEST
|
|
7
|
+
AttributeDefinitions:
|
|
8
|
+
- AttributeName: sessionId
|
|
9
|
+
AttributeType: S
|
|
10
|
+
KeySchema:
|
|
11
|
+
- AttributeName: sessionId
|
|
12
|
+
KeyType: HASH
|
|
13
|
+
TimeToLiveSpecification:
|
|
14
|
+
AttributeName: expires
|
|
15
|
+
Enabled: true
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Resources:
|
|
2
|
+
CloudFrontOriginAccessIdentity:
|
|
3
|
+
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
|
|
4
|
+
Properties:
|
|
5
|
+
CloudFrontOriginAccessIdentityConfig:
|
|
6
|
+
Comment: !Join ["-", ["access-identity", !GetAtt S3Bucket.DomainName]]
|
|
7
|
+
CloudFrontDistribution:
|
|
8
|
+
Type: AWS::CloudFront::Distribution
|
|
9
|
+
DependsOn:
|
|
10
|
+
- S3Bucket
|
|
11
|
+
Properties:
|
|
12
|
+
DistributionConfig:
|
|
13
|
+
PriceClass: PriceClass_All
|
|
14
|
+
ViewerCertificate:
|
|
15
|
+
CloudFrontDefaultCertificate: true
|
|
16
|
+
Enabled: true
|
|
17
|
+
HttpVersion: http2
|
|
18
|
+
DefaultRootObject: index.html
|
|
19
|
+
Origins:
|
|
20
|
+
- Id: api
|
|
21
|
+
DomainName: !Join [".", [!Ref HttpApi, "execute-api", !Ref AWS::Region, "amazonaws.com"]]
|
|
22
|
+
CustomOriginConfig:
|
|
23
|
+
OriginProtocolPolicy: https-only
|
|
24
|
+
- Id: static
|
|
25
|
+
DomainName: !GetAtt S3Bucket.RegionalDomainName
|
|
26
|
+
S3OriginConfig:
|
|
27
|
+
OriginAccessIdentity: !Join ["", ["origin-access-identity/cloudfront/", !Ref CloudFrontOriginAccessIdentity]]
|
|
28
|
+
CacheBehaviors:
|
|
29
|
+
- PathPattern: api/*
|
|
30
|
+
TargetOriginId: api
|
|
31
|
+
# recently deprecated style of setting ForwardedValues
|
|
32
|
+
# but currently there's no alternative if using CloudFormation
|
|
33
|
+
ForwardedValues:
|
|
34
|
+
QueryString: true
|
|
35
|
+
ViewerProtocolPolicy: redirect-to-https
|
|
36
|
+
AllowedMethods: [GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE]
|
|
37
|
+
# No caching by default but caching can be set with appropriate headers in the api responses
|
|
38
|
+
MinTTL: 0
|
|
39
|
+
DefaultTTL: 0
|
|
40
|
+
DefaultCacheBehavior:
|
|
41
|
+
TargetOriginId: static
|
|
42
|
+
# 'Managed-CachingOptimized'
|
|
43
|
+
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
|
|
44
|
+
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
|
|
45
|
+
# 'Managed-CORS-S3Origin'
|
|
46
|
+
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html
|
|
47
|
+
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
|
|
48
|
+
ViewerProtocolPolicy: redirect-to-https
|
|
49
|
+
S3Bucket:
|
|
50
|
+
Type: AWS::S3::Bucket
|
|
51
|
+
Properties:
|
|
52
|
+
BucketName: ${self:custom.bucketName}
|
|
53
|
+
S3BucketPolicy:
|
|
54
|
+
Type: AWS::S3::BucketPolicy
|
|
55
|
+
Properties:
|
|
56
|
+
Bucket: !Ref S3Bucket
|
|
57
|
+
PolicyDocument:
|
|
58
|
+
Statement:
|
|
59
|
+
- Effect: Allow
|
|
60
|
+
Action: "s3:GetObject"
|
|
61
|
+
Resource: !Join ["", [!GetAtt S3Bucket.Arn, "/*"]]
|
|
62
|
+
Principal:
|
|
63
|
+
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
|
|
64
|
+
CustomDNSName:
|
|
65
|
+
Type: AWS::Route53::RecordSetGroup
|
|
66
|
+
Properties:
|
|
67
|
+
HostedZoneName: ${self:custom.hostedZoneName}
|
|
68
|
+
RecordSets:
|
|
69
|
+
- Name: ${self:custom.domainName}
|
|
70
|
+
Type: A
|
|
71
|
+
AliasTarget:
|
|
72
|
+
HostedZoneId: Z2FDTNDATAQYW2 #cloudfront hostedzone id
|
|
73
|
+
DNSName:
|
|
74
|
+
Fn::GetAtt:
|
|
75
|
+
- CloudFrontDistribution
|
|
76
|
+
- DomainName
|
|
77
|
+
|
|
78
|
+
Outputs:
|
|
79
|
+
CloudFrontId:
|
|
80
|
+
Description: CloudFront Id
|
|
81
|
+
Value: !Ref CloudFrontDistribution
|
|
82
|
+
Export:
|
|
83
|
+
Name: CloudFrontId
|
|
84
|
+
WebsiteUrl:
|
|
85
|
+
Description: Cloudfront URL
|
|
86
|
+
Value: !GetAtt CloudFrontDistribution.DomainName
|
|
87
|
+
Export:
|
|
88
|
+
Name: CloudFrontURL
|