Updated nginx prod configuration
Added schedule upload functionality in Filament Updated .gitignore in public directory
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
+77
-22
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,3 +12,5 @@ robots.txt
|
||||
./sveden
|
||||
./abitur
|
||||
sitemap.xml
|
||||
yandex_ab757c77d75e2b8d.html
|
||||
vikon_core
|
||||
Reference in New Issue
Block a user