Запуск приложения Nextjs через менеджера процессов PM2

PM2 — это популярный процессный менеджер для Node.js приложений, который облегчает управление процессами в продакшн-среде. Он используется для мониторинга, управления и автоматического перезапуска Node.js приложений. PM2 предоставляет ряд удобных функций, которые делают развертывание и обслуживание серверных приложений более удобными и эффективными.

Зачем использовать PM2 для запуска приложений?

  1. Автоматический перезапуск: PM2 автоматически перезапускает ваше приложение в случае его сбоя или после внесения изменений в код. Это особенно полезно для продакшн-серверов, где важно минимизировать время простоя.
  2. Мониторинг процессов: PM2 предоставляет удобные инструменты для мониторинга приложений, такие как просмотр логов, статистики и состояния процессов. С помощью команды pm2 status вы можете быстро узнать, какие приложения запущены, их текущий статус, использование ресурсов и другую информацию.
  3. Управление несколькими процессами: PM2 позволяет запускать несколько экземпляров одного приложения, что идеально подходит для многозадачности и увеличения производительности, особенно на многоядерных серверах. Это важно для приложений, таких как Next.js, которые могут обрабатывать большое количество одновременных запросов.
  4. Простота конфигурации и использования: Запуск приложений через PM2 очень прост: всего несколько команд для запуска, мониторинга и перезапуска. PM2 также позволяет создавать конфигурационные файлы, которые помогают управлять приложениями в разных средах.
  5. Логирование: PM2 автоматически собирает логи ошибок и стандартного вывода, которые можно просматривать в реальном времени. Это помогает быстро диагностировать и устранять ошибки, а также отслеживать работу приложения.
  6. Поддержка автозапуска: PM2 может быть настроен для автоматического старта приложений при перезагрузке системы. Это гарантирует, что ваше приложение всегда будет запущено, даже если сервер был перезагружен.

Когда стоит использовать PM2?

PM2 полезен для тех случаев, когда ваше Node.js приложение:

  • Запускается в продакшн-среде, и требуется высокая доступность и стабильность.
  • Нужно обеспечить многозадачность или балансировку нагрузки между несколькими процессами.
  • Важна автоматизация процессов, таких как перезапуск приложения при сбоях.
  • Требуется легкий способ мониторинга и логирования приложения.

Преимущества использования PM2 для Next.js

Next.js — это фреймворк, который часто используется для создания серверных приложений и сайтов с серверным рендерингом (SSR). Использование PM2 для запуска приложения Next.js в продакшн-среде помогает решить следующие задачи:

  • Обеспечивает устойчивость приложения (автоматический перезапуск в случае сбоев).
  • Поддерживает многопроцессность для лучшего использования серверных ресурсов.
  • Упрощает процесс развертывания, так как PM2 автоматически обрабатывает перезапуск после изменений в коде.
  • Предоставляет удобные средства мониторинга и логирования, что важно для эффективного обслуживания приложения.

Таким образом, PM2 — это мощный инструмент для продвинутых пользователей и разработчиков, который помогает надежно запускать, управлять и мониторить Next.js приложения в продакшн-среде.

Как развернуть Next.js приложение с использованием PM2 для продакшн-среды?

Сначала нужно собрать оптимизированную версию вашего приложения:

Шаг 1: Сборка приложения Next.js для продакшна

Это создаст оптимизированную версию вашего приложения в папке .next

cd /var/www/my-site
npm run build

Шаг 2: Установите PM2

npm install -g pm2

Шаг 3: Создайте файл конфигурации для PM2

Создайте файл ecosystem.config.js в корне вашего проекта:

module.exports = {
  apps: [
    {
      name: "my-app",
      script: "npm",
      args: "start",
      cwd: "/var/www/my-site",  // Путь к вашему проекту
      env: {
        NODE_ENV: "production",
        PORT: 3000
      },
      instances: 1,
      autorestart: true,
      watch: false,
      max_memory_restart: "1G",
      log_date_format: "YYYY-MM-DD HH:mm:ss Z"
    }
  ]
};

Шаг 4: Запустите приложение с помощью PM2

pm2 start ecosystem.config.js

Шаг 5: Настройте автозапуск PM2 при старте системы

pm2 startup systemd

Шаг 6: Сохраните текущую конфигурацию PM2

pm2 save

Мониторинг работы

Вы можете проверять состояние приложения с помощью pm2 status и просматривать логи с помощью pm2 logs.

Проверка памяти: Используйте pm2 monit для мониторинга использования памяти и CPU.

Обновление приложения

Для обновления приложения на проде:

cd /var/www/my-site
git pull # или другой способ обновления кода
npm install
npm run build
pm2 restart lic-app

Как удалить все коммиты локально и затем запушить только один новый коммит?

Шаг 1: Удаление всей истории коммитов локально

Перейди в свой репозиторий:

cd /путь/к/вашему/репозиторию

Затем сбросьте всю историю, но с сохранением файлов:

