@voxgig/build 4.0.0 → 4.2.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.
- package/build.ts +46 -0
- package/dist/build.d.ts +31 -1
- package/dist/build.js +33 -1
- package/dist/build.js.map +1 -1
- package/dist/env/env_gen.d.ts +16 -0
- package/dist/env/env_gen.js +143 -0
- package/dist/env/env_gen.js.map +1 -0
- package/dist/env/lambda/generate.d.ts +8 -1
- package/dist/env/lambda/generate.js +59 -0
- package/dist/env/lambda/generate.js.map +1 -1
- package/dist/env/lambda/res_yml.d.ts +1 -0
- package/dist/env/lambda/res_yml.js +12 -41
- package/dist/env/lambda/res_yml.js.map +1 -1
- package/dist/env/lambda/srv_handler.d.ts +1 -0
- package/dist/env/lambda/srv_handler.js +10 -21
- package/dist/env/lambda/srv_handler.js.map +1 -1
- package/dist/env/lambda/srv_yml.d.ts +1 -0
- package/dist/env/lambda/srv_yml.js +11 -9
- package/dist/env/lambda/srv_yml.js.map +1 -1
- package/env/env_gen.ts +165 -0
- package/env/lambda/generate.ts +112 -0
- package/env/lambda/res_yml.ts +140 -0
- package/env/lambda/srv_handler.ts +132 -0
- package/env/lambda/srv_yml.ts +201 -0
- package/package.json +6 -4
- package/tm/env/aws/README.md.frag +9 -0
- package/tm/env/aws/lambda.entry.ts.frag +89 -0
- package/tm/env/aws/serverless.yml.frag +23 -0
- package/tm/env/azure/README.md.frag +7 -0
- package/tm/env/azure/entry.ts.frag +49 -0
- package/tm/env/azure/host.json.frag +8 -0
- package/tm/env/basic/README.md.frag +7 -0
- package/tm/env/basic/backend.service.frag +14 -0
- package/tm/env/basic/deploy.sh.frag +13 -0
- package/tm/env/cloudflare/README.md.frag +7 -0
- package/tm/env/cloudflare/worker.ts.frag +13 -0
- package/tm/env/cloudflare/wrangler.toml.frag +7 -0
- package/tm/env/docker/Dockerfile.frag +10 -0
- package/tm/env/docker/README.md.frag +7 -0
- package/tm/env/docker/compose.yml.frag +10 -0
- package/tm/env/local/README.md.frag +7 -0
- package/tm/env/local/run.sh.frag +6 -0
- package/tm/env/server.entry.ts.frag +48 -0
- package/tm/env/vm/README.md.frag +5 -0
- package/tm/env/vm/cloud-init.yaml.frag +23 -0
- package/tm/lambda/res.queue.yml.frag +6 -0
- package/tm/lambda/res.role.yml.frag +35 -0
- package/tm/lambda/srv.yml.frag +6 -0
- package/tm/lambda/srv_handler.js.frag +17 -0
- package/tm/lambda/srv_handler.ts.frag +17 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/* Copyright © 2022-2026 Voxgig Ltd, MIT License. */
|
|
2
|
+
|
|
3
|
+
// gen/serverless/srv.yml template: one Serverless function definition per
|
|
4
|
+
// lambda-active service, derived from the model.
|
|
5
|
+
|
|
6
|
+
import Fs from 'fs'
|
|
7
|
+
import Path from 'path'
|
|
8
|
+
|
|
9
|
+
import { camelify } from '@voxgig/util'
|
|
10
|
+
|
|
11
|
+
import { CoreConfShape } from '../../shape/conf'
|
|
12
|
+
|
|
13
|
+
import { generate, empty, TM, loadFragment, renderFragment } from './generate'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const srv_yml = async (model: any, spec: {
|
|
17
|
+
folder: string
|
|
18
|
+
tm?: string
|
|
19
|
+
}) => {
|
|
20
|
+
|
|
21
|
+
const core = CoreConfShape(model.main.conf.core)
|
|
22
|
+
|
|
23
|
+
let appname = core.name
|
|
24
|
+
let AppName = camelify(appname)
|
|
25
|
+
|
|
26
|
+
const frag = loadFragment('srv.yml.frag', spec)
|
|
27
|
+
|
|
28
|
+
let srv_yml_prefix_path = Path.join(spec.folder, 'srv.prefix.yml')
|
|
29
|
+
let srv_yml_suffix_path = Path.join(spec.folder, 'srv.suffix.yml')
|
|
30
|
+
|
|
31
|
+
let prefixContent = Fs.existsSync(srv_yml_prefix_path) ?
|
|
32
|
+
Fs.readFileSync(srv_yml_prefix_path) : ''
|
|
33
|
+
let suffixContent = Fs.existsSync(srv_yml_suffix_path) ?
|
|
34
|
+
Fs.readFileSync(srv_yml_suffix_path) : ''
|
|
35
|
+
|
|
36
|
+
let content =
|
|
37
|
+
|
|
38
|
+
prefixContent +
|
|
39
|
+
Object
|
|
40
|
+
.entries(model.main.srv)
|
|
41
|
+
.filter((entry: any) => entry[1].env?.lambda?.active)
|
|
42
|
+
.map((entry: any) => {
|
|
43
|
+
const name = entry[0]
|
|
44
|
+
const srv = entry[1]
|
|
45
|
+
|
|
46
|
+
const lambda = srv.env.lambda
|
|
47
|
+
const handler = lambda.handler
|
|
48
|
+
|
|
49
|
+
// NOTE: gen.custom convention: allows for complete overwrite
|
|
50
|
+
// as a get-out-of-jail
|
|
51
|
+
if (srv.gen?.custom?.lambda?.srv_yml) {
|
|
52
|
+
return srv.gen.custom.lambda.srv_yml
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const web = srv.api.web
|
|
56
|
+
|
|
57
|
+
let events = ''
|
|
58
|
+
|
|
59
|
+
let onEvents = srv.on
|
|
60
|
+
|
|
61
|
+
if (onEvents) {
|
|
62
|
+
Object.entries(onEvents).forEach((entry: any[]) => {
|
|
63
|
+
// let name = entry[0]
|
|
64
|
+
let spec = entry[1]
|
|
65
|
+
|
|
66
|
+
if ('aws' === spec.provider) {
|
|
67
|
+
spec.events.forEach((ev: any) => {
|
|
68
|
+
if ('s3' === ev.source) {
|
|
69
|
+
events += TM(`
|
|
70
|
+
- s3:
|
|
71
|
+
bucket: ${ev.bucket}
|
|
72
|
+
event: ${ev.event}
|
|
73
|
+
existing: true
|
|
74
|
+
`)
|
|
75
|
+
if (ev.rules) {
|
|
76
|
+
events += TM(`
|
|
77
|
+
rules:
|
|
78
|
+
`)
|
|
79
|
+
if (ev.rules.prefix) {
|
|
80
|
+
events += TM(`
|
|
81
|
+
- prefix: ${ev.rules.prefix}
|
|
82
|
+
`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (ev.rules.suffix) {
|
|
86
|
+
events += TM(`
|
|
87
|
+
- suffix: ${ev.rules.suffix}
|
|
88
|
+
`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if ('schedule' === ev.source) {
|
|
93
|
+
let entries =
|
|
94
|
+
'string' === typeof ev.recur ? [ev.recur] : (ev.recur || [])
|
|
95
|
+
let recur = entries.map((entry: string) => {
|
|
96
|
+
let schedule = `
|
|
97
|
+
- schedule:
|
|
98
|
+
rate: ${entry}`
|
|
99
|
+
if (ev.msg) {
|
|
100
|
+
schedule += `
|
|
101
|
+
input:
|
|
102
|
+
msg: ${JSON.stringify(ev.msg)} `
|
|
103
|
+
}
|
|
104
|
+
return schedule
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
events += TM(`
|
|
108
|
+
${recur}
|
|
109
|
+
`)
|
|
110
|
+
|
|
111
|
+
}
|
|
112
|
+
else if ('sqs' === ev.source) {
|
|
113
|
+
events += TM(`
|
|
114
|
+
- sqs:
|
|
115
|
+
arn:
|
|
116
|
+
Fn::GetAtt:
|
|
117
|
+
- ${ev.qrn}
|
|
118
|
+
- Arn
|
|
119
|
+
batchSize: 1
|
|
120
|
+
`)
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// TODO: move to `on`
|
|
129
|
+
if (web.active) {
|
|
130
|
+
let prefix = web.path.prefix
|
|
131
|
+
let suffix = web.path.suffix
|
|
132
|
+
let area = web.path.area
|
|
133
|
+
let method = web.method
|
|
134
|
+
let corsflag = 'false'
|
|
135
|
+
let corsprops = ''
|
|
136
|
+
|
|
137
|
+
let methods = method.split(',')
|
|
138
|
+
// console.log('METHODS', methods)
|
|
139
|
+
|
|
140
|
+
if (web.cors.active) {
|
|
141
|
+
corsflag = 'true'
|
|
142
|
+
if (web.cors.props && !empty(web.cors.props)) {
|
|
143
|
+
corsflag = ''
|
|
144
|
+
corsprops = Object
|
|
145
|
+
.entries(web.cors.props)
|
|
146
|
+
.reduce(((a: any, nv: any) => (
|
|
147
|
+
a += ` ${nv[0]}: ${nv[1]}\n`
|
|
148
|
+
, a)), '')
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if ('v2' === web.lambda?.gateway) {
|
|
153
|
+
for (let method of methods) {
|
|
154
|
+
events += TM(`
|
|
155
|
+
- httpApi:
|
|
156
|
+
path: "${prefix}${area}${name}${suffix}"
|
|
157
|
+
method: ${method}
|
|
158
|
+
`)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
for (let method of methods) {
|
|
163
|
+
events += TM(`
|
|
164
|
+
- http:
|
|
165
|
+
path: "${prefix}${area}${name}${suffix}"
|
|
166
|
+
method: ${method}
|
|
167
|
+
cors: ${corsflag}
|
|
168
|
+
${corsprops}
|
|
169
|
+
`)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
let eventsBlock = ''
|
|
177
|
+
if ('' !== events) {
|
|
178
|
+
eventsBlock = TM(`
|
|
179
|
+
events:
|
|
180
|
+
${events}
|
|
181
|
+
`)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return renderFragment(frag, {
|
|
185
|
+
name,
|
|
186
|
+
handler: handler.path.prefix + name + handler.path.suffix,
|
|
187
|
+
AppName,
|
|
188
|
+
timeout: lambda.timeout,
|
|
189
|
+
memory: lambda.memory || 1024,
|
|
190
|
+
events: eventsBlock,
|
|
191
|
+
})
|
|
192
|
+
}).join('\n\n\n') +
|
|
193
|
+
suffixContent
|
|
194
|
+
|
|
195
|
+
await generate(spec.folder, [{ name: 'srv.yml', content }])
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
export {
|
|
200
|
+
srv_yml,
|
|
201
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voxgig/build",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"main": "dist/build.js",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"types": "dist/build.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"url": "git://github.com/voxgig/build.git"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"model-s01": "voxgig-model test/s01/model/model.
|
|
21
|
+
"model-s01": "voxgig-model test/s01/model/model.aontu",
|
|
22
22
|
"test": "jest --coverage",
|
|
23
23
|
"test-some": "jest -t",
|
|
24
24
|
"test-watch": "jest --coverage --watchAll",
|
|
@@ -42,7 +42,9 @@
|
|
|
42
42
|
"lib",
|
|
43
43
|
"dist",
|
|
44
44
|
"bin",
|
|
45
|
-
"LICENSE"
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"tm",
|
|
47
|
+
"env"
|
|
46
48
|
],
|
|
47
49
|
"dependencies": {
|
|
48
50
|
"@voxgig/util": "0.5.4",
|
|
@@ -54,7 +56,7 @@
|
|
|
54
56
|
"@types/jest": "^29.5.12",
|
|
55
57
|
"@types/js-yaml": "^4.0.9",
|
|
56
58
|
"@types/node": "26.1.2",
|
|
57
|
-
"@voxgig/model": "^2.
|
|
59
|
+
"@voxgig/model": "^9.2.4",
|
|
58
60
|
"@voxgig/system": "^1.2.0",
|
|
59
61
|
"esbuild": "^0.23.0",
|
|
60
62
|
"jest": "^29.7.0",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# $$Name$$ - aws environment (Serverless Framework)
|
|
2
|
+
|
|
3
|
+
cd backend && npm run build
|
|
4
|
+
cd gen/env/aws && npx serverless deploy --stage $$stage$$
|
|
5
|
+
|
|
6
|
+
serverless.yml composes the generated srv.yml (functions) and res.yml
|
|
7
|
+
(resources); both are regenerated by model-build. Provider settings
|
|
8
|
+
(region/stage/runtime) come from the model env entry - change them with
|
|
9
|
+
the model, or eject env/aws/serverless.yml.frag for structural edits.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
|
|
2
|
+
// $$Name$$ backend - AWS Lambda environment bootstrap (generated once;
|
|
3
|
+
// this file is yours to edit). The generated handlers
|
|
4
|
+
// (src/handler/lambda/<srv>.ts) call getSeneca() for a ready Seneca
|
|
5
|
+
// instance wired for the Lambda + API Gateway path, configured by the
|
|
6
|
+
// shared setup (src/env/shared/basic.ts).
|
|
7
|
+
|
|
8
|
+
import Seneca from 'seneca'
|
|
9
|
+
import { Live } from '@voxgig/system'
|
|
10
|
+
|
|
11
|
+
import { basic, base } from '../shared/basic'
|
|
12
|
+
|
|
13
|
+
import Pkg from '../../../package.json'
|
|
14
|
+
import Model from '../../../model/model.json'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const STAGE = process.env.STAGE || 'local'
|
|
18
|
+
|
|
19
|
+
const Main = Model.main as any
|
|
20
|
+
|
|
21
|
+
let seneca: any = null
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async function getSeneca(srvname: string, complete: Function): Promise<any> {
|
|
25
|
+
const { deep } = Seneca.util
|
|
26
|
+
|
|
27
|
+
if (null == seneca) {
|
|
28
|
+
const srv = Main.srv[srvname]
|
|
29
|
+
|
|
30
|
+
seneca = Seneca(deep(base.seneca, {
|
|
31
|
+
tag: srvname + '-$$name$$-' + STAGE + '@' + Pkg.version,
|
|
32
|
+
timeout: srv.env.lambda.timeout * 60 * 1000,
|
|
33
|
+
})).test()
|
|
34
|
+
|
|
35
|
+
seneca.context.model = Model
|
|
36
|
+
seneca.context.srvname = srvname
|
|
37
|
+
seneca.context.stage = STAGE
|
|
38
|
+
seneca.context.env = 'lambda'
|
|
39
|
+
seneca.context.pkg = Pkg
|
|
40
|
+
|
|
41
|
+
basic(seneca, { reload: { active: false } })
|
|
42
|
+
|
|
43
|
+
seneca
|
|
44
|
+
.use('gateway')
|
|
45
|
+
.use('gateway-lambda', {
|
|
46
|
+
auth: {
|
|
47
|
+
token: {
|
|
48
|
+
name: '$$name$$-auth',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
.use('gateway-auth', {
|
|
53
|
+
spec: {
|
|
54
|
+
lambda_cookie: {
|
|
55
|
+
active: true,
|
|
56
|
+
token: {
|
|
57
|
+
name: '$$name$$-auth',
|
|
58
|
+
},
|
|
59
|
+
user: {
|
|
60
|
+
auth: true,
|
|
61
|
+
require: srv.user?.required ?? true,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
seneca.use(Live, {
|
|
68
|
+
srv: {
|
|
69
|
+
name: srvname,
|
|
70
|
+
folder: __dirname + '/../../srv',
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Call ready() exactly once - a second ready() on an already-ready
|
|
75
|
+
// instance never resolves under seneca 4.0.0-rc4.
|
|
76
|
+
await seneca.ready()
|
|
77
|
+
|
|
78
|
+
if (complete) {
|
|
79
|
+
await complete(seneca)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return seneca
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
getSeneca,
|
|
89
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# $$Name$$ backend - Serverless Framework config (generated; customize via
|
|
2
|
+
# tm/env/aws/). Composes the model-generated pieces: per-service function
|
|
3
|
+
# definitions (srv.yml) and resources (res.yml) live beside this file and
|
|
4
|
+
# are regenerated by model-build - edit the model, not those files.
|
|
5
|
+
service: $$name$$-backend
|
|
6
|
+
|
|
7
|
+
frameworkVersion: '4'
|
|
8
|
+
|
|
9
|
+
provider:
|
|
10
|
+
name: aws
|
|
11
|
+
runtime: nodejs$$node$$.x
|
|
12
|
+
region: $$region$$
|
|
13
|
+
stage: ${opt:stage, '$$stage$$'}
|
|
14
|
+
|
|
15
|
+
custom:
|
|
16
|
+
# res.yml derives the IAM role name suffix from this index.
|
|
17
|
+
index:
|
|
18
|
+
BasicLambdaRole: '01'
|
|
19
|
+
|
|
20
|
+
functions: ${file(./srv.yml)}
|
|
21
|
+
|
|
22
|
+
resources:
|
|
23
|
+
Resources: ${file(./res.yml)}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# $$Name$$ - azure environment (starting point)
|
|
2
|
+
|
|
3
|
+
Azure Functions host config for the $$Name$$ backend. This is a starting
|
|
4
|
+
point: Azure needs one function.json binding per exposed function - derive
|
|
5
|
+
them from the service definitions the model generates for aws
|
|
6
|
+
(gen/env/aws/srv.yml lists every handler and route). Eject
|
|
7
|
+
env/azure/host.json.frag to customize.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
// $$Name$$ backend - azure environment entry (generated once; this file is
|
|
3
|
+
// yours to edit). STARTING POINT: an Azure Functions HTTP handler shape -
|
|
4
|
+
// wire the Seneca gateway the way src/env/lambda/lambda.ts does for AWS.
|
|
5
|
+
|
|
6
|
+
import Seneca from 'seneca'
|
|
7
|
+
|
|
8
|
+
import { basic, base } from '../shared/basic'
|
|
9
|
+
|
|
10
|
+
import Pkg from '../../../package.json'
|
|
11
|
+
import Model from '../../../model/model.json'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
let seneca: any = null
|
|
15
|
+
|
|
16
|
+
async function getSeneca(): Promise<any> {
|
|
17
|
+
if (null == seneca) {
|
|
18
|
+
seneca = Seneca(Seneca.util.deep(base.seneca, {
|
|
19
|
+
tag: '$$name$$-azure@' + Pkg.version,
|
|
20
|
+
})).test()
|
|
21
|
+
|
|
22
|
+
seneca.context.model = Model
|
|
23
|
+
seneca.context.env = 'azure'
|
|
24
|
+
seneca.context.pkg = Pkg
|
|
25
|
+
|
|
26
|
+
basic(seneca, { reload: { active: false } })
|
|
27
|
+
|
|
28
|
+
await seneca.ready()
|
|
29
|
+
}
|
|
30
|
+
return seneca
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
// Azure Functions v4 programming model handler (add @azure/functions and
|
|
35
|
+
// register per-route handlers derived from the model - see
|
|
36
|
+
// gen/env/aws/srv.yml for the full route list).
|
|
37
|
+
async function handler(request: any, _context: any) {
|
|
38
|
+
await getSeneca()
|
|
39
|
+
return {
|
|
40
|
+
status: 501,
|
|
41
|
+
jsonBody: { ok: false, why: '$$name$$-backend azure entry not yet wired' },
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
getSeneca,
|
|
48
|
+
handler,
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# systemd unit for the $$Name$$ backend (generated; customize via tm/env/basic/)
|
|
2
|
+
[Unit]
|
|
3
|
+
Description=$$Name$$ backend
|
|
4
|
+
After=network.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
WorkingDirectory=/opt/$$name$$/backend
|
|
8
|
+
ExecStart=/usr/bin/node dist/env/local/local.js
|
|
9
|
+
Restart=always
|
|
10
|
+
Environment=NODE_ENV=production
|
|
11
|
+
Environment=PORT=$$port$$
|
|
12
|
+
|
|
13
|
+
[Install]
|
|
14
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Deploy the $$Name$$ backend to a plain remote server (generated).
|
|
3
|
+
# Usage: BASIC_HOST=user@host sh deploy.sh
|
|
4
|
+
set -e
|
|
5
|
+
: "${BASIC_HOST:?set BASIC_HOST=user@host}"
|
|
6
|
+
cd "$(dirname "$0")/../../.."
|
|
7
|
+
npm run build
|
|
8
|
+
rsync -az --delete \
|
|
9
|
+
--exclude node_modules --exclude gen --exclude dist-test \
|
|
10
|
+
./ "$BASIC_HOST:/opt/$$name$$/backend/"
|
|
11
|
+
ssh "$BASIC_HOST" "cd /opt/$$name$$/backend && npm install --omit=dev \
|
|
12
|
+
&& sudo cp gen/env/basic/$$name$$-backend.service /etc/systemd/system/ \
|
|
13
|
+
&& sudo systemctl daemon-reload && sudo systemctl restart $$name$$-backend"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# $$Name$$ - cloudflare environment (starting point)
|
|
2
|
+
|
|
3
|
+
cd gen/env/cloudflare && npx wrangler deploy
|
|
4
|
+
|
|
5
|
+
The worker entry (src/env/cloudflare/worker.ts - yours to edit, wrangler
|
|
6
|
+
bundles the TS directly) is a 501 stub: wire the Seneca gateway into the
|
|
7
|
+
fetch handler (src/env/lambda/lambda.ts is the AWS model to follow).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// $$Name$$ backend - Cloudflare Worker entry (generated once; this file is
|
|
2
|
+
// yours to edit). STARTING POINT: Workers use a fetch handler rather than
|
|
3
|
+
// a long-lived process - adapt the gateway wiring (src/env/lambda/lambda.ts
|
|
4
|
+
// is the AWS equivalent) to route requests into Seneca here.
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
async fetch(_request: Request, _env: unknown, _ctx: unknown) {
|
|
8
|
+
return new Response(JSON.stringify({
|
|
9
|
+
ok: false,
|
|
10
|
+
why: '$$name$$-backend worker entry not yet wired',
|
|
11
|
+
}), { status: 501, headers: { 'content-type': 'application/json' } })
|
|
12
|
+
},
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# $$Name$$ backend image (generated; customize via tm/env/docker/)
|
|
2
|
+
FROM node:$$node$$-slim
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
COPY package*.json ./
|
|
5
|
+
RUN npm ci --omit=dev
|
|
6
|
+
COPY model/model.json model/model.json
|
|
7
|
+
COPY dist/ dist/
|
|
8
|
+
ENV NODE_ENV=production
|
|
9
|
+
EXPOSE $$port$$
|
|
10
|
+
CMD ["node", "dist/env/local/local.js"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
// $$Name$$ backend - $$env$$ environment entry (generated once; this file
|
|
3
|
+
// is yours to edit). A long-lived server process using the shared Seneca
|
|
4
|
+
// setup (src/env/shared/basic.ts); reads its env config from the model
|
|
5
|
+
// (main.env.$$env$$).
|
|
6
|
+
|
|
7
|
+
import Seneca from 'seneca'
|
|
8
|
+
import { Local } from '@voxgig/system'
|
|
9
|
+
|
|
10
|
+
import { basic, base } from '../shared/basic'
|
|
11
|
+
|
|
12
|
+
import Pkg from '../../../package.json'
|
|
13
|
+
import Model from '../../../model/model.json'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
run()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async function run() {
|
|
20
|
+
const { deep } = Seneca.util
|
|
21
|
+
|
|
22
|
+
const envdef: any = (Model as any).main.env?.['$$env$$'] || {}
|
|
23
|
+
|
|
24
|
+
const seneca = Seneca(deep(base.seneca, {
|
|
25
|
+
tag: '$$name$$-$$env$$',
|
|
26
|
+
log: { level: envdef.log || 'warn' },
|
|
27
|
+
}, envdef.seneca || {}))
|
|
28
|
+
|
|
29
|
+
seneca.context.model = Model
|
|
30
|
+
seneca.context.env = '$$env$$'
|
|
31
|
+
seneca.context.stage = process.env.STAGE || '$$env$$'
|
|
32
|
+
seneca.context.srvname = 'all'
|
|
33
|
+
seneca.context.pkg = Pkg
|
|
34
|
+
|
|
35
|
+
basic(seneca)
|
|
36
|
+
|
|
37
|
+
seneca.use(Local, {
|
|
38
|
+
srv: {
|
|
39
|
+
folder: __dirname + '/../../../dist/srv',
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
await seneca.ready()
|
|
44
|
+
|
|
45
|
+
console.log('$$name$$-backend started', {
|
|
46
|
+
env: '$$env$$', version: Pkg.version, port: $$port$$,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#cloud-config
|
|
2
|
+
# $$Name$$ backend VM bootstrap (generated; customize via tm/env/vm/)
|
|
3
|
+
packages:
|
|
4
|
+
- nodejs
|
|
5
|
+
- npm
|
|
6
|
+
- rsync
|
|
7
|
+
write_files:
|
|
8
|
+
- path: /etc/systemd/system/$$name$$-backend.service
|
|
9
|
+
content: |
|
|
10
|
+
[Unit]
|
|
11
|
+
Description=$$Name$$ backend
|
|
12
|
+
After=network.target
|
|
13
|
+
[Service]
|
|
14
|
+
WorkingDirectory=/opt/$$name$$/backend
|
|
15
|
+
ExecStart=/usr/bin/node dist/env/local/local.js
|
|
16
|
+
Restart=always
|
|
17
|
+
Environment=NODE_ENV=production
|
|
18
|
+
[Install]
|
|
19
|
+
WantedBy=multi-user.target
|
|
20
|
+
runcmd:
|
|
21
|
+
- mkdir -p /opt/$$name$$/backend
|
|
22
|
+
- systemctl daemon-reload
|
|
23
|
+
- systemctl enable $$name$$-backend
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
Basic$$AppName$$LambdaRole:
|
|
3
|
+
Type: AWS::IAM::Role
|
|
4
|
+
Properties:
|
|
5
|
+
RoleName: Basic$$AppName$$LambdaRole${self:custom.index.BasicLambdaRole,"01"}
|
|
6
|
+
AssumeRolePolicyDocument:
|
|
7
|
+
Version: '2012-10-17'
|
|
8
|
+
Statement:
|
|
9
|
+
- Effect: Allow
|
|
10
|
+
Principal:
|
|
11
|
+
Service:
|
|
12
|
+
- lambda.amazonaws.com
|
|
13
|
+
- events.amazonaws.com
|
|
14
|
+
- ecs-tasks.amazonaws.com
|
|
15
|
+
Action: sts:AssumeRole
|
|
16
|
+
Policies:
|
|
17
|
+
- PolicyName: LambdaServiceAccess
|
|
18
|
+
PolicyDocument:
|
|
19
|
+
Version: '2012-10-17'
|
|
20
|
+
Statement:
|
|
21
|
+
- Effect: Allow
|
|
22
|
+
Action:
|
|
23
|
+
- dynamodb:DescribeTable
|
|
24
|
+
- dynamodb:GetItem
|
|
25
|
+
- dynamodb:PutItem
|
|
26
|
+
- dynamodb:UpdateItem
|
|
27
|
+
- dynamodb:DeleteItem
|
|
28
|
+
- dynamodb:Query
|
|
29
|
+
- dynamodb:Scan
|
|
30
|
+
Resource:
|
|
31
|
+
$$dynamoArns$$
|
|
32
|
+
$$customPolicy$$
|
|
33
|
+
ManagedPolicyArns:
|
|
34
|
+
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
|
|
35
|
+
|