From 441854a65abdb6b60c883679f6d5a8d4b16559ff Mon Sep 17 00:00:00 2001 From: Chewing Glass Date: Fri, 6 Oct 2023 12:30:53 -0400 Subject: [PATCH] feat(#441): Upload entities to s3 for faster api --- packages/entity-postgres-s3-sink/.gitignore | 4 + packages/entity-postgres-s3-sink/CHANGELOG.md | 80 ++++++ packages/entity-postgres-s3-sink/Dockerfile | 30 +++ packages/entity-postgres-s3-sink/package.json | 68 +++++ .../program_account_configs_example.json | 121 +++++++++ packages/entity-postgres-s3-sink/src/env.ts | 17 ++ packages/entity-postgres-s3-sink/src/index.ts | 247 ++++++++++++++++++ packages/entity-postgres-s3-sink/src/model.ts | 155 +++++++++++ .../tsconfig.build.json | 27 ++ .../entity-postgres-s3-sink/tsconfig.cjs.json | 7 + .../entity-postgres-s3-sink/tsconfig.esm.json | 8 + .../entity-postgres-s3-sink/tsconfig.json | 26 ++ packages/metadata-service/src/index.ts | 4 +- tsconfig.json | 2 + 14 files changed, 794 insertions(+), 2 deletions(-) create mode 100644 packages/entity-postgres-s3-sink/.gitignore create mode 100644 packages/entity-postgres-s3-sink/CHANGELOG.md create mode 100644 packages/entity-postgres-s3-sink/Dockerfile create mode 100644 packages/entity-postgres-s3-sink/package.json create mode 100644 packages/entity-postgres-s3-sink/program_account_configs_example.json create mode 100644 packages/entity-postgres-s3-sink/src/env.ts create mode 100644 packages/entity-postgres-s3-sink/src/index.ts create mode 100644 packages/entity-postgres-s3-sink/src/model.ts create mode 100644 packages/entity-postgres-s3-sink/tsconfig.build.json create mode 100644 packages/entity-postgres-s3-sink/tsconfig.cjs.json create mode 100644 packages/entity-postgres-s3-sink/tsconfig.esm.json create mode 100644 packages/entity-postgres-s3-sink/tsconfig.json diff --git a/packages/entity-postgres-s3-sink/.gitignore b/packages/entity-postgres-s3-sink/.gitignore new file mode 100644 index 000000000..445f244bc --- /dev/null +++ b/packages/entity-postgres-s3-sink/.gitignore @@ -0,0 +1,4 @@ +npm-debug.log +dist/ +tmp/ +./node_modules \ No newline at end of file diff --git a/packages/entity-postgres-s3-sink/CHANGELOG.md b/packages/entity-postgres-s3-sink/CHANGELOG.md new file mode 100644 index 000000000..d633e8aa5 --- /dev/null +++ b/packages/entity-postgres-s3-sink/CHANGELOG.md @@ -0,0 +1,80 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/helium/helium-program-libary/compare/v0.2.21...v0.4.0) (2023-09-22) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +## [0.3.2](https://github.com/helium/helium-program-libary/compare/v0.2.21...v0.3.2) (2023-09-16) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +## [0.3.1](https://github.com/helium/helium-program-libary/compare/v0.2.21...v0.3.1) (2023-09-15) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +# [0.3.0](https://github.com/helium/helium-program-libary/compare/v0.2.21...v0.3.0) (2023-09-14) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +## [0.2.22](https://github.com/helium/helium-program-libary/compare/v0.2.21...v0.2.22) (2023-09-13) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +## [0.2.15](https://github.com/helium/helium-program-libary/compare/v0.2.14...v0.2.15) (2023-07-31) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +## [0.2.3](https://github.com/helium/helium-program-libary/compare/v0.1.5...v0.2.3) (2023-06-16) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +## [0.2.2](https://github.com/helium/helium-program-libary/compare/v0.1.5...v0.2.2) (2023-06-08) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +## [0.2.1](https://github.com/helium/helium-program-libary/compare/v0.1.5...v0.2.1) (2023-06-08) + +**Note:** Version bump only for package @helium/account-postgres-sink-service + + + + + +# [0.2.0](https://github.com/helium/helium-program-libary/compare/v0.1.5...v0.2.0) (2023-06-06) + +**Note:** Version bump only for package @helium/account-postgres-sink-service diff --git a/packages/entity-postgres-s3-sink/Dockerfile b/packages/entity-postgres-s3-sink/Dockerfile new file mode 100644 index 000000000..d867c1fef --- /dev/null +++ b/packages/entity-postgres-s3-sink/Dockerfile @@ -0,0 +1,30 @@ +# Specify the base image +FROM node:16-alpine AS BUILD_IMAGE + +WORKDIR /usr/src/app + +COPY package.json ./ + +RUN yarn install + +COPY src src +COPY tsconfig.build.json tsconfig.json + +RUN yarn global add typescript +RUN yarn run build + +RUN npm prune --production + +FROM node:16-alpine + +WORKDIR /usr/src/app + +COPY --from=BUILD_IMAGE /usr/src/app/lib ./lib +COPY --from=BUILD_IMAGE /usr/src/app/node_modules ./node_modules + +# This isn't actually used, service is read only. But anchor wants a wallet. +RUN echo "[124,96,181,146,132,165,175,182,60,194,167,230,29,91,110,109,226,38,41,155,207,186,24,33,205,120,108,98,218,67,77,95,13,60,79,204,253,10,183,101,60,94,220,177,117,97,16,29,31,124,35,65,121,147,161,114,159,23,207,202,122,164,170,201]" > id.json + +ENV ANCHOR_WALLET=/usr/src/app/id.json + +CMD ["node", "lib/src/index.js"] \ No newline at end of file diff --git a/packages/entity-postgres-s3-sink/package.json b/packages/entity-postgres-s3-sink/package.json new file mode 100644 index 000000000..0cfa5f911 --- /dev/null +++ b/packages/entity-postgres-s3-sink/package.json @@ -0,0 +1,68 @@ +{ + "name": "@helium/entity-postgres-s3-sink", + "private": true, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "license": "Apache-2.0", + "version": "0.4.0", + "description": "Sync account data to postgres", + "repository": { + "type": "git", + "url": "https://github.com/helium/helium-program-libary" + }, + "main": "./lib/cjs/index.js", + "module": "./lib/esm/src/index.js", + "types": "./lib/types/src/index.d.ts", + "sideEffects": false, + "files": [ + "lib" + ], + "exports": { + "import": "./lib/esm/src/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/types/src/index.d.ts" + }, + "scripts": { + "format": "prettier --write \"src/**/*.{ts,tsx}\"", + "precommit": "npx git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath \"{}\"' .", + "build": "tsc -p tsconfig.json", + "start": "node lib/esm/server.js", + "dev": "npx ts-node --project tsconfig.cjs.json src/index.ts" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.28.0", + "@fastify/cors": "^8.1.1", + "@helium/address": "^4.10.2", + "@helium/account-fetch-cache": "^0.4.0", + "@metaplex-foundation/mpl-token-metadata": "^2.10.0", + "@solana/web3.js": "^1.78.4", + "aws-sdk": "^2.1344.0", + "axios": "^1.3.6", + "axios-retry": "^3.8.0", + "bn.js": "^5.2.0", + "bs58": "^4.0.1", + "h3-js": "^4.1.0", + "p-limit": "3.1.0", + "pg": "^8.9.0", + "prom-client": "^14.2.0", + "sequelize": "^6.28.0", + "yargs": "^17.7.1" + }, + "devDependencies": { + "@types/bn.js": "^5.1.1", + "@types/deep-equal": "^1.0.1", + "@types/node": "^18.11.11", + "@types/pg": "^8.6.6", + "@types/yargs": "^17.0.24", + "git-format-staged": "^2.1.3", + "ts-loader": "^9.2.3", + "ts-node": "^10.9.1", + "ts-node-dev": "^2.0.0", + "typescript": "^4.8.4", + "yarn": "^1.22.18" + }, + "keywords": [], + "author": "" +} diff --git a/packages/entity-postgres-s3-sink/program_account_configs_example.json b/packages/entity-postgres-s3-sink/program_account_configs_example.json new file mode 100644 index 000000000..f84671ad4 --- /dev/null +++ b/packages/entity-postgres-s3-sink/program_account_configs_example.json @@ -0,0 +1,121 @@ +{ + "configs": [ + { + "programId": "1atrmQs3eq1N2FEYWu6tyTXbCjP4uQwExpjtnhXtS8h", + "accounts": [ + { + "type": "LazyTransactionsV0", + "table": "lazy_transactions", + "schema": "public" + }, + { "type": "Block", "table": "blocks", "schema": "public" } + ] + }, + { + "programId": "hvsrNC3NKbcryqDs2DocYHZ9yPKEVzdSjQG6RVtK1s8", + "accounts": [ + { "type": "PositionV0", "table": "positions", "schema": "public" }, + { "type": "Registrar", "table": "registrars", "schema": "public" } + ], + "crons": [ + { "schedule": "*/15 * * * *", "type": "refresh-accounts" }, + { "schedule": "*/15 * * * *", "type": "integrity-check" } + ] + }, + { + "programId": "hdaoVTCqhfHHo75XdAMxBKdUqvq1i5bF23sisBqVgGR", + "accounts": [ + { "type": "DaoV0", "table": "daos", "schema": "public" }, + { "type": "SubDaoV0", "table": "sub_daos", "schema": "public" }, + { + "type": "SubDaoEpochInfoV0", + "table": "sub_dao_epoch_infos", + "schema": "public" + }, + { + "type": "DaoEpochInfoV0", + "table": "dao_epoch_infos", + "schema": "public" + }, + { + "type": "DelegatedPositionV0", + "table": "delegated_positions", + "schema": "public" + } + ] + }, + { + "programId": "circAbx64bbsscPbQzZAUvuXpHqrCe6fLMzc2uKXz9g", + "accounts": [ + { + "type": "MintWindowedCircuitBreakerV0", + "table": "mint_windowed_circuit_breaker_configs", + "schema": "public" + }, + { + "type": "AccountWindowedCircuitBreakerV0", + "table": "account_windowed_circuit_breaker_configs", + "schema": "public" + } + ] + }, + { + "programId": "hemjuPXBpNvggtaUnN1MwT3wrdhttKEfosTcc2P9Pg8", + "accounts": [ + { + "type": "RewardableEntityConfigV0", + "table": "rewardable_entity_configs", + "schema": "public" + }, + { "type": "MakerV0", "table": "makers", "schema": "public" }, + { + "type": "MakerApprovalV0", + "table": "maker_approvals", + "schema": "public" + }, + { + "type": "KeyToAssetV0", + "table": "key_to_assets", + "schema": "public" + }, + { + "type": "IotHotspotInfoV0", + "table": "iot_hotspot_infos", + "schema": "public", + "plugins": [ + { + "type": "ExtractHexLocation", + "config": { + "field": "location" + } + } + ] + }, + { + "type": "MobileHotspotInfoV0", + "table": "mobile_hotspot_infos", + "schema": "public", + "plugins": [ + { + "type": "ExtractHexLocation", + "config": { + "field": "location" + } + } + ] + } + ] + }, + { + "programId": "1azyuavdMyvsivtNxPoz6SucD18eDHeXzFCUPq5XU7w", + "accounts": [ + { + "type": "LazyDistributorV0", + "table": "lazy_distributors", + "schema": "public" + }, + { "type": "RecipientV0", "table": "recipients", "schema": "public" } + ] + } + ] +} diff --git a/packages/entity-postgres-s3-sink/src/env.ts b/packages/entity-postgres-s3-sink/src/env.ts new file mode 100644 index 000000000..cf8aa00d5 --- /dev/null +++ b/packages/entity-postgres-s3-sink/src/env.ts @@ -0,0 +1,17 @@ +export const LOOKBACK_HOURS = process.env.LOOKBACK_HOURS + ? Number(process.env.LOOKBACK_HOURS) + : 25; +export const AWS_REGION = process.env.AWS_REGION; +export const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT; +export const MINIO_ACCESS_KEY = process.env.MINIO_ACCESS_KEY; +export const MINIO_SECRET_KEY = process.env.MINIO_SECRET_KEY; +export const S3_BUCKET = process.env.S3_BUCKET!; +export const CLOUDFRONT_DISTRIBUTION = process.env.CLOUDFRONT_DISTRIBUTION; + +// Check for required environment variables +const requiredEnvVars = ["AWS_REGION", "S3_BUCKET"]; +for (const varName of requiredEnvVars) { + if (!process.env[varName]) { + throw new Error(`Environment variable ${varName} is required`); + } +} diff --git a/packages/entity-postgres-s3-sink/src/index.ts b/packages/entity-postgres-s3-sink/src/index.ts new file mode 100644 index 000000000..6674d3b73 --- /dev/null +++ b/packages/entity-postgres-s3-sink/src/index.ts @@ -0,0 +1,247 @@ +import { decodeEntityKey } from "@helium/helium-entity-manager-sdk"; +import AWS from "aws-sdk"; +import { Op } from "sequelize"; +import { IotHotspotInfo, KeyToAsset, MobileHotspotInfo } from "./model"; +// @ts-ignore +import Address from "@helium/address/build/Address"; +import { truthy } from "@helium/spl-utils"; +import animalHash from "angry-purple-tiger"; +import axios from "axios"; +import pLimit from "p-limit"; +import { + LOOKBACK_HOURS, + AWS_REGION, + MINIO_ACCESS_KEY, + MINIO_ENDPOINT, + MINIO_SECRET_KEY, + S3_BUCKET, + CLOUDFRONT_DISTRIBUTION, +} from "./env"; + +async function run() { + const date = new Date(); + date.setHours(date.getHours() - LOOKBACK_HOURS); + AWS.config.update({ region: AWS_REGION }); + // Additional configuration for Minio + let s3; + if (MINIO_ENDPOINT && MINIO_ACCESS_KEY && MINIO_SECRET_KEY) { + // Create a new S3 instance with specific configuration for Minio + s3 = new AWS.S3({ + endpoint: MINIO_ENDPOINT, + s3ForcePathStyle: true, + accessKeyId: MINIO_ACCESS_KEY, + secretAccessKey: MINIO_SECRET_KEY, + }); + } else { + // Create a new S3 instance with default configuration + s3 = new AWS.S3(); + } + + const cloudfront = new AWS.CloudFront(); + const bucket = S3_BUCKET; + + const limit = 10000; + let lastId = null; + let entities; + const promiseLimit = pLimit(50); + + const lastIdWhere = {}; + const whereClause = { + [Op.or]: [ + { + refreshedAt: { + [Op.gt]: date, + }, + }, + { + "$iot_hotspot_info.refreshed_at$": { + [Op.gt]: date, + }, + }, + { + "$mobile_hotspot_info.refreshed_at$": { + [Op.gt]: date, + }, + }, + ], + }; + const totalCount = await KeyToAsset.count({ + where: whereClause, + include: [ + { + model: IotHotspotInfo, + required: false, + }, + { + model: MobileHotspotInfo, + required: false, + }, + ], + }); + console.log(`Found ${totalCount} updated records`); + let totalProgress = 0; + + do { + if (lastId) { + lastIdWhere["address"] = { + [Op.gt]: lastId, + }; + } + entities = await KeyToAsset.findAll({ + where: { + [Op.and]: [lastIdWhere, whereClause], + }, + include: [ + { + model: IotHotspotInfo, + required: false, + }, + { + model: MobileHotspotInfo, + required: false, + }, + ], + limit: limit, + order: [["address", "ASC"]], + }); + + if (entities.length) { + entities.forEach((entity) => { + entity.entityKeyStr = decodeEntityKey(entity.entityKey, { + [entity.keySerialization.toString()]: {}, + }); + }); + await Promise.all( + entities.map((entity) => { + return promiseLimit(async () => { + await uploadToS3({ + s3, + bucket, + entity, + }); + }); + }) + ); + + if (CLOUDFRONT_DISTRIBUTION) { + const paths = entities.flatMap((entity) => getPaths(entity)); + await cloudfront + .createInvalidation({ + DistributionId: CLOUDFRONT_DISTRIBUTION, + InvalidationBatch: { + CallerReference: `${new Date().getTime()}`, // unique identifier for this invalidation batch + Paths: { + Quantity: paths.length, + Items: paths, + }, + }, + }) + .promise(); + } + + lastId = entities[entities.length - 1].id; + totalProgress += entities.length; + console.log(`Processed ${totalProgress} / ${totalCount}`); + } + } while (entities.length === limit); +} + +function getPaths(entity: KeyToAsset): string[] { + const v1 = `v1/${entity.address}`; + if ((entity.entityKeyStr?.length || 0) >= 200) { + return [v1]; + } + + return [entity.entityKeyStr!, v1]; +} + +async function uploadToS3({ + s3, + entity, + bucket, +}: { + s3: AWS.S3; + entity: KeyToAsset; + bucket: string; +}): Promise { + const keyStr = entity.entityKeyStr; + const digest = animalHash(keyStr); + let json; + if (keyStr?.length === 22) { + try { + const { data } = await axios( + `https://sol.hellohelium.com/api/metadata/${keyStr}` + ); + json = data; + } catch (e: any) { + console.log(`Failed to fetch mobile subscriber ${keyStr}`) + return; + } + } else { + json = { + name: keyStr === "iot_operations_fund" ? "IOT Operations Fund" : digest, + description: + keyStr === "iot_operations_fund" + ? "IOT Operations Fund" + : "A Rewardable NFT on Helium", + // HACK: If it has a long key, it's an RSA key, and this is a mobile hotspot. + // In the future, we need to put different symbols on different types of hotspots + image: + entity.entityKey.length > 100 + ? "https://shdw-drive.genesysgo.net/6tcnBSybPG7piEDShBcrVtYJDPSvGrDbVvXmXKpzBvWP/mobile-hotspot.png" + : "https://shdw-drive.genesysgo.net/6tcnBSybPG7piEDShBcrVtYJDPSvGrDbVvXmXKpzBvWP/hotspot.png", + attributes: [ + keyStr && Address.isValid(keyStr) + ? { trait_type: "ecc_compact", value: keyStr } + : undefined, + { trait_type: "entity_key_string", value: keyStr }, + { trait_type: "rewardable", value: true }, + { + trait_type: "networks", + value: [ + entity?.iot_hotspot_info && "iot", + entity?.mobile_hotspot_info && "mobile", + ].filter(truthy), + }, + ...locationAttributes("iot", entity?.iot_hotspot_info), + ...locationAttributes("mobile", entity?.mobile_hotspot_info), + ], + entity_key_string: keyStr, + entity_key_b64: entity.entityKey.toString("base64"), + key_to_asset_key: entity.address, + iot_hotspot_info: entity.iot_hotspot_info?.dataValues, + mobile_hotspot_info: entity.mobile_hotspot_info?.dataValues, + }; + } + + for (const path of getPaths(entity)) { + await s3 + .upload({ + Bucket: bucket, + Key: path, + Body: JSON.stringify(json), + ContentType: "application/json", + }) + .promise(); + } +} + +function locationAttributes( + name: string, + info: MobileHotspotInfo | IotHotspotInfo | undefined +) { + if (!info) { + return []; + } + + return [ + { trait_type: `${name}_city`, value: info.city }, + { trait_type: `${name}_state`, value: info.state }, + { trait_type: `${name}_country`, value: info.country }, + ]; +} + +run().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/packages/entity-postgres-s3-sink/src/model.ts b/packages/entity-postgres-s3-sink/src/model.ts new file mode 100644 index 000000000..d72b9ec71 --- /dev/null +++ b/packages/entity-postgres-s3-sink/src/model.ts @@ -0,0 +1,155 @@ +import { Sequelize, STRING, Model, DataTypes } from "sequelize"; +import AWS from "aws-sdk"; +import * as pg from "pg"; + +const host = process.env.PGHOST || "localhost"; +const port = Number(process.env.PGPORT) || 5432; +export const sequelize = new Sequelize({ + host: host, + dialect: "postgres", + port: port, + logging: false, + dialectModule: pg, + username: process.env.PGUSER, + database: process.env.PGDATABASE, + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000, + }, + hooks: { + beforeConnect: async (config: any) => { + const isRds = host.includes("rds.amazonaws.com"); + + let password = process.env.PGPASSWORD; + if (isRds && !password) { + const signer = new AWS.RDS.Signer({ + region: process.env.AWS_REGION, + hostname: process.env.PGHOST, + port, + username: process.env.PGUSER, + }); + password = await new Promise((resolve, reject) => + signer.getAuthToken({}, (err, token) => { + if (err) { + return reject(err); + } + resolve(token); + }) + ); + config.dialectOptions = { + ssl: { + require: false, + rejectUnauthorized: false, + }, + }; + } + config.password = password; + }, + }, +}); + +export class MobileHotspotInfo extends Model { + declare address: string; + declare asset: string; + declare city: string; + declare state: string; + declare country: string; +} +MobileHotspotInfo.init( + { + address: { + type: STRING, + primaryKey: true, + }, + city: DataTypes.STRING, + state: DataTypes.STRING, + country: DataTypes.STRING, + asset: DataTypes.STRING, + refreshedAt: DataTypes.TIME, + isFullHotspot: DataTypes.BOOLEAN, + numLocationAsserts: DataTypes.NUMBER, + isActive: DataTypes.BOOLEAN, + dcOnboardingFeePaid: DataTypes.DECIMAL.UNSIGNED, + deviceType: DataTypes.JSONB, + }, + { + sequelize, + modelName: "mobile_hotspot_infos", + tableName: "mobile_hotspot_infos", + underscored: true, + timestamps: false, + } +); + +export class IotHotspotInfo extends Model { + declare address: string; + declare asset: string; + declare city: string; + declare state: string; + declare country: string; +} +IotHotspotInfo.init( + { + address: { + type: STRING, + primaryKey: true, + }, + asset: DataTypes.STRING, + city: DataTypes.STRING, + state: DataTypes.STRING, + country: DataTypes.STRING, + location: DataTypes.DECIMAL.UNSIGNED, + isFullHotspot: DataTypes.BOOLEAN, + numLocationAsserts: DataTypes.NUMBER, + isActive: DataTypes.BOOLEAN, + dcOnboardingFeePaid: DataTypes.DECIMAL.UNSIGNED, + refreshedAt: DataTypes.TIME, + }, + { + sequelize, + modelName: "iot_hotspot_infos", + tableName: "iot_hotspot_infos", + underscored: true, + timestamps: false, + } +); + +export class KeyToAsset extends Model { + declare address: string; + declare asset: string; + declare entityKey: Buffer; + declare entityKeyStr?: string; + declare keySerialization: any; + declare mobile_hotspot_info?: MobileHotspotInfo; + declare iot_hotspot_info?: IotHotspotInfo; +} +KeyToAsset.init( + { + address: { + type: STRING, + primaryKey: true, + }, + entityKey: DataTypes.BLOB, + keySerialization: DataTypes.JSONB, + asset: DataTypes.STRING, + refreshedAt: DataTypes.TIME, + }, + { + sequelize, + modelName: "key_to_assets", + tableName: "key_to_assets", + underscored: true, + timestamps: false, + } +); + +KeyToAsset.hasOne(IotHotspotInfo, { + sourceKey: "asset", + foreignKey: "asset", +}); +KeyToAsset.hasOne(MobileHotspotInfo, { + sourceKey: "asset", + foreignKey: "asset", +}); diff --git a/packages/entity-postgres-s3-sink/tsconfig.build.json b/packages/entity-postgres-s3-sink/tsconfig.build.json new file mode 100644 index 000000000..43c1febce --- /dev/null +++ b/packages/entity-postgres-s3-sink/tsconfig.build.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2016", + "module": "CommonJS", + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "composite": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": false, + "moduleResolution": "Node", + "skipLibCheck": true, + "baseUrl": ".", + "typeRoots": ["node_modules/@types"], + "strict": true, + "paths": { + "@/*": ["*"] + }, + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/entity-postgres-s3-sink/tsconfig.cjs.json b/packages/entity-postgres-s3-sink/tsconfig.cjs.json new file mode 100644 index 000000000..5445b9909 --- /dev/null +++ b/packages/entity-postgres-s3-sink/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.cjs.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/cjs" + } +} diff --git a/packages/entity-postgres-s3-sink/tsconfig.esm.json b/packages/entity-postgres-s3-sink/tsconfig.esm.json new file mode 100644 index 000000000..4b7ba456e --- /dev/null +++ b/packages/entity-postgres-s3-sink/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.esm.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/esm", + "declarationDir": "lib/types" + } +} diff --git a/packages/entity-postgres-s3-sink/tsconfig.json b/packages/entity-postgres-s3-sink/tsconfig.json new file mode 100644 index 000000000..355a52175 --- /dev/null +++ b/packages/entity-postgres-s3-sink/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.root.json", + "references": [ + { + "path": "../account-fetch-cache" + }, + { + "path": "../idls" + }, + { + "path": "../voter-stake-registry-sdk" + }, + { + "path": "../helium-sub-daos-sdk" + }, + { + "path": "../spl-utils" + }, + { + "path": "./tsconfig.cjs.json" + }, + { + "path": "./tsconfig.esm.json" + } + ] +} diff --git a/packages/metadata-service/src/index.ts b/packages/metadata-service/src/index.ts index 636e4b4f1..454234472 100644 --- a/packages/metadata-service/src/index.ts +++ b/packages/metadata-service/src/index.ts @@ -83,7 +83,7 @@ server.get<{ Params: { keyToAssetKey: string } }>( ].filter(truthy), }, ...locationAttributes("iot", record?.iot_hotspot_info), - ...locationAttributes("iot", record?.mobile_hotspot_info), + ...locationAttributes("mobile", record?.mobile_hotspot_info), ], }; } @@ -131,7 +131,7 @@ server.get<{ Params: { eccCompact: string } }>( ].filter(truthy), }, ...locationAttributes("iot", record?.iot_hotspot_info), - ...locationAttributes("iot", record?.mobile_hotspot_info), + ...locationAttributes("mobile", record?.mobile_hotspot_info), ] }; } diff --git a/tsconfig.json b/tsconfig.json index ac726ae8b..56cca60bc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -81,6 +81,8 @@ }, { "path": "./packages/crons" + }, { + "path": "./packages/entity-postgres-s3-sink" } ] }