diff --git a/serverless/awsjs/package-lock.json b/serverless/awsjs/package-lock.json index f4b26c1..4916391 100644 --- a/serverless/awsjs/package-lock.json +++ b/serverless/awsjs/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "devDependencies": { "@types/aws-lambda": "^8.10.108", + "@types/node": "^18.11.2", "esbuild-runner": "^2.2.2", "typescript": "^4.8.4", "zora": "^5.1.0" @@ -54,6 +55,12 @@ "integrity": "sha512-1yh1W1WoqK3lGHy+V/Fi55zobxrDHUUsluCWdMlOXkCvtsCmHPXOG+CQ2STIL4B1g6xi6I6XzxaF8V9+zeIFLA==", "dev": true }, + "node_modules/@types/node": { + "version": "18.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.2.tgz", + "integrity": "sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw==", + "dev": true + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -522,6 +529,12 @@ "integrity": "sha512-1yh1W1WoqK3lGHy+V/Fi55zobxrDHUUsluCWdMlOXkCvtsCmHPXOG+CQ2STIL4B1g6xi6I6XzxaF8V9+zeIFLA==", "dev": true }, + "@types/node": { + "version": "18.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.2.tgz", + "integrity": "sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw==", + "dev": true + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", diff --git a/serverless/awsjs/package.json b/serverless/awsjs/package.json index 6cd8740..af55970 100644 --- a/serverless/awsjs/package.json +++ b/serverless/awsjs/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "devDependencies": { "@types/aws-lambda": "^8.10.108", + "@types/node": "^18.11.2", "esbuild-runner": "^2.2.2", "typescript": "^4.8.4", "zora": "^5.1.0" @@ -11,6 +12,6 @@ "scripts": { "test": "node -r esbuild-runner/register src/index.test.ts", "tsc": "tsc --noEmit --watch", - "build": "esbuild src/index.ts --target=es2020 --outfile=dist/index.js --format=cjs --bundle --banner:js=//$(git describe --always) && cd dist && zip lambda_function.zip index.js" + "build": "esbuild src/index.ts --target=es2020 --outfile=dist/index.js --format=cjs --bundle --platform=node --target=node16 --external:/var/runtime/node_modules/aws-sdk/clients/s3.js --banner:js=//$(git describe --always) && cd dist && zip lambda_function.zip index.js" } } diff --git a/serverless/awsjs/src/index.ts b/serverless/awsjs/src/index.ts index a6c242c..814b58f 100644 --- a/serverless/awsjs/src/index.ts +++ b/serverless/awsjs/src/index.ts @@ -1,14 +1,81 @@ -import "../../../js"; - +import { Readable } from "stream"; import { Context, APIGatewayProxyResult, APIGatewayEvent } from "aws-lambda"; +import { + PMTiles, + ResolvedValueCache, + RangeResponse, + Source, +} from "../../../js"; + +// @ts-ignore +import https from "https"; +// @ts-ignore +import s3client from "/var/runtime/node_modules/aws-sdk/clients/s3.js"; + +const keepAliveAgent = new https.Agent({ keepAlive: true }); +const s3 = new s3client({ + region: process.env.BUCKET_REGION!, + httpOptions: { agent: keepAliveAgent }, +}); + +// TODO: figure out how much memory to allocate +const CACHE = new ResolvedValueCache(); + +class S3Source implements Source { + archive_name: string; + + constructor(archive_name: string) { + this.archive_name = archive_name; + } + + getKey() { + return ""; + } + + async getBytes(offset: number, length: number): Promise { + const resp = await s3 + .getObject({ + Bucket: process.env.BUCKET!, + Key: this.archive_name + ".pmtiles", + Range: "bytes=" + offset + "-" + (offset + length - 1), + }) + .promise(); + + return { data: resp!.Body.buffer }; + } +} export const handler = async ( event: APIGatewayEvent, context: Context ): Promise => { - const response = { - statusCode: 200, - body: JSON.stringify("Hello from Lambda!"), + try { + const source = new S3Source("stamen_toner_z3"); + const p = new PMTiles(source, CACHE); + + const tile = await p.getZxy(0, 0, 0); + if (tile) { + return { + statusCode: 200, + body: Buffer.from(tile.data).toString("base64"), + }; + } else { + return { + statusCode: 204, + body: "", + }; + } + } catch (e) { + if ((e as Error).name === "AccessDenied") { + return { + statusCode: 403, + body: "Bucket access failed: Unauthorized", + }; + } + throw e; + } + return { + statusCode: 404, + body: "Invalid URL", }; - return response; };