@awsless/cli 0.0.45 → 0.0.46-next.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/dist/app.json +1 -1
- package/dist/app.stage.json +1 -1
- package/dist/bin.js +4363 -4253
- package/dist/build-json-schema.js +269 -460
- package/dist/handlers/bundle.mjs +534 -0
- package/dist/handlers/icon.mjs +104 -0
- package/dist/handlers/image.mjs +136 -0
- package/dist/handlers/on-error-log.mjs +177 -0
- package/dist/handlers/on-failure.mjs +138 -0
- package/dist/handlers/pubsub-publisher.mjs +43 -0
- package/dist/handlers/pubsub-server.mjs +63196 -0
- package/dist/handlers/rpc.mjs +465 -0
- package/dist/stack.json +1 -1
- package/dist/stack.stage.json +1 -1
- package/dist/test-global-setup.js +1 -0
- package/package.json +18 -24
- package/dist/prebuild/icon/HASH +0 -1
- package/dist/prebuild/icon/bundle.zip +0 -0
- package/dist/prebuild/image/HASH +0 -1
- package/dist/prebuild/image/bundle.zip +0 -0
- package/dist/prebuild/on-error-log/HASH +0 -1
- package/dist/prebuild/on-error-log/bundle.zip +0 -0
- package/dist/prebuild/on-failure/HASH +0 -1
- package/dist/prebuild/on-failure/bundle.zip +0 -0
- package/dist/prebuild/pubsub-publisher/HASH +0 -1
- package/dist/prebuild/pubsub-publisher/bundle.zip +0 -0
- package/dist/prebuild/pubsub-server/HASH +0 -1
- package/dist/prebuild/pubsub-server/index.mjs +0 -236
- package/dist/prebuild/rpc/HASH +0 -1
- package/dist/prebuild/rpc/bundle.zip +0 -0
- package/dist/prebuild.js +0 -192
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// src/feature/image/server/handle.ts
|
|
2
|
+
import { getObject, putObject } from "@awsless/s3";
|
|
3
|
+
import sharp from "sharp";
|
|
4
|
+
import { getRouteEnv, invokeRoute } from "awsless";
|
|
5
|
+
|
|
6
|
+
// src/feature/image/server/validate.ts
|
|
7
|
+
import { object, picklist, pipe, safeParse, string, transform } from "@awsless/validate";
|
|
8
|
+
var supportedExtensions = ["jpg", "png", "webp"];
|
|
9
|
+
var schema = pipe(string(), transform((path) => {
|
|
10
|
+
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
11
|
+
const parts = cleanPath.split("/");
|
|
12
|
+
const options = parts.pop();
|
|
13
|
+
const originalPath = parts.join("/");
|
|
14
|
+
if (!originalPath || !options) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
const [preset, extension] = options.split(".");
|
|
18
|
+
if (!preset || !extension) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
originalPath,
|
|
23
|
+
preset,
|
|
24
|
+
extension
|
|
25
|
+
};
|
|
26
|
+
}), object({
|
|
27
|
+
originalPath: string(),
|
|
28
|
+
preset: string(),
|
|
29
|
+
extension: picklist(supportedExtensions)
|
|
30
|
+
}));
|
|
31
|
+
var parsePath = (path) => {
|
|
32
|
+
return safeParse(schema, path);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/feature/image/server/handle.ts
|
|
36
|
+
var normalizeExtension = (extension) => {
|
|
37
|
+
if (extension === "jpg") {
|
|
38
|
+
return "jpeg";
|
|
39
|
+
}
|
|
40
|
+
return extension;
|
|
41
|
+
};
|
|
42
|
+
var handle_default = async (event) => {
|
|
43
|
+
try {
|
|
44
|
+
const request = parsePath(event.rawPath);
|
|
45
|
+
const bucket = getRouteEnv("IMAGE_BUCKET");
|
|
46
|
+
const folder = getRouteEnv("IMAGE_FOLDER") ?? "";
|
|
47
|
+
if (!request.success) {
|
|
48
|
+
return { statusCode: 404 };
|
|
49
|
+
}
|
|
50
|
+
const originalPath = request.output.originalPath;
|
|
51
|
+
const preset = request.output.preset;
|
|
52
|
+
const extension = request.output.extension;
|
|
53
|
+
const cacheKey = `${folder}cache/${event.rawPath.startsWith("/") ? event.rawPath.slice(1) : event.rawPath}`;
|
|
54
|
+
if (bucket) {
|
|
55
|
+
const cachedImage = await getObject({
|
|
56
|
+
bucket,
|
|
57
|
+
key: cacheKey
|
|
58
|
+
});
|
|
59
|
+
if (cachedImage) {
|
|
60
|
+
const cachedImageData = await cachedImage.body.transformToByteArray();
|
|
61
|
+
return {
|
|
62
|
+
statusCode: 200,
|
|
63
|
+
body: Buffer.from(cachedImageData).toString("base64"),
|
|
64
|
+
isBase64Encoded: true,
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": `image/${normalizeExtension(extension)}`,
|
|
67
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const configsEnv = getRouteEnv("IMAGE_CONFIG");
|
|
73
|
+
if (!configsEnv) {
|
|
74
|
+
throw new Error("Image configurations not found in environment variables");
|
|
75
|
+
}
|
|
76
|
+
const configs = JSON.parse(configsEnv);
|
|
77
|
+
const presetConfig = configs.presets?.[preset];
|
|
78
|
+
const extensionConfig = configs.extensions?.[extension];
|
|
79
|
+
if (!presetConfig || !extensionConfig) {
|
|
80
|
+
return { statusCode: 404 };
|
|
81
|
+
}
|
|
82
|
+
let baseImage = undefined;
|
|
83
|
+
if (getRouteEnv("IMAGE_ORIGIN_S3")) {
|
|
84
|
+
const result = await getObject({
|
|
85
|
+
bucket,
|
|
86
|
+
key: `${folder}origin/${originalPath}`
|
|
87
|
+
});
|
|
88
|
+
if (result?.body) {
|
|
89
|
+
const data = await result.body.transformToByteArray();
|
|
90
|
+
baseImage = Buffer.from(data);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const originRoute = getRouteEnv("IMAGE_ORIGIN");
|
|
94
|
+
if (!baseImage && originRoute) {
|
|
95
|
+
const result = await invokeRoute(originRoute, { path: originalPath });
|
|
96
|
+
if (typeof result === "string") {
|
|
97
|
+
baseImage = Buffer.from(result, "base64");
|
|
98
|
+
} else if (result === undefined) {
|
|
99
|
+
return { statusCode: 404 };
|
|
100
|
+
} else {
|
|
101
|
+
throw new Error(`Invalid response from image origin lambda. Path: ${originalPath}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!baseImage) {
|
|
105
|
+
return { statusCode: 404 };
|
|
106
|
+
}
|
|
107
|
+
const image = await sharp(baseImage).resize({
|
|
108
|
+
width: presetConfig.width,
|
|
109
|
+
height: presetConfig.height,
|
|
110
|
+
fit: presetConfig.fit,
|
|
111
|
+
position: presetConfig.position
|
|
112
|
+
})[normalizeExtension(extension)]({ ...extensionConfig, quality: presetConfig.quality }).toBuffer();
|
|
113
|
+
await putObject({
|
|
114
|
+
bucket,
|
|
115
|
+
key: cacheKey,
|
|
116
|
+
body: image,
|
|
117
|
+
contentType: `image/${extension}`,
|
|
118
|
+
cacheControl: "public, max-age=31536000, immutable"
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
statusCode: 200,
|
|
122
|
+
body: image.toString("base64"),
|
|
123
|
+
isBase64Encoded: true,
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": `image/${extension}`,
|
|
126
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(error);
|
|
131
|
+
return { statusCode: 500 };
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
export {
|
|
135
|
+
handle_default as default
|
|
136
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// src/feature/on-error-log/server/handle.ts
|
|
2
|
+
import {
|
|
3
|
+
array,
|
|
4
|
+
literal,
|
|
5
|
+
looseObject,
|
|
6
|
+
number,
|
|
7
|
+
object,
|
|
8
|
+
optional,
|
|
9
|
+
picklist,
|
|
10
|
+
pipe,
|
|
11
|
+
safeParse,
|
|
12
|
+
string,
|
|
13
|
+
toLowerCase,
|
|
14
|
+
transform,
|
|
15
|
+
uuid
|
|
16
|
+
} from "@awsless/validate";
|
|
17
|
+
import {
|
|
18
|
+
define,
|
|
19
|
+
getItem,
|
|
20
|
+
number as dbNumber,
|
|
21
|
+
object as dbObject,
|
|
22
|
+
putItem,
|
|
23
|
+
string as dbString
|
|
24
|
+
} from "@awsless/dynamodb";
|
|
25
|
+
import { getRouteEnv, invokeRoute } from "awsless";
|
|
26
|
+
import { createHash } from "crypto";
|
|
27
|
+
import * as zlib from "zlib";
|
|
28
|
+
var RuntimeErrorSchema = object({
|
|
29
|
+
timestamp: string(),
|
|
30
|
+
level: pipe(string(), toLowerCase(), picklist(["error", "warn", "fatal"])),
|
|
31
|
+
requestId: uuid(),
|
|
32
|
+
message: looseObject({
|
|
33
|
+
errorType: string(),
|
|
34
|
+
errorMessage: string(),
|
|
35
|
+
stackTrace: optional(array(string()))
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
var SimpleErrorSchema = object({
|
|
39
|
+
timestamp: string(),
|
|
40
|
+
level: pipe(string(), toLowerCase(), picklist(["error", "warn", "fatal"])),
|
|
41
|
+
requestId: uuid(),
|
|
42
|
+
message: string()
|
|
43
|
+
});
|
|
44
|
+
var SystemErrorSchema = object({
|
|
45
|
+
type: literal("platform.report"),
|
|
46
|
+
time: string(),
|
|
47
|
+
record: looseObject({
|
|
48
|
+
requestId: uuid(),
|
|
49
|
+
status: picklist(["timeout", "error", "failure"]),
|
|
50
|
+
errorType: optional(string())
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
var EventSchema = object({
|
|
54
|
+
logGroup: string(),
|
|
55
|
+
logEvents: array(object({
|
|
56
|
+
id: string(),
|
|
57
|
+
message: string(),
|
|
58
|
+
timestamp: pipe(number(), transform((v) => new Date(v)))
|
|
59
|
+
}))
|
|
60
|
+
});
|
|
61
|
+
var requestTable = define(getRouteEnv("TABLE"), {
|
|
62
|
+
hash: "id",
|
|
63
|
+
schema: dbObject({
|
|
64
|
+
id: dbString(),
|
|
65
|
+
ttl: dbNumber()
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
var registerOwnRequest = async (requestId) => {
|
|
69
|
+
try {
|
|
70
|
+
await putItem(requestTable, {
|
|
71
|
+
id: requestId,
|
|
72
|
+
ttl: Math.floor(Date.now() / 1000) + 3600
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.info("Failed to register the request marker", error);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var isOwnRequest = async (requestId) => {
|
|
79
|
+
try {
|
|
80
|
+
const item = await getItem(requestTable, { id: requestId }, { consistentRead: true });
|
|
81
|
+
return !!item;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.info("Failed to check the request marker", error);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var handle_default = async (event, context) => {
|
|
88
|
+
const consumerRoute = getRouteEnv("CONSUMER");
|
|
89
|
+
if (!consumerRoute) {
|
|
90
|
+
throw new Error("The CONSUMER route env is not set");
|
|
91
|
+
}
|
|
92
|
+
await registerOwnRequest(context.awsRequestId);
|
|
93
|
+
const original = { error: console.error, warn: console.warn };
|
|
94
|
+
console.error = console.info;
|
|
95
|
+
console.warn = console.info;
|
|
96
|
+
try {
|
|
97
|
+
const payload = Buffer.from(event.awslogs.data, "base64");
|
|
98
|
+
const unzipped = zlib.gunzipSync(new Uint8Array(payload));
|
|
99
|
+
const result = safeParse(EventSchema, JSON.parse(unzipped.toString("utf-8")));
|
|
100
|
+
if (!result.success) {
|
|
101
|
+
console.info("Failed to parse log data", result.issues);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const origin = result.output.logGroup.split("/").pop();
|
|
105
|
+
for (const logEvent of result.output.logEvents) {
|
|
106
|
+
const error = parseError(logEvent.message, origin);
|
|
107
|
+
if (!error || await isOwnRequest(error.requestId)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const invoke = invokeRoute(consumerRoute, {
|
|
111
|
+
...error,
|
|
112
|
+
origin,
|
|
113
|
+
date: logEvent.timestamp
|
|
114
|
+
});
|
|
115
|
+
invoke.catch(() => {});
|
|
116
|
+
const deadline = Math.max(0, context.getRemainingTimeInMillis() - 3000);
|
|
117
|
+
await Promise.race([invoke, new Promise((resolve) => setTimeout(resolve, deadline))]);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.info("Failed to consume the error logs", error);
|
|
121
|
+
} finally {
|
|
122
|
+
console.error = original.error;
|
|
123
|
+
console.warn = original.warn;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var parseError = (message, origin) => {
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(message);
|
|
130
|
+
} catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const runtimeError = safeParse(RuntimeErrorSchema, parsed);
|
|
134
|
+
if (runtimeError.success) {
|
|
135
|
+
const { requestId, level } = runtimeError.output;
|
|
136
|
+
const { errorType, errorMessage, stackTrace, ...extra } = runtimeError.output.message;
|
|
137
|
+
const hash = createHash("sha256").update([origin, errorType, errorMessage, stackTrace].join("-")).digest("hex");
|
|
138
|
+
return {
|
|
139
|
+
hash,
|
|
140
|
+
requestId,
|
|
141
|
+
level,
|
|
142
|
+
type: errorType,
|
|
143
|
+
message: errorMessage,
|
|
144
|
+
stackTrace,
|
|
145
|
+
data: Object.keys(extra).length ? extra : undefined
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const systemError = safeParse(SystemErrorSchema, parsed);
|
|
149
|
+
if (systemError.success) {
|
|
150
|
+
const { requestId, status, errorType, ...extra } = systemError.output.record;
|
|
151
|
+
const hash = createHash("sha256").update([origin, errorType, status].join("-")).digest("hex");
|
|
152
|
+
return {
|
|
153
|
+
hash,
|
|
154
|
+
requestId,
|
|
155
|
+
level: "fatal",
|
|
156
|
+
type: errorType ?? status,
|
|
157
|
+
message: `Fatal system error: ${errorType ?? status}`,
|
|
158
|
+
data: Object.keys(extra).length ? extra : undefined
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const simpleError = safeParse(SimpleErrorSchema, parsed);
|
|
162
|
+
if (simpleError.success) {
|
|
163
|
+
const { requestId, level, message: message2 } = simpleError.output;
|
|
164
|
+
const hash = createHash("sha256").update([origin, message2].join("-")).digest("hex");
|
|
165
|
+
return {
|
|
166
|
+
hash,
|
|
167
|
+
requestId,
|
|
168
|
+
level,
|
|
169
|
+
type: "Error",
|
|
170
|
+
message: message2
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
};
|
|
175
|
+
export {
|
|
176
|
+
handle_default as default
|
|
177
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// src/feature/on-failure/server/handle.ts
|
|
2
|
+
import { parse, patch } from "@awsless/json";
|
|
3
|
+
import { invoke } from "@awsless/lambda";
|
|
4
|
+
import { deleteObject, getObject } from "@awsless/s3";
|
|
5
|
+
import { formatRoutePayload, getRouteEnv } from "awsless";
|
|
6
|
+
var handle_default = async (event, context) => {
|
|
7
|
+
if (!Array.isArray(event.Records)) {
|
|
8
|
+
throw new TypeError(`Unknown Event Type: ${JSON.stringify(event)}`);
|
|
9
|
+
}
|
|
10
|
+
await Promise.all(event.Records.map((record) => {
|
|
11
|
+
return unknownRecord(record, context);
|
|
12
|
+
}));
|
|
13
|
+
};
|
|
14
|
+
var unknownRecord = (record, context) => {
|
|
15
|
+
if (typeof record.eventSource === "string") {
|
|
16
|
+
if (record.eventSource.startsWith("aws:sqs")) {
|
|
17
|
+
return sqsRecord(record, context);
|
|
18
|
+
}
|
|
19
|
+
if (record.eventSource.startsWith("aws:s3")) {
|
|
20
|
+
return s3Record(record, context);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
throw new TypeError(`Unknown Record Type: ${JSON.stringify(record)}`);
|
|
24
|
+
};
|
|
25
|
+
var sqsRecord = async (record, context) => {
|
|
26
|
+
const s3Records = parseS3Records(record.body);
|
|
27
|
+
if (s3Records) {
|
|
28
|
+
await Promise.all(s3Records.map((record2) => s3Record(record2, context)));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const payload = {
|
|
32
|
+
type: "queue",
|
|
33
|
+
id: record.messageId,
|
|
34
|
+
date: new Date(Number(record.attributes.SentTimestamp)),
|
|
35
|
+
payload: parsePayload(record.body),
|
|
36
|
+
queue: {
|
|
37
|
+
name: record.messageAttributes.queueName?.stringValue,
|
|
38
|
+
url: record.messageAttributes.queueUrl?.stringValue
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
await invokeConsumer(payload, context);
|
|
42
|
+
};
|
|
43
|
+
var parseS3Records = (body) => {
|
|
44
|
+
try {
|
|
45
|
+
const event = JSON.parse(body);
|
|
46
|
+
if (event?.Event === "s3:TestEvent") {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
if (Array.isArray(event?.Records) && event.Records[0]?.eventSource === "aws:s3") {
|
|
50
|
+
return event.Records;
|
|
51
|
+
}
|
|
52
|
+
} catch {}
|
|
53
|
+
return;
|
|
54
|
+
};
|
|
55
|
+
var s3Record = async (record, context) => {
|
|
56
|
+
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));
|
|
57
|
+
const object = await getObject({
|
|
58
|
+
bucket: record.s3.bucket.name,
|
|
59
|
+
key
|
|
60
|
+
});
|
|
61
|
+
if (!object) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const json = await object.body.transformToString();
|
|
65
|
+
const unknownEvent = JSON.parse(json);
|
|
66
|
+
const payload = formatUnknownFailureEvent(unknownEvent);
|
|
67
|
+
await invokeConsumer(payload, context);
|
|
68
|
+
await deleteObject({
|
|
69
|
+
bucket: record.s3.bucket.name,
|
|
70
|
+
key
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
var isDynamoDBFailureEvent = (event) => {
|
|
74
|
+
return "DDBStreamBatchInfo" in event;
|
|
75
|
+
};
|
|
76
|
+
var formatUnknownFailureEvent = (event) => {
|
|
77
|
+
if (isDynamoDBFailureEvent(event)) {
|
|
78
|
+
return formatDynamoDBStreamFailureEvent(event);
|
|
79
|
+
}
|
|
80
|
+
return formatAsyncLambdaFailureEvent(event);
|
|
81
|
+
};
|
|
82
|
+
var formatAsyncLambdaFailureEvent = (event) => {
|
|
83
|
+
const payload = patchPayload(event.requestPayload);
|
|
84
|
+
const route = payload && typeof payload === "object" ? payload["$awsless-route"] : undefined;
|
|
85
|
+
return {
|
|
86
|
+
type: "async-lambda",
|
|
87
|
+
date: new Date(event.timestamp),
|
|
88
|
+
id: event.requestContext.requestId,
|
|
89
|
+
function: {
|
|
90
|
+
name: typeof route === "string" ? route : event.requestContext.functionArn.split(":")[6]
|
|
91
|
+
},
|
|
92
|
+
payload: typeof route === "string" ? payload.event ?? {} : payload,
|
|
93
|
+
error: {
|
|
94
|
+
type: event.responsePayload.errorType,
|
|
95
|
+
message: event.responsePayload.errorMessage,
|
|
96
|
+
stackTrace: event.responsePayload.stackTrace
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
var formatDynamoDBStreamFailureEvent = (event) => {
|
|
101
|
+
return {
|
|
102
|
+
type: "dynamodb-stream",
|
|
103
|
+
date: new Date(event.timestamp),
|
|
104
|
+
id: event.requestContext.requestId,
|
|
105
|
+
function: {
|
|
106
|
+
name: event.requestContext.functionArn.split(":")[6]
|
|
107
|
+
},
|
|
108
|
+
payload: parsePayload(event.payload)
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
var parsePayload = (payload) => {
|
|
112
|
+
try {
|
|
113
|
+
return parse(payload);
|
|
114
|
+
} catch {
|
|
115
|
+
return payload;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var patchPayload = (payload) => {
|
|
119
|
+
try {
|
|
120
|
+
return patch(payload);
|
|
121
|
+
} catch {
|
|
122
|
+
return payload;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var invokeConsumer = async (payload, context) => {
|
|
126
|
+
const consumerRoute = getRouteEnv("CONSUMER");
|
|
127
|
+
if (!consumerRoute) {
|
|
128
|
+
throw new Error("The CONSUMER route env is not set");
|
|
129
|
+
}
|
|
130
|
+
await invoke({
|
|
131
|
+
name: context.invokedFunctionArn,
|
|
132
|
+
type: "RequestResponse",
|
|
133
|
+
payload: formatRoutePayload(consumerRoute, payload)
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
export {
|
|
137
|
+
handle_default as default
|
|
138
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/feature/pubsub/publisher/handle.ts
|
|
2
|
+
import { stringify } from "@awsless/json";
|
|
3
|
+
import { lambda } from "@awsless/lambda";
|
|
4
|
+
import { maxLength, minLength, object, optional, pipe, string, unknown } from "@awsless/validate";
|
|
5
|
+
import { getRouteEnv } from "awsless";
|
|
6
|
+
import { Redis } from "ioredis";
|
|
7
|
+
var createClient = () => {
|
|
8
|
+
return new Redis({
|
|
9
|
+
host: getRouteEnv("REDIS_HOST"),
|
|
10
|
+
port: Number(getRouteEnv("REDIS_PORT") ?? 6379),
|
|
11
|
+
tls: {},
|
|
12
|
+
lazyConnect: true,
|
|
13
|
+
keepAlive: 0,
|
|
14
|
+
noDelay: true,
|
|
15
|
+
enableReadyCheck: false,
|
|
16
|
+
maxRetriesPerRequest: 3,
|
|
17
|
+
connectTimeout: 5000,
|
|
18
|
+
commandTimeout: 5000
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var handle_default = lambda({
|
|
22
|
+
schema: object({
|
|
23
|
+
topic: pipe(string(), minLength(1), maxLength(128)),
|
|
24
|
+
event: pipe(string(), minLength(1), maxLength(64)),
|
|
25
|
+
payload: optional(unknown())
|
|
26
|
+
}),
|
|
27
|
+
async handle({ topic, event, payload }, ctx) {
|
|
28
|
+
const redis = createClient();
|
|
29
|
+
ctx.onFinally(() => {
|
|
30
|
+
return redis.quit().catch(() => {
|
|
31
|
+
redis.disconnect();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
await redis.publish(getRouteEnv("CHANNEL"), stringify({
|
|
35
|
+
topic,
|
|
36
|
+
event,
|
|
37
|
+
payload: typeof payload === "undefined" ? undefined : stringify(payload)
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
export {
|
|
42
|
+
handle_default as default
|
|
43
|
+
};
|