prepare( 'SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?' ); $stmt->execute([$table, $column]); return (int)$stmt->fetchColumn() > 0; } function setupJson(array $value): string { return json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } function setupEnumValues(PDO $db, string $table, string $column): array { $stmt = $db->prepare( 'SELECT COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1' ); $stmt->execute([$table, $column]); $type = (string)$stmt->fetchColumn(); if (!preg_match("/^enum\\((.*)\\)$/", $type, $matches)) { return []; } $values = str_getcsv($matches[1], ',', "'"); return array_values(array_filter(array_map('strval', $values))); } function setupPreferredValue(array $available, array $preferred, string $fallback): string { foreach ($preferred as $value) { if (in_array($value, $available, true)) { return $value; } } return $available[0] ?? $fallback; } try { $db = dbnToolsDb(); $db->beginTransaction(); $plan = setupPreferredValue( setupEnumValues($db, 'clients', 'plan'), ['enterprise', 'sovereign', 'corporate', 'professional', 'beta', 'starter', 'trial', 'sandbox'], 'professional' ); $client = dbnToolsFetchClient($db); if (!$client) { $apiKeyHash = hash('sha256', random_bytes(32)); $apiKeyPrefix = 'dbn_' . substr(bin2hex(random_bytes(4)), 0, 8); $columns = [ 'slug' => $clientSlug, 'name' => 'Do Better Norge', 'contact_email' => $email, 'industry' => 'Legal services', 'country' => 'NO', 'plan' => $plan, 'api_key_hash' => $apiKeyHash, 'api_key_prefix' => $apiKeyPrefix, 'monthly_query_limit' => 10000, 'monthly_document_limit' => 1000, 'monthly_user_limit' => 25, 'is_active' => 1, 'subscription_status' => 'active', 'payment_method' => 'manual', 'billing_email' => $email, ]; if (setupColumnExists($db, 'clients', 'white_label_enabled')) { $columns['white_label_enabled'] = 1; } if (setupColumnExists($db, 'clients', 'white_label_config')) { $columns['white_label_config'] = setupJson([ 'brand_name' => 'Do Better Norge', 'primary_color' => '#0c5f82', 'support_email' => $email, 'hide_powered_by' => false, ]); } $names = array_keys($columns); $placeholders = array_fill(0, count($names), '?'); $stmt = $db->prepare( 'INSERT INTO clients (' . implode(', ', $names) . ') VALUES (' . implode(', ', $placeholders) . ')' ); $stmt->execute(array_values($columns)); $clientId = (int)$db->lastInsertId(); } else { $clientId = (int)$client['id']; $updates = [ 'name' => 'Do Better Norge', 'contact_email' => $email, 'industry' => 'Legal services', 'country' => 'NO', 'plan' => $plan, 'monthly_query_limit' => 10000, 'monthly_document_limit' => 1000, 'monthly_user_limit' => 25, 'is_active' => 1, 'subscription_status' => 'active', 'payment_method' => 'manual', 'billing_email' => $email, ]; if (setupColumnExists($db, 'clients', 'white_label_enabled')) { $updates['white_label_enabled'] = 1; } if (setupColumnExists($db, 'clients', 'white_label_config')) { $updates['white_label_config'] = setupJson([ 'brand_name' => 'Do Better Norge', 'primary_color' => '#0c5f82', 'support_email' => $email, 'hide_powered_by' => false, ]); } $set = implode(', ', array_map(static fn (string $key): string => "{$key} = ?", array_keys($updates))); $stmt = $db->prepare("UPDATE clients SET {$set} WHERE id = ?"); $stmt->execute([...array_values($updates), $clientId]); } $passwordHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]); $stmt = $db->prepare('SELECT id, client_id FROM client_users WHERE email = ? LIMIT 1'); $stmt->execute([$email]); $existingUser = $stmt->fetch(PDO::FETCH_ASSOC); if ($existingUser) { $stmt = $db->prepare( 'UPDATE client_users SET client_id = ?, username = ?, display_name = ?, password_hash = ?, role = ?, is_active = 1 WHERE id = ?' ); $stmt->execute([$clientId, $username, $displayName, $passwordHash, 'owner', (int)$existingUser['id']]); $userId = (int)$existingUser['id']; } else { $stmt = $db->prepare( 'INSERT INTO client_users (client_id, username, email, display_name, password_hash, role, is_active) VALUES (?, ?, ?, ?, ?, ?, 1)' ); $stmt->execute([$clientId, $username, $email, $displayName, $passwordHash, 'owner']); $userId = (int)$db->lastInsertId(); } $package = dbnToolsFetchPackage($packageSlug, $db); if (!$package) { $stmt = $db->prepare( 'INSERT INTO corpus_packages (slug, name, description, icon, is_free, included_in_plans, is_active, sort_order) VALUES (?, ?, ?, ?, 1, ?, 1, 10)' ); $stmt->execute([ $packageSlug, 'Family Legal', 'Family law corpus package for Do Better Norge legal tools.', 'LAW', setupJson([$plan]), ]); $packageId = (int)$db->lastInsertId(); } else { $packageId = (int)$package['id']; $db->prepare('UPDATE corpus_packages SET is_active = 1 WHERE id = ?')->execute([$packageId]); } $stmt = $db->prepare( 'INSERT INTO client_corpus_subscriptions (client_id, package_id, is_active, source) VALUES (?, ?, 1, ?) ON DUPLICATE KEY UPDATE is_active = VALUES(is_active), source = VALUES(source), cancelled_at = NULL' ); $stmt->execute([$clientId, $packageId, 'manual']); if (setupColumnExists($db, 'client_domains', 'hostname') && $domain !== '') { $domainColumns = [ 'client_id' => $clientId, 'hostname' => $domain, 'service_type' => 'portal', 'is_active' => 1, ]; if (setupColumnExists($db, 'client_domains', 'display_name')) { $domainColumns['display_name'] = 'Do Better Norge Caveau'; } if (setupColumnExists($db, 'client_domains', 'service_config')) { $domainColumns['service_config'] = setupJson([ 'audience' => 'internal', 'search_private' => true, 'search_shared' => true, 'package_ids' => [$packageId], 'allowed_classifications' => ['public', 'public-record', 'internal', 'client-work'], 'language' => 'no', ]); } if (setupColumnExists($db, 'client_domains', 'is_verified')) { $domainColumns['is_verified'] = 1; } if (setupColumnExists($db, 'client_domains', 'verified_at')) { $domainColumns['verified_at'] = date('Y-m-d H:i:s'); } $names = array_keys($domainColumns); $updates = implode(', ', array_map(static fn (string $key): string => "{$key} = VALUES({$key})", $names)); $stmt = $db->prepare( 'INSERT INTO client_domains (' . implode(', ', $names) . ') VALUES (' . implode(', ', array_fill(0, count($names), '?')) . ') ON DUPLICATE KEY UPDATE ' . $updates ); $stmt->execute(array_values($domainColumns)); } $db->commit(); echo "Caveau access configured.\n"; echo "Client: {$clientSlug} (#{$clientId})\n"; echo "User: {$email} (#{$userId}) role owner\n"; echo "Package: {$packageSlug} (#{$packageId}) active subscription\n"; echo "Portal domain: {$domain}\n"; } catch (Throwable $e) { if (isset($db) && $db instanceof PDO && $db->inTransaction()) { $db->rollBack(); } fwrite(STDERR, "Setup failed: {$e->getMessage()}\n"); exit(1); }