git checkout --orphan new-branch

Теперь мы находимся в новой ветке без истории коммитов, но файлы остались.

Шаг 2: Удаление всех файлов из индекса

git reset --hard

Это уберёт все файлы из индекса, но не удалит их с диска.

git add .
git commit -m "Первый (и единственный) коммит"

Шаг 3: Перезапись истории в удалённом репозитории

git branch -M main  # если ветка должна быть main
git push --force origin main

Внимание! --force полностью перезапишет историю в GitHub-репозитории. Убедись, что вам не нужна старая история.

SQL. Как посчитать количество строк с нулевым значением (NULL)?

В SQL для подсчёта количества NULL значений в колонке можно использовать функцию COUNT вместе с CASE WHEN или SUM.

Вот несколько способов:

1. Использование COUNT с CASE WHEN

SELECT COUNT(*) - COUNT(column_name) AS null_count
FROM table_name;
  • COUNT(*) считает все строки.
  • COUNT(column_name) считает только не-NULL значения.
  • Разница между ними даёт количество NULL значений.

2. Использование SUM

SELECT SUM(CASE WHEN column_name IS NULL THEN 1 ELSE 0 END) AS null_count
FROM table_name;
  • CASE WHEN column_name IS NULL THEN 1 ELSE 0 END даёт 1 для NULL и 0 для остальных.
  • SUM суммирует единицы, что даёт общее количество NULL значений.

3. Альтернативный вариант с COUNT(CASE WHEN)

SELECT COUNT(CASE WHEN column_name IS NULL THEN 1 END) AS null_count
FROM table_name;
  • COUNT считает только строки, где CASE WHEN возвращает 1, то есть только NULL значения.

Скрипт для выключения Mac по завершению фильма в QuickTime Player и VLC

Как это работает с QuickTime Player?

Работа с QuickTime Player реализована через AppleScript, аналогично VLC, но есть некоторые особенности:

  1. Статус воспроизведения: Проверка осуществляется через свойство playing первого документа.
  2. Текущий файл: Получается имя документа, который сейчас открыт в плеере.
  3. Оставшееся время: Вычисляется как разница между общей продолжительностью документа и текущей позицией воспроизведения.

Главное отличие в работе с QuickTime — необходимость проверять наличие открытого документа, поскольку QuickTime может быть запущен без открытых файлов.

#!/bin/bash

# Скрипт для выключения Mac по завершению воспроизведения в VLC или QuickTime Player
# Принцип работы: скрипт периодически проверяет, активно ли воспроизведение в выбранном плеере
# Когда воспроизведение останавливается, компьютер выключается после настраиваемой задержки

# Настраиваемые параметры
SHUTDOWN_DELAY=5  # Задержка выключения в минутах после завершения воспроизведения
CHECK_INTERVAL=5  # Интервал проверки состояния плеера в секундах
PLAYER=""         # Будет определено автоматически или выбрано пользователем

# Функция для проверки, запущен ли плеер
is_player_running() {
    local player=$1
    pgrep -x "$player" >/dev/null
    return $?
}

# Функция для определения, какой медиаплеер используется
detect_player() {
    if is_player_running "VLC"; then
        echo "VLC"
        return 0
    elif is_player_running "QuickTime Player"; then
        echo "QuickTime Player"
        return 0
    else
        return 1
    fi
}

# Функция для проверки, воспроизводится ли что-то в VLC
is_vlc_playing() {
    local playing=$(osascript -e 'tell application "VLC" to get playing')
    if [ "$playing" = "true" ]; then
        return 0  # Воспроизведение активно
    else
        return 1  # Воспроизведение не активно
    fi
}

# Функция для получения оставшегося времени фильма в VLC
get_vlc_remaining_time() {
    # Получаем оставшееся время в секундах
    local remaining=$(osascript -e 'tell application "VLC" to get ((duration - time) as integer)')
    echo $remaining
}

# Функция для получения текущего файла в VLC
get_vlc_current_file() {
    local current_file=$(osascript -e 'tell application "VLC" to get name of current item')
    echo "$current_file"
}

