diff --git a/Dockerfile b/Dockerfile index 03a0878..f23a599 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,44 +1,81 @@ -FROM php:8.2-fpm +FROM php:8.3-cli AS vendor -# Installa dipendenze di sistema RUN apt-get update && apt-get install -y \ git \ - curl \ + libfreetype6-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ libpng-dev \ - libonig-dev \ - libxml2-dev \ - zip \ + libzip-dev \ unzip \ - nodejs \ - npm + zip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" gd intl zip \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* -# Pulisce cache -RUN apt-get clean && rm -rf /var/lib/apt/lists/* +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer -# Installa estensioni PHP -RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd +WORKDIR /app +COPY composer.json composer.lock ./ +RUN composer install \ + --no-dev \ + --prefer-dist \ + --no-interaction \ + --no-progress \ + --no-scripts \ + --optimize-autoloader -# Installa Composer -COPY --from=composer:latest /usr/bin/composer /usr/bin/composer +FROM node:20-bookworm-slim AS frontend + +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi +COPY --from=vendor /app/vendor ./vendor +COPY resources ./resources +COPY public ./public +COPY vite.config.js package.json postcss.config.js tailwind.config.js ./ +RUN npm run build + +FROM php:8.3-fpm AS runtime + +RUN apt-get update && apt-get install -y \ + curl \ + default-mysql-client \ + git \ + libfreetype6-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libonig-dev \ + libpng-dev \ + libxml2-dev \ + libzip-dev \ + unzip \ + zip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" bcmath exif gd intl mbstring opcache pcntl pdo_mysql zip \ + && pecl install redis \ + && docker-php-ext-enable redis \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer -# Imposta directory di lavoro WORKDIR /var/www -# Copia file applicazione -COPY . /var/www +COPY --chown=www-data:www-data . . +COPY --chown=www-data:www-data --from=vendor /app/vendor ./vendor +COPY --chown=www-data:www-data --from=frontend /app/public/build ./public/build +COPY --chown=www-data:www-data docker/entrypoint.sh /entrypoint.sh +COPY --chown=www-data:www-data docker/php/local.ini /usr/local/etc/php/conf.d/99-netgescon.ini -# Installa dipendenze PHP -RUN composer install --optimize-autoloader --no-dev +RUN chmod +x /entrypoint.sh \ + && mkdir -p storage/framework/cache/data storage/framework/sessions storage/framework/views bootstrap/cache /opt/netgescon \ + && cp -a public /opt/netgescon/public \ + && composer dump-autoload --no-dev --optimize \ + && chmod -R 775 storage bootstrap/cache -# Installa dipendenze Node.js -RUN npm install && npm run build - -# Imposta permessi -RUN chown -R www-data:www-data /var/www \ - && chmod -R 755 /var/www/storage \ - && chmod -R 755 /var/www/bootstrap/cache - -# Espone porta 9000 EXPOSE 9000 +ENTRYPOINT ["/entrypoint.sh"] CMD ["php-fpm"] \ No newline at end of file diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php index c34b603..6e9fc19 100755 --- a/app/Http/Controllers/Auth/AuthenticatedSessionController.php +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -7,6 +7,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Schema; use Illuminate\Validation\ValidationException; use Illuminate\View\View; @@ -29,14 +30,14 @@ public function store(LoginRequest $request): RedirectResponse $user = $request->user(); - if ($user && ! $user->is_active) { + if ($user && $this->userTableHasColumn($user, 'is_active') && ! $user->is_active) { Auth::guard('web')->logout(); throw ValidationException::withMessages([ 'email' => 'Account disattivato. Contatta il super-admin.', ]); } - if ($user && $user->registration_status !== 'approved') { + if ($user && $this->userTableHasColumn($user, 'registration_status') && $user->registration_status !== 'approved') { Auth::guard('web')->logout(); throw ValidationException::withMessages([ 'email' => 'Registrazione in attesa di approvazione del super-admin.', @@ -44,7 +45,7 @@ public function store(LoginRequest $request): RedirectResponse } $requireEmailVerification = SystemSetting::bool('require_email_verification', true); - $isSelfRegistered = $user && ! empty($user->requested_role); + $isSelfRegistered = $user && $this->userTableHasColumn($user, 'requested_role') && ! empty($user->requested_role); if ($user && $isSelfRegistered && $requireEmailVerification && ! $user->hasVerifiedEmail()) { Auth::guard('web')->logout(); throw ValidationException::withMessages([ @@ -57,6 +58,11 @@ public function store(LoginRequest $request): RedirectResponse return redirect()->intended(route('dashboard', absolute: false)); } + protected function userTableHasColumn($user, string $column): bool + { + return Schema::connection($user->getConnectionName())->hasColumn($user->getTable(), $column); + } + /** * Destroy an authenticated session. */ diff --git a/app/Models/SystemSetting.php b/app/Models/SystemSetting.php index 4751f0b..0eb9524 100644 --- a/app/Models/SystemSetting.php +++ b/app/Models/SystemSetting.php @@ -2,6 +2,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Throwable; class SystemSetting extends Model { @@ -12,7 +13,15 @@ class SystemSetting extends Model public static function getValue(string $key, mixed $default = null): mixed { - $setting = static::query()->where('key', $key)->value('value'); + if (! static::settingsTableExists()) { + return $default; + } + + try { + $setting = static::query()->where('key', $key)->value('value'); + } catch (Throwable) { + return $default; + } if ($setting === null) { return $default; @@ -25,6 +34,10 @@ public static function getValue(string $key, mixed $default = null): mixed public static function setValue(string $key, mixed $value): void { + if (! static::settingsTableExists()) { + return; + } + $storedValue = is_string($value) ? $value : json_encode($value); static::query()->updateOrCreate( @@ -37,4 +50,15 @@ public static function bool(string $key, bool $default = false): bool { return filter_var(static::getValue($key, $default), FILTER_VALIDATE_BOOL); } + + protected static function settingsTableExists(): bool + { + try { + $model = new static(); + + return $model->getConnection()->getSchemaBuilder()->hasTable($model->getTable()); + } catch (Throwable) { + return false; + } + } } diff --git a/app/Services/Cti/LiveIncomingCallService.php b/app/Services/Cti/LiveIncomingCallService.php index 1fb0f79..28f1182 100644 --- a/app/Services/Cti/LiveIncomingCallService.php +++ b/app/Services/Cti/LiveIncomingCallService.php @@ -7,6 +7,7 @@ use App\Models\Soggetto; use App\Models\User; use App\Services\Anagrafiche\RubricaPhoneLookupService; +use Illuminate\Support\Facades\Schema; class LiveIncomingCallService { @@ -15,6 +16,10 @@ class LiveIncomingCallService */ public function getForUser(User $user): ?array { + if (! Schema::hasTable('communication_messages')) { + return null; + } + $userExtension = trim((string) ($user->pbx_extension ?? '')); $watchedExtensions = app(PbxRoutingService::class)->getWatchedExtensionsForUser($user); $popupRestricted = (bool) ($user->pbx_popup_enabled ?? false) && count($watchedExtensions) > 0; diff --git a/app/Services/Cti/PbxRoutingService.php b/app/Services/Cti/PbxRoutingService.php index 300279f..dea2fae 100644 --- a/app/Services/Cti/PbxRoutingService.php +++ b/app/Services/Cti/PbxRoutingService.php @@ -57,7 +57,7 @@ public function resolveByExtension(?string $extension): array } $stabileId = null; - if (method_exists($user, 'stabiliAssegnati')) { + if ($this->canResolveAssignedStabili() && method_exists($user, 'stabiliAssegnati')) { $stabileId = $user->stabiliAssegnati()->value('stabili.id'); } @@ -119,7 +119,7 @@ public function resolveByIncomingLine(?string $line): array if ($mappedUser instanceof User) { $targetRouting = [ 'user_id' => (int) $mappedUser->id, - 'stabile_id' => method_exists($mappedUser, 'stabiliAssegnati') ? ((int) ($mappedUser->stabiliAssegnati()->value('stabili.id') ?? 0) ?: null): null, + 'stabile_id' => $this->canResolveAssignedStabili() && method_exists($mappedUser, 'stabiliAssegnati') ? ((int) ($mappedUser->stabiliAssegnati()->value('stabili.id') ?? 0) ?: null): null, 'user' => $mappedUser, ]; } @@ -335,13 +335,18 @@ private function resolveAmministratoreForUser(User $user): ?Amministratore } $amministratoreId = null; - if (method_exists($user, 'stabiliAssegnati')) { + if ($this->canResolveAssignedStabili() && method_exists($user, 'stabiliAssegnati')) { $amministratoreId = $user->stabiliAssegnati()->value('stabili.amministratore_id'); } return $amministratoreId ? Amministratore::query()->find((int) $amministratoreId) : null; } + private function canResolveAssignedStabili(): bool + { + return Schema::hasTable('collaboratore_stabile') && Schema::hasTable('stabili'); + } + private function resolveAmministratoreIdForUser(User $user): ?int { $amministratore = $this->resolveAmministratoreForUser($user); diff --git a/composer.json b/composer.json index 1ad2467..fef557f 100755 --- a/composer.json +++ b/composer.json @@ -78,7 +78,14 @@ }, "extra": { "laravel": { - "dont-discover": [] + "dont-discover": [ + "laravel/breeze", + "laravel/pail", + "laravel/sail", + "nunomaduro/collision", + "nunomaduro/termwind", + "pestphp/pest-plugin-laravel" + ] } }, "config": { diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 8cb9174..bbe1e74 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -3,6 +3,12 @@ set -euo pipefail cd /var/www +if [[ -d /opt/netgescon/public ]]; then + mkdir -p /var/www/public + find /var/www/public -mindepth 1 -maxdepth 1 -exec rm -rf {} + 2>/dev/null || true + cp -a /opt/netgescon/public/. /var/www/public/ +fi + if [[ -n "${MYSQL_SSL_VERIFY_SERVER_CERT:-}" ]]; then cat > /root/.my.cnf <