From 259d712105f986727a5a202be55e04c136476151 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Fri, 6 Feb 2026 21:43:53 +0100 Subject: [PATCH] fix: Update Gitea workflow to use environment-specific mail and Directus secrets, and refactor Directus branding script for improved CSS management and button styling. --- .gitea/workflows/deploy.yml | 24 +-- scripts/setup-directus-branding.ts | 283 +++++++++++++++-------------- 2 files changed, 163 insertions(+), 144 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 8b6352ed..7f379d8f 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -234,23 +234,23 @@ jobs: NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ needs.prepare.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.TESTING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }} NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ needs.prepare.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.TESTING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }} SENTRY_DSN: ${{ secrets.SENTRY_DSN || vars.SENTRY_DSN || (needs.prepare.outputs.target == 'production' && secrets.SENTRY_DSN || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_SENTRY_DSN || secrets.TESTING_SENTRY_DSN || secrets.SENTRY_DSN)) }} - MAIL_HOST: ${{ secrets.MAIL_HOST || vars.MAIL_HOST }} - MAIL_PORT: ${{ secrets.MAIL_PORT || vars.MAIL_PORT }} - MAIL_USERNAME: ${{ secrets.MAIL_USERNAME || vars.MAIL_USERNAME }} - MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }} - MAIL_FROM: ${{ secrets.MAIL_FROM || vars.MAIL_FROM }} - MAIL_RECIPIENTS: ${{ secrets.MAIL_RECIPIENTS || vars.MAIL_RECIPIENTS }} + MAIL_HOST: ${{ secrets.MAIL_HOST || vars.MAIL_HOST || (needs.prepare.outputs.target == 'production' && (secrets.MAIL_HOST || vars.MAIL_HOST) || (needs.prepare.outputs.target == 'staging' && (secrets.STAGING_MAIL_HOST || vars.STAGING_MAIL_HOST) || (secrets.TESTING_MAIL_HOST || vars.TESTING_MAIL_HOST) || (secrets.MAIL_HOST || vars.MAIL_HOST))) }} + MAIL_PORT: ${{ secrets.MAIL_PORT || vars.MAIL_PORT || (needs.prepare.outputs.target == 'production' && (secrets.MAIL_PORT || vars.MAIL_PORT) || (needs.prepare.outputs.target == 'staging' && (secrets.STAGING_MAIL_PORT || vars.STAGING_MAIL_PORT) || (secrets.TESTING_MAIL_PORT || vars.TESTING_MAIL_PORT) || (secrets.MAIL_PORT || vars.MAIL_PORT))) }} + MAIL_USERNAME: ${{ secrets.MAIL_USERNAME || vars.MAIL_USERNAME || (needs.prepare.outputs.target == 'production' && (secrets.MAIL_USERNAME || vars.MAIL_USERNAME) || (needs.prepare.outputs.target == 'staging' && (secrets.STAGING_MAIL_USERNAME || vars.STAGING_MAIL_USERNAME) || (secrets.TESTING_MAIL_USERNAME || vars.TESTING_MAIL_USERNAME) || (secrets.MAIL_USERNAME || vars.MAIL_USERNAME))) }} + MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD || (needs.prepare.outputs.target == 'production' && secrets.MAIL_PASSWORD || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_MAIL_PASSWORD || secrets.TESTING_MAIL_PASSWORD || secrets.MAIL_PASSWORD)) }} + MAIL_FROM: ${{ secrets.MAIL_FROM || vars.MAIL_FROM || (needs.prepare.outputs.target == 'production' && (secrets.MAIL_FROM || vars.MAIL_FROM) || (needs.prepare.outputs.target == 'staging' && (secrets.STAGING_MAIL_FROM || vars.STAGING_MAIL_FROM) || (secrets.TESTING_MAIL_FROM || vars.TESTING_MAIL_FROM) || (secrets.MAIL_FROM || vars.MAIL_FROM))) }} + MAIL_RECIPIENTS: ${{ secrets.MAIL_RECIPIENTS || vars.MAIL_RECIPIENTS || (needs.prepare.outputs.target == 'production' && (secrets.MAIL_RECIPIENTS || vars.MAIL_RECIPIENTS) || (needs.prepare.outputs.target == 'staging' && (secrets.STAGING_MAIL_RECIPIENTS || vars.STAGING_MAIL_RECIPIENTS) || (secrets.TESTING_MAIL_RECIPIENTS || vars.TESTING_MAIL_RECIPIENTS) || (secrets.MAIL_RECIPIENTS || vars.MAIL_RECIPIENTS))) }} DIRECTUS_URL: ${{ needs.prepare.outputs.directus_url }} DIRECTUS_HOST: ${{ needs.prepare.outputs.directus_host }} PROJECT_NAME: ${{ needs.prepare.outputs.project_name }} - DIRECTUS_KEY: ${{ secrets.DIRECTUS_KEY }} - DIRECTUS_SECRET: ${{ secrets.DIRECTUS_SECRET }} - DIRECTUS_ADMIN_EMAIL: ${{ secrets.DIRECTUS_ADMIN_EMAIL }} - DIRECTUS_ADMIN_PASSWORD: ${{ secrets.DIRECTUS_ADMIN_PASSWORD }} + DIRECTUS_KEY: ${{ secrets.DIRECTUS_KEY || (needs.prepare.outputs.target == 'production' && secrets.DIRECTUS_KEY || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_DIRECTUS_KEY || secrets.TESTING_DIRECTUS_KEY || secrets.DIRECTUS_KEY)) }} + DIRECTUS_SECRET: ${{ secrets.DIRECTUS_SECRET || (needs.prepare.outputs.target == 'production' && secrets.DIRECTUS_SECRET || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_DIRECTUS_SECRET || secrets.TESTING_DIRECTUS_SECRET || secrets.DIRECTUS_SECRET)) }} + DIRECTUS_ADMIN_EMAIL: ${{ secrets.DIRECTUS_ADMIN_EMAIL || (needs.prepare.outputs.target == 'production' && secrets.DIRECTUS_ADMIN_EMAIL || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_DIRECTUS_ADMIN_EMAIL || secrets.TESTING_DIRECTUS_ADMIN_EMAIL || secrets.DIRECTUS_ADMIN_EMAIL)) }} + DIRECTUS_ADMIN_PASSWORD: ${{ secrets.DIRECTUS_ADMIN_PASSWORD || (needs.prepare.outputs.target == 'production' && secrets.DIRECTUS_ADMIN_PASSWORD || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_DIRECTUS_ADMIN_PASSWORD || secrets.TESTING_DIRECTUS_ADMIN_PASSWORD || secrets.DIRECTUS_ADMIN_PASSWORD)) }} DIRECTUS_DB_NAME: ${{ secrets.DIRECTUS_DB_NAME || 'directus' }} DIRECTUS_DB_USER: ${{ secrets.DIRECTUS_DB_USER || 'directus' }} - DIRECTUS_DB_PASSWORD: ${{ secrets.DIRECTUS_DB_PASSWORD }} - DIRECTUS_API_TOKEN: ${{ secrets.DIRECTUS_API_TOKEN }} + DIRECTUS_DB_PASSWORD: ${{ secrets.DIRECTUS_DB_PASSWORD || (needs.prepare.outputs.target == 'production' && secrets.DIRECTUS_DB_PASSWORD || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_DIRECTUS_DB_PASSWORD || secrets.TESTING_DIRECTUS_DB_PASSWORD || secrets.DIRECTUS_DB_PASSWORD)) }} + DIRECTUS_API_TOKEN: ${{ secrets.DIRECTUS_API_TOKEN || (needs.prepare.outputs.target == 'production' && secrets.DIRECTUS_API_TOKEN || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_DIRECTUS_API_TOKEN || secrets.TESTING_DIRECTUS_API_TOKEN || secrets.DIRECTUS_API_TOKEN)) }} GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }} steps: - name: Checkout repository diff --git a/scripts/setup-directus-branding.ts b/scripts/setup-directus-branding.ts index 0116399d..4f8e1735 100644 --- a/scripts/setup-directus-branding.ts +++ b/scripts/setup-directus-branding.ts @@ -9,49 +9,65 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); async function setupBranding() { - console.log('🎨 Refining Directus Branding for Premium Website Look...'); + console.log('🎨 Refining Directus Branding for Premium Website Look...'); - // 1. Authenticate - await ensureAuthenticated(); + // 1. Authenticate + await ensureAuthenticated(); - try { - // 2. Upload Assets (MIME FIXED) - console.log('📤 Re-uploading assets for clean IDs...'); + try { + // 2. Upload Assets (MIME FIXED) + console.log('📤 Re-uploading assets for clean IDs...'); - const getMimeType = (filePath: string) => { - const ext = path.extname(filePath).toLowerCase(); - switch (ext) { - case '.svg': return 'image/svg+xml'; - case '.png': return 'image/png'; - case '.jpg': - case '.jpeg': return 'image/jpeg'; - case '.ico': return 'image/x-icon'; - default: return 'application/octet-stream'; - } - }; + const getMimeType = (filePath: string) => { + const ext = path.extname(filePath).toLowerCase(); + switch (ext) { + case '.svg': + return 'image/svg+xml'; + case '.png': + return 'image/png'; + case '.jpg': + case '.jpeg': + return 'image/jpeg'; + case '.ico': + return 'image/x-icon'; + default: + return 'application/octet-stream'; + } + }; - const uploadAsset = async (filePath: string, title: string) => { - if (!fs.existsSync(filePath)) { - console.warn(`⚠️ File not found: ${filePath}`); - return null; - } - const mimeType = getMimeType(filePath); - const form = new FormData(); - const fileBuffer = fs.readFileSync(filePath); - const blob = new Blob([fileBuffer], { type: mimeType }); - form.append('file', blob, path.basename(filePath)); - form.append('title', title); - const res = await client.request(uploadFiles(form)); - return res.id; - }; + const uploadAsset = async (filePath: string, title: string) => { + if (!fs.existsSync(filePath)) { + console.warn(`⚠️ File not found: ${filePath}`); + return null; + } + const mimeType = getMimeType(filePath); + const form = new FormData(); + const fileBuffer = fs.readFileSync(filePath); + const blob = new Blob([fileBuffer], { type: mimeType }); + form.append('file', blob, path.basename(filePath)); + form.append('title', title); + const res = await client.request(uploadFiles(form)); + return res.id; + }; - const logoWhiteId = await uploadAsset(path.resolve(__dirname, '../public/logo-white.svg'), 'Logo White'); - const logoBlueId = await uploadAsset(path.resolve(__dirname, '../public/logo-blue.svg'), 'Logo Blue'); - const faviconId = await uploadAsset(path.resolve(__dirname, '../public/favicon.ico'), 'Favicon'); + const logoWhiteId = await uploadAsset( + path.resolve(__dirname, '../public/logo-white.svg'), + 'Logo White', + ); + const logoBlueId = await uploadAsset( + path.resolve(__dirname, '../public/logo-blue.svg'), + 'Logo Blue', + ); + const faviconId = await uploadAsset( + path.resolve(__dirname, '../public/favicon.ico'), + 'Favicon', + ); - // Smoother Background SVG - const bgSvgPath = path.resolve(__dirname, '../public/login-bg.svg'); - fs.writeFileSync(bgSvgPath, ` + // Smoother Background SVG + const bgSvgPath = path.resolve(__dirname, '../public/login-bg.svg'); + fs.writeFileSync( + bgSvgPath, + ` @@ -60,122 +76,125 @@ async function setupBranding() { -`); - const backgroundId = await uploadAsset(bgSvgPath, 'Login Bg'); - if (fs.existsSync(bgSvgPath)) fs.unlinkSync(bgSvgPath); +`, + ); + const backgroundId = await uploadAsset(bgSvgPath, 'Login Bg'); + if (fs.existsSync(bgSvgPath)) fs.unlinkSync(bgSvgPath); - // 3. Update Settings with "Premium Web" Theme - console.log('⚙️ Updating Directus settings...'); + // 3. Update Settings with "Premium Web" Theme + console.log('⚙️ Updating Directus settings...'); - const COLOR_PRIMARY = '#001a4d'; // Deep Blue - const COLOR_ACCENT = '#82ed20'; // Sustainability Green - const COLOR_SECONDARY = '#003d82'; + const COLOR_PRIMARY = '#001a4d'; // Deep Blue + const COLOR_ACCENT = '#82ed20'; // Sustainability Green + const COLOR_SECONDARY = '#003d82'; - const cssInjection = ` - + .public-view .v-input { + --v-input-border-radius: 12px !important; + --v-input-background-color: #f8f9fa !important; + } + `; + + const publicNote = `

KLZ INFRASTRUCTURE ENGINE

Sustainable Energy. Industrial Reliability.

`; - await client.request(updateSettings({ - project_name: 'KLZ Cables', - project_url: 'https://klz-cables.com', - project_color: COLOR_ACCENT, - project_descriptor: 'Sustainable Energy Infrastructure', - project_owner: 'KLZ Cables', + await client.request( + updateSettings({ + project_name: 'KLZ Cables', + project_url: 'https://klz-cables.com', + project_color: COLOR_ACCENT, + project_descriptor: 'Sustainable Energy Infrastructure', - // FIXED: Use WHITE logo for the Blue Sidebar - project_logo: logoWhiteId as any, + // FIXED: Use WHITE logo for the Blue Sidebar + project_logo: logoWhiteId as any, - public_foreground: logoWhiteId as any, - public_background: backgroundId as any, - public_note: cssInjection, - public_favicon: faviconId as any, + public_foreground: logoWhiteId as any, + public_background: backgroundId as any, + public_note: publicNote, + public_favicon: faviconId as any, + custom_css: customCss, - // DEEP PREMIUM THEME - theme_light_overrides: { - // Brands - "primary": COLOR_ACCENT, // Buttons/Actions are GREEN like the website - "secondary": COLOR_SECONDARY, + // DEEP PREMIUM THEME + theme_light_overrides: { + // Brands + primary: COLOR_ACCENT, // Buttons/Actions are GREEN like the website + secondary: COLOR_SECONDARY, - // Content Area - "background": "#f1f3f7", - "backgroundNormal": "#ffffff", - "backgroundAccent": "#eef2ff", + // Content Area + background: '#f1f3f7', + backgroundNormal: '#ffffff', + backgroundAccent: '#eef2ff', - // Sidebar Branding - "navigationBackground": COLOR_PRIMARY, - "navigationForeground": "#ffffff", - "navigationBackgroundHover": "rgba(255,255,255,0.05)", - "navigationForegroundHover": "#ffffff", - "navigationBackgroundActive": "rgba(130, 237, 32, 0.15)", // Subtle Green highlight - "navigationForegroundActive": COLOR_ACCENT, // Active item is GREEN + // Sidebar Branding + navigationBackground: COLOR_PRIMARY, + navigationForeground: '#ffffff', + navigationBackgroundHover: 'rgba(255,255,255,0.05)', + navigationForegroundHover: '#ffffff', + navigationBackgroundActive: 'rgba(130, 237, 32, 0.15)', // Subtle Green highlight + navigationForegroundActive: COLOR_ACCENT, // Active item is GREEN - // Module Bar (Thin far left) - "moduleBarBackground": "#000d26", - "moduleBarForeground": "#ffffff", - "moduleBarForegroundActive": COLOR_ACCENT, + // Module Bar (Thin far left) + moduleBarBackground: '#000d26', + moduleBarForeground: '#ffffff', + moduleBarForegroundActive: COLOR_ACCENT, - // UI Standards - "borderRadius": "16px", // Larger radius for modern feel - "borderWidth": "1px", - "borderColor": "#e2e8f0", - "formFieldHeight": "48px" // Touch-target height - } as any, + // UI Standards + borderRadius: '16px', // Larger radius for modern feel + borderWidth: '1px', + borderColor: '#e2e8f0', + formFieldHeight: '48px', // Touch-target height + } as any, - theme_dark_overrides: { - "primary": COLOR_ACCENT, - "background": "#0a0a0a", - "navigationBackground": "#000000", - "moduleBarBackground": COLOR_PRIMARY, - "borderRadius": "16px", - "formFieldHeight": "48px" - } as any - })); + theme_dark_overrides: { + primary: COLOR_ACCENT, + background: '#0a0a0a', + navigationBackground: '#000000', + moduleBarBackground: COLOR_PRIMARY, + borderRadius: '16px', + formFieldHeight: '48px', + } as any, + }), + ); - console.log('✨ Premium Theme applied successfully!'); - - } catch (error: any) { - console.error('❌ Error:', JSON.stringify(error, null, 2)); - } + console.log('✨ Premium Theme applied successfully!'); + } catch (error: any) { + console.error('❌ Error:', JSON.stringify(error, null, 2)); + } } setupBranding();