# Функция для проверки, воспроизводится ли что-то в QuickTime
is_quicktime_playing() {
    local playing=$(osascript -e '
    tell application "QuickTime Player"
        if not (exists document 1) then
            return "false"
        end if
        return (playing of document 1)
    end tell
    ')
    if [ "$playing" = "true" ]; then
        return 0  # Воспроизведение активно
    else
        return 1  # Воспроизведение не активно
    fi
}

# Функция для получения текущего файла в QuickTime
get_quicktime_current_file() {
    local current_file=$(osascript -e '
    tell application "QuickTime Player"
        if not (exists document 1) then
            return "Неизвестный файл"
        end if
        return name of document 1
    end tell
    ')
    echo "$current_file"
}

# Функция для получения оставшегося времени в QuickTime (приблизительно)
get_quicktime_remaining_time() {
    local duration=$(osascript -e '
    tell application "QuickTime Player"
        if not (exists document 1) then
            return 0
        end if
        return duration of document 1
    end tell
    ')
    
    local current_time=$(osascript -e '
    tell application "QuickTime Player"
        if not (exists document 1) then
            return 0
        end if
        return current time of document 1
    end tell
    ')
    
    local remaining=$((duration - current_time))
    echo $remaining
}

# Функция для форматирования времени
format_time() {
    local seconds=$1
    local minutes=$((seconds / 60))
    local remaining_seconds=$((seconds % 60))
    echo "$minutes минут и $remaining_seconds секунд"
}

# Функция для отображения справки
show_help() {
    echo "Использование: $0 [опции]"
    echo "Опции:"
    echo "  -p, --player PLAYER    Указать плеер (vlc или quicktime)"
    echo "  -d, --delay MINUTES    Задать задержку выключения в минутах (по умолчанию: 5)"
    echo "  -h, --help             Показать эту справку"
    echo ""
    echo "Пример: $0 --player vlc --delay 10"
    exit 0
}

# Обработка аргументов командной строки
while [[ $# -gt 0 ]]; do
    key="$1"
    case $key in
        -p|--player)
            case "$2" in
                vlc|VLC)
                    PLAYER="VLC"
                    ;;
                quicktime|QuickTime|"QuickTime Player")
                    PLAYER="QuickTime Player"
                    ;;
                *)
                    echo "Ошибка: неизвестный плеер: $2"
                    echo "Поддерживаемые плееры: vlc, quicktime"
                    exit 1
                    ;;
            esac
            shift 2
            ;;
        -d|--delay)
            SHUTDOWN_DELAY="$2"
            shift 2
            ;;
        -h|--help)
            show_help
            ;;
        *)
            echo "Неизвестный параметр: $1"
            show_help
            ;;
    esac
done

# Если плеер не указан, пытаемся определить его автоматически
if [ -z "$PLAYER" ]; then
    PLAYER=$(detect_player)
    if [ $? -ne 0 ]; then
        echo "Ошибка: не найден запущенный медиаплеер (VLC или QuickTime Player)."
        echo "Запустите медиаплеер и начните воспроизведение, затем выполните скрипт снова."
        echo "Или укажите плеер вручную с помощью параметра --player."
        exit 1
    fi
fi

echo "Выбран плеер: $PLAYER"

# Проверяем, запущен ли выбранный плеер
if ! is_player_running "$PLAYER"; then
    echo "$PLAYER не запущен. Запустите $PLAYER и начните воспроизведение, затем выполните скрипт снова."
    exit 1
fi

# Проверяем, воспроизводится ли что-то в выбранном плеере
if [ "$PLAYER" = "VLC" ]; then
    if ! is_vlc_playing; then
        echo "В VLC ничего не воспроизводится. Начните воспроизведение фильма, затем выполните скрипт снова."
        exit 1
    fi
    
    # Получаем информацию о воспроизведении
    current_file=$(get_vlc_current_file)
    remaining_seconds=$(get_vlc_remaining_time)
elif [ "$PLAYER" = "QuickTime Player" ]; then
    if ! is_quicktime_playing; then
        echo "В QuickTime Player ничего не воспроизводится. Начните воспроизведение фильма, затем выполните скрипт снова."
        exit 1
    fi
    
    # Получаем информацию о воспроизведении
    current_file=$(get_quicktime_current_file)
    remaining_seconds=$(get_quicktime_remaining_time)
fi

# Выводим информацию
echo "Обнаружен воспроизводимый файл: $current_file"
echo "Оставшееся время: $(format_time $remaining_seconds)"
echo "Компьютер будет выключен после завершения воспроизведения."
echo "После завершения воспроизведения компьютер выключится через $SHUTDOWN_DELAY минут."
echo "Для отмены этого скрипта нажмите Ctrl+C."

# Цикл ожидания завершения воспроизведения
last_update_time=0
while true; do
    # Проверяем, запущен ли плеер
    if ! is_player_running "$PLAYER"; then
        echo "$PLAYER был закрыт. Начинаем процедуру выключения..."
        break
    fi
    
    # Проверяем состояние воспроизведения в зависимости от плеера
    if [ "$PLAYER" = "VLC" ]; then
        if ! is_vlc_playing; then
            echo "Воспроизведение в VLC завершено. Начинаем процедуру выключения..."
            break
        fi
        
        # Обновляем информацию каждые CHECK_INTERVAL секунд
        current_time=$(date +%s)
        if [ $((current_time - last_update_time)) -ge $CHECK_INTERVAL ]; then
            remaining_seconds=$(get_vlc_remaining_time)
            echo "Оставшееся время: $(format_time $remaining_seconds)"
            last_update_time=$current_time
        fi
    elif [ "$PLAYER" = "QuickTime Player" ]; then
        if ! is_quicktime_playing; then
            echo "Воспроизведение в QuickTime Player завершено. Начинаем процедуру выключения..."
            break
        fi
        
        # Обновляем информацию каждые CHECK_INTERVAL секунд
        current_time=$(date +%s)
        if [ $((current_time - last_update_time)) -ge $CHECK_INTERVAL ]; then
            remaining_seconds=$(get_quicktime_remaining_time)
            echo "Оставшееся время: $(format_time $remaining_seconds)"
            last_update_time=$current_time
        fi
    fi
    
    # Пауза между проверками для снижения нагрузки
    sleep 1
