Files
lsky-pro-docker/Dockerfile
T
mole404 db05433596 feat(细节三): 确认框动画与背景位移、主菜单防误触高亮、头像阻塞加载、弹窗滚动链
① 三个(实为四个)sweetalert2 确认框(设置权限/重命名/删除/删相册):保留原实现,只改动画 +
   斩断它对 body 的动手脚。四处 Swal.fire 加 heightAuto:false + scrollbarPadding:false;
   common.less 里把 .swal2-show/.swal2-hide 换成与 x-modal 同一套(桌面 300ms ease-out 淡入+
   scale .95→1、200ms ease-in 淡出;<768px 换成 translateY(16px)),遮罩过渡同源,并用
   html body.swal2-shown:not(...) 兑掉 sweetalert2 对 body 的 overflow/padding 注入(不用 !important)。
   实测:修前 4 个弹窗×2 视口都会让整页位移(桌面滚动条消失 +8px、手机注入 padding-right 8px 且
   文档高 +20px、工具栏换行下移);修后逐项相等(关闭后复原也逐项相等)。动画实测 opacity 落在与
   x-modal 同一条贝塞尔曲线上(如 ct=100/300 实测 0.6853 vs 曲线 0.6851,线性应为 0.3333)。
② 长按主菜单防误触高亮:把二级菜单那套手势级 arming 复用过来(menu-arming,420ms + 抬手后 260ms,
   跟手势走),只在「这一发菜单确实紧随一次触摸」时武装,桌面右键路径一行都不走。
   CDP 真触摸实测(390x844、iPhone UA、手指压在菜单项上):修前 elementFromPoint 命中 <a>(会高亮,
   钉住粘滞 :hover 后整行变蓝 = 老师截图那个现象);修后按住期间/抬手+60ms 命中的都是外层容器、
   不跳色,抬手+480ms 恢复正常可点;冗余窗内真点「复制图片」仍触发、点「复制链接」仍就地展开子菜单、
   桌面鼠标 hover/点击行为与改前逐项一致。
③ 右上角头像:真因查明 —— 它是外部服务 https://cravatar.cn/avatar/<md5(email)>?s=96&d=mp&r=g
   (Utils::getAvatar),实测 376B、0.85~0.96s、每次 1 跳重定向,且页面 load 事件被它拖住(实测
   慢源下 load 3214ms)、加载失败时没有任何兜底(所以空着)。改法:先渲染固定 28×28 占位(不抖动),
   真实地址放 data-avatar-src,页面 load 之后再异步接上;加 decoding=async + onerror 失败兜底
   (名字首字色块圆圈)、成功则移除首字占位。实测:慢源下 load 3214ms → 470ms(不再等它),
   404 时从"空白"变成"首字圆圈",且头像块与整个按钮的位置尺寸与修前逐项相等。
④ 手机在弹窗内下滑底部空一块:真因是**滚动链**——弹窗内滚到尽头后滚动传给背后文档(实测修前
   document.scrollTop 从 0 冲到 2628),安卓上地址栏随之收起、底部露出空带。修法:弹窗滚动容器加
   overscroll-contain 阻断接力;高度链上残留的 100vh 换成「同一块内先 vh 后 dvh」的内联声明
   (CSSOM 实测 min-height 解析为 100dvh,祖先链已无 vh)。实测:修后 6 组手势(手机/桌面 × 长/短
   内容 × wheel/touch)背后文档 scrollTop 恒为 0,弹窗内仍能正常滚到 maxScroll;桌面各项与改前逐项一致。
验证:longpress 79/79、sidebar 6/6、images-modal 35/35、images-modal-dom 35/35、swal-theme 48/48;
webpack compiled successfully;补丁钉住值同步为 context-js.js 3614ec17…、images.blade.php 09ba35f3…,
补丁重生成 2802 行且「打回上游 = 仓库文件」字节一致。另清理了子代理遗留的调试脚本 test/dbg-gap.mjs。
2026-09-29 17:02:01 +08:00

