@midwayjs/faas 3.4.0-beta.1 → 3.4.0-beta.10

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2013 - Now midwayjs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -33,8 +33,10 @@ let FaaSConfiguration = class FaaSConfiguration {
33
33
  async onReady(container) { }
34
34
  async onServerReady() {
35
35
  if (!this.framework.isEnable()) {
36
+ // just in legacy local dev and test
36
37
  await this.framework.run();
37
38
  }
39
+ await this.framework.loadFunction();
38
40
  }
39
41
  };
40
42
  __decorate([
@@ -11,34 +11,29 @@ export declare class MidwayFaaSFramework extends BaseFramework<Application, Cont
11
11
  app: Application;
12
12
  private isReplaceLogger;
13
13
  private developmentRun;
14
- private serverlessRoutes;
15
14
  private server;
16
15
  private respond;
17
16
  private applicationAdapter;
17
+ private serverlessFunctionService;
18
18
  protected httpMiddlewareManager: ContextMiddlewareManager<Context, unknown, unknown>;
19
19
  protected eventMiddlewareManager: ContextMiddlewareManager<Context, unknown, unknown>;
20
+ private legacyVersion;
21
+ private loadedFunction;
20
22
  environmentService: MidwayEnvironmentService;
21
23
  middlewareService: MidwayMiddlewareService<Context, any>;
22
24
  configure(options: IFaaSConfigurationOptions): any;
23
25
  isEnable(): boolean;
24
26
  applicationInitialize(options: IMidwayBootstrapOptions): Promise<void>;
25
27
  run(): Promise<void>;
28
+ loadFunction(): Promise<void>;
26
29
  getFrameworkType(): MidwayFrameworkType;
27
30
  /**
28
31
  * @deprecated
29
32
  * @param handlerMapping
30
33
  */
31
34
  handleInvokeWrapper(handlerMapping: string): (...args: any[]) => Promise<any>;
32
- getTriggerFunction(handlerMapping: string): (context: any, options: HandlerOptions) => Promise<{
33
- result: any;
34
- error: Error;
35
- } | {
36
- isBase64Encoded: boolean;
37
- statusCode: any;
38
- headers: any;
39
- body: any;
40
- }>;
41
- wrapHttpRequest(req: http.IncomingMessage, res?: http.ServerResponse): Promise<unknown>;
35
+ getTriggerFunction(handlerMapping: string): (context: any, options: HandlerOptions) => Promise<any>;
36
+ wrapHttpRequest(req: http.IncomingMessage | Record<string, any>, res?: http.ServerResponse): Promise<unknown>;
42
37
  /**
43
38
  * @deprecated
44
39
  * @param middlewareId
@@ -50,11 +45,11 @@ export declare class MidwayFaaSFramework extends BaseFramework<Application, Cont
50
45
  createLogger(name: string, option?: LoggerOptions): import("@midwayjs/core").ILogger;
51
46
  getFrameworkName(): string;
52
47
  getServer(): http.Server;
48
+ beforeStop(): Promise<void>;
53
49
  protected createHttpContext(req: any, res: any): Promise<unknown>;
54
50
  useMiddleware(middleware: CommonMiddlewareUnion<Context, NextFunction, undefined>): void;
55
51
  useEventMiddleware(middleware: CommonMiddlewareUnion<Context, NextFunction, undefined>): void;
56
52
  getEventMiddleware(): ContextMiddlewareManager<Context, NextFunction, undefined>;
57
53
  getAllHandlerNames(): string[];
58
54
  }
59
- export declare const createModuleServerlessFramework: (globalOption: Omit<IMidwayBootstrapOptions, 'applicationContext'> & IFaaSConfigurationOptions) => Promise<MidwayFaaSFramework>;
60
55
  //# sourceMappingURL=framework.d.ts.map
package/dist/framework.js CHANGED
@@ -9,12 +9,14 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.createModuleServerlessFramework = exports.MidwayFaaSFramework = void 0;
12
+ exports.MidwayFaaSFramework = void 0;
13
13
  const core_1 = require("@midwayjs/core");
14
14
  const decorator_1 = require("@midwayjs/decorator");
15
15
  const simple_lock_1 = require("@midwayjs/simple-lock");
16
16
  const logger_1 = require("@midwayjs/logger");
17
17
  const serverless_http_parser_1 = require("@midwayjs/serverless-http-parser");
18
+ const util_1 = require("util");
19
+ const { isAnyArrayBuffer, isUint8Array } = util_1.types;
18
20
  const LOCK_KEY = '_faas_starter_start_key';
19
21
  let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework {
20
22
  constructor() {
@@ -24,9 +26,10 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
24
26
  this.lock = new simple_lock_1.default();
25
27
  this.isReplaceLogger = process.env['MIDWAY_SERVERLESS_REPLACE_LOGGER'] === 'true';
26
28
  this.developmentRun = false;
27
- this.serverlessRoutes = [];
28
29
  this.httpMiddlewareManager = this.createMiddlewareManager();
29
30
  this.eventMiddlewareManager = this.createMiddlewareManager();
31
+ this.legacyVersion = false;
32
+ this.loadedFunction = false;
30
33
  }
31
34
  configure(options) {
32
35
  var _a;
@@ -49,6 +52,9 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
49
52
  }
50
53
  this.applicationAdapter =
51
54
  this.configurationOptions.applicationAdapter || {};
55
+ if (this.applicationAdapter.getApplication) {
56
+ this.legacyVersion = true;
57
+ }
52
58
  this.app =
53
59
  ((_b = (_a = this.applicationAdapter).getApplication) === null || _b === void 0 ? void 0 : _b.call(_a)) ||
54
60
  new serverless_http_parser_1.Application();
@@ -104,26 +110,27 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
104
110
  }
105
111
  }
106
112
  async run() {
107
- return this.lock.sureOnce(async () => {
108
- var _a;
109
- // set app keys
110
- this.app['keys'] = (_a = this.configService.getConfiguration('keys')) !== null && _a !== void 0 ? _a : '';
111
- // store all http function entry
112
- const collector = new core_1.ServerlessTriggerCollector();
113
- const functionList = await collector.getFunctionList();
114
- for (const funcInfo of functionList) {
115
- // store handler
116
- this.funMappingStore.set(funcInfo.funcHandlerName, funcInfo);
117
- if (funcInfo.url) {
118
- // store router
119
- this.serverlessRoutes.push({
120
- matchPattern: (0, core_1.pathToRegexp)(funcInfo.url, [], { end: false }),
121
- funcInfo: funcInfo,
122
- });
113
+ if (this.legacyVersion) {
114
+ return this.loadFunction();
115
+ }
116
+ }
117
+ async loadFunction() {
118
+ if (!this.loadedFunction) {
119
+ this.loadedFunction = true;
120
+ return this.lock.sureOnce(async () => {
121
+ var _a;
122
+ // set app keys
123
+ this.app['keys'] = (_a = this.configService.getConfiguration('keys')) !== null && _a !== void 0 ? _a : '';
124
+ // store all http function entry
125
+ this.serverlessFunctionService = await this.applicationContext.getAsync(core_1.MidwayServerlessFunctionService);
126
+ const functionList = await this.serverlessFunctionService.getFunctionList();
127
+ for (const funcInfo of functionList) {
128
+ // store handler
129
+ this.funMappingStore.set(funcInfo.funcHandlerName, funcInfo);
123
130
  }
124
- }
125
- this.respond = this.app.callback();
126
- }, LOCK_KEY);
131
+ this.respond = this.app.callback();
132
+ }, LOCK_KEY);
133
+ }
127
134
  }
128
135
  getFrameworkType() {
129
136
  return core_1.MidwayFrameworkType.FAAS;
@@ -160,7 +167,13 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
160
167
  // invoke handler
161
168
  const result = await this.invokeHandler(funOptions, ctx, args, isHttpFunction);
162
169
  if (isHttpFunction && result !== undefined) {
163
- ctx.body = result;
170
+ if (result === null) {
171
+ // 这样设置可以绕过 koa 的 _explicitStatus 赋值机制
172
+ ctx.response._body = null;
173
+ }
174
+ else {
175
+ ctx.body = result;
176
+ }
164
177
  }
165
178
  return result;
166
179
  },
@@ -177,12 +190,10 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
177
190
  var _a;
178
191
  const isHttpFunction = options.isHttpFunction;
179
192
  if (!funOptions && isHttpFunction) {
180
- for (const item of this.serverlessRoutes) {
181
- if (context.method === item.funcInfo['requestMethod'].toUpperCase() &&
182
- item.matchPattern.test(context.path)) {
183
- funOptions = item.funcInfo;
184
- break;
185
- }
193
+ funOptions = await this.serverlessFunctionService.getMatchedRouterInfo(context.path, context.method);
194
+ if (funOptions) {
195
+ const matchRes = core_1.PathToRegexpUtil.match(funOptions.fullUrlFlattenString)(context.path);
196
+ context.req.pathParameters = matchRes['params'] || {};
186
197
  }
187
198
  }
188
199
  if (!funOptions) {
@@ -207,9 +218,18 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
207
218
  // invoke handler
208
219
  const result = await this.invokeHandler(funOptions, ctx, args, isHttpFunction);
209
220
  if (isHttpFunction && result !== undefined) {
210
- ctx.body = result;
221
+ if (result === null) {
222
+ // 这样设置可以绕过 koa 的 _explicitStatus 赋值机制
223
+ ctx.response._body = null;
224
+ }
225
+ else {
226
+ ctx.body = result;
227
+ }
228
+ }
229
+ // http 靠 ctx.body,否则会出现状态码不正确的问题
230
+ if (!isHttpFunction) {
231
+ return result;
211
232
  }
212
- return result;
213
233
  },
214
234
  ], this.app);
215
235
  return await fn(ctx, next);
@@ -230,13 +250,13 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
230
250
  }
231
251
  context.body = data;
232
252
  }
233
- else if (Buffer.isBuffer(data)) {
253
+ else if (isAnyArrayBuffer(data) || isUint8Array(data)) {
234
254
  encoded = true;
235
255
  if (!context.type) {
236
256
  context.type = 'application/octet-stream';
237
257
  }
238
258
  // data is reserved as buffer
239
- context.body = data.toString('base64');
259
+ context.body = Buffer.from(data).toString('base64');
240
260
  }
241
261
  else if (typeof data === 'object') {
242
262
  if (!context.type) {
@@ -252,6 +272,13 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
252
272
  // set data to string
253
273
  context.body = data = data + '';
254
274
  }
275
+ // middleware return value and will be got 204 status
276
+ if (context.body === undefined &&
277
+ !context.response._explicitStatus &&
278
+ context._matchedRoute) {
279
+ // 如果进了路由,重新赋值,防止 404
280
+ context.body = undefined;
281
+ }
255
282
  return {
256
283
  isBase64Encoded: encoded,
257
284
  statusCode: context.status,
@@ -296,36 +323,44 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
296
323
  return context;
297
324
  }
298
325
  async invokeHandler(routerInfo, context, args, isHttpFunction) {
299
- const funModule = await context.requestContext.getAsync(routerInfo.controllerId);
300
- const handlerName = this.getFunctionHandler(context, args, funModule, routerInfo.method) ||
301
- this.defaultHandlerMethod;
302
- if (funModule[handlerName]) {
303
- // invoke real method
304
- const result = await funModule[handlerName](...args);
305
- // implement response decorator
306
- const routerResponseData = routerInfo.responseMetadata;
307
- if (isHttpFunction) {
308
- for (const routerRes of routerResponseData) {
309
- switch (routerRes.type) {
310
- case decorator_1.WEB_RESPONSE_HTTP_CODE:
311
- context.status = routerRes.code;
312
- break;
313
- case decorator_1.WEB_RESPONSE_HEADER:
314
- for (const key in (routerRes === null || routerRes === void 0 ? void 0 : routerRes.setHeaders) || {}) {
315
- context.set(key, routerRes.setHeaders[key]);
316
- }
317
- break;
318
- case decorator_1.WEB_RESPONSE_CONTENT_TYPE:
319
- context.type = routerRes.contentType;
320
- break;
321
- case decorator_1.WEB_RESPONSE_REDIRECT:
322
- context.status = routerRes.code;
323
- context.redirect(routerRes.url);
324
- return;
326
+ if (typeof routerInfo.method !== 'string') {
327
+ if (!isHttpFunction) {
328
+ args.unshift(context);
329
+ }
330
+ return routerInfo.method(...args);
331
+ }
332
+ else {
333
+ const funModule = await context.requestContext.getAsync(routerInfo.controllerId);
334
+ const handlerName = this.getFunctionHandler(context, args, funModule, routerInfo.method) ||
335
+ this.defaultHandlerMethod;
336
+ if (funModule[handlerName]) {
337
+ // invoke real method
338
+ const result = await funModule[handlerName](...args);
339
+ // implement response decorator
340
+ const routerResponseData = routerInfo.responseMetadata;
341
+ if (isHttpFunction) {
342
+ for (const routerRes of routerResponseData) {
343
+ switch (routerRes.type) {
344
+ case decorator_1.WEB_RESPONSE_HTTP_CODE:
345
+ context.status = routerRes.code;
346
+ break;
347
+ case decorator_1.WEB_RESPONSE_HEADER:
348
+ for (const key in (routerRes === null || routerRes === void 0 ? void 0 : routerRes.setHeaders) || {}) {
349
+ context.set(key, routerRes.setHeaders[key]);
350
+ }
351
+ break;
352
+ case decorator_1.WEB_RESPONSE_CONTENT_TYPE:
353
+ context.type = routerRes.contentType;
354
+ break;
355
+ case decorator_1.WEB_RESPONSE_REDIRECT:
356
+ context.status = routerRes.code;
357
+ context.redirect(routerRes.url);
358
+ return;
359
+ }
325
360
  }
326
361
  }
362
+ return result;
327
363
  }
328
- return result;
329
364
  }
330
365
  }
331
366
  getFunctionHandler(ctx, args, target, method) {
@@ -348,6 +383,13 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
348
383
  getServer() {
349
384
  return this.server;
350
385
  }
386
+ async beforeStop() {
387
+ if (this.server) {
388
+ new Promise(resolve => {
389
+ this.server.close(resolve);
390
+ });
391
+ }
392
+ }
351
393
  async createHttpContext(req, res) {
352
394
  return new Promise(resolve => {
353
395
  this.respond(req, res, resolve);
@@ -378,13 +420,4 @@ MidwayFaaSFramework = __decorate([
378
420
  (0, decorator_1.Framework)()
379
421
  ], MidwayFaaSFramework);
380
422
  exports.MidwayFaaSFramework = MidwayFaaSFramework;
381
- const createModuleServerlessFramework = async (globalOption) => {
382
- const applicationContext = await (0, core_1.initializeGlobalApplicationContext)({
383
- ...globalOption,
384
- baseDir: '',
385
- appDir: '',
386
- });
387
- return applicationContext.get(MidwayFaaSFramework);
388
- };
389
- exports.createModuleServerlessFramework = createModuleServerlessFramework;
390
423
  //# sourceMappingURL=framework.js.map
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './interface';
2
- export { MidwayFaaSFramework as Framework, createModuleServerlessFramework, } from './framework';
2
+ export { MidwayFaaSFramework as Framework } from './framework';
3
3
  export { FaaSConfiguration as Configuration } from './configuration';
4
+ export { AbstractBootstrapStarter } from './starter';
4
5
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -14,11 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.Configuration = exports.createModuleServerlessFramework = exports.Framework = void 0;
17
+ exports.AbstractBootstrapStarter = exports.Configuration = exports.Framework = void 0;
18
18
  __exportStar(require("./interface"), exports);
19
19
  var framework_1 = require("./framework");
20
20
  Object.defineProperty(exports, "Framework", { enumerable: true, get: function () { return framework_1.MidwayFaaSFramework; } });
21
- Object.defineProperty(exports, "createModuleServerlessFramework", { enumerable: true, get: function () { return framework_1.createModuleServerlessFramework; } });
22
21
  var configuration_1 = require("./configuration");
23
22
  Object.defineProperty(exports, "Configuration", { enumerable: true, get: function () { return configuration_1.FaaSConfiguration; } });
23
+ var starter_1 = require("./starter");
24
+ Object.defineProperty(exports, "AbstractBootstrapStarter", { enumerable: true, get: function () { return starter_1.AbstractBootstrapStarter; } });
24
25
  //# sourceMappingURL=index.js.map
@@ -47,6 +47,9 @@ export interface IFaaSConfigurationOptions extends IConfigurationOptions {
47
47
  config?: object;
48
48
  initializeContext?: object;
49
49
  applicationAdapter?: {
50
+ /**
51
+ * @deprecated
52
+ */
50
53
  getApplication(): Application;
51
54
  getFunctionName(): string;
52
55
  getFunctionServiceName(): string;
@@ -61,6 +64,9 @@ export interface IWebMiddleware {
61
64
  }
62
65
  export interface ServerlessStarterOptions extends IMidwayBootstrapOptions {
63
66
  initializeMethodName?: string;
67
+ handlerName?: string;
68
+ aggregationHandlerName?: string;
69
+ handlerNameMapping?: (handlerName: string, ...args: unknown[]) => [string, ...unknown[]];
64
70
  createAdapter?: () => Promise<{
65
71
  close(): any;
66
72
  createAppHook(app?: any): any;
@@ -69,6 +75,5 @@ export interface ServerlessStarterOptions extends IMidwayBootstrapOptions {
69
75
  mark(label: string): any;
70
76
  end(): any;
71
77
  };
72
- exportAllHandler?: boolean;
73
78
  }
74
79
  //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { ServerlessStarterOptions } from './interface';
2
+ import { IMidwayBootstrapOptions } from '@midwayjs/core';
3
+ export declare abstract class AbstractBootstrapStarter {
4
+ protected options: ServerlessStarterOptions;
5
+ protected applicationContext: any;
6
+ protected framework: any;
7
+ constructor(options?: ServerlessStarterOptions);
8
+ getApplicationContext(): any;
9
+ close(): Promise<void>;
10
+ start(options?: ServerlessStarterOptions): any;
11
+ initFramework(bootstrapOptions?: IMidwayBootstrapOptions): Promise<void>;
12
+ abstract onStart(): any;
13
+ abstract onInit(...args: unknown[]): any;
14
+ abstract onRequest(...args: unknown[]): any;
15
+ abstract onClose(): any;
16
+ }
17
+ //# sourceMappingURL=starter.d.ts.map
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractBootstrapStarter = void 0;
4
+ const core_1 = require("@midwayjs/core");
5
+ class AbstractBootstrapStarter {
6
+ constructor(options = {}) {
7
+ this.options = options;
8
+ }
9
+ getApplicationContext() {
10
+ return this.applicationContext;
11
+ }
12
+ async close() {
13
+ await this.onClose();
14
+ }
15
+ start(options) {
16
+ this.options = Object.assign(this.options, options);
17
+ return this.onStart();
18
+ }
19
+ async initFramework(bootstrapOptions = {}) {
20
+ // init midway
21
+ this.applicationContext = await (0, core_1.initializeGlobalApplicationContext)(bootstrapOptions);
22
+ const midwayFrameworkService = this.applicationContext.get(core_1.MidwayFrameworkService);
23
+ this.framework = midwayFrameworkService.getMainFramework();
24
+ }
25
+ }
26
+ exports.AbstractBootstrapStarter = AbstractBootstrapStarter;
27
+ //# sourceMappingURL=starter.js.map
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@midwayjs/faas",
3
- "version": "3.4.0-beta.1",
3
+ "version": "3.4.0-beta.10",
4
4
  "main": "dist/index",
5
5
  "typings": "index.d.ts",
6
6
  "dependencies": {
7
- "@midwayjs/core": "^3.4.0-beta.1",
7
+ "@midwayjs/core": "^3.4.0-beta.10",
8
8
  "@midwayjs/faas-typings": "^3.3.5",
9
9
  "@midwayjs/logger": "^2.15.0",
10
- "@midwayjs/serverless-http-parser": "^3.3.5",
10
+ "@midwayjs/serverless-http-parser": "^3.4.0-beta.10",
11
11
  "@midwayjs/simple-lock": "^1.1.4"
12
12
  },
13
13
  "devDependencies": {
14
- "@midwayjs/decorator": "^3.4.0-beta.1",
15
- "@midwayjs/mock": "^3.4.0-beta.1",
16
- "@midwayjs/serverless-fc-starter": "^3.4.0-beta.1",
17
- "@midwayjs/serverless-scf-starter": "^3.3.5",
14
+ "@midwayjs/decorator": "^3.4.0-beta.10",
15
+ "@midwayjs/mock": "^3.4.0-beta.10",
16
+ "@midwayjs/serverless-fc-starter": "^3.4.0-beta.10",
17
+ "@midwayjs/serverless-scf-starter": "^3.4.0-beta.10",
18
18
  "mm": "3.2.0"
19
19
  },
20
20
  "engines": {
@@ -46,5 +46,5 @@
46
46
  "url": "git@github.com:midwayjs/midway.git"
47
47
  },
48
48
  "license": "MIT",
49
- "gitHead": "a603d2348d6141f8f723901498f03a162a037708"
49
+ "gitHead": "560a5fa311605e4008c55f31c31bc4e12eeff2a5"
50
50
  }