done

# Показываем уведомление о предстоящем выключении
osascript -e "display notification \"Компьютер будет выключен через $SHUTDOWN_DELAY минут\" with title \"Автоматическое выключение\""

# Запускаем таймер выключения
sudo shutdown -h +$SHUTDOWN_DELAY

echo "Компьютер будет выключен через $SHUTDOWN_DELAY минут."
echo "Чтобы отменить выключение, выполните команду: sudo shutdown -c"

Как использовать скрипт

  1. Базовое использование:
./media_shutdown.sh

Скрипт автоматически определит, какой плеер запущен.

  1. Указание плеера вручную:
./media_shutdown.sh --player vlc
./media_shutdown.sh --player quicktime
  1. Изменение задержки выключения:
./media_shutdown.sh --delay 10

Технические особенности

  1. Низкая нагрузка на систему: Скрипт проверяет состояние воспроизведения каждую секунду, но обновляет информацию о времени только каждые 5 секунд, что снижает нагрузку на систему.
  2. Универсальные функции: Код организован по принципу модульности — для каждого плеера есть отдельные функции, что упрощает добавление поддержки других плееров в будущем.
  3. Обработка ошибок: Скрипт корректно обрабатывает ситуации, когда плеер закрывается или фильм останавливается, и в любом случае запускает процедуру выключения.

Скрипт для выключения Mac по завершению фильма в VLC

Как работает скрипт

Скрипт использует несколько важных механизмов:

  1. AppleScript для связи с VLC: macOS позволяет использовать AppleScript для взаимодействия с приложениями. Через эти команды скрипт может узнать, играет ли сейчас фильм и сколько времени осталось до его окончания.
  2. Проверка состояния воспроизведения: Скрипт постоянно (каждую секунду) проверяет, продолжает ли VLC воспроизведение. Как только воспроизведение останавливается, запускается процедура выключения.
  3. Отложенное выключение: После завершения фильма скрипт не выключает компьютер немедленно, а устанавливает таймер на 5 минут. Это даёт вам возможность отменить выключение, если вы решите посмотреть ещё что-то.
  4. Информирование пользователя: Скрипт показывает системное уведомление о предстоящем выключении и выводит в терминал информацию о том, как отменить выключение.

Как использовать скрипт

  1. Сохраните код в файл, например vlc_shutdown.sh
  2. Сделайте файл исполняемым: chmod +x vlc_shutdown.sh
  3. Запустите VLC и начните воспроизведение фильма
  4. Запустите скрипт: ./vlc_shutdown.sh

Какие проблемы решает этот подход

Этот скрипт имеет несколько преимуществ перед простым таймером выключения:

  • Адаптивность: Скрипт автоматически определяет, сколько времени осталось до конца фильма, вместо того чтобы полагаться на фиксированное время.
  • Интеллектуальное поведение: Если вы остановите фильм или закроете VLC, скрипт все равно сработает и выключит компьютер.
  • Безопасность: Задержка в 5 минут перед выключением даёт вам время отменить выключение, если вы передумали.
#!/bin/bash

# Скрипт для выключения Mac по завершению воспроизведения в VLC
# Принцип работы: скрипт периодически проверяет, активно ли воспроизведение в VLC
# Когда воспроизведение останавливается, компьютер выключается после небольшой задержки

# Функция для проверки, запущен ли VLC
is_vlc_running() {
    pgrep -x VLC >/dev/null
    return $?
}

# Функция для проверки, воспроизводится ли что-то в VLC
is_vlc_playing() {
    # Используем AppleScript для проверки статуса воспроизведения
    playing=$(osascript -e 'tell application "VLC" to get playing')
    if [ "$playing" = "true" ]; then
        return 0  # Воспроизведение активно
    else
        return 1  # Воспроизведение не активно
    fi
}

# Функция для получения оставшегося времени фильма
get_remaining_time() {
    # Получаем оставшееся время в секундах
    remaining=$(osascript -e 'tell application "VLC" to get ((duration - time) as integer)')
    echo $remaining
}

# Проверяем, запущен ли VLC
if ! is_vlc_running; then
    echo "VLC не запущен. Запустите VLC и начните воспроизведение фильма, затем выполните скрипт снова."
    exit 1
fi

# Проверяем, воспроизводится ли что-то
if ! is_vlc_playing; then
    echo "В VLC ничего не воспроизводится. Начните воспроизведение фильма, затем выполните скрипт снова."
    exit 1
fi

