DumbWareio_DumbPad/scripts/notepad-migration.js
Chris 47caab05b7
use notepad names for filenames (#67)
* use notepad names for filenames

* add MAX_FILENAME_COLLISION_ATTEMPTS variable

* refactor findnotepadbyid into a reusable function

* bump version to 1.0.3

* sanitize file id

* keep default notepad id

* add foundavailablepath check in loop when renaming notepads

* add logic to retain id for default notepad
2025-07-14 11:53:22 -07:00

157 lines
5.3 KiB
JavaScript

const fs = require('fs').promises;
const path = require('path');
/**
* Helper function to sanitize filename for file system
* @param {string} name - The notepad name to sanitize
* @returns {string} - Sanitized filename safe for filesystem use
*/
function sanitizeFilename(name) {
// Replace invalid filesystem characters with underscores
return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_').trim();
}
/**
* Helper function to get file path for notepad (tries name-based first, falls back to ID-based)
* @param {Object} notepad - The notepad object with id and name
* @param {string} dataDir - The data directory path
* @returns {Promise<string>} - The file path to use for this notepad
*/
async function getNotepadFilePath(notepad, dataDir) {
// Default notepad always uses default.txt filename
if (notepad.id === 'default') {
return path.join(dataDir, 'default.txt');
}
const sanitizedName = sanitizeFilename(notepad.name);
const nameBasedPath = path.join(dataDir, `${sanitizedName}.txt`);
const idBasedPath = path.join(dataDir, `${notepad.id}.txt`);
// Check if name-based file exists
try {
await fs.access(nameBasedPath);
return nameBasedPath;
} catch {
// Fall back to ID-based file
try {
await fs.access(idBasedPath);
return idBasedPath;
} catch {
// Neither exists, return name-based path for new files
return nameBasedPath;
}
}
}
/**
* Helper function to migrate file from ID-based to name-based filename
* @param {Object} notepad - The notepad object with id and name
* @param {string} dataDir - The data directory path
* @returns {Promise<string>} - The final file path after migration
*/
async function migrateNotepadFile(notepad, dataDir) {
const sanitizedName = sanitizeFilename(notepad.name);
const oldPath = path.join(dataDir, `${notepad.id}.txt`);
const newPath = path.join(dataDir, `${sanitizedName}.txt`);
// Skip if already using name-based filename
if (oldPath === newPath) return newPath;
try {
// Check if old file exists
await fs.access(oldPath);
// Check if new path already exists
try {
await fs.access(newPath);
// New file exists, keep old one to avoid conflicts
return oldPath;
} catch {
// New path doesn't exist, safe to migrate
await fs.rename(oldPath, newPath);
console.log(`Migrated notepad file: ${oldPath} -> ${newPath}`);
return newPath;
}
} catch {
// Old file doesn't exist, return new path
return newPath;
}
}
/**
* Function to migrate all existing notepads to name-based filenames
* @param {Array} notepads - Array of notepad objects
* @param {string} dataDir - The data directory path
* @returns {Promise<void>}
*/
async function migrateAllNotepadsToNameBasedFiles(notepads, dataDir) {
try {
console.log('Checking for notepad files to migrate...');
let migratedCount = 0;
for (const notepad of notepads) {
// Skip default notepad - it should always use default.txt filename
if (notepad.id === 'default') {
continue;
}
const oldPath = path.join(dataDir, `${notepad.id}.txt`);
const sanitizedName = sanitizeFilename(notepad.name);
const newPath = path.join(dataDir, `${sanitizedName}.txt`);
// Only migrate if old path exists and new path doesn't
if (oldPath !== newPath) {
try {
await fs.access(oldPath);
try {
await fs.access(newPath);
// Both exist, keep old one
console.log(`Skipping migration for ${notepad.name}: both ${oldPath} and ${newPath} exist`);
} catch {
// Only old exists, safe to migrate
await fs.rename(oldPath, newPath);
console.log(`Migrated: ${oldPath} -> ${newPath}`);
migratedCount++;
}
} catch {
// Old file doesn't exist, nothing to migrate
}
}
}
if (migratedCount > 0) {
console.log(`Successfully migrated ${migratedCount} notepad files to name-based filenames`);
} else {
console.log('No notepad files needed migration');
}
} catch (err) {
console.error('Error during notepad file migration:', err);
}
}
/**
* Ensure default notepad file exists
* @param {string} dataDir - The data directory path
* @returns {Promise<void>}
*/
async function migrateDefaultNotepad(dataDir) {
const defaultNotePath = path.join(dataDir, 'default.txt');
try {
await fs.access(defaultNotePath);
// File exists, nothing to do
} catch {
// File doesn't exist, create it
await fs.writeFile(defaultNotePath, '');
console.log(`Created default notepad file: ${defaultNotePath}`);
}
}
module.exports = {
sanitizeFilename,
getNotepadFilePath,
migrateNotepadFile,
migrateAllNotepadsToNameBasedFiles,
migrateDefaultNotepad
};