'Kirjaudu sisään']); exit; } } function loadCustomers(): array { $data = file_get_contents(DATA_FILE); $customers = json_decode($data, true) ?: []; // Migroi vanha data: jos asiakkaalla ei ole liittymat-arrayta, luo se $migrated = false; foreach ($customers as &$c) { if (!isset($c['liittymat'])) { $c['liittymat'] = [[ 'asennusosoite' => $c['asennusosoite'] ?? '', 'postinumero' => $c['postinumero'] ?? '', 'kaupunki' => $c['kaupunki'] ?? '', 'liittymanopeus' => $c['liittymanopeus'] ?? '', 'hinta' => floatval($c['hinta'] ?? 0), 'sopimuskausi' => '', 'alkupvm' => '', ]]; unset($c['asennusosoite'], $c['postinumero'], $c['kaupunki'], $c['liittymanopeus'], $c['hinta']); $migrated = true; } } unset($c); if ($migrated) { file_put_contents(DATA_FILE, json_encode($customers, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); } return $customers; } function saveCustomers(array $customers): void { // Automaattinen backup ennen tallennusta if (file_exists(DATA_FILE) && filesize(DATA_FILE) > 2) { $backupDir = __DIR__ . '/data/backups'; if (!file_exists($backupDir)) mkdir($backupDir, 0755, true); copy(DATA_FILE, $backupDir . '/customers_' . date('Y-m-d_His') . '.json'); // Säilytä vain 30 viimeisintä backuppia $backups = glob($backupDir . '/customers_*.json'); if (count($backups) > 30) { sort($backups); array_map('unlink', array_slice($backups, 0, count($backups) - 30)); } } file_put_contents(DATA_FILE, json_encode($customers, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); } function generateId(): string { return bin2hex(random_bytes(8)); } switch ($action) { case 'login': if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $password = $input['password'] ?? ''; if ($password === ADMIN_PASSWORD) { $_SESSION['authenticated'] = true; echo json_encode(['success' => true]); } else { http_response_code(401); echo json_encode(['error' => 'Väärä salasana']); } break; case 'logout': session_destroy(); echo json_encode(['success' => true]); break; case 'check_auth': echo json_encode(['authenticated' => isset($_SESSION['authenticated']) && $_SESSION['authenticated'] === true]); break; case 'customers': requireAuth(); if ($method === 'GET') { $customers = loadCustomers(); echo json_encode($customers); } break; case 'customer': requireAuth(); if ($method === 'POST') { $input = json_decode(file_get_contents('php://input'), true); $customers = loadCustomers(); $liittymat = []; foreach (($input['liittymat'] ?? []) as $l) { $liittymat[] = [ 'asennusosoite' => trim($l['asennusosoite'] ?? ''), 'postinumero' => trim($l['postinumero'] ?? ''), 'kaupunki' => trim($l['kaupunki'] ?? ''), 'liittymanopeus' => trim($l['liittymanopeus'] ?? ''), 'hinta' => floatval($l['hinta'] ?? 0), 'sopimuskausi' => trim($l['sopimuskausi'] ?? ''), 'alkupvm' => trim($l['alkupvm'] ?? ''), ]; } if (empty($liittymat)) { $liittymat[] = ['asennusosoite' => '', 'postinumero' => '', 'kaupunki' => '', 'liittymanopeus' => '', 'hinta' => 0, 'sopimuskausi' => '', 'alkupvm' => '']; } $customer = [ 'id' => generateId(), 'yritys' => trim($input['yritys'] ?? ''), 'yhteyshenkilö' => trim($input['yhteyshenkilö'] ?? ''), 'puhelin' => trim($input['puhelin'] ?? ''), 'sahkoposti' => trim($input['sahkoposti'] ?? ''), 'laskutusosoite' => trim($input['laskutusosoite'] ?? ''), 'laskutuspostinumero' => trim($input['laskutuspostinumero'] ?? ''), 'laskutuskaupunki' => trim($input['laskutuskaupunki'] ?? ''), 'laskutussahkoposti' => trim($input['laskutussahkoposti'] ?? ''), 'elaskuosoite' => trim($input['elaskuosoite'] ?? ''), 'elaskuvalittaja' => trim($input['elaskuvalittaja'] ?? ''), 'ytunnus' => trim($input['ytunnus'] ?? ''), 'lisatiedot' => trim($input['lisatiedot'] ?? ''), 'liittymat' => $liittymat, 'luotu' => date('Y-m-d H:i:s'), ]; if (empty($customer['yritys'])) { http_response_code(400); echo json_encode(['error' => 'Yrityksen nimi vaaditaan']); break; } $customers[] = $customer; saveCustomers($customers); echo json_encode($customer); } break; case 'customer_update': requireAuth(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? ''; $customers = loadCustomers(); $found = false; foreach ($customers as &$c) { if ($c['id'] === $id) { $c['yritys'] = trim($input['yritys'] ?? $c['yritys']); $c['yhteyshenkilö'] = trim($input['yhteyshenkilö'] ?? $c['yhteyshenkilö']); $c['puhelin'] = trim($input['puhelin'] ?? $c['puhelin']); $c['sahkoposti'] = trim($input['sahkoposti'] ?? $c['sahkoposti']); $c['laskutusosoite'] = trim($input['laskutusosoite'] ?? $c['laskutusosoite']); $c['laskutuspostinumero'] = trim($input['laskutuspostinumero'] ?? ($c['laskutuspostinumero'] ?? '')); $c['laskutuskaupunki'] = trim($input['laskutuskaupunki'] ?? ($c['laskutuskaupunki'] ?? '')); $c['laskutussahkoposti'] = trim($input['laskutussahkoposti'] ?? $c['laskutussahkoposti']); $c['elaskuosoite'] = trim($input['elaskuosoite'] ?? ($c['elaskuosoite'] ?? '')); $c['elaskuvalittaja'] = trim($input['elaskuvalittaja'] ?? ($c['elaskuvalittaja'] ?? '')); $c['ytunnus'] = trim($input['ytunnus'] ?? $c['ytunnus']); $c['lisatiedot'] = trim($input['lisatiedot'] ?? $c['lisatiedot']); if (isset($input['liittymat'])) { $liittymat = []; foreach ($input['liittymat'] as $l) { $liittymat[] = [ 'asennusosoite' => trim($l['asennusosoite'] ?? ''), 'postinumero' => trim($l['postinumero'] ?? ''), 'kaupunki' => trim($l['kaupunki'] ?? ''), 'liittymanopeus' => trim($l['liittymanopeus'] ?? ''), 'hinta' => floatval($l['hinta'] ?? 0), 'sopimuskausi' => trim($l['sopimuskausi'] ?? ''), 'alkupvm' => trim($l['alkupvm'] ?? ''), ]; } $c['liittymat'] = $liittymat; } $c['muokattu'] = date('Y-m-d H:i:s'); $found = true; echo json_encode($c); break; } } unset($c); if (!$found) { http_response_code(404); echo json_encode(['error' => 'Asiakasta ei löydy']); break; } saveCustomers($customers); break; case 'file_upload': requireAuth(); if ($method !== 'POST') break; $customerId = $_POST['customer_id'] ?? ''; if (!$customerId || !preg_match('/^[a-f0-9]+$/', $customerId)) { http_response_code(400); echo json_encode(['error' => 'Virheellinen asiakas-ID']); break; } if (empty($_FILES['file'])) { http_response_code(400); echo json_encode(['error' => 'Tiedosto puuttuu']); break; } $file = $_FILES['file']; if ($file['error'] !== UPLOAD_ERR_OK) { http_response_code(400); echo json_encode(['error' => 'Tiedoston lähetys epäonnistui']); break; } // Max 20MB if ($file['size'] > 20 * 1024 * 1024) { http_response_code(400); echo json_encode(['error' => 'Tiedosto on liian suuri (max 20 MB)']); break; } $uploadDir = __DIR__ . '/data/files/' . $customerId; if (!file_exists($uploadDir)) mkdir($uploadDir, 0755, true); $safeName = preg_replace('/[^a-zA-Z0-9._-]/', '_', basename($file['name'])); // Jos samanniminen tiedosto on jo olemassa, lisää aikaleima $dest = $uploadDir . '/' . $safeName; if (file_exists($dest)) { $ext = pathinfo($safeName, PATHINFO_EXTENSION); $base = pathinfo($safeName, PATHINFO_FILENAME); $safeName = $base . '_' . date('His') . ($ext ? '.' . $ext : ''); $dest = $uploadDir . '/' . $safeName; } if (move_uploaded_file($file['tmp_name'], $dest)) { echo json_encode([ 'success' => true, 'filename' => $safeName, 'size' => $file['size'], ]); } else { http_response_code(500); echo json_encode(['error' => 'Tallennusvirhe']); } break; case 'file_list': requireAuth(); $customerId = $_GET['customer_id'] ?? ''; if (!$customerId || !preg_match('/^[a-f0-9]+$/', $customerId)) { echo json_encode([]); break; } $dir = __DIR__ . '/data/files/' . $customerId; $files = []; if (is_dir($dir)) { foreach (scandir($dir) as $f) { if ($f === '.' || $f === '..') continue; $path = $dir . '/' . $f; $files[] = [ 'filename' => $f, 'size' => filesize($path), 'modified' => date('Y-m-d H:i', filemtime($path)), ]; } } usort($files, fn($a, $b) => strcmp($b['modified'], $a['modified'])); echo json_encode($files); break; case 'file_download': requireAuth(); $customerId = $_GET['customer_id'] ?? ''; $filename = $_GET['filename'] ?? ''; if (!$customerId || !preg_match('/^[a-f0-9]+$/', $customerId) || !$filename) { http_response_code(400); echo json_encode(['error' => 'Virheelliset parametrit']); break; } $safeName = basename($filename); $path = __DIR__ . '/data/files/' . $customerId . '/' . $safeName; if (!file_exists($path)) { http_response_code(404); echo json_encode(['error' => 'Tiedostoa ei löydy']); break; } header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $safeName . '"'); header('Content-Length: ' . filesize($path)); readfile($path); exit; case 'file_delete': requireAuth(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $customerId = $input['customer_id'] ?? ''; $filename = $input['filename'] ?? ''; if (!$customerId || !preg_match('/^[a-f0-9]+$/', $customerId) || !$filename) { http_response_code(400); echo json_encode(['error' => 'Virheelliset parametrit']); break; } $safeName = basename($filename); $path = __DIR__ . '/data/files/' . $customerId . '/' . $safeName; if (file_exists($path)) { unlink($path); } echo json_encode(['success' => true]); break; case 'customer_delete': requireAuth(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? ''; $customers = loadCustomers(); $customers = array_values(array_filter($customers, fn($c) => $c['id'] !== $id)); saveCustomers($customers); // Poista asiakkaan tiedostot $filesDir = __DIR__ . '/data/files/' . $id; if (is_dir($filesDir)) { array_map('unlink', glob($filesDir . '/*')); rmdir($filesDir); } echo json_encode(['success' => true]); break; default: http_response_code(404); echo json_encode(['error' => 'Tuntematon toiminto']); break; }