#2227 Implement content scaffolding

This commit is contained in:
Cohee 2024-05-17 02:43:14 +03:00
parent 782f85e05d
commit c661fea07d
3 changed files with 72 additions and 7 deletions

1
.gitignore vendored
View File

@ -47,3 +47,4 @@ access.log
public/css/user.css
/plugins/
/data
/default/scaffold

View 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"
}
]
```

View File

@ -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