Files
optima/src/constants.ts
T
HoloPanio 6d935e7180 feat: Redis opportunity cache, CW API retry/logging, adaptive TTLs
- Add Redis-backed opportunity cache with background refresh (30s interval)
- Fix concurrency bug: use lazy thunks instead of eager promises for batching
- Add withCwRetry utility with exponential backoff for transient CW errors
- Add adaptive TTL algorithms (primary, sub-resource, products) based on opportunity activity
- Add include query param on GET /sales/opportunities/:id (notes,contacts,products)
- Add opt-in CW API logger (LOG_CW_API env var) with timestamped files in cw-api-logs/
- Add debug-scripts/analyze-cw-calls.py for API call analysis
- Add computeSubResourceCacheTTL and computeProductsCacheTTL algorithms with tests
- Increase CW API timeout from 15s to 30s
- Unblock cache refresh from startup chain (remove await)
- Prioritize recently updated opportunities in refresh cycle
- Add CACHING.md documentation
- Update API_ROUTES.md with caching details and include param
- Update copilot instructions to require CACHING.md sync
- Add dev:log script for CW API call logging during development
2026-03-02 23:23:24 -06:00

101 lines
3.1 KiB
TypeScript

import { readFileSync } from "fs";
import { PrismaPg } from "@prisma/adapter-pg";
import { Prisma, PrismaClient } from "../generated/prisma/client";
import * as msal from "@azure/msal-node";
import { Server } from "socket.io";
import { Server as Engine } from "@socket.io/bun-engine";
import axios from "axios";
import { UnifiClient } from "./modules/unifi-api/UnifiClient";
import { attachCwApiLogger } from "./modules/cw-utils/cwApiLogger";
import Redis from "ioredis";
const connectionString = `${process.env.DATABASE_URL}`;
const adapter = new PrismaPg({ connectionString });
interface EnvKey {
PORT: number;
}
// ENV CONSTANTS
export const PORT = process.env.PORT;
export const API_BASE_URL =
process.env.API_BASE_URL || `http://localhost:${PORT || 3000}`;
export const prisma = new PrismaClient({ adapter });
// Redis Client
export const redis = new Redis(process.env.REDIS_URL!);
export const sessionDuration = 30 * 24 * 60 * 60000;
export const accessTokenDuration = "10min";
export const refreshTokenDuration = "30d";
const isProduction = process.env.NODE_ENV === "production";
const readKeyFile = (path: string) => readFileSync(path).toString();
export const accessTokenPrivateKey = isProduction
? process.env.ACCESS_TOKEN_PRIVATE_KEY!
: readKeyFile(`.accessToken.key`);
export const refreshTokenPrivateKey = isProduction
? process.env.REFRESH_TOKEN_PRIVATE_KEY!
: readKeyFile(`.refreshToken.key`);
export const permissionsPrivateKey = isProduction
? process.env.PERMISSIONS_PRIVATE_KEY!
: readKeyFile(`.permissions.key`);
export const secureValuesPrivateKey = isProduction
? process.env.SECURE_VALUES_PRIVATE_KEY!
: readKeyFile(`.secureValues.key`);
export const secureValuesPublicKey = isProduction
? process.env.SECURE_VALUES_PUBLIC_KEY!
: readKeyFile(`public-keys/.secureValues.pub`);
// Microsoft Auth Constants
const msalConfig: msal.Configuration = {
auth: {
clientId: process.env.MICROSOFT_CLIENT_ID!,
authority: `https://login.microsoftonline.com/${process.env.MICROSOFT_TENANT_ID!}`,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
},
};
// MSAL Client Instance
export const msalClient = new msal.ConfidentialClientApplication(msalConfig);
// Socket.io
const io = new Server();
const authIO = io.of("/auth_callback");
const engine = new Engine();
io.bind(engine);
export { io, engine };
// Connectwise API Client
const connectWiseApi = axios.create({
baseURL: `https://ttscw.totaltech.net/v4_6_release/apis/3.0/`,
headers: {
Authorization: `Basic ${process.env.CW_BASIC_TOKEN}`,
clientId: `${process.env.CW_CLIENT_ID}`,
"Content-Type": "application/json",
},
timeout: 30_000, // 30 s — prevents indefinite hangs on CW API
});
attachCwApiLogger(connectWiseApi);
export { connectWiseApi };
// Unifi API Constants
export const unifiControllerBaseUrl =
process.env.UNIFI_CONTROLLER_BASE_URL || "https://unifi.example.com";
export const unifiSite = process.env.UNIFI_SITE || "default";
export const unifiUsername = process.env.UNIFI_USERNAME || "admin";
export const unifiPassword = process.env.UNIFI_PASSWORD || "";
export const unifi = new UnifiClient(unifiControllerBaseUrl);