snipe-it

应用程序

grokability/snipe-it

开源IT资产与许可证管理系统。

项目简介

Snipe-IT 是一款免费开源的基于 Web 的 IT 资产管理系统,帮助组织跟踪硬件资产、管理软件许可证与折旧,基于 Laravel 构建,提供 REST API 并支持 Docker 部署。

README 预览

\n\n[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/)  [](https://github.com/grokability/snipe-it/actions/workflows/tests-mysql.yml)\n[](#contributing) [](https://discord.gg/yZFtShAcKk)\n\n## Snipe-IT - Open Source Asset Management System\n\nThis is a FOSS project for asset management in IT Operations. Knowing who has which laptop, when it was purchased in order to depreciate it correctly, handling software licenses, etc.\n\nIt is built on [Laravel 12](http://laravel.com).\n\nSnipe-IT is actively developed and we [release quite frequently](https://github.com/grokability/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)\n\n> [!TIP]\n> __This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, any flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.\n\n-----\n\n### Table of Contents\n* [Installation](#installation)\n* [User's Manual](#users-manual)\n* [Bug Reports & Feature Requests](#bug-reports--feature-requests)\n* [Security](#security)\n* [Upgrading](#upgrading)\n* [Translations!](#translations-)\n* [Libraries, Modules & Related Projects](#libraries-modules--related-projects)\n* [Join the Community!](#join-the-community)\n* [Contributing](#contributing)\n* [Announcement List](#announcement-list)\n\n\n-----\n\n### Installation\n\nFor instructions on installing and configuring Snipe-IT on your server, check out the [installation manual](https://snipe-it.readme.io/docs). (Please see the [requirements documentation](https://snipe-it.readme.io/docs/requirements) for full requirements.)\n\nIf you're having trouble with the installation, please check the [Common Issues](https://snipe-it.readme.io/docs/common-issues) and [Getting Help](https://snipe-it.readme.io/docs/getting-help) documen

常见问题 (5)

故障排除
# Why Snipe-IT API pagination skips or misses assets This is almost always caused by one (or more) of these issues with offset-based pagination: ## 1. Unstable sort order (most common) If you don't specify `sort`, or you sort by a non-unique column (`created_at`, `name`, or joined columns like `location` or `company`), MySQL does not guarantee consistent ordering between queries when values tie or are NULL. The same asset can appear twice while another is skipped entirely. ## 2. Data changing while you paginate Offset pagination is not a snapshot: - **Deletions** between page requests shift all subsequent rows up by one → the next page skips an asset. - **Insertions** shift rows down → you see duplicates. - If the `total` value in the response changes between your page requests, this is happening. ## 3. The server caps your `limit` Snipe-IT enforces a maximum page size (commonly 500). If you request `limit=1000` but the server silently caps it to 500, and your client still increments `offset` by 1000, you'll skip 500 assets per page. ## 4. Client-side offset bugs - Off-by-one errors (starting offset at 1, or treating offset as a page number). - Silently ignoring a failed/throttled request (HTTP 429/500) and continuing to the next page. ## Fixes **Always sort by the unique, immutable primary key:** ``` GET /api/v1/hardware?limit=500&offset=0&sort=id&order=asc ``` With `sort=id&order=asc`: - Newly created assets get higher IDs and land at the **end**, so they never shift rows you've already fetched. - Ordering is fully deterministic — no tie-breaking instability. **Additionally:** - Don't assume the server honored your `limit` — increment `offset` by the **actual number of rows returned**, not your requested limit. - Run bulk exports during low-activity windows, or pause other integrations that create/modify/delete assets. - Handle failed page requests explicitly — retry with backoff, never skip. - As a safety net, deduplicate collected assets by their `id` client-side and compare your final count against the first response's `total`. If they don't match, re-run. If you must sort by a business field (e.g., `name`), there's no cursor-based pagination in the Snipe-IT API, so sorting by `id` remains the only fully reliable approach.

Snipe-IT 默认将 /api/v1/hardware 列表接口的排序字段设为 created_at,而该字段的值并不唯一。当大量资产具有相同的创建时间戳时(例如批量导入的情况),基于 offset 的分页可能会出现页面重叠或静默遗漏记录的问题。修复方法:为每个分页请求显式指定唯一排序字段,例如 GET /api/v1/hardware?limit=200&offset=0&sort=id&order=asc,然后逐页递增 offset。这样即可稳定地返回完整的资产清单。另外,在断定这是权限 bug 之前,请先在最新版本的 Snipe-IT 上验证该问题,并确认其并非 FMCS 的公司范围(company-scoping)限制所致。

来源 Issue #19866
故障排除
为什么在 Snipe-IT 中无法取消勾选同步适配器上的“Create new Snipe-IT assets”复选框?

这是 Snipe-IT v8.8.3 中的一个已知 bug(会影响 Microsoft Intune 等同步适配器)。在 app/SyncAdapters/SyncAdapter.php(约第 907 行)中,代码使用了 $request->boolean($slug.'_create_snipeit_assets_on_pull', true) ——由于未勾选的 HTML 复选框不会被提交,缺失的字段会回退到 true 默认值,从而始终保存 "1"。临时解决办法:直接更新数据库:

UPDATE sync_adapter_instances
SET config = JSON_SET(config, '$.create_snipeit_assets_on_pull', '0')
WHERE slug = '<your-adapter-slug>';

永久修复:移除 $request->boolean(...) 中的 true 默认值,或在表单中添加一个隐藏的 0 输入框,以确保该值始终被提交。

来源 Issue #19917
故障排除
为什么 Snipe-IT Intune 同步(snipeit:pull-inventory)一直运行且永远不会完成?

此问题影响拥有超过约 999 台受管设备的 Intune 租户,由 app/SyncAdapters/Intune/IntuneClient.php(managedDevices(),约第 80 行)中的一个分页 bug 引起。在获取 Graph API 的第一页之后,空的 $query 数组仍被传递给 get()。Laravel 会将其作为 Guzzle 的 query 选项转发,而即使是空数组,Guzzle 也会替换请求 URI 的查询字符串——从而从 Graph 的 @odata.nextLink 游标中剥离 $skiptoken。于是 Graph 会无限地反复返回第 1 页,导致同步在同一批设备上不断循环,永远无法完成。修复方法:修改该行,使 $query 仅在非空时才传递:

$response = ($query === [] ? $this->request()->get($url) : $this->request()->get($url, $query))->throw()->json();

使用 php artisan snipeit:pull-inventory <slug> --dry-run --limit=1500 验证修复——它应返回 1,500 个不同的设备 ID,而不是被限制在第一页(约 999 个)。之后完整拉取应能正常完成(例如,约 6,200 台设备大约在一小时内完成)。

来源 Issue #19916
故障排除
如何修复当 Active Flag 为空时 Snipe-IT LDAP 同步未禁用 Active Directory 用户的问题?

在 Snipe-IT v8.8.0 中,新的 LDAP 向导可能会将留空的 Active Flag 保存为空字符串('')而非 NULL。LdapSync 仅在该值为 null 时才请求 userAccountControl,因此在 AD 中被禁用的用户仍会保持活跃状态。临时解决方法:打开旧版 LDAP 设置表单,将 Active Flag 留空并保存,然后运行 php artisan snipeit:ldap-sync。旧版表单会将空字符串转换为 NULL。可使用以下命令验证:php artisan tinker --execute="var_dump(Setting::getSettings()->ldap_active_flag);",应输出 NULL。永久代码修复方案:在 app/Console/Commands/LdapSync.php 中,将 userAccountControl 属性请求检查里的 is_null($ldap_map['activated']) 替换为 blank($ldap_map['activated']),或者让 app/Livewire/LdapSettings.php 将留空的可选映射保存为 null。

来源 Issue #19789
故障排除
如何修复在 v8.8.0 中使用德语 de-DE 区域设置时 Snipe-IT 表格为空以及出现 "string literal contains an unescaped line break" 错误的问题?

这是一个 v8.8.0 的 bug,由 de-DE 翻译字符串中包含原始换行符引起,该换行符破坏了内联 JavaScript 字符串,导致 bootstrap-table 无法初始化。临时解决方法:将受影响用户的语言更改为英语,或在 .env 中设置 APP_LOCALE=en 并重启容器。永久修复:升级到包含提交 6525787ba523ea9655ce8f1dfe3310e4faaa63ee 的、高于 v8.8.0 的 Snipe-IT 版本。对于 Docker,运行 docker compose pull && docker compose up -d,然后强制刷新浏览器。现有资产和许可证数据不会丢失。

来源 Issue #19772