All files / src/oauth/storage factory.ts

36.58% Statements 15/41
17.94% Branches 7/39
42.85% Functions 3/7
36.58% Lines 15/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127              75x 75x 75x 75x                             75x   107x   107x             107x         107x 107x     107x       107x 107x                                                                                 75x             75x                                                        
/**
 * Session Storage Factory
 *
 * Creates and configures the appropriate storage backend based on configuration.
 */
 
import { SessionStorageBackend, StorageConfig } from "./types";
import { MemoryStorageBackend } from "./memory";
import { FileStorageBackend } from "./file";
import { PostgreSQLStorageBackend } from "./postgresql";
import { logger } from "../../logger";
 
/**
 * Create a storage backend based on configuration
 *
 * Configuration can come from:
 * 1. Explicit StorageConfig object
 * 2. Environment variables
 *
 * Environment variables:
 * - OAUTH_STORAGE_TYPE: "memory" | "file" | "postgresql" (default: "memory")
 * - OAUTH_STORAGE_FILE_PATH: Path for file storage (default: ./data/oauth-sessions.json)
 * - OAUTH_STORAGE_POSTGRESQL_URL: PostgreSQL connection string
 * - OAUTH_STORAGE_TABLE_PREFIX: PostgreSQL table prefix (default: "oauth_")
 */
export function createStorageBackend(config?: StorageConfig): SessionStorageBackend {
  // Use config if provided, otherwise read from environment
  const storageType = config?.type ?? getEnvStorageType();
 
  switch (storageType) {
    case "file":
      return createFileBackend(config);
    case "postgresql":
      return createPostgreSQLBackend();
    case "memory":
    default:
      return createMemoryBackend();
  }
}
 
function getEnvStorageType(): "memory" | "file" | "postgresql" {
  const type = process.env.OAUTH_STORAGE_TYPE?.toLowerCase();
  Iif (type === "file" || type === "postgresql") {
    return type;
  }
  return "memory";
}
 
function createMemoryBackend(): MemoryStorageBackend {
  logger.info("Using in-memory session storage (sessions will be lost on restart)");
  return new MemoryStorageBackend();
}
 
function createFileBackend(config?: StorageConfig): FileStorageBackend {
  const filePath =
    config?.file?.path ?? process.env.OAUTH_STORAGE_FILE_PATH ?? "./data/oauth-sessions.json";
 
  const saveInterval =
    config?.file?.saveInterval ?? parseInt(process.env.OAUTH_STORAGE_SAVE_INTERVAL ?? "30000", 10);
 
  const prettyPrint =
    config?.file?.prettyPrint ?? process.env.OAUTH_STORAGE_PRETTY_PRINT === "true";
 
  logger.info({ filePath, saveInterval }, "Using file-based session storage");
 
  return new FileStorageBackend({
    filePath,
    saveInterval,
    prettyPrint,
  });
}
 
function createPostgreSQLBackend(): PostgreSQLStorageBackend {
  // Prisma uses OAUTH_STORAGE_POSTGRESQL_URL or DATABASE_URL from environment
  const connectionString = process.env.OAUTH_STORAGE_POSTGRESQL_URL ?? process.env.DATABASE_URL;
 
  if (!connectionString) {
    throw new Error(
      "PostgreSQL storage requires a connection string. " +
        "Set OAUTH_STORAGE_POSTGRESQL_URL or DATABASE_URL environment variable"
    );
  }
 
  logger.info("Using PostgreSQL session storage (via Prisma)");
 
  return new PostgreSQLStorageBackend();
}
 
/**
 * Get storage type from environment or config
 */
export function getStorageType(config?: StorageConfig): "memory" | "file" | "postgresql" {
  return config?.type ?? getEnvStorageType();
}
 
/**
 * Validate storage configuration
 */
export function validateStorageConfig(config?: StorageConfig): string[] {
  const errors: string[] = [];
  const type = getStorageType(config);
 
  if (type === "postgresql") {
    // Prisma uses OAUTH_STORAGE_POSTGRESQL_URL or DATABASE_URL
    const connectionString = process.env.OAUTH_STORAGE_POSTGRESQL_URL ?? process.env.DATABASE_URL;
 
    if (!connectionString) {
      errors.push(
        "PostgreSQL storage requires OAUTH_STORAGE_POSTGRESQL_URL or DATABASE_URL environment variable"
      );
    }
  }
 
  if (type === "file") {
    const filePath = config?.file?.path ?? process.env.OAUTH_STORAGE_FILE_PATH;
    // File path is optional - defaults to ./data/oauth-sessions.json
    if (filePath) {
      // Basic path validation
      if (filePath.includes("..")) {
        errors.push("File storage path must not contain '..'");
      }
    }
  }
 
  return errors;
}