Домашнее облако на роутере keenetic/netcraze с nextcloud

Давно хотел сменить домашний роутер, но пока провайдерский работал, не находил повода тратиться на это. А тут внезапно "1Тб навсегда" от Мэйла превратился в тыкву. Да и недоступность Яндекс.Диск доставляет некоторые неудобства, хоть и винить за это ребят из Яндекса я не могу. В общем появилась задача максимально бюджетно организовать хранилище фото и не очень важных файлов. Выбор пал на домашний сетап: роутер Netcraze Hopper + обычный внешний диск с usb3 (в качестве диска Adata HD710 Pro на 4тб). Поднять nextcloud вроде бы не рокет сайнс, но, учитывая ограничения окружения, были свои нюансы, о которых я расскажу в данном гайде.
Вводные требования
Бюджетно
Есть мобильное приложение
Синхронизация фото
Возможность шарить доступ между пользователями
Хранение данных на fs без лишних абстракций (например без s3)
Опционально: доступ из интернета
Подготовка диска
Для меня важна возможность в любой момент воткнуть диск в ПК с виндой, поэтому разбивка получилась следующая:
- swap: 2гб
- ext4: 25гб (под entware)
- ntfs: всё остальное пространство (хранилище nextcloud и smb шара)
Установка Entware хорошо описана в официальной документации, поэтому останавливаться на этом не буду.
Теперь установим необходимые пакеты для nextcloud
Если хочется генерировать превью для видео, нужен ffmpeg.
opkg update
opkg install ffmpeg libffmpegthumbnailerСтавим nginx + php-fpm
На текущий момент в entware версия php 8.4, поэтому проблем с запуском nextvloud 35 не будет.
opkg install bzip2 curl glib2 gnupg nginx-mod-headers-more nginx-ssl imagemagick php8 php8-cgi php8-cli php8-fpm php8-mod-ctype php8-mod-curl php8-mod-dom php8-mod-exif php8-mod-fileinfo php8-mod-filter php8-mod-gd php8-mod-iconv php8-mod-intl php8-mod-mbstring php8-mod-opcache php8-mod-openssl php8-mod-pcntl php8-mod-pdo php8-mod-pdo-sqlite php8-mod-session php8-mod-simplexml php8-mod-sodium php8-mod-sqlite3 php8-mod-xml php8-mod-xmlreader php8-mod-xmlwriter php8-mod-zip php8-pecl-apcu php8-pecl-imagick libopenssl libopenssl-conf wget-ssl zlib zlib-dev jqПодготовим конфиги
/opt/etc/php.ini
[PHP]
engine = On
precision = 12
output_buffering = Off
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = 100
disable_functions =
disable_classes =
expose_php = Off
max_execution_time = 3600
max_input_time = 3600
memory_limit = 128M
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED
display_errors = Off
display_startup_errors = Off
log_errors = Off
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
variables_order = "EGPCS"
request_order = "GP"
register_argc_argv = On
auto_globals_jit = On
post_max_size = 10000M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
doc_root = "/opt/share/www"
user_dir =
extension_dir = "/opt/lib/php8"
enable_dl = On
cgi.fix_pathinfo = 1
file_uploads = On
upload_tmp_dir = "/opt/tmp"
upload_max_filesize = 10000M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 60
opcache.enable=1
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=20000
opcache.save_comments=1
opcache.revalidate_freq=60
apc.enabled=1
apc.shm_size=32M
apc.enable_cli=0/opt/etc/php8-fpm.d/www.conf
запускаем от рута, потому что иначе будут проблемы с чтением файлов с ntfs, на котором лежит рабочая директория nextcloud вместе с базой sqlite
[www]
user = root
group = root
listen = /opt/var/run/php8-fpm.sock
listen.owner = root
listen.group = root
listen.mode = 0660
pm = ondemand
pm.max_children = 2
pm.process_idle_timeout = 30s;
pm.max_requests = 200
php_flag[display_errors] = off
php_admin_value[error_log] = /opt/var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 128M/opt/etc/nginx/nginx.conf
так же от рута, чтобы читать сокет php-fpm
user root;
worker_processes 1;
load_module /opt/lib/nginx/modules/ngx_http_headers_more_filter_module.so;
events {
worker_connections 64;
}
http {
server_tokens off;
include mime.types;
default_type application/octet-stream;
include conf.d/*;
}/opt/etc/nginx/conf.d/nextcloud.conf
почти стандартный из документации, с небольшими доработками
upstream php-handler {
server unix:/opt/var/run/php8-fpm.sock;
}
map $arg_v $asset_immutable {
"" "";
default ", immutable";
}
server {
listen 127.0.0.1:81;
server_name <домен>;
server_tokens off;
more_clear_headers Server;
root /opt/share/www;
client_max_body_size 10240M;
client_body_timeout 300s;
client_body_buffer_size 512k;
fastcgi_buffers 64 4K;
keepalive_timeout 60s;
send_timeout 300s;
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300s;
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types
application/atom+xml
text/javascript
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/wasm
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-XSS-Protection "0" always;
add_header Permissions-Policy "accelerometer=(), camera=(), display-capture=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;
add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always;
add_header Cross-Origin-Resource-Policy "same-site" always;
add_header X-DNS-Prefetch-Control "off" always;
fastcgi_hide_header X-Powered-By;
include mime.types;
types {
text/javascript mjs;
}
index index.php index.html /index.php$request_uri;
location = / {
if ($http_user_agent ~ ^DavClnt) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ^~ /.well-known {
location = /.well-known/carddav {
return 301 /remote.php/dav/;
}
location = /.well-known/caldav {
return 301 /remote.php/dav/;
}
location /.well-known/acme-challenge {
try_files $uri $uri/ =404;
}
location /.well-known/pki-validation {
try_files $uri $uri/ =404;
}
return 301 /index.php$request_uri;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) {
return 404;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
return 404;
}
location ~ ^/(?:composer\.(?:json|lock)|package(?:-lock)?\.json|core/shipped\.json)$ {
return 404;
}
location ~ \.php(?:$|/) {
rewrite ^/(?!index|remote|public|cron|status|ocs\/v[12]|ocs-provider\/.+|core\/ajax\/update|updater\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_pass php-handler;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
# The Keenetic nginx cuts these request headers.
fastcgi_param HTTP_ORIGIN "https://<домен>";
fastcgi_param HTTP_REFERER "https://<домен>/";
fastcgi_intercept_errors on;
fastcgi_request_buffering on;
fastcgi_max_temp_file_size 0;
}
location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|heic|png|webp|wasm|tflite|map|ogg|flac|mp4|webm)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463$asset_immutable";
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-XSS-Protection "0" always;
add_header Permissions-Policy "accelerometer=(), camera=(), display-capture=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;
add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always;
add_header Cross-Origin-Resource-Policy "same-site" always;
add_header X-DNS-Prefetch-Control "off" always;
access_log off;
}
location ~ \.(?:otf|woff2?)$ {
try_files $uri /index.php$request_uri;
expires 7d;
access_log off;
}
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}Вот тут первый важный нюанс. В конфиге nginx самого роутера для пользовательких приложений в location добавляется следующее
proxy_set_header Referer "";
proxy_set_header Origin "";
proxy_set_header Host "127.0.0.1";В связи с этим при попытке залогиниться будем получать “303 See Other” с редиректом на “/index.php/login?user=<юзернейм>&direct=1”, потому что в коде nextcloud есть проверка на origin
Поэтому в location с php нужно добавить восстановление этих хедеров.
# The Keenetic nginx cuts these request headers.
fastcgi_param HTTP_ORIGIN "https://<домен>";
fastcgi_param HTTP_REFERER "https://<домен>/";Да, это не очень безопасно, ибо как минимум пропустит запрос с любой вкладки любого другого сайта. Но более элегантного решения, чем такой костыль, я не нашёл. Если знаете такое, буду рад прочитать в комментах.
Ставим nextcloud
cd /opt/root
mkdir -p /opt/share/www
curl https://download.nextcloud.com/server/releases/nextcloud-35.0.2.tar.bz2 --output nextcloud-35.0.2.tar.bz2
bzip2 -d nextcloud-35.0.2.tar.bz2
tar -xv nextcloud-35.0.2.tar.bz2 -C /opt/share/www/Стартуем сервисы
/opt/etc/init.d/S79php8-fpm start
/opt/etc/init.d/S80nginx startБазовая настройка nextcloud
cd /opt/share/www
php8-cli occ maintenance:install --database=sqlite --admin-user=<админ_юзер> --admin-pass='<админ_пароль>' --data-dir='/tmp/mnt/<ид_диска>/nextcloud'
php8-cli occ config:system:set overwrite.cli.url --value="https://<домен>"
php8-cli occ config:system:set overwritehost --value=<домен>
php8-cli occ config:system:set overwriteprotocol --value=https
php8-cli occ config:system:set trusted_domains --type json --value '["localhost","127.0.0.1","192.168.1.1","<домен>"]'
php8-cli occ config:system:set memcache.local --value='\OC\Memcache\APCu'
# переключаем фоновые задачи на запуск по cron
php8-cli occ background:cron
# и добавим в cron
*/5 * * * * /opt/bin/php8-cli -f /opt/share/www/cron.php
# если нужны превью
php8-cli occ config:system:set enable_previews --value true
php8-cli occ config:system:set preview_max_memory --value 128
php8-cli occ config:system:set preview_max_x --value 1080
php8-cli occ config:system:set preview_max_y --value 1920
php8-cli occ config:system:set preview_concurrency_new --value 1
php8-cli occ config:system:set preview_concurrency_all --value 2
# и превью каких файлов хотим видеть (при передаче настройки массивом было некорректное экранирование)
php8-cli occ config:system:set enabledPreviewProviders 0 --value 'OC\Preview\JPEG'
php8-cli occ config:system:set enabledPreviewProviders 1 --value 'OC\Preview\PNG'
php8-cli occ config:system:set enabledPreviewProviders 2 --value 'OC\Preview\GIF'
php8-cli occ config:system:set enabledPreviewProviders 3 --value 'OC\Preview\BMP'
php8-cli occ config:system:set enabledPreviewProviders 4 --value 'OC\Preview\XBitmap'
php8-cli occ config:system:set enabledPreviewProviders 5 --value 'OC\Preview\HEIC'
php8-cli occ config:system:set enabledPreviewProviders 6 --value 'OC\Preview\Movie'
php8-cli occ config:system:set enabledPreviewProviders 7 --value 'OC\Preview\PDF'
php8-cli occ config:system:set enabledPreviewProviders 8 --value 'OC\Preview\MP4'
php8-cli occ config:system:set enabledPreviewProviders 9 --value 'OC\Preview\AVI'
php8-cli occ config:system:set enabledPreviewProviders 10 --value 'OC\Preview\MOV'
php8-cli occ config:system:set enabledPreviewProviders 11 --value 'OC\Preview\WebP'
php8-cli occ config:system:set enabledPreviewProviders 12 --value 'OC\Preview\JPG'
# если ставили ffmpeg
php8-cli occ config:system:set preview_ffmpeg_path --value="/opt/bin/ffmpeg"
# если хотим предварительно генерить превьюшки
php8-cli occ app:install previewgenerator
php8-cli occ config:app:set --value="32 64 256 1024 1920" previewgenerator squareSizes
php8-cli occ config:app:set --value="64 128 1024 1080 2030" previewgenerator widthSizes
php8-cli occ config:app:set --value="64 256 1024 1080 1920" previewgenerator heightSizes
# и добавим в cron. Но если планируете залить большое количество фоток после установки, лучше добавить после этого
*/15 * * * * /opt/bin/php8-cli /opt/share/www/occ preview:pre-generate
# лишние приложения nextcloud можно выключить, чтобы убрать дополнительную нагрузку
php8-cli occ app:disable <appname>
# для себя определил эти
# Disabled:
# - activity: 8.0.0 (installed 8.0.0)
# - admin_audit: 2.0.0
# - app_api: 35.0.0 (installed 35.0.0)
# - circles: 35.0.0 (installed 35.0.0)
# - comments: 2.0.0 (installed 2.0.0)
# - contactsinteraction: 2.0.0 (installed 2.0.0)
# - dashboard: 8.0.0 (installed 8.0.0)
# - encryption: 3.0.1
# - federation: 2.0.0 (installed 2.0.0)
# - files_downloadlimit: 5.3.0 (installed 5.3.0)
# - files_external: 2.0.0
# - files_lock: 35.0.1 (installed 35.0.1)
# - files_reminders: 2.0.0 (installed 2.0.0)
# - files_versions: 2.0.0 (installed 2.0.0)
# - firstrunwizard: 8.0.0 (installed 8.0.0)
# - nextcloud_announcements: 7.0.0 (installed 7.0.0)
# - office: 1.1.0 (installed 1.1.0)
# - recommendations: 8.0.0 (installed 8.0.0)
# - sharebymail: 2.0.0 (installed 2.0.0)
# - support: 7.0.0 (installed 7.0.0)
# - survey_client: 7.0.0 (installed 7.0.0)
# - suspicious_login: 13.0.0
# - systemtags: 2.0.0 (installed 2.0.0)
# - text: 9.0.1 (installed 9.0.1)
# - twofactor_nextcloud_notification: 9.0.0
# - updatenotification: 2.0.0 (installed 2.0.0)
# - user_ldap: 2.0.0
# - user_status: 2.0.0 (installed 2.0.0)
# - weather_status: 2.0.0 (installed 2.0.0)
# - webhook_listeners: 2.0.0 (installed 2.0.0)Массовая генерация превью
php8-cli occ preview:generate-all -w 1 --path="/<username>/files/Photos" -vvvТакже массовую генерацию можно делегировать более мощному железу, например, своему ПК. Я использовал WSL c debian13, чтобы не доставать рабочий комп во время отпуска.
генерация превью в wsl
# cтавим всё необходимое
apt install ffmpeg libffmpegthumbnailer4v5 php8.4 php8.4-apcu php8.4-cli php8.4-common php8.4-curl php8.4-gd php8.4-imagick php8.4-intl php8.4-mbstring php8.4-opcache php8.4-readline php8.4-sqlite3 php8.4-xml php8.4-zip libheif1 rclone
# настраиваем remote в rclone (смонтируем smb шару с файлами nextcloud)
rclone config
# монтируем в тот же путь, что и на роутере (в отдельном окне терминала)
rclone mount <название_remote>:/nextcloud /tmp/mnt/<ид_диска>/nextcloud --uid 0 --gid 0 --file-perms 0770 --dir-perms 0770 --vfs-cache-mode writes
# копируем /opt/share/www с роутера в wsl по тому же пути, либо распаковываем архив с nextcloud той же версии, что на роутере, главное - не забыть стянуть конфиг с роутера
# проверить, что всё получилось
cd /opt/share/www
php8.4 occ preview:queue-stats
# и стартуем генерацию. Так будет гораздо быстрее и без лишней нагрузки на роутер
php8.4 occ preview:generate-all --path="/<username>/files/Photos" -vvvВ целом на этом можно было бы и закончить, но у меня зачесалось сгружать не конвертированные в jpeg фотки с телефона, а оригинальный heic. И чтобы preview генерились.
По дефолту - мечта недостижимая, так как imagemagick в репе entware скомпилирован БЕЗ поддержки heic. А отдельно libheif в репе нет, потому опенсорс.
Ну что ж, соберём сами
Нам нужно будет: - собрать pkg-config (https://github.com/pkgconf/pkgconf) - собрать libde265 (https://github.com/strukturag/libde265) - зависимость libheif - собрать libheif (https://github.com/strukturag/libheif) - собрать кодер heif imagemagick - перелинковать собранный декодер heif
Всю сборку делал прямо на роутере, собирать отдельное окружение для сборки было лень. Если решите повторить, тормозите php-fpm и nginx для экономии памяти.
По версиям.
На текущий момент в репе entware лежит GCC/G++ 8.4.0 и ImageMagick 7.1.2-1 Q8.
Настраиваем окружение
opkg update
opkg install gcc binutils coreutils make cmake git git-http tar xz libltdlСоздаём новый /opt/bin/gcc_env.sh (тот, что приходит в пакете не совсем правильный).
Для Keenetic Hopper - cortex-a53. Для другой модели с другим процессором, соответственно, нужен соответствующий флаг.
#!/bin/sh
export CFLAGS="-O2 -pipe -mcpu=cortex-a53"
export CXXFLAGS="-O2 -pipe -mcpu=cortex-a53"
export CPPFLAGS="-I/opt/include"
export LDFLAGS="-Wl,-rpath=/opt/lib -Wl,--dynamic-linker=/opt/lib/ld-linux-aarch64.so.1 -L/opt/lib"и даём права на запуск
chmod +x /opt/bin/gcc_env.shПеред сборкой для каждой отдельной ssh сессии
unset CFLAGS CXXFLAGS CPPFLAGS LDFLAGS LIBS
. /opt/bin/gcc_env.shСобираем pkg-config
cd /opt/root
git clone --depth 1 https://github.com/pkgconf/pkgconf.git
cd pkgconf
make -f Makefile.lite -j1 \
CC=/opt/bin/gcc \
SYSTEM_LIBDIR=/lib:/usr/lib \
SYSTEM_INCLUDEDIR=/usr/include \
PKG_DEFAULT_PATH=/opt/lib/pkgconfig:/opt/share/pkgconfig
mv pkgconf-lite /opt/bin/pkgconf
chmod +x /opt/bin/pkgconf
ln -sf /opt/bin/pkgconf /opt/bin/pkg-configГотовим libltdl
Та, что в репе не имеет dev-файлов (только runtime), необходимых для сборки ImageMagick. Берём той же версии, что и установлена в системе (в моём случае 2.5.4).
Качаем https://ftp.gnu.org/gnu/libtool/libtool-2.5.4.tar.xz перепаковываем в zip и закидываем в /opt/root (в моем случае tar в упор отказывался видеть этот архив как архив)
cd /opt/root
unzip libtool-2.5.4.zip
cd libtool-2.5.4
mkdir -p /opt/include/libltdl
cp libltdl/ltdl.h /opt/include/ltdl.h
cp libltdl/libltdl/lt_system.h /opt/include/libltdl/
cp libltdl/libltdl/lt_error.h /opt/include/libltdl/
cp libltdl/libltdl/lt_dlloader.h /opt/include/libltdl/
# смотрим линку
ls -l /opt/lib/libltdl.so*
# если нет, то создаём (у меня не было)
ln -s libltdl.so.7 /opt/lib/libltdl.soСобираем libde265
В моём случае было достаточно текущей последней версии 1.1.3.
cd /opt/root
git clone --depth 1 --branch v1.1.3 https://github.com/strukturag/libde265.git
cd libde265
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt \
-DCMAKE_INSTALL_LIBDIR=lib \
-DENABLE_DECODER=OFF \
-DENABLE_SHERLOCK265=OFF
cmake --build build --parallel 1
cmake --install buildСобираем libheif
Максимальную версию можем взять 1.18.2, так как GCC 8.4.0, а начиная с libheif 1.19.0 произошёл переход на C++20.
cd /opt/root
git clone --depth 1 --branch v1.18.2 https://github.com/strukturag/libheif.git
cd libheif
export PKG_CONFIG_PATH=/opt/lib/pkgconfig
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS_RELEASE="-O1 -DNDEBUG -g0" \
-DCMAKE_C_FLAGS_RELEASE="-O1 -DNDEBUG -g0" \
-DCMAKE_INSTALL_PREFIX=/opt \
-DCMAKE_INSTALL_LIBDIR=lib \
-DBUILD_SHARED_LIBS=ON \
-DBUILD_TESTING=OFF \
-DWITH_EXAMPLES=OFF \
-DWITH_GDK_PIXBUF=OFF \
-DENABLE_PLUGIN_LOADING=OFF \
-DENABLE_MULTITHREADING_SUPPORT=OFF \
-DENABLE_PARALLEL_TILE_DECODING=OFF \
-DWITH_LIBDE265=ON \
-DWITH_LIBDE265_PLUGIN=OFF \
-DWITH_X265=OFF \
-DWITH_KVAZAAR=OFF \
-DWITH_DAV1D=OFF \
-DWITH_AOM_DECODER=OFF \
-DWITH_AOM_ENCODER=OFF \
-DWITH_SvtEnc=OFF \
-DWITH_RAV1E=OFF \
-DWITH_OpenH264_DECODER=OFF \
-DWITH_X264=OFF \
-DWITH_JPEG_DECODER=OFF \
-DWITH_JPEG_ENCODER=OFF \
-DWITH_OpenJPEG_DECODER=OFF \
-DWITH_OpenJPEG_ENCODER=OFF \
-DWITH_FFMPEG_DECODER=OFF \
-DWITH_LIBSHARPYUV=OFF
cmake --build build --parallel 1
cmake --install buildСобираем кодер heic
cd /opt/root
git clone --depth 1 --branch 7.1.2-1 https://github.com/ImageMagick/ImageMagick.git
cd ImageMagick
./configure \
--prefix=/opt \
--disable-cipher \
--disable-docs \
--disable-hdri \
--disable-static \
--enable-dependency-tracking \
--with-modules \
--with-quantum-depth=8 \
--with-heic=yes \
--without-bzlib \
--without-djvu \
--without-fontconfig \
--without-gvc \
--without-jbig \
--without-lcms \
--without-lqr \
--without-lzma \
--without-magick-plus-plus \
--without-openexr \
--without-openjp2 \
--without-pango \
--without-raqm \
--without-raw \
--without-webp \
--without-wmf \
--without-x \
--without-xml \
--without-zstd \
--with-gs-font-dir=/opt/share/fonts/type1/gsfonts \
--with-dejavu-font-dir=/opt/share/fonts/ttf-dejavu
make -j1 coders/heic.la
# проверим, что всё собралось
ls -lh coders/.libs/heic_la-heic.oЛинкуем
Сначала проверим во временной директории
mkdir -p /tmp/im-heic-test
cd /opt/root/ImageMagick
g++ -shared \
-o /tmp/im-heic-test/heic.so \
coders/.libs/heic_la-heic.o \
/opt/lib/libMagickCore-7.Q8.so.10 \
-L/opt/lib \
-lheif \
-lz \
-lltdl \
-lm \
-lpthread \
-pthread \
-Wl,-rpath,/opt/lib \
-Wl,--no-undefined
# и проверим, чтобы не было ссылок на /opt/root/ImageMagick/...
readelf -d /tmp/im-heic-test/heic.so | grep -E 'NEEDED|RPATH|RUNPATH'
LD_LIBRARY_PATH=/opt/lib ldd /tmp/im-heic-test/heic.soСоздадим файл описания
/tmp/im-heic-test/heic.la
# heic.la - ImageMagick HEIC coder module
dlname='heic.so'
library_names='heic.so heic.so heic.so'
old_library=''
inherited_linker_flags=' -pthread'
dependency_libs=' /opt/lib/libMagickCore-7.Q8.so.10 -L/opt/lib -lheif -lz -lltdl -lpthread -lm'
weak_library_names=''
current=0
age=0
revision=0
installed=yes
shouldnotlink=yes
dlopen=''
dlpreopen=''
libdir='/tmp/im-heic-test'Для теста нужно скопировать magick.la
cp /opt/lib/ImageMagick-7.1.2/modules-Q8/coders/magick.la /tmp/im-heic-test/magick.laИ чекнем, подключается ли кодер
MAGICK_CODER_MODULE_PATH=/tmp/im-heic-test magick -list format | grep -Ei 'HEIC|HEIF'Успешный вывод будет примерно таким
AVCI HEIC --- AVC Image File Format (1.18.2)
AVIF HEIC --- AV1 Image File Format (1.18.2)
HEIC HEIC r-- High Efficiency Image Format (1.18.2)
HEIF HEIC r-- High Efficiency Image Format (1.18.2)И можем чекнуть на любом реальном heic файле
MAGICK_CODER_MODULE_PATH=/tmp/im-heic-test magick identify /path/to/photo.HEICЕсли всё успешно, можем ставить кодер
cp /tmp/im-heic-test/heic.so /opt/lib/ImageMagick-7.1.2/modules-Q8/coders/heic.so
sed "s|/tmp/im-heic-test|/opt/lib/ImageMagick-7.1.2/modules-Q8/coders|g" /tmp/im-heic-test/heic.la
cp /tmp/im-heic-test/heic.la /opt/lib/ImageMagick-7.1.2/modules-Q8/coders/heic.laЕсли перед сборкой останавливали php-fpm, запускаем и загружаем в nextcloud любую heic фотку, а затем радуемся появлению превью.
Таким образом получаем бюджетное хранилище, доступное для 2-10 пользователей (зависит от выделенных под php ресурсов и скорости диска).
P.S.
Также пробовал:
owncloud (который infinite scale): жрёт ресурсы в простое (в отличии от ondemand воркеров php-fpm), архитектура - моё почтение (куча сервисов в одном бинаре на одном PID)
opencloud (форк первого пункта): аналогично первому пункту, но ест оперативки на 30мб больше
мобильное приложение Pho (которое использует webdav): не совсем то что нужно, так как заливает фото согласно своей структуре (год/месяц/день), настроек нет - кастомизировать не получится
owncloud 10 (до go-шный): не поддерживает php8.4, а тащить из архивов 7ку и заниматься некромантией без докера - желания нет
Поэтому идеальным кандидитом на роль домашнего облака и стал nextcloud.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.