| 1 | import fs from 'node:fs'; |
| 2 | import path from 'node:path'; |
| 3 | |
| 4 | import express from 'express'; |
| 5 | import sanitize from 'sanitize-filename'; |
| 6 | import { sync as writeFileAtomicSync } from 'write-file-atomic'; |
| 7 | |
| 8 | import { getDefaultPresetFile, getDefaultPresets } from './content-manager.js'; |
| 9 | |
| 10 | /** |
| 11 | * Gets the folder and extension for the preset settings based on the API source ID. |
| 12 | * @param {string} apiId API source ID |
| 13 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 14 | * @returns {{folder: string?, extension: string?}} Object containing the folder and extension for the preset settings |
| 15 | */ |
| 16 | function getPresetSettingsByAPI(apiId, directories) { |
| 17 | switch (apiId) { |
| 18 | case 'kobold': |
| 19 | case 'koboldhorde': |
| 20 | return { folder: directories.koboldAI_Settings, extension: '.json' }; |
| 21 | case 'novel': |
| 22 | return { folder: directories.novelAI_Settings, extension: '.json' }; |
| 23 | case 'textgenerationwebui': |
| 24 | return { folder: directories.textGen_Settings, extension: '.json' }; |
| 25 | case 'openai': |
| 26 | return { folder: directories.openAI_Settings, extension: '.json' }; |
| 27 | case 'instruct': |
| 28 | return { folder: directories.instruct, extension: '.json' }; |
| 29 | case 'context': |
| 30 | return { folder: directories.context, extension: '.json' }; |
| 31 | case 'sysprompt': |
| 32 | return { folder: directories.sysprompt, extension: '.json' }; |
| 33 | case 'reasoning': |
| 34 | return { folder: directories.reasoning, extension: '.json' }; |
| 35 | default: |
| 36 | return { folder: null, extension: null }; |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | export const router = express.Router(); |
| 41 | |
| 42 | router.post('/save', function (request, response) { |
| 43 | const name = sanitize(request.body.name); |
| 44 | if (!request.body.preset || !name) { |
| 45 | return response.sendStatus(400); |
| 46 | } |
| 47 | |
| 48 | const settings = getPresetSettingsByAPI(request.body.apiId, request.user.directories); |
| 49 | const filename = name + settings.extension; |
| 50 | |
| 51 | if (!settings.folder) { |
| 52 | return response.sendStatus(400); |
| 53 | } |
| 54 | |
| 55 | const fullpath = path.join(settings.folder, filename); |
| 56 | writeFileAtomicSync(fullpath, JSON.stringify(request.body.preset, null, 4), 'utf-8'); |
| 57 | return response.send({ name }); |
| 58 | }); |
| 59 | |
| 60 | router.post('/delete', function (request, response) { |
| 61 | const name = sanitize(request.body.name); |
| 62 | if (!name) { |
| 63 | return response.sendStatus(400); |
| 64 | } |
| 65 | |
| 66 | const settings = getPresetSettingsByAPI(request.body.apiId, request.user.directories); |
| 67 | const filename = name + settings.extension; |
| 68 | |
| 69 | if (!settings.folder) { |
| 70 | return response.sendStatus(400); |
| 71 | } |
| 72 | |
| 73 | const fullpath = path.join(settings.folder, filename); |
| 74 | |
| 75 | if (fs.existsSync(fullpath)) { |
| 76 | fs.unlinkSync(fullpath); |
| 77 | return response.sendStatus(200); |
| 78 | } else { |
| 79 | return response.sendStatus(404); |
| 80 | } |
| 81 | }); |
| 82 | |
| 83 | router.post('/restore', function (request, response) { |
| 84 | try { |
| 85 | const settings = getPresetSettingsByAPI(request.body.apiId, request.user.directories); |
| 86 | const name = sanitize(request.body.name); |
| 87 | const defaultPresets = getDefaultPresets(request.user.directories); |
| 88 | |
| 89 | const defaultPreset = defaultPresets.find(p => p.name === name && p.folder === settings.folder); |
| 90 | |
| 91 | const result = { isDefault: false, preset: {} }; |
| 92 | |
| 93 | if (defaultPreset) { |
| 94 | result.isDefault = true; |
| 95 | result.preset = getDefaultPresetFile(defaultPreset.filename) || {}; |
| 96 | } |
| 97 | |
| 98 | return response.send(result); |
| 99 | } catch (error) { |
| 100 | console.error(error); |
| 101 | return response.sendStatus(500); |
| 102 | } |
| 103 | }); |