feat(schedules): add schedules server

This commit is contained in:
Mauricio Siu
2024-10-05 22:11:38 -06:00
parent 651bf3a303
commit 43555cdabe
19 changed files with 584 additions and 122 deletions

View File

@@ -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"
},

View File

@@ -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 });

10
apps/api/src/logger.ts Normal file
View File

@@ -0,0 +1,10 @@
import pino from "pino";
export const logger = pino({
transport: {
target: "pino-pretty",
options: {
colorize: true,
},
},
});

View File

@@ -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

View File

@@ -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,9 +35,17 @@ export const backupRouter = createTRPCRouter({
const backup = await findBackupById(newBackup.backupId);
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({
code: "BAD_REQUEST",
@@ -55,12 +65,20 @@ export const backupRouter = createTRPCRouter({
await updateBackupById(input.backupId, input);
const backup = await findBackupById(input.backupId);
if (IS_CLOUD && backup.enabled) {
await schedule({
cronSchedule: backup.schedule,
backupId: backup.backupId,
type: "backup",
});
} else {
if (backup.enabled) {
removeScheduleBackup(input.backupId);
scheduleBackup(backup);
} else {
removeScheduleBackup(input.backupId);
}
}
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -73,7 +91,15 @@ export const backupRouter = createTRPCRouter({
.mutation(async ({ input }) => {
try {
const value = await removeBackupById(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({

View File

@@ -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;
}
};

View File

@@ -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");

28
apps/schedules/.gitignore vendored Normal file
View File

@@ -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

8
apps/schedules/README.md Normal file
View File

@@ -0,0 +1,8 @@
```
npm install
npm run dev
```
```
open http://localhost:3000
```

View File

@@ -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"
}

View File

@@ -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 });

View File

@@ -0,0 +1,10 @@
import pino from "pino";
export const logger = pino({
transport: {
target: "pino-pretty",
options: {
colorize: true,
},
},
});

View File

@@ -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;
}
};

View File

@@ -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<typeof jobQueueSchema>;

View File

@@ -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;
};

View File

@@ -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<QueueJob>) => {
await runJobs(job.data);
},
{
concurrency: 50,
connection: {
host: process.env.REDIS_URL,
},
},
);
export const secondWorker = new Worker(
"backupQueue",
async (job: Job<QueueJob>) => {
await runJobs(job.data);
},
{
concurrency: 50,
connection: {
host: process.env.REDIS_URL,
},
},
);

View File

@@ -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"]
}

223
pnpm-lock.yaml generated
View File

@@ -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:

View File

@@ -3,5 +3,6 @@ packages:
- "apps/docs"
- "apps/website"
- "apps/api"
- "apps/schedules"
- "apps/models"
- "packages/builders"