根因:6d17ea9 给桌面端加的 html{zoom:1.1} 会把菜单写入的 left/top 长度再放大一次;
而 MouseEvent.pageX/pageY、getBoundingClientRect、documentElement.clientWidth 都是
视口物理像素 → 渲染位置 = (点击坐标 - 13) × zoom,偏差 ≈ 0.1×点击坐标 - 14.3,
越靠右下偏得越多,靠近右/下边缘时被拉出视口而无法完整显示。
实测(真 Chromium 153,1440×900,菜单左上角相对点击点):
居中 (720,450) 改前 +57.7/+58.2 → 改后 -13/+12
右上/右下/左下/底部中央 改前均越界(menu right=1575 / bottom=983>900 等)→ 改后全部在视口内
无缩放对照组偏差恒为 -13/+12,证明偏差就是这 0.1 倍缩放;1280×800 / 1920×1080 同规律。
修法(src/resources/js/context-js.js,同步 public/js/context-js/context-js.js,两份逐字节一致):
- zoomFactor():运行时读 getComputedStyle(documentElement).zoom,缺省 1(不写死 1.1)
- measureBox():用 getBoundingClientRect 取真实尺寸(display:none 时沿用临时显示测量)
- clampMenu()/fitSubmenu()/openMenu():所有位置与尺寸先在「元素本地单位」里换算
(物理量一律先除以 z),保证四边都落在视口内,靠边缘时翻转到另一侧而非硬夹
- z === 1 时表达式逐字退化为原算术 → <768px 行为逐字节不变(手机端 Δ=0)
验证:3 种桌面宽度 × 5 个点击位置 = 25 次,改前 23 处失败 → 改后 0 处;
zoom 改 1.2 抽查 0 处失败;zoom 1.0 与改前逐字节相同;
滚动关闭菜单行为保留(3 种视口均验 open→scroll→closed);jsdom 290/290。
未改动 images.blade.php 与 README;patches/ios-longpress.patch 已同步再生成
(git apply 到上游快照可还原 src/ 三文件,逐字节一致)。
Lsky Pro Docker 镜像
基于 Lsky Pro 的自维护 Docker 镜像:
应用源码 vendored 在本仓库的 src/ 里,构建不联网拉上游,直接用本仓库内的源码。
- 镜像:
ghcr.io/mole404/lsky-pro-docker:latest(同一提交另发布不可变 tag:ghcr.io/mole404/lsky-pro-docker:sha-<完整 commit>) - 本仓库源码相对上游的偏离清单维护在
tools/diff-vs-upstream.sh,可随时用bash tools/diff-vs-upstream.sh自行核对。
快速开始
compose.yaml:
services:
lsky-pro:
image: ghcr.io/mole404/lsky-pro-docker:latest
container_name: lsky-pro
restart: unless-stopped
ports:
- "127.0.0.1:8089:8089" # 宿主端口:容器端口(WEB_PORT)
volumes:
- /root/lsky-pro/data:/var/www/html # 站点数据全在这个目录里
environment:
- WEB_PORT=8089
等价的 docker run:
docker run -d --name lsky-pro --restart unless-stopped \
-p 127.0.0.1:8089:8089 \
-v /root/lsky-pro/data:/var/www/html \
-e WEB_PORT=8089 \
ghcr.io/mole404/lsky-pro-docker:latest
卷目录按你的实际情况改(上例是宿主 /root/lsky-pro/data)。上面把端口只绑到 127.0.0.1,
生产环境推荐这样,再在前面放一层 Nginx 反代终结 TLS。
首次安装与访问
- 容器起来后访问
http://<主机>:8089/,会自动跳到安装向导。 - 按向导填写数据库(默认 SQLite,镜像自带驱动,不需要额外数据库服务)与管理员账号。
- 安装结果写进卷里的
.env与database/—— 也就是说备份卷就等于备份了配置与数据。
升级
docker compose pull
docker compose up -d # 不需要 --force-recreate
同一个 tag 下镜像 digest 变了,compose 会自己重建容器。入口脚本会按版本标记把新代码同步进卷、
并作废编译视图缓存;.env / database/ / storage/ 等站点数据保持不变。
备份与恢复
备份
备份整个卷目录,别只抓 database.sqlite 一个文件:
docker compose stop lsky-pro
tar -C /root/lsky-pro/data -czf ~/lsky-backup-$(date +%F-%H%M).tar.gz .
docker compose start lsky-pro
为什么整目录:SQLite 开了 WAL(journal_mode=WAL),写入过程中的新数据会短暂落在同目录的
database.sqlite-wal / -shm 伴生文件里。只拷主文件可能拿到不一致的快照,整个目录一起打包
才是完整的。先停容器再拷最稳(单文件数据库运行中直拷最容易翻车)。
卷里哪些是数据、哪些是可替换代码:
- 丢了不可再生,务必备份:
.env(配置与密钥)、database/(SQLite 数据库)、storage/(上传的图片、日志、会话)、bootstrap/cache/、public/thumbnails/、public/i(本地存储策略的软链/目录)、installed.lock(已安装标记)。 - 可重建,不用备份:
app/、config/、resources/、routes/、public/js、public/css、vendor/等 —— 它们就是镜像里的代码,换镜像时入口脚本会同步回去。
恢复
docker compose stop lsky-pro
mkdir -p /root/lsky-pro/data
tar -C /root/lsky-pro/data -xzf ~/lsky-backup-YYYY-MM-DD-HHMM.tar.gz
docker compose up -d
属主不用管:入口脚本每次启动都会对整卷做 chown -R www-data + chmod -R 755。
换机器迁移 = 把归档解到新机器的卷目录 + 复用同一份 compose.yaml。
常见配置
端口与证书
| 环境变量 | 默认值 | 说明 |
|---|---|---|
WEB_PORT |
8089 |
容器内 Apache 的 HTTP 监听端口 |
HTTPS_PORT |
8088 |
容器内 HTTPS 端口(自签证书,仅供内网调试) |
自签证书落在容器里的 /etc/apache2/ssl/lsky-selfsigned.crt 与 .key(CN=lsky-pro,RSA 2048,
3650 天),不随镜像发货、也不进数据卷:每个容器首次启动自己生成,重建容器会重新生成。
想用自己的证书,把 .crt / .key 挂载到这两个路径覆盖即可(入口脚本见到文件已存在就跳过生成)。
Apache MPM(并发与内存)
镜像内置一套面向低配单用户的参数,覆盖 Apache 的出厂值。每一项都能逐项用环境变量覆盖, 写哪个改哪个、没写的保持默认:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
APACHE_START_SERVERS |
2 |
启动时预建的子进程数 |
APACHE_MIN_SPARE_SERVERS |
1 |
空闲子进程下限 |
APACHE_MAX_SPARE_SERVERS |
3 |
空闲子进程上限 |
APACHE_MAX_REQUEST_WORKERS |
5 |
并发请求上限(内存占用的主要来源) |
APACHE_MAX_CONNECTIONS_PER_CHILD |
5 |
单个子进程处理多少个请求后回收(0 = 永不回收) |
APACHE_KEEP_ALIVE |
Off |
只接受 On / Off;Off 时连接用完即关,最省内存 |
另有两个可选变量,只在 APACHE_KEEP_ALIVE=On 且显式设置时才各多写一行,不设就跟随
Apache 发行版默认(KeepAliveTimeout 5 秒 / MaxKeepAliveRequests 100):
| 环境变量 | 渲染出的行 |
|---|---|
APACHE_KEEPALIVE_TIMEOUT |
KeepAliveTimeout <值>(仅 KeepAlive On 时) |
APACHE_MAX_KEEPALIVE_REQUESTS |
MaxKeepAliveRequests <值>(仅 KeepAlive On 时) |
写了非法值只会把那一项退回默认值并在日志打一行警告,不会让容器起不来。取值按
「一个 Apache 子进程跑起 Laravel 后 ≈ 30–50MB」估算 MaxRequestWorkers × 单进程内存 ≤ 机器可用内存,
宁小勿大 —— 超了被 OOM 杀掉比排队慢得多。
许可与致谢
- 应用本体:lsky-org/lsky-pro(GPL-3.0);
src/保留上游的LICENSE与全部署名。 - Docker 打包:fork 自 HalcyonAzure/lsky-pro-docker (AGPL-3.0),本仓库自身的打包脚本沿用该许可。