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 AssistantBar AssistantREST API自托管Laravel2026/11/01

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.comSMTP 配置验证配合 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 keybar:setup-meilisearch 后轮换 master key

十一、系列导航

  • 选型:《Bar Assistant 介绍与选型指南》
  • 部署:《Bar Assistant Docker Compose 部署指南》
  • 配置:《Bar Assistant 6.10.1 配置参数详解》
Bar Assistant 6.10.1 常用 CLI/API 命令详解 - Quzuo