config.ts•3.87 kB
import { z } from "zod";
import fs from "fs/promises";
import path from "path";
// Define auth configuration schema
const AuthConfigSchema = z.object({
  type: z.enum(["basic", "bearer", "apiKey", "oauth2"]),
  token: z.string().optional(),
  username: z.string().optional(),
  password: z.string().optional(),
  apiKey: z.string().optional(),
  apiKeyName: z.string().optional(),
  apiKeyIn: z.enum(["header", "query"]).optional(),
});
// Define the configuration schema
export const ConfigSchema = z
  .object({
    api: z.object({
      type: z.enum(["openapi", "postman"]),
      openapi: z
        .object({
          url: z.string().url(),
          apiBaseUrl: z.string().url(),
          defaultAuth: AuthConfigSchema.optional(),
        })
        .optional(),
      postman: z
        .object({
          collectionUrl: z.string().url().optional(),
          collectionFile: z.string().optional(),
          environmentUrl: z.string().url().optional(),
          environmentFile: z.string().optional(),
          defaultAuth: AuthConfigSchema.optional(),
        })
        .optional(),
    }),
    // Keep legacy swagger config for backward compatibility
    swagger: z
      .object({
        url: z.string().url(),
        apiBaseUrl: z.string().url(),
        defaultAuth: AuthConfigSchema.optional(),
      })
      .optional(),
    log: z.object({
      level: z.enum(["debug", "info", "warn", "error"]),
    }),
    server: z.object({
      port: z.number().default(3000),
      host: z.string().default("0.0.0.0"),
    }),
  })
  .refine(
    (data) => {
      // Ensure we have either the new api config or legacy swagger config
      if (data.api.type === "openapi" && !data.api.openapi && !data.swagger) {
        return false;
      }
      if (data.api.type === "postman" && !data.api.postman) {
        return false;
      }
      return true;
    },
    {
      message:
        "Configuration must include appropriate API settings based on type",
    }
  );
export type Config = z.infer<typeof ConfigSchema>;
const defaultConfig: Config = {
  api: {
    type: "openapi",
    openapi: {
      url: "https://petstore.swagger.io/v2/swagger.json",
      apiBaseUrl: "https://petstore.swagger.io/v2",
      defaultAuth: {
        type: "apiKey",
        apiKey: "special-key",
        apiKeyName: "api_key",
        apiKeyIn: "header",
      },
    },
  },
  swagger: {
    url: "https://petstore.swagger.io/v2/swagger.json",
    apiBaseUrl: "https://petstore.swagger.io/v2",
    defaultAuth: {
      type: "apiKey",
      apiKey: "special-key",
      apiKeyName: "api_key",
      apiKeyIn: "header",
    },
  },
  log: {
    level: "info",
  },
  server: {
    port: 3000,
    host: "0.0.0.0",
  },
};
export async function loadConfig(configPath?: string): Promise<Config> {
  try {
    // If no config path provided, create default config file
    if (!configPath) {
      configPath = path.join(process.cwd(), "config.json");
      // Check if config file exists, if not create it with default values
      try {
        await fs.access(configPath);
      } catch {
        await fs.writeFile(configPath, JSON.stringify(defaultConfig, null, 2));
        console.log(`Created default configuration file at ${configPath}`);
      }
    }
    const configFile = await fs.readFile(configPath, "utf-8");
    const config = JSON.parse(configFile);
    // Handle legacy config migration
    if (config.swagger && !config.api) {
      config.api = {
        type: "openapi",
        openapi: config.swagger,
      };
    }
    return ConfigSchema.parse(config);
  } catch (error) {
    if (error instanceof z.ZodError) {
      console.error("Invalid configuration:", error.errors);
    } else {
      console.error("Error loading configuration:", error);
    }
    console.log("Using default configuration");
    return defaultConfig;
  }
}