lambder 1.0.15 → 1.0.17

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/dist/index.d.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from "aws-lambda";
2
2
  import Resolver, { ResolverResponse } from "./Resolver.js";
3
+ type DDBSession = {
4
+ userId: string;
5
+ userIdHash: string;
6
+ sessionHash: string;
7
+ csrfToken: string;
8
+ createdTimeStamp: number;
9
+ expiresTimeStamp: number;
10
+ data: {
11
+ [key: string]: any;
12
+ };
13
+ } | null;
3
14
  type RenderContext = {
4
15
  host: string;
5
16
  url: string;
@@ -14,6 +25,7 @@ type RenderContext = {
14
25
  [key: string]: any;
15
26
  };
16
27
  headers: APIGatewayProxyEventHeaders;
28
+ session: DDBSession;
17
29
  lambdaContext: Context;
18
30
  };
19
31
  type ConditionFunction = (renderContext: RenderContext) => boolean | Promise<boolean>;
@@ -39,18 +51,19 @@ export default class Lambder {
39
51
  apiVersion?: string;
40
52
  isCorsEnabled?: boolean;
41
53
  });
42
- private apiHandler;
43
- private pathHandler;
44
54
  setFallbackHandler(fallbackHandler: (renderContext: RenderContext, resolver: Resolver) => ResolverResponse): void;
45
55
  setGlobalErrorHandler(globalErrorHandler: (err: Error) => ResolverResponse): void;
