From 73f7ffed007f17b668181dfb6956ac5bb2ab792d Mon Sep 17 00:00:00 2001 From: nobswebdev Date: Wed, 16 Sep 2026 11:48:59 +0200 Subject: [PATCH] Expose blog toggle via shop settings API and storefront branding. Add PATCH /shop-settings/blog-enabled and surface blogEnabled in ShopSettingsView and StorefrontBranding. --- .../controllers/ShopSettingsController.ts | 6 ++++++ .../shopSettings/dto/UpdateBlogEnabledDto.ts | 7 +++++++ .../services/ShopSettingsService.spec.ts | 20 ++++++++++++++++++- .../services/ShopSettingsService.ts | 14 ++++++++++++- .../shopSettings/types/ShopSettingsView.ts | 1 + .../shopSettings/types/StorefrontBranding.ts | 1 + .../StorefrontShopViewService.spec.ts | 3 ++- .../StorefrontShopViewServiceTestTypes.ts | 1 + 8 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 backend/src/modules/shopSettings/dto/UpdateBlogEnabledDto.ts diff --git a/backend/src/modules/shopSettings/controllers/ShopSettingsController.ts b/backend/src/modules/shopSettings/controllers/ShopSettingsController.ts index 6480a70..639fe4c 100644 --- a/backend/src/modules/shopSettings/controllers/ShopSettingsController.ts +++ b/backend/src/modules/shopSettings/controllers/ShopSettingsController.ts @@ -13,6 +13,7 @@ import { JwtGuard } from '../../../guards/JwtGuard'; import { shopFaviconFilePipe, shopFaviconUploadOptions } from '../config/shopFaviconUpload'; import { shopLogoFilePipe, shopLogoUploadOptions } from '../config/shopLogoUpload'; import { ConnectSimplexNotificationsDto } from '../dto/ConnectSimplexNotificationsDto'; +import { UpdateBlogEnabledDto } from '../dto/UpdateBlogEnabledDto'; import { UpdateNotificationsDto } from '../dto/UpdateNotificationsDto'; import { UpdateShippingNoteDto } from '../dto/UpdateShippingNoteDto'; import { UpdateSimplexLinkDto } from '../dto/UpdateSimplexLinkDto'; @@ -43,6 +44,11 @@ export class ShopSettingsController { return this.shopSettingsService.updateNotifications(dto); } + @Patch('/blog-enabled') + updateBlogEnabled(@Body() dto: UpdateBlogEnabledDto) { + return this.shopSettingsService.updateBlogEnabled(dto); + } + @Post('/simplex-connect') connectSimplexNotifications(@Body() dto: ConnectSimplexNotificationsDto) { return this.shopSettingsService.connectSimplexNotifications(dto.simplexNotificationLink); diff --git a/backend/src/modules/shopSettings/dto/UpdateBlogEnabledDto.ts b/backend/src/modules/shopSettings/dto/UpdateBlogEnabledDto.ts new file mode 100644 index 0000000..0e40ce4 --- /dev/null +++ b/backend/src/modules/shopSettings/dto/UpdateBlogEnabledDto.ts @@ -0,0 +1,7 @@ +import { IsBoolean, IsNotEmpty } from 'class-validator'; + +export class UpdateBlogEnabledDto { + @IsNotEmpty() + @IsBoolean() + blogEnabled: boolean; +} diff --git a/backend/src/modules/shopSettings/services/ShopSettingsService.spec.ts b/backend/src/modules/shopSettings/services/ShopSettingsService.spec.ts index 2b4b964..a03818a 100644 --- a/backend/src/modules/shopSettings/services/ShopSettingsService.spec.ts +++ b/backend/src/modules/shopSettings/services/ShopSettingsService.spec.ts @@ -25,6 +25,7 @@ describe('ShopSettingsService', () => { notifyOnNewOrder: false, notifyOnOrderMessage: false, simplexNotificationContactId: null, + blogEnabled: false, createdAt: new Date('2026-01-01T00:00:00.000Z'), updatedAt: new Date('2026-01-01T00:00:00.000Z') } as ShopSettings; @@ -67,10 +68,27 @@ describe('ShopSettingsService', () => { logoUrl: '/uploads/public/shop/logo.png', faviconUrl: null, simplexLink: 'https://simplex.example', - shippingNote: 'Ships in 3 days' + shippingNote: 'Ships in 3 days', + blogEnabled: false }); }); + it('updates blog enabled flag', async () => { + await service.updateBlogEnabled({ blogEnabled: true }); + + expect(shopSettingsRepo.update).toHaveBeenCalledWith('settings-1', { blogEnabled: true }); + }); + + it('includes blog enabled in shop settings view', async () => { + shopSettingsRepo.find.mockResolvedValue([{ ...settings, blogEnabled: true }]); + + await expect(service.getView()).resolves.toEqual( + expect.objectContaining({ + blogEnabled: true + }) + ); + }); + it('trims simplex links on update', async () => { await service.updateSimplexLink({ simplexLink: ' https://simplex.example/new ' }); diff --git a/backend/src/modules/shopSettings/services/ShopSettingsService.ts b/backend/src/modules/shopSettings/services/ShopSettingsService.ts index 293e154..f53b3cd 100644 --- a/backend/src/modules/shopSettings/services/ShopSettingsService.ts +++ b/backend/src/modules/shopSettings/services/ShopSettingsService.ts @@ -6,6 +6,7 @@ import { getShopBrandingPublicUrl, resolveShopBrandingPath } from '../../../conf import { Config } from '../../../types/Config'; import { removeFileFromDisk } from '../../../utils/removeFileFromDisk'; import { SimplexChatClient } from '../../simplex/services/SimplexChatClient'; +import { UpdateBlogEnabledDto } from '../dto/UpdateBlogEnabledDto'; import { UpdateNotificationsDto } from '../dto/UpdateNotificationsDto'; import { UpdateShippingNoteDto } from '../dto/UpdateShippingNoteDto'; import { UpdateSimplexLinkDto } from '../dto/UpdateSimplexLinkDto'; @@ -43,7 +44,8 @@ export class ShopSettingsService { logoUrl, faviconUrl, simplexLink: settings.simplexLink, - shippingNote: settings.shippingNote + shippingNote: settings.shippingNote, + blogEnabled: settings.blogEnabled }; } @@ -93,6 +95,14 @@ export class ShopSettingsService { return this.getView(); } + async updateBlogEnabled({ blogEnabled }: UpdateBlogEnabledDto): Promise { + const settings = await this.findSettings(); + + await this.shopSettingsRepo.update(settings.id, { blogEnabled }); + + return this.getView(); + } + async uploadLogo(filename: string): Promise { const settings = await this.findSettings(); @@ -191,6 +201,7 @@ export class ShopSettingsService { notifyOnNewOrder, notifyOnOrderMessage, simplexNotificationContactId, + blogEnabled, createdAt, updatedAt }: ShopSettings): ShopSettingsView { @@ -220,6 +231,7 @@ export class ShopSettingsService { notifyOnNewOrder, notifyOnOrderMessage, simplexNotificationConnected: simplexNotificationContactId !== null, + blogEnabled, isSetupComplete, setupChecklist, createdAt, diff --git a/backend/src/modules/shopSettings/types/ShopSettingsView.ts b/backend/src/modules/shopSettings/types/ShopSettingsView.ts index 9c889de..cd7b6fa 100644 --- a/backend/src/modules/shopSettings/types/ShopSettingsView.ts +++ b/backend/src/modules/shopSettings/types/ShopSettingsView.ts @@ -18,6 +18,7 @@ export interface ShopSettingsView { notifyOnNewOrder: boolean; notifyOnOrderMessage: boolean; simplexNotificationConnected: boolean; + blogEnabled: boolean; isSetupComplete: boolean; setupChecklist: SetupChecklist; createdAt: Date | null; diff --git a/backend/src/modules/shopSettings/types/StorefrontBranding.ts b/backend/src/modules/shopSettings/types/StorefrontBranding.ts index 39d5da5..1eaca45 100644 --- a/backend/src/modules/shopSettings/types/StorefrontBranding.ts +++ b/backend/src/modules/shopSettings/types/StorefrontBranding.ts @@ -3,4 +3,5 @@ export interface StorefrontBranding { faviconUrl: string | null; simplexLink: string | null; shippingNote: string | null; + blogEnabled: boolean; } diff --git a/backend/src/modules/storefrontCore/services/StorefrontShopViewService.spec.ts b/backend/src/modules/storefrontCore/services/StorefrontShopViewService.spec.ts index ca6dda6..80b877c 100644 --- a/backend/src/modules/storefrontCore/services/StorefrontShopViewService.spec.ts +++ b/backend/src/modules/storefrontCore/services/StorefrontShopViewService.spec.ts @@ -29,7 +29,8 @@ describe('StorefrontShopViewService', () => { logoUrl: null, faviconUrl: null, simplexLink: null, - shippingNote: null + shippingNote: null, + blogEnabled: false }, shopSettings = { shopName: 'Demo Shop', diff --git a/backend/src/modules/storefrontCore/types/StorefrontShopViewServiceTestTypes.ts b/backend/src/modules/storefrontCore/types/StorefrontShopViewServiceTestTypes.ts index 4b68795..03847da 100644 --- a/backend/src/modules/storefrontCore/types/StorefrontShopViewServiceTestTypes.ts +++ b/backend/src/modules/storefrontCore/types/StorefrontShopViewServiceTestTypes.ts @@ -13,6 +13,7 @@ export type StorefrontShopViewServiceOverrides = { faviconUrl: string | null; simplexLink: string | null; shippingNote: string | null; + blogEnabled: boolean; }; shopSettings?: { shopName: string; shopFiatCurrency: string; enabledPaymentMethods?: PaymentMethod[] }; fiatPerXmr?: number;