From ab81475ad65fe544da07cc5b92b14b4123fb1346 Mon Sep 17 00:00:00 2001 From: creations Date: Thu, 1 May 2025 15:32:09 -0400 Subject: [PATCH] add redis, cassandra support, change helpers to lib --- .env.example | 19 +++++++++++++++++ .gitignore | 1 + config/environment.ts | 47 ++++++++++++++++++++++++++++++++++++++++++- package.json | 3 ++- src/index.ts | 21 ++++++++++++++++--- src/lib/cassandra.ts | 47 +++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 2 +- types/config.d.ts | 11 ++++++++++ 8 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 .env.example create mode 100644 src/lib/cassandra.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..256b06a --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# NODE_ENV=development +HOST=0.0.0.0 +PORT=8080 + +# Redis (Required) +REDIS_URL=redis://localhost:6379 +REDIS_TTL=3600 + +# Cassandra (Required) +CASSANDRA_HOST=localhost +CASSANDRA_PORT=9042 +CASSANDRA_DATACENTER=datacenter1 +CASSANDRA_CONTACT_POINTS=localhost + +# Optional +CASSANDRA_KEYSPACE= +CASSANDRA_USERNAME= +CASSANDRA_AUTH_ENABLED=false +CASSANDRA_PASSWORD= diff --git a/.gitignore b/.gitignore index 58b5bae..d23d9c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules bun.lock +.env diff --git a/config/environment.ts b/config/environment.ts index cdd86e8..10461f9 100644 --- a/config/environment.ts +++ b/config/environment.ts @@ -1,6 +1,51 @@ +import { logger } from "@creations.works/logger"; + export const environment: Environment = { - port: Number.parseInt(process.env.PORT || "8080", 10), + port: Number.parseInt(process.env.PORT || "", 10), host: process.env.HOST || "0.0.0.0", development: process.env.NODE_ENV === "development" || process.argv.includes("--dev"), }; + +export const redisTtl: number = process.env.REDIS_TTL + ? Number.parseInt(process.env.REDIS_TTL, 10) + : 60 * 60 * 1; // 1 hour + +export const cassandra: CassandraConfig = { + host: process.env.CASSANDRA_HOST || "localhost", + port: Number.parseInt(process.env.CASSANDRA_PORT || "9042", 10), + keyspace: process.env.CASSANDRA_KEYSPACE || "", + username: process.env.CASSANDRA_USERNAME || "", + password: process.env.CASSANDRA_PASSWORD || "", + datacenter: process.env.CASSANDRA_DATACENTER || "", + contactPoints: (process.env.CASSANDRA_CONTACT_POINTS || "localhost").split(","), + authEnabled: process.env.CASSANDRA_AUTH_ENABLED === "false", +}; + +export function verifyRequiredVariables(): void { + const requiredVariables = [ + "HOST", + "PORT", + "REDIS_URL", + "REDIS_TTL", + "CASSANDRA_HOST", + "CASSANDRA_PORT", + "CASSANDRA_CONTACT_POINTS", + "CASSANDRA_AUTH_ENABLED", + "CASSANDRA_DATACENTER", + ]; + + let hasError = false; + + for (const key of requiredVariables) { + const value = process.env[key]; + if (value === undefined || value.trim() === "") { + logger.error(`Missing or empty environment variable: ${key}`); + hasError = true; + } + } + + if (hasError) { + process.exit(1); + } +} diff --git a/package.json b/package.json index 63acdfd..a2517f3 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "typescript": "^5.8.3" }, "dependencies": { - "@creations.works/logger": "^1.0.3" + "@creations.works/logger": "^1.0.3", + "cassandra-driver": "^4.8.0" } } diff --git a/src/index.ts b/src/index.ts index c4148b4..a3acbf1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,27 @@ -import { logger } from "@creations.works/logger"; - +import { CassandraService } from "@/lib/cassandra"; import { serverHandler } from "@/server"; +import { verifyRequiredVariables } from "@config/environment"; +import { logger } from "@creations.works/logger"; +import { redis } from "bun"; async function main(): Promise { + verifyRequiredVariables(); + + // Redis + try { + await redis.connect(); + logger.info("Redis connection successful."); + } catch (error) { + logger.error(["Redis connection failed:", error as Error]); + process.exit(1); + } + + await CassandraService.connect(); + serverHandler.initialize(); } main().catch((error: Error) => { - logger.error(["Error initializing the server:", error]); + logger.error(error); process.exit(1); }); diff --git a/src/lib/cassandra.ts b/src/lib/cassandra.ts new file mode 100644 index 0000000..a533c4d --- /dev/null +++ b/src/lib/cassandra.ts @@ -0,0 +1,47 @@ +import { cassandra as config } from "@config/environment"; +import { logger } from "@creations.works/logger"; +import { Client, auth } from "cassandra-driver"; + +class CassandraService { + private static instance: Client | null = null; + + private constructor() {} + + public static getClient(): Client { + if (!CassandraService.instance) { + CassandraService.instance = new Client({ + contactPoints: config.contactPoints, + localDataCenter: config.datacenter, + keyspace: config.keyspace, + authProvider: config.authEnabled + ? new auth.PlainTextAuthProvider(config.username, config.password) + : undefined, + protocolOptions: { + port: config.port, + }, + }); + } + + return CassandraService.instance; + } + + public static async connect(): Promise { + try { + await CassandraService.getClient().connect(); + logger.info("Connected to Cassandra successfully."); + } catch (error) { + logger.error(["Failed to connect to Cassandra:", error as Error]); + process.exit(1); + } + } + + public static async shutdown(): Promise { + if (CassandraService.instance) { + await CassandraService.instance.shutdown(); + logger.info("Cassandra client shut down."); + CassandraService.instance = null; + } + } +} + +export { CassandraService }; diff --git a/tsconfig.json b/tsconfig.json index 68a5a97..7b46a11 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "@/*": ["src/*"], "@config/*": ["config/*"], "@types/*": ["types/*"], - "@helpers/*": ["src/helpers/*"] + "@lib/*": ["src/lib/*"] }, "typeRoots": ["./src/types", "./node_modules/@types"], // Enable latest features diff --git a/types/config.d.ts b/types/config.d.ts index 57584ed..d826680 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -3,3 +3,14 @@ type Environment = { host: string; development: boolean; }; + +type CassandraConfig = { + host: string; + port: number; + keyspace: string; + username: string; + password: string; + datacenter: string; + contactPoints: string[]; + authEnabled: boolean; +};