# Получаем оставшееся время фильма и название файла
remaining_seconds=$(get_remaining_time)
current_file=$(osascript -e 'tell application "VLC" to get name of current item')

# Выводим информацию
echo "Обнаружен воспроизводимый файл: $current_file"
echo "Оставшееся время: $(($remaining_seconds / 60)) минут и $(($remaining_seconds % 60)) секунд"
echo "Компьютер будет выключен после завершения воспроизведения."

# Добавляем небольшую задержку перед выключением (5 минут)
shutdown_delay=5

echo "После завершения воспроизведения компьютер выключится через $shutdown_delay минут."
echo "Для отмены этого скрипта нажмите Ctrl+C."

# Цикл ожидания завершения воспроизведения
while true; do
    if ! is_vlc_running; then
        echo "VLC был закрыт. Начинаем процедуру выключения..."
        break
    fi
    
    if ! is_vlc_playing; then
        echo "Воспроизведение завершено. Начинаем процедуру выключения..."
        break
    fi
    
    # Обновляем оставшееся время каждые 30 секунд
    if (( $(date +%s) % 30 == 0 )); then
        remaining_seconds=$(get_remaining_time)
        echo "Оставшееся время: $(($remaining_seconds / 60)) минут и $(($remaining_seconds % 60)) секунд"
    fi
    
    # Пауза в 1 секунду между проверками
    sleep 1
done

# Показываем уведомление о предстоящем выключении
osascript -e "display notification \"Компьютер будет выключен через $shutdown_delay минут\" with title \"Автоматическое выключение\""

# Запускаем таймер выключения
sudo shutdown -h +$shutdown_delay

echo "Компьютер будет выключен через $shutdown_delay минут."
echo "Чтобы отменить выключение, выполните команду: sudo shutdown -c"

Cкрипт для выключения Mac в заданное время

Этот скрипт позволяет указать время выключения компьютера в формате ЧЧ:MM. Вот как им пользоваться:

  1. Сохраните скрипт в файл, например shutdown_timer.sh
  2. Сделайте его исполняемым командой: chmod +x shutdown_timer.sh
  3. Запустите скрипт с указанием времени: ./shutdown_timer.sh 23:30

Как работает скрипт:

  • Принимает время в формате ЧЧ:MM
  • Проверяет, что формат времени корректный
  • Вычисляет, сколько минут осталось до указанного времени
  • Использует стандартную команду shutdown для планирования выключения
  • Если указанное время уже прошло сегодня, планирует выключение на завтра

Для отмены запланированного выключения используйте команду sudo shutdown -c.

#!/bin/bash

