# PicList-Core Multi-Config Architecture for Uploaders: How ConfigManager Handles Multiple Accounts

> Explore PicList-Core's multi-config architecture. Learn how ConfigManager manages multiple uploader accounts with backward compatibility and seamless migration for enhanced functionality.

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

---

**PicList-Core enables multiple independent configurations per uploader through the `ConfigManager` class, which automatically migrates legacy single-config data and synchronizes default settings with legacy `picBed.*` keys for seamless backward compatibility.**

Managing multiple API keys and endpoints for the same image hosting service requires a flexible configuration system. The `kuingsmile/piclist-core` repository implements a sophisticated multi-config architecture that allows users to store several accounts for each uploader while maintaining full compatibility with existing plugins. This architecture centers on the **`ConfigManager`** utility in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts), which handles data migration, CRUD operations, and transparent synchronization between the new multi-config structure and legacy configuration keys.

## Core Architecture Components

The multi-config system consists of three integrated components working within the PicGo core context. The **`PicGo` core** instantiates `ConfigManager` at lines 88-90 of [`src/core/PicGo.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/core/PicGo.ts), injecting it into the plugin context as `ctx.configManager`. The **`ConfigManager`** class serves as the central engine for configuration persistence, implementing migration logic and legacy key synchronization. Finally, **uploader plugins** consume configurations through the traditional `ctx.getConfig('picBed.<uploader>')` interface, remaining completely unaware of the underlying multi-config complexity.

## Configuration Data Model and Storage Structure

The global configuration JSON utilizes a dual-tier structure that separates multi-config data from legacy compatibility fields. The **`uploader`** object contains nested entries for each uploader type (e.g., `smms`, `imgur`), each maintaining a **`configList`** array of configuration objects and a **`defaultId`** string pointing to the active configuration.

Each entry in `configList` contains a unique **`_id`**, a **`_configName`** for display purposes, and uploader-specific credentials such as tokens or endpoints. Simultaneously, the legacy **`picBed`** object maintains single-config entries that mirror the current default, ensuring existing plugins continue functioning without modification.

```json
{
  "uploader": {
    "smms": {
      "configList": [
        {
          "_id": "c1a2b3...",
          "_configName": "My SM.MS Account",
          "token": "xxxx"
        }
      ],
      "defaultId": "c1a2b3..."
    }
  },
  "picBed": {
    "smms": {
      "_id": "c1a2b3...",
      "_configName": "My SM.MS Account",
      "token": "xxxx"
    }
  }
}

```

## Automatic Migration from Legacy Single-Config

When `ConfigManager` first accesses an uploader's configuration, it invokes **`migrateToMultiConfig`** (lines 12-53 of [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts)) to transform legacy data transparently. This method checks for the existence of a `uploader.<name>` entry; if absent and a legacy `picBed.<name>` configuration exists, it wraps that single object into a `configList` array with an auto-generated `_id` and `_configName`.

The migration process preserves existing user data by reading the complete configuration via `this.ctx.getConfig<IConfig>()`, detecting legacy entries when the new structure is absent, and persisting the transformed structure while maintaining the original legacy key for immediate compatibility. This ensures zero-downtime upgrades for existing installations.

## CRUD Operations and Configuration Management

The `ConfigManager` exposes specific methods for managing uploader configurations, each invoking `migrateToMultiConfig` first to ensure data shape consistency.

**`addUploaderConfig`** (lines 74-101): Generates a new `_id`, pushes the configuration to `configList`, and automatically sets the first configuration as the default. It triggers `syncConfigToPicBed` to update legacy keys immediately.

**`updateUploaderConfig`** (lines 104-132): Locates the target by `_id`, performs a shallow merge of changes, updates timestamps, and resyncs to legacy keys if the modified configuration is the current default.

**`deleteUploaderConfig`** (lines 134-172): Implements safety constraints by preventing deletion of the only remaining configuration or the default when other configs exist. Removes the legacy `picBed` entry when the list becomes empty.

**`setDefaultConfig`** (lines 174-194): Updates the `defaultId` pointer and immediately synchronizes the newly selected default to the legacy `picBed.<name>` key.

**`getAllUploaderConfigs`**: Returns the complete `configList` array for a given uploader, enabling UI components to display available options.

## Legacy Synchronization and Plugin Integration

To maintain backward compatibility without modifying existing uploader plugins, `ConfigManager` implements **`syncConfigToPicBed`** (lines 96-100). This private method writes the current default configuration back to `picBed.<uploaderName>` whenever the default changes through add, update, delete, or set default operations.

Uploader plugins retrieve effective configurations using the established interface:

```typescript
const config = ctx.getConfig('picBed.smms');
const token = config.token;

```

Because `ConfigManager` guarantees that `picBed.<uploaderName>` always contains the synchronized default configuration, plugins in `src/plugins/uploader/*.ts` require no awareness of the underlying multi-config infrastructure. The complexity remains encapsulated within `ConfigManager` while the plugin ecosystem continues using established patterns from the PicGo framework.

## End-to-End Configuration Workflow

Consider a user adding a second SM.MS account to their PicList-Core installation:

1. The application calls `ctx.configManager.addUploaderConfig('smms', 'Work Account', {token: 'new-token'})`
2. `ConfigManager` generates a unique `_id`, appends the configuration to `uploader.smms.configList`, and detects this is an additional configuration
3. The method preserves the existing `defaultId`, leaving the original account active
4. The user subsequently calls `ctx.configManager.setDefaultConfig('smms', 'new-work-id')` to switch contexts
5. `setDefaultConfig` updates `uploader.smms.defaultId` and invokes `syncConfigToPicBed`, writing the work account to `picBed.smms`
6. When the SM.MS uploader plugin executes `ctx.getConfig('picBed.smms')`, it receives the work account credentials automatically

## Summary

- **Centralized Management**: The `ConfigManager` class in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts) encapsulates all multi-config logic, exposed via `ctx.configManager` from the PicGo core at [`src/core/PicGo.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/core/PicGo.ts).
- **Transparent Migration**: Legacy single-config data automatically converts to the new structure via `migrateToMultiConfig`, ensuring zero-downtime upgrades for existing users.
- **Dual Storage Model**: The system maintains both the new `uploader` object with `configList` and `defaultId`, and legacy `picBed` keys synchronized through `syncConfigToPicBed`.
- **Unchanged Plugin Interface**: Uploaders continue reading configurations via `ctx.getConfig('picBed.<name>')`, receiving the current default without requiring code modifications.
- **Safe CRUD Operations**: Built-in constraints prevent deletion of required configurations and automatically handle default assignment for new entries.

## Frequently Asked Questions

### How does PicList-Core maintain backward compatibility when upgrading to multi-config support?

PicList-Core preserves backward compatibility through the `syncConfigToPicBed` method, which continuously mirrors the current default configuration to the legacy `picBed.<uploader>` key. Existing uploader plugins reading `ctx.getConfig('picBed.smms')` receive valid configuration data without requiring updates, while the new `uploader` object stores the complete multi-config dataset internally according to the implementation in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts).

### What data structure does PicList-Core use to store multiple uploader configurations?

The system stores configurations in a global JSON structure containing an `uploader` object with nested entries for each uploader type. Each entry maintains a `configList` array holding all configuration variants with unique `_id` fields and a `defaultId` string referencing the active configuration. The legacy `picBed` object coexists at the root level, containing only the currently selected default for each uploader to ensure plugin compatibility.

### Can I programmatically add new uploader configurations in PicList-Core?

Yes, use the `ctx.configManager.addUploaderConfig(uploaderName, configName, configData)` method available in the `ConfigManager` class. This API generates a unique identifier using timestamp-based generation, appends the configuration to the specified uploader's `configList`, and automatically sets the configuration as default if it's the first entry for that uploader type. The method handles persistence and legacy synchronization automatically without manual JSON manipulation.

### How does switching between multiple accounts work in PicList-Core?

Switching accounts involves calling `ctx.configManager.setDefaultConfig(uploaderName, configId)`, which updates the `defaultId` pointer in the configuration structure. The method then triggers `syncConfigToPicBed` to propagate the selected configuration to the legacy `picBed` key. Subsequent upload operations by the uploader plugin automatically use the newly selected default credentials without requiring application restarts or plugin reinitialization.