refactor(server): split logic in to packages

This commit is contained in:
Mauricio Siu
2024-09-29 02:28:58 -06:00
parent 21dee4abac
commit 9b7aacc934
280 changed files with 28569 additions and 196 deletions

View File

@@ -0,0 +1,21 @@
version: "2"
services:
zookeeper:
image: "confluentinc/cp-zookeeper:latest"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- "2181:2181"
kafka:
image: "confluentinc/cp-kafka:latest"
depends_on:
- zookeeper
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
ports:
- "9092:9092"

View File

@@ -1,14 +1,29 @@
{
"name": "my-app",
"scripts": {
"dev": "tsx watch src/index.ts"
"dev": "tsx watch src/index.ts",
"tsc": "tsc --project tsconfig.json"
},
"dependencies": {
"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"
"dotenv": "^16.3.1",
"@upstash/qstash": "2.7.9",
"ioredis": "5.4.1",
"nats": "2.28.2",
"bullmq": "5.13.2",
"@nerimity/mimiqueue": "1.2.3",
"timers": "0.1.1",
"redis": "4.7.0",
"date-fns": "4.1.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"
}

View File

@@ -1,66 +1,57 @@
import { serve } from "@hono/node-server";
import { config } from "dotenv";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { validateLemonSqueezyLicense } from "./utils";
config();
import "dotenv/config";
import { createClient } from "redis";
import { Queue } from "@nerimity/mimiqueue";
import { deployApplication } from "@dokploy/builders";
// import { setTimeout } from "timers/promises";
const app = new Hono();
app.use(
"/*",
cors({
origin: ["http://localhost:3000", "http://localhost:3001"], // Ajusta esto a los orígenes de tu aplicación Next.js
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowHeaders: ["Content-Type", "Authorization"],
exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
maxAge: 600,
credentials: true,
}),
);
export const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY;
export const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID;
app.get("/v1/health", (c) => {
return c.text("Hello Hono!");
const redisClient = createClient({
socket: {
host: "localhost",
port: 6379,
},
// password: "xlfvpQ0ma2BkkkPX",
});
app.post("/v1/validate-license", async (c) => {
const { licenseKey } = await c.req.json();
if (!licenseKey) {
return c.json({ error: "License key is required" }, 400);
}
try {
const licenseValidation = await validateLemonSqueezyLicense(licenseKey);
if (licenseValidation.valid) {
return c.json({
valid: true,
message: "License is valid",
metadata: licenseValidation.meta,
});
}
return c.json(
app.post("/publish", async (c) => {
const { userId, applicationId } = await c.req.json();
queue
.add(
{
valid: false,
message: licenseValidation.error || "Invalid license",
userId,
applicationId,
},
400,
);
} catch (error) {
console.error("Error during license validation:", error);
return c.json({ error: "Internal server error" }, 500);
}
});
{ groupName: userId },
)
.then((res) => {
console.log(res);
});
return c.json({ message: `Despliegue encolado para el usuario ${userId}` });
});
// await redisClient.connect();
// await redisClient.flushAll();
const queue = new Queue({
name: "deployments",
process: async (data) => {
// await setTimeout(8000);
await deployApplication({
applicationId: data.applicationId,
titleLog: "HHHHH",
descriptionLog: "",
});
return { done: "lol", data };
},
redisClient,
});
const port = 4000;
console.log(`Server is running on port ${port}`);
(async () => {
await redisClient.connect();
await redisClient.flushAll();
})();
serve({
fetch: app.fetch,
port,
});
console.log("Starting Server ✅");
serve({ fetch: app.fetch, port });

82
apps/api/src/test.ts Normal file
View File

@@ -0,0 +1,82 @@
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

@@ -1,12 +1,14 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"skipLibCheck": true,
"types": ["node"],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"skipLibCheck": true,
"types": ["node"],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"traceResolution": true,
"diagnostics": true
}
}