feat: auto committed

This commit is contained in:
SpecialX
2026-07-10 18:57:57 +08:00
parent 5e0e20b1ce
commit 9fae2b0e78
74 changed files with 5362 additions and 304 deletions

View File

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

View File

@@ -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(),

View 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
}
}