formatting

This commit is contained in:
Dmitri 2026-05-04 17:30:32 +02:00
parent e4dd94f478
commit 6581fd70c2
13 changed files with 156 additions and 87 deletions

View File

@ -73,7 +73,11 @@ class Project_transition_settingsDBApi extends GenericDBApi {
* @param {object} options - Query options
* @returns {object|null} Settings record or null
*/
static async findByProjectAndEnvironment(projectId, environment, options = {}) {
static async findByProjectAndEnvironment(
projectId,
environment,
options = {},
) {
const transaction = options.transaction;
const record = await this.MODEL.findOne({

View File

@ -52,12 +52,13 @@ class ProjectsDBApi extends GenericDBApi {
// Note: transition_settings moved to project_transition_settings table
return {
id: data.id || undefined,
name: 'name' in data ? (data.name || null) : undefined,
slug: 'slug' in data ? (data.slug || null) : undefined,
description: 'description' in data ? (data.description || null) : undefined,
logo_url: 'logo_url' in data ? (data.logo_url || null) : undefined,
favicon_url: 'favicon_url' in data ? (data.favicon_url || null) : undefined,
og_image_url: 'og_image_url' in data ? (data.og_image_url || null) : undefined,
name: 'name' in data ? data.name || null : undefined,
slug: 'slug' in data ? data.slug || null : undefined,
description: 'description' in data ? data.description || null : undefined,
logo_url: 'logo_url' in data ? data.logo_url || null : undefined,
favicon_url: 'favicon_url' in data ? data.favicon_url || null : undefined,
og_image_url:
'og_image_url' in data ? data.og_image_url || null : undefined,
design_width: 'design_width' in data ? data.design_width : undefined,
design_height: 'design_height' in data ? data.design_height : undefined,
};

View File

@ -130,13 +130,20 @@ module.exports = {
try {
settings = JSON.parse(settings);
} catch (e) {
console.warn(`Failed to parse transition_settings for project ${project.id}:`, e);
console.warn(
`Failed to parse transition_settings for project ${project.id}:`,
e,
);
continue;
}
}
// Skip if settings is null, empty object, or has no actual values
if (!settings || typeof settings !== 'object' || Object.keys(settings).length === 0) {
if (
!settings ||
typeof settings !== 'object' ||
Object.keys(settings).length === 0
) {
continue;
}
@ -160,7 +167,9 @@ module.exports = {
if (records.length > 0) {
await queryInterface.bulkInsert('project_transition_settings', records);
console.log(`Migrated ${records.length} project transition settings to 'dev' environment`);
console.log(
`Migrated ${records.length} project transition settings to 'dev' environment`,
);
}
// Step 3: Drop the transition_settings column from projects table
@ -192,16 +201,19 @@ module.exports = {
overlayColor: setting.overlay_color,
});
await queryInterface.sequelize.query(`
await queryInterface.sequelize.query(
`
UPDATE projects
SET transition_settings = :settings::jsonb
WHERE id = :projectId
`, {
replacements: {
settings: jsonValue,
projectId: setting.projectId,
`,
{
replacements: {
settings: jsonValue,
projectId: setting.projectId,
},
},
});
);
}
// Step 3: Drop indexes and table
@ -212,6 +224,8 @@ module.exports = {
await queryInterface.dropTable('project_transition_settings');
// Drop the ENUM type
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_project_transition_settings_environment";');
await queryInterface.sequelize.query(
'DROP TYPE IF EXISTS "enum_project_transition_settings_environment";',
);
},
};

View File

@ -114,7 +114,11 @@ const sanitizePublicRuntimeListResponse = (entityName) => {
pattern instanceof RegExp ? pattern.test(req.path) : req.path === pattern,
);
if (!isPublicRuntimeReadRequest(req) || !pathMatches || fields.length === 0) {
if (
!isPublicRuntimeReadRequest(req) ||
!pathMatches ||
fields.length === 0
) {
return next();
}

View File

@ -433,10 +433,7 @@ const downloadFile = async (req, res) => {
res.setHeader('Content-Length', result.contentLength);
// Add caching headers for browser
res.setHeader(
'Cache-Control',
`public, max-age=${config.s3CacheMaxAge}`,
);
res.setHeader('Cache-Control', `public, max-age=${config.s3CacheMaxAge}`);
if (useCache && typeof result.body.pipe === 'function') {
// Stream to both response and cache file
@ -839,13 +836,11 @@ const finalizeUploadSession = async (req, res) => {
// Verify all chunks exist
for (let i = 0; i < session.totalChunks; i++) {
if (!sessionManager.chunkExists(sessionId, i)) {
return res
.status(400)
.send(
createErrorResponse(`Missing chunk ${i}`, 'MISSING_CHUNK', {
missingChunk: i,
}),
);
return res.status(400).send(
createErrorResponse(`Missing chunk ${i}`, 'MISSING_CHUNK', {
missingChunk: i,
}),
);
}
}
@ -956,12 +951,16 @@ const getMimeTypeFromExtension = (filepath) => {
*/
const copyFile = async (sourceKey, destKey, options = {}) => {
const provider = getFileStorageProvider();
const contentType = options.contentType || getMimeTypeFromExtension(sourceKey);
const contentType =
options.contentType || getMimeTypeFromExtension(sourceKey);
if (provider === 's3') {
const s3 = getS3Provider();
const result = await s3.copy(sourceKey, destKey, { contentType });
logger.debug({ sourceKey, destKey, provider: 's3' }, 'File copied (server-side)');
logger.debug(
{ sourceKey, destKey, provider: 's3' },
'File copied (server-side)',
);
return { url: result.url };
}
@ -969,7 +968,9 @@ const copyFile = async (sourceKey, destKey, options = {}) => {
const local = getLocalProvider();
await local.copy(sourceKey, destKey);
logger.debug({ sourceKey, destKey, provider: 'local' }, 'File copied');
return { url: `/api/file/download?privateUrl=${encodeURIComponent(destKey)}` };
return {
url: `/api/file/download?privateUrl=${encodeURIComponent(destKey)}`,
};
}
// GCloud fallback: download + upload (no native copy implemented)
@ -1029,13 +1030,20 @@ const copyFilesParallel = async (copies, options = {}) => {
throw new Error(`Copy failed for ${copy.sourceKey}: ${errorMsg}`);
}
logger.warn({ sourceKey: copy.sourceKey, error: errorMsg }, 'File copy failed');
logger.warn(
{ sourceKey: copy.sourceKey, error: errorMsg },
'File copy failed',
);
}
}
}
logger.info(
{ succeeded: succeeded.length, failed: failed.length, total: copies.length },
{
succeeded: succeeded.length,
failed: failed.length,
total: copies.length,
},
'Batch file copy completed',
);

View File

@ -124,7 +124,11 @@ module.exports = class Project_transition_settingsService {
/**
* Find settings by project ID and environment
*/
static async findByProjectAndEnvironment(projectId, environment, currentUser) {
static async findByProjectAndEnvironment(
projectId,
environment,
currentUser,
) {
return Project_transition_settingsDBApi.findByProjectAndEnvironment(
projectId,
environment,

View File

@ -293,10 +293,13 @@ class ProjectsService extends BaseProjectsService {
'Starting parallel file copy for project clone',
);
const { succeeded, failed } = await FileService.copyFilesParallel(copyOperations, {
concurrency: 10,
continueOnError: true,
});
const { succeeded, failed } = await FileService.copyFilesParallel(
copyOperations,
{
concurrency: 10,
continueOnError: true,
},
);
// ============================================
// Phase D: Build assetPathMap from results
@ -322,7 +325,9 @@ class ProjectsService extends BaseProjectsService {
asset_type: sourceAsset.asset_type,
type: sourceAsset.type || 'general',
cdn_url: '', // Will be populated on first presigned URL request
storage_key: assetPathMap.get(sourceAsset.storage_key) || sourceAsset.storage_key,
storage_key:
assetPathMap.get(sourceAsset.storage_key) ||
sourceAsset.storage_key,
mime_type: sourceAsset.mime_type,
size_mb: sourceAsset.size_mb,
width_px: sourceAsset.width_px,
@ -345,7 +350,8 @@ class ProjectsService extends BaseProjectsService {
if (sourceVariant.variant_type === 'reversed') continue; // Handled in Phase F
const variantStorageKey =
assetPathMap.get(sourceVariant.storage_key) || sourceVariant.storage_key;
assetPathMap.get(sourceVariant.storage_key) ||
sourceVariant.storage_key;
await db.asset_variants.create(
{
@ -385,10 +391,13 @@ class ProjectsService extends BaseProjectsService {
'Copying reversed videos for cloned assets',
);
const reversedResults = await FileService.copyFilesParallel(reversedCopyOps, {
concurrency: 10,
continueOnError: true, // Many assets won't have reversed videos - that's OK
});
const reversedResults = await FileService.copyFilesParallel(
reversedCopyOps,
{
concurrency: 10,
continueOnError: true, // Many assets won't have reversed videos - that's OK
},
);
// Add successful reversed video copies to assetPathMap
for (const { sourceKey, destKey } of reversedResults.succeeded) {
@ -431,15 +440,18 @@ class ProjectsService extends BaseProjectsService {
// Transform background URLs to new storage keys
if (pageData.background_image_url) {
pageData.background_image_url =
assetPathMap.get(pageData.background_image_url) || pageData.background_image_url;
assetPathMap.get(pageData.background_image_url) ||
pageData.background_image_url;
}
if (pageData.background_video_url) {
pageData.background_video_url =
assetPathMap.get(pageData.background_video_url) || pageData.background_video_url;
assetPathMap.get(pageData.background_video_url) ||
pageData.background_video_url;
}
if (pageData.background_audio_url) {
pageData.background_audio_url =
assetPathMap.get(pageData.background_audio_url) || pageData.background_audio_url;
assetPathMap.get(pageData.background_audio_url) ||
pageData.background_audio_url;
}
await db.tour_pages.create(

View File

@ -257,20 +257,21 @@ module.exports = class PublishService {
transaction,
) {
// Get source content
const [sourcePages, sourceAudioTracks, sourceTransitionSettings] = await Promise.all([
db.tour_pages.findAll({
where: { projectId, environment: fromEnv },
transaction,
}),
db.project_audio_tracks.findAll({
where: { projectId, environment: fromEnv },
transaction,
}),
db.project_transition_settings.findOne({
where: { projectId, environment: fromEnv },
transaction,
}),
]);
const [sourcePages, sourceAudioTracks, sourceTransitionSettings] =
await Promise.all([
db.tour_pages.findAll({
where: { projectId, environment: fromEnv },
transaction,
}),
db.project_audio_tracks.findAll({
where: { projectId, environment: fromEnv },
transaction,
}),
db.project_transition_settings.findOne({
where: { projectId, environment: fromEnv },
transaction,
}),
]);
// Clean up target environment (hard delete - paranoid models need force: true)
await Promise.all([

View File

@ -773,19 +773,20 @@ export function ElementEditorPanel({
selectedElement.type === 'gallery'
? selectedElement.gallerySlideTransitionDurationMs !==
undefined &&
selectedElement.gallerySlideTransitionDurationMs !== ''
selectedElement.gallerySlideTransitionDurationMs !==
''
? String(
selectedElement.gallerySlideTransitionDurationMs,
)
: ''
: selectedElement.carouselSlideTransitionDurationMs !==
undefined &&
selectedElement.carouselSlideTransitionDurationMs !==
''
? String(
selectedElement.carouselSlideTransitionDurationMs,
)
: '',
undefined &&
selectedElement.carouselSlideTransitionDurationMs !==
''
? String(
selectedElement.carouselSlideTransitionDurationMs,
)
: '',
slideTransitionEasing:
selectedElement.type === 'gallery'
? selectedElement.gallerySlideTransitionEasing || ''
@ -848,7 +849,8 @@ export function ElementEditorPanel({
} else if (prop === 'slideTransitionOverlayColor') {
if (selectedElement.type === 'gallery') {
updateSelectedElement({
gallerySlideTransitionOverlayColor: value || undefined,
gallerySlideTransitionOverlayColor:
value || undefined,
});
} else if (selectedElement.type === 'carousel') {
updateSelectedElement({

View File

@ -270,7 +270,9 @@ const EffectsSettingsSectionCompact: React.FC<
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.slideTransitionType || ''}
onChange={(e) => onChange('slideTransitionType', e.target.value)}
onChange={(e) =>
onChange('slideTransitionType', e.target.value)
}
>
<option value=''>Use Default</option>
<option value='fade'>Fade</option>

View File

@ -14,7 +14,13 @@
* - Navigation-style rendering when custom icons with dimensions are set
*/
import React, { useMemo, useCallback, useEffect, useRef, useState } from 'react';
import React, {
useMemo,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import { createPortal } from 'react-dom';
import type { CSSProperties } from 'react';
import Icon from '@mdi/react';

View File

@ -110,12 +110,15 @@ export function resolveSlideTransition(
* Extract slide transition override from carousel element
*/
export function extractCarouselSlideOverride(
element: {
carouselSlideTransitionType?: SlideTransitionType | '';
carouselSlideTransitionDurationMs?: number | '';
carouselSlideTransitionEasing?: EasingFunction | '';
carouselSlideTransitionOverlayColor?: string;
} | null | undefined,
element:
| {
carouselSlideTransitionType?: SlideTransitionType | '';
carouselSlideTransitionDurationMs?: number | '';
carouselSlideTransitionEasing?: EasingFunction | '';
carouselSlideTransitionOverlayColor?: string;
}
| null
| undefined,
): SlideTransitionOverride | null {
if (!element) return null;
return {
@ -130,12 +133,15 @@ export function extractCarouselSlideOverride(
* Extract slide transition override from gallery element
*/
export function extractGallerySlideOverride(
element: {
gallerySlideTransitionType?: SlideTransitionType | '';
gallerySlideTransitionDurationMs?: number | '';
gallerySlideTransitionEasing?: EasingFunction | '';
gallerySlideTransitionOverlayColor?: string;
} | null | undefined,
element:
| {
gallerySlideTransitionType?: SlideTransitionType | '';
gallerySlideTransitionDurationMs?: number | '';
gallerySlideTransitionEasing?: EasingFunction | '';
gallerySlideTransitionOverlayColor?: string;
}
| null
| undefined,
): SlideTransitionOverride | null {
if (!element) return null;
return {

View File

@ -347,7 +347,9 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
// Look up current element for gallery carousel (so it receives updates from element editor)
const activeGalleryCarouselElement = useMemo(() => {
if (!activeGalleryCarousel) return null;
return elements.find((el) => el.id === activeGalleryCarousel.elementId) || null;
return (
elements.find((el) => el.id === activeGalleryCarousel.elementId) || null
);
}, [activeGalleryCarousel, elements]);
// Draggable panels using useDraggable hook
@ -1303,7 +1305,10 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
const handleGalleryCardClick = useCallback(
(element: CanvasElement, cardIndex: number) => {
if (element.galleryCards && element.galleryCards.length > 0) {
setActiveGalleryCarousel({ elementId: element.id, initialIndex: cardIndex });
setActiveGalleryCarousel({
elementId: element.id,
initialIndex: cardIndex,
});
}
},
[],