① 三个(实为四个)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。
221 lines
13 KiB
Docker
221 lines
13 KiB
Docker
# ---------------------------------------------------------------------------
|
||
# 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"]
|