Bar Assistant 6.10.1 常用 CLI/API 命令详解
Bar Assistant v6.10.1 运维实操:php artisan bar:* 十一命令组(full-backup/export-recipes 六格式/import-recipes/refresh-search/setup-meilisearch/delete-user/maintenance)实测回显,API 侧库存登记与有货可做联动、购物清单推荐、32 键过滤、令牌 abilities、导出签名链接,附 batch-store 422、导出 500、抓取超时、静默回退等排障实录。
Bar Assistant 6.10.1 常用 CLI/API 命令详解
适用版本:Bar Assistant API v6.10.1(Laravel Framework 12.70.2 / PHP 8.4.27,
php artisan --version、php -v实测)。Bar Assistant 是 API-first 产品:运维面收敛在应用目录/var/www/cocktails下的php artisan命令组,业务面是 OpenAPI 3.1 规范的 REST API(109 路径 / 40 tag 域,GET /api/server/openapi实测)。本文命令与回显均为 2026-11-01 实机照抄。示例账号 admin / q1w2e3r4。
一、命令入口与版本自证
cd /var/www/cocktails
php -v
PHP 8.4.27 (cli) (built: Oct 21 2026 15:53:31) (NTS)
Copyright (c) The PHP Group
php artisan --version
Laravel Framework 12.70.2
php artisan about
关键行(实测节选):Application Name .... Bar Assistant、Laravel Version .... 12.70.2、PHP Version .... 8.4.27、URL .... localhost:8135、Cache/Events/Routes .... CACHED、Database .... sqlite、Logs .... stderr、Queue .... sync、Scout .... meilisearch、public/storage LINKED。
二、bar:* 运维命令组总览
php artisan list 实测,产品自带 11 个 bar: 前缀命令:
bar
bar:anon [DEV] Anonymize data
bar:clear-metrics Clears metrics redis storage
bar:delete-user Delete user and all his data
bar:export-recipes Export data from a single bar
bar:full-backup Create a .zip file with the full backup of your Bar Assistant instance data
bar:import-recipes Import recipes exported as a Bar Assistant datapack
bar:maintenance This will remove unused images, update missing ABVs, fix ingredient sort and refresh cache
bar:merge-ingredients Merge multiple ingredients into one
bar:refresh-search Sync search engine indices with the latest Bar Assistant data
bar:setup-meilisearch Using master key is not recommended for API calls. This command will generate a new scoped API key and update the ENV file.
bar:test-email Send a test email to given email address
另有标准 Laravel 运维件可用:migrate:status、config:show、optimize、down/up、cache:clear、auth:clear-resets、db:show、horizon:*(启用 redis 队列后)。
三、备份:bar:full-backup
一条命令把整站数据(SQLite 库 + 图片 + 附件)打成 zip:
php artisan bar:full-backup
[OK] Backup exported to file:
/var/www/cocktails/storage/bar-assistant/backups/202611011148_bass-backup.zip
产物落在存储卷内 backups/ 目录,直接拷贝该卷即完成异地备份。跨大版本升级前先跑它(见部署篇第九节)。
四、导出:bar:export-recipes
单吧台配方导出,六种格式(--help 实测):
Usage:
bar:export-recipes [options] [--] [<barId>]
Arguments:
barId
Options:
-t, --type[=TYPE] Export type (datapack, schema, md, json-ld, xml, yaml) [default: "datapack"]
-u, --units[=UNITS] Force unit conversion when possible (none, ml, oz, cl)
实测执行(datapack 整包,含原料/杯型/标签全量关系,可被他实例导入):
php artisan bar:export-recipes 1 --type=datapack
Exporting data from a bar
Selected export type: datapack
Selected unit conversion: none
Starting new datapack export from bar: [1] Home Bar
[OK] Data exported to file: 1/datapack_20261101_ZAaLXNyD.zip
强制盎司单位导出:
php artisan bar:export-recipes 1 --type=json --units=oz
Selected export type: datapack
Selected unit conversion: oz
Starting new datapack export from bar: [1] Home Bar
[OK] Data exported to file: 1/datapack_20261101_b4OcHmCi.zip
排障实录:上面这条本意是 JSON 导出——--type=json 并不存在(合法值是 json-ld),命令静默回退默认 datapack,回显第一行 Selected export type: datapack 就是识别点。批量脚本务必核对这一行。
API 侧导出(Web/脚本路径)
POST /api/exports 建导出任务 → GET /api/exports 查状态(实测):
{"data":[{"id":"2","filename":"datapack_20261101_F4s6chNK.zip","created_at":"2026-11-01T11:27:17.000000Z","bar_name":"Home Bar","is_done":true},{"id":"1","filename":"datapack_20261101_gP1DHfhN.zip","created_at":"2026-11-01T10:59:41.000000Z","bar_name":"Home Bar","is_done":true}]}
GET /api/exports/{id}/download 取签名链接(实测):
{"data":{"url":"/api/exports/2/download?t=47fbd0d1f2dbd2d638829c5753815a93ad9989f77ee427808b56dcf564634f55&e=1793536190","token":"47fbd0d1f2dbd2d638829c5753815a93ad9989f77ee427808b56dcf564634f55","expires":"2026-11-01T12:29:50+00:00"}}
排障实录:对 is_done:false 的记录直接请求 download,服务端 500,stderr 日志(实测照抄):
[2026-11-01 11:27:27] production.ERROR: DateTimeImmutable::createFromFormat(): Argument #2 ($datetime) must be of type string, null given {"exception":"[object] (TypeError(code: 0): ... at /var/www/cocktails/app/Http/Controllers/ExportController.php:102)
原因:导出未完成时 created_at 为空,拼签名 URL 的过期时间戳炸掉。先轮询 is_done 再下载。
五、导入:bar:import-recipes 与数据包/URL 导入
CLI 导入 datapack(--help 实测:filename 为相对存储卷路径):
Usage:
bar:import-recipes [<filename>]
Arguments:
filename Filename relative to the bar-assistant storage volume
php artisan bar:import-recipes 1/datapack_20261101_ZAaLXNyD.zip
官方数据包整库同步走 API(首次部署初始化用,实测 204、约 0.5s 返回后台异步导入 663 条):
curl -s -o /dev/null -w "HTTP:%{http_code}\n" -X POST http://127.0.0.1:8135/api/bars/1/sync-datapack \
-H "Authorization: Bearer $TOKEN" -H "Bar-Assistant-Bar-Id: 1"
单配方 URL 抓取导入 POST /api/import/cocktail {"source":"<网页地址>"}——排障实录:服务器出网受限时 10 秒超时报错(实测两种形态):
{"message":"cURL error 28: Operation timed out after 10000 milliseconds with 0 bytes received (see https://curl.se/libcurl/c/libcurl-errors.html) for https://www.thecocktaildb.com/drink/11007-Old-Fashioned-Cocktail"}
{"message":"cURL error 28: Operation timed out after 10001 milliseconds with 18500 out of 680165 bytes received ... for https://www.liquor.com/recipes/negroni-4779854"}
修复:配 SCRAPING_HTTP_PROXY(配置篇第八节),或改走 datapack 离线导入。
六、搜索维护:bar:refresh-search 与 bar:setup-meilisearch
索引与库漂移(批量导入/迁移后)时全量重建:
php artisan bar:refresh-search
Syncing search index...
安全加固——把 API 从 master key 换成受限 scoped key(自动回写 ENV,实测帮助文本即此语义):
php artisan bar:setup-meilisearch
Meilisearch 直连验证(master key 认证,实测):
curl -s -X POST http://127.0.0.1:8132/indexes/cocktails/search \
-H "Authorization: Bearer $MEILI_MASTER_KEY" -H "Content-Type: application/json" \
-d '{"q":"negroni","limit":3}'
{"hits":[{"id":18,"name":"Yuzu Negroni",...},{"id":228,"name":"Negroni Sbagliato",...},{"id":251,"name":"Negroni",...}],"query":"negroni","processingTimeMs":0,"estimatedTotalHits":49}
七、用户与数据清理
| 命令 | 用途 | 实测要点 |
|---|---|---|
php artisan bar:delete-user admin@example.com | 删用户及其全部数据(参数是邮箱,--help 实测 Usage: bar:delete-user <email>) | 不可逆,先跑 full-backup |
php artisan bar:merge-ingredients | 多原料合并为一个(如重复的 "Gin" 词条) | 交互式 |
php artisan bar:maintenance | 清理孤儿图片、补算缺失 ABV、修原料排序、刷缓存 | 升级后推荐跑一次 |
php artisan bar:clear-metrics | 清 Redis 里的 Prometheus 指标累积 | 指标卡死/占内存时 |
php artisan bar:test-email you@example.com | SMTP 配置验证 | 配合 MAIL_* 配置篇 |
php artisan auth:clear-resets | 清过期密码重置 token | 标准 Laravel 件 |
八、数据库迁移状态
php artisan migrate:status | tail -n 8
2026_08_19_201442_widen_ratings_rating_to_decimal .................. [1] Ran
2026_08_21_090000_add_storage_metadata_to_images ................... [1] Ran
2026_08_26_000000_create_reviews_table ............................. [1] Ran
2026_09_12_000001_create_ingredient_reviews_table .................. [1] Ran
2026_09_12_000002_create_taste_descriptors_table ................... [1] Ran
2026_09_12_000003_create_ingredient_review_taste_descriptor_table .. [1] Ran
2026_09_15_100000_add_origin_bar_column_to_cocktails ............... [1] Ran
升级后核对新 migration 是否 Ran;[1] 为连接标识(sqlite)。
九、API 实操速查(curl 实测回显)
Base:http://127.0.0.1:8135/api。认证:POST /auth/login 取 Sanctum token(实测 {"data":{"token":"1|XzZf…"}}),请求带 Authorization: Bearer $TOKEN。
1. 吧台上下文头(最大的坑)
所有吧内资源必须带 Bar-Assistant-Bar-Id 请求头,否则实测 400:
{"message":"Missing required bar reference while requesting 'api/cocktails'. Use 'Bar-Assistant-Bar-Id' header to specify bar id."}
2. 库存登记(batch-store)
排障实录:带数量对象格式实测 422:
{"message":"The ingredients.0 field must be an integer. (and 2 more errors)","errors":{"ingredients.0":["The ingredients.0 field must be an integer."],"ingredients.1":["The ingredients.1 field must be an integer."],"ingredients.2":["The ingredients.2 field must be an integer."]}}
POST /api/bars/1/inventory/ingredients/batch-store 的 ingredients 只收原料 ID 整数数组(记「有/无」,数量模型属购物清单侧):
curl -s -o /dev/null -w "HTTP:%{http_code}\n" -X POST \
http://127.0.0.1:8135/api/bars/1/inventory/ingredients/batch-store \
-H "Authorization: Bearer $TOKEN" -H "Bar-Assistant-Bar-Id: 1" -H "Content-Type: application/json" \
-d '{"ingredients":[252,292,148]}'
HTTP:204
登记 Gin(252)/Campari(292)/Sweet Vermouth(148) 三样后,「有货可做」立刻联动(实测):
curl -s "http://127.0.0.1:8135/api/cocktails?filter[bar_shelf]=true" -H ...
total: 1 ['Negroni']
3. 购物清单/补货推荐
curl -s http://127.0.0.1:8135/api/bars/1/inventory/ingredients/recommend -H ...
实测按「能解锁多少配方」排序:Lime juice(191 潜在)、Lemon juice(163)、Simple Syrup(137)、Angostura Bitters(98)……
4. 过滤查询(32 个 filter 键)
GET /api/cocktails 的 filter[...] 实测支持 32 键:name、ingredient_name、tag_id、author、glass_id、favorites、on_shelf、bar_shelf、is_public、abv_min、abv_max、year_min、year_max、main_ingredient_id、ingredient_id、total_ingredients、missing_ingredients、specific_ingredients、ignore_ingredients、locked_bar_cocktails 等。组合示例(实测 148 条命中):
curl -s "http://127.0.0.1:8135/api/cocktails?filter[abv_min]=25&filter[abv_max]=35&filter[total_ingredients]=3" -H ...
收藏列表(实测 Negroni 完整对象,is_favorited:true、in_bar_shelf:true、abv 25.15、99 ml、2.46 酒精单位、174 kcal、ml/oz/cl 三制式 formatted 全量返回):
curl -s "http://127.0.0.1:8135/api/cocktails?filter[favorites]=true" -H ...
5. 收藏、评分与公开链接
curl -s -X POST http://127.0.0.1:8135/api/cocktails/251/toggle-favorite -H ...
{"data":{"id":251,"is_favorited":true}}
POST /api/cocktails/251/ratings {"rating":5} 实测 204;POST /api/cocktails/251/public-link 生成分享后,公开对象带 public_id(实测收藏响应中 "public_id":"01M6BDNAWAQ3TKZ2KVKK88XQ00"),免认证走 GET /api/public/links/cocktails/{id}。
6. 令牌与权限(abilities)
自助签发 API 令牌 POST /api/tokens。排障实录:不带 abilities 实测 400,报错顺带给出合法值清单(注意官方拼写笔误 abilties):
Unsupported abilities given, valid abilties include: cocktails.read, cocktails.write, cocktails.import, ingredients.read, ingredients.write, bars.read, bars.write, menu.read, menu.write
正确签发只读令牌(实测返回):
curl -s -X POST http://127.0.0.1:8135/api/tokens -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"readonly-token","abilities":["cocktails.read","ingredients.read"],"expires_at":"2026-12-31T00:00:00.000000Z"}'
受限令牌实测:读 OK;写 → 403 {"type":"api_error","message":"You are not authorized for this action."};无/坏 token → 401 {"message":"Unauthenticated."}。GET /api/tokens 可审计(登录会话令牌名自动取 UA,实测 (1,'curl/8.5.0',['*']))。
7. 统计与指标
curl -s http://127.0.0.1:8135/api/bars/1/stats/totals -H ...
{"total_cocktails":663,"total_ingredients":297,"total_favorited_cocktails":1,"total_bar_shelf_cocktails":1,"total_bar_shelf_ingredients":3,"total_collections":0,"total_bar_members":1}
Prometheus 端点(METRICS_ENABLED=true + 白名单后实测):
curl -s http://127.0.0.1:8135/api/metrics
# HELP bar_assistant_api_active_bars_total Total number of active bars
# TYPE bar_assistant_api_active_bars_total gauge
bar_assistant_api_active_bars_total 1
# HELP bar_assistant_api_api_request_processing_milliseconds Time spent processing API request
bar_assistant_api_api_request_processing_milliseconds_bucket{route="/api/auth/login",method="POST",status="200",le="250"} 2
十、运维速查表
| 场景 | 命令 |
|---|---|
| 每日备份 | php artisan bar:full-backup + 拷 storage 卷 |
| 迁移实例 | 新实例 bar:import-recipes <datapack> 或 API sync-datapack |
| 升级后 | php artisan migrate:status → bar:refresh-search → bar:maintenance |
| 搜索没结果 | bar:refresh-search;查 GET /api/server/version 的 search_host/search_version |
| URL 导入超时 | 配 SCRAPING_HTTP_PROXY;看 stderr 的 cURL error 28 |
| 导出下载 500 | 轮询 is_done:true 再取 download 签名链接 |
| 用户离职 | bar:delete-user <email>(先备份) |
| 换 scoped key | bar:setup-meilisearch 后轮换 master key |
十一、系列导航
- 选型:《Bar Assistant 介绍与选型指南》
- 部署:《Bar Assistant Docker Compose 部署指南》
- 配置:《Bar Assistant 6.10.1 配置参数详解》