221 lines
13 KiB
Docker
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ---------------------------------------------------------------------------
# Lsky Pro Docker 镜像 —— mole404/lsky-pro-docker
#
# 上游应用:https://github.com/lsky-org/lsky-pro (GPL-3.0,官方已停止维护)
# 上游镜像:https://github.com/HalcyonAzure/lsky-pro-docker (AGPL-3.0,本仓库由它衍生)
#
# 本仓库 = 上游应用源码快照(vendored 在 src/)+ Docker 构建脚本 + 少量前端补丁。
#
# 与上游镜像的差异(详见 README):
# 1. 应用源码**已 vendored 进本仓库**(src/ = 上游 commit 38d52c46… 即 2025-11-24 的 master 完整树)。
# 构建不再联网拉 GitHub archive:上游已明示停止维护,万一仓库被删/归档/改名也还能重建镜像;
# 之后要改 PHP 代码,直接改 src/ 下的文件、提交即可,不用再维护 overlay 叠加。
# 2. 前端补丁已就地落在 src/ 里(iOS Safari 长按弹菜单 + 菜单交互修复)。
# "我们相对上游改了什么"见 patches/ios-longpress.patch,可用 tools/diff-vs-upstream.sh 重新生成/核对。
# 3. 基础镜像显式写成 Debian bookworm 变体(与 2024-04 那版线上镜像同一 Debian 大版本)
# 4. install-php-extensions 钉到具体版本,不再用 latest
# 5. 构建期自证:① 我们从不修改的上游文件按上游 md5 校验(快照没被动过)
# ② 补丁产物与「有意偏离上游」的 composer.lock 按预期 md5 校验(改了不更新就构建失败)
#
# 构建:docker build -t lsky-pro-docker .
# ---------------------------------------------------------------------------
# 上游源码快照的版本号(只用于版本标记与镜像元数据;代码本体是仓库里的 src/)。
# 改这里 = 必须同时用 tools/vendor-upstream.sh 重新 vendor src/,否则标记与实际代码不符(自证 1 会失败)。
ARG LSKY_COMMIT=38d52c4609eb85236b45ac75acac2ced55174953
# PHP 版本:8.1 已于 2025-12-31 EOL,8.2 的 EOL 是 2026-12-31(都太近),
# 所以选 8.3(安全维护到 2027-12-31)。依赖侧配套升到同一大版本线内的最新:
# laravel/framework 9.52.21(9.x 最后一个补丁)+ symfony/* 6.4 LTS(Laravel 9 的 ^6.0 正好允许)。
# 要回退 PHP:改这个数字即可,但请连镜像 tag 一起回退(新 lock 里的包可能要求 ≥8.3)。
ARG PHP_VERSION=8.3
ARG DEBIAN_RELEASE=bookworm
ARG PHP_EXT_INSTALLER_VERSION=2.12.0
FROM php:${PHP_VERSION}-cli-${DEBIAN_RELEASE} AS build
ARG LSKY_COMMIT
ARG FORK_SHA=unknown
WORKDIR /build
# 安装必要的依赖
# curl —— 装 composer
# unzip —— composer 解压 dist 包要用(去掉它会导致 composer install 直接失败:CI 实测踩过)
# git —— 部分包会走 source 安装时的兜底
RUN docker-php-ext-install ftp
# 依赖安装(builder 阶段的平台要求必须满足,否则 composer install 直接失败)
RUN apt-get update && \
apt-get install -y curl unzip git && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# 应用源码:本仓库 src/ 里的 vendored 快照(= 上游那个 commit 的完整树,304 个文件)。
# 前端补丁已经就地在 src/ 里改好,这里不做任何"叠加覆盖"。改代码 = 改 src/ 然后提交。
COPY src/ /build/
# 自证 1:vendored 快照确实是上游那一版。
# 下面这两个文件我们从不修改,md5 全部等于上游原值;任何一处对不上都说明 src/ 被误改
# (或与 LSKY_COMMIT 不符)→ 直接构建失败,不产出不可信的镜像。
RUN printf '%s\n' \
'9fb843806abd6b778d8acd3366e2f0f1 ./app/Services/ImageService.php' \
'3b58bff18126a61c142cc576457c82a6 ./public/index.php' \
| md5sum -c -
# 自证 1b:有意偏离上游、但仍需锁死的文件。
# config/convention.php 与 routes/web.php 被本 fork 改过 —— 移除了「画廊」「系统升级」,
# 且首页 / 在登录状态下改为跳转「我的图片」(游客上传页只留给未登录用户)
# 两个功能(删配置项常量、删路由表条目,详见 README)。md5 随改动更新,作用不变:
# 这两个文件以后只要被改动(哪怕手滑),构建就会红。
RUN printf '%s\n' \
'ee439977cfcb2e4d3545b25689198c71 ./config/convention.php' \
'f6167a0726f8f2892494952a14c2bf49 ./routes/web.php' \
| md5sum -c -
# 有意偏离上游的另一个文件:composer.lock。
# 上游冻结在 2024-12 的解析结果(laravel 9.52.17 / symfony 6.0.x —— 两者都已 EOL),
# 我们把它升到「同一大版本线内的安全版本」:
# laravel/framework 9.52.21(9.x 最后一个补丁)、symfony/* 6.4 LTS(Laravel 9 的 ^6.0 允许)、
# guzzlehttp/guzzle 7.15.5、guzzlehttp/psr7 2.13.1、phpseclib 3.0.57、
# league/commonmark 2.10.3、aws/aws-sdk-php 3.398.1、laminas-diactoros 2.26.0
# —— 修掉了除「Laravel 9 框架自身那几条(9.x 线没有修复版本)」以外的已知公告。
# 所以它不再等于上游值 —— 但仍钉 md5:任何改动都必须同步更新这里(防止有人别处悄悄改)。
RUN printf '%s\n' \
'9dfc7d029808dbca37cc239560e8f49f ./composer.lock' \
| md5sum -c -
# --no-dev:镜像只装运行时依赖。开发包(debugbar / ignition / whoops / phpunit / faker / sail…)
# 对线上没有用途,留着既是体积也是暴露面(debugbar 那类在调试模式下会漏内部信息)。
# 连带项:卷里的 bootstrap/cache/packages.php 是"当年装着开发包"时生成的,里面记着这些包的
# ServiceProvider 类名 —— 包没了之后,Laravel 引导时加载该清单会 Class not found → 整站 500。
# 所以 entrypoint 的同步分支会作废这份清单(连同 services.php),CI 里也有对应的回归断言。
RUN php -r "file_exists('.env') || copy('.env.example', '.env');" \
&& composer install --no-dev
# ---------------------------------------------------------------------------
# fork 补丁:iOS 长按菜单修复 + 菜单交互修复(已就地在 src/ 里,这里只做校验)
#
# 补丁涉及三个文件(两份 JS 内容必须一致,别只改一份):
# src/resources/js/context-js.js —— 可读源码
# src/public/js/context-js/context-js.js —— 浏览器实际加载的那份
# src/resources/views/user/images.blade.php —— 引入脚本那一行(资源版本串)
# (上游 webpack.mix.js 里本来也是 mix.copy('resources/js/context-js.js', 'public/js/context-js'),
# 只是仓库里 public/ 下那份是旧工具链留下的压缩产物,一直没跟着源码更新。)
# ---------------------------------------------------------------------------
# 自证 2:补丁内容与预期完全一致,blade 的资源版本串也在。
# 改 src/ 里这三个文件后必须同步更新这里的 md5(故意做成"改了不更新就构建失败"),
# 并重新生成 patches/ios-longpress.patch(tools/diff-vs-upstream.sh)。
RUN printf '%s\n' \
'3614ec17912e56dd6439b7acfccffd2c ./public/js/context-js/context-js.js' \
'3614ec17912e56dd6439b7acfccffd2c ./resources/js/context-js.js' \
'09ba35f39ace53533513f168c739e0ab ./resources/views/user/images.blade.php' \
| md5sum -c - \
&& grep -q "assetVersion('js/context-js/context-js.js')" ./resources/views/user/images.blade.php \
&& grep -q 'isIOSWebKit' ./public/js/context-js/context-js.js \
&& grep -q 'LONG_PRESS_DELAY = 250' ./public/js/context-js/context-js.js \
&& grep -q 'LONG_PRESS_DELAY = 250' ./resources/js/context-js.js \
&& grep -q 'installMenuGuard' ./public/js/context-js/context-js.js \
&& grep -q 'MENU_OPEN_GRACE' ./public/js/context-js/context-js.js \
&& grep -q 'touch-open' ./public/js/context-js/context-js.js \
&& grep -q 'fitSubmenu' ./public/js/context-js/context-js.js \
&& grep -q 'clampMenu' ./resources/js/context-js.js \
&& grep -q 'submenu-inplace' ./public/js/context-js/context-js.js \
&& grep -q 'SUBMENU_GUARD' ./public/js/context-js/context-js.js
# 代码版本标记:入口脚本用它判断「卷里的代码是不是当前镜像这一版」,不一致才同步(见 entrypoint.sh)。
# 它由源码 commit + 补丁 md5 组成,正好是上面刚断言过的值 —— 任何代码/补丁变化都会让它变。
# 改 src/ 下任何文件(哪怕一行)后,都要把对应的 md5 一并更新,否则容器不会重新同步进卷。
RUN printf '%s\n' \
"fork_sha=${FORK_SHA}" \
"lsky_commit=${LSKY_COMMIT}" \
"context_js_md5=3614ec17912e56dd6439b7acfccffd2c" \
"images_blade_md5=09ba35f39ace53533513f168c739e0ab" \
> .code-revision \
&& cat .code-revision
FROM php:${PHP_VERSION}-apache-${DEBIAN_RELEASE}
ARG LSKY_COMMIT
ARG PHP_EXT_INSTALLER_VERSION
ARG FORK_SHA=unknown
LABEL org.opencontainers.image.source="https://github.com/mole404/lsky-pro-docker" \
org.opencontainers.image.title="lsky-pro-docker (ios-longpress + menu-ux)" \
org.opencontainers.image.description="Lsky Pro 图床的 Docker 镜像;应用源码 vendored 取自 lsky-org/lsky-pro@${LSKY_COMMIT}(构建不再依赖上游在线),并在其上做了 iOS 长按菜单修复与菜单交互修复(点菜单外只关菜单、触摸端二级菜单点击展开、滚动/缩放/Esc 关闭)" \
org.opencontainers.image.licenses="AGPL-3.0" \
org.opencontainers.image.revision="${FORK_SHA}" \
lsky.source.commit="${LSKY_COMMIT}"
# 如果构建速度慢可以换源
# RUN sed -i -E "s@http://***@http://mirrors.cloud.tencent.com@g" /etc/apt/sources.list
# 安装相关拓展(钉到具体版本,不使用 latest)
RUN curl -sSL -o /usr/local/bin/install-php-extensions \
"https://github.com/mlocati/docker-php-extension-installer/releases/download/${PHP_EXT_INSTALLER_VERSION}/install-php-extensions"
# 开启SSL
RUN a2enmod ssl && a2ensite default-ssl
# ftp 必须**显式**装进运行时镜像:官方 php:8.1 镜像自带 ftp,php:8.3 的没有
# (2026-09-28 换 8.3 时 CI 实测:builder 阶段 composer install 直接失败,
# 报 league/flysystem-ftp requires ext-ftp)。它是 Lsky 的 FTP 存储驱动要用的扩展,
# 不能依赖"从基镜像继承"。CI 里也加了 ftp 的硬断言。
RUN apt-get update && \
apt-get install -y gettext && \
apt-get clean && rm -rf /var/cache/apt/* && rm -rf /var/lib/apt/lists/* && rm -rf /tmp/* && \
a2enmod rewrite && chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions imagick bcmath pdo_mysql pdo_pgsql redis ftp && \
\
{ \
echo 'post_max_size = 100M;';\
echo 'upload_max_filesize = 100M;';\
echo 'max_execution_time = 600S;';\
} > /usr/local/etc/php/conf.d/docker-php-upload.ini; \
\
{ \
echo 'opcache.enable=1'; \
echo 'opcache.interned_strings_buffer=8'; \
echo 'opcache.max_accelerated_files=10000'; \
echo 'opcache.memory_consumption=128'; \
echo 'opcache.save_comments=1'; \
echo 'opcache.revalidate_freq=1'; \
} > /usr/local/etc/php/conf.d/opcache-recommended.ini; \
\
echo 'apc.enable_cli=1' >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini; \
\
echo 'memory_limit=512M' > /usr/local/etc/php/conf.d/memory-limit.ini; \
\
mkdir /var/www/data; \
chown -R www-data:root /var/www; \
chmod -R g=u /var/www
COPY ./ssl /etc/ssl
COPY --from=build /build /var/www/lsky/
COPY ./000-default.conf.template /etc/apache2/sites-enabled/
COPY ./ports.conf.template /etc/apache2/
COPY entrypoint.sh /
# 数据安全护栏:强制同步的排除清单缺任何一项都可能覆盖站点数据/配置,缺了就构建失败。
# (镜像自带入口脚本运行时才会执行,这里只做静态断言,保证清单不被误删。)
RUN grep -q -- '--exclude=.\/.env' /entrypoint.sh \
&& grep -q -- '--exclude=.\/storage' /entrypoint.sh \
&& grep -q -- '--exclude=.\/database' /entrypoint.sh \
&& grep -q -- '--exclude=.\/bootstrap\/cache' /entrypoint.sh \
&& grep -q -- '--exclude=.\/public\/i' /entrypoint.sh \
&& grep -q 'views/\*.php' /entrypoint.sh \
&& grep -q 'code-revision' /entrypoint.sh \
&& test -f /var/www/lsky/.code-revision \
&& grep -q '^fork_sha=' /var/www/lsky/.code-revision \
&& grep -q 'tar cf - --exclude=./.env --exclude=./.code-revision' /entrypoint.sh \
&& grep -q 'cp -a "$IMAGE_MARKER" "$VOLUME_MARKER"' /entrypoint.sh
WORKDIR /var/www/html/
VOLUME /var/www/html
ENV WEB_PORT 8089
ENV HTTPS_PORT 8088
EXPOSE ${WEB_PORT}
EXPOSE ${HTTPS_PORT}
RUN chmod a+x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["apachectl","-D","FOREGROUND"]