diff --git a/apps/api/package.json b/apps/api/package.json index 3c17f132..85e74e12 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -8,6 +8,8 @@ "start": "node --experimental-specifier-resolution=node dist/index.js" }, "dependencies": { + "pino": "9.4.0", + "pino-pretty": "11.2.2", "@hono/zod-validator": "0.3.0", "zod": "^3.23.4", "react": "18.2.0", @@ -16,7 +18,6 @@ "@hono/node-server": "^1.12.1", "hono": "^4.5.8", "dotenv": "^16.3.1", - "@upstash/qstash": "2.7.9", "redis": "4.7.0", "@nerimity/mimiqueue": "1.2.3" }, diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index aeeceabd..283de9f5 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -6,6 +6,7 @@ import { Queue } from "@nerimity/mimiqueue"; import { zValidator } from "@hono/zod-validator"; import { type DeployJob, deployJobSchema } from "./schema"; import { deploy } from "./utils"; +import { logger } from "./logger"; const app = new Hono(); const redisClient = createClient({ @@ -14,12 +15,10 @@ const redisClient = createClient({ app.post("/deploy", zValidator("json", deployJobSchema), (c) => { const data = c.req.valid("json"); - queue.add(data, { groupName: data.serverId }).then((res) => { - console.log(res); - }); + const res = queue.add(data, { groupName: data.serverId }); return c.json( { - message: "Deployment started", + message: "Deployment Added", }, 200, ); @@ -32,17 +31,18 @@ app.get("/health", async (c) => { const queue = new Queue({ name: "deployments", process: async (job: DeployJob) => { - console.log(job); + logger.info("Deploying job", job); return await deploy(job); }, redisClient, }); -const port = Number.parseInt(process.env.PORT || "3000"); (async () => { await redisClient.connect(); await redisClient.flushAll(); + logger.info("Cleaning Redis"); })(); -console.log("Starting Server ✅", port); +const port = Number.parseInt(process.env.PORT || "3000"); +logger.info("Starting Deployments Server ✅", port); serve({ fetch: app.fetch, port }); diff --git a/apps/api/src/logger.ts b/apps/api/src/logger.ts new file mode 100644 index 00000000..3d8b5840 --- /dev/null +++ b/apps/api/src/logger.ts @@ -0,0 +1,10 @@ +import pino from "pino"; + +export const logger = pino({ + transport: { + target: "pino-pretty", + options: { + colorize: true, + }, + }, +}); diff --git a/apps/api/src/test.ts b/apps/api/src/test.ts deleted file mode 100644 index 635e0078..00000000 --- a/apps/api/src/test.ts +++ /dev/null @@ -1,82 +0,0 @@ -// import { Hono } from "hono"; -// import { Client } from "@upstash/qstash"; -// import { serve } from "@hono/node-server"; -// import dotenv from "dotenv"; -// import Redis from "ioredis"; - -// dotenv.config(); - -// const redis = new Redis({ -// host: "localhost", -// port: 7777, -// password: "xlfvpQ0ma2BkkkPX", -// }); - -// // redis.set("test", "test"); -// // console.log(await redis.get("test")); - -// // console.log(await redis.get("user-1-processing")); -// const app = new Hono(); -// console.log("QStash Token:", process.env.PUBLIC_URL); - -// const qstash = new Client({ -// token: process.env.QSTASH_TOKEN as string, -// }); - -// const queue = qstash.queue({ -// queueName: "deployments", -// }); - -// // Endpoint que publica un mensaje en QStash -// app.post("/enqueue", async (c) => { -// const { userId, deploymentId } = await c.req.json(); -// const response = await qstash.publishJSON({ -// url: `${process.env.PUBLIC_URL}/process`, // Endpoint para procesar la tarea -// body: { userId, deploymentId }, // Datos del despliegue - -// }); - -// return c.json({ message: "Task enqueued", id: response.messageId }); -// }); - -// // Endpoint que recibe el mensaje procesado -// app.post("/process", async (c) => { -// const { userId, deploymentId } = await c.req.json(); - -// const isProcessing = await redis.get(`user-${userId}-processing`); -// console.log(`isProcessing for user ${userId}:`, isProcessing); - -// if (isProcessing === "true") { -// console.log( -// `User ${userId} is already processing a deployment. Queuing the next one.`, -// ); -// return c.json( -// { -// status: "User is already processing a deployment, waiting...", -// }, -// { -// status: 400, -// }, -// ); -// } -// redis.set(`user-${userId}-processing`, "true"); - -// try { -// await new Promise((resolve) => setTimeout(resolve, 5000)); -// } catch (error) { -// } finally { -// await redis.del(`user-${userId}-processing`); -// } - -// return c.json({ status: "Processed", userId, deploymentId }); -// }); - -// // Inicia el servidor en el puerto 3000 -// const port = 3000; -// console.log(`Server is running on port http://localhost:${port}`); - -// serve({ -// fetch: app.fetch, -// port, -// }); -// // 18 diff --git a/apps/dokploy/server/api/routers/backup.ts b/apps/dokploy/server/api/routers/backup.ts index b18cca6a..7521889b 100644 --- a/apps/dokploy/server/api/routers/backup.ts +++ b/apps/dokploy/server/api/routers/backup.ts @@ -5,6 +5,7 @@ import { apiRemoveBackup, apiUpdateBackup, } from "@/server/db/schema"; +import { removeJob, schedule } from "@/server/utils/backup"; import { createBackup, findBackupById, @@ -20,6 +21,7 @@ import { findMongoByBackupId, findMySqlByBackupId, findPostgresByBackupId, + IS_CLOUD, } from "@dokploy/builders"; import { TRPCError } from "@trpc/server"; @@ -33,8 +35,16 @@ export const backupRouter = createTRPCRouter({ const backup = await findBackupById(newBackup.backupId); - if (backup.enabled) { - scheduleBackup(backup); + if (IS_CLOUD && backup.enabled) { + await schedule({ + cronSchedule: backup.schedule, + backupId: backup.backupId, + type: "backup", + }); + } else { + if (backup.enabled) { + scheduleBackup(backup); + } } } catch (error) { throw new TRPCError({ @@ -55,11 +65,19 @@ export const backupRouter = createTRPCRouter({ await updateBackupById(input.backupId, input); const backup = await findBackupById(input.backupId); - if (backup.enabled) { - removeScheduleBackup(input.backupId); - scheduleBackup(backup); + if (IS_CLOUD && backup.enabled) { + await schedule({ + cronSchedule: backup.schedule, + backupId: backup.backupId, + type: "backup", + }); } else { - removeScheduleBackup(input.backupId); + if (backup.enabled) { + removeScheduleBackup(input.backupId); + scheduleBackup(backup); + } else { + removeScheduleBackup(input.backupId); + } } } catch (error) { throw new TRPCError({ @@ -73,7 +91,15 @@ export const backupRouter = createTRPCRouter({ .mutation(async ({ input }) => { try { const value = await removeBackupById(input.backupId); - removeScheduleBackup(input.backupId); + if (IS_CLOUD && value) { + removeJob({ + backupId: input.backupId, + cronSchedule: value.schedule, + type: "backup", + }); + } else { + removeScheduleBackup(input.backupId); + } return value; } catch (error) { throw new TRPCError({ diff --git a/apps/dokploy/server/utils/backup.ts b/apps/dokploy/server/utils/backup.ts new file mode 100644 index 00000000..edb490c0 --- /dev/null +++ b/apps/dokploy/server/utils/backup.ts @@ -0,0 +1,46 @@ +type QueueJob = + | { + type: "backup"; + cronSchedule: string; + backupId: string; + } + | { + type: "server"; + cronSchedule: string; + serverId: string; + }; +export const schedule = async (job: QueueJob) => { + try { + const result = await fetch(`${process.env.JOBS_URL}/create-backup`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(job), + }); + const data = await result.json(); + console.log(data); + return data; + } catch (error) { + console.log(error); + throw error; + } +}; + +export const removeJob = async (job: QueueJob) => { + try { + const result = await fetch(`${process.env.JOBS_URL}/remove-job`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(job), + }); + const data = await result.json(); + console.log(data); + return data; + } catch (error) { + console.log(error); + throw error; + } +}; diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts index 3557a71a..c25fb931 100644 --- a/apps/dokploy/server/wss/docker-container-logs.ts +++ b/apps/dokploy/server/wss/docker-container-logs.ts @@ -33,8 +33,6 @@ export const setupDockerContainerLogsWebSocketServer = ( const tail = url.searchParams.get("tail"); const serverId = url.searchParams.get("serverId"); const { user, session } = await validateWebSocketRequest(req); - console.log(wssTerm.clients); - console.log(wssTerm.clients.size); if (!containerId) { ws.close(4000, "containerId no provided"); diff --git a/apps/schedules/.gitignore b/apps/schedules/.gitignore new file mode 100644 index 00000000..36fabb6c --- /dev/null +++ b/apps/schedules/.gitignore @@ -0,0 +1,28 @@ +# dev +.yarn/ +!.yarn/releases +.vscode/* +!.vscode/launch.json +!.vscode/*.code-snippets +.idea/workspace.xml +.idea/usage.statistics.xml +.idea/shelf + +# deps +node_modules/ + +# env +.env +.env.production + +# logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# misc +.DS_Store diff --git a/apps/schedules/README.md b/apps/schedules/README.md new file mode 100644 index 00000000..e12b31db --- /dev/null +++ b/apps/schedules/README.md @@ -0,0 +1,8 @@ +``` +npm install +npm run dev +``` + +``` +open http://localhost:3000 +``` diff --git a/apps/schedules/package.json b/apps/schedules/package.json new file mode 100644 index 00000000..6013e560 --- /dev/null +++ b/apps/schedules/package.json @@ -0,0 +1,30 @@ +{ + "name": "@dokploy/schedules", + "scripts": { + "dev": "PORT=4001 tsx watch src/index.ts", + "build": "tsc --project tsconfig.json", + "start": "node --experimental-specifier-resolution=node dist/index.js" + }, + "dependencies": { + "bullmq": "5.4.2", + "@hono/zod-validator": "0.3.0", + "zod": "^3.23.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "@dokploy/builders": "workspace:*", + "@hono/node-server": "^1.12.1", + "hono": "^4.5.8", + "dotenv": "^16.3.1", + "pino": "9.4.0", + "pino-pretty": "11.2.2", + "redis": "4.7.0" + }, + "devDependencies": { + "typescript": "^5.4.2", + "@types/react": "^18.2.37", + "@types/react-dom": "^18.2.15", + "@types/node": "^20.11.17", + "tsx": "^4.7.1" + }, + "packageManager": "pnpm@9.5.0" +} diff --git a/apps/schedules/src/index.ts b/apps/schedules/src/index.ts new file mode 100644 index 00000000..f83bde24 --- /dev/null +++ b/apps/schedules/src/index.ts @@ -0,0 +1,55 @@ +import { serve } from "@hono/node-server"; +import { Hono } from "hono"; +import "dotenv/config"; +import { zValidator } from "@hono/zod-validator"; +import { jobQueueSchema } from "./schema"; +import { firstWorker, secondWorker } from "./workers"; +import { logger } from "./logger"; +import { cleanQueue, removeJob, scheduleJob } from "./queue"; + +const app = new Hono(); + +cleanQueue(); + +app.post("/create-backup", zValidator("json", jobQueueSchema), (c) => { + const data = c.req.valid("json"); + scheduleJob(data); + + logger.info("Backup created successfully", data); + return c.json({ message: "Backup created successfully" }); +}); + +app.post("/remove-job", zValidator("json", jobQueueSchema), async (c) => { + const data = c.req.valid("json"); + const result = await removeJob(data); + logger.info("Job removed successfully", data); + return c.json({ message: "Job removed successfully", result }); +}); + +app.get("/health", async (c) => { + return c.json({ status: "ok" }); +}); + +export const gracefulShutdown = async (signal: string) => { + logger.warn(`Received ${signal}, closing server...`); + await firstWorker.close(); + await secondWorker.close(); + process.exit(0); +}; + +process.on("SIGINT", () => gracefulShutdown("SIGINT")); + +process.on("SIGTERM", () => gracefulShutdown("SIGTERM")); + +process.on("uncaughtException", (err) => { + logger.error(err, "Uncaught exception"); +}); + +process.on("unhandledRejection", (reason, promise) => { + logger.error({ promise, reason }, "Unhandled Rejection at: Promise"); +}); + +const port = Number.parseInt(process.env.PORT || "3000"); + +logger.info("Starting Schedules Server ✅", port); +serve({ fetch: app.fetch, port }); diff --git a/apps/schedules/src/logger.ts b/apps/schedules/src/logger.ts new file mode 100644 index 00000000..3d8b5840 --- /dev/null +++ b/apps/schedules/src/logger.ts @@ -0,0 +1,10 @@ +import pino from "pino"; + +export const logger = pino({ + transport: { + target: "pino-pretty", + options: { + colorize: true, + }, + }, +}); diff --git a/apps/schedules/src/queue.ts b/apps/schedules/src/queue.ts new file mode 100644 index 00000000..40baba61 --- /dev/null +++ b/apps/schedules/src/queue.ts @@ -0,0 +1,55 @@ +import { Queue } from "bullmq"; +import type { QueueJob } from "./schema"; +import { logger } from "./logger"; + +export const jobQueue = new Queue("backupQueue", { + connection: { + host: process.env.REDIS_URL, + }, + defaultJobOptions: { + removeOnComplete: true, + removeOnFail: true, + }, +}); + +export const cleanQueue = async () => { + try { + await jobQueue.obliterate({ force: true }); + logger.info("Queue Cleaned"); + } catch (error) { + logger.error("Error cleaning queue:", error); + } +}; + +export const scheduleJob = (job: QueueJob) => { + if (job.type === "backup") { + jobQueue.add(job.backupId, job, { + repeat: { + pattern: job.cronSchedule, + }, + }); + } else if (job.type === "server") { + jobQueue.add(`${job.serverId}-cleanup`, job, { + repeat: { + pattern: job.cronSchedule, + }, + }); + } +}; + +export const removeJob = async (data: QueueJob) => { + if (data.type === "backup") { + const { backupId, cronSchedule } = data; + const result = await jobQueue.removeRepeatable(backupId, { + pattern: cronSchedule, + }); + return result; + } + if (data.type === "server") { + const { serverId, cronSchedule } = data; + const result = await jobQueue.removeRepeatable(`${serverId}-cleanup`, { + pattern: cronSchedule, + }); + return result; + } +}; diff --git a/apps/schedules/src/schema.ts b/apps/schedules/src/schema.ts new file mode 100644 index 00000000..feadb5a9 --- /dev/null +++ b/apps/schedules/src/schema.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; + +export const jobQueueSchema = z.discriminatedUnion("type", [ + z.object({ + cronSchedule: z.string(), + type: z.literal("backup"), + backupId: z.string(), + }), + z.object({ + cronSchedule: z.string(), + type: z.literal("server"), + serverId: z.string(), + }), +]); + +export type QueueJob = z.infer; diff --git a/apps/schedules/src/utils.ts b/apps/schedules/src/utils.ts new file mode 100644 index 00000000..1ff7f283 --- /dev/null +++ b/apps/schedules/src/utils.ts @@ -0,0 +1,42 @@ +import { + cleanUpDockerBuilder, + cleanUpSystemPrune, + cleanUpUnusedImages, + findBackupById, + runMariadbBackup, + runMongoBackup, + runMySqlBackup, + runPostgresBackup, +} from "@dokploy/builders"; +import type { QueueJob } from "./schema"; +import { logger } from "./logger"; + +export const runJobs = async (job: QueueJob) => { + try { + if (job.type === "backup") { + const { backupId } = job; + const backup = await findBackupById(backupId); + const { databaseType, postgres, mysql, mongo, mariadb } = backup; + if (databaseType === "postgres" && postgres) { + await runPostgresBackup(postgres, backup); + } else if (databaseType === "mysql" && mysql) { + await runMySqlBackup(mysql, backup); + } else if (databaseType === "mongo" && mongo) { + await runMongoBackup(mongo, backup); + } else if (databaseType === "mariadb" && mariadb) { + await runMariadbBackup(mariadb, backup); + } + } + if (job.type === "server") { + const { serverId } = job; + await cleanUpUnusedImages(serverId); + await cleanUpDockerBuilder(serverId); + await cleanUpSystemPrune(serverId); + // await sendDockerCleanupNotifications(); + } + } catch (error) { + logger.error(error); + } + + return true; +}; diff --git a/apps/schedules/src/workers.ts b/apps/schedules/src/workers.ts new file mode 100644 index 00000000..cd5b0eb9 --- /dev/null +++ b/apps/schedules/src/workers.ts @@ -0,0 +1,28 @@ +import type { QueueJob } from "./schema"; +import { type Job, Worker } from "bullmq"; +import { runJobs } from "./utils"; + +export const firstWorker = new Worker( + "backupQueue", + async (job: Job) => { + await runJobs(job.data); + }, + { + concurrency: 50, + connection: { + host: process.env.REDIS_URL, + }, + }, +); +export const secondWorker = new Worker( + "backupQueue", + async (job: Job) => { + await runJobs(job.data); + }, + { + concurrency: 50, + connection: { + host: process.env.REDIS_URL, + }, + }, +); diff --git a/apps/schedules/tsconfig.json b/apps/schedules/tsconfig.json new file mode 100644 index 00000000..8d3088b4 --- /dev/null +++ b/apps/schedules/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx" + }, + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 232b5b18..eec9b3d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,15 +54,18 @@ importers: '@nerimity/mimiqueue': specifier: 1.2.3 version: 1.2.3(redis@4.7.0) - '@upstash/qstash': - specifier: 2.7.9 - version: 2.7.9 dotenv: specifier: ^16.3.1 version: 16.4.5 hono: specifier: ^4.5.8 version: 4.5.8 + pino: + specifier: 9.4.0 + version: 9.4.0 + pino-pretty: + specifier: 11.2.2 + version: 11.2.2 react: specifier: 18.2.0 version: 18.2.0 @@ -442,6 +445,61 @@ importers: specifier: ^1.6.0 version: 1.6.0(@types/node@18.19.42)(terser@5.31.3) + apps/schedules: + dependencies: + '@dokploy/builders': + specifier: workspace:* + version: link:../../packages/builders + '@hono/node-server': + specifier: ^1.12.1 + version: 1.12.1 + '@hono/zod-validator': + specifier: 0.3.0 + version: 0.3.0(hono@4.5.8)(zod@3.23.8) + bullmq: + specifier: 5.4.2 + version: 5.4.2 + dotenv: + specifier: ^16.3.1 + version: 16.4.5 + hono: + specifier: ^4.5.8 + version: 4.5.8 + pino: + specifier: 9.4.0 + version: 9.4.0 + pino-pretty: + specifier: 11.2.2 + version: 11.2.2 + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + redis: + specifier: 4.7.0 + version: 4.7.0 + zod: + specifier: ^3.23.4 + version: 3.23.8 + devDependencies: + '@types/node': + specifier: ^20.11.17 + version: 20.14.10 + '@types/react': + specifier: 18.3.5 + version: 18.3.5 + '@types/react-dom': + specifier: 18.3.0 + version: 18.3.0 + tsx: + specifier: ^4.7.1 + version: 4.16.2 + typescript: + specifier: ^5.4.2 + version: 5.5.3 + apps/website: dependencies: '@headlessui/react': @@ -3639,9 +3697,6 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@upstash/qstash@2.7.9': - resolution: {integrity: sha512-3eniaa7bAycKQfEzHQS02FBtYwCWcw0oywkW36fv0paEB+ub1RqlDIQlZieozKTQcflK9ezFZUuSiwxKRbdv+A==} - '@vitest/expect@1.6.0': resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} @@ -3917,6 +3972,10 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + autolinker@3.16.2: resolution: {integrity: sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==} @@ -4347,9 +4406,6 @@ packages: uWebSockets.js: optional: true - crypto-js@4.2.0: - resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -4435,6 +4491,9 @@ packages: date-fns@3.6.0: resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -5032,6 +5091,9 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} @@ -5055,6 +5117,13 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-uri@3.0.1: resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} @@ -5387,6 +5456,9 @@ packages: heap@0.2.7: resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hi-base32@0.5.1: resolution: {integrity: sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==} @@ -5723,8 +5795,9 @@ packages: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true - jose@5.9.3: - resolution: {integrity: sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} js-beautify@1.15.1: resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} @@ -6542,6 +6615,10 @@ packages: ohash@1.1.3: resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -6700,6 +6777,20 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + + pino-pretty@11.2.2: + resolution: {integrity: sha512-2FnyGir8nAJAqD3srROdrF1J5BIcMT4nwj7hHSc60El6Uxlym00UbCCd8pYIterstVBFlMyF1yFV8XdGIPbj4A==} + hasBin: true + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@9.4.0: + resolution: {integrity: sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==} + hasBin: true + pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -6850,6 +6941,9 @@ packages: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} + process-warning@4.0.0: + resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -6899,6 +6993,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -7107,6 +7204,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + recharts-scale@0.4.5: resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} @@ -7266,6 +7367,10 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7287,6 +7392,9 @@ packages: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + selderee@0.11.0: resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} @@ -7382,6 +7490,9 @@ packages: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} engines: {node: '>=8.0.0'} + sonic-boom@4.1.0: + resolution: {integrity: sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==} + sonner@1.5.0: resolution: {integrity: sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==} peerDependencies: @@ -7651,6 +7762,9 @@ packages: peerDependencies: tslib: ^2 + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -11517,11 +11631,6 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@upstash/qstash@2.7.9': - dependencies: - crypto-js: 4.2.0 - jose: 5.9.3 - '@vitest/expect@1.6.0': dependencies: '@vitest/spy': 1.6.0 @@ -11853,6 +11962,8 @@ snapshots: asynckit@0.4.0: {} + atomic-sleep@1.0.0: {} + autolinker@3.16.2: dependencies: tslib: 2.6.3 @@ -12318,8 +12429,6 @@ snapshots: crossws@0.2.4: {} - crypto-js@4.2.0: {} - css.escape@1.5.1: {} cssesc@3.0.0: {} @@ -12395,6 +12504,8 @@ snapshots: date-fns@3.6.0: {} + dateformat@4.6.3: {} + debug@3.2.7: dependencies: ms: 2.1.3 @@ -12900,7 +13011,7 @@ snapshots: eslint: 8.45.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1)(eslint@8.45.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.45.0) eslint-plugin-react: 7.35.0(eslint@8.45.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.45.0) @@ -12924,7 +13035,7 @@ snapshots: enhanced-resolve: 5.17.1 eslint: 8.45.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1)(eslint@8.45.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.15.0 @@ -12946,7 +13057,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1)(eslint@8.45.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -13172,6 +13283,8 @@ snapshots: extend@3.0.2: {} + fast-copy@3.0.2: {} + fast-deep-equal@2.0.1: {} fast-deep-equal@3.1.3: {} @@ -13192,6 +13305,10 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-redact@3.5.0: {} + + fast-safe-stringify@2.1.1: {} + fast-uri@3.0.1: {} fastq@1.17.1: @@ -13643,6 +13760,8 @@ snapshots: heap@0.2.7: {} + help-me@5.0.0: {} + hi-base32@0.5.1: {} highlight.js@10.7.3: {} @@ -13760,7 +13879,7 @@ snapshots: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.5 + debug: 4.3.7 denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -13957,7 +14076,7 @@ snapshots: jiti@1.21.6: {} - jose@5.9.3: {} + joycon@3.1.1: {} js-beautify@1.15.1: dependencies: @@ -15078,6 +15197,8 @@ snapshots: ohash@1.1.3: {} + on-exit-leak-free@2.1.2: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -15236,6 +15357,44 @@ snapshots: pify@2.3.0: {} + pino-abstract-transport@1.2.0: + dependencies: + readable-stream: 4.5.2 + split2: 4.2.0 + + pino-pretty@11.2.2: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.2.0 + pump: 3.0.0 + readable-stream: 4.5.2 + secure-json-parse: 2.7.0 + sonic-boom: 4.1.0 + strip-json-comments: 3.1.1 + + pino-std-serializers@7.0.0: {} + + pino@9.4.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.2.0 + pino-std-serializers: 7.0.0 + process-warning: 4.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.1.0 + thread-stream: 3.1.0 + pirates@4.0.6: {} pkg-types@1.1.3: @@ -15336,6 +15495,8 @@ snapshots: prismjs@1.29.0: {} + process-warning@4.0.0: {} + process@0.11.10: {} prop-types@15.8.1: @@ -15384,6 +15545,8 @@ snapshots: queue-microtask@1.2.3: {} + quick-format-unescaped@4.0.4: {} + quick-lru@5.1.1: {} radix3@1.1.2: {} @@ -15632,6 +15795,8 @@ snapshots: dependencies: picomatch: 2.3.1 + real-require@0.2.0: {} + recharts-scale@0.4.5: dependencies: decimal.js-light: 2.5.1 @@ -15846,6 +16011,8 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 + safe-stable-stringify@2.5.0: {} + safer-buffer@2.1.2: {} scheduler@0.23.2: @@ -15874,6 +16041,8 @@ snapshots: extend-shallow: 2.0.1 kind-of: 6.0.3 + secure-json-parse@2.7.0: {} + selderee@0.11.0: dependencies: parseley: 0.12.1 @@ -15969,6 +16138,10 @@ snapshots: slugify@1.6.6: {} + sonic-boom@4.1.0: + dependencies: + atomic-sleep: 1.0.0 + sonner@1.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 @@ -16325,6 +16498,10 @@ snapshots: dependencies: tslib: 2.6.3 + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + through@2.3.8: {} timers-ext@0.1.8: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 89a83b55..f2d5248a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,5 +3,6 @@ packages: - "apps/docs" - "apps/website" - "apps/api" + - "apps/schedules" - "apps/models" - "packages/builders"