Updated nginx prod configuration

Added schedule upload functionality in Filament
Updated .gitignore in public directory
This commit is contained in:
F4ilji
2026-01-18 01:20:16 +05:00
parent 1f61601e05
commit afda333c25
4 changed files with 115 additions and 35 deletions
+34 -11
View File
@@ -14,12 +14,10 @@ server {
return 301 https://www.ntspi.ru$request_uri;
}
server {
listen 443 ssl http2;
server_name www.ntspi.ru;
# SSL-конфигурация
ssl_certificate /etc/letsencrypt/live/www.ntspi.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.ntspi.ru/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/www.ntspi.ru/chain.pem;
@@ -34,7 +32,6 @@ server {
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Ваши текущие настройки
gzip on;
gzip_disable "msie6";
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
@@ -49,13 +46,46 @@ server {
root /var/www/public;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
location ~ ^/(upload|storage|images|docs|files)/.*\.php$ {
deny all;
return 404;
}
location ~ ^/storage/app/public/upload/iblock/.*\.php$ {
deny all;
return 404;
}
location ~ ^/vendor/.*\.php$ {
deny all;
return 404;
}
location ~ /vikon_core/internal/config\.php$ {
deny all;
return 404;
}
location ~ ^/vikon_core/internal/ {
deny all;
return 404;
}
location ~ ^/vikon_core/(?!update/index\.php|update/make_executor\.php).*\.php$ {
deny all;
return 404;
}
location ~ /\.(?!well-known).* {
deny all;
}
location / {
add_header Access-Control-Allow-Origin *;
try_files $uri /index.php?$args;
@@ -99,13 +129,6 @@ server {
fastcgi_param HTTPS On;
}
location ~ /\.ht {
deny all;
}
# Блок для обновления Let's Encrypt (оставить!)
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
+1 -1
View File
@@ -172,7 +172,7 @@ class ScheduleResource extends Resource
'index' => Pages\ListSchedules::route('/'),
'create' => Pages\CreateSchedule::route('/create'),
'edit' => Pages\EditSchedule::route('/{record}/edit'),
'upload' => Pages\ScheduleResource\Pages\UploadSchedules::route('/upload'), // Добавьте эту строку
'upload' => Pages\ScheduleResource\Pages\UploadSchedules::route('/upload'),
];
}
}
@@ -104,11 +104,7 @@ class UploadSchedules extends Page
return true; // Уже обработан, ничего не делаем
}
$groupTitleParts = explode(' ', $originalFileName, 3);
$groupSearchTitle = implode(' ', array_slice($groupTitleParts, 0, 2));
$educationalGroup = EducationalGroup::where('title', 'like', $groupSearchTitle . '%')
->first();
$educationalGroup = $this->findEducationalGroupByFileName($originalFileName);
if (!$educationalGroup) {
$this->failedFiles[] = $originalFileNameWithExtension;
@@ -157,11 +153,6 @@ class UploadSchedules extends Page
});
$this->processedFiles[] = $originalFileNameWithExtension;
Notification::make()
->title('Расписание успешно загружено')
->body("Файл `{$originalFileNameWithExtension}` для группы `{$educationalGroup->title}`")
->success()
->send();
return true;
} catch (\Throwable $e) {
@@ -199,20 +190,30 @@ class UploadSchedules extends Page
}
// Отправляем итоговое уведомление
if (!empty($this->processedFiles) || !empty($this->failedFiles)) {
$message = '';
if (!empty($this->processedFiles)) {
$message .= 'Успешно обработаны: ' . implode(', ', $this->processedFiles) . '. ';
}
if (!empty($this->failedFiles)) {
$message .= 'Не удалось обработать: ' . implode(', ', $this->failedFiles) . '. ';
$hasProcessed = !empty($this->processedFiles);
$hasFailed = !empty($this->failedFiles);
if ($hasProcessed || $hasFailed) {
$notification = Notification::make();
if ($hasProcessed && $hasFailed) {
$notification
->title('Частично обработано')
->body('Успешно: ' . count($this->processedFiles) . '. Ошибки: ' . count($this->failedFiles) . '. Подробности в логах.')
->warning();
} elseif ($hasProcessed) {
$notification
->title('Все файлы успешно загружены')
->body('Успешно обработано: ' . count($this->processedFiles) . ' файл(ов)')
->success();
} else { // $hasFailed only
$notification
->title('Ошибка при обработке файлов')
->body('Ошибки при обработке: ' . count($this->failedFiles) . ' файл(ов)')
->danger();
}
Notification::make()
->title('Результаты загрузки файлов')
->body($message)
->info()
->send();
$notification->send();
}
// Очищаем форму и связанные свойства после обработки
@@ -242,4 +243,58 @@ class UploadSchedules extends Page
{
return __('Расписание и группы');
}
/**
* Находит образовательную группу по названию файла, используя различные стратегии сопоставления
*
* @param string $fileName
* @return EducationalGroup|null
*/
protected function findEducationalGroupByFileName(string $fileName): ?EducationalGroup
{
// Удаляем расширение файла, если оно есть
$fileNameWithoutExt = pathinfo($fileName, PATHINFO_FILENAME);
// Попытка 1: поиск точного совпадения
$exactMatch = EducationalGroup::where('title', $fileNameWithoutExt)->first();
if ($exactMatch) {
return $exactMatch;
}
// Попытка 2: используем регулярные выражения для поиска групп вида БФКф-2531, Нт-102 и т.д.
// Шаблон: любая последовательность букв/цифр с дефисом и цифрой
if (preg_match('/([А-Яа-яЁёA-Za-z]+[-_\s]?[\dА-Яа-яЁёA-Za-z]*\d)/u', $fileName, $matches)) {
$potentialGroupCode = trim($matches[1]);
// Убираем лишние символы вроде пробелов и дефисов в конце
$potentialGroupCode = preg_replace('/[-_\s]+$/', '', $potentialGroupCode);
// Попытка найти группу по коду
$group = EducationalGroup::where('title', 'like', '%' . $potentialGroupCode . '%')->first();
if ($group) {
return $group;
}
}
// Попытка 3: разбиение на слова и поиск по первым двум словам (старая логика)
$words = preg_split('/\s+/', $fileName, 3); // разбиваем на 3 части максимум
if (count($words) >= 2) {
$groupTitleFromWords = $words[0] . ' ' . $words[1];
$group = EducationalGroup::where('title', 'like', $groupTitleFromWords . '%')->first();
if ($group) {
return $group;
}
}
// Попытка 4: если осталось только одно слово
if (isset($words[0])) {
$group = EducationalGroup::where('title', 'like', $words[0] . '%')->first();
if ($group) {
return $group;
}
}
// Если ничего не нашли, возвращаем null
return null;
}
}
+2
View File
@@ -12,3 +12,5 @@ robots.txt
./sveden
./abitur
sitemap.xml
yandex_ab757c77d75e2b8d.html
vikon_core