@yunsoft/yuncms 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yunsoft/yuncms",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Command-line setup and runtime launcher for YunCMS.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "test": "node --test"
31
31
  },
32
32
  "dependencies": {
33
- "@yunsoft/yuncms-api": "0.1.8",
34
- "@yunsoft/yuncms-core": "0.1.8"
33
+ "@yunsoft/yuncms-api": "0.1.10",
34
+ "@yunsoft/yuncms-core": "0.1.10"
35
35
  }
36
36
  }
@@ -32,6 +32,7 @@ const LEGACY_BACKUP_FORMAT_VERSION = 1;
32
32
  export const BACKUP_FORMAT_VERSION = 2;
33
33
  const SUPPORTED_BACKUP_FORMATS = new Set([LEGACY_BACKUP_FORMAT_VERSION, BACKUP_FORMAT_VERSION]);
34
34
  const SHA256_PATTERN = /^[0-9a-f]{64}$/i;
35
+ const AI_SETTINGS_KEY_RELATIVE_PATH = join('.yuncms', 'ai-settings.key');
35
36
 
36
37
  async function exists(path) {
37
38
  try {
@@ -115,6 +116,9 @@ function validateBackupManifest(manifest, manifestPath) {
115
116
  for (const key of ['env', 'packageJson', 'packageLock', 'extensions', 'localFiles']) {
116
117
  assertManifestBoolean(manifest.project[key], `project.${key}`, manifestPath);
117
118
  }
119
+ if (manifest.project.aiSettingsKey !== undefined) {
120
+ assertManifestBoolean(manifest.project.aiSettingsKey, 'project.aiSettingsKey', manifestPath);
121
+ }
118
122
  assertManifestString(manifest.project.localFilesRoot, 'project.localFilesRoot', manifestPath);
119
123
 
120
124
  if (!isObject(manifest.s3)) throw manifestError(manifestPath, 's3 metadata is required');
@@ -133,16 +137,21 @@ function validateBackupManifest(manifest, manifestPath) {
133
137
  if (!isObject(manifest.integrity.project)) {
134
138
  throw manifestError(manifestPath, 'integrity.project is required');
135
139
  }
136
- for (const [key, present] of [
137
- ['env', manifest.project.env],
138
- ['packageJson', manifest.project.packageJson],
139
- ['packageLock', manifest.project.packageLock],
140
- ['extensions', manifest.project.extensions],
141
- ['localFiles', manifest.project.localFiles],
140
+ for (const [key, present, optionalLegacy] of [
141
+ ['env', manifest.project.env, false],
142
+ ['packageJson', manifest.project.packageJson, false],
143
+ ['packageLock', manifest.project.packageLock, false],
144
+ ['extensions', manifest.project.extensions, false],
145
+ ['localFiles', manifest.project.localFiles, false],
146
+ ['aiSettingsKey', manifest.project.aiSettingsKey === true, manifest.project.aiSettingsKey === undefined],
142
147
  ]) {
143
148
  const digest = manifest.integrity.project[key];
144
149
  if (present) assertDigest(digest, `integrity.project.${key}`, manifestPath);
145
- else if (digest !== null) throw manifestError(manifestPath, `integrity.project.${key} must be null when the asset is absent`);
150
+ else if (!optionalLegacy && digest !== null) {
151
+ throw manifestError(manifestPath, `integrity.project.${key} must be null when the asset is absent`);
152
+ } else if (optionalLegacy && digest !== undefined && digest !== null) {
153
+ throw manifestError(manifestPath, `integrity.project.${key} must be null when the legacy manifest has no asset flag`);
154
+ }
146
155
  }
147
156
  }
148
157
 
@@ -208,6 +217,7 @@ async function assertExpectedBackupAssets(backupPath, manifest) {
208
217
  [manifest.project.env, join(backupPath, 'project', '.env'), 'file'],
209
218
  [manifest.project.packageJson, join(backupPath, 'project', 'package.json'), 'file'],
210
219
  [manifest.project.packageLock, join(backupPath, 'project', 'package-lock.json'), 'file'],
220
+ [manifest.project.aiSettingsKey === true, join(backupPath, 'project', 'ai-settings.key'), 'file'],
211
221
  [manifest.project.extensions, join(backupPath, 'extensions'), 'directory'],
212
222
  [manifest.project.localFiles, join(backupPath, 'files'), 'directory'],
213
223
  ];
@@ -227,6 +237,7 @@ async function calculateBackupIntegrity(destination, presence) {
227
237
  env: presence.env ? await hashFile(join(destination, 'project', '.env')) : null,
228
238
  packageJson: presence.packageJson ? await hashFile(join(destination, 'project', 'package.json')) : null,
229
239
  packageLock: presence.packageLock ? await hashFile(join(destination, 'project', 'package-lock.json')) : null,
240
+ aiSettingsKey: presence.aiSettingsKey ? await hashFile(join(destination, 'project', 'ai-settings.key')) : null,
230
241
  extensions: presence.extensions ? await hashDirectory(join(destination, 'extensions')) : null,
231
242
  localFiles: presence.localFiles ? await hashDirectory(join(destination, 'files')) : null,
232
243
  },
@@ -240,6 +251,7 @@ async function verifyBackupIntegrity(backupPath, manifest) {
240
251
  ['env', ['project', '.env'], 'file'],
241
252
  ['packageJson', ['project', 'package.json'], 'file'],
242
253
  ['packageLock', ['project', 'package-lock.json'], 'file'],
254
+ ['aiSettingsKey', ['project', 'ai-settings.key'], 'file'],
243
255
  ['extensions', ['extensions'], 'directory'],
244
256
  ['localFiles', ['files'], 'directory'],
245
257
  ]) {
@@ -308,6 +320,7 @@ async function assertRestoreTargets(cwd, localFilesPath, extensionsPath, allowDi
308
320
  await assertRestoreTarget(extensionsPath, 'directory');
309
321
  await assertRestoreTarget(resolve(cwd, 'package.json'), 'file');
310
322
  await assertRestoreTarget(resolve(cwd, 'package-lock.json'), 'file');
323
+ await assertRestoreTarget(resolve(cwd, AI_SETTINGS_KEY_RELATIVE_PATH), 'file');
311
324
  if (!allowDifferentDatabaseTarget) await assertRestoreTarget(resolve(cwd, '.env'), 'file');
312
325
  }
313
326
 
@@ -393,6 +406,7 @@ export async function createProjectBackup({
393
406
  env: await copyOptionalFile(resolve(cwd, '.env'), join(destination, 'project', '.env')),
394
407
  packageJson: await copyOptionalFile(resolve(cwd, 'package.json'), join(destination, 'project', 'package.json')),
395
408
  packageLock: await copyOptionalFile(resolve(cwd, 'package-lock.json'), join(destination, 'project', 'package-lock.json')),
409
+ aiSettingsKey: await copyOptionalFile(resolve(cwd, AI_SETTINGS_KEY_RELATIVE_PATH), join(destination, 'project', 'ai-settings.key')),
396
410
  extensions: await copyOptionalDirectory(extensionsPath, join(destination, 'extensions')),
397
411
  localFiles: await copyOptionalDirectory(localFilesPath, join(destination, 'files')),
398
412
  };
@@ -462,7 +476,7 @@ async function restoreOptionalFile({ backupPath, existed, sourceName, target })
462
476
  await rm(target, { force: true });
463
477
  return;
464
478
  }
465
- await mkdir(dirname(target), { recursive: true });
479
+ await mkdir(dirname(target), { recursive: true, mode: 0o700 });
466
480
  await copyFile(source, target);
467
481
  }
468
482
 
@@ -548,6 +562,16 @@ export async function restoreProjectBackup({
548
562
  sourceName: 'package-lock.json',
549
563
  target: resolve(cwd, 'package-lock.json'),
550
564
  });
565
+ if (manifest.project.aiSettingsKey !== undefined) {
566
+ await restoreOptionalFile({
567
+ backupPath: resolvedBackupPath,
568
+ existed: manifest.project.aiSettingsKey,
569
+ sourceName: 'ai-settings.key',
570
+ target: resolve(cwd, AI_SETTINGS_KEY_RELATIVE_PATH),
571
+ });
572
+ } else {
573
+ output.warn?.('Backup predates AI settings key snapshots; preserving the current .yuncms/ai-settings.key if present.');
574
+ }
551
575
  if (allowDifferentDatabaseTarget) {
552
576
  output.warn?.('Preserving the current .env because restore targets a different database; the backup .env remains available inside the backup directory.');
553
577
  } else {
@@ -562,3 +586,5 @@ export async function restoreProjectBackup({
562
586
  output.log?.(`Restore completed: ${resolvedBackupPath}`);
563
587
  return { backupPath: resolvedBackupPath, manifest };
564
588
  }
589
+
590
+ export { AI_SETTINGS_KEY_RELATIVE_PATH, validateBackupManifest };