# How PicList-Core's Configuration Manager Handles and Stores Multiple Configurations

> Discover how PicList-Core's ConfigManager handles and stores multiple configurations in a version-aware file, ensuring backward compatibility and seamless upgrades for your uploaders.

- Repository: [Kuingsmile/piclist-core](https://github.com/kuingsmile/piclist-core)
- Tags: internals
- Published: 2026-03-05

---

**PicList-Core stores multiple uploader configurations in a single, version-aware configuration file using the `ConfigManager` class located in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts), which automatically migrates legacy single-config formats to a new multi-config schema while maintaining backward compatibility through the `picBed` field.**

PicList-Core, an enhanced fork of the PicGo image uploader maintained at `kuingsmile/piclist-core`, introduces robust multi-configuration support that allows users to maintain multiple named profiles per uploader (such as GitHub, SM.MS, or Qiniu) within one unified configuration file. The system handles complex configuration state through a centralized `ConfigManager` utility that provides both programmatic APIs and CLI interfaces for creating, updating, and switching between configuration profiles.

## Data Structure and Type Definitions

The configuration system relies on strict TypeScript interfaces defined in [`src/types/index.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/types/index.ts) to ensure type safety across the multi-config architecture.

**`IConfigItem`** represents a single configuration entry with metadata and uploader-specific fields:

```typescript
export interface IConfigItem {
  _id: string                    // Unique identifier
  _configName: string             // Human-readable name
  _createdAt: number              // Timestamp
  _updatedAt: number            // Timestamp
  [propName: string]: any         // Uploader-specific settings (tokens, paths, etc.)
}

```

**`IUploaderConfigList`** wraps multiple configurations for a specific uploader type and tracks which configuration is currently active:

```typescript
export interface IUploaderConfigList {
  configList: IConfigItem[]      // Array of all configs for this uploader
  defaultId: string               // _id of the currently selected default config
}

```

The top-level configuration interface **`IConfig`** stores the multi-config data under an optional `uploader` property:

```typescript
export interface IConfig {
  // ... other PicGo settings
  uploader?: Record<string, IUploaderConfigList>  // Multi-config structure
}

```

Each uploader (e.g., `smms`, `github`, `qiniu`) maintains an independent entry under `uploader.<uploaderName>` containing a `configList` array and a `defaultId` string pointing to the active configuration.

## Core Configuration Management Methods

The `ConfigManager` class in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts) implements a complete CRUD interface for configuration management. All methods automatically invoke `migrateToMultiConfig` to ensure legacy data compatibility.

### Retrieving Configurations

- **`getCurrentUploaderConfig(uploaderName)`** – Returns the default `IConfigItem` for the specified uploader by matching `_id` against `defaultId` (lines 55-65).
- **`getAllUploaderConfigs(uploaderName)`** – Retrieves the complete `configList` array for an uploader, enabling users to view all available profiles (lines 67-71).

### Modifying Configurations

- **`addUploaderConfig(uploaderName, configName, config)`** – Creates a new `IConfigItem` with auto-generated `_id`, timestamps, and the provided `configName`. If this is the first configuration for the uploader, it automatically becomes the default and syncs to the legacy `picBed` field (lines 74-101).
- **`updateUploaderConfig(uploaderName, id, newConfig)`** – Overwrites an existing configuration matched by `_id` while preserving immutable metadata fields (`_id`, `_createdAt`). If updating the default configuration, changes propagate to the legacy `picBed.<uploader>` location (lines 104-132).
- **`deleteUploaderConfig(uploaderName, id)`** – Removes a configuration from `configList` with safety guards: it prevents deletion of the only remaining config for an active uploader and blocks deletion of the current default (users must switch defaults first). After deletion, it cleans up empty uploader entries (lines 134-173).

### Managing Defaults and Metadata

- **`setDefaultConfig(uploaderName, id)`** – Switches the `defaultId` to the specified configuration `_id` and immediately syncs the selected config to `picBed.<uploader>` to maintain compatibility with legacy plugins (lines 175-193).
- **`renameConfig(uploaderName, id, newName)`** – Updates `_configName` and `_updatedAt` timestamp. If renaming the default configuration, the change also propagates to the legacy sync (lines 209-230).

## Migration Strategy for Legacy Configurations

Older PicGo versions stored single configurations under `picBed.<uploader>`. The `migrateToMultiConfig` method (lines 12-53 in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts)) automatically handles upgrades without breaking existing setups.

The migration process executes when first accessing an uploader:

1. **Detection** – Reads legacy configuration via `this.ctx.getConfig(`picBed.${uploaderName}`)`.
2. **Early Exit** – If the new `uploader` section already contains data for this uploader, the function returns immediately to avoid overwriting.
3. **Structure Creation** – Constructs a new `IUploaderConfigList`:
   - If legacy config exists with data, it becomes the first entry in `configList` with `defaultId` pointing to it.
   - If no legacy data exists, creates an empty list with `defaultId` set to an empty string.
4. **Dual Storage** – Preserves the original config under `picBed.<uploader>` for backward compatibility while saving the new structure under `uploader.<uploader>`.

This dual-write strategy ensures existing plugins reading the old `picBed` shape continue functioning, while new code leverages the multi-config capabilities.

## CLI Interface for Configuration Management

The commander plugin at [`src/plugins/commander/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/plugins/commander/configManager.ts) exposes `ConfigManager` functionality through terminal commands, wrapping the programmatic methods with user-friendly interfaces:

