| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- <?php
- namespace Controllers;
- use Libs\Logger;
- use Libs\Payload;
- use Libs\Validator;
- use Models\CompanyModel;
- use Psr\Http\Message\ServerRequestInterface;
- /**
- * Provisiona uma nova empresa (tenant) junto com o seu primeiro usuário admin.
- *
- * Criar uma empresa nova é uma operação de PLATAFORMA: não pode herdar o
- * company_id de um usuário autenticado (ele estaria criando OUTRA empresa).
- * Por isso o acesso é protegido por um segredo de provisionamento
- * (PROVISION_SECRET), enviado no header X-Provision-Secret.
- *
- * Corpo esperado:
- * {
- * "company": { "name": "...", "cnpj": "14 dígitos", "logo": "(opcional)" },
- * "admin": { "name": "...", "email": "...", "phone": "...", "password": "min 8" }
- * }
- *
- * A resposta inclui o company_hmac_secret gerado — é o segredo a ser entregue
- * ao CRM dessa empresa (ver crm.md).
- */
- class RegisterCompanyController
- {
- private CompanyModel $companyModel;
- public function __construct()
- {
- $this->companyModel = new CompanyModel();
- }
- public function __invoke(ServerRequestInterface $request): \React\Http\Message\Response
- {
- if (!$this->isAuthorized($request)) {
- return Payload::fail('Unauthorized', [], 'E_VALIDATE', 401);
- }
- $body = json_decode((string) $request->getBody(), true);
- if (!is_array($body)) {
- return Payload::fail('Invalid JSON payload', [], 'E_VALIDATE', 400);
- }
- $company = is_array($body['company'] ?? null) ? $body['company'] : [];
- $admin = is_array($body['admin'] ?? null) ? $body['admin'] : [];
- $error = $this->validate($company, $admin);
- if ($error !== null) {
- return Payload::fail($error, [], 'E_VALIDATE', 400);
- }
- try {
- $result = $this->companyModel->createCompanyWithAdmin($company, $admin);
- } catch (\Throwable $e) {
- Logger::error('Failed to register company', ['error' => $e->getMessage()]);
- return Payload::fail('Failed to register company', [], 'E_GENERIC', 500);
- }
- switch ($result['status']) {
- case 'cnpj_exists':
- return Payload::fail('CNPJ already registered', [], 'E_VALIDATE', 400);
- case 'email_exists':
- return Payload::fail('Email already exists', [], 'E_VALIDATE', 400);
- case 'created':
- return Payload::ok(
- ['company' => $result['company'], 'user' => $result['user']],
- 'S_CREATED',
- 'Company created.'
- );
- default:
- return Payload::fail('Failed to register company', [], 'E_GENERIC', 500);
- }
- }
- /**
- * Valida os campos da empresa e do admin. Retorna a mensagem de erro ou null.
- */
- private function validate(array $company, array $admin): ?string
- {
- $cnpj = preg_replace('/\D/', '', (string) ($company['cnpj'] ?? ''));
- $validator = (new Validator([
- 'company_name' => $company['name'] ?? null,
- 'cnpj' => $cnpj,
- 'name' => $admin['name'] ?? null,
- 'email' => $admin['email'] ?? null,
- 'phone' => $admin['phone'] ?? null,
- 'password' => $admin['password'] ?? null,
- ]))
- ->required('company_name')->maxLength('company_name', 100)
- ->required('cnpj')
- ->required('name')->maxLength('name', 100)
- ->required('email')->email('email')->maxLength('email', 100)
- ->required('phone')->phone('phone')->maxLength('phone', 20)
- ->required('password')->minLength('password', 8)->maxLength('password', 255);
- if ($validator->fails()) {
- return $validator->firstError();
- }
- if (strlen($cnpj) !== 14) {
- return 'CNPJ must have 14 digits';
- }
- return null;
- }
- private function isAuthorized(ServerRequestInterface $request): bool
- {
- $expected = (string) ($_ENV['PROVISION_SECRET'] ?? '');
- if ($expected === '') {
- Logger::error('PROVISION_SECRET is not configured; rejecting company provisioning');
- return false;
- }
- return hash_equals($expected, $request->getHeaderLine('X-Provision-Secret'));
- }
- }
|