fix: Update Gitea workflow to use environment-specific mail and Directus secrets, and refactor Directus branding script for improved CSS management and button styling.
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 8s
Build & Deploy KLZ Cables / 🏗️ Build App (push) Has been cancelled
Build & Deploy KLZ Cables / 🚀 Deploy (push) Has been cancelled
Build & Deploy KLZ Cables / ⚡ PageSpeed (push) Has been cancelled
Build & Deploy KLZ Cables / 🔔 Notifications (push) Has been cancelled
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Has been cancelled

This commit is contained in:
2026-02-06 21:43:53 +01:00
parent 0178e828d6
commit 259d712105
2 changed files with 163 additions and 144 deletions

View File

@@ -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_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) }} 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)) }} 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_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 }} 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 }} 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 }} 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 }} 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 }} 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_URL: ${{ needs.prepare.outputs.directus_url }}
DIRECTUS_HOST: ${{ needs.prepare.outputs.directus_host }} DIRECTUS_HOST: ${{ needs.prepare.outputs.directus_host }}
PROJECT_NAME: ${{ needs.prepare.outputs.project_name }} PROJECT_NAME: ${{ needs.prepare.outputs.project_name }}
DIRECTUS_KEY: ${{ secrets.DIRECTUS_KEY }} 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 }} 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 }} 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 }} 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_NAME: ${{ secrets.DIRECTUS_DB_NAME || 'directus' }}
DIRECTUS_DB_USER: ${{ secrets.DIRECTUS_DB_USER || 'directus' }} DIRECTUS_DB_USER: ${{ secrets.DIRECTUS_DB_USER || 'directus' }}
DIRECTUS_DB_PASSWORD: ${{ secrets.DIRECTUS_DB_PASSWORD }} 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 }} 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' }} GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
steps: steps:
- name: Checkout repository - name: Checkout repository

View File

@@ -21,12 +21,17 @@ async function setupBranding() {
const getMimeType = (filePath: string) => { const getMimeType = (filePath: string) => {
const ext = path.extname(filePath).toLowerCase(); const ext = path.extname(filePath).toLowerCase();
switch (ext) { switch (ext) {
case '.svg': return 'image/svg+xml'; case '.svg':
case '.png': return 'image/png'; return 'image/svg+xml';
case '.png':
return 'image/png';
case '.jpg': case '.jpg':
case '.jpeg': return 'image/jpeg'; case '.jpeg':
case '.ico': return 'image/x-icon'; return 'image/jpeg';
default: return 'application/octet-stream'; case '.ico':
return 'image/x-icon';
default:
return 'application/octet-stream';
} }
}; };
@@ -45,13 +50,24 @@ async function setupBranding() {
return res.id; return res.id;
}; };
const logoWhiteId = await uploadAsset(path.resolve(__dirname, '../public/logo-white.svg'), 'Logo White'); const logoWhiteId = await uploadAsset(
const logoBlueId = await uploadAsset(path.resolve(__dirname, '../public/logo-blue.svg'), 'Logo Blue'); path.resolve(__dirname, '../public/logo-white.svg'),
const faviconId = await uploadAsset(path.resolve(__dirname, '../public/favicon.ico'), 'Favicon'); '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 // Smoother Background SVG
const bgSvgPath = path.resolve(__dirname, '../public/login-bg.svg'); const bgSvgPath = path.resolve(__dirname, '../public/login-bg.svg');
fs.writeFileSync(bgSvgPath, `<svg width="1920" height="1080" viewBox="0 0 1920 1080" fill="none" xmlns="http://www.w3.org/2000/svg"> fs.writeFileSync(
bgSvgPath,
`<svg width="1920" height="1080" viewBox="0 0 1920 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="1920" height="1080" fill="#001a4d"/> <rect width="1920" height="1080" fill="#001a4d"/>
<ellipse cx="960" cy="540" rx="800" ry="600" fill="url(#paint0_radial_premium)"/> <ellipse cx="960" cy="540" rx="800" ry="600" fill="url(#paint0_radial_premium)"/>
<defs> <defs>
@@ -60,7 +76,8 @@ async function setupBranding() {
<stop offset="1" stop-color="#001a4d" stop-opacity="0"/> <stop offset="1" stop-color="#001a4d" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
</svg>`); </svg>`,
);
const backgroundId = await uploadAsset(bgSvgPath, 'Login Bg'); const backgroundId = await uploadAsset(bgSvgPath, 'Login Bg');
if (fs.existsSync(bgSvgPath)) fs.unlinkSync(bgSvgPath); if (fs.existsSync(bgSvgPath)) fs.unlinkSync(bgSvgPath);
@@ -71,8 +88,7 @@ async function setupBranding() {
const COLOR_ACCENT = '#82ed20'; // Sustainability Green const COLOR_ACCENT = '#82ed20'; // Sustainability Green
const COLOR_SECONDARY = '#003d82'; const COLOR_SECONDARY = '#003d82';
const cssInjection = ` const customCss = `
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
/* Global Login Styles */ /* Global Login Styles */
@@ -92,7 +108,7 @@ async function setupBranding() {
} }
.public-view .v-button { .public-view .v-button {
border-radius: full !important; border-radius: 9999px !important;
height: 56px !important; height: 56px !important;
font-weight: 600 !important; font-weight: 600 !important;
letter-spacing: -0.01em !important; letter-spacing: -0.01em !important;
@@ -108,71 +124,74 @@ async function setupBranding() {
--v-input-border-radius: 12px !important; --v-input-border-radius: 12px !important;
--v-input-background-color: #f8f9fa !important; --v-input-background-color: #f8f9fa !important;
} }
</style> `;
const publicNote = `
<div style="font-family: 'Inter', sans-serif; text-align: center; margin-top: 24px;"> <div style="font-family: 'Inter', sans-serif; text-align: center; margin-top: 24px;">
<p style="color: rgba(255,255,255,0.7); font-size: 14px; margin-bottom: 4px; font-weight: 500;">KLZ INFRASTRUCTURE ENGINE</p> <p style="color: rgba(255,255,255,0.7); font-size: 14px; margin-bottom: 4px; font-weight: 500;">KLZ INFRASTRUCTURE ENGINE</p>
<h1 style="color: #ffffff; font-size: 18px; font-weight: 700; margin: 0;">Sustainable Energy. <span style="color: #82ed20;">Industrial Reliability.</span></h1> <h1 style="color: #ffffff; font-size: 18px; font-weight: 700; margin: 0;">Sustainable Energy. <span style="color: #82ed20;">Industrial Reliability.</span></h1>
</div> </div>
`; `;
await client.request(updateSettings({ await client.request(
updateSettings({
project_name: 'KLZ Cables', project_name: 'KLZ Cables',
project_url: 'https://klz-cables.com', project_url: 'https://klz-cables.com',
project_color: COLOR_ACCENT, project_color: COLOR_ACCENT,
project_descriptor: 'Sustainable Energy Infrastructure', project_descriptor: 'Sustainable Energy Infrastructure',
project_owner: 'KLZ Cables',
// FIXED: Use WHITE logo for the Blue Sidebar // FIXED: Use WHITE logo for the Blue Sidebar
project_logo: logoWhiteId as any, project_logo: logoWhiteId as any,
public_foreground: logoWhiteId as any, public_foreground: logoWhiteId as any,
public_background: backgroundId as any, public_background: backgroundId as any,
public_note: cssInjection, public_note: publicNote,
public_favicon: faviconId as any, public_favicon: faviconId as any,
custom_css: customCss,
// DEEP PREMIUM THEME // DEEP PREMIUM THEME
theme_light_overrides: { theme_light_overrides: {
// Brands // Brands
"primary": COLOR_ACCENT, // Buttons/Actions are GREEN like the website primary: COLOR_ACCENT, // Buttons/Actions are GREEN like the website
"secondary": COLOR_SECONDARY, secondary: COLOR_SECONDARY,
// Content Area // Content Area
"background": "#f1f3f7", background: '#f1f3f7',
"backgroundNormal": "#ffffff", backgroundNormal: '#ffffff',
"backgroundAccent": "#eef2ff", backgroundAccent: '#eef2ff',
// Sidebar Branding // Sidebar Branding
"navigationBackground": COLOR_PRIMARY, navigationBackground: COLOR_PRIMARY,
"navigationForeground": "#ffffff", navigationForeground: '#ffffff',
"navigationBackgroundHover": "rgba(255,255,255,0.05)", navigationBackgroundHover: 'rgba(255,255,255,0.05)',
"navigationForegroundHover": "#ffffff", navigationForegroundHover: '#ffffff',
"navigationBackgroundActive": "rgba(130, 237, 32, 0.15)", // Subtle Green highlight navigationBackgroundActive: 'rgba(130, 237, 32, 0.15)', // Subtle Green highlight
"navigationForegroundActive": COLOR_ACCENT, // Active item is GREEN navigationForegroundActive: COLOR_ACCENT, // Active item is GREEN
// Module Bar (Thin far left) // Module Bar (Thin far left)
"moduleBarBackground": "#000d26", moduleBarBackground: '#000d26',
"moduleBarForeground": "#ffffff", moduleBarForeground: '#ffffff',
"moduleBarForegroundActive": COLOR_ACCENT, moduleBarForegroundActive: COLOR_ACCENT,
// UI Standards // UI Standards
"borderRadius": "16px", // Larger radius for modern feel borderRadius: '16px', // Larger radius for modern feel
"borderWidth": "1px", borderWidth: '1px',
"borderColor": "#e2e8f0", borderColor: '#e2e8f0',
"formFieldHeight": "48px" // Touch-target height formFieldHeight: '48px', // Touch-target height
} as any, } as any,
theme_dark_overrides: { theme_dark_overrides: {
"primary": COLOR_ACCENT, primary: COLOR_ACCENT,
"background": "#0a0a0a", background: '#0a0a0a',
"navigationBackground": "#000000", navigationBackground: '#000000',
"moduleBarBackground": COLOR_PRIMARY, moduleBarBackground: COLOR_PRIMARY,
"borderRadius": "16px", borderRadius: '16px',
"formFieldHeight": "48px" formFieldHeight: '48px',
} as any } as any,
})); }),
);
console.log('✨ Premium Theme applied successfully!'); console.log('✨ Premium Theme applied successfully!');
} catch (error: any) { } catch (error: any) {
console.error('❌ Error:', JSON.stringify(error, null, 2)); console.error('❌ Error:', JSON.stringify(error, null, 2));
} }