@tremho/mist-lift 1.0.0

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.
Files changed (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/build/commands/actions/initQuestions.js +136 -0
  4. package/build/commands/actions/initQuestions.js.map +1 -0
  5. package/build/commands/actions/makePackageJson.js +2 -0
  6. package/build/commands/actions/makePackageJson.js.map +1 -0
  7. package/build/commands/actions/setupPackageJson.js +68 -0
  8. package/build/commands/actions/setupPackageJson.js.map +1 -0
  9. package/build/commands/build.js +191 -0
  10. package/build/commands/build.js.map +1 -0
  11. package/build/commands/builtin/ApiDocMaker.js +72 -0
  12. package/build/commands/builtin/ApiDocMaker.js.map +1 -0
  13. package/build/commands/builtin/BuiltInHandler.js +62 -0
  14. package/build/commands/builtin/BuiltInHandler.js.map +1 -0
  15. package/build/commands/builtin/DeployBuiltInZip.js +57 -0
  16. package/build/commands/builtin/DeployBuiltInZip.js.map +1 -0
  17. package/build/commands/builtin/StageWebrootZip.js +70 -0
  18. package/build/commands/builtin/StageWebrootZip.js.map +1 -0
  19. package/build/commands/builtin/prebuilt-zips/API.zip +0 -0
  20. package/build/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
  21. package/build/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
  22. package/build/commands/create.js +75 -0
  23. package/build/commands/create.js.map +1 -0
  24. package/build/commands/deploy.js +187 -0
  25. package/build/commands/deploy.js.map +1 -0
  26. package/build/commands/doctor.js +137 -0
  27. package/build/commands/doctor.js.map +1 -0
  28. package/build/commands/help.js +205 -0
  29. package/build/commands/help.js.map +1 -0
  30. package/build/commands/init.js +92 -0
  31. package/build/commands/init.js.map +1 -0
  32. package/build/commands/package.js +249 -0
  33. package/build/commands/package.js.map +1 -0
  34. package/build/commands/publish.js +344 -0
  35. package/build/commands/publish.js.map +1 -0
  36. package/build/commands/settings.js +95 -0
  37. package/build/commands/settings.js.map +1 -0
  38. package/build/commands/start.js +66 -0
  39. package/build/commands/start.js.map +1 -0
  40. package/build/commands/test.js +52 -0
  41. package/build/commands/test.js.map +1 -0
  42. package/build/commands/user.js +20 -0
  43. package/build/commands/user.js.map +1 -0
  44. package/build/expressRoutes/all.js +129 -0
  45. package/build/expressRoutes/all.js.map +1 -0
  46. package/build/expressRoutes/api.js +22 -0
  47. package/build/expressRoutes/api.js.map +1 -0
  48. package/build/expressRoutes/functionBinder.js +191 -0
  49. package/build/expressRoutes/functionBinder.js.map +1 -0
  50. package/build/lib/CaseUtils.js +57 -0
  51. package/build/lib/CaseUtils.js.map +1 -0
  52. package/build/lib/DirectoryUtils.js +37 -0
  53. package/build/lib/DirectoryUtils.js.map +1 -0
  54. package/build/lib/LiftConfig.js +83 -0
  55. package/build/lib/LiftConfig.js.map +1 -0
  56. package/build/lib/LiftVersion.js +117 -0
  57. package/build/lib/LiftVersion.js.map +1 -0
  58. package/build/lib/Tests/fileCompare.test.js +55 -0
  59. package/build/lib/Tests/fileCompare.test.js.map +1 -0
  60. package/build/lib/askQuestion.js +41 -0
  61. package/build/lib/askQuestion.js.map +1 -0
  62. package/build/lib/executeCommand.js +45 -0
  63. package/build/lib/executeCommand.js.map +1 -0
  64. package/build/lib/fileCompare.js +48 -0
  65. package/build/lib/fileCompare.js.map +1 -0
  66. package/build/lib/openAPI/ApiBuildCollector.js +58 -0
  67. package/build/lib/openAPI/ApiBuildCollector.js.map +1 -0
  68. package/build/lib/openAPI/WebrootFileSupport.js +44 -0
  69. package/build/lib/openAPI/WebrootFileSupport.js.map +1 -0
  70. package/build/lib/openAPI/openApiConstruction.js +203 -0
  71. package/build/lib/openAPI/openApiConstruction.js.map +1 -0
  72. package/build/lib/pathResolve.js +27 -0
  73. package/build/lib/pathResolve.js.map +1 -0
  74. package/build/lib/utils.js +76 -0
  75. package/build/lib/utils.js.map +1 -0
  76. package/build/lift.js +124 -0
  77. package/build/lift.js.map +1 -0
  78. package/package.json +69 -0
  79. package/src/commands/actions/initQuestions.ts +131 -0
  80. package/src/commands/actions/makePackageJson.ts +0 -0
  81. package/src/commands/actions/setupPackageJson.ts +36 -0
  82. package/src/commands/build.ts +165 -0
  83. package/src/commands/builtin/ApiDocMaker.ts +70 -0
  84. package/src/commands/builtin/BuiltInHandler.ts +51 -0
  85. package/src/commands/builtin/DeployBuiltInZip.ts +27 -0
  86. package/src/commands/builtin/StageWebrootZip.ts +39 -0
  87. package/src/commands/builtin/prebuilt-zips/API.zip +0 -0
  88. package/src/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
  89. package/src/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
  90. package/src/commands/create.ts +55 -0
  91. package/src/commands/deploy.ts +171 -0
  92. package/src/commands/doctor.ts +102 -0
  93. package/src/commands/help.ts +171 -0
  94. package/src/commands/init.ts +62 -0
  95. package/src/commands/package.ts +228 -0
  96. package/src/commands/publish.ts +350 -0
  97. package/src/commands/settings.ts +76 -0
  98. package/src/commands/start.ts +46 -0
  99. package/src/commands/test.ts +38 -0
  100. package/src/commands/user.ts +20 -0
  101. package/src/expressRoutes/all.ts +104 -0
  102. package/src/expressRoutes/api.ts +24 -0
  103. package/src/expressRoutes/functionBinder.ts +169 -0
  104. package/src/lib/CaseUtils.ts +68 -0
  105. package/src/lib/DirectoryUtils.ts +36 -0
  106. package/src/lib/LiftConfig.ts +83 -0
  107. package/src/lib/LiftVersion.ts +95 -0
  108. package/src/lib/Tests/dir1/file.1 +0 -0
  109. package/src/lib/Tests/dir1/file.2 +0 -0
  110. package/src/lib/Tests/dir2/file.1 +0 -0
  111. package/src/lib/Tests/fileCompare.test.ts +34 -0
  112. package/src/lib/askQuestion.ts +18 -0
  113. package/src/lib/executeCommand.ts +38 -0
  114. package/src/lib/fileCompare.ts +46 -0
  115. package/src/lib/openAPI/ApiBuildCollector.ts +47 -0
  116. package/src/lib/openAPI/WebrootFileSupport.ts +21 -0
  117. package/src/lib/openAPI/openApiConstruction.ts +202 -0
  118. package/src/lib/pathResolve.ts +32 -0
  119. package/src/lib/utils.ts +45 -0
  120. package/src/lift.ts +82 -0
  121. package/tsconfig.json +28 -0
@@ -0,0 +1,350 @@
1
+
2
+ import {
3
+ LambdaClient,
4
+ ListFunctionsCommand, ResourceNotFoundException,
5
+ } from "@aws-sdk/client-lambda";
6
+ import {
7
+ APIGatewayClient,
8
+ ImportRestApiCommand,
9
+ GetRestApisCommand,
10
+ GetResourcesCommand,
11
+ DeleteRestApiCommand,
12
+ Resource,
13
+ PutIntegrationRequest,
14
+ PutIntegrationCommand,
15
+ GetStageCommand,
16
+ NotFoundException,
17
+ CreateStageCommand, CreateDeploymentCommand, GetDeploymentsCommand, GetDeploymentCommand
18
+ } from "@aws-sdk/client-api-gateway";
19
+
20
+ import {getAWSCredentials, getSettings} from "../lib/LiftConfig";
21
+
22
+ import {doDeployAsync} from "./deploy";
23
+ import * as ac from "ansi-colors"
24
+
25
+ import {executeCommand} from "../lib/executeCommand";
26
+
27
+ import path from 'path';
28
+ import fs, {mkdirSync} from 'fs';
29
+ import {all} from "axios";
30
+ import {resolvePaths} from "../lib/pathResolve";
31
+ import {gatherFunctionDefinitions} from "../lib/openAPI/ApiBuildCollector";
32
+ import {delay} from "../lib/utils";
33
+ import {addBuiltInDefinitions, MakeBuiltinApiDoc, MakePublicApiDoc} from "./builtin/ApiDocMaker";
34
+ import {DeployApiBuiltin, DeployRootFileserves, DeployWebrootBuiltIn} from "./builtin/BuiltInHandler";
35
+ import {getProjectName, getProjectVersion} from "../lib/LiftVersion";
36
+ import md5 from 'md5';
37
+
38
+ let projectPaths:any;
39
+
40
+ export async function doPublishAsync(stageName?:string): Promise<number>
41
+ {
42
+ // let retCode = await doDeployAsync([])
43
+ // if(retCode) return retCode
44
+ let retCode = 0;
45
+
46
+ projectPaths = resolvePaths()
47
+
48
+ await publishApi(stageName ?? "Dev")
49
+
50
+ return retCode;
51
+ }
52
+
53
+
54
+ async function publishApi(stageName:string)
55
+ {
56
+ if(!stageName) stageName = "Dev"
57
+ console.log(ac.green.bold(`Publishing Api to ${stageName}`));
58
+
59
+ // do the built-in deploys
60
+ await DeployWebrootBuiltIn();
61
+ await DeployRootFileserves();
62
+ await DeployApiBuiltin();
63
+
64
+ const projectPaths = resolvePaths();
65
+
66
+ // make a private api
67
+ const fs = require('fs');
68
+ const os = require('os');
69
+ const path = require('path');
70
+
71
+ let tmpDir;
72
+ let apiBytes;
73
+ const appPrefix = 'MistLift';
74
+ try {
75
+ tmpDir = path.join(os.tmpdir(), appPrefix);
76
+ // console.log("making "+tmpDir)
77
+ mkdirSync(tmpDir, {recursive:true});
78
+
79
+ const yamlFile = path.join(tmpDir, 'papi.yaml')
80
+ await MakeBuiltinApiDoc(yamlFile);
81
+ // console.log("reading "+yamlFile)
82
+ apiBytes = fs.readFileSync(yamlFile)
83
+ }
84
+ catch (e:any) {
85
+ console.error(ac.red.bold("Error creating temp staging folder ")+e.message);
86
+ throw Error();
87
+ }
88
+ finally {
89
+ try {
90
+ if (tmpDir) {
91
+ // console.log("Removing "+tmpDir)
92
+ fs.rmSync(tmpDir, { recursive: true });
93
+ }
94
+ }
95
+ catch (e) {
96
+ console.error(`An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`);
97
+ }
98
+ }
99
+
100
+ const client = new APIGatewayClient(getAWSCredentials());
101
+
102
+ await RemoveExistingVersions(client)
103
+
104
+ const command = new ImportRestApiCommand({
105
+ failOnWarnings: false,
106
+ body: apiBytes
107
+ })
108
+ let apiId;
109
+ try {
110
+ const response = await client.send(command)
111
+ const {name, id, rootResourceId, version, warnings} = response
112
+ console.log(ac.grey(`\n\nAPI ${name} version ${version} [${id}] schema created`))
113
+ apiId = id;
114
+ if (warnings?.length) {
115
+ console.log(ac.magenta(` with ${warnings.length} warnings`))
116
+ for (let warning of warnings) {
117
+ console.log(ac.grey.italic(` ${warning}`))
118
+ }
119
+ }
120
+ }
121
+ catch(e:any) {
122
+ console.error(ac.bold.red(e.message));
123
+ }
124
+ var apidoc = new TextDecoder().decode(apiBytes);
125
+ // console.log(apidoc)
126
+ if(!apiId) return;
127
+
128
+ console.log(ac.grey("Continuing with binding..."))
129
+ const prereq = await PrequisiteValues(apiId);
130
+ const intRequests = prereq.MakeRequests();
131
+ await PutIntegrations(intRequests);
132
+ await DeployApi(apiId, stageName);
133
+ const region = getSettings().awsPreferredRegion;
134
+ console.log(ac.green.bold(`\n Successfully deployed to https://${apiId}.execute-api.${region}.amazonaws.com/${stageName}`));
135
+ }
136
+ function findApiName()
137
+ {
138
+ const infoFile = path.join(projectPaths.functionPath, "apiService.info.json")
139
+ const pkgFile = projectPaths.packagePath;
140
+ let pkg:any = {};
141
+ if(fs.existsSync(pkgFile)) { pkg = JSON.parse(fs.readFileSync(pkgFile).toString()) }
142
+ let info:any = {};
143
+ if(fs.existsSync(infoFile)) { info = JSON.parse(fs.readFileSync(infoFile).toString()) }
144
+
145
+ return info.title ?? pkg.name ?? "API";
146
+ }
147
+
148
+ function RemoveExistingVersions(client:APIGatewayClient)
149
+ {
150
+ return new Promise(resolve => {
151
+ const ourName = findApiName();
152
+ // ClogDebug("looking for name "+ ourName)
153
+ const listCommand = new GetRestApisCommand({})
154
+ const all:any = [];
155
+ client.send(listCommand).then((response:any) => {
156
+ // ClogInfo(`There are ${response.items.length} other APIs`)
157
+
158
+
159
+ const DeleteMatchingApis = (i: number = 0):Promise<any> => {
160
+ if (i >= response.items.length) {
161
+ resolve(undefined);
162
+ return Promise.resolve(undefined);
163
+ }
164
+ const item = response.items[i];
165
+ if (item.name === ourName) {
166
+ // ClogInfo(`Found previous ${ourName}, [${item.id}] -- deleting...`)
167
+ const deleteCommand = new DeleteRestApiCommand({
168
+ restApiId: item.id
169
+ })
170
+ return client.send(deleteCommand).then(() => {
171
+ delay(5000).then(() => {
172
+ console.log(ac.magenta.bold(`Removed previous id ${item.id}`));
173
+ return DeleteMatchingApis(++i);
174
+ })
175
+ })
176
+ } else {
177
+ return DeleteMatchingApis(++i);
178
+ }
179
+ }
180
+ DeleteMatchingApis();
181
+ });
182
+ })
183
+ }
184
+
185
+
186
+ class FunctionInfo {
187
+ name: string = ""
188
+ arn: string = ""
189
+ }
190
+ class APIInfo {
191
+ id: string = ""
192
+ parentId: string = ""
193
+ path:string = ""
194
+ pathPart: string = "";
195
+ resourceMethods: object = {}
196
+ }
197
+
198
+
199
+ class PrereqInfo {
200
+ requestApiId:string = ""
201
+ functions: FunctionInfo[] = []
202
+ apis: any[] = [] // Resource + method
203
+ defs: any[] = []
204
+
205
+ public constructor() {
206
+ this.functions = []
207
+ this.apis = []
208
+ this.defs = []
209
+ }
210
+
211
+
212
+ public findApi(pathMap:string, allowedMethods:string):any
213
+ {
214
+ // find the pathmap in the apis list
215
+ // console.log("findApi "+pathMap, allowedMethods)
216
+ // see that this method exists in method map
217
+ for(let api of this.apis) {
218
+ if (api?.path === pathMap) {
219
+ // console.log("resourceMethods", api.resourceMethods)
220
+ const methodList = Object.getOwnPropertyNames(api.resourceMethods);
221
+ // ClogTrace("methodList", methodList)
222
+ for(let meth of allowedMethods.toUpperCase().split(',')) {
223
+ // console.log("meth", meth)
224
+ if(methodList.indexOf(meth) !== -1) {
225
+ (api as any).method = meth
226
+ return api;
227
+ }
228
+ }
229
+ }
230
+ }
231
+ return null;
232
+ }
233
+
234
+ public findARN(name:string):string
235
+ {
236
+ for(let f of this.functions ?? []) {
237
+ let lastus = f.name.lastIndexOf('_');
238
+ const fname = f.name.substring(0, lastus);
239
+ if(fname.toLowerCase() === name.toLowerCase()) return f.arn;
240
+ }
241
+ console.log("$$$ Couldn't find "+name+" in ", this.functions)
242
+ return "";
243
+ }
244
+
245
+ public MakeRequests():PutIntegrationRequest[]
246
+ {
247
+ const region = getSettings().awsPreferredRegion;
248
+ const out:PutIntegrationRequest[] = [];
249
+ for(let d of this.defs) {
250
+ const def = (d as any);
251
+ const api = this.findApi(def.pathMap, def.allowedMethods)
252
+ const arn = this.findARN(def.name)
253
+ if(!arn) {
254
+ console.log(`>>> No ARN for ${def.name} ${def.pathMap}, ${api.id}`)
255
+ }
256
+ if(api) {
257
+ out.push({
258
+ restApiId: this.requestApiId,
259
+ resourceId: api.id, // api.parentId
260
+ httpMethod: api.method,
261
+ integrationHttpMethod: 'POST', // api.method,
262
+ type: "AWS_PROXY",
263
+ uri: `arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/${arn}/invocations`,
264
+ credentials: "arn:aws:iam::545650260286:role/TBDLambdaExecution2"
265
+ })
266
+ }
267
+
268
+ }
269
+ return out;
270
+ }
271
+ }
272
+
273
+
274
+ async function PrequisiteValues(id:string):Promise<PrereqInfo>
275
+ {
276
+ const pri = new PrereqInfo()
277
+ const out = await GetFunctionInfo(pri)
278
+ out.defs = gatherFunctionDefinitions()
279
+ addBuiltInDefinitions(out.defs)
280
+ out.requestApiId = id;
281
+ await GetMethodResources(id, out);
282
+
283
+ return out;
284
+ }
285
+
286
+ async function GetFunctionInfo(info:PrereqInfo):Promise<PrereqInfo>
287
+ {
288
+ const client = new LambdaClient(getAWSCredentials());
289
+ const listCommand:any = new ListFunctionsCommand({})
290
+ const response:any = await client.send(listCommand);
291
+
292
+ const sfx = "_"+md5((getProjectName()??"")+(getProjectVersion()??""))
293
+
294
+ for(let func of response.Functions ?? []) {
295
+ if(func.FunctionName.endsWith(sfx)) {
296
+ info.functions.push({
297
+ name: func.FunctionName ?? "",
298
+ arn: func.FunctionArn ?? ""
299
+ })
300
+ }
301
+ }
302
+ return info;
303
+
304
+ }
305
+
306
+ async function GetMethodResources(id:string, info:PrereqInfo)
307
+ {
308
+ const client = new APIGatewayClient(getAWSCredentials())
309
+ const listCommand = new GetResourcesCommand({
310
+ restApiId: id
311
+ });
312
+ const response:any|null = await client.send(listCommand);
313
+ // ClogInfo("Resources response", response)
314
+ if(response?.items) info.apis = response.items;
315
+
316
+ return info;
317
+ }
318
+
319
+ async function PutIntegrations(integrations:PutIntegrationRequest[])
320
+ {
321
+ const client = new APIGatewayClient(getAWSCredentials())
322
+ for(let input of integrations) {
323
+
324
+ try {
325
+ const command = new PutIntegrationCommand(input);
326
+ const intResp = await client.send(command);
327
+ // ClogDebug("integration response", intResp);
328
+ await delay(5000);
329
+ } catch(e:any) {
330
+ console.error("Problem with integration for "+input.uri, {input});
331
+ throw e
332
+ }
333
+ }
334
+ }
335
+
336
+ async function DeployApi(restApiId:string, stageName:string)
337
+ {
338
+ try {
339
+ const client = new APIGatewayClient(getAWSCredentials())
340
+ const command = new CreateDeploymentCommand({
341
+ restApiId,
342
+ stageName
343
+ })
344
+ const resp = await client.send(command);
345
+ }
346
+ catch(e:any) {
347
+ console.error(ac.red.bold("Error with deployApi: "+e.message));
348
+ throw e;
349
+ }
350
+ }
@@ -0,0 +1,76 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import {LiftConfig, RuntimeType} from '../lib/LiftConfig'
4
+ import {homedir} from 'os'
5
+ import {ask} from "../lib/askQuestion";
6
+ import * as ac from 'ansi-colors'
7
+
8
+
9
+ export async function doSettings()
10
+ {
11
+
12
+ try {
13
+ const mistLiftPath = path.join(homedir(), ".mistlift")
14
+ let settings:LiftConfig = {cloudHost: "AWS"}
15
+ if(fs.existsSync(mistLiftPath)) {
16
+ settings = JSON.parse(fs.readFileSync(mistLiftPath).toString())
17
+ }
18
+
19
+ let ok = false;
20
+ while(!ok) {
21
+ settings.cloudHost = ask("Type of cloud host (AWS)",
22
+ "cloud host",
23
+ settings.cloudHost
24
+ )
25
+ ok = settings.cloudHost == "AWS" // all we support now
26
+ }
27
+ ok = false
28
+
29
+ if(settings.cloudHost === "AWS") {
30
+ console.log("Choose from a configured AWS profile, or leave empty to use a custom credentials config file")
31
+ while (!ok) {
32
+ settings.awsIniProfile = ask("Name of AWS profile to use",
33
+ "AWS Profile",
34
+ settings.awsIniProfile??"default"
35
+ )
36
+ ok = settings.awsIniProfile !== "";
37
+ }
38
+ ok = false;
39
+ let runtime:string = settings.awsNodeRuntime as string
40
+ while(!ok) {
41
+ runtime = ask("AWS Node Runtime Version",
42
+ "NodeJS Runtime",
43
+ "Nodejs20.x"
44
+ )
45
+ ok = !!runtime
46
+ }
47
+ settings.awsNodeRuntime = runtime.toLowerCase() as RuntimeType
48
+
49
+ ok = false;
50
+ while(!ok) {
51
+ settings.awsPreferredRegion = ask("Preferred AWS region",
52
+ "preferred AWS region",
53
+ settings.awsPreferredRegion??"us-east-1"
54
+ )
55
+ ok = !!settings.awsPreferredRegion
56
+ }
57
+ ok = false;
58
+ console.log("Supply the ARN for the service role you have created in your AWS IAM account for Lambda, S3, and Cloudwatch access")
59
+ while(!ok) {
60
+ settings.awsServiceRoleARN = ask("AWS service role ARN",
61
+ "serviceRole ARN",
62
+ settings.awsServiceRoleARN??"arn:aws.iam::xxxxxxxxxxxx:role/service-role-name"
63
+ )
64
+ ok = settings.awsServiceRoleARN !== "arn:aws.iam::xxxxxxxxxxxx:role/service-role-name"
65
+ && settings.awsServiceRoleARN !== ""
66
+ }
67
+ }
68
+ fs.writeFileSync(mistLiftPath, JSON.stringify(settings))
69
+
70
+ return 0;
71
+ }
72
+ catch(e:any) {
73
+ console.error(ac.bold.red("Error with settings:"), e)
74
+ return -1;
75
+ }
76
+ }
@@ -0,0 +1,46 @@
1
+ /** Run Express Server */
2
+ import express from 'express'
3
+ import fs from 'fs'
4
+ import path from 'path'
5
+ import bodyParser from 'body-parser'
6
+
7
+ import functionRouter, {functionBinder} from "../expressRoutes/functionBinder"
8
+
9
+ import apiRouter from "../expressRoutes/api"
10
+ import allRouter, {allBinder} from "../expressRoutes/all"
11
+ import {gatherFunctionDefinitions} from "../lib/openAPI/ApiBuildCollector";
12
+ import {buildOpenApi} from "../lib/openAPI/openApiConstruction";
13
+ import {resolvePaths} from "../lib/pathResolve";
14
+
15
+ import * as ac from "ansi-colors"
16
+
17
+ export function startLocalServer() {
18
+ const projectPaths = resolvePaths();
19
+ if(!projectPaths.verified) {
20
+ console.log(ac.bold.red("Cannot start local server"))
21
+ console.log(ac.blue.italic("Not in a valid MistLift project directory"))
22
+ console.log("")
23
+ return
24
+ }
25
+ const port = 8081
26
+ allBinder()
27
+ functionBinder()
28
+ const app = express()
29
+ // for JSON posts
30
+ // app.use(bodyParser.json({limit: '50mb'}))
31
+ app.use(express.json());
32
+ // for form posts
33
+ app.use(express.urlencoded({extended: true}))
34
+
35
+ app.use('/', functionRouter)
36
+ app.use('/api', apiRouter)
37
+ app.use('*', allRouter)
38
+
39
+ // =========================================
40
+ // Start server
41
+ // http only
42
+ app.listen(port, function () {
43
+ console.log('http listening on port ' + port)
44
+ })
45
+ // ================================================
46
+ }
@@ -0,0 +1,38 @@
1
+ import {executeCommand} from "../lib/executeCommand";
2
+ import {doBuildAsync} from "./build";
3
+
4
+ export async function doTestAsync(args:string[] ): Promise<number>
5
+ {
6
+ if(await doBuildAsync(args)) return 1 // don't test if build fails
7
+ if(args.length === 0) args = ['*']
8
+ let ret = 0;
9
+ for (let funcName of args) {
10
+ const result = await executeCommand('tap', [
11
+ `build/functions/${funcName}/*-tests/*.js`,
12
+ /*
13
+ - base -- looks a lot like terse
14
+ - terse -- pass/fail counts
15
+ - min -- errors only
16
+ - dot -- like min, but a dot per assert
17
+ - silent
18
+ - json
19
+ - jsonstream
20
+ - markdown
21
+ - junit
22
+ - tap
23
+ */
24
+ '--reporter=base',
25
+ '--color',
26
+ '--passes',
27
+ '--allow-empty-coverage',
28
+ '--allow-incomplete-coverage'
29
+
30
+ ], '', true)
31
+
32
+ if (result.retcode) {
33
+ ret = result.retcode
34
+ break;
35
+ }
36
+ }
37
+ return ret;
38
+ }
@@ -0,0 +1,20 @@
1
+ /** Handles the set of user management commands
2
+ *
3
+ * Default and prime user is the default of the AWS profile set
4
+ * In configuration, this can be changed to a different profile
5
+ * The user command module is meant to
6
+ */
7
+
8
+ // TODO:
9
+
10
+ /*
11
+ - lift user create <name>
12
+ - lift user destroy <name>
13
+ - lift user grant <name> <privilege>
14
+ - lift user revoke <name> <privilege>
15
+ - lift user show <name>
16
+ - lift user list
17
+
18
+ */
19
+ // }
20
+ //}
@@ -0,0 +1,104 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+
4
+ import * as ac from "ansi-colors"
5
+ import {resolvePaths} from "../lib/pathResolve";
6
+ import {gatherFunctionDefinitions} from "../lib/openAPI/ApiBuildCollector";
7
+
8
+ import express from 'express'
9
+ const router = express.Router();
10
+
11
+ export function allBinder() {
12
+ const projectPaths = resolvePaths();
13
+ const defs = gatherFunctionDefinitions();
14
+
15
+ /**
16
+ * @name /all
17
+ * @description
18
+ * Catch-all handler for URLs
19
+ * Provides some simple spam blocking and checks for file pass through at file root.
20
+ *
21
+ * ##### Filtering:
22
+ * - all .php extension references
23
+ *
24
+ * ##### File root:
25
+ * - file root is at service root + 'Public'
26
+ * - no path or path ending in / is appended with '/index.html' for folder root default behavior
27
+ * - File contents passed through using `sendFile` (will handle its own 404 if not found)
28
+ *
29
+ */
30
+ router.all('*', (req, res, next) => {
31
+
32
+ // any PHP requests should be ignored
33
+ // ClogTrace('incoming: '+req.originalUrl)
34
+ if (req.originalUrl.indexOf('.php') !== -1) {
35
+ return res.send('')
36
+ }
37
+ if (req.originalUrl.indexOf('.env') !== -1) {
38
+ return res.send('')
39
+ }
40
+
41
+
42
+
43
+ let filepath = req.originalUrl || '/'
44
+ // match cloud behavior for docs because /api doesn't change cwd there
45
+ if(filepath.substring(0, 10) == '/api/docs/') {
46
+ filepath = filepath.replace('/api', '');
47
+ }
48
+ let funcFound = filepath === '/api'; // reserved "function"
49
+ if(!funcFound) {
50
+ if (filepath.charAt(0) === '/') {
51
+ let n = filepath.indexOf('/', 1);
52
+ if (n === -1) n = filepath.length;
53
+ let rootEntity = filepath.substring(0, n)
54
+ n = rootEntity.indexOf('?');
55
+ if (n === -1) n = rootEntity.length;
56
+ rootEntity = rootEntity.substring(0, n);
57
+ for (const entry of defs) {
58
+ let entryRoot = entry.pathMap;
59
+ let n = entryRoot.indexOf("/{");
60
+ if (n !== -1) entryRoot = entryRoot.substring(0, n);
61
+ if (rootEntity === entryRoot) {
62
+ funcFound = true;
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ if (!funcFound) {
70
+ // check for '/' in path beyond the first one
71
+ if(filepath.indexOf('/',1) !== -1)
72
+ {
73
+ // console.log(ac.magenta.italic("Warning: / path delimiters will not be honored in a cloud deployment, use + instead ")+filepath)
74
+ }
75
+ // convert any incoming + to / to match cloud behavior
76
+ while(filepath.indexOf("+")!== -1) filepath = filepath.replace("+", "/")
77
+
78
+ // Put any other filtering here
79
+ if (filepath.indexOf('?') !== -1) {
80
+ filepath = filepath.substring(0, filepath.indexOf('?'))
81
+ }
82
+ if (filepath.substring(filepath.length - 1) === '/') filepath += 'index.html'
83
+ if(filepath.substring(0, 10) == '/api/docs/') {
84
+ filepath = filepath.replace('/api', '');
85
+ }
86
+ if(filepath.indexOf("/docs") !== -1) {
87
+ if(filepath.indexOf('.map') !== -1) {
88
+ return res.sendStatus(200)
89
+ }
90
+ }
91
+ filepath = path.resolve(path.join(projectPaths.basePath, 'webroot', filepath)) // .. out of build
92
+ if (fs.existsSync(filepath)) {
93
+ return res.sendFile(filepath)
94
+ } else {
95
+ console.log(ac.red.bold("can't find " + filepath));
96
+ return res.sendStatus(404);
97
+ }
98
+ }
99
+ // if none of the above, tickle enumerator so we can process other paths
100
+ next()
101
+ })
102
+ }
103
+ export default router
104
+
@@ -0,0 +1,24 @@
1
+ import express from 'express'
2
+ const router = express.Router();
3
+
4
+ import {getProjectName, getProjectVersion} from "../lib/LiftVersion";
5
+
6
+ const openApiUI = require("openapi-ui");
7
+
8
+
9
+ router.get('/', function(req, res, next) {
10
+ res.send(generateApiDoc())
11
+ });
12
+
13
+ export default router
14
+
15
+ function generateApiDoc()
16
+ {
17
+ console.log("---- Serving up api")
18
+ return openApiUI.generateIndex({
19
+ baseUrl: "docs",
20
+ title: `${getProjectName()} ${getProjectVersion()}`,
21
+ url: "docs/apidoc.yaml"
22
+ });
23
+
24
+ }