56
+ addModule(moduleFn: Function): Promise<void>;
57
+ private validateSession;
58
+ addRoute(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
59
+ addSessionRoute(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
46
60
  addApi(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
47
- addPath(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
61
+ addSessionApi(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
48
62
  addHook(hookEvent: 'created', hookFn: HookCreatedFunction, priority?: number): Promise<void>;
49
63
  addHook(hookEvent: 'beforeRender', hookFn: HookBeforeRenderFunction, priority?: number): Promise<void>;
50
64
  addHook(hookEvent: 'afterRender', hookFn: HookAfterRenderFunction, priority?: number): Promise<void>;
51
65
  addHook(hookEvent: 'fallback', hookFn: HookFallbackFunction, priority?: number): Promise<void>;
52
66
  addHook(hookEvent: 'completed', hookFn: HookCompletedFunction, priority?: number): Promise<void>;
53
- addModule(moduleFn: Function): Promise<void>;
54
67
  render(event: APIGatewayProxyEvent, lambdaContext: Context): Promise<ResolverResponse>;
55
68
  }
56
69
  export {};
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export const createContext = (event, lambdaContext, resolve, reject) => {
9
9
  const cookie = cookieParser.parse(event.headers.Cookie || event.headers.cookie || "");
10
10
  const headers = event.headers;
11
11
  let post = {};
12
+ const session = null;
12
13
  try {
13
14
  const decodedBody = event.isBase64Encoded ? (event.body ? Buffer.from(event.body, "base64").toString() : "{}") : (event.body || "{}");
14
15
  try {
@@ -22,7 +23,7 @@ export const createContext = (event, lambdaContext, resolve, reject) => {
22
23
  if (/^(((\d+\.)+\d+)|(([a-f\d]+:)+[a-f\d]+))$/.test(host)) {
23
24
  reject(new Error("[404] Host cannot be an ip address: " + host));
24
25
  }
25
- return { host, url, method, get, post, cookie, headers, lambdaContext };
26
+ return { host, url, method, get, post, cookie, headers, session, lambdaContext };
26
27
  };
27
28
  export default class Lambder {
28
29
  apiPath;
@@ -46,56 +47,66 @@ export default class Lambder {
46
47
  "completed": []
47
48
  };
48
49
  }
49
- async apiHandler(renderContext, resolver, actionFn) {
50
- // Do context stuff;
51
- return await actionFn(renderContext, resolver);
52
- }
53
- async pathHandler(renderContext, resolver, actionFn) {
54
- // Do context stuff;
55
- return await actionFn(renderContext, resolver);
56
- }
57
50
  setFallbackHandler(fallbackHandler) {
58
51
  this.fallbackHandler = fallbackHandler;
59
52
  }
60
53
  setGlobalErrorHandler(globalErrorHandler) {
61
54
  this.globalErrorHandler = globalErrorHandler;
62
55
  }
56
+ async addModule(moduleFn) {
57
+ await moduleFn(this);
58
+ }
59
+ async validateSession(session) {
60
+ if (!session)
61
+ return false;
62
+ if (!session.userId || !session.userIdHash || !session.sessionHash || !session.csrfToken)
63
+ return false;
64
+ if (!session.createdTimeStamp || !session.expiresTimeStamp)
65
+ return false;
66
+ if (session.expiresTimeStamp > Date.now())
67
+ return false;
68
+ // Check DDB;
69
+ return false;
70
+ }
71
+ addRoute(condition, actionFn) {
72
+ this._actionList.push({
73
+ conditionFn: async (renderContext) => (renderContext.method === "GET" &&
74
+ ((typeof condition === "string" && renderContext.url === condition) ||
75
+ (typeof condition === "function" && (await condition(renderContext))) ||
76
+ (condition?.constructor == RegExp && condition.test(renderContext.url)))),
77
+ actionFn: async (renderContext, resolver) => await actionFn(renderContext, resolver)
78
+ });
79
+ }
80
+ addSessionRoute(condition, actionFn) {
81
+ this._actionList.push({
82
+ conditionFn: async (renderContext) => (renderContext.method === "GET" &&
83
+ ((typeof condition === "string" && renderContext.url === condition) ||
84
+ (typeof condition === "function" && (await condition(renderContext))) ||
85
+ (condition?.constructor == RegExp && condition.test(renderContext.url)))),
86
+ actionFn: async (renderContext, resolver) => await actionFn(renderContext, resolver)
87
+ });
88
+ }
63
89
  addApi(condition, actionFn) {
64
- let conditionFn = (renderContext) => false;
65
- if (typeof condition === "string") {
66
- conditionFn = (renderContext) => renderContext.method === "POST" && renderContext.url === this.apiPath && (renderContext.post?.api) === condition;
67
- }
68
- else if (typeof condition === "function") {
69
- conditionFn = async (renderContext) => renderContext.method === "POST" && renderContext.url === this.apiPath && (await condition(renderContext));
70
- }
71
- else if (condition?.constructor == RegExp) {
72
- conditionFn = (renderContext) => renderContext.method === "POST" && renderContext.url === this.apiPath && condition.test(renderContext.post?.api);
73
- }
74
- else {
75
- throw "Unsupported API Condition";
76
- }
77
90
  this._actionList.push({
78
- conditionFn,
79
- actionFn: async (renderContext, resolver) => await this.apiHandler(renderContext, resolver, actionFn)
91
+ conditionFn: async (renderContext) => (renderContext.method === "POST" && renderContext.url === this.apiPath &&
92
+ ((typeof condition === "string" && renderContext.post?.api === condition) ||
93
+ (typeof condition === "function" && (await condition(renderContext))) ||
94
+ (condition?.constructor == RegExp && condition.test(renderContext.post?.api)))),
95
+ actionFn: async (renderContext, resolver) => await actionFn(renderContext, resolver)
80
96
  });
81
97
  }
82
- addPath(condition, actionFn) {
83
- let conditionFn = (renderContext) => false;
84
- if (typeof condition === "string") {
85
- conditionFn = (renderContext) => renderContext.method === "GET" && renderContext.url === condition;
86
- }
87
- else if (typeof condition === "function") {
88
- conditionFn = async (renderContext) => renderContext.method === "GET" && (await condition(renderContext));
89
- }
90
- else if (condition?.constructor == RegExp) {
91
- conditionFn = (renderContext) => renderContext.method === "GET" && condition.test(renderContext.url);
92
- }
93
- else {
94
- throw "Unsupported Path Condition";
95
- }
98
+ addSessionApi(condition, actionFn) {
96
99
  this._actionList.push({
97
- conditionFn,
98
- actionFn: async (renderContext, resolver) => await this.pathHandler(renderContext, resolver, actionFn)
100
+ conditionFn: async (renderContext) => (renderContext.method === "POST" && renderContext.url === this.apiPath &&
101
+ ((typeof condition === "string" && renderContext.post?.api === condition) ||
102
+ (typeof condition === "function" && (await condition(renderContext))) ||
103
+ (condition?.constructor == RegExp && condition.test(renderContext.post?.api)))),
104
+ actionFn: async (renderContext, resolver) => {
105
+ const isSessionValid = this.validateSession(renderContext.session);
106
+ if (!isSessionValid)
107
+ throw new Error("Session not found");
108
+ return await actionFn(renderContext, resolver);
109
+ }
99
110
  });
100
111
  }
101
112
  async addHook(hookEvent, hookFn, priority = 0) {
@@ -107,9 +118,6 @@ export default class Lambder {
107
118
  this._hookList[hookEvent].sort((a, b) => a.priority - b.priority);
108
119
  }
109
120
  }
110
- async addModule(moduleFn) {
111
- await moduleFn(this);
112
- }
113
121
  async render(event, lambdaContext) {
114
122
  return await new Promise(async (resolve, reject) => {
115
123
  const resolver = new Resolver({
@@ -121,14 +129,12 @@ export default class Lambder {
121
129
  let renderContext = createContext(event, lambdaContext, resolve, reject);
122
130
  if (renderContext.method === "OPTIONS")
123
131
  return resolver.cors();
124
- let firstMatchedAction = null;
125
- for (const action of this._actionList) {
126
- const isConditionMet = await action.conditionFn(renderContext);
127
- if (isConditionMet) {
128
- firstMatchedAction = action;
129
- break;
132
+ const firstMatchedAction = await (async () => {
133
+ for (const action of this._actionList) {
134
+ if (await action.conditionFn(renderContext))
135
+ return action;
130
136
  }
131
- }
137
+ })();
132
138
  if (firstMatchedAction) {
133
139
  for (const hook of this._hookList["beforeRender"]) {
134
140
  renderContext = await hook.hookFn(renderContext, resolver);
@@ -140,8 +146,9 @@ export default class Lambder {
140
146
  resolve(renderResult);
141
147
  }
142
148
  else {
143
- for (const hook of this._hookList["fallback"])
149
+ for (const hook of this._hookList["fallback"]) {
144
150
  await hook.hookFn(renderContext, resolver);
151
+ }
145
152
  if (this.fallbackHandler) {
146
153
  const renderResult = await this.fallbackHandler(renderContext, resolver);
147
154
  resolve(renderResult);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambder",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -4,6 +4,16 @@ import cookieParser from "cookie";
4
4
  import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from "aws-lambda";
5
5
  import Resolver, { ResolverResponse } from "./Resolver.js";
6
6
 
7
+ type DDBSession = {
8
+ userId: string,
9
+ userIdHash: string;
10
+ sessionHash: string;
11
+ csrfToken: string;
12
+ createdTimeStamp: number;
13
+ expiresTimeStamp: number;
14
+ data: { [key:string]: any };
15
+ } | null;
16
+
7
17
  type RenderContext = {
8
18
  host: string;
9
19
  url: string;
@@ -12,6 +22,7 @@ type RenderContext = {
12
22
  post: { [key: string]: any };
13
23
  cookie: { [key: string]: any };
14
24
  headers: APIGatewayProxyEventHeaders;
25
+ session: DDBSession;
15
26
  lambdaContext: Context;
16
27
  };
17
28
 
@@ -44,13 +55,14 @@ export const createContext = (
44
55
  const cookie = cookieParser.parse(event.headers.Cookie || event.headers.cookie || "");
45
56
  const headers = event.headers;
46
57
  let post:{ [key:string]: any } = {};
58
+ const session = null;
47
59
  try {
48
60
  const decodedBody = event.isBase64Encoded ? ( event.body ? Buffer.from(event.body,"base64").toString() : "{}" ) : ( event.body || "{}" );
49
61
  try { post = JSON.parse(decodedBody) || {}; }
50
62
  catch(e){ post = querystring.parse(decodedBody) || {}; }
51
63
  }catch(e){}
52
64
  if(/^(((\d+\.)+\d+)|(([a-f\d]+:)+[a-f\d]+))$/.test(host)){ reject(new Error("[404] Host cannot be an ip address: " + host)); }
53
- return { host, url, method, get, post, cookie, headers, lambdaContext };
65
+ return { host, url, method, get, post, cookie, headers, session, lambdaContext };
54
66
  }
55
67
 
56
68
 
@@ -88,14 +100,6 @@ export default class Lambder {
88
100
  };
89
101
  }
90
102
 
91
- private async apiHandler (renderContext:RenderContext, resolver: Resolver, actionFn: ActionFunction){
92
- // Do context stuff;
93
- return await actionFn(renderContext, resolver);
94
- }
95
- private async pathHandler (renderContext:RenderContext, resolver: Resolver, actionFn: ActionFunction){
96
- // Do context stuff;
97
- return await actionFn(renderContext, resolver);
98
- }
99
103
  setFallbackHandler(fallbackHandler: (renderContext:RenderContext, resolver: Resolver)=>ResolverResponse){
100
104
  this.fallbackHandler = fallbackHandler;
101
105
  }
@@ -103,42 +107,78 @@ export default class Lambder {
103
107
  this.globalErrorHandler = globalErrorHandler;
104
108
  }
105
109
 
106
- addApi(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
107
- let conditionFn: ConditionFunction = (renderContext:RenderContext) => false;
108
- if(typeof condition === "string"){
109
- conditionFn = (renderContext:RenderContext) =>
110
- renderContext.method === "POST" && renderContext.url === this.apiPath && (renderContext.post?.api) === condition;
111
- }else if(typeof condition === "function"){
112
- conditionFn = async (renderContext:RenderContext) =>
113
- renderContext.method === "POST" && renderContext.url === this.apiPath && (await condition(renderContext));
114
- }else if(condition?.constructor == RegExp){
115
- conditionFn = (renderContext:RenderContext) =>
116
- renderContext.method === "POST" && renderContext.url === this.apiPath && condition.test(renderContext.post?.api);
117
- }else { throw "Unsupported API Condition"; }
110
+ async addModule(moduleFn: Function): Promise<void>{
111
+ await moduleFn(this);
112
+ }
113
+
114
+ private async validateSession (session: DDBSession): Promise<boolean>{
115
+ if(!session) return false;
116
+ if(!session.userId || !session.userIdHash || !session.sessionHash || !session.csrfToken) return false;
117
+ if(!session.createdTimeStamp || !session.expiresTimeStamp) return false;
118
+ if(session.expiresTimeStamp > Date.now()) return false;
119
+ // Check DDB;
120
+ return false;
121
+ }
122
+
123
+ addRoute(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
118
124
  this._actionList.push({
119
- conditionFn,
120
- actionFn: async (renderContext:RenderContext, resolver: Resolver) => await this.apiHandler(renderContext, resolver, actionFn)
125
+ conditionFn: async (renderContext:RenderContext) => (
126
+ renderContext.method === "GET" &&
127
+ (
128
+ (typeof condition === "string" && renderContext.url === condition) ||
129
+ (typeof condition === "function" && (await condition(renderContext))) ||
130
+ (condition?.constructor == RegExp && condition.test(renderContext.url))
131
+ )
132
+ ),
133
+ actionFn: async (renderContext:RenderContext, resolver: Resolver) => await actionFn(renderContext, resolver)
121
134
  });
122
135
  }
123
136
 
124
- addPath(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
125
- let conditionFn: ConditionFunction = (renderContext:RenderContext) => false;
126
- if(typeof condition === "string"){
127
- conditionFn = (renderContext:RenderContext) =>
128
- renderContext.method === "GET" && renderContext.url === condition;
129
- }else if(typeof condition === "function"){
130
- conditionFn = async (renderContext:RenderContext) =>
131
- renderContext.method === "GET" && (await condition(renderContext));
132
- }else if(condition?.constructor == RegExp){
133
- conditionFn = (renderContext:RenderContext) =>
134
- renderContext.method === "GET" && condition.test(renderContext.url);
135
- }else { throw "Unsupported Path Condition"; }
137
+ addSessionRoute(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
138
+ this._actionList.push({
139
+ conditionFn: async (renderContext:RenderContext) => (
140
+ renderContext.method === "GET" &&
141
+ (
142
+ (typeof condition === "string" && renderContext.url === condition) ||
143
+ (typeof condition === "function" && (await condition(renderContext))) ||
144
+ (condition?.constructor == RegExp && condition.test(renderContext.url))
145
+ )
146
+ ),
147
+ actionFn: async (renderContext:RenderContext, resolver: Resolver) => await actionFn(renderContext, resolver)
148
+ });
149
+ }
150
+
151
+ addApi(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
136
152
  this._actionList.push({
137
- conditionFn,
138
- actionFn: async (renderContext:RenderContext, resolver: Resolver) => await this.pathHandler(renderContext, resolver, actionFn)
153
+ conditionFn: async (renderContext:RenderContext) => (
154
+ renderContext.method === "POST" && renderContext.url === this.apiPath &&
155
+ (
156
+ (typeof condition === "string" && renderContext.post?.api === condition) ||
157
+ (typeof condition === "function" && (await condition(renderContext))) ||
158
+ (condition?.constructor == RegExp && condition.test(renderContext.post?.api))
159
+ )
160
+ ),
161
+ actionFn: async (renderContext:RenderContext, resolver: Resolver) => await actionFn(renderContext, resolver)
139
162
  });
140
163
  }
141
164
 
165
+ addSessionApi(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
166
+ this._actionList.push({
167
+ conditionFn: async (renderContext:RenderContext) => (
168
+ renderContext.method === "POST" && renderContext.url === this.apiPath &&
169
+ (
170
+ (typeof condition === "string" && renderContext.post?.api === condition) ||
171
+ (typeof condition === "function" && (await condition(renderContext))) ||
172
+ (condition?.constructor == RegExp && condition.test(renderContext.post?.api))
173
+ )
174
+ ),
175
+ actionFn: async (renderContext:RenderContext, resolver: Resolver) => {
176
+ const isSessionValid = this.validateSession(renderContext.session);
177
+ if(!isSessionValid) throw new Error("Session not found");
178
+ return await actionFn(renderContext, resolver);
179
+ }
180
+ });
181
+ }
142
182
 
143
183
 
144
184
  async addHook(hookEvent: 'created', hookFn: HookCreatedFunction, priority?: number): Promise<void>;
@@ -159,10 +199,6 @@ export default class Lambder {
159
199
  }
160
200
  }
161
201
 
162
- async addModule(moduleFn: Function): Promise<void>{
163
- await moduleFn(this);
164
- }
165
-
166
202
  async render(
167
203
  event: APIGatewayProxyEvent,
168
204
  lambdaContext: Context
@@ -171,7 +207,6 @@ export default class Lambder {
171
207
  resolve:(renderResult: ResolverResponse)=>void,
172
208
  reject:(err: Error)=>void
173
209
  )=> {
174
-
175
210
  const resolver = new Resolver({
176
211
  isCorsEnabled: this.isCorsEnabled,
177
212
  publicPath: this.publicPath,
@@ -179,17 +214,13 @@ export default class Lambder {
179
214
  resolve, reject,
180
215
  });
181
216
  let renderContext:RenderContext = createContext(event, lambdaContext, resolve, reject)
182
-
183
217
  if(renderContext.method === "OPTIONS") return resolver.cors();
184
218
 
185
- let firstMatchedAction: null|ActionType = null;
186
- for(const action of this._actionList){
187
- const isConditionMet = await action.conditionFn(renderContext);
188
- if(isConditionMet){
189
- firstMatchedAction = action;
190
- break;
219
+ const firstMatchedAction = await (async () => {
220
+ for (const action of this._actionList) {
221
+ if (await action.conditionFn(renderContext)) return action;
191
222
  }
192
- }
223
+ })();
193
224
 
194
225
  if(firstMatchedAction){
195
226
  for(const hook of this._hookList["beforeRender"]){
@@ -201,7 +232,9 @@ export default class Lambder {
201
232
  }
202
233
  resolve(renderResult);
203
234
  }else{
204
- for(const hook of this._hookList["fallback"]) await hook.hookFn(renderContext, resolver);
235
+ for(const hook of this._hookList["fallback"]){
236
+ await hook.hookFn(renderContext, resolver);
237
+ }
205
238
  if(this.fallbackHandler){
206
239
  const renderResult = await this.fallbackHandler(renderContext, resolver);
207
240
  resolve(renderResult);