lcoy / waterfall
A Pixiv-style image waterfall (masonry) sharing page for Flarum, with external image host uploads, async queue processing, likes and a recommendation feed.
Requires
- flarum/core: ^2.0.0
Requires (Dev)
- flarum/testing: ^2.0.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A Pixiv-style image gallery for Flarum 2.x: uploads are relayed to an external image host and rendered as a masonry feed with a lightbox, likes, tags and a recommendation feed.
一个面向 Flarum 2.x 的 Pixiv 风格图片瀑布流扩展:图片异步转存到外部图床, 以瀑布流卡片展示,支持灯箱看图、点赞、标签与推荐排序。
English
- Package:
lcoy/waterfall - Extension ID:
lcoy-waterfall - Namespace:
Lcoy\Waterfall - Forum route:
/waterfall - Source: https://gitee.com/lcoy/Flarum-Waterfall (mirrored to https://github.com/Lcoy2004/Flarum-Waterfall, which is what Packagist tracks)
What it does
One upload session (one or more files chosen together) becomes one image set. The feed lists sets, one card per set; opening a card browses that set's images in a lightbox.
/waterfallroute with the first page preloaded server-side (fast LCP)- Feed cards in a fixed 3:4 portrait frame, laid out with CSS Grid
(
repeat(auto-fill, minmax(min(280px, 45%), 1fr))) — automatic responsive column count, no JS measuring or virtual scrolling - Native browser lazy-loading, skeleton placeholders, infinite scroll
- Optional cover slideshow per card (crossfades through the set's first images)
- Dependency-free lightbox: keyboard navigation, wheel/pinch zoom, drag pan, swipe or arrows to browse, double-click reset, per-image likes and delete
- Freeform tags per set (up to 5 tags, 20 characters each)
- Inline title rename (owner or moderator)
- Uploads relayed to an external image host asynchronously through Flarum's queue, with exponential-backoff retry
- Browser-side card thumbnail so the grid does not download full-size originals
- Per-user hourly / concurrent limits and a site-wide per-minute transfer quota
- Recommendation score from likes, views and recency
- Admin settings panel, a one-click image host check and an upload log panel
How uploads flow
- The browser creates the set (
POST /api/waterfall-sets) and, for each file, encodes a card-sized copy (WebP, JPEG fallback; skipped for GIFs, files under 150 KB, or images already within 800 px). - Each file is posted to
POST /api/waterfall-imagesas multipart (file, plusthumb,title,set_id,position). - The bytes are staged in
storage/waterfall-tmp(not web-accessible) and a queued job is pushed. The API answers immediately with apendingimage. - The queue worker transfers the file (and the card copy) to the image host,
then publishes the image:
src,thumb,status = published,score. - The frontend polls pending sets until they resolve to
publishedorfailed.
The extension never hosts the final image itself.
Requirements
- Flarum
^2.0 - PHP
^8.3(the version Flarum 2.x requires) - A queue worker running if your queue driver is not
sync - An external image host that accepts a
multipart/form-dataPOST with the file under thefilefield (see Image host setup)
Installation
composer require lcoy/waterfall
Then enable it and run the migrations:
php flarum extension:enable lcoy-waterfall php flarum migrate
Installing without Packagist
To track the repository directly instead — for example to pin to a branch — add it as a VCS repository:
composer config repositories.waterfall vcs https://gitee.com/lcoy/Flarum-Waterfall.git
composer require lcoy/waterfall:"*@dev"
Tagged releases are also attached as a self-contained zip (no vendor/, no
js/node_modules) on the GitHub releases
page, for hosts where
Composer cannot be run.
Developing locally
To work on a checkout as a symlinked path package, add a path repository in the
site's composer.json and require it:
{
"repositories": [
{
"name": "lcoy-waterfall",
"type": "path",
"url": "vendor/lcoy/waterfall",
"options": { "symlink": true }
}
]
}
composer require lcoy/waterfall:"@dev"
composer dump-autoload
The compiled bundle in js/dist/ is committed, so the extension runs without a
frontend build step.
Permissions
Grant them in Admin → Permissions.
| Ability | Grants |
|---|---|
lcoy-waterfall.upload |
Create sets and upload images |
lcoy-waterfall.like |
Like and unlike images |
lcoy-waterfall.moderate |
Delete or rename anyone's sets and images, read upload error messages, attach images to anyone's set |
The upload log panel is admin-only (admin permission), not part of
lcoy-waterfall.moderate.
Visibility: published sets and images are public (including to guests). A user
additionally sees their own pending and failed rows, and the error text on
an image is only exposed to its owner and to moderators.
Image host setup
A transfer is a multipart/form-data POST to
lcoy-waterfall.upload_url. The file field name is fixed to file — it is
not configurable, so the host must accept that name.
Authentication and extra fields are configured with settings:
basic_auth_user/basic_auth_pass→Authorization: Basic …extra_headers→ arbitrary HTTP headers, as a JSON objectextra_params→ extra multipart fields, as a JSON object
# Equivalent of what the extension sends, for a host that wants a bearer token # and an extra form field: curl -X POST "https://img.example.com/api/v1/upload" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: application/json" \ -F "file=@/path/to/image.jpg" \ -F "format=json"
Configure that as:
- Upload URL:
https://img.example.com/api/v1/upload - Extra headers (JSON):
{"Authorization":"Bearer YOUR_TOKEN","Accept":"application/json"} - Extra params (JSON):
{"format":"json"}
Expected response
A 200 response whose JSON contains a src key, either as a top-level object
or as the first element of an array:
[{ "src": "/file/abc.webp", "thumb": "/file/abc.thumb.webp" }]
A root-relative src (/file/abc.webp) is resolved against the configured
upload URL. An optional thumb key is used as the card image; when it is
missing, the card falls back to src.
Use Admin → Extensions → Waterfall → Test image host to probe the configured endpoint with the real auth and headers. It reports whether the credentials were accepted, rejected, or the endpoint is wrong.
Settings
All keys are namespaced with lcoy-waterfall..
| Setting | Default | Description |
|---|---|---|
upload_url |
https://your.domain/upload |
Image host multipart endpoint. Replace the placeholder. |
extra_params |
"" |
JSON object merged in as extra multipart fields |
extra_headers |
"" |
JSON object merged in as request headers |
basic_auth_user |
"" |
HTTP Basic auth username |
basic_auth_pass |
"" |
HTTP Basic auth password |
mime_whitelist |
jpg,jpeg,png,gif,webp |
Allowed formats, matched against sniffed magic bytes |
max_size_mb |
10 |
Maximum upload size in megabytes |
user_hourly_limit |
20 |
Maximum uploads per user per hour (0 disables) |
user_concurrent_uploads |
3 |
Maximum uploads left pending per user (0 disables) |
global_per_minute_limit |
60 |
Site-wide transfers per minute; over-quota jobs are deferred, not dropped |
upload_timeout |
30 |
Transfer timeout in seconds |
per_page |
24 |
Sets per feed page (1–100) |
card_radius |
8 |
Card corner radius (px) |
card_gutter |
12 |
Gap between cards (px) |
show_like_button |
true |
Show the like button in the lightbox |
slideshow_images |
3 |
Images per card slideshow; 0 or 1 disables it (cover only) |
poll_interval |
5 |
Frontend poll interval for pending uploads, in seconds |
local_relay |
false |
Keep a copy of each upload in storage/waterfall-relay for auditing |
The MIME check reads the file's magic bytes, never the client-supplied type or
extension, so renaming an executable to .jpg does not get it through. SVG is
not supported: it is sniffed as image/svg+xml, and since the whitelist matches
the MIME subtype, listing svg alone does not enable it.
Recommendation score
The "popular" sort uses each image's score (rounded to 4 decimals):
score = weight_likes * log10(1 + likes_count)
+ weight_views * log10(1 + views_count)
+ weight_recency * exp(-decay_lambda * age_hours)
age_hours is the image's age in hours. A set's score is the sum of its images'
scores.
Recalculation is off the request path. Uploads compute the score as part of the
publish write; likes, unlikes and views dispatch RecalculateScoresJob, which
carries a batch of image ids (a whole lightbox page arrives as one job) and
re-syncs each affected set once. Views are coalesced to at most one
recalculation per image per minute.
API
Two JSON:API resources back the feature: waterfall-sets (the feed) and
waterfall-images (the individual pictures).
waterfall-sets
| Method | Route | Notes |
|---|---|---|
| GET | /api/waterfall-sets |
List sets (the feed). Default sort -createdAt |
| GET | /api/waterfall-sets/{id} |
Show one set |
| POST | /api/waterfall-sets |
Create a set (needs lcoy-waterfall.upload) |
| PATCH | /api/waterfall-sets/{id} |
Update title / tags (owner or moderator) |
| DELETE | /api/waterfall-sets/{id} |
Delete (owner or moderator) |
waterfall-images
| Method | Route | Notes |
|---|---|---|
| GET | /api/waterfall-images |
List images |
| GET | /api/waterfall-images/{id} |
Show one image |
| POST | /api/waterfall-images |
Upload (multipart, needs lcoy-waterfall.upload) |
| DELETE | /api/waterfall-images/{id} |
Delete (owner or moderator) |
| POST | /api/waterfall-images/{id}/like |
Like (idempotent, needs lcoy-waterfall.like) |
| DELETE | /api/waterfall-images/{id}/like |
Unlike (idempotent) |
| POST | /api/waterfall-images/{id}/view |
Record a view (1 per IP per image per minute) |
| POST | /api/waterfall-images/views |
Record a batch of views (same counting rules) |
Other endpoints
| Method | Route | Notes |
|---|---|---|
| GET | /api/waterfall-upload-logs |
Transfer log, last 100 (admin only) |
| POST | /api/waterfall/test-host |
Image host connectivity check (admin only) |
Filters and sorts
- Sets:
filter[id],filter[user]; sorts-createdAt,-score,-likesCount - Images:
filter[id],filter[user],filter[set]; sorts-createdAt,-score,-likesCount,position
Both camelCase (-createdAt) and snake_case (-created_at) sort names are
accepted. Pagination is offset-based: page[offset] and page[limit] (max 100).
Upload example
curl -X POST "https://forum.example.com/api/waterfall-images" \ -H "Authorization: Token YOUR_API_TOKEN" \ -F "file=@/path/to/image.jpg" \ -F "title=My cat" \ -F "set_id=12" \ -F "position=0"
The response is 201 with the image in pending status. set_id must belong to
the uploader (or the uploader must be a moderator); an image with no set_id is
allowed but will not appear in the feed.
Storage and queue
storage/waterfall-tmp— staging spool for bytes awaiting transfer. Files are removed right after the transfer.storage/waterfall-relay— only created whenlocal_relayis enabled, which retains the staged original for auditing.
Jobs run on Flarum's default queue, so the existing
php flarum queue:work handles them and no extra worker is needed. The queue
driver (sync, database, redis, file, …) is whatever the site uses. When
the site-wide per-minute quota is exhausted the job is released back onto the
queue for the next minute and logged as deferred, never dropped.
Upload log rows are pruned opportunistically after 3 days.
Development
cd js yarn install yarn build # production bundle → js/dist/forum.js, js/dist/admin.js yarn dev # development build with a watcher yarn check-typings # tsc --noEmit yarn format-check # prettier --check src yarn test # Jest unit tests
The webpack entry points are js/forum.ts and js/admin.ts; they re-export
js/src/common, js/src/forum and js/src/admin.
PHP integration tests:
composer test:setup # one-time: create the test database composer test # run the integration suite
Troubleshooting
Uploads stay in pending forever
If the queue driver is not sync, make sure a worker is running
(php flarum queue:work). After 5 minutes of polling the frontend marks the set
failed locally; reloading the page reconciles with the server.
Upload fails with "the file type is not allowed"
The sniffed MIME type is not in mime_whitelist. The check uses magic bytes, so
the extension of the file does not matter. WebP and HEIC are common surprises.
An image fails with one of these error codes (visible in the upload log)
image_host_not_configured (upload URL empty or invalid),
image_host_http_error (non-200), image_host_invalid_response (response was
not JSON), image_host_missing_src (no src key),
image_host_unreachable (network/TLS failure after 3 attempts),
staged_file_missing (spool file vanished before the worker ran),
unexpected_error (a bug — check storage/logs/).
Uploads are delayed with a deferred log status
The per-minute transfer quota was hit. Raise global_per_minute_limit or add
queue workers.
A forum-facing setting changed but the frontend still shows the old value
Settings serialized to the forum (per_page, card_radius, card_gutter,
show_like_button, slideshow_images, poll_interval) clear the JS cache when
saved through the admin UI. If you changed one directly in the database, run
php flarum cache:clear.
Build fails with "No JS entrypoints could be found"
flarum-webpack-config expects js/forum.ts and js/admin.ts to exist.
License
MIT — see LICENSE.
中文
- 包名:
lcoy/waterfall - 扩展 ID:
lcoy-waterfall - 命名空间:
Lcoy\Waterfall - 前台路由:
/waterfall - 源码: https://gitee.com/lcoy/Flarum-Waterfall(镜像到 https://github.com/Lcoy2004/Flarum-Waterfall,Packagist 跟踪的是后者)
功能概述
一次上传(一次选中的一个或多个文件)构成一个图片集。瀑布流以"集"为卡片单位展示, 点开卡片即在该集的图片间用灯箱浏览。
/waterfall路由,首页由服务端预加载(改善首屏 LCP)- 卡片固定 3:4 竖版画框,使用 CSS Grid 布局
(
repeat(auto-fill, minmax(min(280px, 45%), 1fr)))——列数自适应,无需 JS 测量或虚拟滚动 - 浏览器原生懒加载、骨架占位、无限滚动
- 卡片封面幻灯片(在集内前几张图之间交叉淡入,可关闭)
- 零依赖灯箱:键盘导航、滚轮/双指缩放、拖拽平移、左右滑动或箭头切换、双击复位、单图点赞与删除
- 集级自由标签(最多 5 个,每个 20 字符)
- 可原地重命名标题(作者或版主)
- 上传经 Flarum 队列异步转存到外部图床,失败按指数退避重试
- 浏览器侧生成卡片缩略图,瀑布流不必下载原图
- 用户每小时/并发限额,以及全站每分钟转存配额
- 基于点赞、浏览与时效的推荐评分
- 后台设置面板、一键图床连通性检测、上传日志面板
上传流程
- 浏览器先创建集(
POST /api/waterfall-sets),并逐个文件生成卡片尺寸副本 (优先 WebP,回退 JPEG;GIF、小于 150KB 或本身不超过 800px 的图跳过)。 - 每个文件以 multipart 提交到
POST /api/waterfall-images(字段file,以及可选的thumb、title、set_id、position)。 - 字节暂存到
storage/waterfall-tmp(不可通过 Web 访问)并推入队列任务, 接口立即返回pending状态的图片。 - 队列 worker 把文件(及卡片副本)转存到图床,然后发布该图片:
src、thumb、status = published、score。 - 前台轮询 pending 状态的集,直到变为
published或failed。
扩展本身不保存最终图片。
环境要求
- Flarum
^2.0 - PHP
^8.3(Flarum 2.x 的要求) - 若队列驱动不是
sync,需要运行队列 worker - 一个接受
multipart/form-dataPOST、且文件字段名为file的图床 (见图床配置)
安装
composer require lcoy/waterfall
然后启用并执行迁移:
php flarum extension:enable lcoy-waterfall php flarum migrate
不走 Packagist 的安装方式
若希望直接跟踪仓库(例如锁定到某个分支),可把它注册为 VCS 仓库:
composer config repositories.waterfall vcs https://gitee.com/lcoy/Flarum-Waterfall.git
composer require lcoy/waterfall:"*@dev"
另外,每个标签发布时都会附带一个自包含 zip(不含 vendor/、不含
js/node_modules),见 GitHub releases
页面,适用于无法运行
Composer 的主机。
本地开发
若要以软链接的 path 包形式开发,在站点的 composer.json 中加入 path 仓库:
{
"repositories": [
{
"name": "lcoy-waterfall",
"type": "path",
"url": "vendor/lcoy/waterfall",
"options": { "symlink": true }
}
]
}
composer require lcoy/waterfall:"@dev"
composer dump-autoload
js/dist/ 中的构建产物已随仓库提交,因此无需前端构建步骤即可运行。
权限
在后台 → 权限中授予。
| 权限 | 含义 |
|---|---|
lcoy-waterfall.upload |
创建集并上传图片 |
lcoy-waterfall.like |
点赞与取消点赞 |
lcoy-waterfall.moderate |
删除/重命名他人的集与图片、查看上传错误信息、向他人的集上传图片 |
上传日志面板仅管理员可见(admin 权限),不属于 lcoy-waterfall.moderate。
可见性:已发布的集与图片对所有人(含游客)公开;用户额外可见自己的 pending
与 failed 记录;图片的 error 文案只对作者与版主暴露。
图床配置
转存为向 lcoy-waterfall.upload_url 发起的 multipart/form-data POST。
文件字段名固定为 file,不可配置,因此图床必须接受该字段名。
认证与附加字段由设置项提供:
basic_auth_user/basic_auth_pass→Authorization: Basic …extra_headers→ 任意 HTTP 头,JSON 对象extra_params→ 额外的 multipart 字段,JSON 对象
# 等价于扩展发出的请求(示例:需要 Bearer 令牌与一个额外表单字段) curl -X POST "https://img.example.com/api/v1/upload" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: application/json" \ -F "file=@/path/to/image.jpg" \ -F "format=json"
对应配置为:
- 上传地址:
https://img.example.com/api/v1/upload - 额外请求头 (JSON):
{"Authorization":"Bearer YOUR_TOKEN","Accept":"application/json"} - 额外参数 (JSON):
{"format":"json"}
期望的响应
返回 200,且 JSON 中包含 src 键——可以是顶层对象,也可以是数组的第一个元素:
[{ "src": "/file/abc.webp", "thumb": "/file/abc.thumb.webp" }]
根相对路径的 src(/file/abc.webp)会按上传地址解析为绝对地址。可选的 thumb
用作卡片图;缺失时卡片回退使用 src。
可在后台 → 扩展 → Waterfall → 测试图床用真实的认证与请求头探测该地址, 它会告知凭证被接受、被拒绝,或地址有误。
设置项
所有键均以 lcoy-waterfall. 为前缀。
| 设置项 | 默认值 | 说明 |
|---|---|---|
upload_url |
https://your.domain/upload |
图床上传地址。请替换该占位值。 |
extra_params |
"" |
作为额外 multipart 字段合并的 JSON 对象 |
extra_headers |
"" |
作为请求头合并的 JSON 对象 |
basic_auth_user |
"" |
HTTP Basic 认证用户名 |
basic_auth_pass |
"" |
HTTP Basic 认证密码 |
mime_whitelist |
jpg,jpeg,png,gif,webp |
允许的格式,按嗅探到的魔数匹配 |
max_size_mb |
10 |
单文件最大体积(MB) |
user_hourly_limit |
20 |
每用户每小时上传上限(0 表示不限制) |
user_concurrent_uploads |
3 |
每用户同时处于 pending 的上传上限(0 表示不限制) |
global_per_minute_limit |
60 |
全站每分钟转存次数;超配额的任务会延迟而非丢弃 |
upload_timeout |
30 |
转存超时(秒) |
per_page |
24 |
瀑布流每页集数(1–100) |
card_radius |
8 |
卡片圆角(px) |
card_gutter |
12 |
卡片间距(px) |
show_like_button |
true |
是否在灯箱显示点赞按钮 |
slideshow_images |
3 |
卡片幻灯片图片数;0 或 1 表示关闭(只显示封面) |
poll_interval |
5 |
前台轮询 pending 状态的间隔(秒) |
local_relay |
false |
转存后在 storage/waterfall-relay 保留一份副本以便审计 |
MIME 校验读取文件魔数,不信任客户端提供的类型或扩展名,因此把可执行文件改名为
.jpg 无法通过。不支持 SVG:它会被嗅探为 image/svg+xml,而白名单匹配的是 MIME 子类型,
所以只写 svg 无法启用。
推荐评分
"热门"排序使用每张图片的 score(保留 4 位小数):
score = weight_likes * log10(1 + likes_count)
+ weight_views * log10(1 + views_count)
+ weight_recency * exp(-decay_lambda * age_hours)
age_hours 为图片已发布的小时数。集的评分等于其所有图片评分之和。
重算不在请求链路上:上传时随发布写入一并计算;点赞、取消点赞与浏览会派发
RecalculateScoresJob——它携带一批图片 id(一次灯箱浏览合成一个任务),并把
受影响的集各同步一次。浏览的重算被合并为每图每分钟至多一次。
API
由两个 JSON:API 资源支撑:waterfall-sets(瀑布流主体)与
waterfall-images(单张图片)。
waterfall-sets
| 方法 | 路由 | 说明 |
|---|---|---|
| GET | /api/waterfall-sets |
列出集(瀑布流)。默认排序 -createdAt |
| GET | /api/waterfall-sets/{id} |
查看单个集 |
| POST | /api/waterfall-sets |
创建集(需 lcoy-waterfall.upload) |
| PATCH | /api/waterfall-sets/{id} |
更新 title / tags(作者或版主) |
| DELETE | /api/waterfall-sets/{id} |
删除(作者或版主) |
waterfall-images
| 方法 | 路由 | 说明 |
|---|---|---|
| GET | /api/waterfall-images |
列出图片 |
| GET | /api/waterfall-images/{id} |
查看单张图片 |
| POST | /api/waterfall-images |
上传(multipart,需 lcoy-waterfall.upload) |
| DELETE | /api/waterfall-images/{id} |
删除(作者或版主) |
| POST | /api/waterfall-images/{id}/like |
点赞(幂等,需 lcoy-waterfall.like) |
| DELETE | /api/waterfall-images/{id}/like |
取消点赞(幂等) |
| POST | /api/waterfall-images/{id}/view |
记录浏览(每 IP 每图每分钟 1 次) |
| POST | /api/waterfall-images/views |
批量记录浏览(计数规则相同,一次请求一批) |
其他接口
| 方法 | 路由 | 说明 |
|---|---|---|
| GET | /api/waterfall-upload-logs |
转存日志,最近 100 条(仅管理员) |
| POST | /api/waterfall/test-host |
图床连通性检测(仅管理员) |
过滤与排序
- 集:
filter[id]、filter[user];排序-createdAt、-score、-likesCount - 图片:
filter[id]、filter[user]、filter[set];排序-createdAt、-score、-likesCount、position
排序名同时接受驼峰(-createdAt)与下划线(-created_at)两种写法。分页基于
offset:page[offset] 与 page[limit](上限 100)。
上传示例
curl -X POST "https://forum.example.com/api/waterfall-images" \ -H "Authorization: Token YOUR_API_TOKEN" \ -F "file=@/path/to/image.jpg" \ -F "title=我的猫" \ -F "set_id=12" \ -F "position=0"
返回 201,图片状态为 pending。set_id 必须属于上传者(或上传者为版主);不带
set_id 的图片允许上传,但不会出现在瀑布流中。
存储与队列
storage/waterfall-tmp—— 等待转存的暂存目录,转存完成后立即删除文件。storage/waterfall-relay—— 仅在启用local_relay时创建,保留暂存原件以便审计。
任务运行在 Flarum 的默认队列上,现有的 php flarum queue:work 即可处理,
无需额外 worker。队列驱动(sync、database、redis、file 等)取决于站点配置。
全站每分钟配额耗尽时,任务会被放回队列延迟到下一分钟执行,并记为 deferred,
不会丢弃。
上传日志超过 3 天的记录会被顺带清理。
开发
cd js yarn install yarn build # 生产构建 → js/dist/forum.js、js/dist/admin.js yarn dev # 开发构建并监听 yarn check-typings # tsc --noEmit yarn format-check # prettier --check src yarn test # Jest 单元测试
webpack 入口为 js/forum.ts 与 js/admin.ts,它们再导出 js/src/common、
js/src/forum 与 js/src/admin。
PHP 集成测试:
composer test:setup # 仅需执行一次:创建测试数据库 composer test # 运行集成测试
常见问题
图片一直停留在 pending
若队列驱动不是 sync,请确认 worker 正在运行(php flarum queue:work)。
前台轮询超过 5 分钟会把该集在本地标记为失败;刷新页面会与服务端重新对齐。
上传报"文件类型不被允许"
嗅探到的 MIME 不在 mime_whitelist 中。校验依据魔数,与文件扩展名无关。WebP 与
HEIC 是最常被忽略的情况。
图片失败并带有以下错误码(见上传日志)
image_host_not_configured(上传地址为空或非法)、image_host_http_error(非 200)、
image_host_invalid_response(响应不是 JSON)、image_host_missing_src(缺少 src)、
image_host_unreachable(3 次尝试后网络/TLS 仍失败)、staged_file_missing
(worker 执行前暂存文件已消失)、unexpected_error(程序缺陷,请查看 storage/logs/)。
上传被延迟,日志状态为 deferred
触及了每分钟转存配额。提高 global_per_minute_limit 或增加队列 worker。
改了前台相关的设置,但页面仍是旧值
会被序列化到前台的设置(per_page、card_radius、card_gutter、show_like_button、
slideshow_images、poll_interval)在后台保存时会自动清除 JS 缓存。若直接改数据库,
请执行 php flarum cache:clear。
构建报 "No JS entrypoints could be found"
flarum-webpack-config 需要 js/forum.ts 与 js/admin.ts 存在。
许可
MIT,详见 LICENSE。