All files / src/oauth/storage factory.ts

37.5% Statements 15/40
18.91% Branches 7/37
42.85% Functions 3/7
37.5% Lines 15/40

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              91x 91x 91x 91x                             91x   146x   146x             146x         146x 146x     146x       146x 146x                                                                         91x             91x                                                        
/**
 * 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 { logInfo } 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 {
  logInfo("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);
 
  logInfo("Using file-based session storage", { filePath, saveInterval });
 
  return new FileStorageBackend({
    filePath,
    saveInterval,
  });
}
 
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"
    );
  }
 
  logInfo("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;
}