White-label multi-domain tuki (Noxus Intra)
- CSS-muuttujat: kaikki kovakoodatut #0f3460/#16213e korvattu var(--primary-color)/var(--primary-dark) - Uudet API-endpointit: branding (julkinen, domain-pohjainen), company_logo, company_logo_upload - Domain-pohjainen brändäys: HTTP_HOST → yrityksen domains-arrayn matchaus - Login: domain asettaa oletusyrityksen sessioon - check_auth: palauttaa branding-objektin (primary_color, subtitle, logo_url) - company_create/update: käsittelee domains, primary_color, subtitle, logo_file - Dynaaminen login-sivu, header ja footer (logo, nimi, alaotsikko, värit) - JS: loadBranding(), applyBranding(), yritysvaihdon brändäyspäivitys - Admin-paneeli: brändäysasetukset (logo-upload, väri, alaotsikko, domainit) - Git-repo siirretty intra.noxus.fi:hin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
186
api.php
186
api.php
@@ -1016,6 +1016,143 @@ switch ($action) {
|
||||
echo json_encode(['question' => "$a + $b = ?"]);
|
||||
break;
|
||||
|
||||
// ---------- BRANDING (julkinen) ----------
|
||||
case 'branding':
|
||||
$host = $_SERVER['HTTP_HOST'] ?? '';
|
||||
// Stripaa portti pois (localhost:3001 → localhost)
|
||||
$host = strtolower(explode(':', $host)[0]);
|
||||
$companies = loadCompanies();
|
||||
$matchedCompany = null;
|
||||
foreach ($companies as $comp) {
|
||||
$domains = $comp['domains'] ?? [];
|
||||
foreach ($domains as $d) {
|
||||
if (strtolower(trim($d)) === strtolower($host)) {
|
||||
$matchedCompany = $comp;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($matchedCompany) {
|
||||
$logoUrl = !empty($matchedCompany['logo_file'])
|
||||
? "api.php?action=company_logo&company_id=" . urlencode($matchedCompany['id'])
|
||||
: '';
|
||||
echo json_encode([
|
||||
'found' => true,
|
||||
'company_id' => $matchedCompany['id'],
|
||||
'nimi' => $matchedCompany['nimi'],
|
||||
'primary_color' => $matchedCompany['primary_color'] ?? '#0f3460',
|
||||
'subtitle' => $matchedCompany['subtitle'] ?? '',
|
||||
'logo_url' => $logoUrl,
|
||||
]);
|
||||
} else {
|
||||
// Noxus Intra -oletusbrändäys
|
||||
echo json_encode([
|
||||
'found' => false,
|
||||
'company_id' => '',
|
||||
'nimi' => 'Noxus Intra',
|
||||
'primary_color' => '#0f3460',
|
||||
'subtitle' => 'Hallintapaneeli',
|
||||
'logo_url' => '',
|
||||
]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'company_logo':
|
||||
$companyId = $_GET['company_id'] ?? '';
|
||||
if (empty($companyId) || !preg_match('/^[a-z0-9-]+$/', $companyId)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Virheellinen company_id']);
|
||||
break;
|
||||
}
|
||||
$companies = loadCompanies();
|
||||
$logoFile = '';
|
||||
foreach ($companies as $comp) {
|
||||
if ($comp['id'] === $companyId) {
|
||||
$logoFile = $comp['logo_file'] ?? '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty($logoFile)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Logoa ei löydy']);
|
||||
break;
|
||||
}
|
||||
$logoPath = DATA_DIR . '/companies/' . $companyId . '/' . $logoFile;
|
||||
if (!file_exists($logoPath)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Logotiedostoa ei löydy']);
|
||||
break;
|
||||
}
|
||||
$ext = strtolower(pathinfo($logoFile, PATHINFO_EXTENSION));
|
||||
$mimeTypes = ['png' => 'image/png', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'svg' => 'image/svg+xml', 'webp' => 'image/webp'];
|
||||
$mime = $mimeTypes[$ext] ?? 'application/octet-stream';
|
||||
header('Content-Type: ' . $mime);
|
||||
header('Cache-Control: public, max-age=3600');
|
||||
readfile($logoPath);
|
||||
exit;
|
||||
|
||||
case 'company_logo_upload':
|
||||
requireAdmin();
|
||||
if ($method !== 'POST') break;
|
||||
$companyId = $_POST['company_id'] ?? '';
|
||||
if (empty($companyId) || !preg_match('/^[a-z0-9-]+$/', $companyId)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Virheellinen company_id']);
|
||||
break;
|
||||
}
|
||||
if (!isset($_FILES['logo']) || $_FILES['logo']['error'] !== UPLOAD_ERR_OK) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Logotiedosto puuttuu tai virhe uploadissa']);
|
||||
break;
|
||||
}
|
||||
$file = $_FILES['logo'];
|
||||
// Validoi koko (max 2MB)
|
||||
if ($file['size'] > 2 * 1024 * 1024) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Logo on liian suuri (max 2MB)']);
|
||||
break;
|
||||
}
|
||||
// Validoi tyyppi
|
||||
$allowedTypes = ['image/png', 'image/jpeg', 'image/svg+xml', 'image/webp'];
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$detectedType = finfo_file($finfo, $file['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
if (!in_array($detectedType, $allowedTypes)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Sallitut tiedostotyypit: PNG, JPG, SVG, WebP']);
|
||||
break;
|
||||
}
|
||||
$extMap = ['image/png' => 'png', 'image/jpeg' => 'jpg', 'image/svg+xml' => 'svg', 'image/webp' => 'webp'];
|
||||
$ext = $extMap[$detectedType] ?? 'png';
|
||||
$newFilename = 'logo.' . $ext;
|
||||
$compDir = DATA_DIR . '/companies/' . $companyId;
|
||||
if (!file_exists($compDir)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Yritystä ei löydy']);
|
||||
break;
|
||||
}
|
||||
// Poista vanha logo
|
||||
$companies = loadCompanies();
|
||||
foreach ($companies as &$comp) {
|
||||
if ($comp['id'] === $companyId) {
|
||||
$oldLogo = $comp['logo_file'] ?? '';
|
||||
if ($oldLogo && $oldLogo !== $newFilename && file_exists($compDir . '/' . $oldLogo)) {
|
||||
unlink($compDir . '/' . $oldLogo);
|
||||
}
|
||||
$comp['logo_file'] = $newFilename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unset($comp);
|
||||
saveCompanies($companies);
|
||||
move_uploaded_file($file['tmp_name'], $compDir . '/' . $newFilename);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'logo_file' => $newFilename,
|
||||
'logo_url' => "api.php?action=company_logo&company_id=" . urlencode($companyId),
|
||||
]);
|
||||
break;
|
||||
|
||||
// ---------- AUTH ----------
|
||||
case 'login':
|
||||
if ($method !== 'POST') break;
|
||||
@@ -1050,8 +1187,24 @@ switch ($action) {
|
||||
// Multi-company: aseta käyttäjän yritykset sessioon
|
||||
$userCompanies = $u['companies'] ?? [];
|
||||
$_SESSION['companies'] = $userCompanies;
|
||||
// Valitse ensimmäinen yritys oletukseksi
|
||||
$_SESSION['company_id'] = !empty($userCompanies) ? $userCompanies[0] : '';
|
||||
// Domain-pohjainen oletusyritys
|
||||
$host = strtolower(explode(':', $_SERVER['HTTP_HOST'] ?? '')[0]);
|
||||
$domainCompanyId = '';
|
||||
$allComps = loadCompanies();
|
||||
foreach ($allComps as $dc) {
|
||||
foreach ($dc['domains'] ?? [] as $d) {
|
||||
if (strtolower(trim($d)) === strtolower($host)) {
|
||||
$domainCompanyId = $dc['id'];
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Jos domain matchaa ja käyttäjällä on oikeus → käytä sitä
|
||||
if ($domainCompanyId && in_array($domainCompanyId, $userCompanies)) {
|
||||
$_SESSION['company_id'] = $domainCompanyId;
|
||||
} else {
|
||||
$_SESSION['company_id'] = !empty($userCompanies) ? $userCompanies[0] : '';
|
||||
}
|
||||
// Hae yritysten nimet
|
||||
$allCompanies = loadCompanies();
|
||||
$companyList = [];
|
||||
@@ -1116,6 +1269,20 @@ switch ($action) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Brändäystiedot aktiivisesta yrityksestä
|
||||
$branding = ['primary_color' => '#0f3460', 'subtitle' => '', 'logo_url' => '', 'company_nimi' => ''];
|
||||
$activeCompanyId = $_SESSION['company_id'] ?? '';
|
||||
foreach ($allCompanies as $bc) {
|
||||
if ($bc['id'] === $activeCompanyId) {
|
||||
$branding['primary_color'] = $bc['primary_color'] ?? '#0f3460';
|
||||
$branding['subtitle'] = $bc['subtitle'] ?? '';
|
||||
$branding['company_nimi'] = $bc['nimi'] ?? '';
|
||||
$branding['logo_url'] = !empty($bc['logo_file'])
|
||||
? "api.php?action=company_logo&company_id=" . urlencode($bc['id'])
|
||||
: '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo json_encode([
|
||||
'authenticated' => true,
|
||||
'user_id' => $_SESSION['user_id'],
|
||||
@@ -1125,6 +1292,7 @@ switch ($action) {
|
||||
'companies' => $companyList,
|
||||
'company_id' => $_SESSION['company_id'] ?? '',
|
||||
'signatures' => $userSignatures,
|
||||
'branding' => $branding,
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(['authenticated' => false]);
|
||||
@@ -2479,9 +2647,18 @@ switch ($action) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
// Brändäyskentät
|
||||
$domains = [];
|
||||
if (isset($input['domains']) && is_array($input['domains'])) {
|
||||
$domains = array_values(array_filter(array_map('trim', $input['domains'])));
|
||||
}
|
||||
$company = [
|
||||
'id' => $id,
|
||||
'nimi' => $nimi,
|
||||
'domains' => $domains,
|
||||
'primary_color' => trim($input['primary_color'] ?? '#0f3460'),
|
||||
'subtitle' => trim($input['subtitle'] ?? ''),
|
||||
'logo_file' => '',
|
||||
'luotu' => date('Y-m-d H:i:s'),
|
||||
'aktiivinen' => true,
|
||||
];
|
||||
@@ -2521,6 +2698,11 @@ switch ($action) {
|
||||
if ($c['id'] === $id) {
|
||||
if (isset($input['nimi'])) $c['nimi'] = trim($input['nimi']);
|
||||
if (isset($input['aktiivinen'])) $c['aktiivinen'] = (bool)$input['aktiivinen'];
|
||||
if (isset($input['domains']) && is_array($input['domains'])) {
|
||||
$c['domains'] = array_values(array_filter(array_map('trim', $input['domains'])));
|
||||
}
|
||||
if (isset($input['primary_color'])) $c['primary_color'] = trim($input['primary_color']);
|
||||
if (isset($input['subtitle'])) $c['subtitle'] = trim($input['subtitle']);
|
||||
$found = true;
|
||||
echo json_encode($c);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user