mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-03 04:37:40 +01:00
#2227 Implement content scaffolding
This commit is contained in:
parent
782f85e05d
commit
c661fea07d
1
.gitignore
vendored
1
.gitignore
vendored
@ -47,3 +47,4 @@ access.log
|
||||
public/css/user.css
|
||||
/plugins/
|
||||
/data
|
||||
/default/scaffold
|
||||
|
26
default/scaffold/README.md
Normal file
26
default/scaffold/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Content Scaffolding
|
||||
|
||||
Content files in this folder will be copied for all users (old and new) on the server startup.
|
||||
|
||||
1. You **must** create an `index.json` file in `/default/scaffold` for it to work. The syntax is the same as for default content.
|
||||
2. All file paths should be relative to `/default/scaffold`, the use of subdirectories is allowed.
|
||||
3. Scaffolded files are copied first, so they override any of the default files (presets/settings/etc.) that have the same file name.
|
||||
|
||||
## Example
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"filename": "themes/Midnight.json",
|
||||
"type": "theme"
|
||||
},
|
||||
{
|
||||
"filename": "backgrounds/city.png",
|
||||
"type": "background"
|
||||
},
|
||||
{
|
||||
"filename": "characters/Charlie.png",
|
||||
"type": "character"
|
||||
}
|
||||
]
|
||||
```
|
@ -7,7 +7,9 @@ const { getConfigValue, color } = require('../util');
|
||||
const { jsonParser } = require('../express-common');
|
||||
const writeFileAtomicSync = require('write-file-atomic').sync;
|
||||
const contentDirectory = path.join(process.cwd(), 'default/content');
|
||||
const scaffoldDirectory = path.join(process.cwd(), 'default/scaffold');
|
||||
const contentIndexPath = path.join(contentDirectory, 'index.json');
|
||||
const scaffoldIndexPath = path.join(scaffoldDirectory, 'index.json');
|
||||
const characterCardParser = require('../character-card-parser.js');
|
||||
|
||||
const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDomains', []);
|
||||
@ -16,6 +18,8 @@ const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDo
|
||||
* @typedef {Object} ContentItem
|
||||
* @property {string} filename
|
||||
* @property {string} type
|
||||
* @property {string} [name]
|
||||
* @property {string|null} [folder]
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -48,9 +52,7 @@ const CONTENT_TYPES = {
|
||||
*/
|
||||
function getDefaultPresets(directories) {
|
||||
try {
|
||||
const contentIndexText = fs.readFileSync(contentIndexPath, 'utf8');
|
||||
const contentIndex = JSON.parse(contentIndexText);
|
||||
|
||||
const contentIndex = getContentIndex();
|
||||
const presets = [];
|
||||
|
||||
for (const contentItem of contentIndex) {
|
||||
@ -112,8 +114,12 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
|
||||
continue;
|
||||
}
|
||||
|
||||
contentLog.push(contentItem.filename);
|
||||
const contentPath = path.join(contentDirectory, contentItem.filename);
|
||||
if (!contentItem.folder) {
|
||||
console.log(`Content file ${contentItem.filename} has no parent folder`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const contentPath = path.join(contentItem.folder, contentItem.filename);
|
||||
|
||||
if (!fs.existsSync(contentPath)) {
|
||||
console.log(`Content file ${contentItem.filename} is missing`);
|
||||
@ -129,6 +135,7 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
|
||||
|
||||
const basePath = path.parse(contentItem.filename).base;
|
||||
const targetPath = path.join(contentTarget, basePath);
|
||||
contentLog.push(contentItem.filename);
|
||||
|
||||
if (fs.existsSync(targetPath)) {
|
||||
console.log(`Content file ${contentItem.filename} already exists in ${contentTarget}`);
|
||||
@ -157,8 +164,7 @@ async function checkForNewContent(directoriesList, forceCategories = []) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentIndexText = fs.readFileSync(contentIndexPath, 'utf8');
|
||||
const contentIndex = JSON.parse(contentIndexText);
|
||||
const contentIndex = getContentIndex();
|
||||
let anyContentAdded = false;
|
||||
|
||||
for (const directories of directoriesList) {
|
||||
@ -179,6 +185,38 @@ async function checkForNewContent(directoriesList, forceCategories = []) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets combined content index from the content and scaffold directories.
|
||||
* @returns {ContentItem[]} Array of content index
|
||||
*/
|
||||
function getContentIndex() {
|
||||
const result = [];
|
||||
|
||||
if (fs.existsSync(scaffoldIndexPath)) {
|
||||
const scaffoldIndexText = fs.readFileSync(scaffoldIndexPath, 'utf8');
|
||||
const scaffoldIndex = JSON.parse(scaffoldIndexText);
|
||||
if (Array.isArray(scaffoldIndex)) {
|
||||
scaffoldIndex.forEach((item) => {
|
||||
item.folder = scaffoldDirectory;
|
||||
});
|
||||
result.push(...scaffoldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (fs.existsSync(contentIndexPath)) {
|
||||
const contentIndexText = fs.readFileSync(contentIndexPath, 'utf8');
|
||||
const contentIndex = JSON.parse(contentIndexText);
|
||||
if (Array.isArray(contentIndex)) {
|
||||
contentIndex.forEach((item) => {
|
||||
item.folder = contentDirectory;
|
||||
});
|
||||
result.push(...contentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the target directory for the specified asset type.
|
||||
* @param {ContentType} type Asset type
|
||||
|
Loading…
x
Reference in New Issue
Block a user