Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ STELLAR_KEY=your_stellar_key_here
# Server Configuration
PORT=3000
API_KEY=the_secret_api_key_here
REDIS_URL=redis://localhost:6379

# API Keys (optional - some rate sources may require)
OPENEXCHANGE_RATES_API_KEY=your_api_key_here
Expand Down
129 changes: 107 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"helmet": "^8.1.0",
"morgan": "^1.10.1",
"pg": "^8.20.0",
"redis": "^5.11.0",
"socket.io": "^4.8.3",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
Expand Down
8 changes: 6 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import priceUpdatesRouter from "./routes/priceUpdates";
import assetsRouter from "./routes/assets";
import statusRouter from "./routes/status";
import prisma from "./lib/prisma";
import { disconnectRedis } from "./lib/redis";
import { initSocket } from "./lib/socket";
import { SorobanEventListener } from "./services/sorobanEventListener";
import { specs } from "./lib/swagger";
Expand Down Expand Up @@ -288,7 +289,7 @@ app.use(
err: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction,
_next: express.NextFunction,
) => {
console.error("Unhandled error:", err);
res.status(500).json({
Expand Down Expand Up @@ -329,7 +330,7 @@ const closeHttpServer = (): Promise<void> =>
});
});

const shutdown = async (signal: NodeJS.Signals): Promise<void> => {
const shutdown = async (signal: "SIGINT" | "SIGTERM"): Promise<void> => {
if (isShuttingDown) {
console.log(
`Shutdown already in progress. Received duplicate ${signal} signal.`,
Expand All @@ -351,6 +352,9 @@ const shutdown = async (signal: NodeJS.Signals): Promise<void> => {
await prisma.$disconnect();
console.log("Database connections closed cleanly.");

await disconnectRedis();
console.log("Redis connections closed cleanly.");

process.exit(0);
} catch (error) {
console.error("Graceful shutdown failed:", error);
Expand Down
51 changes: 51 additions & 0 deletions src/lib/redis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createClient, type RedisClientType } from "redis";
import dotenv from "dotenv";

dotenv.config();

const redisUrl = process.env.REDIS_URL;

let redisClient: RedisClientType | null = null;

if (redisUrl) {
redisClient = createClient({
url: redisUrl,
socket: {
connectTimeout: 3000,
reconnectStrategy: (retries) => Math.min(retries * 50, 1000),
},
});

redisClient.on("error", (error) => {
console.error("[Redis] Client error:", error);
});

redisClient.on("connect", () => {
console.info("[Redis] Connected");
});

redisClient.on("reconnecting", () => {
console.warn("[Redis] Reconnecting...");
});

void redisClient.connect().catch((error) => {
console.error(
"[Redis] Failed to connect. Continuing without Redis cache:",
error,
);
});
} else {
console.info("[Redis] REDIS_URL not set. Redis caching is disabled.");
}

export function getRedisClient(): RedisClientType | null {
return redisClient;
}

export async function disconnectRedis(): Promise<void> {
if (!redisClient || !redisClient.isOpen) {
return;
}

await redisClient.quit();
}
Loading
Loading