feat: auto committed
This commit is contained in:
@@ -1,16 +1,25 @@
|
||||
import { drizzle } from "drizzle-orm/mysql2";
|
||||
import type { MySql2Database } from "drizzle-orm/mysql2";
|
||||
import mysql from "mysql2/promise";
|
||||
import { env } from "./env.js";
|
||||
|
||||
const pool = mysql.createPool({
|
||||
uri: env.DATABASE_URL,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
});
|
||||
let pool: mysql.Pool | null = null;
|
||||
|
||||
export const db = drizzle(pool);
|
||||
export function getDb(): MySql2Database {
|
||||
if (!pool) {
|
||||
pool = mysql.createPool({
|
||||
uri: env.DATABASE_URL,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
});
|
||||
}
|
||||
return drizzle(pool);
|
||||
}
|
||||
|
||||
export async function closeDb(): Promise<void> {
|
||||
await pool.end();
|
||||
if (pool) {
|
||||
await pool.end();
|
||||
pool = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ import { z } from "zod";
|
||||
|
||||
const envSchema = z.object({
|
||||
PORT: z.string().default("3005"),
|
||||
GRPC_PORT: z.string().default("50054"),
|
||||
DATABASE_URL: z.string().url(),
|
||||
REDIS_URL: z.string().url().optional(),
|
||||
NEO4J_URL: z.string().url().optional(),
|
||||
NEO4J_PASSWORD: z.string().optional(),
|
||||
ES_URL: z.string().url().optional(),
|
||||
KAFKA_BROKERS: z.string().default("localhost:9092"),
|
||||
JWT_SECRET: z.string().optional(),
|
||||
JWT_ISSUER: z.string().default("next-edu-cloud"),
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url().optional(),
|
||||
|
||||
57
services/content/src/config/kafka.ts
Normal file
57
services/content/src/config/kafka.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Kafka } from "kafkajs";
|
||||
import { env } from "./env.js";
|
||||
import { logger } from "../shared/observability/logger.js";
|
||||
|
||||
export const kafka = new Kafka({
|
||||
brokers: env.KAFKA_BROKERS.split(","),
|
||||
clientId: "content-service",
|
||||
});
|
||||
|
||||
export const producer = kafka.producer({
|
||||
idempotent: true,
|
||||
transactionalId: "content-tx",
|
||||
});
|
||||
|
||||
export const neo4jSyncConsumer = kafka.consumer({
|
||||
groupId: "content-neo4j-sync",
|
||||
});
|
||||
|
||||
let producerIsConnected = false;
|
||||
|
||||
producer.on("producer.connect", () => {
|
||||
producerIsConnected = true;
|
||||
});
|
||||
producer.on("producer.disconnect", () => {
|
||||
producerIsConnected = false;
|
||||
});
|
||||
|
||||
export function isKafkaProducerConnected(): boolean {
|
||||
return producerIsConnected;
|
||||
}
|
||||
|
||||
export async function connectKafka(): Promise<void> {
|
||||
try {
|
||||
await producer.connect();
|
||||
logger.info("Kafka producer connected");
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
{
|
||||
err: err instanceof Error ? err.message : String(err),
|
||||
},
|
||||
"Kafka connect failed, running without Kafka (outbox will retry)",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function disconnectKafka(): Promise<void> {
|
||||
try {
|
||||
await producer.disconnect();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
await neo4jSyncConsumer.disconnect();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user