diff --git a/backend/routes/admin.js b/backend/routes/admin.js index e1b16fc..f52a4cf 100644 --- a/backend/routes/admin.js +++ b/backend/routes/admin.js @@ -891,12 +891,22 @@ const settingsHandler = (key) => ({ sendSuccess(res, { settings }); }), post: asyncHandler(async (req, res) => { - const settings = req.body; + const newSettings = req.body; + // Get existing settings first and merge + const existingResult = await query( + "SELECT settings FROM site_settings WHERE key = $1", + [key], + ); + const existingSettings = + existingResult.rows.length > 0 ? existingResult.rows[0].settings : {}; + // Merge new settings with existing (new settings overwrite existing for same keys) + const mergedSettings = { ...existingSettings, ...newSettings }; + await query( `INSERT INTO site_settings (key, settings, updatedat) VALUES ($1, $2, NOW()) ON CONFLICT (key) DO UPDATE SET settings = $2, updatedat = NOW()`, - [key, JSON.stringify(settings)], + [key, JSON.stringify(mergedSettings)], ); sendSuccess(res, { message: `${key} settings saved successfully` }); }), diff --git a/backend/routes/public.js b/backend/routes/public.js index 613e7ce..9d8f7b3 100644 --- a/backend/routes/public.js +++ b/backend/routes/public.js @@ -83,12 +83,15 @@ router.get( }), ); -// Get site settings +// Get site settings (from site_settings table with key='general') router.get( "/settings", asyncHandler(async (req, res) => { - const result = await query("SELECT * FROM sitesettings LIMIT 1"); - sendSuccess(res, { settings: result.rows[0] || {} }); + const result = await query( + "SELECT settings FROM site_settings WHERE key = 'general'", + ); + const settings = result.rows.length > 0 ? result.rows[0].settings : {}; + sendSuccess(res, settings); }), ); diff --git a/backend/server.js b/backend/server.js index a9943ad..f0cb4bc 100644 --- a/backend/server.js +++ b/backend/server.js @@ -64,6 +64,12 @@ app.use( ], connectSrc: ["'self'", "https://cdn.jsdelivr.net"], objectSrc: ["'none'"], + frameSrc: [ + "'self'", + "https://www.google.com", + "https://maps.google.com", + "https://www.openstreetmap.org", + ], upgradeInsecureRequests: !isDevelopment() ? [] : null, }, }, @@ -72,11 +78,11 @@ app.use( includeSubDomains: true, preload: true, }, - frameguard: { action: "deny" }, + frameguard: { action: "sameorigin" }, xssFilter: true, noSniff: true, referrerPolicy: { policy: "strict-origin-when-cross-origin" }, - }) + }), ); // CORS configuration @@ -85,7 +91,7 @@ if (process.env.CORS_ORIGIN) { cors({ origin: process.env.CORS_ORIGIN.split(","), credentials: true, - }) + }), ); } @@ -95,7 +101,7 @@ app.set("trust proxy", 1); // Body parsers app.use(express.json({ limit: BODY_PARSER_LIMITS.JSON })); app.use( - express.urlencoded({ extended: true, limit: BODY_PARSER_LIMITS.URLENCODED }) + express.urlencoded({ extended: true, limit: BODY_PARSER_LIMITS.URLENCODED }), ); // Fallback middleware for missing product images @@ -105,7 +111,7 @@ const productImageFallback = (req, res, next) => { "assets", "images", "products", - req.path + req.path, ); if (fs.existsSync(imagePath)) { @@ -117,7 +123,7 @@ const productImageFallback = (req, res, next) => { "assets", "images", "products", - "placeholder.jpg" + "placeholder.jpg", ); logger.debug("Serving placeholder image", { requested: req.path }); res.sendFile(placeholderPath); @@ -150,7 +156,7 @@ app.use( res.setHeader("Cache-Control", "public, max-age=86400"); // 1 day default } }, - }) + }), ); app.use( "/assets", @@ -172,7 +178,7 @@ app.use( res.setHeader("Cache-Control", "public, max-age=86400"); // 1 day for images } }, - }) + }), ); // Optimized image serving with aggressive caching app.use("/uploads", imageOptimization(path.join(baseDir, "uploads"))); @@ -183,7 +189,7 @@ app.use( etag: true, lastModified: true, immutable: true, - }) + }), ); // Session middleware @@ -194,12 +200,12 @@ if ( ) { if (!isDevelopment()) { logger.error( - "CRITICAL: SESSION_SECRET environment variable must be set in production!" + "CRITICAL: SESSION_SECRET environment variable must be set in production!", ); process.exit(1); } logger.warn( - "WARNING: Using insecure session secret. Set SESSION_SECRET in production!" + "WARNING: Using insecure session secret. Set SESSION_SECRET in production!", ); } @@ -224,7 +230,7 @@ app.use( proxy: !isDevelopment(), name: SESSION_CONFIG.SESSION_NAME, rolling: true, // Reset session expiration on each request - }) + }), ); // Request logging @@ -333,7 +339,7 @@ app.use( maxAge: "1d", etag: true, lastModified: true, - }) + }), ); // Favicon route @@ -353,7 +359,7 @@ app.get("/health", async (req, res) => { try { const dbHealth = await healthCheck(); const missingImages = CRITICAL_IMAGES.filter( - (img) => !fs.existsSync(path.join(baseDir, img)) + (img) => !fs.existsSync(path.join(baseDir, img)), ); const assetsHealthy = missingImages.length === 0; diff --git a/config/nginx-skyartshop-secured.conf b/config/nginx-skyartshop-secured.conf index e5d4f10..0f5ca91 100755 --- a/config/nginx-skyartshop-secured.conf +++ b/config/nginx-skyartshop-secured.conf @@ -34,8 +34,8 @@ server { add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; - # Content Security Policy - Allow only trusted CDNs - add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://cdn.ckeditor.com https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self' https://cdn.jsdelivr.net https://cdn.ckeditor.com; frame-src 'none';" always; + # Content Security Policy - Allow only trusted CDNs and Maps + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://cdn.ckeditor.com https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self' https://cdn.jsdelivr.net https://cdn.ckeditor.com; frame-src https://www.google.com https://maps.google.com https://www.openstreetmap.org;" always; # Prevent clickjacking add_header X-Permitted-Cross-Domain-Policies "none" always; diff --git a/website/admin/blog.html b/website/admin/blog.html index 7c272e1..58bbf98 100644 --- a/website/admin/blog.html +++ b/website/admin/blog.html @@ -4,6 +4,7 @@ Blog Management - Sky Art Shop + Customer Management - Sky Art Shop + Dashboard - Sky Art Shop Admin + Admin Dashboard - Sky Art Shop - + Homepage Editor - Sky Art Shop - + Admin Login - Sky Art Shop + Logout Debug Tool + Media Library - Sky Art Shop - + diff --git a/website/admin/test-inline-logout.html b/website/admin/test-inline-logout.html index 7501dcf..2788225 100644 --- a/website/admin/test-inline-logout.html +++ b/website/admin/test-inline-logout.html @@ -2,6 +2,7 @@ Test Inline Logout + diff --git a/website/admin/test-logout-click.html b/website/admin/test-logout-click.html index 4ad58a3..1665644 100644 --- a/website/admin/test-logout-click.html +++ b/website/admin/test-logout-click.html @@ -2,6 +2,7 @@ Logout Click Test + diff --git a/website/admin/test-logout-fix.html b/website/admin/test-logout-fix.html index 39bc5d2..f25f5eb 100644 --- a/website/admin/test-logout-fix.html +++ b/website/admin/test-logout-fix.html @@ -4,6 +4,7 @@ Logout Fix Test - Sky Art Shop + Logout Test + diff --git a/website/admin/test-logout.html b/website/admin/test-logout.html index 0be1778..64f40d9 100644 --- a/website/admin/test-logout.html +++ b/website/admin/test-logout.html @@ -4,6 +4,7 @@ Test Logout Button + @@ -277,15 +313,25 @@

About Us

Learn more about Sky Art Shop

+
-
-
-
+
+
+
-
@@ -506,9 +600,9 @@
@@ -557,7 +651,7 @@
- + - + + + + - + - + + - + + + - + diff --git a/website/public/signin.html b/website/public/signin.html index 9066129..ce2322d 100644 --- a/website/public/signin.html +++ b/website/public/signin.html @@ -4,6 +4,7 @@ Sign In | Sky Art Shop + Create Account | Sky Art Shop + Sticky Test - Inline CSS +