# Функция для проверки, что время введено в правильном формате (HH:MM)
validate_time() {
    if [[ ! $1 =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then
        echo "Ошибка: введите время в формате ЧЧ:МM (например, 23:30)"
        exit 1
    fi
}

# Проверяем, указано ли время в аргументах
if [ $# -eq 0 ]; then
    echo "Пожалуйста, укажите время выключения в формате ЧЧ:MM"
    echo "Пример: $0 23:30"
    exit 1
fi

# Проверяем формат времени
validate_time "$1"

# Текущее время
current_time=$(date +%s)

# Преобразуем введенное время в секунды с начала дня
shutdown_hour=$(echo $1 | cut -d':' -f1)
shutdown_min=$(echo $1 | cut -d':' -f2)

# Вычисляем время выключения в секундах
target_time=$(date -j -f "%H:%M" "$shutdown_hour:$shutdown_min" +%s 2>/dev/null)

# Проверка на случай ошибки конвертации времени
if [ $? -ne 0 ]; then
    echo "Ошибка при обработке введенного времени."
    exit 1
fi

# Если целевое время уже прошло сегодня, переносим на завтра
if [ $target_time -le $current_time ]; then
    target_time=$((target_time + 86400)) # Добавляем 24 часа (в секундах)
fi

# Вычисляем разницу в секундах
seconds_diff=$((target_time - current_time))

# Выводим информацию
echo "Компьютер будет выключен в $1"
echo "До выключения осталось: $(($seconds_diff / 60)) минут"

# Устанавливаем команду выключения
sudo shutdown -h +$(($seconds_diff / 60))

# Выводим сообщение о том, как отменить выключение
echo "Чтобы отменить запланированное выключение, выполните команду:"
echo "sudo killall shutdown или sudo shutdown -c"

Как закрыть все страницы сайта и сделать их доступными только после авторизации?

Закрыть сайт вручную можно нехитрый через код.

  1. Откройте файл functions.php вашей темы.
  2. Добавьте следующий код:
function poet_restrict_access() {
    if ( ! is_user_logged_in() && ! is_page('login') ) {
        wp_redirect( wp_login_url() );
        exit;
    }
}
add_action( 'template_redirect', 'poet_restrict_access' );

Всё! Теперь сайт будет перенаправлять на страницу входа, если пользователь не авторизован.

Так как админка WordPress доступна только авторизованным пользователям, то «сюрпризов» не будет. Админка будет доступна 🙂

Разрешение доступа к определённым страницам (например, исключим страницу входа, регистрации и контактов)

function poet_restrict_access() {
    // Список страниц, которые остаются открытыми
    $allowed_pages = array( 'login', 'register', 'contact' ); 

    if ( ! is_user_logged_in() && ! is_page( $allowed_pages ) ) {
        wp_redirect( wp_login_url() ); // Перенаправление на страницу входа
        exit;
    }
}
add_action( 'template_redirect', 'poet_restrict_access' );

Если ваш сайт использует AJAX (например, в теме или плагинах), нужно разрешить доступ к AJAX-запросам, иначе они перестанут работать. Добавьте это в функцию:

function poet_restrict_access() {
    // Разрешить доступ к AJAX и cron-запросам
    if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
        return;
    }

    if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
        return;
    }

    // Список страниц, которые остаются открытыми
    $allowed_pages = array( 'login', 'register', 'contact' );

    if ( ! is_user_logged_in() && ! is_page( $allowed_pages ) ) {
        wp_redirect( wp_login_url() ); // Перенаправление на страницу входа
        exit;
    }
}
add_action( 'template_redirect', 'poet_restrict_access' );

Настройка редиректа после входа

function custom_login_redirect( $redirect_to, $request, $user ) {
    // Убедимся, что пользователь авторизован
    if ( isset( $user->roles ) && is_array( $user->roles ) ) {
        return home_url(); // Перенаправление на главную страницу
    }

    return $redirect_to;
}
add_filter( 'login_redirect', 'custom_login_redirect', 10, 3 );

Как закрыть и админку ?

Если вы хотите ограничить доступ к админке для определённых ролей или полностью исключить его, добавьте это:

function restrict_admin_access() {
    if ( ! current_user_can( 'manage_options' ) && ! wp_doing_ajax() ) {
        wp_redirect( home_url() );
        exit;
    }
}
add_action( 'admin_init', 'restrict_admin_access' );

Как добавить класс к корневому элементу html с помощью Javascript?

Добавление класса ‘js’ к корневому HTML-элементу обычно используется для того чтобы убедиться в поддержки JavaScript (чтобы можно было менять стили и поведение элементов в зависимости от поддержки JS)

Чистый JavaScript (ES6+):

document.documentElement.classList.add('js');

Vanilla JavaScript (более старый синтаксис):

document.documentElement.className += ' js';

С проверкой поддержки classList:

if (document.documentElement.classList) {
    document.documentElement.classList.add('js');
}

Универсальный вариант, который:

  • Проверяет поддержку classList
  • Если поддерживает — использует classList.add()
  • Если не поддерживает — использует старый метод конкатенации className
  • Добавляет пробел перед ‘js’, только если className уже не пустой
if ('classList' in document.documentElement) {
    document.documentElement.classList.add('js');
} else {
    document.documentElement.className += (document.documentElement.className ? ' ' : '') + 'js';
}

Эти варианты полностью заменяют jQuery-версию и добавляют класс ‘js’ к корневому HTML-элементу, что обычно используется для индикации поддержки JavaScript в браузере.

Альтернативный вариант, который дополнительно удаляет класс ‘no-js’, если он был установлен заранее.

document.documentElement.className = 
    document.documentElement.className.replace(/\bno-js\b/, '') + ' js';

Основные преимущества:

  • Не требуется подключение jQuery
  • Нативный JavaScript
  • Меньше кода
  • Лучшая производительность

Как выбрать соседний элемент после первого дочернего в JavaScript?

Чтобы выбрать соседний элемент после первого дочернего элемента в JavaScript, вы можете использовать свойство nextElementSibling. Вот пример кода:

// Получаем родительский элемент по классу
const parentElement = document.querySelector('.parent-class');

// Получаем первый дочерний элемент
const firstChild = parentElement.firstElementChild;

// Получаем соседний элемент после первого дочернего
const nextSibling = firstChild.nextElementSibling;

// Выводим результат в консоль
console.log(nextSibling);

Объяснение:

  • parentElement.firstElementChild возвращает первый дочерний элемент родителя.
  • firstChild.nextElementSibling возвращает следующий элемент-сосед после первого дочернего.

Таким образом, вы можете легко получить соседний элемент после первого дочернего. Замените '.parent-class' на ваш класс родителя.

Как добавить третье поле цены в карточку товара WooCommerce?

Этот код добавит третье поле цены в карточку товара сайта WooCommerce с возможностью сохранения, редактирования и вывода на фронтенде.

1. Регистрация мета-поля в functions.php:

function poet_register_additional_price_meta() {
    add_post_type_support('product', 'custom_price_field');
    
    register_post_meta('product', 'additional_product_price', [
        'show_in_rest' => true,
        'type' => 'number',
        'single' => true,
        'default' => '',
    ]);
}
add_action('init', 'poet_register_additional_price_meta');

2. Добавление поля в панель редактирования карточки товара:

function poet_add_custom_price_field_to_product_data() {
    woocommerce_wp_text_input([
        'id' => 'additional_product_price',
        'label' => __('Дополнительная цена', 'textdomain'). ' (' . get_woocommerce_currency_symbol() . ')',
        'placeholder' => __('Введите дополнительную цену', 'textdomain'),
        'desc_tip' => true,
        'description' => __('Третье поле цены для специальных условий', 'textdomain')
    ]);
}
add_action('woocommerce_product_options_pricing', 'poet_add_custom_price_field_to_product_data');

3. Сохранение значения поля:

function poet_save_additional_price_meta($post_id) {
    $additional_price = isset($_POST['additional_product_price']) 
        ? wc_clean($_POST['additional_product_price']) 
        : '';
    
    update_post_meta($post_id, 'additional_product_price', $additional_price);
}
add_action('woocommerce_process_product_meta', 'poet_save_additional_price_meta');

4. Сохранение значения поля:

function poet_display_additional_product_price() {
    global $product;
    
    $additional_price = get_post_meta($product->get_id(), 'additional_product_price', true);
    
    if ($additional_price) {
        echo '<div class="additional-product-price">';
        echo '<span class="label">' . __('Специальная цена:', 'textdomain') . '</span>';
        echo '<span class="price">' . wc_price($additional_price) . '</span>';
        echo '</div>';
    }
}
add_action('woocommerce_single_product_summary', 'poet_display_additional_product_price', 25);

5. Для REST API (опционально):

function poet_add_additional_price_to_product_response($response, $post, $request) {
    $additional_price = get_post_meta($post->ID, 'additional_product_price', true);
    
    if ($additional_price) {
        $response->data['additional_price'] = $additional_price;
    }
    
    return $response;
}
add_filter('woocommerce_rest_product_object_response', 'poet_add_additional_price_to_product_response', 10, 3);

Как сделать выбор элементов из DOM на JavaScript?

В этой статье проведём обзор способов выбора элементов на чистом JavaScript.

Базовые методы querySelector:

// Выбор первого совпадения
const element1 = document.querySelector('.class');      // по классу
const element2 = document.querySelector('#id');         // по ID
const element3 = document.querySelector('div');         // по тегу
const element4 = document.querySelector('[data-attr]'); // по атрибуту

// Выбор ВСЕХ совпадений
const elements1 = document.querySelectorAll('.class');

Классические методы DOM:

// По ID
const elementById = document.getElementById('myId');

// По имени класса
const elementsByClass = document.getElementsByClassName('myClass');

// По тегу
const elementsByTag = document.getElementsByTagName('div');

Вложенные селекторы:

// Nested селекторы
const nestedElement = document.querySelector('.parent .child');

// Прямой потомок
const directChild = document.querySelector('.parent > .child');

// Nth элемент
const nthElement = document.querySelector('.list li:nth-child(2)');

Методы с деструктуризацией:

// Первый и последний элемент
const [firstElement] = document.querySelectorAll('.items');
const [lastElement] = [...document.querySelectorAll('.items')].reverse();

Поиск ближайших элементов:

// Родительский элемент
const parentElement = currentElement.parentElement;

// Дочерние элементы
const childElements = currentElement.children;

// Соседние элементы
const nextSibling = currentElement.nextElementSibling;
const prevSibling = currentElement.previousElementSibling;

Безопасный выбор с проверкой:

Пример реализации через функцию:

function safeSelect(selector) {
    const element = document.querySelector(selector);
    
    if (!element) {
        console.warn(`Элемент с селектором ${selector} не найден`);
        return null;
    }
    
    return element;
}

? Рекомендации:

  • querySelector медленнее, чем getElementById
  • Используйте const для неизменяемых селекторов
  • Кэшируйте часто используемые элементы
  • Проверяйте существование элемента перед работой
  • Для производительности используйте делегирование событий
  • Учтите, что querySelectorAll() возвращает NodeList
  • Всегда проверяйте поддержку браузеров

? Производительность (от быстрого к медленному):

  1. getElementById() — Самый быстрый
  2. getElementsByClassName()
  3. getElementsByTagName()
  4. querySelector()
  5. querySelectorAll()

Как автоматически заполнять поле alt и title при загрузке картинок в WordPress?

Вот полный код плагина для WordPress, который автоматически прописывает alt и title при загрузке изображений:

<?php
/*
Plugin Name: Auto Image Alt and Title
Plugin URI: http://yoursite.com
Description: Автоматическая генерация alt и title для загружаемых изображений
Version: 1.0
Author: Your Name
Author URI: http://yoursite.com
*/

class AutoImageMetaPlugin {
    public function __construct() {
        // Хуки для обработки загрузки изображений
        add_filter('wp_generate_attachment_metadata', [$this, 'auto_set_image_meta'], 10, 2);
        add_action('add_attachment', [$this, 'set_image_meta_on_upload'], 10, 1);
    }

    // Генерация метаданных при загрузке
    public function set_image_meta_on_upload($post_id) {
        // Проверяем, что это изображение
        if (wp_attachment_is_image($post_id)) {
            $image_meta = $this->generate_image_meta($post_id);
            
            // Обновляем метаданные
            update_post_meta($post_id, '_wp_attachment_image_alt', $image_meta['alt']);
            
            // Обновляем заголовок вложения
            wp_update_post([
                'ID' => $post_id,
                'post_title' => $image_meta['title']
            ]);
        }
    }

    // Генерация метаданных при создании миниатюр
    public function auto_set_image_meta($metadata, $attachment_id) {
        if (wp_attachment_is_image($attachment_id)) {
            $image_meta = $this->generate_image_meta($attachment_id);
            
            // Обновляем метаданные изображения
            if (!isset($metadata['image_meta'])) {
                $metadata['image_meta'] = [];
            }
            
            $metadata['image_meta']['title'] = $image_meta['title'];
            
            return $metadata;
        }
        
        return $metadata;
    }

    // Генерация метаданных для изображения
    private function generate_image_meta($attachment_id) {
        // Получаем информацию о файле
        $file_path = get_attached_file($attachment_id);
        $file_name = basename($file_path);
        
        // Очищаем имя файла
        $clean_name = $this->sanitize_filename($file_name);
        
        // Генерация заголовка и alt
        $title = $this->generate_title($clean_name);
        $alt = $this->generate_alt($clean_name);
        
        return [
            'title' => $title,
            'alt' => $alt
        ];
    }

    // Очистка имени файла
    private function sanitize_filename($filename) {
        // Удаляем расширение
        $filename = pathinfo($filename, PATHINFO_FILENAME);
        
        // Заменяем служебные символы
        $filename = preg_replace('/[^a-zA-ZА-Яа-я0-9\s-]/', '', $filename);
        
        // Заменяем несколько пробелов на один
        $filename = preg_replace('/\s+/', ' ', $filename);
        
        return trim($filename);
    }

    // Генерация заголовка
    private function generate_title($clean_name) {
        // Capitalize first letter
        $title = ucfirst(str_replace(['-', '_'], ' ', $clean_name));
        
        // Усечение до разумной длины
        return mb_substr($title, 0, 100);
    }

    // Генерация alt
    private function generate_alt($clean_name) {
        // Преобразуем в нижний регистр
        $alt = strtolower(str_replace(['-', '_'], ' ', $clean_name));
        
        // Добавляем общие теги
        $alt_variants = [
            $alt . ' фото',
            $alt . ' изображение',
            'фото ' . $alt,
            'изображение ' . $alt
        ];
        
        // Случайный выбор варианта
        return $alt_variants[array_rand($alt_variants)];
    }

    // Административные настройки (опционально)
    public function add_settings_page() {
        add_options_page(
            'Настройки Auto Image Meta', 
            'Auto Image Meta', 
            'manage_options', 
            'auto-image-meta', 
            [$this, 'render_settings_page']
        );
    }

    // Рендер страницы настроек
    public function render_settings_page() {
        ?>
        <div class="wrap">
            <h1>Настройки Auto Image Meta</h1>
            <form method="post" action="options.php">
                <p>Автоматическая генерация alt и title для изображений</p>
                <!-- Можно добавить дополнительные настройки -->
            </form>
        </div>
        <?php
    }
}

// Инициализация плагина
function init_auto_image_meta_plugin() {
    new AutoImageMetaPlugin();
}
add_action('plugins_loaded', 'init_auto_image_meta_plugin');

// Хук для обновления существующих изображений (опционально)
function bulk_update_image_meta() {
    $attachments = get_posts([
        'post_type' => 'attachment',
        'post_mime_type' => 'image',
        'numberposts' => -1
    ]);

    $plugin = new AutoImageMetaPlugin();

    foreach ($attachments as $attachment) {
        $plugin->set_image_meta_on_upload($attachment->ID);
    }
}
register_activation_hook(__FILE__, 'bulk_update_image_meta');

Функционал плагина:

  1. Автоматическая генерация alt и title
  2. Очистка имени файла
  3. Несколько вариантов генерации alt
  4. Capitalize заголовка
  5. Ограничение длины

Преимущества:

  • Полностью автоматическая работа
  • Поддержка кириллицы
  • Случайный выбор alt-текста
  • Очистка имени файла
  • Минимальные настройки

Установка:

  1. Сохраните код в файл auto-image-meta.php
  2. Загрузите в папку wp-content/plugins/
  3. Активируйте в панели управления

Рекомендации:

  • Протестируйте на staging-сервере
  • Настройте под свои нужды
  • Используйте осторожно с большим количеством изображений

Дополнительно:

  • Можно добавить страницу настроек
  • Реализовать кастомные правила генерации
  • Добавить поддержку мультиязычности

Примечание: При первой активации плагин обновит метаданные для всех существующих изображений.