- **`config-list`** – Displays all configurations for an uploader using `getAllUploaderConfigs` and highlights the current default via `getCurrentUploaderConfig`.
- **`config-use`** – Switches active configurations by calling `setDefaultConfig`.
- **`config-remove`** – Deletes configurations through `deleteUploaderConfig` with appropriate validation.
- **`config-rename`** – Updates configuration names using `renameConfig`.

These commands provide direct access to the configuration management system without requiring JavaScript programming knowledge.

## Programmatic Usage Examples

The following TypeScript example demonstrates complete configuration lifecycle management using the `ConfigManager` API:

```typescript
import PicGo from '../core/PicGo'
import { ConfigManager } from '../utils/configManager'

// Initialize PicGo context and ConfigManager
const ctx = new PicGo()
const cfgMgr = new ConfigManager(ctx)

// Add a new GitHub configuration
const githubCfg = cfgMgr.addUploaderConfig('github', 'Work Account', {
  repo: 'company/images',
  branch: 'main',
  token: 'ghp_xxxxxxxxxxxx'
})

// List all GitHub configurations with default indicator
const allConfigs = cfgMgr.getAllUploaderConfigs('github')
const current = cfgMgr.getCurrentUploaderConfig('github')

allConfigs.forEach(cfg => {
  const marker = cfg._id === current?._id ? ' [DEFAULT]' : ''
  console.log(`- ${cfg._configName}${marker} (${cfg._id})`)
})

// Switch to a different configuration
if (allConfigs.length > 1) {
  cfgMgr.setDefaultConfig('github', allConfigs[1]._id)
  console.log(`Switched default to: ${allConfigs[1]._configName}`)
}

// Update configuration details
cfgMgr.updateUploaderConfig('github', githubCfg._id, {
  token: 'ghp_newtoken_value',
  _configName: githubCfg._configName  // Preserve original name
})

// Safely delete a non-default configuration
const toDelete = allConfigs.find(c => c._id !== current?._id)
if (toDelete) {
  cfgMgr.deleteUploaderConfig('github', toDelete._id)
}

```

All methods internally trigger `migrateToMultiConfig`, ensuring seamless upgrades from legacy single-config formats during any operation.

## Summary

- PicList-Core stores multiple uploader configurations in a unified file structure under the `uploader` property, with each uploader maintaining a `configList` array and a `defaultId` pointer.
- The `ConfigManager` class in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts) provides atomic CRUD operations, default management, and automatic migration from legacy `picBed` formats.
- **Backward compatibility** is maintained through dual-write synchronization to the legacy `picBed.<uploader>` field, ensuring existing plugins continue functioning.
- Configurations include immutable metadata (`_id`, `_createdAt`) and mutable display names (`_configName`), with safety guards preventing deletion of the last or default configuration.
- Both programmatic APIs and CLI commands (via [`src/plugins/commander/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/plugins/commander/configManager.ts)) provide full access to configuration management capabilities.

## Frequently Asked Questions

### Where does PicList-Core physically store the configuration file?

PicList-Core utilizes PicGo's standard configuration storage mechanism, typically reading from and writing to the user's home directory configuration file (the exact path depends on the operating system and PicGo's config resolution). The `ConfigManager` accesses this through the PicGo context (`this.ctx`), treating it as a single JSON file containing both the legacy `picBed` structure and the new `uploader` multi-config structure.

### How does the migration from single-config to multi-config work?

When the `ConfigManager` first accesses an uploader, it automatically invokes `migrateToMultiConfig` (lines 12-53). This method detects legacy data stored under `picBed.<uploader>` and transforms it into the new `uploader.<uploader>` structure with a `configList` containing the legacy config as the first entry. The original `picBed` data is preserved for backward compatibility, while the new structure enables multiple configurations.

### Can I use multiple configurations simultaneously for different upload operations?

No, PicList-Core maintains a single **default** configuration per uploader at any given time, identified by `defaultId`. While you can store multiple configurations in `configList`, active operations use whichever configuration is currently set as default. You must switch defaults using `setDefaultConfig` (or the `config-use` CLI command) to change which configuration applies to subsequent uploads.

### Is it safe to delete the legacy `picBed` configuration entries?

Deleting legacy `picBed` entries is **not recommended** because `ConfigManager` relies on this field for backward compatibility synchronization. The private `syncConfigToPicBed` method (lines 196-200) continuously updates `picBed.<uploader>` to match the current default configuration, ensuring that legacy plugins and external tools reading the old format receive the correct active settings. Removing this field would break compatibility with standard PicGo plugins.