From caf8005e318cab2323c9c65cef744411c2240231 Mon Sep 17 00:00:00 2001 From: Shivam Ross Date: Wed, 19 Mar 2025 23:31:33 +0530 Subject: [PATCH 1/2] added ip and location tracking and redis also added even distribution to workers --- apps/frontend/.gitignore | 3 + apps/hub/index.ts | 215 ++++++++++++++++++----------------- apps/validator/index.ts | 3 +- apps/validator/package.json | 1 + bun.lock | 46 +++++++- package.json | 1 + packages/cache/.gitignore | 34 ++++++ packages/cache/README.md | 15 +++ packages/cache/package.json | 13 +++ packages/cache/redis.ts | 2 + packages/cache/tsconfig.json | 27 +++++ packages/db/package.json | 1 + 12 files changed, 252 insertions(+), 109 deletions(-) create mode 100644 packages/cache/.gitignore create mode 100644 packages/cache/README.md create mode 100644 packages/cache/package.json create mode 100644 packages/cache/redis.ts create mode 100644 packages/cache/tsconfig.json diff --git a/apps/frontend/.gitignore b/apps/frontend/.gitignore index 5ef6a52..e2764f9 100644 --- a/apps/frontend/.gitignore +++ b/apps/frontend/.gitignore @@ -39,3 +39,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# clerk configuration (can include secrets) +/.clerk/ diff --git a/apps/hub/index.ts b/apps/hub/index.ts index e8fac06..fac8fc7 100644 --- a/apps/hub/index.ts +++ b/apps/hub/index.ts @@ -4,26 +4,24 @@ import { prismaClient } from "db/client"; import { PublicKey } from "@solana/web3.js"; import nacl from "tweetnacl"; import nacl_util from "tweetnacl-util"; +import { redis } from "cache/redis"; -const availableValidators: { validatorId: string, socket: ServerWebSocket, publicKey: string }[] = []; - -const CALLBACKS : { [callbackId: string]: (data: IncomingMessage) => void } = {} +const CALLBACKS: { [callbackId: string]: (data: IncomingMessage) => void } = {}; const COST_PER_VALIDATION = 100; // in lamports -Bun.serve({ +const server = Bun.serve({ fetch(req, server) { - if (server.upgrade(req)) { - return; - } - return new Response("Upgrade failed", { status: 500 }); + if (server.upgrade(req)) { + return; + } + return new Response("Upgrade failed", { status: 500 }); }, port: 8081, websocket: { async message(ws: ServerWebSocket, message: string) { const data: IncomingMessage = JSON.parse(message); - - if (data.type === 'signup') { + if (data.type === "signup") { const verified = await verifyMessage( `Signed message for ${data.data.callbackId}, ${data.data.publicKey}`, data.data.publicKey, @@ -32,127 +30,132 @@ Bun.serve({ if (verified) { await signupHandler(ws, data.data); } - } else if (data.type === 'validate') { + } else if (data.type === "validate" && CALLBACKS[data.data.callbackId]) { CALLBACKS[data.data.callbackId](data); delete CALLBACKS[data.data.callbackId]; } }, async close(ws: ServerWebSocket) { - availableValidators.splice(availableValidators.findIndex(v => v.socket === ws), 1); - } + const validatorId = await redis.hget("wsToValidator", ws.toString()); + if (!validatorId) { + console.log("Disconnected WebSocket not found in Redis."); + return; + } + + console.log(`Removing validator ${validatorId} from active list`); + + await redis.srem("availableValidators", validatorId); + await redis.lrem("validatorQueue", 0, validatorId); + await redis.hdel("wsToValidator", ws.toString()); + await redis.hdel("validatorWs", validatorId); + + const channelName = `validator:${validatorId}`; + ws.unsubscribe(channelName); + + console.log(`Validator ${validatorId} successfully removed.`); + + }, }, }); -async function signupHandler(ws: ServerWebSocket, { ip, publicKey, signedMessage, callbackId }: SignupIncomingMessage) { - const validatorDb = await prismaClient.validator.findFirst({ - where: { - publicKey, - }, - }); +async function getIpLocation(ip: string) { + try { + const res = await fetch(`http://ip-api.com/json/${ip}`); + const data = await res.json(); + return JSON.stringify({ + country: data.country, + city: data.city, + region: data.regionName, + lat: data.lat, + lon: data.lon, + }); + } catch (error) { + console.error("Error fetching IP location:", error); + return "unknown"; + } +} - if (validatorDb) { - ws.send(JSON.stringify({ - type: 'signup', - data: { - validatorId: validatorDb.id, - callbackId, - }, - })); - availableValidators.push({ - validatorId: validatorDb.id, - socket: ws, - publicKey: validatorDb.publicKey, +async function signupHandler(ws: ServerWebSocket, { publicKey, callbackId }: SignupIncomingMessage) { + let validator = await prismaClient.validator.findFirst({ + where: { publicKey }, + }); + + if (!validator) { + const ip = ws?.remoteAddress || "unknown"; + const location = await getIpLocation(ip) + validator = await prismaClient.validator.create({ + data: { ip, publicKey, location }, }); - return; } - - //TODO: Given the ip, return the location - const validator = await prismaClient.validator.create({ - data: { - ip, - publicKey, - location: 'unknown', - }, - }); ws.send(JSON.stringify({ - type: 'signup', - data: { - validatorId: validator.id, - callbackId, - }, + type: "signup", + data: { validatorId: validator.id, callbackId }, })); - availableValidators.push({ - validatorId: validator.id, - socket: ws, - publicKey: validator.publicKey, - }); + await redis.sadd("availableValidators", validator.id); + await redis.lpush("validatorQueue", validator.id); + await redis.hset("wsToValidator", ws.toString(), validator.id); + const channelName = `validator:${validator.id}`; + ws.subscribe(channelName); + await redis.hset("validatorWs", validator.id, ws.toString()); } async function verifyMessage(message: string, publicKey: string, signature: string) { const messageBytes = nacl_util.decodeUTF8(message); - const result = nacl.sign.detached.verify( - messageBytes, - new Uint8Array(JSON.parse(signature)), - new PublicKey(publicKey).toBytes(), - ); - - return result; + const signatureBytes = new Uint8Array(JSON.parse(signature)); + return nacl.sign.detached.verify(messageBytes, signatureBytes, new PublicKey(publicKey).toBuffer()); } setInterval(async () => { const websitesToMonitor = await prismaClient.website.findMany({ - where: { - disabled: false, - }, + where: { disabled: false }, }); - for (const website of websitesToMonitor) { - availableValidators.forEach(validator => { - const callbackId = randomUUIDv7(); - console.log(`Sending validate to ${validator.validatorId} ${website.url}`); - validator.socket.send(JSON.stringify({ - type: 'validate', - data: { - url: website.url, - callbackId - }, - })); - - CALLBACKS[callbackId] = async (data: IncomingMessage) => { - if (data.type === 'validate') { - const { validatorId, status, latency, signedMessage } = data.data; - const verified = await verifyMessage( - `Replying to ${callbackId}`, - validator.publicKey, - signedMessage - ); - if (!verified) { - return; - } - - await prismaClient.$transaction(async (tx) => { - await tx.websiteTick.create({ - data: { - websiteId: website.id, - validatorId, - status, - latency, - createdAt: new Date(), - }, - }); - - await tx.validator.update({ - where: { id: validatorId }, - data: { - pendingPayouts: { increment: COST_PER_VALIDATION }, - }, - }); + const validationQueue = websitesToMonitor.map(website => ({ + websiteId: website.id, + url: website.url, + })); + + while (validationQueue.length > 0) { + const validationRequest = validationQueue.shift(); + if (!validationRequest) continue; + + const validatorId = await redis.rpoplpush("validatorQueue", "validatorQueue"); + if (!validatorId) continue; + + const validatorWsString = await redis.hget("validatorWs", validatorId); + if (!validatorWsString) continue; + + const validator = await prismaClient.validator.findUnique({ where: { id: validatorId } }); + if (!validator) continue; + + const callbackId = randomUUIDv7(); + console.log(`Sending validation request to ${validatorId}: ${validationRequest.url}`); + + server.publish(`validator:${validatorId}`, JSON.stringify({ + type: "validate", + data: { url: validationRequest.url, callbackId }, + })); + + CALLBACKS[callbackId] = async (data: IncomingMessage) => { + if (data.type === "validate") { + const { status, latency, signedMessage } = data.data; + const verified = await verifyMessage(`Replying to ${callbackId}`, validator.publicKey, signedMessage); + if (!verified) return; + + await prismaClient.$transaction(async tx => { + await tx.websiteTick.create({ + data: { websiteId: validationRequest.websiteId, validatorId, status, latency, createdAt: new Date() }, }); - } - }; - }); + + await tx.validator.update({ + where: { id: validatorId }, + data: { pendingPayouts: { increment: COST_PER_VALIDATION } }, + }); + }); + } + }; } -}, 60 * 1000); \ No newline at end of file +}, 60 * 10); diff --git a/apps/validator/index.ts b/apps/validator/index.ts index 1736c7e..98a86b5 100644 --- a/apps/validator/index.ts +++ b/apps/validator/index.ts @@ -3,6 +3,7 @@ import type { OutgoingMessage, SignupOutgoingMessage, ValidateOutgoingMessage } import { Keypair } from "@solana/web3.js"; import nacl from "tweetnacl"; import nacl_util from "tweetnacl-util"; +import bs58 from 'bs58' const CALLBACKS: {[callbackId: string]: (data: SignupOutgoingMessage) => void} = {} @@ -10,7 +11,7 @@ let validatorId: string | null = null; async function main() { const keypair = Keypair.fromSecretKey( - Uint8Array.from(JSON.parse(process.env.PRIVATE_KEY!)) + bs58.decode(process.env.PRIVATE_KEY!) ); const ws = new WebSocket("ws://localhost:8081"); diff --git a/apps/validator/package.json b/apps/validator/package.json index 8b0102e..73b0af5 100644 --- a/apps/validator/package.json +++ b/apps/validator/package.json @@ -12,6 +12,7 @@ "dependencies": { "@solana/kit": "^2.1.0", "@solana/web3.js": "^1.98.0", + "bs58": "^6.0.0", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1" } diff --git a/bun.lock b/bun.lock index 1b01488..b778bd7 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,7 @@ "": { "name": "dpin-uptime", "dependencies": { + "ioredis": "^5.6.0", "prisma": "^6.5.0", }, "devDependencies": { @@ -82,6 +83,7 @@ "dependencies": { "@solana/kit": "^2.1.0", "@solana/web3.js": "^1.98.0", + "bs58": "^6.0.0", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", }, @@ -93,6 +95,15 @@ "typescript": "^5.0.0", }, }, + "packages/cache": { + "name": "cache", + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, "packages/common": { "name": "common", "devDependencies": { @@ -105,6 +116,7 @@ "packages/db": { "name": "db", "dependencies": { + "@prisma/client": "6.5.0", "prisma": "^6.5.0", }, "devDependencies": { @@ -289,6 +301,8 @@ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "@ioredis/commands": ["@ioredis/commands@1.2.0", "", {}, "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="], + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], @@ -327,6 +341,8 @@ "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + "@prisma/client": ["@prisma/client@6.5.0", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw=="], + "@prisma/config": ["@prisma/config@6.5.0", "", { "dependencies": { "esbuild": ">=0.12 <1", "esbuild-register": "3.6.0" } }, "sha512-sOH/2Go9Zer67DNFLZk6pYOHj+rumSb0VILgltkoxOjYnlLqUpHPAN826vnx8HigqnOCxj9LRhT6U7uLiIIWgw=="], "@prisma/debug": ["@prisma/debug@6.5.0", "", {}, "sha512-fc/nusYBlJMzDmDepdUtH9aBsJrda2JNErP9AzuHbgUEQY0/9zQYZdNlXmKoIWENtio+qarPNe/+DQtrX5kMcQ=="], @@ -613,7 +629,7 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], + "base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -635,7 +651,7 @@ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + "bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], @@ -649,6 +665,8 @@ "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cache": ["cache@workspace:packages/cache"], + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -683,6 +701,8 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], + "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -751,6 +771,8 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], @@ -1003,6 +1025,8 @@ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + "ioredis": ["ioredis@5.6.0", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-tBZlIIWbndeWBWCXWZiqtOF/yxf6yZX3tAlTJ7nfo5jhd6dctNxF7QnYlZLZ1a0o0pDoen7CgZqO+zjNaFbJAg=="], + "ip-address": ["ip-address@9.0.5", "", { "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" } }, "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -1155,10 +1179,14 @@ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="], + "lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="], "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], + "lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="], + "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], @@ -1351,6 +1379,10 @@ "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="], + + "redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="], + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], @@ -1449,6 +1481,8 @@ "stable-hash": ["stable-hash@0.0.4", "", {}, "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g=="], + "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="], + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], "std-env": ["std-env@3.8.1", "", {}, "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA=="], @@ -1647,6 +1681,8 @@ "@solana/rpc-transport-http/undici-types": ["undici-types@7.5.0", "", {}, "sha512-CxNFga24pkqrtk9aO4jV78tWXLZhVVU9J2/EAhBGwqJ1+tsLydMI2Vaq7wj3ba/SZL7BL8aq5rflf75DhbgkhA=="], + "@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + "@swc/helpers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@turbo/gen/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -1669,6 +1705,8 @@ "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "borsh/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-import-resolver-node/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], @@ -1733,6 +1771,8 @@ "tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + "@solana/web3.js/bs58/base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], + "@turbo/gen/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "@turbo/workspaces/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -1745,6 +1785,8 @@ "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "borsh/bs58/base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], + "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], diff --git a/package.json b/package.json index 17d11e2..fe347f7 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "packages/*" ], "dependencies": { + "ioredis": "^5.6.0", "prisma": "^6.5.0" } } diff --git a/packages/cache/.gitignore b/packages/cache/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/packages/cache/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/packages/cache/README.md b/packages/cache/README.md new file mode 100644 index 0000000..b3d75fa --- /dev/null +++ b/packages/cache/README.md @@ -0,0 +1,15 @@ +# cache + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run +``` + +This project was created using `bun init` in bun v1.2.4. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/packages/cache/package.json b/packages/cache/package.json new file mode 100644 index 0000000..aae1da3 --- /dev/null +++ b/packages/cache/package.json @@ -0,0 +1,13 @@ +{ + "name": "cache", + "private": true, + "exports": { + "./redis": "./redis.ts" + }, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + } +} \ No newline at end of file diff --git a/packages/cache/redis.ts b/packages/cache/redis.ts new file mode 100644 index 0000000..a2b6c3c --- /dev/null +++ b/packages/cache/redis.ts @@ -0,0 +1,2 @@ +import Redis from "ioredis"; +export const redis = new Redis(process.env.REDIS_URL || "redis://localhost:6379"); diff --git a/packages/cache/tsconfig.json b/packages/cache/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/packages/cache/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/packages/db/package.json b/packages/db/package.json index 833e905..9d56099 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -12,6 +12,7 @@ "typescript": "^5.0.0" }, "dependencies": { + "@prisma/client": "6.5.0", "prisma": "^6.5.0" }, "prisma": { From 7913afa3a1a7ad80a42f4573d16a08080ad1c48d Mon Sep 17 00:00:00 2001 From: Shivam Ross <144504100+shivam-ross@users.noreply.github.com> Date: Wed, 19 Mar 2025 23:46:38 +0530 Subject: [PATCH 2/2] Update index.ts --- apps/hub/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/hub/index.ts b/apps/hub/index.ts index fac8fc7..9853bf0 100644 --- a/apps/hub/index.ts +++ b/apps/hub/index.ts @@ -158,4 +158,4 @@ setInterval(async () => { } }; } -}, 60 * 10); +}, 60 * 1000);