diff --git a/apps/api/index.ts b/apps/api/index.ts deleted file mode 100644 index e791ed7..0000000 --- a/apps/api/index.ts +++ /dev/null @@ -1,90 +0,0 @@ -import express from "express" -import { authMiddleware } from "./middleware"; -import { prismaClient } from "db/client"; -import cors from "cors"; -import { Transaction, SystemProgram, Connection } from "@solana/web3.js"; - - -const connection = new Connection("https://api.mainnet-beta.solana.com"); -const app = express(); - -app.use(cors()); -app.use(express.json()); - -app.post("/api/v1/website", authMiddleware, async (req, res) => { - const userId = req.userId!; - const { url } = req.body; - - const data = await prismaClient.website.create({ - data: { - userId, - url - } - }) - - res.json({ - id: data.id - }) -}) - -app.get("/api/v1/website/status", authMiddleware, async (req, res) => { - const websiteId = req.query.websiteId! as unknown as string; - const userId = req.userId; - - const data = await prismaClient.website.findFirst({ - where: { - id: websiteId, - userId, - disabled: false - }, - include: { - ticks: true - } - }) - - res.json(data) - -}) - -app.get("/api/v1/websites", authMiddleware, async (req, res) => { - const userId = req.userId!; - - const websites = await prismaClient.website.findMany({ - where: { - userId, - disabled: false - }, - include: { - ticks: true - } - }) - - res.json({ - websites - }) -}) - -app.delete("/api/v1/website/", authMiddleware, async (req, res) => { - const websiteId = req.body.websiteId; - const userId = req.userId!; - - await prismaClient.website.update({ - where: { - id: websiteId, - userId - }, - data: { - disabled: true - } - }) - - res.json({ - message: "Deleted website successfully" - }) -}) - -app.post("/api/v1/payout/:validatorId", async (req, res) => { - -}) - -app.listen(8080); diff --git a/apps/api/middleware.ts b/apps/api/middleware.ts deleted file mode 100644 index 5217ce0..0000000 --- a/apps/api/middleware.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { NextFunction, Request, Response } from "express"; -import jwt from "jsonwebtoken"; -import { JWT_PUBLIC_KEY } from "./config"; - -export function authMiddleware(req: Request, res: Response, next: NextFunction) { - const token = req.headers['authorization']; - if (!token) { - return res.status(401).json({ error: 'Unauthorized' }); - } - - const decoded = jwt.verify(token, JWT_PUBLIC_KEY); - console.log(decoded); - if (!decoded || !decoded.sub) { - return res.status(401).json({ error: 'Unauthorized' }); - } - - req.userId = decoded.sub as string; - - next() -} \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json index 1901ff9..08470ce 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -3,7 +3,8 @@ "module": "index.ts", "type": "module", "devDependencies": { - "@types/bun": "latest" + "@types/bun": "latest", + "@types/morgan": "^1.9.9" }, "peerDependencies": { "typescript": "^5.0.0" @@ -17,6 +18,8 @@ "db": "*", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", - "jwt": "^0.2.0" + "jwt": "^0.2.0", + "morgan": "^1.10.0", + "zod": "^3.24.2" } } \ No newline at end of file diff --git a/apps/api/config.ts b/apps/api/src/config/config.ts similarity index 100% rename from apps/api/config.ts rename to apps/api/src/config/config.ts diff --git a/apps/api/src/controllers/website.ts b/apps/api/src/controllers/website.ts new file mode 100644 index 0000000..c029d5f --- /dev/null +++ b/apps/api/src/controllers/website.ts @@ -0,0 +1,148 @@ +import { client } from "db/client"; +import type { Request, Response } from "express"; +import { WebsiteDataValidation } from "../validations/website"; + +export const CreateWebsite = async (req: Request, res: Response) => { + try { + const userId = req.userId; + + if (!userId) { + res.status(401).json({ success: false, message: "Unauthorized" }); + return; + } + + const data = WebsiteDataValidation.parse(req.body); + + const { url } = data; + + const checkIfWebsiteExists = await client.website.findFirst({ + where: { + url, + }, + }); + + if (checkIfWebsiteExists) { + res.status(409).json({ + success: false, + message: "Website already exists in our database", + }); + return; + } + + const website = await client.website.create({ + data: { + url, + userId, + }, + }); + + res.status(201).json({ + success: true, + data: website.id, + message: "Website created successfully", + }); + } catch (error) { + console.error("Failed to create website", error); + res.status(500).json({ + success: false, + message: "Internal Server Error", + }); + } +}; + +export const GetAllWebsites = async (req: Request, res: Response) => { + try { + const userId = req.userId; + + if (!userId) { + res.status(401).json({ success: false, message: "Unauthorized" }); + return; + } + + const websites = await client.website.findMany({ + where: { + userId, + disabled: false, + }, + include: { + ticks: true, + }, + }); + + res + .status(200) + .json({ success: true, data: websites, message: "Get all websites" }); + } catch (error) { + console.error("Failed to get websites", error); + res.status(500).json({ + success: false, + message: "Internal Server Error", + }); + } +}; + +export const GetWebsiteStatus = async (req: Request, res: Response) => { + try { + const userId = req.userId; + + if (!userId) { + res.status(401).json({ success: false, message: "Unauthorized" }); + return; + } + + const websiteId = req.query.websiteId! as unknown as string; + + const website = await client.website.findFirst({ + where: { + id: websiteId, + userId, + disabled: false, + }, + include: { + ticks: true, + }, + }); + + res.status(200).json({ + success: true, + data: website, + message: "Website status retrieved successfully", + }); + } catch (error) { + console.error("Failed to delete Website", error); + res.status(500).json({ success: false, message: "Internal server error" }); + } +}; + +export const DeleteWebsite = async (req: Request, res: Response) => { + try { + const userId = req.userId; + + if (!userId) { + res.status(401).json({ success: false, message: "Unauthorized" }); + return; + } + + const websiteId = req.body.websiteId; // not nesecarry to validate + + await client.website.delete({ + where: { + id: websiteId, + userId, + }, + }); + + res.status(200).json({ + success: true, + message: "Website deleted successfully", + }); + } catch (error) { + console.error("Failed to delete Website", error); + res.status(500).json({ success: false, message: "Internal server error" }); + } +}; + +export const Payout = async (req: Request, res: Response) => { + try { + } catch (error) {} +}; diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts new file mode 100644 index 0000000..5fcbb01 --- /dev/null +++ b/apps/api/src/index.ts @@ -0,0 +1,20 @@ +import cors from "cors"; +import morgan from "morgan"; +import express from "express"; +import { websiteRouter } from "./routes/website"; +import { Transaction, SystemProgram, Connection } from "@solana/web3.js"; + +const connection = new Connection("https://api.mainnet-beta.solana.com"); +const app = express(); + +// Middleware +app.use(cors()); +app.use(morgan("dev")); +app.use(express.json()); + +// Routes +app.use("/api/v1/website", websiteRouter); + +app.listen(8080, () => { + console.log(`Api server is running at port 8080...`); +}); diff --git a/apps/api/src/middleware/middleware.ts b/apps/api/src/middleware/middleware.ts new file mode 100644 index 0000000..26663a3 --- /dev/null +++ b/apps/api/src/middleware/middleware.ts @@ -0,0 +1,38 @@ +import jwt from "jsonwebtoken"; +import { JWT_PUBLIC_KEY } from "../config/config"; +import type { NextFunction, Request, Response } from "express"; + +export function authMiddleware( + req: Request, + res: Response, + next: NextFunction +): void { + try { + const token = req.headers["authorization"]; + + if (!token) { + res.status(401).json({ + success: false, + message: "You must be logged in", + error: "Unauthorized", + }); + return; + } + + const decoded = jwt.verify(token, JWT_PUBLIC_KEY); + + if (!decoded || decoded.sub) { + res.status(401).json({ success: false, error: "Unauthorized" }); + return; + } + + req.userId = decoded.sub as string; + next(); + } catch (error) { + res.status(401).json({ + success: false, + message: "Invalid token", + error: "Unauthorized", + }); + } +} diff --git a/apps/api/src/routes/website.ts b/apps/api/src/routes/website.ts new file mode 100644 index 0000000..bb3007f --- /dev/null +++ b/apps/api/src/routes/website.ts @@ -0,0 +1,17 @@ +import express from "express"; +import { authMiddleware } from "../middleware/middleware"; +import { + CreateWebsite, + DeleteWebsite, + GetAllWebsites, + GetWebsiteStatus, + Payout, +} from "../controllers/website"; + +export const websiteRouter = express.Router(); + +websiteRouter.get("/", authMiddleware, GetAllWebsites); +websiteRouter.post("/create", authMiddleware, CreateWebsite); +websiteRouter.get("/status", authMiddleware, GetWebsiteStatus); +websiteRouter.delete("/delete", authMiddleware, DeleteWebsite); +websiteRouter.post("/payout/:validatorId", authMiddleware, Payout); diff --git a/apps/api/src/validations/website.ts b/apps/api/src/validations/website.ts new file mode 100644 index 0000000..7855e69 --- /dev/null +++ b/apps/api/src/validations/website.ts @@ -0,0 +1,5 @@ +import { z } from "zod"; + +export const WebsiteDataValidation = z.object({ + url: z.string().url({ message: "Invalid URL format" }), +}); diff --git a/apps/frontend/app/dashboard/page.tsx b/apps/frontend/app/dashboard/page.tsx index 5420e04..65804ac 100644 --- a/apps/frontend/app/dashboard/page.tsx +++ b/apps/frontend/app/dashboard/page.tsx @@ -236,7 +236,7 @@ function App() { const token = await getToken(); setIsModalOpen(false) - axios.post(`${API_BACKEND_URL}/api/v1/website`, { + axios.post(`${API_BACKEND_URL}/api/v1/website/create`, { url, }, { headers: { diff --git a/apps/frontend/hooks/useWebsites.tsx b/apps/frontend/hooks/useWebsites.tsx index e61d0e6..b2396b9 100644 --- a/apps/frontend/hooks/useWebsites.tsx +++ b/apps/frontend/hooks/useWebsites.tsx @@ -21,7 +21,7 @@ export function useWebsites() { async function refreshWebsites() { const token = await getToken(); - const response = await axios.get(`${API_BACKEND_URL}/api/v1/websites`, { + const response = await axios.get(`${API_BACKEND_URL}/api/v1/website/`, { headers: { Authorization: token, }, diff --git a/apps/hub/index.ts b/apps/hub/index.ts index e8fac06..a86f5c3 100644 --- a/apps/hub/index.ts +++ b/apps/hub/index.ts @@ -1,6 +1,6 @@ import { randomUUIDv7, type ServerWebSocket } from "bun"; import type { IncomingMessage, SignupIncomingMessage } from "common/types"; -import { prismaClient } from "db/client"; +import { client } from "db/client"; import { PublicKey } from "@solana/web3.js"; import nacl from "tweetnacl"; import nacl_util from "tweetnacl-util"; @@ -44,7 +44,7 @@ Bun.serve({ }); async function signupHandler(ws: ServerWebSocket, { ip, publicKey, signedMessage, callbackId }: SignupIncomingMessage) { - const validatorDb = await prismaClient.validator.findFirst({ + const validatorDb = await client.validator.findFirst({ where: { publicKey, }, @@ -68,7 +68,7 @@ async function signupHandler(ws: ServerWebSocket, { ip, publicKey, sign } //TODO: Given the ip, return the location - const validator = await prismaClient.validator.create({ + const validator = await client.validator.create({ data: { ip, publicKey, @@ -103,7 +103,7 @@ async function verifyMessage(message: string, publicKey: string, signature: stri } setInterval(async () => { - const websitesToMonitor = await prismaClient.website.findMany({ + const websitesToMonitor = await client.website.findMany({ where: { disabled: false, }, @@ -133,7 +133,7 @@ setInterval(async () => { return; } - await prismaClient.$transaction(async (tx) => { + await client.$transaction(async (tx) => { await tx.websiteTick.create({ data: { websiteId: website.id, diff --git a/bun.lock b/bun.lock index 1b01488..f70c17e 100644 --- a/bun.lock +++ b/bun.lock @@ -24,9 +24,12 @@ "express": "^4.21.2", "jsonwebtoken": "^9.0.2", "jwt": "^0.2.0", + "morgan": "^1.10.0", + "zod": "^3.24.2", }, "devDependencies": { "@types/bun": "latest", + "@types/morgan": "^1.9.9", }, "peerDependencies": { "typescript": "^5.0.0", @@ -105,6 +108,7 @@ "packages/db": { "name": "db", "dependencies": { + "@prisma/client": "6.5.0", "prisma": "^6.5.0", }, "devDependencies": { @@ -327,6 +331,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=="], @@ -503,6 +509,8 @@ "@types/minimatch": ["@types/minimatch@5.1.2", "", {}, "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA=="], + "@types/morgan": ["@types/morgan@1.9.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ=="], + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], "@types/node": ["@types/node@22.13.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="], @@ -617,6 +625,8 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="], + "basic-ftp": ["basic-ftp@5.0.5", "", {}, "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="], "bigint-buffer": ["bigint-buffer@1.1.5", "", { "dependencies": { "bindings": "^1.3.0" } }, "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA=="], @@ -1217,6 +1227,8 @@ "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], + "morgan": ["morgan@1.10.0", "", { "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.0.2" } }, "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "mute-stream": ["mute-stream@0.0.8", "", {}, "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="], @@ -1265,6 +1277,8 @@ "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "on-headers": ["on-headers@1.0.2", "", {}, "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], @@ -1629,6 +1643,8 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], + "@clerk/backend/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], "@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -1665,6 +1681,8 @@ "ast-types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -1703,6 +1721,10 @@ "log-symbols/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + "morgan/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "morgan/on-finished": ["on-finished@2.3.0", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww=="], + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], "node-plop/inquirer": ["inquirer@7.3.3", "", { "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.19", "mute-stream": "0.0.8", "run-async": "^2.4.0", "rxjs": "^6.6.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" } }, "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA=="], @@ -1763,6 +1785,8 @@ "log-symbols/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + "morgan/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "next/postcss/picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "node-plop/inquirer/rxjs": ["rxjs@6.6.7", "", { "dependencies": { "tslib": "^1.9.0" } }, "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ=="], diff --git a/packages/db/.env.example b/packages/db/.env.example new file mode 100644 index 0000000..01ec508 --- /dev/null +++ b/packages/db/.env.example @@ -0,0 +1 @@ +DATABASE_URL="" 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": { diff --git a/packages/db/prisma/seed.ts b/packages/db/prisma/seed.ts index 379553d..064ced1 100644 --- a/packages/db/prisma/seed.ts +++ b/packages/db/prisma/seed.ts @@ -1,60 +1,59 @@ - -import { prismaClient } from "../src"; +import { client } from "../src"; const USER_ID = "4"; async function seed() { - await prismaClient.user.create({ - data: { - id: USER_ID, - email: "test@test.com", - } - }) - - const website = await prismaClient.website.create({ - data: { - url: "https://test.com", - userId: USER_ID - } - }) - - const validator = await prismaClient.validator.create({ - data: { - publicKey: "0x12341223123", - location: "Delhi", - ip: "127.0.0.1", - } - }) - - await prismaClient.websiteTick.create({ - data: { - websiteId: website.id, - status: "Good", - createdAt: new Date(), - latency: 100, - validatorId: validator.id - } - }) - - await prismaClient.websiteTick.create({ - data: { - websiteId: website.id, - status: "Good", - createdAt: new Date(Date.now() - 1000 * 60 *10), - latency: 100, - validatorId: validator.id - } - }) - - await prismaClient.websiteTick.create({ - data: { - websiteId: website.id, - status: "Bad", - createdAt: new Date(Date.now() - 1000 * 60 * 20), - latency: 100, - validatorId: validator.id - } - }) + await client.user.create({ + data: { + id: USER_ID, + email: "test@test.com", + }, + }); + + const website = await client.website.create({ + data: { + url: "https://test.com", + userId: USER_ID, + }, + }); + + const validator = await client.validator.create({ + data: { + publicKey: "0x12341223123", + location: "Delhi", + ip: "127.0.0.1", + }, + }); + + await client.websiteTick.create({ + data: { + websiteId: website.id, + status: "Good", + createdAt: new Date(), + latency: 100, + validatorId: validator.id, + }, + }); + + await client.websiteTick.create({ + data: { + websiteId: website.id, + status: "Good", + createdAt: new Date(Date.now() - 1000 * 60 * 10), + latency: 100, + validatorId: validator.id, + }, + }); + + await client.websiteTick.create({ + data: { + websiteId: website.id, + status: "Bad", + createdAt: new Date(Date.now() - 1000 * 60 * 20), + latency: 100, + validatorId: validator.id, + }, + }); } -seed(); \ No newline at end of file +seed(); diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index 7b50121..04456bb 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -1,5 +1,15 @@ -import { PrismaClient } from "@prisma/client" +import { PrismaClient } from "@prisma/client"; -export const prismaClient = new PrismaClient() +const prismaClientSingleton = () => { + return new PrismaClient(); +}; +declare global { + var prisma: PrismaClient | undefined; +} +export const client = globalThis.prisma ?? prismaClientSingleton(); + +if (process.env.NODE_ENV !== "production") { + globalThis.prisma = client; +}