Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | /**
* Library Service
*
* Service class containing utility functions for creating and manipulating unmounted sites.
*/
import fs from 'fs-extra';
import path from 'path';
import { InitialWorkspaceConfigBuilder } from '../workspace/initial-workspace-config-builder.js';
/**
* LibraryService handles site configuration management and site lifecycle operations
*/
export class LibraryService {
appContainer;
constructor(appContainer) {
this.appContainer = appContainer;
}
/**
* Get a site configuration by its key
*
* @param siteKey - The unique key identifying the site
* @returns The site configuration
* @throws Error if site is not found
*/
async getSiteConf(siteKey) {
const options = { invalidateCache: true };
const configurations = await this.appContainer.configurationProvider.getConfigurations(options);
const site = configurations.sites.find((x) => x.key === siteKey);
if (!site) {
throw new Error(`Could not find siteconf with sitekey ${siteKey}`);
}
return site;
}
/**
* Check if a site configuration attribute value is already in use
*
* @param attr - The attribute name to check (e.g., 'key', 'name')
* @param value - The value to check for duplicates
* @returns True if duplicate exists, false otherwise
*/
async checkDuplicateSiteConfAttrStringValue(attr, value) {
const options = { invalidateCache: false };
const configurations = await this.appContainer.configurationProvider.getConfigurations(options);
const duplicate = configurations.sites.find((x) => x[attr]?.toString().toLowerCase() === value.toLowerCase());
return !!duplicate;
}
/**
* Create a new SSG-based Quiqr site
*
* @param siteName - The display name for the site
* @param ssgType - The SSG type (e.g., 'hugo', 'eleventy')
* @param ssgVersion - The SSG version to use
* @param configFormat - The configuration file format (toml, yaml, json)
* @returns The generated site key
*/
async createNewSite(siteName, ssgType, ssgVersion, configFormat) {
const siteKey = await this.createSiteKeyFromName(siteName);
const pathSite = this.appContainer.pathHelper.getSiteRoot(siteKey);
if (!pathSite) {
throw new Error(`Could not create site root for siteKey: ${siteKey}`);
}
await fs.ensureDir(pathSite);
const pathSource = path.join(pathSite, 'main');
// Create site using provider
const provider = await this.appContainer.providerFactory.getProvider(ssgType);
await provider.createSite({
directory: pathSource,
title: siteName,
configFormat
});
const configBuilder = new InitialWorkspaceConfigBuilder(pathSource, this.appContainer.formatResolver, this.appContainer.pathHelper);
configBuilder.buildAll(ssgType, ssgVersion);
const newConf = this.createMountConfUnmanaged(siteKey, siteKey, pathSource);
await fs.writeFile(this.appContainer.pathHelper.getSiteMountConfigPath(siteKey), JSON.stringify(newConf, null, 2), { encoding: 'utf8' });
return siteKey;
}
/**
* @deprecated Use createNewSite instead
* Create a new Hugo-based Quiqr site (backward compatibility)
*/
async createNewHugoQuiqrSite(siteName, hugoVersion, configFormat) {
return this.createNewSite(siteName, 'hugo', hugoVersion, configFormat);
}
/**
* Generate a unique site key from a site name
*
* @param name - The site name to convert to a key
* @returns A unique, URL-safe site key
*/
async createSiteKeyFromName(name) {
let newKey = name.replace(/[^a-z0-9_-]/gi, '_').toLowerCase();
const duplicate = await this.checkDuplicateSiteConfAttrStringValue('key', newKey);
if (duplicate) {
newKey = newKey + '-' + this.appContainer.pathHelper.randomPathSafeString(4);
}
return newKey;
}
/**
* Create an unmanaged site mount configuration
*
* @param siteKey - The unique site key
* @param siteName - The display name
* @param pathSource - The source directory path
* @returns Site configuration object
*/
createMountConfUnmanaged(siteKey, siteName, pathSource) {
return {
key: siteKey,
name: siteName,
source: {
type: 'folder',
path: path.basename(pathSource), // Always relative from 30sep2024
},
publish: [],
lastPublish: 0,
};
}
/**
* Create a new site from an existing temporary directory
*
* @param siteKey - The unique site key
* @param tempDir - The temporary directory containing the site files
*/
async createNewSiteWithTempDirAndKey(siteKey, tempDir) {
const pathSite = this.appContainer.pathHelper.getSiteRoot(siteKey);
if (!pathSite) {
throw new Error(`Could not create site root for siteKey: ${siteKey}`);
}
const pathSource = path.join(pathSite, 'main');
await fs.ensureDir(pathSite);
await fs.move(tempDir, pathSource);
const newConf = this.createMountConfUnmanaged(siteKey, siteKey, pathSource);
await fs.writeFile(this.appContainer.pathHelper.getSiteMountConfigPath(siteKey), JSON.stringify(newConf, null, 2), { encoding: 'utf8' });
}
/**
* Remove invalid configuration keys that shouldn't be persisted
*
* @param conf - The configuration object to clean
* @returns The cleaned configuration
*/
deleteInvalidConfKeys(conf) {
const cleanConf = { ...conf };
delete cleanConf['configPath'];
delete cleanConf['owner'];
delete cleanConf['published'];
delete cleanConf['publishKey'];
delete cleanConf['etalage'];
return cleanConf;
}
/**
* Write a site configuration to disk
*
* @param newConf - The configuration to write
* @param siteKey - The site key
* @returns True on success
*/
async writeSiteConf(newConf, siteKey) {
const cleanConf = this.deleteInvalidConfKeys(newConf);
// Ensure name field always exists - use key as fallback
if (!cleanConf.name) {
cleanConf.name = cleanConf.key || siteKey;
}
await fs.writeFile(this.appContainer.pathHelper.getSiteMountConfigPath(siteKey), JSON.stringify(cleanConf, null, 2), { encoding: 'utf8' });
return true;
}
/**
* Delete a site and all its files
*
* @param siteKey - The site key to delete
*/
async deleteSite(siteKey) {
await fs.remove(this.appContainer.pathHelper.getSiteMountConfigPath(siteKey));
const siteRoot = this.appContainer.pathHelper.getSiteRoot(siteKey);
if (siteRoot) {
await fs.remove(siteRoot);
}
}
}
|