6033461be1
## New Features - Apple Liquid Glass CSS template with iOS 26-inspired design - Liquid Glass Dark theme with neon accents - Image Security settings tab with per-event protection levels - Release Please automation for versioning and changelog ## Improvements - Update CSS template migration with final working templates - Add search placeholder visibility fix for glass themes - Update README roadmap (Download Protection, Gallery Templates, Filtering & Export now implemented) ## Infrastructure - Add release-please.yml workflow for automated releases - Add release-please-config.json and manifest - Update docker-build.yml with Release Please integration comments - Add comprehensive CHANGELOG.md ## Cleanup - Add working/planning docs to .gitignore (CLAUDE.md, test-*.md, feature-*.md, etc.) - Remove internal planning documents from git tracking (kept locally) ## Files Added - .github/workflows/release-please.yml - .release-please-manifest.json - release-please-config.json - CHANGELOG.md - frontend/src/features/settings/tabs/ImageSecurityTab.tsx
103 lines
2.9 KiB
JavaScript
103 lines
2.9 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const fsp = fs.promises;
|
|
|
|
async function pathExists(location) {
|
|
try {
|
|
await fsp.access(location);
|
|
return true;
|
|
} catch (error) {
|
|
if (error && error.code === 'ENOENT') {
|
|
return false;
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function moveFile(source, destination) {
|
|
await fsp.mkdir(path.dirname(destination), { recursive: true });
|
|
try {
|
|
await fsp.rename(source, destination);
|
|
} catch (error) {
|
|
if (error.code === 'EXDEV') {
|
|
await fsp.copyFile(source, destination);
|
|
await fsp.unlink(source);
|
|
} else if (error.code === 'EEXIST') {
|
|
console.warn(`Destination already exists, leaving original in place: ${destination}`);
|
|
return;
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
async function migrate() {
|
|
const backendRoot = path.resolve(__dirname, '..');
|
|
const defaultStorage = path.resolve(backendRoot, '../storage');
|
|
const targetStorage = path.resolve(process.env.STORAGE_PATH || defaultStorage);
|
|
const legacyUploadsRoot = path.resolve(backendRoot, 'storage/uploads');
|
|
const targetUploadsRoot = path.join(targetStorage, 'uploads');
|
|
|
|
if (legacyUploadsRoot === targetUploadsRoot) {
|
|
console.log('Legacy uploads directory already matches target STORAGE_PATH. Nothing to migrate.');
|
|
return;
|
|
}
|
|
|
|
if (!fs.existsSync(legacyUploadsRoot)) {
|
|
console.log(`Legacy uploads directory not found at ${legacyUploadsRoot}. Nothing to migrate.`);
|
|
return;
|
|
}
|
|
|
|
const categories = ['logos', 'favicons'];
|
|
let migratedCounter = 0;
|
|
|
|
for (const category of categories) {
|
|
const legacyDir = path.join(legacyUploadsRoot, category);
|
|
if (!fs.existsSync(legacyDir)) {
|
|
continue;
|
|
}
|
|
|
|
const targetDir = path.join(targetUploadsRoot, category);
|
|
await fsp.mkdir(targetDir, { recursive: true });
|
|
|
|
const entries = await fsp.readdir(legacyDir, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
if (!entry.isFile()) {
|
|
continue;
|
|
}
|
|
|
|
const sourcePath = path.join(legacyDir, entry.name);
|
|
const destinationPath = path.join(targetDir, entry.name);
|
|
|
|
if (await pathExists(destinationPath)) {
|
|
console.warn(`Skipping ${sourcePath} because ${destinationPath} already exists.`);
|
|
continue;
|
|
}
|
|
|
|
await moveFile(sourcePath, destinationPath);
|
|
migratedCounter += 1;
|
|
}
|
|
|
|
const remaining = await fsp.readdir(legacyDir);
|
|
if (remaining.length === 0) {
|
|
await fsp.rm(legacyDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
if (migratedCounter === 0) {
|
|
console.log('No legacy logo or favicon files needed migration.');
|
|
return;
|
|
}
|
|
|
|
console.log(`Migrated ${migratedCounter} files into ${targetUploadsRoot}.`);
|
|
console.log('If the database still references legacy absolute paths, they will be cleaned up automatically on the next upload.');
|
|
}
|
|
|
|
migrate().catch((error) => {
|
|
console.error('Migration failed:', error);
|
|
process.exitCode = 1;
|
|
});
|