New card context menu entry 'Restore Downloaded Version' discards local edits made via the write-back flow: - Container artifacts (.zip/images) were never modified — restore deletes the derived .workflow.json sidecar and re-extracts from the pristine archive, then re-syncs analysis metadata. Purely local, works for self-hosted workflows too. - Bare .json artifacts are rewritten in place by write-backs, so restore re-downloads the recorded CivitAI version file (API-key auth, temp file + workflow validation + SHA256 check before the atomic replace). The menu entry is disabled for local-only .json workflows that have no restore source. On success the card is refreshed in place with the shared 'Updated' cue.
264 lines
9.7 KiB
JavaScript
264 lines
9.7 KiB
JavaScript
import { state } from '../state/index.js';
|
|
|
|
/**
|
|
* API Configuration
|
|
* Centralized configuration for all model types and their endpoints
|
|
*/
|
|
|
|
// Model type definitions
|
|
export const MODEL_TYPES = {
|
|
LORA: 'loras',
|
|
CHECKPOINT: 'checkpoints',
|
|
EMBEDDING: 'embeddings',
|
|
OTHER: 'other',
|
|
WORKFLOW: 'workflows'
|
|
};
|
|
|
|
// Base API configuration for each model type
|
|
export const MODEL_CONFIG = {
|
|
[MODEL_TYPES.LORA]: {
|
|
displayName: 'LoRA',
|
|
singularName: 'lora',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: true,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'loras.html'
|
|
},
|
|
[MODEL_TYPES.CHECKPOINT]: {
|
|
displayName: 'Checkpoint',
|
|
singularName: 'checkpoint',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: false,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'checkpoints.html'
|
|
},
|
|
[MODEL_TYPES.EMBEDDING]: {
|
|
displayName: 'Embedding',
|
|
singularName: 'embedding',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: true,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'embeddings.html'
|
|
},
|
|
[MODEL_TYPES.OTHER]: {
|
|
displayName: 'Other Model',
|
|
singularName: 'other',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: false,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'other.html'
|
|
},
|
|
[MODEL_TYPES.WORKFLOW]: {
|
|
displayName: 'Workflow',
|
|
singularName: 'workflow',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: false,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'workflows.html'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generate API endpoints for a given model type
|
|
* @param {string} modelType - The model type (e.g., 'loras', 'checkpoints')
|
|
* @returns {Object} Object containing all API endpoints for the model type
|
|
*/
|
|
export function getApiEndpoints(modelType) {
|
|
return {
|
|
// Base CRUD operations
|
|
list: `/api/lm/${modelType}/list`,
|
|
excluded: `/api/lm/${modelType}/excluded`,
|
|
delete: `/api/lm/${modelType}/delete`,
|
|
exclude: `/api/lm/${modelType}/exclude`,
|
|
unexclude: `/api/lm/${modelType}/unexclude`,
|
|
rename: `/api/lm/${modelType}/rename`,
|
|
save: `/api/lm/${modelType}/save-metadata`,
|
|
cancelTask: `/api/lm/${modelType}/cancel-task`,
|
|
|
|
// Bulk operations
|
|
bulkDelete: `/api/lm/${modelType}/bulk-delete`,
|
|
|
|
// Tag operations
|
|
addTags: `/api/lm/${modelType}/add-tags`,
|
|
|
|
// Move operations (now common for all model types that support move)
|
|
moveModel: `/api/lm/${modelType}/move_model`,
|
|
moveBulk: `/api/lm/${modelType}/move_models_bulk`,
|
|
createFolder: `/api/lm/${modelType}/create-folder`,
|
|
deleteFolder: `/api/lm/${modelType}/delete-folder`,
|
|
// Drops the sidebar records of a folder that no longer exists on disk:
|
|
// the only cleanup left for a node whose directory was moved or deleted
|
|
// outside LoRA Manager.
|
|
forgetFolder: `/api/lm/${modelType}/forget-folder`,
|
|
renameFolder: `/api/lm/${modelType}/rename-folder`,
|
|
// Resolves a tree-relative folder onto the root(s) that hold it: the
|
|
// unified folder tree merges every root, so a node does not carry its
|
|
// own absolute path.
|
|
resolveFolder: `/api/lm/${modelType}/resolve-folder`,
|
|
|
|
// CivitAI integration
|
|
fetchCivitai: `/api/lm/${modelType}/fetch-civitai`,
|
|
fetchAllCivitai: `/api/lm/${modelType}/fetch-all-civitai`,
|
|
relinkCivitai: `/api/lm/${modelType}/relink-civitai`,
|
|
civitaiModelByVersion: `/api/lm/${modelType}/civitai/model/version`,
|
|
civitaiVersions: `/api/lm/${modelType}/civitai/versions`,
|
|
refreshUpdates: `/api/lm/${modelType}/updates/refresh`,
|
|
fetchMissingLicenses: `/api/lm/${modelType}/updates/fetch-missing-license`,
|
|
modelUpdateStatus: `/api/lm/${modelType}/updates/status`,
|
|
modelUpdateVersions: `/api/lm/${modelType}/updates/versions`,
|
|
ignoreModelUpdate: `/api/lm/${modelType}/updates/ignore`,
|
|
ignoreVersionUpdate: `/api/lm/${modelType}/updates/ignore-version`,
|
|
|
|
// Price alerts are app-wide (all model types in one query), so they are
|
|
// not part of this per-type endpoint map; see UpdateService.js.
|
|
|
|
// Preview management
|
|
replacePreview: `/api/lm/${modelType}/replace-preview`,
|
|
setPreviewFromUrl: `/api/lm/${modelType}/set-preview-from-url`,
|
|
|
|
// Query operations
|
|
scan: `/api/lm/${modelType}/scan`,
|
|
topTags: `/api/lm/${modelType}/top-tags`,
|
|
searchTags: `/api/lm/${modelType}/search-tags`,
|
|
baseModels: `/api/lm/${modelType}/base-models`,
|
|
roots: `/api/lm/${modelType}/roots`,
|
|
folders: `/api/lm/${modelType}/folders`,
|
|
folderTree: `/api/lm/${modelType}/folder-tree`,
|
|
unifiedFolderTree: `/api/lm/${modelType}/unified-folder-tree`,
|
|
duplicates: `/api/lm/${modelType}/find-duplicates`,
|
|
conflicts: `/api/lm/${modelType}/find-filename-conflicts`,
|
|
verify: `/api/lm/${modelType}/verify-duplicates`,
|
|
metadata: `/api/lm/${modelType}/metadata`,
|
|
modelDescription: `/api/lm/${modelType}/model-description`,
|
|
|
|
// Auto-organize operations
|
|
autoOrganize: `/api/lm/${modelType}/auto-organize`,
|
|
autoOrganizeProgress: `/api/lm/${modelType}/auto-organize-progress`,
|
|
|
|
// Filename template operations
|
|
applyFilenameTemplate: `/api/lm/${modelType}/apply-filename-template`,
|
|
|
|
// Model-specific endpoints (will be merged with specific configs)
|
|
specific: {}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Model-specific endpoint configurations
|
|
*/
|
|
export const MODEL_SPECIFIC_ENDPOINTS = {
|
|
[MODEL_TYPES.LORA]: {
|
|
letterCounts: `/api/lm/${MODEL_TYPES.LORA}/letter-counts`,
|
|
notes: `/api/lm/${MODEL_TYPES.LORA}/get-notes`,
|
|
triggerWords: `/api/lm/${MODEL_TYPES.LORA}/get-trigger-words`,
|
|
previewUrl: `/api/lm/${MODEL_TYPES.LORA}/preview-url`,
|
|
civitaiUrl: `/api/lm/${MODEL_TYPES.LORA}/civitai-url`,
|
|
metadata: `/api/lm/${MODEL_TYPES.LORA}/metadata`,
|
|
getTriggerWordsPost: `/api/lm/${MODEL_TYPES.LORA}/get_trigger_words`,
|
|
civitaiModelByHash: `/api/lm/${MODEL_TYPES.LORA}/civitai/model/hash`,
|
|
},
|
|
[MODEL_TYPES.CHECKPOINT]: {
|
|
info: `/api/lm/${MODEL_TYPES.CHECKPOINT}/info`,
|
|
checkpoints_roots: `/api/lm/${MODEL_TYPES.CHECKPOINT}/checkpoints_roots`,
|
|
unet_roots: `/api/lm/${MODEL_TYPES.CHECKPOINT}/unet_roots`,
|
|
metadata: `/api/lm/${MODEL_TYPES.CHECKPOINT}/metadata`,
|
|
},
|
|
[MODEL_TYPES.EMBEDDING]: {
|
|
metadata: `/api/lm/${MODEL_TYPES.EMBEDDING}/metadata`,
|
|
},
|
|
[MODEL_TYPES.OTHER]: {
|
|
metadata: `/api/lm/${MODEL_TYPES.OTHER}/metadata`,
|
|
roots_by_subtype: `/api/lm/${MODEL_TYPES.OTHER}/roots_by_subtype`,
|
|
},
|
|
[MODEL_TYPES.WORKFLOW]: {
|
|
metadata: `/api/lm/${MODEL_TYPES.WORKFLOW}/metadata`,
|
|
sendToComfy: `/api/lm/${MODEL_TYPES.WORKFLOW}/send-to-comfy`,
|
|
content: `/api/lm/${MODEL_TYPES.WORKFLOW}/content`,
|
|
comfyuiState: `/api/lm/${MODEL_TYPES.WORKFLOW}/comfyui-state`,
|
|
updateFromComfy: `/api/lm/${MODEL_TYPES.WORKFLOW}/update-from-comfy`,
|
|
restore: `/api/lm/${MODEL_TYPES.WORKFLOW}/restore`,
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get complete API configuration for a model type
|
|
* @param {string} modelType - The model type
|
|
* @returns {Object} Complete API configuration
|
|
*/
|
|
export function getCompleteApiConfig(modelType) {
|
|
const baseEndpoints = getApiEndpoints(modelType);
|
|
const specificEndpoints = MODEL_SPECIFIC_ENDPOINTS[modelType] || {};
|
|
const config = MODEL_CONFIG[modelType];
|
|
|
|
return {
|
|
modelType,
|
|
config,
|
|
endpoints: {
|
|
...baseEndpoints,
|
|
specific: specificEndpoints
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Validate if a model type is supported
|
|
* @param {string} modelType - The model type to validate
|
|
* @returns {boolean} True if valid, false otherwise
|
|
*/
|
|
export function isValidModelType(modelType) {
|
|
return Object.values(MODEL_TYPES).includes(modelType);
|
|
}
|
|
|
|
/**
|
|
* Get model type from current page or explicit parameter
|
|
* @param {string} [explicitType] - Explicitly provided model type
|
|
* @returns {string} The model type
|
|
*/
|
|
export function getCurrentModelType(explicitType = null) {
|
|
if (explicitType && isValidModelType(explicitType)) {
|
|
return explicitType;
|
|
}
|
|
|
|
return state.currentPageType || MODEL_TYPES.LORA;
|
|
}
|
|
|
|
// Download API endpoints (shared across all model types)
|
|
export const DOWNLOAD_ENDPOINTS = {
|
|
download: '/api/lm/download-model',
|
|
downloadGet: '/api/lm/download-model-get',
|
|
cancelGet: '/api/lm/cancel-download-get',
|
|
progress: '/api/lm/download-progress',
|
|
routing: '/api/lm/download/routing',
|
|
exampleImages: '/api/lm/force-download-example-images', // Re-process example images ignoring previous status
|
|
exampleImagesMissing: '/api/lm/download-example-images' // Download only missing example images
|
|
};
|
|
|
|
// External model source endpoints (Hugging Face / ModelScope).
|
|
// The hf-* paths are the historical names, kept as server-side aliases.
|
|
export const MODEL_SOURCE_ENDPOINTS = {
|
|
repoFiles: '/api/lm/model-source-files',
|
|
download: '/api/lm/download-model-source',
|
|
sources: '/api/lm/model-sources',
|
|
};
|
|
|
|
/** @deprecated use MODEL_SOURCE_ENDPOINTS */
|
|
export const HF_ENDPOINTS = {
|
|
repoFiles: MODEL_SOURCE_ENDPOINTS.repoFiles,
|
|
download: MODEL_SOURCE_ENDPOINTS.download,
|
|
};
|
|
|
|
// WebSocket endpoints
|
|
export const WS_ENDPOINTS = {
|
|
fetchProgress: '/ws/fetch-progress'
|
|
};
|