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

PicList-Core stores multiple uploader configurations in a single, version-aware configuration file using the ConfigManager class located in 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 to ensure type safety across the multi-config architecture.

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

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:

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:

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 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) 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 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:

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 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) 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →