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

@@ -9,49 +9,65 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
async function setupBranding() { async function setupBranding() {
console.log('🎨 Refining Directus Branding for Premium Website Look...'); console.log('🎨 Refining Directus Branding for Premium Website Look...');
// 1. Authenticate // 1. Authenticate
await ensureAuthenticated(); await ensureAuthenticated();
try { try {
// 2. Upload Assets (MIME FIXED) // 2. Upload Assets (MIME FIXED)
console.log('📤 Re-uploading assets for clean IDs...'); console.log('📤 Re-uploading assets for clean IDs...');
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 '.jpg': case '.png':
case '.jpeg': return 'image/jpeg'; return 'image/png';
case '.ico': return 'image/x-icon'; case '.jpg':
default: return 'application/octet-stream'; case '.jpeg':
} return 'image/jpeg';
}; case '.ico':
return 'image/x-icon';
default:
return 'application/octet-stream';
}
};
const uploadAsset = async (filePath: string, title: string) => { const uploadAsset = async (filePath: string, title: string) => {
if (!fs.existsSync(filePath)) { if (!fs.existsSync(filePath)) {
console.warn(`⚠️ File not found: ${filePath}`); console.warn(`⚠️ File not found: ${filePath}`);
return null; return null;
} }
const mimeType = getMimeType(filePath); const mimeType = getMimeType(filePath);
const form = new FormData(); const form = new FormData();
const fileBuffer = fs.readFileSync(filePath); const fileBuffer = fs.readFileSync(filePath);
const blob = new Blob([fileBuffer], { type: mimeType }); const blob = new Blob([fileBuffer], { type: mimeType });
form.append('file', blob, path.basename(filePath)); form.append('file', blob, path.basename(filePath));
form.append('title', title); form.append('title', title);
const res = await client.request(uploadFiles(form)); const res = await client.request(uploadFiles(form));
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,122 +76,125 @@ 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'); );
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 // 3. Update Settings with "Premium Web" Theme
console.log('⚙️ Updating Directus settings...'); console.log('⚙️ Updating Directus settings...');
const COLOR_PRIMARY = '#001a4d'; // Deep Blue const COLOR_PRIMARY = '#001a4d'; // Deep Blue
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 */ body, .v-app {
body, .v-app { font-family: 'Inter', sans-serif !important;
font-family: 'Inter', sans-serif !important; -webkit-font-smoothing: antialiased;
-webkit-font-smoothing: antialiased; }
}
/* Glassmorphism Effect for Login Card */
/* Glassmorphism Effect for Login Card */ .public-view .v-card {
.public-view .v-card { background: rgba(255, 255, 255, 0.95) !important;
background: rgba(255, 255, 255, 0.95) !important; backdrop-filter: blur(20px);
backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.3) !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important; border-radius: 32px !important;
border-radius: 32px !important; box-shadow: 0 50px 100px -20px rgba(0, 0, 0, 0.4) !important;
box-shadow: 0 50px 100px -20px rgba(0, 0, 0, 0.4) !important; padding: 40px !important;
padding: 40px !important; }
}
.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;
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1) !important; transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1) !important;
} }
.public-view .v-button:hover { .public-view .v-button:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 15px 30px rgba(130, 237, 32, 0.2) !important; box-shadow: 0 15px 30px rgba(130, 237, 32, 0.2) !important;
} }
.public-view .v-input { .public-view .v-input {
--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(
project_name: 'KLZ Cables', updateSettings({
project_url: 'https://klz-cables.com', project_name: 'KLZ Cables',
project_color: COLOR_ACCENT, project_url: 'https://klz-cables.com',
project_descriptor: 'Sustainable Energy Infrastructure', project_color: COLOR_ACCENT,
project_owner: 'KLZ Cables', project_descriptor: 'Sustainable Energy Infrastructure',
// 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)); }
}
} }
setupBranding(); setupBranding();