wirejs-deploy-amplify-basic 0.0.138 → 0.0.139-payments
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/amplify-backend-assets/functions/api/handler.ts +113 -26
- package/amplify-hosting-assets/compute/default/index.js +129 -42
- package/amplify-hosting-assets/compute/default/package.json +2 -2
- package/build.js +11 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/resources/distributed-table.d.ts +4 -1
- package/dist/resources/distributed-table.js +18 -1
- package/dist/resources/endpoint.d.ts +4 -0
- package/dist/resources/endpoint.js +11 -0
- package/dist/services/authentication.js +4 -2
- package/dist/services/realtime.d.ts +2 -2
- package/dist/services/realtime.js +5 -4
- package/package.json +3 -3
|
@@ -4,21 +4,54 @@ import {
|
|
|
4
4
|
BackgroundJob,
|
|
5
5
|
CookieJar,
|
|
6
6
|
Context,
|
|
7
|
+
Endpoint,
|
|
8
|
+
SystemAttribute,
|
|
7
9
|
requiresContext,
|
|
8
10
|
} from 'wirejs-resources';
|
|
9
11
|
|
|
10
12
|
// @ts-ignore
|
|
11
13
|
import * as api from '../../../api/index';
|
|
12
14
|
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
function deriveLocation(event: APIGatewayProxyEventV2) {
|
|
16
|
+
if (event.headers['x-wirejs-location']) {
|
|
17
|
+
return new URL(event.headers['x-wirejs-location']);
|
|
18
|
+
} else {
|
|
19
|
+
const baseUrl = `https://${event.requestContext.domainName}${event.rawPath}`;
|
|
20
|
+
const queryString = event.queryStringParameters
|
|
21
|
+
? `?${new URLSearchParams(event.queryStringParameters as any).toString()}`
|
|
22
|
+
: '';
|
|
23
|
+
return new URL(`${baseUrl}${queryString}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
19
26
|
|
|
27
|
+
function createContext(event: APIGatewayProxyEventV2) {
|
|
20
28
|
const cookies = new CookieJar(event.headers['cookie']);
|
|
21
|
-
|
|
29
|
+
const location = deriveLocation(event);
|
|
30
|
+
return new Context({
|
|
31
|
+
cookies,
|
|
32
|
+
location,
|
|
33
|
+
httpMethod: event.requestContext.http.method,
|
|
34
|
+
requestBody: event.body,
|
|
35
|
+
requestHeaders: event.headers as Record<string, string>,
|
|
36
|
+
runtimeAttributes: [
|
|
37
|
+
new SystemAttribute('wirejs', 'deployment-type', {
|
|
38
|
+
description: 'Deployment under which your system is running.',
|
|
39
|
+
value: 'wirejs-deploy-amplify-basic'
|
|
40
|
+
}),
|
|
41
|
+
new SystemAttribute('wirejs', 'http-origin-local', {
|
|
42
|
+
description: 'HTTP origin (base address) to use for local development.',
|
|
43
|
+
value: null,
|
|
44
|
+
}),
|
|
45
|
+
new SystemAttribute('wirejs', 'http-origin-network', {
|
|
46
|
+
description: 'HTTP origin (base address) for machines on your network to use.',
|
|
47
|
+
value: null,
|
|
48
|
+
}),
|
|
49
|
+
new SystemAttribute('wirejs', 'http-origin-public', {
|
|
50
|
+
description: 'HTTP origin (base address) for machines outside your network to use. Only populated for `npm run start:public`, and only accessible in environments that support NAT-PMP.',
|
|
51
|
+
value: location.origin
|
|
52
|
+
}),
|
|
53
|
+
]
|
|
54
|
+
});
|
|
22
55
|
}
|
|
23
56
|
|
|
24
57
|
async function callApiMethod(api: any, call: any, context: any) {
|
|
@@ -53,32 +86,70 @@ function isBackgroundJob(o: any): o is BackgroundJob<any> {
|
|
|
53
86
|
return typeof o === 'object' && typeof o.start === 'function';
|
|
54
87
|
}
|
|
55
88
|
|
|
89
|
+
function extractSetCookies(context: Context) {
|
|
90
|
+
console.log('setting cookies', context.cookies.getSetCookies());
|
|
91
|
+
const cookies: string[] = [];
|
|
92
|
+
for (const cookie of context.cookies.getSetCookies()) {
|
|
93
|
+
const cookieOptions = [];
|
|
94
|
+
if (cookie.maxAge) cookieOptions.push(`Max-Age=${cookie.maxAge}`);
|
|
95
|
+
if (cookie.httpOnly) cookieOptions.push('HttpOnly');
|
|
96
|
+
if (cookie.secure) cookieOptions.push('Secure');
|
|
97
|
+
cookies.push(`${cookie.name}=${cookie.value}; ${cookieOptions.join('; ')}`);
|
|
98
|
+
}
|
|
99
|
+
return cookies;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
*
|
|
104
|
+
* @param {Context} context
|
|
105
|
+
* @param {http.ServerResponse} res
|
|
106
|
+
*/
|
|
107
|
+
function getResponseHeadersFromContext(context: Context) {
|
|
108
|
+
const headers = {} as Record<string, any>;
|
|
109
|
+
for (const [k, v] of Object.entries(context.responseHeaders)) {
|
|
110
|
+
headers[k] = v;
|
|
111
|
+
}
|
|
112
|
+
if (context.locationIsDirty) {
|
|
113
|
+
headers['x-wirejs-redirect'] = context.location.href;
|
|
114
|
+
}
|
|
115
|
+
return headers;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param pattern string pattern, where `*` matches anything
|
|
120
|
+
* @param text
|
|
121
|
+
* @returns
|
|
122
|
+
*/
|
|
123
|
+
function globMatch(pattern: string, text: string) {
|
|
124
|
+
const parts = pattern.split('%');
|
|
125
|
+
const regex = new RegExp(parts.join('.+'));
|
|
126
|
+
return regex.test(text);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Compare two strings by length for sorting in order of increasing length.
|
|
131
|
+
*
|
|
132
|
+
* @returns
|
|
133
|
+
*/
|
|
134
|
+
function byPathLength(a: Endpoint, b: Endpoint) {
|
|
135
|
+
return a.path.length - b.path.length;
|
|
136
|
+
}
|
|
137
|
+
|
|
56
138
|
export const handler: LambdaFunctionURLHandler = async (event, context) => {
|
|
57
139
|
const calls = JSON.parse(event.body!);
|
|
140
|
+
const wjsContext = createContext(event);
|
|
141
|
+
const path = wjsContext.location.pathname;
|
|
58
142
|
|
|
59
|
-
if (Array.isArray(calls)) {
|
|
143
|
+
if ((path === '/api' || path.startsWith('/api/')) && Array.isArray(calls)) {
|
|
60
144
|
const responses = [];
|
|
61
|
-
const wjsContext = createContext(event);
|
|
62
145
|
for (const call of calls) {
|
|
63
146
|
console.log('handling API call', call);
|
|
64
147
|
responses.push(await callApiMethod(api, call, wjsContext));
|
|
65
148
|
}
|
|
66
|
-
|
|
67
|
-
console.log('setting cookies', wjsContext.cookies.getSetCookies());
|
|
68
|
-
|
|
69
|
-
const cookies: string[] = [];
|
|
70
|
-
for (const cookie of wjsContext.cookies.getSetCookies()) {
|
|
71
|
-
const cookieOptions = [];
|
|
72
|
-
if (cookie.maxAge) cookieOptions.push(`Max-Age=${cookie.maxAge}`);
|
|
73
|
-
if (cookie.httpOnly) cookieOptions.push('HttpOnly');
|
|
74
|
-
if (cookie.secure) cookieOptions.push('Secure');
|
|
75
|
-
cookies.push(`${cookie.name}=${cookie.value}; ${cookieOptions.join('; ')}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
149
|
return {
|
|
79
|
-
statusCode: 200,
|
|
80
|
-
cookies,
|
|
150
|
+
statusCode: wjsContext.responseCode ?? 200,
|
|
81
151
|
headers: {
|
|
152
|
+
...getResponseHeadersFromContext(wjsContext),
|
|
82
153
|
'Content-Type': 'application/json; charset=utf-8'
|
|
83
154
|
},
|
|
84
155
|
body: JSON.stringify(responses)
|
|
@@ -124,10 +195,26 @@ export const handler: LambdaFunctionURLHandler = async (event, context) => {
|
|
|
124
195
|
body: JSON.stringify({ message: 'Background job complete' })
|
|
125
196
|
};
|
|
126
197
|
} else {
|
|
127
|
-
|
|
198
|
+
const allHandlers = [...Endpoint.list()];
|
|
199
|
+
const matchingHandlers = allHandlers
|
|
200
|
+
.filter(e => globMatch(e.path, wjsContext.location.pathname));
|
|
201
|
+
const matchingEndpoint = matchingHandlers.sort(byPathLength).pop();
|
|
202
|
+
|
|
203
|
+
if (!matchingEndpoint) {
|
|
204
|
+
console.error('Invalid request format', calls);
|
|
205
|
+
return {
|
|
206
|
+
statusCode: 400,
|
|
207
|
+
body: JSON.stringify({ error: 'Invalid request format' })
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const response = await matchingEndpoint.handle(wjsContext);
|
|
128
212
|
return {
|
|
129
|
-
statusCode:
|
|
130
|
-
|
|
213
|
+
statusCode: wjsContext.responseCode ?? 200,
|
|
214
|
+
headers: {
|
|
215
|
+
...getResponseHeadersFromContext(wjsContext),
|
|
216
|
+
},
|
|
217
|
+
body: response
|
|
131
218
|
};
|
|
132
219
|
}
|
|
133
220
|
}
|
|
@@ -9,6 +9,7 @@ import { Context, CookieJar } from 'wirejs-resources';
|
|
|
9
9
|
|
|
10
10
|
const SSR_ROOT = path.join(path.dirname(new URL(import.meta.url).pathname), 'ssr');
|
|
11
11
|
let API_URL = undefined;
|
|
12
|
+
let GENERATED = undefined;
|
|
12
13
|
|
|
13
14
|
const logger = {
|
|
14
15
|
log(...items) {
|
|
@@ -31,6 +32,7 @@ try {
|
|
|
31
32
|
logger.log("backend config found", backendConfig);
|
|
32
33
|
if (backendConfig.apiUrl) {
|
|
33
34
|
API_URL = backendConfig.apiUrl;
|
|
35
|
+
GENERATED = backendConfig.generated;
|
|
34
36
|
}
|
|
35
37
|
} catch {
|
|
36
38
|
logger.log("No backend API config found.");
|
|
@@ -74,12 +76,37 @@ function toJSPath(path) {
|
|
|
74
76
|
* @param {http.IncomingMessage} req
|
|
75
77
|
* @returns
|
|
76
78
|
*/
|
|
77
|
-
function createContext(req) {
|
|
78
|
-
const { url, headers } = req;
|
|
79
|
+
async function createContext(req) {
|
|
80
|
+
const { url, headers, method } = req;
|
|
81
|
+
const body = method.toLowerCase() === 'post'
|
|
82
|
+
? (await postData(req))
|
|
83
|
+
: undefined;
|
|
79
84
|
const origin = headers.origin || `https://${headers.host}`;
|
|
80
85
|
const location = new URL(`${origin}${url}`);
|
|
81
86
|
const cookies = new CookieJar(headers.cookie);
|
|
82
|
-
return new Context({
|
|
87
|
+
return new Context({
|
|
88
|
+
cookies,
|
|
89
|
+
location,
|
|
90
|
+
httpMethod: method,
|
|
91
|
+
requestHeaders: headers,
|
|
92
|
+
requestBody: body,
|
|
93
|
+
runtimeAttributes: [
|
|
94
|
+
new SystemAttribute('wirejs', 'deployment-type', {
|
|
95
|
+
description: 'Deployment under which your system is running.',
|
|
96
|
+
value: 'wirejs-deploy-amplify-basic'
|
|
97
|
+
}),
|
|
98
|
+
new SystemAttribute('wirejs', 'http-origin-local', {
|
|
99
|
+
description: 'HTTP origin (base address) to use for local development.',
|
|
100
|
+
}),
|
|
101
|
+
new SystemAttribute('wirejs', 'http-origin-network', {
|
|
102
|
+
description: 'HTTP origin (base address) for machines on your network to use.',
|
|
103
|
+
}),
|
|
104
|
+
new SystemAttribute('wirejs', 'http-origin-public', {
|
|
105
|
+
description: 'HTTP origin (base address) for machines outside your network to use. Only populated for `npm run start:public`, and only accessible in environments that support NAT-PMP.',
|
|
106
|
+
value: origin
|
|
107
|
+
}),
|
|
108
|
+
]
|
|
109
|
+
});
|
|
83
110
|
}
|
|
84
111
|
|
|
85
112
|
/**
|
|
@@ -109,12 +136,11 @@ function routeSSR(context, forceExt) {
|
|
|
109
136
|
}
|
|
110
137
|
|
|
111
138
|
/**
|
|
112
|
-
* @param {
|
|
139
|
+
* @param {Context} context
|
|
113
140
|
* @param {http.ServerResponse} res
|
|
114
141
|
* @returns
|
|
115
142
|
*/
|
|
116
|
-
async function trySSRScriptPath(
|
|
117
|
-
const context = createContext(req);
|
|
143
|
+
async function trySSRScriptPath(context, res) {
|
|
118
144
|
const srcPath = routeSSR(context);
|
|
119
145
|
if (!srcPath) return false;
|
|
120
146
|
|
|
@@ -134,13 +160,26 @@ async function trySSRScriptPath(req, res) {
|
|
|
134
160
|
|
|
135
161
|
/**
|
|
136
162
|
*
|
|
137
|
-
* @param {
|
|
163
|
+
* @param {Context} context
|
|
164
|
+
* @param {http.ServerResponse} res
|
|
165
|
+
*/
|
|
166
|
+
function setResponseDetailsFromContext(context, res) {
|
|
167
|
+
for (const [k, v] of Object.entries(context.responseHeaders)) {
|
|
168
|
+
res.setHeader(k, v);
|
|
169
|
+
}
|
|
170
|
+
if (context.locationIsDirty) {
|
|
171
|
+
res.setHeader('Location', context.location.href);
|
|
172
|
+
}
|
|
173
|
+
if (context.responseCode) res.statusCode = context.responseCode;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @param {Context} context
|
|
138
179
|
* @param {http.ServerResponse} res
|
|
139
180
|
* @returns
|
|
140
181
|
*/
|
|
141
|
-
async function trySSRPath(
|
|
142
|
-
const context = createContext(req);
|
|
143
|
-
|
|
182
|
+
async function trySSRPath(context, res) {
|
|
144
183
|
const asJSPath = context.location.pathname.replace(/\.(\w+)$/, '') + '.js';
|
|
145
184
|
const srcPath = routeSSR(context, 'js');
|
|
146
185
|
if (!srcPath) return false;
|
|
@@ -155,26 +194,34 @@ async function trySSRPath(req, res) {
|
|
|
155
194
|
const module = self.exports;
|
|
156
195
|
if (typeof module.generate === 'function') {
|
|
157
196
|
const doc = await module.generate(context);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
197
|
+
if (typeof doc.outerHTML === 'undefined') {
|
|
198
|
+
res.setHeader('Content-Type', contentType(
|
|
199
|
+
context.location.pathname.split('.').pop()
|
|
200
|
+
));
|
|
201
|
+
setResponseDetailsFromContext(context, res);
|
|
202
|
+
res.end(doc);
|
|
203
|
+
} else {
|
|
204
|
+
const doctype = doc.parentNode.doctype?.name || '';
|
|
205
|
+
|
|
206
|
+
let hydrationsFound = 0;
|
|
207
|
+
while (globalThis.pendingDehydrations?.length > 0) {
|
|
208
|
+
globalThis.pendingDehydrations.shift()(doc);
|
|
209
|
+
hydrationsFound++;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (hydrationsFound) {
|
|
213
|
+
const script = doc.parentNode.createElement('script');
|
|
214
|
+
script.src = asJSPath;
|
|
215
|
+
doc.parentNode.body.appendChild(script);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
219
|
+
setResponseDetailsFromContext(context, res);
|
|
220
|
+
res.end([
|
|
221
|
+
doctype ? `<!doctype ${doctype}>\n` : '',
|
|
222
|
+
doc.outerHTML
|
|
223
|
+
].join(''));
|
|
170
224
|
}
|
|
171
|
-
|
|
172
|
-
res.setHeader('Content-type', 'text/html; charset=utf-8')
|
|
173
|
-
res.end([
|
|
174
|
-
doctype ? `<!doctype ${doctype}>\n` : '',
|
|
175
|
-
doc.outerHTML
|
|
176
|
-
].join(''));
|
|
177
|
-
|
|
178
225
|
return true;
|
|
179
226
|
} else {
|
|
180
227
|
logger.info('SSR module missing generate function');
|
|
@@ -209,34 +256,71 @@ async function postData(request) {
|
|
|
209
256
|
});
|
|
210
257
|
};
|
|
211
258
|
|
|
212
|
-
|
|
259
|
+
/**
|
|
260
|
+
*
|
|
261
|
+
* @param {Context} context
|
|
262
|
+
* @param {http.ServerResponse} res
|
|
263
|
+
* @returns
|
|
264
|
+
*/
|
|
265
|
+
async function tryAPIPath(context, res) {
|
|
213
266
|
if (!API_URL) {
|
|
214
267
|
logger.error('Tried to proxy without API_URL config.');
|
|
215
268
|
return false;
|
|
216
269
|
}
|
|
270
|
+
|
|
271
|
+
const path = context.location.pathname;
|
|
217
272
|
|
|
218
|
-
if (!
|
|
273
|
+
if (!path === '/api' && !path.startsWith('/api/')) {
|
|
219
274
|
return false;
|
|
220
275
|
}
|
|
221
276
|
|
|
222
|
-
return proxyRequest(
|
|
277
|
+
return proxyRequest(context, res, API_URL);
|
|
223
278
|
}
|
|
224
279
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
280
|
+
/**
|
|
281
|
+
*
|
|
282
|
+
* @param {Context} context
|
|
283
|
+
* @param {http.ServerResponse} res
|
|
284
|
+
* @returns
|
|
285
|
+
*/
|
|
286
|
+
async function tryEndpointPath(context, res) {
|
|
287
|
+
const endpoints = GENERATED.filter(r => r.type === 'Endpoint');
|
|
288
|
+
const matchingEndpoints = endpoints
|
|
289
|
+
.filter(e => globMatch(e.path, context.location.pathname));
|
|
290
|
+
if (matchingEndpoints.length === 0) return;
|
|
291
|
+
|
|
292
|
+
proxyRequest(context, res, API_URL);
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
229
295
|
|
|
230
|
-
|
|
296
|
+
/**
|
|
297
|
+
*
|
|
298
|
+
* @param {Context} context
|
|
299
|
+
* @param {http.ServerResponse} res
|
|
300
|
+
* @param {*} targetUrl
|
|
301
|
+
* @returns
|
|
302
|
+
*/
|
|
303
|
+
async function proxyRequest(context, res, targetUrl) {
|
|
304
|
+
const method = context.httpMethod;
|
|
305
|
+
const headers = {
|
|
306
|
+
...context.requestHeaders,
|
|
307
|
+
'x-wirejs-location': context.location.toString()
|
|
308
|
+
};
|
|
309
|
+
const body = context.body;
|
|
231
310
|
|
|
232
311
|
try {
|
|
233
|
-
const response = await fetch(targetUrl,
|
|
312
|
+
const response = await fetch(targetUrl, { method, headers, body });
|
|
234
313
|
const responseBody = await response.text();
|
|
235
314
|
const responseHeaders = response.headers;
|
|
236
315
|
|
|
237
316
|
res.statusCode = response.status;
|
|
317
|
+
|
|
238
318
|
[...responseHeaders.keys()].forEach((header) => {
|
|
239
|
-
|
|
319
|
+
if (header === 'x-wirejs-redirect') {
|
|
320
|
+
res.setHeader('Location', responseHeaders.get(header));
|
|
321
|
+
} else {
|
|
322
|
+
res.setHeader(header, responseHeaders.get(header));
|
|
323
|
+
}
|
|
240
324
|
});
|
|
241
325
|
|
|
242
326
|
res.end(responseBody);
|
|
@@ -258,9 +342,12 @@ async function proxyRequest(req, res, targetUrl) {
|
|
|
258
342
|
async function handleRequest(req, res) {
|
|
259
343
|
logger.info('received', JSON.stringify({ url: req.url }, null, 2));
|
|
260
344
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (await
|
|
345
|
+
const context = await createContext(req);
|
|
346
|
+
|
|
347
|
+
if (await trySSRScriptPath(context, res)) return;
|
|
348
|
+
if (await trySSRPath(context, res)) return;
|
|
349
|
+
if (await tryAPIPath(context, res)) return;
|
|
350
|
+
if (await tryEndpointPath(context, res)) return;
|
|
264
351
|
|
|
265
352
|
// if we've made it this far, we don't have what you're looking for
|
|
266
353
|
res.statusCode = '404';
|
package/build.js
CHANGED
|
@@ -94,6 +94,10 @@ async function discoverResources() {
|
|
|
94
94
|
path.join(BACKEND_DIR, 'generated-resources.ts'),
|
|
95
95
|
`export default ${JSON.stringify(globalThis.wirejsResources, null, 2)}`
|
|
96
96
|
);
|
|
97
|
+
await fs.promises.writeFile(
|
|
98
|
+
path.join(BACKEND_DIR, 'generated-resources.json'),
|
|
99
|
+
JSON.stringify(globalThis.wirejsResources, null, 2)
|
|
100
|
+
);
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
async function deployFrontend() {
|
|
@@ -131,16 +135,17 @@ if (action === 'prebuild') {
|
|
|
131
135
|
console.log("prebuild done");
|
|
132
136
|
} else if (action === 'inject-backend') {
|
|
133
137
|
console.log("starting inject-backend");
|
|
134
|
-
const config = JSON.parse(await fs.promises.readFile(
|
|
138
|
+
const config = JSON.parse(await fs.promises.readFile(
|
|
139
|
+
path.join('.', 'amplify_outputs.json')
|
|
140
|
+
));
|
|
141
|
+
const generated = JSON.parse(await fs.promises.readFile(
|
|
142
|
+
path.join(BACKEND_DIR, 'generated-resources.json')
|
|
143
|
+
));
|
|
135
144
|
|
|
136
145
|
const apiUrl = config.custom.api;
|
|
137
|
-
const auth = {
|
|
138
|
-
|
|
139
|
-
};
|
|
140
|
-
|
|
141
146
|
const configJSON = JSON.stringify({
|
|
142
147
|
apiUrl,
|
|
143
|
-
|
|
148
|
+
generated
|
|
144
149
|
});
|
|
145
150
|
const configJS = `const config = ${configJSON};\nexport default config;`;
|
|
146
151
|
|
package/dist/index.d.ts
CHANGED
|
@@ -4,3 +4,4 @@ export { AuthenticationService } from './services/authentication.js';
|
|
|
4
4
|
export { DistributedTable } from './resources/distributed-table.js';
|
|
5
5
|
export { RealtimeService } from './services/realtime.js';
|
|
6
6
|
export { BackgroundJob } from './resources/background-job.js';
|
|
7
|
+
export { Endpoint } from './resources/endpoint.js';
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,12 @@ import { RealtimeService } from './services/realtime.js';
|
|
|
12
12
|
export { RealtimeService } from './services/realtime.js';
|
|
13
13
|
import { BackgroundJob } from './resources/background-job.js';
|
|
14
14
|
export { BackgroundJob } from './resources/background-job.js';
|
|
15
|
+
import { Endpoint } from './resources/endpoint.js';
|
|
16
|
+
export { Endpoint } from './resources/endpoint.js';
|
|
15
17
|
// expose resources to other resources that might depend on it.
|
|
16
18
|
overrides.AuthenticationService = AuthenticationService;
|
|
17
19
|
overrides.DistributedTable = DistributedTable;
|
|
18
20
|
overrides.FileService = FileService;
|
|
19
21
|
overrides.RealtimeService = RealtimeService;
|
|
20
22
|
overrides.BackgroundJob = BackgroundJob;
|
|
23
|
+
overrides.Endpoint = Endpoint;
|
|
@@ -25,7 +25,9 @@ export declare class DistributedTable<const P extends Parser<any>, const T exten
|
|
|
25
25
|
});
|
|
26
26
|
get partitionKeyName(): Key['partition']['field'];
|
|
27
27
|
get sortKeyName(): 'field' extends keyof Key['sort'] ? (Key['sort']['field'] extends string ? Key['sort']['field'] : undefined) : undefined;
|
|
28
|
-
save(item: T
|
|
28
|
+
save(item: T, options?: {
|
|
29
|
+
onlyIfNotExists?: boolean;
|
|
30
|
+
}): Promise<void>;
|
|
29
31
|
saveMany(items: T[]): Promise<void>;
|
|
30
32
|
delete(item: RecordKey<T, Key>): Promise<void>;
|
|
31
33
|
deleteMany(items: (RecordKey<T, Key>)[]): Promise<void>;
|
|
@@ -38,4 +40,5 @@ export declare class DistributedTable<const P extends Parser<any>, const T exten
|
|
|
38
40
|
where: KeyCondition<BaseDistributedTable<P, T, Key, Indexes>, GivenPartition>;
|
|
39
41
|
filter?: Filter<Omit<T, IndexFieldNames<BaseDistributedTable<P, T, Key, Indexes>, GivenPartition> & string>>;
|
|
40
42
|
}): AsyncGenerator<T>;
|
|
43
|
+
isAlreadyExistsError(error: any): boolean;
|
|
41
44
|
}
|
|
@@ -151,16 +151,30 @@ export class DistributedTable extends Resource {
|
|
|
151
151
|
}
|
|
152
152
|
return ddbKey;
|
|
153
153
|
}
|
|
154
|
-
async save(item) {
|
|
154
|
+
async save(item, options) {
|
|
155
155
|
const key = this.#getDDBKey(item);
|
|
156
156
|
const itemToSave = {
|
|
157
157
|
...key,
|
|
158
158
|
...item,
|
|
159
159
|
};
|
|
160
160
|
console.log('Saving item to DynamoDB:', itemToSave);
|
|
161
|
+
let ConditionExpression = undefined;
|
|
162
|
+
let ExpressionAttributeNames = undefined;
|
|
163
|
+
if (options?.onlyIfNotExists) {
|
|
164
|
+
ExpressionAttributeNames = {
|
|
165
|
+
[fieldAlias(this.partitionKeyName)]: this.partitionKeyName
|
|
166
|
+
};
|
|
167
|
+
ConditionExpression = `attribute_not_exists(${fieldAlias(this.partitionKeyName)})`;
|
|
168
|
+
if (this.sortKeyName) {
|
|
169
|
+
ExpressionAttributeNames[fieldAlias(this.sortKeyName)] = this.sortKeyName;
|
|
170
|
+
ConditionExpression += ` AND attribute_not_exists(${fieldAlias(this.sortKeyName)})`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
161
173
|
await this.ddbClient.send(new PutCommand({
|
|
162
174
|
TableName: this.table,
|
|
163
175
|
Item: itemToSave,
|
|
176
|
+
ConditionExpression,
|
|
177
|
+
ExpressionAttributeNames,
|
|
164
178
|
}));
|
|
165
179
|
}
|
|
166
180
|
async saveMany(items) {
|
|
@@ -265,4 +279,7 @@ export class DistributedTable extends Resource {
|
|
|
265
279
|
}
|
|
266
280
|
} while (lastEvaluatedKey);
|
|
267
281
|
}
|
|
282
|
+
isAlreadyExistsError(error) {
|
|
283
|
+
return error.name === 'ConditionalCheckFailedException';
|
|
284
|
+
}
|
|
268
285
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Endpoint as BaseEndpoint } from 'wirejs-resources';
|
|
2
|
+
import { addResource } from '../resource-collector';
|
|
3
|
+
export class Endpoint extends BaseEndpoint {
|
|
4
|
+
constructor(scope, id, options) {
|
|
5
|
+
super(scope, id, options);
|
|
6
|
+
addResource('Endpoint', {
|
|
7
|
+
absoluteId: this.absoluteId,
|
|
8
|
+
path: this.path
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { env } from 'process';
|
|
2
2
|
import * as jose from 'jose';
|
|
3
3
|
import { CognitoIdentityProviderClient, SignUpCommand, ForgotPasswordCommand, ConfirmForgotPasswordCommand, ConfirmSignUpCommand, ResendConfirmationCodeCommand, InitiateAuthCommand, ChangePasswordCommand, } from '@aws-sdk/client-cognito-identity-provider';
|
|
4
|
-
import { withContext, SignedCookie,
|
|
4
|
+
import { withContext, SignedCookie, Setting, AuthenticationService as AuthenticationServiceBase, } from 'wirejs-resources';
|
|
5
5
|
import { addResource } from '../resource-collector.js';
|
|
6
6
|
const ClientId = env['COGNITO_CLIENT_ID'];
|
|
7
7
|
const actions = {
|
|
@@ -120,7 +120,9 @@ export class AuthenticationService extends AuthenticationServiceBase {
|
|
|
120
120
|
#keepalive;
|
|
121
121
|
constructor(scope, id, options = {}) {
|
|
122
122
|
super(scope, id, options);
|
|
123
|
-
const signingSecret = new
|
|
123
|
+
const signingSecret = new Setting(this, 'jwt-signing-secret', {
|
|
124
|
+
private: true, init: 'random'
|
|
125
|
+
});
|
|
124
126
|
this.#keepalive = options.keepalive ?? false;
|
|
125
127
|
this.#cookie = new SignedCookie(this, options.cookie ?? 'identity', signingSecret, { maxAge: ONE_WEEK });
|
|
126
128
|
addResource('AuthenticationService', { absoluteId: this.absoluteId });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Resource } from 'wirejs-resources';
|
|
1
|
+
import { Context, Resource } from 'wirejs-resources';
|
|
2
2
|
export declare const REALTIME_SECRET_SCOPE = "wirejs-global";
|
|
3
3
|
export declare const REALTIME_SECRET_ID = "realtime-secret";
|
|
4
4
|
export declare class RealtimeService<T = any> extends Resource {
|
|
@@ -9,5 +9,5 @@ export declare class RealtimeService<T = any> extends Resource {
|
|
|
9
9
|
*/
|
|
10
10
|
get address(): string;
|
|
11
11
|
publish(channel: string, events: T[]): Promise<any>;
|
|
12
|
-
getStream(channel: string): Promise<any>;
|
|
12
|
+
getStream(_context: Context, channel: string): Promise<any>;
|
|
13
13
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as jose from 'jose';
|
|
2
|
-
import { Resource,
|
|
2
|
+
import { Resource, Setting } from 'wirejs-resources';
|
|
3
3
|
import { SignatureV4 } from '@aws-sdk/signature-v4';
|
|
4
4
|
import { HttpRequest } from '@aws-sdk/protocol-http';
|
|
5
5
|
import { defaultProvider } from '@aws-sdk/credential-provider-node';
|
|
@@ -10,7 +10,9 @@ export const REALTIME_SECRET_ID = 'realtime-secret';
|
|
|
10
10
|
let secret = undefined;
|
|
11
11
|
function createSecret() {
|
|
12
12
|
if (!secret) {
|
|
13
|
-
secret = new
|
|
13
|
+
secret = new Setting(REALTIME_SECRET_SCOPE, REALTIME_SECRET_ID, {
|
|
14
|
+
private: true, init: 'random'
|
|
15
|
+
});
|
|
14
16
|
}
|
|
15
17
|
return secret;
|
|
16
18
|
}
|
|
@@ -70,7 +72,6 @@ export class RealtimeService extends Resource {
|
|
|
70
72
|
}
|
|
71
73
|
// TODO: Utility for making SigV4 requests a little more concise.
|
|
72
74
|
const credentials = await defaultProvider()();
|
|
73
|
-
;
|
|
74
75
|
const signer = new SignatureV4({
|
|
75
76
|
service: 'appsync',
|
|
76
77
|
region: process.env.AWS_REGION || 'us-east-1',
|
|
@@ -106,7 +107,7 @@ export class RealtimeService extends Resource {
|
|
|
106
107
|
}
|
|
107
108
|
return response.json();
|
|
108
109
|
}
|
|
109
|
-
async getStream(channel) {
|
|
110
|
+
async getStream(_context, channel) {
|
|
110
111
|
this.#validateChannelName(channel);
|
|
111
112
|
const channelString = `${this.#namespace}/${channel}`;
|
|
112
113
|
const payload = { channel: channelString };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wirejs-deploy-amplify-basic",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.139-payments",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"jsdom": "^25.0.0",
|
|
42
42
|
"recursive-copy": "^2.0.14",
|
|
43
43
|
"rimraf": "^6.0.1",
|
|
44
|
-
"wirejs-dom": "^1.0.
|
|
45
|
-
"wirejs-resources": "^0.1.
|
|
44
|
+
"wirejs-dom": "^1.0.42",
|
|
45
|
+
"wirejs-resources": "^0.1.107-payments"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@aws-amplify/backend": "^1.14.0",
|