Compare commits

..

1 Commits

Author SHA1 Message Date
Brady Wetherington
297ae34b4f Add some safeties around the charset-detection and transliteration 2025-01-23 13:04:16 +00:00
4315 changed files with 325636 additions and 210329 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ APP_DEBUG=true
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en-US
APP_LOCALE=en
MAX_RESULTS=500
# --------------------------------------------
@@ -35,7 +35,6 @@ DB_USERNAME=snipeit
DB_PASSWORD=changeme1234
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_DUMP_SKIP_SSL=true
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
@@ -79,13 +78,6 @@ MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
# --------------------------------------------
#PHP_UPLOAD_LIMIT=10
#PHP_POST_MAX_SIZE=10
#PHP_UPLOAD_MAX_FILESIZE=10
#PHP_MEMORY_LIMIT=10
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS

View File

@@ -28,7 +28,6 @@ PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=db
DB_SOCKET=null
DB_PORT='3306'
DB_DATABASE=snipeit
DB_USERNAME=snipeit
@@ -36,7 +35,6 @@ DB_PASSWORD=changeme1234
MYSQL_ROOT_PASSWORD=changeme1234
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_DUMP_SKIP_SSL=true
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
@@ -85,15 +83,6 @@ MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
# --------------------------------------------
#PHP_UPLOAD_LIMIT=10
#PHP_POST_MAX_SIZE=10
#PHP_UPLOAD_MAX_FILESIZE=10
#PHP_MEMORY_LIMIT=10
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
@@ -169,7 +158,6 @@ AWS_DEFAULT_REGION=null
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC

View File

@@ -24,14 +24,12 @@ PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_SOCKET=null
DB_PORT=3306
DB_DATABASE=null
DB_USERNAME=null
DB_PASSWORD=null
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_DUMP_SKIP_SSL=false
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
DB_SANITIZE_BY_DEFAULT=false
@@ -101,7 +99,7 @@ PASSPORT_COOKIE_NAME='snipeit_passport_token'
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=15
BS_TABLE_STORAGE=localStorage
BS_TABLE_STORAGE=cookieStorage
BS_TABLE_DEEPLINK=true
# --------------------------------------------
@@ -175,13 +173,11 @@ LOGIN_AUTOCOMPLETE=false
RESET_PASSWORD_LINK_EXPIRES=15
PASSWORD_CONFIRM_TIMEOUT=10800
PASSWORD_RESET_MAX_ATTEMPTS_PER_MIN=50
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=single
LOG_DEPRECATIONS=false
LOG_MAX_DAYS=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC
@@ -193,17 +189,11 @@ LDAP_TIME_LIM=600
IMPORT_TIME_LIMIT=600
IMPORT_MEMORY_LIMIT=500M
REPORT_TIME_LIMIT=12000
REQUIRE_SAML=false
API_THROTTLE_PER_MINUTE=120
CSV_ESCAPE_FORMULAS=true
LIVEWIRE_URL_PREFIX=null
# --------------------------------------------
# OPTIONAL: SAML SETTINGS
# --------------------------------------------
REQUIRE_SAML=false
SAML_KEY_SIZE=2048
# --------------------------------------------
# OPTIONAL: HASHING
# --------------------------------------------

View File

@@ -1,105 +0,0 @@
name: Bug Report
description: File a bug report.
title: "[Bug]: "
projects: ["grokability/snipe-it"]
type: bug
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! Most issues are documented in the [Snipe-IT repository's issues](https://github.com/grokability/snipe-it/issues) or in the official [Common Issues section of the Documentation](https://snipe-it.readme.io/docs/common-issues#/) and are due to the following:
- `.env` misconfiguration
- [Server Permissions](https://snipe-it.readme.io/docs/debugging-permissions#/)
- [Database Migrations](https://snipe-it.readme.io/docs/database-issues#run-migrations)
Please make sure you've checked these resources before submitting a new issue. If you find an existing issue, please add your context to it instead of opening a new issue. If your issue is more of a question, consider [opening a new discussion](https://github.com/grokability/snipe-it/discussions) or [pop by our Discord](https://discord.gg/yZFtShAcKk) instead of creating an issue.
**Please write your bug report in English.** You can use tools like [DeepL](https://www.deepl.com) or [Google Translate](https://translate.google.com/) to translate if necessary.
**If you choose to upload screenshots or videos (which we always encourage), please make sure they do not contain any sensitive information.**
- type: input
id: version
attributes:
label: Snipe-IT Version
description: What version of Snipe-IT are you seeing this issue on? You can find the version number in the footer of any page in Snipe-IT.
placeholder: ex. v8.3.1 - build 19577 (master)
validations:
required: true
- type: input
id: db-version
attributes:
label: MySQL/MariaDB version
description: What database are you using, and what version?
placeholder: ex. MySQL 5.7
validations:
required: true
- type: dropdown
id: install-method
attributes:
label: How did you install Snipe-IT?
options:
- Git install
- Manual install (downloading zip/tar.gz)
- Docker
- install.sh
- Hosted by Grokability
- Other
- Not sure
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see! (Be nice!)
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Firefox
- Chrome
- Safari
- Microsoft Edge
- Other
- type: textarea
id: server-logs
attributes:
label: Application log output
description: Please copy and paste any relevant log output from `storage/logs/laravel.log`. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: textarea
id: browser-logs
attributes:
label: Browser console output
description: Please copy and paste any relevant log output from your browser console. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: checkboxes
id: common-issues
attributes:
label: Common Issues
description: Please make sure you have done the following before submitting your issue.
options:
- label: I have searched this repo for existing issues related to my issue (including closed issues)
required: true
- label: My APP_URL is set correctly in my .env file (including http or https and no trailing slash)
required: true
- label: I have searched the official Snipe-IT documentation and have checked the Common Issues documentation (where applicable)
required: true
- label: I have run database migrations (where applicable).
required: true
- label: I have attached screenshots and/or videos of the issue (where applicable)
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/grokability/snipe-it/blob/master/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@@ -1,38 +0,0 @@
name: Feature Request
description: Request a new feature.
title: "[Feature]: "
projects: ["grokability/snipe-it"]
type: feature
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request! Please make sure to search the existing issues in this repository to see if your feature has already been requested, and feel free to add your context to any existing requests.
**Please write your issue in English.** You can use tools like [DeepL](https://www.deepl.com) or [Google Translate](https://translate.google.com/) to translate if necessary.
**If you choose to upload screenshots or videos (which we always encourage), please make sure they do not contain any sensitive information.**
- type: input
id: version
attributes:
label: Snipe-IT Version
description: What version of Snipe-IT are you currently running? You can find the version number in the footer of any page in Snipe-IT.
placeholder: ex. v8.3.1 - build 19577 (master)
validations:
required: true
- type: textarea
id: feature-description
attributes:
label: How can we help?
description: Let us know in detail what feature you'd like to see added. While we can't promise to implement every feature request, we do read every one and take them into consideration when planning future releases.
placeholder: Tell us what you'd like to see in Snipe-IT! (Be nice!)
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/grokability/snipe-it/blob/master/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@@ -26,7 +26,7 @@ jobs:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -10,10 +10,10 @@ name: Codacy Security Scan
on:
push:
branches: [ develop ]
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ develop ]
branches: [ master ]
schedule:
- cron: '36 23 * * 3'
@@ -22,21 +22,21 @@ permissions:
jobs:
codacy-security-scan:
# Ensure schedule job never runs on forked repos. It's only executed for 'grokability/snipe-it'
# Ensure schedule job never runs on forked repos. It's only executed for 'snipe/snipe-it'
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
if: (github.repository == 'grokability/snipe-it') || ((github.repository != 'grokability/snipe-it') && (github.event_name != 'schedule'))
if: (github.repository == 'snipe/snipe-it') || ((github.repository != 'snipe/snipe-it') && (github.event_name != 'schedule'))
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@v4.4.7
uses: codacy/codacy-analysis-cli-action@v4.4.5
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Crowdin push
uses: crowdin/github-action@v2

View File

@@ -20,8 +20,8 @@ permissions:
jobs:
docker:
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
if: github.repository == 'grokability/snipe-it'
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
if: github.repository == 'snipe/snipe-it'
runs-on: ubuntu-latest
env:
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
@@ -32,7 +32,7 @@ jobs:
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=tag,suffix=-alpine
type=semver,pattern=v{{major}}-latest-alpine
type=semver,pattern=v{{major}}-latest-alpine
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.
@@ -42,7 +42,7 @@ jobs:
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v4
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx

View File

@@ -1,5 +1,5 @@
# Snipe-IT Docker image build for hub.docker.com
name: Docker images (Ubuntu)
name: Docker images
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
# Also run for PRs to ensure PR doesn't break Docker build process
@@ -20,8 +20,8 @@ permissions:
jobs:
docker:
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
if: github.repository == 'grokability/snipe-it'
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
if: github.repository == 'snipe/snipe-it'
runs-on: ubuntu-latest
env:
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
@@ -32,7 +32,7 @@ jobs:
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=tag
type=semver,pattern=v{{major}}-latest
type=semver,pattern=v{{major}}-latest
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.
@@ -42,7 +42,7 @@ jobs:
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v4
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx

View File

@@ -11,7 +11,7 @@ jobs:
dockerHubDescription:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Docker Hub Description
uses: grokability/dockerhub-description@7ea9d275c7cdbe2b676a093a0308c50665e3b8b4

View File

@@ -11,7 +11,7 @@ jobs:
issues: write
# pull-requests: write
steps:
- uses: actions/stale@v10
- uses: actions/stale@v9
with:
debug-only: true
ascending: true

View File

@@ -25,9 +25,9 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
name: PHP ${{ matrix.php-version }}
@@ -37,7 +37,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
coverage: none
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Get Composer Cache Directory
id: composer-cache
@@ -67,7 +67,7 @@ jobs:
run: |
php artisan key:generate
php artisan migrate --force
php artisan passport:install --no-interaction
php artisan passport:install
chmod -R 777 storage bootstrap/cache
- name: Execute tests (Unit and Feature tests) via PHPUnit
@@ -76,16 +76,4 @@ jobs:
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.mysql.ports[3306] }}
DB_USERNAME: root
LOG_CHANNEL: single
LOG_LEVEL: debug
run: php artisan test
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
storage/logs/*.log
if-no-files-found: ignore
retention-days: 7

View File

@@ -21,10 +21,9 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
name: PHP ${{ matrix.php-version }}
@@ -34,7 +33,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
coverage: none
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Get Composer Cache Directory
id: composer-cache
@@ -65,7 +64,7 @@ jobs:
run: |
php artisan key:generate
php artisan migrate --force
php artisan passport:install --no-interaction
php artisan passport:install
chmod -R 777 storage bootstrap/cache
- name: Execute tests (Unit and Feature tests) via PHPUnit
@@ -75,16 +74,4 @@ jobs:
DB_PORT: ${{ job.services.postgresql.ports[5432] }}
DB_USERNAME: snipeit
DB_PASSWORD: password
LOG_CHANNEL: single
LOG_LEVEL: debug
run: php artisan test
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
storage/logs/*.log
if-no-files-found: ignore
retention-days: 7

View File

@@ -15,7 +15,7 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.3"
- "8.1.1"
name: PHP ${{ matrix.php-version }}
@@ -25,7 +25,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
coverage: none
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Get Composer Cache Directory
id: composer-cache
@@ -61,16 +61,4 @@ jobs:
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite
LOG_CHANNEL: single
LOG_LEVEL: debug
run: php artisan test
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
storage/logs/*.log
if-no-files-found: ignore
retention-days: 7

View File

@@ -1,240 +0,0 @@
{
"standard": "WCAG2AA",
"level": "error",
"defaults": {
"useIncognitoBrowserContext": false,
"timeout": 500000,
"wait": 5000,
"ignore" : [
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G145.Fail",
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail"
],
"viewport": {
"width": 1280,
"height": 1024
}
},
"urls": [
{
"__NOTE" : "this should always be FIRST (if browser context is preserved)",
"url": "https://snipe-it.test/login",
"actions": [
"navigate to https://snipe-it.test/login",
"screen capture tests/pa11y/login.png",
"set field input[name='username'] to admin",
"set field input[name='password'] to password",
"click element button[type=submit]",
"wait for url to be https://snipe-it.test/",
"screen capture tests/pa11y/dashboard.png"
]
},
{
"url" : "https://snipe-it.test/admin",
"actions" : [
"navigate to https://snipe-it.test/admin",
"screen capture tests/pa11y/admin-settings.png"
]
},
{
"url" : "https://snipe-it.test/admin/branding",
"actions" : [
"navigate to https://snipe-it.test/admin/branding",
"screen capture tests/pa11y/admin-branding.png"
]
},
{
"url" : "https://snipe-it.test/admin/general",
"actions" : [
"navigate to https://snipe-it.test/admin/general",
"screen capture tests/pa11y/admin-general.png"
]
},
{
"url" : "https://snipe-it.test/hardware/create",
"actions" : [
"navigate to https://snipe-it.test/hardware/create",
"screen capture tests/pa11y/asset-create.png"
]
},
{
"url" : "https://snipe-it.test/hardware",
"actions" : [
"navigate to https://snipe-it.test/hardware",
"screen capture tests/pa11y/asset-list.png"
]
},
{
"url" : "https://snipe-it.test/hardware/1",
"actions" : [
"navigate to https://snipe-it.test/hardware/1",
"screen capture tests/pa11y/asset-detail.png"
]
},
{
"url" : "https://snipe-it.test/account/view-assets",
"actions" : [
"navigate to https://snipe-it.test/account/view-assets",
"screen capture tests/pa11y/profile.png"
]
},
{
"url" : "https://snipe-it.test/licences",
"actions" : [
"navigate to https://snipe-it.test/licenses",
"screen capture tests/pa11y/license-list.png"
]
},
{
"url" : "https://snipe-it.test/licences/create",
"actions" : [
"navigate to https://snipe-it.test/licenses/create",
"screen capture tests/pa11y/license-create.png"
]
},
{
"url" : "https://snipe-it.test/licences/1",
"actions" : [
"navigate to https://snipe-it.test/licenses/1",
"screen capture tests/pa11y/license-view.png"
]
},
{
"url" : "https://snipe-it.test/consumables",
"actions" : [
"navigate to https://snipe-it.test/consumables",
"screen capture tests/pa11y/consumable-list.png"
]
},
{
"url" : "https://snipe-it.test/consumables/create",
"actions" : [
"navigate to https://snipe-it.test/consumables/create",
"screen capture tests/pa11y/consumable-create.png"
]
},
{
"url" : "https://snipe-it.test/consumables/1",
"actions" : [
"navigate to https://snipe-it.test/consumables/1",
"screen capture tests/pa11y/consumable-view.png"
]
},
{
"url" : "https://snipe-it.test/accessories",
"actions" : [
"navigate to https://snipe-it.test/accessories",
"screen capture tests/pa11y/accessory-list.png"
]
},
{
"url" : "https://snipe-it.test/accessories/create",
"actions" : [
"navigate to https://snipe-it.test/accessories/create",
"screen capture tests/pa11y/accessory-create.png"
]
},
{
"url" : "https://snipe-it.test/accessories/1",
"actions" : [
"navigate to https://snipe-it.test/accessories/1",
"screen capture tests/pa11y/accessory-view.png"
]
},
{
"url" : "https://snipe-it.test/locations",
"actions" : [
"navigate to https://snipe-it.test/locations",
"screen capture tests/pa11y/location-list.png"
]
},
{
"url" : "https://snipe-it.test/locations/create",
"actions" : [
"navigate to https://snipe-it.test/locations/create",
"screen capture tests/pa11y/location-create.png"
]
},
{
"url" : "https://snipe-it.test/locations/1",
"actions" : [
"navigate to https://snipe-it.test/locations/1",
"screen capture tests/pa11y/location-view.png"
]
},
{
"url" : "https://snipe-it.test/models",
"actions" : [
"navigate to https://snipe-it.test/models",
"screen capture tests/pa11y/model-list.png"
]
},
{
"url" : "https://snipe-it.test/models/create",
"actions" : [
"navigate to https://snipe-it.test/models/create",
"screen capture tests/pa11y/model-create.png"
]
},
{
"url" : "https://snipe-it.test/models/1",
"actions" : [
"navigate to https://snipe-it.test/models/1",
"screen capture tests/pa11y/model-view.png"
]
},
{
"url" : "https://snipe-it.test/companies",
"actions" : [
"navigate to https://snipe-it.test/companies",
"screen capture tests/pa11y/company-list.png"
]
},
{
"url" : "https://snipe-it.test/companies/create",
"actions" : [
"navigate to https://snipe-it.test/companies/create",
"screen capture tests/pa11y/company-create.png"
]
},
{
"url" : "https://snipe-it.test/companies/1",
"actions" : [
"navigate to https://snipe-it.test/companies/1",
"screen capture tests/pa11y/company-view.png"
]
},
{
"url" : "https://snipe-it.test/departments",
"actions" : [
"navigate to https://snipe-it.test/departments",
"screen capture tests/pa11y/department-list.png"
]
},
{
"url" : "https://snipe-it.test/departments/create",
"actions" : [
"navigate to https://snipe-it.test/departments/create",
"screen capture tests/pa11y/department-create.png"
]
},
{
"url" : "https://snipe-it.test/departments/1",
"actions" : [
"navigate to https://snipe-it.test/departments/1",
"screen capture tests/pa11y/department-view.png"
]
},
{
"url" : "https://snipe-it.test/invalid-url",
"actions" : [
"navigate to https://snipe-it.test/invalid-url",
"screen capture tests/pa11y/404.png"
]
}
]
}

View File

@@ -1,4 +0,0 @@
# GENERATED. YOU SHOULDN'T MODIFY OR DELETE THIS FILE.
# Scribe uses this file to know when you change something manually in your docs.
.scribe/intro.md=f325b28dd095cc9f79495c3014b20f70
.scribe/auth.md=a1780016c25c90a3c1e981b22cfb10e6

View File

@@ -1,7 +0,0 @@
# Authenticating requests
To authenticate requests, include an **`Authorization`** header with the value **`"Bearer your-token"`**.
All authenticated endpoints are marked with a `requires authentication` badge in the documentation below.
If your account has API access enabled, you can generate a token by clicking in the top right account menu and clicking <b>API tokens</b>.

View File

@@ -1,329 +0,0 @@
name: Account
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/account/requests
metadata:
groupName: Account
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Display Requested Assets'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=qIWm6chVMaJXUcKG9E8EHhnb7F9wQT14AjEraiFE; expires=Sat, 18 Oct 2025 20:42:51 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/account/eulas
metadata:
groupName: Account
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Display Accepted EULAs'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=nhItaWioeRLO0dKBgjno1X73ttHn50uXRQ7L7BAc; expires=Sat, 18 Oct 2025 20:42:51 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/account/request/{asset_id}'
metadata:
groupName: Account
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Store Asset Request'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
asset_id:
name: asset_id
description: 'The ID of the asset.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
asset_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/account/request/{asset_id}/cancel'
metadata:
groupName: Account
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Cancel Asset Request'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
asset_id:
name: asset_id
description: 'The ID of the asset.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
asset_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/account/personal-access-tokens
metadata:
groupName: Account
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create API token'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/account/personal-access-tokens
metadata:
groupName: Account
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show API tokens'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=yUjH3zpUXM18qZVCvvVEp1y5zfNNok8NctCrwGof; expires=Sat, 18 Oct 2025 20:42:51 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/account/personal-access-tokens/{tokenId}'
metadata:
groupName: Account
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete API token'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
tokenId:
name: tokenId
description: ''
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
tokenId: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

File diff suppressed because it is too large Load Diff

View File

@@ -1,636 +0,0 @@
name: Accessories
description: ''
endpoints:
-
httpMethods:
- GET
uri: 'api/v1/accessories/{accessory}/checkedout'
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Accessory Checkouts'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
accessory:
name: accessory
description: 'The accessory.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
accessory: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=QwqPH3txF8T3F17zuocUxlzlAourAOPvfWbLeuj9; expires=Sat, 18 Oct 2025 20:42:51 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/accessories/{accessory_id}/checkout'
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Checkout Accessory'
description: |-
If Slack is enabled and/or asset acceptance is enabled, it will also
trigger a Slack message and send an email.
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
accessory_id:
name: accessory_id
description: 'The ID of the accessory.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
accessory_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/accessories/{accessory}/checkin'
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Checkin Accessory'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
accessory:
name: accessory
description: 'The accessory.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
accessory: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/accessories/selectlist
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'A search term to filter results by name.'
required: false
example: null
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=kCGqdKxExAZopEwv7oy5l7SRnyqXXXDCU5Jyu7jU; expires=Sat, 18 Oct 2025 20:42:51 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/accessories
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List accessories'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'A search term to filter results by.'
required: false
example: keyboard
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
'filter[<fieldname>]':
name: 'filter[<fieldname>]'
description: 'A field to filter by. Example'
required: false
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
company_id:
name: company_id
description: 'Filter by company ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
category_id:
name: category_id
description: 'Filter by category ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
manufacturer_id:
name: manufacturer_id
description: 'Filter by manufacturer ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
supplier_id:
name: supplier_id
description: 'Filter by supplier ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
location_id:
name: location_id
description: 'Filter by location ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
notes:
name: notes
description: 'Filter by notes.'
required: false
example: 'For office use only'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
offset:
name: offset
description: 'The number of items to skip before starting to collect the result set.'
required: false
example: 0
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
limit:
name: limit
description: 'The number of items to return.'
required: false
example: 50
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
sort:
name: sort
description: 'The field to sort by.'
required: false
example: created_at
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order:
name: order
description: 'The order to sort by.'
required: false
example: desc
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: keyboard
'filter[<fieldname>]': architecto
company_id: 1
category_id: 1
manufacturer_id: 1
supplier_id: 1
location_id: 1
notes: 'For office use only'
offset: 0
limit: 50
sort: created_at
order: desc
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=2HABGaRbPqMGeiNPKWTPnN0AwM4t7W2yN4TFOJqm; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/accessories
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Accessory'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters:
name:
name: name
description: 'The name of the accessory.'
required: true
example: 'Apple Bluetooth Keyboard'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
qty:
name: qty
description: 'The number of accessories to create.'
required: true
example: 10
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
category_id:
name: category_id
description: 'The ID of the category to assign this accessory to.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
image:
name: image
description: ''
required: false
example: null
type: file
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanBodyParameters:
name: 'Apple Bluetooth Keyboard'
qty: 10
category_id: 1
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/accessories/{id}'
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Accessory'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the accessory.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=1Tv5gekmNofKNDwolDkOEcEH9JUfM9rX0PaTFfWI; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/accessories/{id}'
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update accessory.'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the accessory.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/accessories/{id}'
metadata:
groupName: Accessories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Accessory'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the accessory.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,306 +0,0 @@
name: Categories
description: ''
endpoints:
-
httpMethods:
- GET
uri: 'api/v1/categories/{item_type}/selectlist'
metadata:
groupName: Categories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
item_type:
name: item_type
description: ''
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
item_type: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=Wy5FSLyBn1xpLQy08hVrBlmoANJohF4ZIzpEcIZy; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/categories
metadata:
groupName: Categories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Categories'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=ErvltKvHZdVobMWCabFa83cWRCOTTtIgAPf7kLWS; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/categories
metadata:
groupName: Categories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Category'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/categories/{id}'
metadata:
groupName: Categories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Category'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the category.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=V2YFWiG0waCLFesMFr3kkLb0Io41yz4MPzVEfytq; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/categories/{id}'
metadata:
groupName: Categories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Category'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the category.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/categories/{id}'
metadata:
groupName: Categories
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Category'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the category.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,295 +0,0 @@
name: Companies
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/companies/selectlist
metadata:
groupName: Companies
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=IqjgsK1DVTQS5qlZI7l96rTJH0m95aILfWx6eAS2; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/companies
metadata:
groupName: Companies
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Companies'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=cEVlfAwtjQtu004rU9aEgFwcozHbApb3l0gEpL3C; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/companies
metadata:
groupName: Companies
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Company'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/companies/{id}'
metadata:
groupName: Companies
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Company'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the company.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=YqczMPKZfCp7CKkULGsRoDqufIWQ9yGkni3Cto4R; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/companies/{id}'
metadata:
groupName: Companies
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Company'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the company.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/companies/{id}'
metadata:
groupName: Companies
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Company'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the company.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,394 +0,0 @@
name: Departments
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/departments/selectlist
metadata:
groupName: Departments
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=0Ld2mnJEdBRIaREXQ37xGTuIS9SWJ4pT3WC79wc3; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/departments
metadata:
groupName: Departments
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Departments'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'Search term to filter results.'
required: false
example: IT
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
name:
name: name
description: 'Filter by exact department name.'
required: false
example: IT
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
company_id:
name: company_id
description: 'Filter by exact company ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
manager_id:
name: manager_id
description: 'Filter by exact manager (user) ID. Example:'
required: false
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
location_id:
name: location_id
description: 'Filter by exact location ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
sort:
name: sort
description: 'Column to sort results by. Allowed values: id, name, image, users_count, notes, created_at, updated_at, location, manager, company. Default: created_at.'
required: false
example: name
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order:
name: order
description: 'Order of sorted results. Allowed values: asc, desc. Default: desc.'
required: false
example: asc
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
offset:
name: offset
description: 'Offset/starting position of the results. Default: 0.'
required: false
example: 0
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
limit:
name: limit
description: 'Limit the number of results returned. Default: 25. Maximum: 100.'
required: false
example: 50
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: IT
name: IT
company_id: 1
manager_id: 16
location_id: 1
sort: name
order: asc
offset: 0
limit: 50
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=A3I9GYZHlismQEeDjfx7XM9EFccWImskxth6x9OH; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/departments
metadata:
groupName: Departments
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Department'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/departments/{id}'
metadata:
groupName: Departments
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Department'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the department.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=f5nA1I31NqT4m4T3BeSDmKt2LuBQs7KtcZZNRDKq; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/departments/{id}'
metadata:
groupName: Departments
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Department'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the department.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/departments/{id}'
metadata:
groupName: Departments
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Department'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the department.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,419 +0,0 @@
name: Components
description: ''
endpoints:
-
httpMethods:
- GET
uri: 'api/v1/components/{component}/assets'
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Component Assets'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
component:
name: component
description: 'The component.'
required: true
example: 3
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
component: 3
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=wiPOiV7Xae1fyzS2Jv3w9d060WDRgnET70IrBjTj; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/components/{id}/checkin'
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Checkin Component'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the component.'
required: true
example: 3
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 3
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/components/{id}/checkout'
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Checkout Component'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the component.'
required: true
example: 3
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 3
queryParameters: []
cleanQueryParameters: []
bodyParameters:
assigned_to:
name: assigned_to
description: 'The <code>id</code> of an existing record in the assets table.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
assigned_qty:
name: assigned_qty
description: ''
required: false
example: null
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanBodyParameters:
assigned_to: architecto
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/components
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Categories'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=vXpBDH4e4puagaeAOAWHnpHEOQ7rCwXKeEDeGn5W; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/components
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Component'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/components/{id}'
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Component'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the component.'
required: true
example: 3
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 3
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=mIFtURJNZowrCIpSMAMYNVYW5XvKIReOCIJ57XgL; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/components/{id}'
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Component'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the component.'
required: true
example: 3
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 3
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/components/{id}'
metadata:
groupName: Components
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Component'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the component.'
required: true
example: 3
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 3
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,535 +0,0 @@
name: Consumables
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/consumables/selectlist
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=9SzZeaG6J8yoJtsrcqY5kJPcLvX7ObZeYK0JivkB; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/consumables/{id}/users'
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'User Assignments'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the consumable.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=eO7NKRGNCHJ34NL6woGI5Ux80o2tg9rIBW0LuGGd; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/consumables/{consumable}/checkout'
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Checkout Consumable'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
consumable:
name: consumable
description: 'The consumable.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
consumable: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/consumables
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Consumables'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
filter:
name: filter
description: 'A JSON encoded array of key/value pairs to filter results by.'
required: false
example: '{"company":"1","location":"2"}'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
search:
name: search
description: 'A search term to filter results by.'
required: false
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
name:
name: name
description: 'Filter by exact name.'
required: false
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
company_id:
name: company_id
description: 'Filter by exact company ID.'
required: false
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
category_id:
name: category_id
description: 'Filter by exact category ID.'
required: false
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
model_number:
name: model_number
description: 'Filter by exact model number.'
required: false
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
manufacturer_id:
name: manufacturer_id
description: 'Filter by exact manufacturer ID.'
required: false
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
supplier_id:
name: supplier_id
description: 'Filter by exact supplier ID.'
required: false
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
location_id:
name: location_id
description: 'Filter by exact location ID.'
required: false
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
notes:
name: notes
description: 'Filter by exact notes.'
required: false
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
sort:
name: sort
description: 'The column to sort results by. Must be one of the following: id, name, order_number, min_amt, purchase_date, purchase_cost, company, category, model_number, item_no, manufacturer, location, qty, image, company, location, category, supplier, manufacturer. Default is created_at.'
required: false
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
order:
name: order
description: 'The order to sort results by. Must be one of the following: asc, desc. Default is desc.'
required: false
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanQueryParameters:
filter: '{"company":"1","location":"2"}'
search: architecto
name: architecto
company_id: 16
category_id: 16
model_number: architecto
manufacturer_id: 16
supplier_id: 16
location_id: 16
notes: architecto
sort: architecto
order: architecto
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=WMv6quPUXaLnZTkEXk5aBwdTDMuUgt7fQTd6uLZf; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/consumables
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Store a newly created resource in storage.'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/consumables/{id}'
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Consumable'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the consumable.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=oTfWSAjrq9p5g7qEYt7eFY4Dl7io060HverF1daF; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/consumables/{id}'
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Consumable'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the consumable.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/consumables/{id}'
metadata:
groupName: Consumables
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Consumable'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the consumable.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,244 +0,0 @@
name: Depreciations
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/depreciations
metadata:
groupName: Depreciations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Depreciations'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=TaZAI56rS9h74YByHlSA9ZrXGsTjQD4onwKaqiFF; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/depreciations
metadata:
groupName: Depreciations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Depreciation'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/depreciations/{depreciation_id}'
metadata:
groupName: Depreciations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Depreciation'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
depreciation_id:
name: depreciation_id
description: 'The ID of the depreciation.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
depreciation_id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=qFky4u180BfuHQB1d4a5A7K5Q4MOHcVdqELFA4ow; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/depreciations/{depreciation_id}'
metadata:
groupName: Depreciations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Depreciation'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
depreciation_id:
name: depreciation_id
description: 'The ID of the depreciation.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
depreciation_id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/depreciations/{depreciation_id}'
metadata:
groupName: Depreciations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Depreciation'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
depreciation_id:
name: depreciation_id
description: 'The ID of the depreciation.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
depreciation_id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,726 +0,0 @@
name: 'Custom Fields'
description: ''
endpoints:
-
httpMethods:
- POST
uri: 'api/v1/fields/fieldsets/{id}/order'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Reorder Fields'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the fieldset.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/fields/{field}/associate'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Add Field to Fieldset'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
field:
name: field
description: 'The field.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
field: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/fields/{field}/disassociate'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Remove Field from Fieldset'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
field:
name: field
description: 'The field.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
field: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/fields
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Custom Fields'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=MTrOxDhLsWMBw1aLYKK9q1GA3VjT43PhCRp7KBa9; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/fields
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Field'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/fields/{field}'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Field'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
field:
name: field
description: 'The field.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
field: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=jdlBn85i46xp3MdUb7Dbb1igonFqeCNdzQwTOmnl; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/fields/{field}'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Field'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
field:
name: field
description: 'The field.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
field: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/fields/{field}'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Field'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
field:
name: field
description: 'The field.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
field: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/fieldsets/{fieldset}/fields'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: 'Custom Fieldsets'
subgroupDescription: ''
title: 'Show Fields in Fieldset'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
fieldset:
name: fieldset
description: 'The fieldset.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
fieldset: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/fieldsets/{fieldset}/fields/{model}'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: 'Custom Fieldsets'
subgroupDescription: ''
title: 'Fields in Fieldset with Default Values for Model'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
fieldset:
name: fieldset
description: 'The fieldset.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
model:
name: model
description: ''
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
fieldset: architecto
model: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/fieldsets
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: 'Custom Fieldsets'
subgroupDescription: ''
title: 'List Fieldsets'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=DtH1Q5VV2HTu9NhlX5CxdYBImN4ZqxolMxRmhDyk; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/fieldsets
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: 'Custom Fieldsets'
subgroupDescription: ''
title: 'Create Fieldset'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/fieldsets/{id}'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: 'Custom Fieldsets'
subgroupDescription: ''
title: 'Show Fieldset and Fields'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the fieldset.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=haApTgbAdDCFdvIN3hU8DshPXbfKMRdlXqBMBKdM; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/fieldsets/{id}'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: 'Custom Fieldsets'
subgroupDescription: ''
title: 'Update Fieldset'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the fieldset.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/fieldsets/{id}'
metadata:
groupName: 'Custom Fields'
groupDescription: ''
subgroup: 'Custom Fieldsets'
subgroupDescription: ''
title: 'Delete Fieldset'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the fieldset.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,244 +0,0 @@
name: 'User Groups'
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/groups
metadata:
groupName: 'User Groups'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Display a listing of the resource.'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=evRZVbrjaLPlDGuAmSjuQxAfBkaXVVI3h6YJ8xCC; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/groups
metadata:
groupName: 'User Groups'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Group'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/groups/{id}'
metadata:
groupName: 'User Groups'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Group'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the group.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=lUFcyrjpTiMJiziJCYaZzhxr9o4YDOHuJhtyxe9O; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/groups/{id}'
metadata:
groupName: 'User Groups'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Group'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the group.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/groups/{id}'
metadata:
groupName: 'User Groups'
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Remove the specified resource from storage.'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the group.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,354 +0,0 @@
name: Maintenances
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/maintenances
metadata:
groupName: Maintenances
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Maintenances'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'Search term to filter results.'
required: false
example: repair
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
asset_id:
name: asset_id
description: 'Filter by exact asset ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
supplier_id:
name: supplier_id
description: 'Filter by exact supplier ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
created_by:
name: created_by
description: 'Filter by exact user ID who created the maintenance. Example'
required: false
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
url:
name: url
description: 'Filter by exact URL.'
required: false
example: 'http://example.com'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
asset_maintenance_type:
name: asset_maintenance_type
description: 'Filter by exact maintenance type.'
required: false
example: repair
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
sort:
name: sort
description: 'Column to sort results by. Allowed values: id, name, asset_maintenance_time, asset_maintenance_type, cost, start_date, completion_date, notes, asset_tag, asset_name, serial, created_by, supplier, location, is_warranty, status_label. Default: created_at.'
required: false
example: name
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order:
name: order
description: 'Order of sorted results. Allowed values: asc, desc. Default: desc.'
required: false
example: asc
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
offset:
name: offset
description: 'Offset/starting position of the results. Default: 0.'
required: false
example: 0
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
limit:
name: limit
description: 'Limit the number of results returned. Default: 25. Maximum: 100.'
required: false
example: 50
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: repair
asset_id: 1
supplier_id: 1
created_by: 16
url: 'http://example.com'
asset_maintenance_type: repair
sort: name
order: asc
offset: 0
limit: 50
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=HIPWn994ZVwWTllISCuyT1n0vLjhvE6GtJdyvVt5; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/maintenances
metadata:
groupName: Maintenances
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Maintenance'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/maintenances/{id}'
metadata:
groupName: Maintenances
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'View Maintenance'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the maintenance.'
required: true
example: 2
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 2
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=SMRgTfJyiKW4MwqoQkxPhRwZ7s0NG6iXfqrgvOF3; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/maintenances/{id}'
metadata:
groupName: Maintenances
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Maintenance'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the maintenance.'
required: true
example: 2
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 2
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/maintenances/{id}'
metadata:
groupName: Maintenances
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Maintenance'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the maintenance.'
required: true
example: 2
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 2
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,181 +0,0 @@
name: Imports
description: ''
endpoints:
-
httpMethods:
- POST
uri: 'api/v1/imports/process/{import}'
metadata:
groupName: Imports
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Process Import'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
import:
name: import
description: ''
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
import: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/imports
metadata:
groupName: Imports
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Import Files'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=lj8o7doJNMvuEIVNyZKehmNRHGnHDPN2afmwMD6i; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/imports
metadata:
groupName: Imports
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Save Import File'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/imports/{id}'
metadata:
groupName: Imports
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Import File'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the import.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,116 +0,0 @@
name: Labels
description: ''
endpoints:
-
httpMethods:
- GET
uri: 'api/v1/labels/{name}'
metadata:
groupName: Labels
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Label'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
name:
name: name
description: ''
required: true
example: '|{+-0p'
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
name: '|{+-0p'
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=rEUcrmsA92vKXWOMa9gjkZircoRHtwh2NGzh46Ym; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/labels
metadata:
groupName: Labels
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Labels'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=uOfOzJMa5lIhRnkxrSV7W6Lh6At2caeav8pgRq3i; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,576 +0,0 @@
name: Licenses
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/licenses/selectlist
metadata:
groupName: Licenses
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=WCUA668q64Tb6nEAAMH6QDj0KxiWwfnKfCVC1WL5; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/licenses
metadata:
groupName: Licenses
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Licenses'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
status:
name: status
description: 'Filter by license status. Options: active, inactive, expiring'
required: false
example: '?status=active'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
company_id:
name: company_id
description: 'Filter by exact company ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
name:
name: name
description: 'Filter by exact license name.'
required: false
example: 'Microsoft 365'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
product_key:
name: product_key
description: 'Filter by exact product key.'
required: false
example: W269N
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order_number:
name: order_number
description: 'Filter by exact order number.'
required: false
example: '12345'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
purchase_order:
name: purchase_order
description: 'Filter by exact purchase order.'
required: false
example: PO12345
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
license_name:
name: license_name
description: 'Filter by exact licensee name.'
required: false
example: 'John Doe'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
license_email:
name: license_email
description: 'Filter by exact licensee email.'
required: false
example: john.d
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
status: '?status=active'
company_id: 1
name: 'Microsoft 365'
product_key: W269N
order_number: '12345'
purchase_order: PO12345
license_name: 'John Doe'
license_email: john.d
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=FsSfFEzLbFI2oqcTFwXV523YHYRteVgVbhwU1KjU; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/licenses
metadata:
groupName: Licenses
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create License'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/licenses/{license_id}'
metadata:
groupName: Licenses
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show License'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
license_id:
name: license_id
description: 'The ID of the license.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
license_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=fr7kJMaaASq4b53DwioCqOIG1A7k7COxiBsYFjXL; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/licenses/{license_id}'
metadata:
groupName: Licenses
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update License'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
license_id:
name: license_id
description: 'The ID of the license.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
license_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/licenses/{license_id}'
metadata:
groupName: Licenses
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete License'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
license_id:
name: license_id
description: 'The ID of the license.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
license_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/licenses/{license_id}/seats'
metadata:
groupName: Licenses
groupDescription: ''
subgroup: 'License Seats'
subgroupDescription: ''
title: 'List License Seats'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
license_id:
name: license_id
description: 'The ID of the license.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
license_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=StfngPkbKXNpOhusZSc3gJVfFrY1dHeurAa4otwt; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/licenses/{license_id}/seats/{id}'
metadata:
groupName: Licenses
groupDescription: ''
subgroup: 'License Seats'
subgroupDescription: ''
title: 'Show License Seat'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
license_id:
name: license_id
description: 'The ID of the license.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
id:
name: id
description: 'The ID of the seat.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
license_id: 1
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=4NcQjrTDkyy5Ps1lwLlvwwkW8zLNdvkt4jLniAbj; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/licenses/{license_id}/seats/{id}'
metadata:
groupName: Licenses
groupDescription: ''
subgroup: 'License Seats'
subgroupDescription: ''
title: 'Update License Seat'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
license_id:
name: license_id
description: 'The ID of the license.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
id:
name: id
description: 'The ID of the seat.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
license_id: 1
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,644 +0,0 @@
name: Locations
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/locations/selectlist
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Gets a paginated collection for the select2 menus'
description: |-
This is handled slightly differently as of ~4.7.8-pre, as
we have to do some recursive magic to get the hierarchy to display
properly when looking at the parent/child relationship in the
rich menus.
This means we can't use the normal pagination that we use elsewhere
in our selectlists, since we have to get the full set before we can
determine which location is parent/child/grandchild, etc.
This also means that hierarchy display gets a little funky when people
use the Select2 search functionality, but there's not much we can do about
that right now.
As a result, instead of paginating as part of the query, we have to grab
the entire data set, and then invoke a paginator manually and pass that
through to the SelectListTransformer.
Many thanks to @uberbrady for the help getting this working better.
Recursion still sucks, but I guess he doesn't have to get in the
sea... this time.
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=UpVwalXoUKqNpdc1JZOoxLIbtfuGTDjmXv2FL0U4; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/locations/{location_id}/assets'
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Assets with Default Location'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
location_id:
name: location_id
description: 'The ID of the location.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
location_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=s6eLrXoHfx9aHRHsGokhuXGIfcWMyMK6s8Y7CuQb; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/locations/{location_id}/assigned/assets'
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Assets Assigned to Location'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
location_id:
name: location_id
description: 'The ID of the location.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
location_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=6IjNjFVBRoxfcpk3lgt97JCcwcwkJ18MblUUArEa; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/locations/{location_id}/assigned/accessories'
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Accessories Assigned to Location'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
location_id:
name: location_id
description: 'The ID of the location.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
location_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=8QVLWPz9VFdTVM9oelFsOvXEnefwKOeIuos7D23G; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/locations
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Locations'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'Search term to filter results.'
required: false
example: Headquarters
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
name:
name: name
description: 'Filter by exact location name.'
required: false
example: Headquarters
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
address:
name: address
description: 'Filter by exact address.'
required: false
example: '123 Main St'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
address2:
name: address2
description: 'Filter by exact address2.'
required: false
example: 'Suite 100'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
city:
name: city
description: 'Filter by exact city.'
required: false
example: Springfield
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
zip:
name: zip
description: 'Filter by exact zip code.'
required: false
example: '12345'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
country:
name: country
description: 'Filter by exact country.'
required: false
example: USA
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
manager_id:
name: manager_id
description: 'Filter by exact manager (user) ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
company_id:
name: company_id
description: 'Filter by exact company ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
parent_id:
name: parent_id
description: 'Filter by exact parent location ID.'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
status:
name: status
description: 'Filter by location status. Allowed values: active, deleted.'
required: false
example: active
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
sort:
name: sort
description: 'Column to sort results by. Allowed values: accessorries_count, address, address2, assets_count, assigned_assets_count, rtd_assets_count, accessories_count, assigned_accessories_count, components_count, consumables_count, users_count, children_count, city, country, created_at, currency, id, image, ldap_ou, company_id, manager_id, name, rtd_assets_count, state, updated_at, zip. Default: created_at.'
required: false
example: name
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order:
name: order
description: 'Order of sorted results. Allowed values: asc, desc. Default: desc.'
required: false
example: asc
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: Headquarters
name: Headquarters
address: '123 Main St'
address2: 'Suite 100'
city: Springfield
zip: '12345'
country: USA
manager_id: 1
company_id: 1
parent_id: 1
status: active
sort: name
order: asc
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=OrGO92pRr2bN1MO0HEk74EaOyIBpyNWW8nBBM9w1; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/locations
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Location'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/locations/{id}'
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Location'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the location.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=r40jhYlCjVborgeetW7XFFJzC3FPckbK3wiqaq9b; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/locations/{id}'
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Location'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the location.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/locations/{id}'
metadata:
groupName: Locations
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Location'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the location.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,462 +0,0 @@
name: Manufacturers
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/manufacturers/selectlist
metadata:
groupName: Manufacturers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=wsdZCoP7veaDGNxSgmEVyiIXMCynDJaplPtmclKS; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/manufacturers/{id}/restore'
metadata:
groupName: Manufacturers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Restore Deleted Manufacturer'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the manufacturer.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/manufacturers
metadata:
groupName: Manufacturers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Manufacturers'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'Search term to filter results.'
required: false
example: Dell
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
name:
name: name
description: 'Filter by exact manufacturer name.'
required: false
example: Dell
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
url:
name: url
description: 'Filter by exact URL.'
required: false
example: 'http://example.com'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
support_url:
name: support_url
description: 'Filter by exact support URL.'
required: false
example: 'http://support.example.com'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
warranty_lookup_url:
name: warranty_lookup_url
description: 'Filter by exact warranty lookup URL.'
required: false
example: 'http://warranty.example.com'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
support_phone:
name: support_phone
description: 'Filter by exact support phone number.'
required: false
example: 1-800-555-5555
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
support_email:
name: support_email
description: 'Filter by exact support email address.'
required: false
example: support@example.org
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
sort:
name: sort
description: 'Column to sort results by. Allowed values: id, name, url, support_url, support_email, warranty_lookup_url, support_phone, created_at, updated_at, assets_count, consumables_count, components_count, licenses_count. Default: created_at.'
required: false
example: name
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order:
name: order
description: 'Order of sorted results. Allowed values: asc, desc. Default: desc.'
required: false
example: asc
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
offset:
name: offset
description: 'Offset/starting position of the results. Default: 0.'
required: false
example: 0
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
limit:
name: limit
description: 'Limit the number of results returned. Default: 25. Maximum: 100.'
required: false
example: 50
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: Dell
name: Dell
url: 'http://example.com'
support_url: 'http://support.example.com'
warranty_lookup_url: 'http://warranty.example.com'
support_phone: 1-800-555-5555
support_email: support@example.org
sort: name
order: asc
offset: 0
limit: 50
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=hLpuGgLKlkZ3lxAdCVVQh6AknrP7V8ucHP12iJyL; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/manufacturers
metadata:
groupName: Manufacturers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Maintenance'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/manufacturers/{id}'
metadata:
groupName: Manufacturers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Manufacturer'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the manufacturer.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=AihyA0pGOpolpeaHgVkhwxZ48ZIGlH5C4cWn2ky1; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/manufacturers/{id}'
metadata:
groupName: Manufacturers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Manufacturer'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the manufacturer.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/manufacturers/{id}'
metadata:
groupName: Manufacturers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Manufacturer'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the manufacturer.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,357 +0,0 @@
name: Models
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/models/selectlist
metadata:
groupName: Models
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Selectlist of Models'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=jwtFIyYGPRatDfntlAj40jo0O4Sj0FCnKi976PR2; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/models/assets
metadata:
groupName: Models
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Assets in Model'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the model.'
required: true
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 16
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=nvMGTR69fETV88dw8dfKSbREZAJ66IYQUUNSpf7Y; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/models
metadata:
groupName: Models
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Models'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=QArLWCify7SZSmEfdwLa8MuiE73ySbcjczNWAP5t; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/models
metadata:
groupName: Models
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Model'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/models/{id}'
metadata:
groupName: Models
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Model'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the model.'
required: true
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 16
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=Fi7bcrWvfZzmZFbGKAT0DPwaht8G2vkcbjoJPdZ1; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/models/{id}'
metadata:
groupName: Models
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Model'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the model.'
required: true
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 16
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/models/{id}'
metadata:
groupName: Models
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Model'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the model.'
required: true
example: 16
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: 16
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,117 +0,0 @@
name: Notes
description: ''
endpoints:
-
httpMethods:
- POST
uri: 'api/v1/notes/{asset_id}/store'
metadata:
groupName: Notes
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Store Note'
description: |-
Checks authorization for updating assets, validates the presence of the 'note',
attempts to find the asset by ID, and creates a new ActionLog entry if successful.
Returns JSON responses indicating success or failure with appropriate HTTP status codes.
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
asset_id:
name: asset_id
description: 'The ID of the asset.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
asset_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/notes/{asset_id}/index'
metadata:
groupName: Notes
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'List Notes'
description: |-
Checks authorization to view assets, attempts to find the asset by ID,
and fetches related action log entries of type 'note added', including
user information for each note. Returns a JSON response with the notes or errors.
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
asset_id:
name: asset_id
description: 'The ID of the asset.'
required: true
example: 1
type: integer
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
asset_id: 1
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=0996I9pgG6hTiplyAUfm6hARQqlLp2KcZRKbrsM4; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,398 +0,0 @@
name: Settings
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/settings/ldaptest
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Test LDAP Connection'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=sXQmK1seBet712r1kd4fxKvJ8eS1G5JIQGi7Jk76; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/settings/purge_barcodes
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Barcode Cache'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/settings/login-attempts
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Get a list of login attempts'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=3WvRIFmkRUjvmDyyocP7gGkeVA8nsKCstMlpD2ll; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/settings/ldaptestlogin
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Test LDAP Login'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters:
ldaptest_user:
name: ldaptest_user
description: ''
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
ldaptest_password:
name: ldaptest_password
description: ''
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanBodyParameters:
ldaptest_user: architecto
ldaptest_password: architecto
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/settings/mailtest
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Test Email Configuration'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/settings/backups
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Lists backup files'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=c1LnO2y90g9cXIQdH1B3ALRTwBznlJR0MPn7UYwI; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/settings/backups/download/latest
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Determines and downloads the latest backup'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=h8vmsBd9n2ThBnox919dPAo17WMNdGSmFDgmUWbJ; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/settings/backups/download/{file}'
metadata:
groupName: Settings
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Downloads a backup file.'
description: |-
We use response()->download() here instead of Storage::download() because Storage::download()
exhausts memory on larger files.
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
file:
name: file
description: ''
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
file: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=dEPJObU3yZeyRQ0ZNZQmLDg70DDH6mt8OLUoXk7b; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,539 +0,0 @@
name: 'Status Labels'
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/statuslabels/selectlist
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=wkMPeN6kxHTgcy7pBWCmBb2skYp7Ozxo03ll2QRI; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/statuslabels/assets/name
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Show Count for Pie Chart'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=BwSbIkmBBnKEl1eHDB5irvFrSmdOermismgPqdVt; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/statuslabels/assets/type
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Show Count for Pie Chart by Meta Status'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=qgDpluENzw06DYesVDJ62VPNypFZR7TMXA5YuERD; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/statuslabels/{id}/assetlist'
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Show Assets with Status Label'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the statuslabel.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=prHwFCnHCRfbaQBGGsb9zl9HAqpNQVahgTzOn5TU; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/statuslabels/{statuslabel}/deployable'
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Check for Deployable Status'
description: |-
Returns a boolean response based on whether the status label
is one that is deployable or pending.
This is used by the hardware create/edit view to determine whether
we should provide a dropdown of users for them to check the asset out to,
and whether we show a warning that the asset will be checked in if it's already
assigned but the status is changed to one that isn't pending or deployable
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
statuslabel:
name: statuslabel
description: 'The statuslabel.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
statuslabel: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=gGlaRILsVZF4fEWWem29LTEhtwQLJo5jRgFzetFi; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/statuslabels
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Show Status Labels'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'Search term to filter results.'
required: false
example: Inventory
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: Inventory
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=wZlTwk63tGu1kRBFJuzb8R1UY1k0ZU2YI5NAAZt8; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/statuslabels
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Store a newly created resource in storage.'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/statuslabels/{id}'
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Show Status Labels'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the statuslabel.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=BoPls7yDoCtaubgDPfK5nEabHin9oYkJpg9vZT3b; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/statuslabels/{id}'
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Update Status Label'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the statuslabel.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/statuslabels/{id}'
metadata:
groupName: 'Status Labels'
groupDescription: ''
subgroup: Assets
subgroupDescription: ''
title: 'Delete Status Label'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the statuslabel.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,482 +0,0 @@
name: Suppliers
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/suppliers/selectlist
metadata:
groupName: Suppliers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: Selectlist
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=XCddlMqcqKqWUTBwJenBf996h9VUAuQn9onGzMNw; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: api/v1/suppliers
metadata:
groupName: Suppliers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Suppliers'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'Search term to filter results.'
required: false
example: Acme
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
name:
name: name
description: 'Filter by exact supplier name.'
required: false
example: 'Acme Corp'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
address:
name: address
description: 'Filter by exact address.'
required: false
example: '123 Main St'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
address2:
name: address2
description: 'Filter by exact address2.'
required: false
example: 'Suite 100'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
city:
name: city
description: 'Filter by exact city.'
required: false
example: Springfield
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
state:
name: state
description: 'Filter by exact state.'
required: false
example: IL
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
zip:
name: zip
description: 'Filter by exact zip code.'
required: false
example: '62701'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
country:
name: country
description: 'Filter by exact country.'
required: false
example: USA
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
phone:
name: phone
description: 'Filter by exact phone number.'
required: false
example: 555-1234
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
fax:
name: fax
description: 'Filter by exact fax number.'
required: false
example: 555-5678
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
email:
name: email
description: 'Filter by exact email address.'
required: false
example: info@example.org
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
url:
name: url
description: 'Filter by exact URL.'
required: false
example: 'http://example.com'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
notes:
name: notes
description: 'Filter by exact notes.'
required: false
example: 'This is a note.'
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
sort:
name: sort
description: 'Column to sort results by. Allowed values: id, name, address, address2, city, state, country, zip, phone, contact, fax, email, image, assets_count, licenses_count, accessories_count, components_count, consumables_count, url, notes. Default: created_at.'
required: false
example: name
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order:
name: order
description: 'Order of sorted results. Allowed values: asc, desc. Default: desc.'
required: false
example: asc
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
offset:
name: offset
description: 'Offset/starting position of the results. Default: 0.'
required: false
example: 0
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
limit:
name: limit
description: 'Limit the number of results returned. Default: 25. Maximum: 100.'
required: false
example: 50
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: Acme
name: 'Acme Corp'
address: '123 Main St'
address2: 'Suite 100'
city: Springfield
state: IL
zip: '62701'
country: USA
phone: 555-1234
fax: 555-5678
email: info@example.org
url: 'http://example.com'
notes: 'This is a note.'
sort: name
order: asc
offset: 0
limit: 50
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=R5PUii8mbVR2t8EyWTQHM80dstDyhZxcQR15nKb7; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: api/v1/suppliers
metadata:
groupName: Suppliers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Create Supplier'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/suppliers/{id}'
metadata:
groupName: Suppliers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Show Supplier'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the supplier.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=aqFNCFcjAl7hMG1Q0RCnYmK7HTNWcq0kYhd1R9NN; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- PUT
- PATCH
uri: 'api/v1/suppliers/{id}'
metadata:
groupName: Suppliers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Update Supplier'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the supplier.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/suppliers/{id}'
metadata:
groupName: Suppliers
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete Supplier'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
id:
name: id
description: 'The ID of the supplier.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,207 +0,0 @@
name: Reports
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/reports/activity
metadata:
groupName: Reports
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Activity Report'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters:
search:
name: search
description: 'Search term to filter results'
required: false
example: updated
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
target_type:
name: target_type
description: 'Filter by target type'
required: false
example: user
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
target_id:
name: target_id
description: 'Filter by target ID'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
item_type:
name: item_type
description: 'Filter by item type'
required: false
example: asset
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
item_id:
name: item_id
description: 'Filter by item ID'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
action_type:
name: action_type
description: 'Filter by action type'
required: false
example: create
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
created_by:
name: created_by
description: 'Filter by user ID who created the log'
required: false
example: 1
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
action_source:
name: action_source
description: 'Filter by action source'
required: false
example: web
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
remote_ip:
name: remote_ip
description: 'Filter by remote IP address Example:'
required: false
example: null
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
uploads:
name: uploads
description: 'Filter to only show logs with file uploads'
required: false
example: true
type: boolean
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
sort:
name: sort
description: 'Column to sort by. Allowed values: id, created_at, target_id, created_by, accept_signature, action_type, note, remote_ip, user_agent, target_type, item_type, action_source, action_date. Default is created_at.'
required: false
example: created_at
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
order:
name: order
description: 'Order of sorting. Allowed values: asc, desc. Default is desc.'
required: false
example: desc
type: string
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
offset:
name: offset
description: 'Number of records to skip for pagination. Default is 0.'
required: false
example: 0
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
limit:
name: limit
description: 'Maximum number of records to return. Default is 25.'
required: false
example: 25
type: integer
enumValues: []
exampleWasSpecified: true
nullable: false
custom: []
cleanQueryParameters:
search: updated
target_type: user
target_id: 1
item_type: asset
item_id: 1
action_type: create
created_by: 1
action_source: web
uploads: true
sort: created_at
order: desc
offset: 0
limit: 25
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=Q7c7iu2qi9Mil7UrdGdQhd5UHhMHwIs9J7wKxvhY; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,54 +0,0 @@
name: Misc
description: ''
endpoints:
-
httpMethods:
- GET
uri: api/v1/version
metadata:
groupName: Misc
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Version API routes'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters: []
cleanUrlParameters: []
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=4opUPyEuZlodDlaWWG7JNFN7lCuHNMim1KxZHPur; expires=Sat, 18 Oct 2025 20:42:52 GMT; Max-Age=719999; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,285 +0,0 @@
name: Files
description: ''
endpoints:
-
httpMethods:
- GET
uri: 'api/v1/{object_type}/{id}/files'
metadata:
groupName: Files
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'List Files for an Object'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
object_type:
name: object_type
description: ''
required: true
example: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
id:
name: id
description: 'The ID of the {object type}.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
object_type: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=HfR9HTwK8SuIGE2OuiaImTbUpBhp1HtUBHMeilHy; expires=Sat, 18 Oct 2025 20:42:53 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- GET
uri: 'api/v1/{object_type}/{id}/files/{file_id}'
metadata:
groupName: Files
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Display File'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
object_type:
name: object_type
description: ''
required: true
example: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
id:
name: id
description: 'The ID of the {object type}.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
file_id:
name: file_id
description: 'The ID of the file.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
object_type: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
id: architecto
file_id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses:
-
status: 401
content: '{"error":"Unauthorized or unauthenticated."}'
headers:
cache-control: 'max-age=0, must-revalidate, no-cache, no-store, private'
content-type: application/json
vary: Origin
pragma: no-cache
expires: 'Sun, 02 Jan 1990 00:00:00 GMT'
x-content-type-options: nosniff
feature-policy: "accelerometer 'none';autoplay 'none';camera 'none';display-capture 'none';document-domain 'none';encrypted-media 'none';fullscreen 'none';geolocation 'none';sync-xhr 'none';usb 'none';xr-spatial-tracking 'none'"
referrer-policy: same-origin
content-security-policy: "default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src 'self';object-src 'none';font-src 'self' data:;img-src 'self' data: https://snipe-it.test https://avatars.githubusercontent.com/u/ https://www.google.com/images/branding/googlelogo/2x/ https://snipe-flysystem-public-test.s3-us-west-2.amazonaws.com https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com"
set-cookie: 'snipe-dev_local=N688uqN5BWG2WKvT3v6TxAJAl1Il05lzaTycQ40x; expires=Sat, 18 Oct 2025 20:42:53 GMT; Max-Age=720000; path=/; secure; httponly; samesite=lax'
description: null
custom: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- POST
uri: 'api/v1/{object_type}/{id}/files'
metadata:
groupName: Files
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Upload File to an Object'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
object_type:
name: object_type
description: ''
required: true
example: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
id:
name: id
description: 'The ID of the {object type}.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
object_type: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []
-
httpMethods:
- DELETE
uri: 'api/v1/{object_type}/{id}/files/{file_id}/delete'
metadata:
groupName: Files
groupDescription: ''
subgroup: ''
subgroupDescription: ''
title: 'Delete File'
description: ''
authenticated: true
deprecated: false
custom: []
headers:
Authorization: 'Bearer 6g43cv8PD1aE5beadkZfhV6'
Content-Type: application/json
Accept: application/json
urlParameters:
object_type:
name: object_type
description: ''
required: true
example: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
id:
name: id
description: 'The ID of the {object type}.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
file_id:
name: file_id
description: 'The ID of the file.'
required: true
example: architecto
type: string
enumValues: []
exampleWasSpecified: false
nullable: false
custom: []
cleanUrlParameters:
object_type: accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|users
id: architecto
file_id: architecto
queryParameters: []
cleanQueryParameters: []
bodyParameters: []
cleanBodyParameters: []
fileParameters: []
responses: []
responseFields: []
auth:
- headers
- Authorization
- 'Bearer 6g43cv8PD1aE5beadkZfhV6'
controller: null
method: null
route: null
custom: []

View File

@@ -1,53 +0,0 @@
# To include an endpoint that isn't a part of your Laravel app (or belongs to a vendor package),
# you can define it in a custom.*.yaml file, like this one.
# Each custom file should contain an array of endpoints. Here's an example:
# See https://scribe.knuckles.wtf/laravel/documenting/custom-endpoints#extra-sorting-groups-in-custom-endpoint-files for more options
#- httpMethods:
# - POST
# uri: api/doSomething/{param}
# metadata:
# groupName: The group the endpoint belongs to. Can be a new group or an existing group.
# groupDescription: A description for the group. You don't need to set this for every endpoint; once is enough.
# subgroup: You can add a subgroup, too.
# title: Do something
# description: 'This endpoint allows you to do something.'
# authenticated: false
# headers:
# Content-Type: application/json
# Accept: application/json
# urlParameters:
# param:
# name: param
# description: A URL param for no reason.
# required: true
# example: 2
# type: integer
# queryParameters:
# speed:
# name: speed
# description: How fast the thing should be done. Can be `slow` or `fast`.
# required: false
# example: fast
# type: string
# bodyParameters:
# something:
# name: something
# description: The things we should do.
# required: true
# example:
# - string 1
# - string 2
# type: 'string[]'
# responses:
# - status: 200
# description: 'When the thing was done smoothly.'
# content: # Your response content can be an object, an array, a string or empty.
# {
# "hey": "ho ho ho"
# }
# responseFields:
# hey:
# name: hey
# description: Who knows?
# type: string # This is optional

View File

@@ -1,11 +0,0 @@
# Introduction
This documentation aims to provide the information you need to work with the Snipe-IT JSON REST API.
<aside>
<strong>Base URL</strong>: <code>https://snipe-it.test/api/v1</code>
</aside>
<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>

View File

@@ -3,8 +3,8 @@
"DOC2": "In other words, what you see locally are the requirements for your _current_ install",
"DOC3": "Please don't rely on these versions for planning upgrades unless you've fetched the most recent version",
"DOC4": "You should really just ignore it and run upgrade.php. Really",
"php_min_version": "8.2.0",
"php_max_major_minor": "8.4",
"php_max_wontwork": "8.5.0",
"current_snipeit_version": "8.0"
"php_min_version": "8.1.0",
"php_max_major_minor": "8.3",
"php_max_wontwork": "8.4.0",
"current_snipeit_version": "7.0"
}

View File

@@ -42,33 +42,18 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/1975640?v=4" width="110px;"/><br /><sub>Evan Taylor</sub>](https://github.com/Delta5)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Delta5 "Code") | [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") |
| [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") | [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") | [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/88776392?v=4" width="110px;"/><br /><sub>PlaneNuts</sub>](https://github.com/PlaneNuts)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PlaneNuts "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") |
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/qveensi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qveensi "Code") | [<img src="https://avatars.githubusercontent.com/u/3839381?v=4" width="110px;"/><br /><sub>Achmad Fienan Rahardianto</sub>](https://github.com/veenone)<br />[💻](https://github.com/snipe/snipe-it/commits?author=veenone "Code") | [<img src="https://avatars.githubusercontent.com/u/97299851?v=4" width="110px;"/><br /><sub>Christian Weirich</sub>](https://github.com/chrisweirich)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chrisweirich "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1294403?v=4" width="110px;"/><br /><sub>denzfarid</sub>](https://github.com/denzfarid)<br /> | [<img src="https://avatars.githubusercontent.com/u/94018771?v=4" width="110px;"/><br /><sub>ntbutler-nbcs</sub>](https://github.com/ntbutler-nbcs)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntbutler-nbcs "Code") | [<img src="https://avatars.githubusercontent.com/u/172697?v=4" width="110px;"/><br /><sub>Naveen</sub>](https://naveensrinivasan.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=naveensrinivasan "Code") | [<img src="https://avatars.githubusercontent.com/u/55674383?v=4" width="110px;"/><br /><sub>Mike Roquemore</sub>](https://github.com/mikeroq)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikeroq "Code") | [<img src="https://avatars.githubusercontent.com/u/7991086?v=4" width="110px;"/><br /><sub>Daniel Reeder</sub>](https://github.com/reederda)<br />[🌍](#translation-reederda "Translation") [🌍](#translation-reederda "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=reederda "Code") | [<img src="https://avatars.githubusercontent.com/u/109422491?v=4" width="110px;"/><br /><sub>vickyjaura183</sub>](https://github.com/vickyjaura183)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vickyjaura183 "Code") | [<img src="https://avatars.githubusercontent.com/u/32363424?v=4" width="110px;"/><br /><sub>Peace</sub>](https://github.com/julian-piehl)<br />[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") |
| [<img src="https://avatars.githubusercontent.com/u/231528?v=4" width="110px;"/><br /><sub>Kyle Gordon</sub>](https://github.com/kylegordon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [<img src="https://avatars.githubusercontent.com/u/53009155?v=4" width="110px;"/><br /><sub>Katharina Drexel</sub>](http://www.bfh.ch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [<img src="https://avatars.githubusercontent.com/u/1931963?v=4" width="110px;"/><br /><sub>David Sferruzza</sub>](https://david.sferruzza.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [<img src="https://avatars.githubusercontent.com/u/19511639?v=4" width="110px;"/><br /><sub>Rick Nelson</sub>](https://github.com/rnelsonee)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [<img src="https://avatars.githubusercontent.com/u/94169344?v=4" width="110px;"/><br /><sub>BasO12</sub>](https://github.com/BasO12)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [<img src="https://avatars.githubusercontent.com/u/111710123?v=4" width="110px;"/><br /><sub>Vautia</sub>](https://github.com/Vautia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") | [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") | [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") |
| [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") | [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") | [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") |
| [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") | [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") | [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") | [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") |
| [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") | [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") [💻](https://github.com/snipe/snipe-it/commits?author=swift2512 "Code") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") | [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") |
| [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") | [<img src="https://avatars.githubusercontent.com/u/31522486?v=4" width="110px;"/><br /><sub>James M</sub>](https://github.com/azmcnutt)<br />[💻](https://github.com/snipe/snipe-it/commits?author=azmcnutt "Code") | [<img src="https://avatars.githubusercontent.com/u/5183146?v=4" width="110px;"/><br /><sub>Fiala06</sub>](https://github.com/Fiala06)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Fiala06 "Code") | [<img src="https://avatars.githubusercontent.com/u/28693782?v=4" width="110px;"/><br /><sub>Nathan Taylor</sub>](https://github.com/ntaylor-86)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntaylor-86 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/16699443?v=4" width="110px;"/><br /><sub>fvollmer</sub>](https://github.com/fvollmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fvollmer "Code") | [<img src="https://avatars.githubusercontent.com/u/109086466?v=4" width="110px;"/><br /><sub>36864</sub>](https://github.com/36864)<br />[💻](https://github.com/snipe/snipe-it/commits?author=36864 "Code") | [<img src="https://avatars.githubusercontent.com/u/365751?v=4" width="110px;"/><br /><sub>Daniel O'Connor</sub>](http://clockwerx.blogspot.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CloCkWeRX "Code") | [<img src="https://avatars.githubusercontent.com/u/102852568?v=4" width="110px;"/><br /><sub>BeatSpark</sub>](https://github.com/BeatSpark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BeatSpark "Code") | [<img src="https://avatars.githubusercontent.com/u/59203607?v=4" width="110px;"/><br /><sub>mrdahbi</sub>](https://github.com/mrdahbi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mrdahbi "Code") | [<img src="https://avatars.githubusercontent.com/u/6661332?v=4" width="110px;"/><br /><sub>Fabian Schmid</sub>](http://sr.solutions)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chfsx "Code") | [<img src="https://avatars.githubusercontent.com/u/1288116?v=4" width="110px;"/><br /><sub>Chris Olin</sub>](https://www.chrisolin.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=realchrisolin "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3803132?v=4" width="110px;"/><br /><sub>Dan</sub>](https://github.com/mnemonicly)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mnemonicly "Code") | [<img src="https://avatars.githubusercontent.com/u/43917728?v=4" width="110px;"/><br /><sub>Nebel</sub>](https://github.com/NebelKreis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NebelKreis "Code") | [<img src="https://avatars.githubusercontent.com/u/132433803?v=4" width="110px;"/><br /><sub>test1337ahp</sub>](https://github.com/test1337ahp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=test1337ahp "Code") | [<img src="https://avatars.githubusercontent.com/u/1916566?v=4" width="110px;"/><br /><sub>Jonathon Reinhart</sub>](https://github.com/JonathonReinhart)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JonathonReinhart "Code") | [<img src="https://avatars.githubusercontent.com/u/484742?v=4" width="110px;"/><br /><sub>aranar-pro</sub>](https://github.com/aranar-pro)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aranar-pro "Code") | [<img src="https://avatars.githubusercontent.com/u/27019397?v=4" width="110px;"/><br /><sub>Phil</sub>](https://github.com/phil-flip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phil-flip "Code") | [<img src="https://avatars.githubusercontent.com/u/6473460?v=4" width="110px;"/><br /><sub>Steffy Fort</sub>](https://fe80.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fe80 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3302372?v=4" width="110px;"/><br /><sub>Jared Busch</sub>](https://github.com/sorvani)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sorvani "Code") | [<img src="https://avatars.githubusercontent.com/u/111956991?v=4" width="110px;"/><br /><sub>seanborg-codethink</sub>](https://github.com/seanborg-codethink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanborg-codethink "Code") | [<img src="https://avatars.githubusercontent.com/u/160669961?v=4" width="110px;"/><br /><sub>dkaatz</sub>](https://github.com/dkaatz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkaatz "Code") | [<img src="https://avatars.githubusercontent.com/u/827205?v=4" width="110px;"/><br /><sub>Daniel Ruf</sub>](https://threema.id/74SF7MW6?text=)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DanielRuf "Code") | [<img src="https://avatars.githubusercontent.com/u/38883201?v=4" width="110px;"/><br /><sub>ahpaleus</sub>](https://github.com/ahpaleus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ahpaleus "Code") | [<img src="https://avatars.githubusercontent.com/u/22906055?v=4" width="110px;"/><br /><sub>Anh DAO-DUY</sub>](https://github.com/mink-adao-duy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mink-adao-duy "Code") | [<img src="https://avatars.githubusercontent.com/u/4723453?v=4" width="110px;"/><br /><sub>Andres Gutierrez</sub>](https://github.com/Serdnad)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Serdnad "Code") |
| [<img src="https://avatars.githubusercontent.com/u/111083379?v=4" width="110px;"/><br /><sub>Warren White</sub>](https://github.com/wewhite)<br />[💻](https://github.com/snipe/snipe-it/commits?author=wewhite "Code") | [<img src="https://avatars.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://robintemme.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=robintemme "Code") | [<img src="https://avatars.githubusercontent.com/u/47008367?v=4" width="110px;"/><br /><sub>herroworrd</sub>](https://github.com/herroworrd)<br />[💻](https://github.com/snipe/snipe-it/commits?author=herroworrd "Code") | [<img src="https://avatars.githubusercontent.com/u/28558609?v=4" width="110px;"/><br /><sub>vicleos</sub>](https://mubiu.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vicleos "Code") | [<img src="https://avatars.githubusercontent.com/u/1016780?v=4" width="110px;"/><br /><sub>Bob Clough</sub>](http://thinkl33t.co.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thinkl33t "Code") | [<img src="https://avatars.githubusercontent.com/u/10648463?v=4" width="110px;"/><br /><sub>Brandon Daniel Bailey</sub>](https://github.com/brandon-bailey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brandon-bailey "Code") | [<img src="https://avatars.githubusercontent.com/u/23556080?v=4" width="110px;"/><br /><sub>Marc Bartelt</sub>](https://github.com/marcquark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcquark "Code") |
| [<img src="https://avatars.githubusercontent.com/u/18286893?v=4" width="110px;"/><br /><sub>manu-crealytics</sub>](https://github.com/manu-crealytics)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manu-crealytics "Code") | [<img src="https://avatars.githubusercontent.com/u/18245993?v=4" width="110px;"/><br /><sub>Konstantin Köhring</sub>](https://www.galaxy102.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Galaxy102 "Code") | [<img src="https://avatars.githubusercontent.com/u/685167?v=4" width="110px;"/><br /><sub>Deloz</sub>](https://deloz.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deloz "Code") | [<img src="https://avatars.githubusercontent.com/u/2682426?v=4" width="110px;"/><br /><sub>Martin Berg</sub>](https://github.com/mbrrg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mbrrg "Code") | [<img src="https://avatars.githubusercontent.com/u/3694534?v=4" width="110px;"/><br /><sub>Richard Schwab</sub>](https://github.com/Nothing4You)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nothing4You "Code") | [<img src="https://avatars.githubusercontent.com/u/8959676?v=4" width="110px;"/><br /><sub>Rick Heil</sub>](https://rickheil.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rickheil "Code") | [<img src="https://avatars.githubusercontent.com/u/397106?v=4" width="110px;"/><br /><sub>Ross Crawford-d'Heureuse</sub>](https://github.com/rosscdh)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rosscdh "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1621107?v=4" width="110px;"/><br /><sub>Ryan McGuire</sub>](https://github.com/McG800)<br />[💻](https://github.com/snipe/snipe-it/commits?author=McG800 "Code") | [<img src="https://avatars.githubusercontent.com/u/77835667?v=4" width="110px;"/><br /><sub>SBrown2021</sub>](https://github.com/SBrown2021)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SBrown2021 "Code") | [<img src="https://avatars.githubusercontent.com/u/8780913?v=4" width="110px;"/><br /><sub>Serkan</sub>](https://github.com/serkanerip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=serkanerip "Code") | [<img src="https://avatars.githubusercontent.com/u/63188620?v=4" width="110px;"/><br /><sub>Shanks</sub>](https://www.yudelei.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Shankschn "Code") | [<img src="https://avatars.githubusercontent.com/u/198525698?v=4" width="110px;"/><br /><sub>cendai-mis</sub>](https://github.com/cendai-mis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cendai-mis "Code") | [<img src="https://avatars.githubusercontent.com/u/8724583?v=4" width="110px;"/><br /><sub>Shaun McPeck</sub>](https://smcpeck.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smcpeck "Code") | [<img src="https://avatars.githubusercontent.com/u/1378836?v=4" width="110px;"/><br /><sub>Stephen</sub>](https://github.com/snazy2000)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snazy2000 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/4462739?v=4" width="110px;"/><br /><sub>Steven</sub>](http://nevets82.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nevets82 "Code") | [<img src="https://avatars.githubusercontent.com/u/29017267?v=4" width="110px;"/><br /><sub>Mateus Villar</sub>](https://mateusvillar.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Mateus-Romera "Code") | [<img src="https://avatars.githubusercontent.com/u/12749393?v=4" width="110px;"/><br /><sub>Matthew Zackschewski</sub>](https://github.com/mzack5020)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mzack5020 "Code") | [<img src="https://avatars.githubusercontent.com/u/12660103?v=4" width="110px;"/><br /><sub>Matthias Frei</sub>](https://www.frei.media/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=firefrei "Code") | [<img src="https://avatars.githubusercontent.com/u/824840?v=4" width="110px;"/><br /><sub>Nenad Ticaric</sub>](https://github.com/nticaric)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nticaric "Code") | [<img src="https://avatars.githubusercontent.com/u/706439?v=4" width="110px;"/><br /><sub>Nikolay Didenko</sub>](https://github.com/Scorcher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scorcher "Code") | [<img src="https://avatars.githubusercontent.com/u/5457236?v=4" width="110px;"/><br /><sub>Nuno Maduro</sub>](https://nunomaduro.com/sponsorships)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nunomaduro "Code") |
| [<img src="https://avatars.githubusercontent.com/u/8883074?v=4" width="110px;"/><br /><sub>Oliver Walerys</sub>](https://tektikhq.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=owalerys "Code") | [<img src="https://avatars.githubusercontent.com/u/3102039?v=4" width="110px;"/><br /><sub>R. Christian McDonald</sub>](https://keybase.io/rcmcdonald91)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rcmcdonald91 "Code") | [<img src="https://avatars.githubusercontent.com/u/1525581?v=4" width="110px;"/><br /><sub>nix</sub>](https://nnix.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nixn "Code") | [<img src="https://avatars.githubusercontent.com/u/55462380?v=4" width="110px;"/><br /><sub>octobunny</sub>](https://github.com/octobunny)<br />[💻](https://github.com/snipe/snipe-it/commits?author=octobunny "Code") | [<img src="https://avatars.githubusercontent.com/u/8558670?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/sreyemnayr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sreyemnayr "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>p3nj</sub>](https://benji.ltd/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=p3nj "Code") | [<img src="https://avatars.githubusercontent.com/u/6201617?v=4" width="110px;"/><br /><sub>Tim White</sub>](https://github.com/timwsuqld)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timwsuqld "Code") |
| [<img src="https://avatars.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>yannikp</sub>](https://github.com/yannikp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=yannikp "Code") | [<img src="https://avatars.githubusercontent.com/u/20525448?v=4" width="110px;"/><br /><sub>victoria</sub>](https://github.com/viclou)<br />[💻](https://github.com/snipe/snipe-it/commits?author=viclou "Code") | [<img src="https://avatars.githubusercontent.com/u/40685314?v=4" width="110px;"/><br /><sub>Valentyn Tulub</sub>](https://github.com/valentyntu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=valentyntu "Code") | [<img src="https://avatars.githubusercontent.com/u/864520?v=4" width="110px;"/><br /><sub>Wouter van Os</sub>](http://wouter0100.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Wouter0100 "Code") | [<img src="https://avatars.githubusercontent.com/u/3946540?v=4" width="110px;"/><br /><sub>Wyatt Teeter</sub>](https://www.linkedin.com/in/wyatt-teeter)<br />[💻](https://github.com/snipe/snipe-it/commits?author=xWyatt "Code") | [<img src="https://avatars.githubusercontent.com/u/1596124?v=4" width="110px;"/><br /><sub>Yorick Terweijden</sub>](https://github.com/terwey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=terwey "Code") | [<img src="https://avatars.githubusercontent.com/u/69298836?v=4" width="110px;"/><br /><sub>bmkalle</sub>](https://github.com/bmkalle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bmkalle "Code") |
| [<img src="https://avatars.githubusercontent.com/u/28403467?v=4" width="110px;"/><br /><sub>bricelabelle</sub>](https://github.com/bricelabelle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bricelabelle "Code") | [<img src="https://avatars.githubusercontent.com/u/97770090?v=4" width="110px;"/><br /><sub>corydlamb</sub>](https://github.com/corydlamb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=corydlamb "Code") | [<img src="https://avatars.githubusercontent.com/u/1154133?v=4" width="110px;"/><br /><sub>Diogenes S. Jesus</sub>](http://twitter.com/splash)<br />[💻](https://github.com/snipe/snipe-it/commits?author=splashx "Code") | [<img src="https://avatars.githubusercontent.com/u/5826629?v=4" width="110px;"/><br /><sub>D M</sub>](https://github.com/dkmansion)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkmansion "Code") | [<img src="https://avatars.githubusercontent.com/u/14837699?v=4" width="110px;"/><br /><sub>Dustin B</sub>](https://github.com/Jarli01)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jarli01 "Code") | [<img src="https://avatars.githubusercontent.com/u/348344?v=4" width="110px;"/><br /><sub>Fabian Grutschus</sub>](https://github.com/fabiang)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fabiang "Code") | [<img src="https://avatars.githubusercontent.com/u/1491053?v=4" width="110px;"/><br /><sub>MelonSmasher</sub>](https://github.com/MelonSmasher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MelonSmasher "Code") |
| [<img src="https://avatars.githubusercontent.com/u/80526133?v=4" width="110px;"/><br /><sub>AlexanderWPapyrus</sub>](https://github.com/AlexanderWPapyrus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [<img src="https://avatars.githubusercontent.com/u/306231?v=4" width="110px;"/><br /><sub>Alexandr Hacicheant</sub>](https://github.com/disc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [<img src="https://avatars.githubusercontent.com/u/3032891?v=4" width="110px;"/><br /><sub>Hex</sub>](https://hex128.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [<img src="https://avatars.githubusercontent.com/u/8697942?v=4" width="110px;"/><br /><sub>Arunas Skirius</sub>](https://github.com/arukompas)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [<img src="https://avatars.githubusercontent.com/u/104396?v=4" width="110px;"/><br /><sub>Ben Periton</sub>](https://github.com/benperiton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [<img src="https://avatars.githubusercontent.com/u/11906832?v=4" width="110px;"/><br /><sub>Byron Wolfman</sub>](https://wolfman.dev/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [<img src="https://avatars.githubusercontent.com/u/56485508?v=4" width="110px;"/><br /><sub>Calvin</sub>](https://github.com/CalvinSchwartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1571724?v=4" width="110px;"/><br /><sub>Герхард PICCORO Lenz McKAY </sub>](https://github-readme-stats.vercel.app/api?username=mckaygerhard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") | [<img src="https://avatars.githubusercontent.com/u/15015119?v=4" width="110px;"/><br /><sub>Johannes Pollitt</sub>](https://github.com/FlorestanII)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorestanII "Code") | [<img src="https://avatars.githubusercontent.com/u/14185442?v=4" width="110px;"/><br /><sub>Michael Strobel</sub>](https://strobelm.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=strobelm "Code") | [<img src="https://avatars.githubusercontent.com/u/634790?v=4" width="110px;"/><br /><sub>Nicky West</sub>](http://nickwest.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nickwest "Code") | [<img src="https://avatars.githubusercontent.com/u/1347327?v=4" width="110px;"/><br /><sub>akaspeh1</sub>](https://github.com/akaspeh1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akaspeh1 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/QveenSi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=QveenSi "Code") | [<img src="https://avatars.githubusercontent.com/u/3839381?v=4" width="110px;"/><br /><sub>Achmad Fienan Rahardianto</sub>](https://github.com/veenone)<br />[💻](https://github.com/snipe/snipe-it/commits?author=veenone "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/QveenSi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=QveenSi "Code") |
| [<img src="https://avatars.githubusercontent.com/u/97299851?v=4" width="110px;"/><br /><sub>Christian Weirich</sub>](https://github.com/chrisweirich)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chrisweirich "Code") | [<img src="https://avatars.githubusercontent.com/u/1294403?v=4" width="110px;"/><br /><sub>denzfarid</sub>](https://github.com/denzfarid)<br /> | [<img src="https://avatars.githubusercontent.com/u/94018771?v=4" width="110px;"/><br /><sub>ntbutler-nbcs</sub>](https://github.com/ntbutler-nbcs)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntbutler-nbcs "Code") | [<img src="https://avatars.githubusercontent.com/u/172697?v=4" width="110px;"/><br /><sub>Naveen</sub>](https://naveensrinivasan.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=naveensrinivasan "Code") | [<img src="https://avatars.githubusercontent.com/u/55674383?v=4" width="110px;"/><br /><sub>Mike Roquemore</sub>](https://github.com/mikeroq)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikeroq "Code") | [<img src="https://avatars.githubusercontent.com/u/7991086?v=4" width="110px;"/><br /><sub>Daniel Reeder</sub>](https://github.com/reederda)<br />[🌍](#translation-reederda "Translation") [🌍](#translation-reederda "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=reederda "Code") | [<img src="https://avatars.githubusercontent.com/u/109422491?v=4" width="110px;"/><br /><sub>vickyjaura183</sub>](https://github.com/vickyjaura183)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vickyjaura183 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/32363424?v=4" width="110px;"/><br /><sub>Peace</sub>](https://github.com/julian-piehl)<br />[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") | [<img src="https://avatars.githubusercontent.com/u/231528?v=4" width="110px;"/><br /><sub>Kyle Gordon</sub>](https://github.com/kylegordon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [<img src="https://avatars.githubusercontent.com/u/53009155?v=4" width="110px;"/><br /><sub>Katharina Drexel</sub>](http://www.bfh.ch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [<img src="https://avatars.githubusercontent.com/u/1931963?v=4" width="110px;"/><br /><sub>David Sferruzza</sub>](https://david.sferruzza.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [<img src="https://avatars.githubusercontent.com/u/19511639?v=4" width="110px;"/><br /><sub>Rick Nelson</sub>](https://github.com/rnelsonee)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [<img src="https://avatars.githubusercontent.com/u/94169344?v=4" width="110px;"/><br /><sub>BasO12</sub>](https://github.com/BasO12)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [<img src="https://avatars.githubusercontent.com/u/111710123?v=4" width="110px;"/><br /><sub>Vautia</sub>](https://github.com/Vautia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") |
| [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") | [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") |
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") |
| [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") | [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -1,8 +1,8 @@
FROM ubuntu:24.04
FROM ubuntu:22.04
LABEL maintainer="Brady Wetherington <bwetherington@grokability.com>"
# No need to add `apt-get clean` here, reference:
# - https://github.com/grokability/snipe-it/pull/9201
# - https://github.com/snipe/snipe-it/pull/9201
# - https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
RUN export DEBIAN_FRONTEND=noninteractive; \
@@ -14,16 +14,16 @@ RUN export DEBIAN_FRONTEND=noninteractive; \
apt-utils \
apache2 \
apache2-bin \
libapache2-mod-php8.3 \
php8.3-curl \
php8.3-ldap \
php8.3-mysql \
php8.3-gd \
php8.3-xml \
php8.3-mbstring \
php8.3-zip \
php8.3-bcmath \
php8.3-redis \
libapache2-mod-php8.1 \
php8.1-curl \
php8.1-ldap \
php8.1-mysql \
php8.1-gd \
php8.1-xml \
php8.1-mbstring \
php8.1-zip \
php8.1-bcmath \
php8.1-redis \
php-memcached \
patch \
curl \
@@ -40,7 +40,8 @@ autoconf \
libc-dev \
libldap-common \
pkg-config \
php8.3-dev \
libmcrypt-dev \
php8.1-dev \
ca-certificates \
unzip \
dnsutils \
@@ -50,13 +51,18 @@ dnsutils \
RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar
RUN php go-pear.phar
RUN pecl install mcrypt
RUN bash -c "echo extension=/usr/lib/php/20210902/mcrypt.so > /etc/php/8.1/mods-available/mcrypt.ini"
RUN phpenmod mcrypt
RUN phpenmod gd
RUN phpenmod bcmath
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.3/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.3/cli/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.1/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.1/cli/php.ini
RUN useradd -m --uid 10000 --gid 50 docker
RUN useradd -m --uid 1000 --gid 50 docker
RUN echo export APACHE_RUN_USER=docker >> /etc/apache2/envvars
RUN echo export APACHE_RUN_GROUP=staff >> /etc/apache2/envvars
@@ -110,7 +116,7 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Get dependencies
USER docker
RUN COMPOSER_CACHE_DIR=/dev/null composer install --no-dev --working-dir=/var/www/html && rm -rf /var/www/html/vendor/*/*/.git
RUN composer install --no-dev --working-dir=/var/www/html
USER root
############### APPLICATION INSTALL/INIT #################

View File

@@ -70,7 +70,7 @@ COPY --from=composer /usr/bin/composer /usr/local/bin
ARG COMPOSER_ALLOW_SUPERUSER=1
RUN set -eux; \
# Download and extract snipeit tarball
curl -o snipeit.tar.gz -fL "https://github.com/grokability/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
curl -o snipeit.tar.gz -fL "https://github.com/snipe/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
tar -xzf snipeit.tar.gz --strip-components=1 -C /var/www/html/; \
rm snipeit.tar.gz; \
# Install composer php dependencies

View File

@@ -1,15 +1,15 @@
![snipe-it-by-grok](https://github.com/grokability/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
![snipe-it-by-grok](https://github.com/snipe/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/804dd1beb14a41f38810ab77d64fc4fc)](https://app.codacy.com/gh/grokability/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/grokability/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/grokability/snipe-it/actions/workflows/tests.yml)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
[![All Contributors](https://img.shields.io/badge/all_contributors-331-orange.svg?style=flat-square)](#contributing) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk)
## Snipe-IT - Open Source Asset Management System
This 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.
It is built on [Laravel 11](http://laravel.com).
It is built on [Laravel 10](http://laravel.com).
Snipe-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/).)
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
> [!TIP]
> __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.
@@ -44,7 +44,7 @@ For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.i
-----
### Bug Reports & Feature Requests
Feel free to check out the [GitHub Issues for this project](https://github.com/grokability/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
> [!IMPORTANT]
> **PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
@@ -76,36 +76,23 @@ Since the release of the JSON REST API, several third-party developers have been
> [!NOTE]
> As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
#### Libraries & Modules
- [Python Module](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
- [InQRy -unmaintained-](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
- [jamf2snipe](https://github.com/grokability/jamf2snipe) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance
- [jamf-snipe-rename](https://macblog.org/jamf-snipe-rename/) - Python script to rename computers in Jamf from Snipe-IT
- [Marksman](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT
- [Snipe-IT plugin for Jira Service Desk](https://marketplace.atlassian.com/apps/1220964/snipe-it-for-jira)
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-IT.
- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@Karpadiem](https://github.com/Karpadiem) - Python script to synchronize information between Mosyle and Snipe-IT.
- [WWW::SnipeIT](https://github.com/SEDC/perl-www-snipeit) by [@SEDC](https://github.com/SEDC) - perl module for accessing the API
- [UniFi to Snipe-IT](https://www.edtechirl.com/p/snipe-it-and-azure-asset-management) originally by [@karpadiem](https://github.com/karpadiem) - Python script that synchronizes UniFi devices with Snipe-IT.
- [UniFi to Snipe-IT](https://github.com/RodneyLeeBrands/UnifiSnipeSync) by [@karpadiem](https://github.com/karpadiem) - Python script that synchronizes UniFi devices with Snipe-IT.
- [Kandji2Snipe](https://github.com/grokability/kandji2snipe) by [@briangoldstein](https://github.com/briangoldstein) - Python script that synchronizes Kandji with Snipe-IT.
- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by [@ReticentRobot](https://github.com/ReticentRobot) - Windows agent for Snipe-IT.
- [Gate Pass Generator](https://github.com/cha7uraAE/snipe-it-gate-pass-system) by [@cha7uraAE](https://github.com/cha7uraAE) - A Streamlit application for generating gate passes based on hardware data from a Snipe-IT API.
- [InQRy (archived)](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
- [Marksman (archived)](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT
- [Python Module (archived)](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
We also have a handful of [Google Apps scripts](https://github.com/grokability/google-apps-scripts-for-snipe-it) to help with various tasks.
#### Mobile Apps
We're currently working on our own mobile app, but in the meantime, check out these third-party apps that work with Snipe-IT:
- [SnipeMate](https://snipemate.app/) (iOS, Google Play, Huawei AppGallery) by Mars Technology
- [Snipe-Scan](https://apps.apple.com/do/app/snipe-scan/id6744179400?uo=2) (iOS) by Nicolas Maton
- [Snipe-IT Assets Management](https://play.google.com/store/apps/details?id=com.diegogarciadev.assetsmanager.snipeit&hl=en&pli=1) (Google Play) by DiegoGarciaDEV
- [AssetX](https://apps.apple.com/my/app/assetx-for-snipe-it/id6741996196?uo=2) (iOS) for Snipe-IT by Rishi Gupta
-----
@@ -133,15 +120,9 @@ The ERD is available [online here](https://drawsql.app/templates/snipe-it).
Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years!
-----
### Star History
[![Star History Chart](https://api.star-history.com/svg?repos=grokability/snipe-it&type=Date)](https://www.star-history.com/#grokability/snipe-it&Date)
------
### Announcement List
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
We also usually make smaller announcements on our social accounts, our Discord, and our blog, so be sure to subscribe to those if you're looking for more granular announcements.

View File

@@ -11,7 +11,6 @@ make it impossible to backport security fixes on older versions.
| Version | Supported |
|---------| ------------------ |
| 8.x | :white_check_mark: |
| 7.x | :white_check_mark: |
| 6.x | :x: |
| 5.1.x | :x: |

2
Vagrantfile vendored
View File

@@ -1,7 +1,7 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/grokability/snipe-it/master/snipeit.sh"
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/snipe/snipe-it/master/snipeit.sh"
NETWORK_BRIDGE= "en0: Wi-Fi (AirPort)"
Vagrant.configure("2") do |config|

View File

@@ -6,7 +6,7 @@
"it asset"
],
"website": "https://snipeitapp.com/",
"repository": "https://github.com/grokability/snipe-it",
"repository": "https://github.com/snipe/snipe-it",
"logo": "https://pbs.twimg.com/profile_images/976748875733020672/K-HnZCCK_400x400.jpg",
"success_url": "/setup",
"env": {

View File

@@ -1,48 +0,0 @@
<?php
namespace App\Actions\CheckoutRequests;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetCancelation;
use Illuminate\Auth\Access\AuthorizationException;
class CancelCheckoutRequestAction
{
public static function run(Asset $asset, User $user)
{
if (!Company::isCurrentUserHasAccess($asset)) {
throw new AuthorizationException();
}
$asset->cancelRequest();
$asset->decrement('requests_counter', 1);
$data['item'] = $asset;
$data['target'] = $user;
$data['item_quantity'] = 1;
$settings = Setting::getSettings();
$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $asset->id;
$logaction->item_type = $data['item_type'] = Asset::class;
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
$logaction->target_id = $data['user_id'] = auth()->id();
$logaction->target_type = User::class;
$logaction->location_id = $user->location_id ?? null;
$logaction->logaction('request canceled');
try {
$settings->notify(new RequestAssetCancelation($data));
} catch (\Exception $e) {
\Log::warning($e);
}
return true;
}
}

View File

@@ -1,54 +0,0 @@
<?php
namespace App\Actions\CheckoutRequests;
use App\Exceptions\AssetNotRequestable;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetNotification;
use Illuminate\Auth\Access\AuthorizationException;
use Log;
class CreateCheckoutRequestAction
{
/**
* @throws AssetNotRequestable
* @throws AuthorizationException
*/
public static function run(Asset $asset, User $user): string
{
if (is_null(Asset::RequestableAssets()->find($asset->id))) {
throw new AssetNotRequestable($asset);
}
if (!Company::isCurrentUserHasAccess($asset)) {
throw new AuthorizationException();
}
$data['item'] = $asset;
$data['target'] = $user;
$data['item_quantity'] = 1;
$settings = Setting::getSettings();
$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $asset->id;
$logaction->item_type = $data['item_type'] = Asset::class;
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
$logaction->target_id = $data['user_id'] = auth()->id();
$logaction->target_type = User::class;
$logaction->location_id = $user->location_id ?? null;
$logaction->logaction('requested');
$asset->request();
$asset->increment('requests_counter', 1);
try {
$settings->notify(new RequestAssetNotification($data));
} catch (\Exception $e) {
Log::warning($e);
}
return true;
}
}

View File

@@ -1,68 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use App\Models\User;
use Illuminate\Console\Command;
class CleanIncorrectCheckoutAcceptances extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:clean-checkout-acceptances';
/**
* The console command description.
*
* @var string
*/
protected $description = "Delete checkout acceptances for checkouts to non-users";
/**
* Execute the console command.
*/
public function handle()
{
$deletions = 0;
$skips = 0;
// This walks *every* checkoutacceptance. That's gnarly. But necessary
$this->withProgressBar(CheckoutAcceptance::all(), function ($checkoutAcceptance) use (&$deletions, &$skips) {
$item = $checkoutAcceptance->checkoutable;
$checkout_to_id = $checkoutAcceptance->assigned_to_id;
if(is_null($item)) {
$this->info("'Checkoutable' Item is null, going to next record");
return; //'false' allegedly breaks execution entirely, so 'true' maybe doesn't? hrm. just straight return maybe?
}
if(get_class($item) == LicenseSeat::class) {
$item = $item->license;
}
foreach($item->assetlog()->where('action_type','checkout')->get() as $assetlog) {
if ($assetlog->target_id == $checkout_to_id && $assetlog->target_type != User::class) {
//We have a checkout-to an ID for a non-User, which matches to an ID in the checkout_acceptances table
//now, let's compare the _times_ - are they close?
//I'm picking `created_at` over `action_date` because I'm more interested in when the actionlogs
//were _created_, not when they were alleged to have happened - those created_at times need to be within 'X' seconds of
//each other (currently 5)
if ($assetlog->created_at->diffInSeconds($checkoutAcceptance->created_at, true) <= 5) { //we're allowing for five _ish_ seconds of slop
$deletions++;
$checkoutAcceptance->forceDelete(); // HARD delete this record; it should have never been
return;
} else {
//$this->info("The two records are too far apart");
}
} else {
//$this->info("No match! checkout to id: " . $checkout_to_id." target_id: ".$assetlog->target_id." target_type: ".$assetlog->target_type);
}
}
$skips++;
});
$this->error("Final deletion count: $deletions, and skip count: $skips");
}
}

View File

@@ -1,74 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutRequest;
use Illuminate\Console\Command;
class CleanOldCheckoutRequests extends Command
{
private int $deletions = 0;
private int $skips = 0;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:clean-old-checkout-requests';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes checkout requests that reference deleted assets or users.';
/**
* Execute the console command.
*/
public function handle()
{
$requests = CheckoutRequest::with([
'user' => function ($query) {
$query->withTrashed();
},
'requestedItem' => function ($query) {
$query->withTrashed();
},
])->get();
$this->info("Processing {$requests->count()} checkout requests");
$this->withProgressBar($requests, function ($request) {
if ($this->shouldForceDelete($request)) {
$request->forceDelete();
$this->deletions++;
return;
}
if ($this->shouldSoftDelete($request)) {
$request->delete();
$this->deletions++;
return;
}
$this->skips++;
});
$this->info("Final deletion count: $this->deletions, and skip count: $this->skips");
return 0;
}
private function shouldForceDelete(CheckoutRequest $request)
{
// check if the requestable or user relationship is null
return !$request->requestable || !$request->user;
}
private function shouldSoftDelete(CheckoutRequest $request)
{
return $request->requestable->trashed() || $request->user->trashed();
}
}

View File

@@ -1,53 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use Illuminate\Console\Command;
class DisableSAML extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:saml-disable';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This is a rescue command that can be used to turn off SAML settings in the event that you managed to lock yourself out using bad SAML settings.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm("\n****************************************************\nThis will disable SAML support. You will not be able \nto login with an account that does not exist \nlocally in the Snipe-IT local database. \n****************************************************\n\nDo you wish to continue? [y|N]")) {
$setting = Setting::getSettings();
$setting->saml_enabled = 0;
if ($setting->save()) {
$this->info('SAML has been set to disabled.');
} else {
$this->info('Unable to disable SAML.');
}
} else {
$this->info('Canceled. No actions taken.');
}
}
}

View File

@@ -1,151 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Accessory;
use App\Models\Actionlog;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Model;
class FixBulkAccessoryCheckinActionLogEntries extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:fix-bulk-accessory-action-log-entries {--dry-run : Run the sync process but don\'t update the database} {--skip-backup : Skip pre-execution backup}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This script attempts to fix timestamps and missing created_by values for bulk checkin entries in the log table';
private bool $dryrun = false;
private bool $skipBackup = false;
/**
* Execute the console command.
*/
public function handle()
{
$this->skipBackup = $this->option('skip-backup');
$this->dryrun = $this->option('dry-run');
if ($this->dryrun) {
$this->info('This is a DRY RUN - no changes will be saved.');
$this->newLine();
}
$logs = Actionlog::query()
// only look for accessory checkin logs
->where('item_type', Accessory::class)
// that were part of a bulk checkin
->where('note', 'Bulk checkin items')
// logs that were improperly timestamped should have created_at in the 1970s
->whereYear('created_at', '1970')
->get();
if ($logs->isEmpty()) {
$this->info('No logs found with incorrect timestamps.');
return 0;
}
$this->info('Found ' . $logs->count() . ' logs with incorrect timestamps:');
$this->table(
['ID', 'Created By', 'Created At', 'Updated At'],
$logs->map(function ($log) {
return [
$log->id,
$log->created_by,
$log->created_at,
$log->updated_at,
];
})
);
if (!$this->dryrun && !$this->confirm('Update these logs?')) {
return 0;
}
if (!$this->dryrun && !$this->skipBackup) {
$this->info('Backing up the database before making changes...');
$this->call('snipeit:backup');
}
if ($this->dryrun) {
$this->newLine();
$this->info('DRY RUN. NOT ACTUALLY UPDATING LOGS.');
}
foreach ($logs as $log) {
$this->newLine();
$this->info('Processing log id:' . $log->id);
// created_by was not being set for accessory bulk checkins
// so let's see if there was another bulk checkin log
// with the same timestamp and a created_by value we can use.
if (is_null($log->created_by)) {
$createdByFromSimilarLog = $this->getCreatedByAttributeFromSimilarLog($log);
if ($createdByFromSimilarLog) {
$this->line(vsprintf('Updating log id:%s created_by to %s', [$log->id, $createdByFromSimilarLog]));
$log->created_by = $createdByFromSimilarLog;
} else {
$this->warn(vsprintf('No created_by found for log id:%s', [$log->id]));
$this->warn('Skipping updating this log since no similar log was found to update created_by from.');
// If we can't find a similar log then let's skip updating it
continue;
}
}
$this->line(vsprintf('Updating log id:%s from %s to %s', [$log->id, $log->created_at, $log->updated_at]));
$log->created_at = $log->updated_at;
if (!$this->dryrun) {
Model::withoutTimestamps(function () use ($log) {
$log->saveQuietly();
});
}
}
$this->newLine();
if ($this->dryrun) {
$this->info('DRY RUN. NO CHANGES WERE ACTUALLY MADE.');
}
return 0;
}
/**
* Hopefully the bulk checkin included other items like assets or licenses
* so we can use one of those logs to get the correct created_by value.
*
* This method attempts to find a bulk check in log that was
* created at the same time as the log passed in.
*/
private function getCreatedByAttributeFromSimilarLog(Actionlog $log): null|int
{
$similarLog = Actionlog::query()
->whereNotNull('created_by')
->where([
'action_type' => 'checkin from',
'note' => 'Bulk checkin items',
'target_id' => $log->target_id,
'target_type' => $log->target_type,
'created_at' => $log->updated_at,
])
->first();
if ($similarLog) {
return $similarLog->created_by;
}
return null;
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class FixUpAssignedTypeWithoutAssignedTo extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:assigned-type-fixup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes up assets that have an assigned_type but no assigned_to';
/**
* Execute the console command.
*/
public function handle()
{
DB::table('assets')->whereNotNull('assigned_type')->whereNull('assigned_to')->update(['assigned_type' => null]);
$this->info("Assets with an assigned_type but no assigned_to are fixed");
}
}

View File

@@ -2,9 +2,11 @@
namespace App\Console\Commands;
use App\Helpers\Helper;
use Illuminate\Console\Command;
use App\Models\User;
use Laravel\Passport\TokenRepository;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Support\Facades\DB;
class GeneratePersonalAccessToken extends Command
@@ -41,8 +43,9 @@ class GeneratePersonalAccessToken extends Command
*
* @return void
*/
public function __construct(TokenRepository $tokenRepository)
public function __construct(TokenRepository $tokenRepository, ValidationFactory $validation)
{
$this->validation = $validation;
$this->tokenRepository = $tokenRepository;
parent::__construct();
}
@@ -73,7 +76,7 @@ class GeneratePersonalAccessToken extends Command
} else {
$this->warn('Your API Token has been created. Be sure to copy this token now, as it WILL NOT be accessible again.');
$this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.');
if ($token = DB::table('oauth_access_tokens')->where('user_id', '=', $user->id)->where('name','=',$accessTokenName)->orderBy('created_at', 'desc')->first()) {
$this->info('API Token ID: '.$token->id);

View File

@@ -55,8 +55,6 @@ class LdapSync extends Command
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
// Map the LDAP attributes to the Snipe-IT user fields.
$ldap_map = [
"username" => Setting::getSettings()->ldap_username_field,
"last_name" => Setting::getSettings()->ldap_lname_field,
@@ -65,17 +63,11 @@ class LdapSync extends Command
"emp_num" => Setting::getSettings()->ldap_emp_num,
"email" => Setting::getSettings()->ldap_email,
"phone" => Setting::getSettings()->ldap_phone_field,
"mobile" => Setting::getSettings()->ldap_mobile,
"jobtitle" => Setting::getSettings()->ldap_jobtitle,
"address" => Setting::getSettings()->ldap_address,
"city" => Setting::getSettings()->ldap_city,
"state" => Setting::getSettings()->ldap_state,
"zip" => Setting::getSettings()->ldap_zip,
"country" => Setting::getSettings()->ldap_country,
"location" => Setting::getSettings()->ldap_location,
"dept" => Setting::getSettings()->ldap_dept,
"manager" => Setting::getSettings()->ldap_manager,
"display_name" => Setting::getSettings()->ldap_display_name,
];
$ldap_default_group = Setting::getSettings()->ldap_default_group;
@@ -133,10 +125,6 @@ class LdapSync extends Command
*/
$attributes = array_values(array_filter($ldap_map));
if (Setting::getSettings()->is_ad === 1 && is_null($ldap_map['active_flag'])) {
$attributes[] = 'useraccountcontrol';
}
$results = Ldap::findLdapUsers($search_base, -1, $filter, $attributes);
} catch (\Exception $e) {
@@ -190,7 +178,7 @@ class LdapSync extends Command
// Inject location information fields
for ($i = 0; $i < $results['count']; $i++) {
$results[$i]['ldap_location_override'] = false;
$results[$i]['location_id'] = null;
$results[$i]['location_id'] = 0;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
@@ -242,11 +230,9 @@ class LdapSync extends Command
}
// Assign the mapped LDAP attributes for each user to the Snipe-IT user fields
for ($i = 0; $i < $results['count']; $i++) {
$item = [];
$item['username'] = $results[$i][$ldap_map["username"]][0] ?? '';
$item['display_name'] = $results[$i][$ldap_map["display_name"]][0] ?? '';
$item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? '';
$item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? '';
$item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? '';
@@ -254,13 +240,8 @@ class LdapSync extends Command
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? '';
$item['location_id'] = $results[$i]['location_id'] ?? '';
$item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? '';
$item['mobile'] = $results[$i][$ldap_map["mobile"]][0] ?? '';
$item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? '';
$item['address'] = $results[$i][$ldap_map["address"]][0] ?? '';
$item['city'] = $results[$i][$ldap_map["city"]][0] ?? '';
$item['state'] = $results[$i][$ldap_map["state"]][0] ?? '';
$item['country'] = $results[$i][$ldap_map["country"]][0] ?? '';
$item['zip'] = $results[$i][$ldap_map["zip"]][0] ?? '';
$item['department'] = $results[$i][$ldap_map["dept"]][0] ?? '';
$item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? '';
$item['location'] = $results[$i][$ldap_map["location"]][0] ?? '';
@@ -293,9 +274,6 @@ class LdapSync extends Command
if($ldap_map["username"] != null){
$user->username = $item['username'];
}
if($ldap_map["display_name"] != null){
$user->display_name = $item['display_name'];
}
if($ldap_map["last_name"] != null){
$user->last_name = $item['lastname'];
}
@@ -311,9 +289,6 @@ class LdapSync extends Command
if($ldap_map["phone"] != null){
$user->phone = $item['telephone'];
}
if($ldap_map["mobile"] != null){
$user->mobile = $item['mobile'];
}
if($ldap_map["jobtitle"] != null){
$user->jobtitle = $item['jobtitle'];
}
@@ -382,15 +357,9 @@ class LdapSync extends Command
// (Specifically, we don't handle a value of '0.0' correctly)
$raw_value = @$results[$i][$ldap_map["active_flag"]][0];
$filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
$boolean_cast = (bool) $raw_value;
if (Setting::getSettings()->ldap_invert_active_flag === 1) {
// Because ldap_active_flag is set, if filter_var is true or boolean_cast is true, then user is suspended
$user->activated = !($filter_var ?? $boolean_cast);
}else{
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
}
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
} elseif (array_key_exists('useraccountcontrol', $results[$i])) {
// ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists,
@@ -455,12 +424,8 @@ class LdapSync extends Command
$item['note'] = $item['createorupdate'];
$item['status'] = 'success';
if ($item['createorupdate'] === 'created' && $ldap_default_group) {
// Check if the relationship already exists
if (!$user->groups()->where('group_id', $ldap_default_group)->exists()) {
$user->groups()->attach($ldap_default_group);
}
$user->groups()->attach($ldap_default_group);
}
//updates assets location based on user's location
if ($user->wasChanged('location_id')) {
foreach ($user->assets as $asset) {

View File

@@ -6,7 +6,6 @@ use Illuminate\Console\Command;
use App\Models\Setting;
use Exception;
use Illuminate\Support\Facades\Crypt;
use App\Models\Ldap;
/**
* Check if a given ip is in a network
@@ -161,15 +160,7 @@ class LdapTroubleshooter extends Command
$output[] = "-x";
$output[] = "-b ".escapeshellarg($settings->ldap_basedn);
$output[] = "-D ".escapeshellarg($settings->ldap_uname);
try {
$w = Crypt::Decrypt($settings->ldap_pword);
} catch (\Exception $e) {
$this->warn("Could not decrypt password. This usually means an LDAP password was not set or the APP_KEY was changed since the LDAP pasword was last saved. Aborting.");
exit(0);
}
$output[] = "-w ". escapeshellarg($w);
$output[] = "-w ".escapeshellarg(Crypt::Decrypt($settings->ldap_pword));
$output[] = escapeshellarg(parenthesized_filter($settings->ldap_filter));
if($settings->ldap_tls) {
$this->line("# adding STARTTLS option");
@@ -180,23 +171,6 @@ class LdapTroubleshooter extends Command
$this->line(implode(" \\\n",$output));
exit(0);
}
//PHP Version check for warning
$php_version = phpversion();
list($major, $minor, $patch) = explode('.', $php_version);
if (
$major < 8 ||
($major == 8 && $minor < 3) ||
($major == 8 && $minor == 3 && $patch < 21) ||
($major == 8 && $minor == 4 && $patch < 7)
) {
$this->warn("PHP Version: $php_version WARNING - Versions before 8.3.21 or 8.4.7 will return INCONSISTENT results!");
if (!$this->confirm("Are you sure you wish to continue?")) {
$this->warn("ABORTING");
exit(-1);
}
}
if(!$this->option('force')) {
$confirmation = $this->confirm('WARNING: This command will make several attempts to connect to your LDAP server. Are you sure this is ok?');
if(!$confirmation) {
@@ -205,7 +179,7 @@ class LdapTroubleshooter extends Command
}
}
//$this->line(print_r($settings,true));
$this->line("STAGE 1: Checking settings");
$this->info("STAGE 1: Checking settings");
if(!$settings->ldap_enabled) {
$this->error("WARNING: Snipe-IT's LDAP setting is not turned on. (That may be OK if you're still trying to figure out settings)");
}
@@ -236,40 +210,32 @@ class LdapTroubleshooter extends Command
$this->info("Determined LDAP hostname to be: ".$parsed['host']);
}
$this->info("Performing DNS lookup of: ".$parsed['host']);
$ips = dns_get_record($parsed['host']);
$raw_ips = [];
if (inet_pton($parsed['host']) !== false) {
$this->line($parsed['host'] . " already looks like an address; skipping DNS lookup");
$raw_ips[] = $parsed['host'];
} else {
$this->line("Performing DNS lookup of: " . $parsed['host']);
$ips = dns_get_record($parsed['host']);
//$this->info("Host IP is: ".print_r($ips,true));
//$this->info("Host IP is: ".print_r($ips,true));
if (!$ips || count($ips) == 0) {
$this->error("ERROR: DNS lookup of host: " . $parsed['host'] . " has failed. ABORTING.");
exit(-1);
}
$this->debugout("IP's? " . print_r($ips, true));
foreach ($ips as $ip) {
if (!isset($ip['ip'])) {
continue;
}
$raw_ips[] = $ip['ip'];
}
if(!$ips || count($ips) == 0) {
$this->error("ERROR: DNS lookup of host: ".$parsed['host']." has failed. ABORTING.");
exit(-1);
}
foreach ($raw_ips as $ip) {
if ($ip == "127.0.0.1") {
$this->debugout("IP's? ".print_r($ips,true));
foreach($ips as $ip) {
if(!isset($ip['ip'])) {
continue;
}
$raw_ips[]=$ip['ip'];
if($ip['ip'] == "127.0.0.1") {
$this->error("WARNING: Using the localhost IP as the LDAP server. This is usually wrong");
}
if (ip_in_range($ip, '10.0.0.0/8') || ip_in_range($ip, '192.168.0.0/16') || ip_in_range($ip, '172.16.0.0/12')) {
if(ip_in_range($ip['ip'],'10.0.0.0/8') || ip_in_range($ip['ip'],'192.168.0.0/16') || ip_in_range($ip['ip'], '172.16.0.0/12')) {
$this->error("WARNING: Using an RFC1918 Private address for LDAP server. This may be correct, but it can be a problem if your Snipe-IT instance is not hosted on your private network");
}
}
$this->line("STAGE 2: Checking basic network connectivity");
$ports = [636, 389];
$this->info("STAGE 2: Checking basic network connectivity");
$ports = [389,636];
if(@$parsed['port'] && !in_array($parsed['port'],$ports)) {
$ports[] = $parsed['port'];
}
@@ -280,7 +246,7 @@ class LdapTroubleshooter extends Command
$errstr = '';
$timeout = 30.0;
$result = '';
$this->line("Attempting to connect to port: " . $port . " - may take up to $timeout seconds");
$this->info("Attempting to connect to port: ".$port." - may take up to $timeout seconds");
try {
$result = fsockopen($parsed['host'], $port, $errno, $errstr, 30.0);
} catch(Exception $e) {
@@ -299,9 +265,9 @@ class LdapTroubleshooter extends Command
exit(-1);
}
$this->line("STAGE 3: Determine encryption algorithm, if any");
$this->info("STAGE 3: Determine encryption algorithm, if any");
$ldap_urls = []; // [url, cert-check?, start_tls?]
$ldap_urls = [];
$pretty_ldap_urls = [];
foreach($open_ports as $port) {
$this->line("Trying TLS first for port $port");
@@ -309,46 +275,35 @@ class LdapTroubleshooter extends Command
if($this->test_anonymous_bind($ldap_url)) {
$this->info("Anonymous bind succesful to $ldap_url!");
$ldap_urls[] = [ $ldap_url, true, false ];
$pretty_ldap_urls[] = [$ldap_url, "enabled", "n/a (no)"];
$pretty_ldap_urls[] = [ $ldap_url, "YES", "no" ];
continue; // TODO - lots of copypasta in these if(test_anonymous_bind()) routines...
} else {
$this->error("WARNING: Failed to bind to $ldap_url - trying without certificate checks.");
}
if($this->test_anonymous_bind($ldap_url, false)) {
$this->info("Anonymous bind successful to $ldap_url with certificate-checks disabled");
$ldap_urls[] = [$ldap_url, false, false];
$pretty_ldap_urls[] = [$ldap_url, "DISABLED", "n/a (no)"];
$this->info("Anonymous bind succesful to $ldap_url with certifcate-checks disabled");
$ldap_urls[] = [ $ldap_url, false, false ];
$pretty_ldap_urls[] = [ $ldap_url, "no", "no" ];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with certificate checks disabled. Trying unencrypted with STARTTLS");
}
// now switching to ldap:// URL's from ldaps://
$ldap_url = "ldap://".$parsed['host'].":$port";
if($this->test_anonymous_bind($ldap_url, true, true)) {
$this->info("Plain connection to $ldap_url with STARTTLS succesful!");
$ldap_urls[] = [ $ldap_url, true, true ];
$pretty_ldap_urls[] = [$ldap_url, "enabled", "STARTTLS ENABLED"];
$pretty_ldap_urls[] = [ $ldap_url, "YES", "YES" ];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with STARTTLS enabled. Trying without certificate checks.");
}
if ($this->test_anonymous_bind($ldap_url, false, true)) {
$this->info("Plain connection to $ldap_url with STARTTLS and cert checks *disabled* successful!");
$ldap_urls[] = [$ldap_url, false, true];
$pretty_ldap_urls[] = [$ldap_url, "DISABLED", "STARTTLS ENABLED"];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with STARTTLS enabled, and cert checks disabled. Trying without STARTTLS");
$this->error("WARNING: Failed to bind to $ldap_url with STARTTLS enabled. Trying without STARTTLS");
}
if($this->test_anonymous_bind($ldap_url)) {
$this->info("Plain connection to $ldap_url succesful!");
$ldap_urls[] = [ $ldap_url, true, false ];
$pretty_ldap_urls[] = [$ldap_url, "n/a", "starttls disabled"];
$pretty_ldap_urls[] = [ $ldap_url, "YES", "no" ];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url. Giving up on port $port");
@@ -358,29 +313,23 @@ class LdapTroubleshooter extends Command
$this->debugout(print_r($ldap_urls,true));
if(count($ldap_urls) > 0 ) {
$this->debugout("Found working LDAP URL's: ");
$this->info("Found working LDAP URL's: ");
foreach($ldap_urls as $ldap_url) { // TODO maybe do this as a $this->table() instead?
$this->debugout("LDAP URL: " . $ldap_url[0]);
$this->debugout($ldap_url[0] . ($ldap_url[1] ? " certificate checks enabled" : " certificate checks disabled") . ($ldap_url[2] ? " STARTTLS Enabled " : " STARTTLS Disabled"));
$this->info("LDAP URL: ".$ldap_url[0]);
$this->info($ldap_url[0]. ($ldap_url[1] ? " certificate checks enabled" : " certificate checks disabled"). ($ldap_url[2] ? " STARTTLS Enabled ": " STARTTLS Disabled"));
}
$this->table(["URL", "Cert Checks?", "STARTTLS?"], $pretty_ldap_urls);
$this->table(["URL", "Cert Checks Enabled?", "STARTTLS Enabled?"],$pretty_ldap_urls);
} else {
$this->error("ERROR - no valid LDAP URL's available - ABORTING");
exit(1);
}
$this->line("STAGE 4: Test Administrative Bind for LDAP Sync");
$this->info("STAGE 4: Test Administrative Bind for LDAP Sync");
foreach($ldap_urls AS $ldap_url) {
try {
$w = Crypt::Decrypt($settings->ldap_pword);
} catch (\Exception $e) {
$this->warn("Could not decrypt password. This usually means an LDAP password was not set or the APP_KEY was changed since the LDAP pasword was last saved. Aborting.");
exit(0);
}
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $settings->ldap_uname, $w);
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $settings->ldap_uname, Crypt::decrypt($settings->ldap_pword));
}
$this->line("STAGE 5: Test BaseDN");
$this->info("STAGE 5: Test BaseDN");
//grab all LDAP_ constants and fill up a reversed array mapping from weird LDAP dotted-strings to (Constant Name)
$all_defined_constants = get_defined_constants();
$ldap_constants = [];
@@ -392,23 +341,16 @@ class LdapTroubleshooter extends Command
$this->debugout("LDAP constants are: ".print_r($ldap_constants,true));
foreach($ldap_urls AS $ldap_url) {
try {
$w = Crypt::Decrypt($settings->ldap_pword);
} catch (\Exception $e) {
$this->warn("Could not decrypt password. This usually means an LDAP password was not set or the APP_KEY was changed since the LDAP pasword was last saved. Aborting.");
exit(0);
}
if($this->test_informational_bind($ldap_url[0],$ldap_url[1],$ldap_url[2],$settings->ldap_uname,$w,$settings)) {
if($this->test_informational_bind($ldap_url[0],$ldap_url[1],$ldap_url[2],$settings->ldap_uname,Crypt::decrypt($settings->ldap_pword),$settings)) {
$this->info("Success getting informational bind!");
} else {
$this->error("Unable to get information from bind.");
}
}
$this->line("STAGE 6: Test LDAP Login to Snipe-IT");
$this->info("STAGE 6: Test LDAP Login to Snipe-IT");
foreach($ldap_urls AS $ldap_url) {
$this->line("Starting auth to " . $ldap_url[0]);
$this->info("Starting auth to ".$ldap_url[0]);
while(true) {
$with_tls = $ldap_url[1] ? "with": "without";
$with_startssl = $ldap_url[2] ? "using": "not using";
@@ -417,12 +359,7 @@ class LdapTroubleshooter extends Command
}
$username = $this->ask("Username");
$password = $this->secret("Password");
$results = $this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $username, $password); // FIXME - should do some other stuff here, maybe with the concatenating or something? maybe? and/or should put up some results?
if ($results) {
$this->info("Success authenticating with " . $username);
} else {
$this->error("Unable to authenticate with " . $username);
}
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $username, $password); // FIXME - should do some other stuff here, maybe with the concatenating or something? maybe? and/or should put up some results?
}
}
@@ -431,17 +368,14 @@ class LdapTroubleshooter extends Command
public function connect_to_ldap($ldap_url, $check_cert, $start_tls)
{
if ($check_cert) {
$this->line("we *ARE* checking certs");
Ldap::ignoreCertificates(false);
} else {
$this->line("we are IGNORING certs");
Ldap::ignoreCertificates(true);
}
$lconn = ldap_connect($ldap_url);
ldap_set_option($lconn, LDAP_OPT_PROTOCOL_VERSION, 3); // should we 'test' different protocol versions here? Does anyone even use anything other than LDAPv3?
// no - it's formally deprecated: https://tools.ietf.org/html/rfc3494
if(!$check_cert) {
putenv('LDAPTLS_REQCERT=never'); // This is horrible; is this *really* the only way to do it?
} else {
putenv('LDAPTLS_REQCERT'); // have to very explicitly and manually *UN* set the env var here to ensure it works
}
if($this->settings->ldap_client_tls_cert && $this->settings->ldap_client_tls_key) {
// client-side TLS certificate support for LDAP (Google Secure LDAP)
putenv('LDAPTLS_CERT=storage/ldap_client_tls.cert');
@@ -470,10 +404,9 @@ class LdapTroubleshooter extends Command
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert , $start_tls) {
try {
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$this->line("Attempting to bind now, this can take a while if we mess it up");
$this->info("gonna try to bind now, this can take a while if we mess it up");
$bind_results = ldap_bind($lconn);
$this->line("Bind results are: " . $bind_results . " which translate into boolean: " . (bool)$bind_results);
ldap_close($lconn);
$this->info("Bind results are: ".$bind_results." which translate into boolean: ".(bool)$bind_results);
return (bool)$bind_results;
} catch (Exception $e) {
$this->error("WARNING: Exception caught during bind - ".$e->getMessage());
@@ -488,7 +421,6 @@ class LdapTroubleshooter extends Command
try {
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$bind_results = ldap_bind($lconn, $username, $password);
ldap_close($lconn);
if(!$bind_results) {
$this->error("WARNING: Failed to bind to $ldap_url as $username");
return false;
@@ -514,62 +446,22 @@ class LdapTroubleshooter extends Command
return false;
}
$this->info("SUCCESS - Able to bind to $ldap_url as $username");
$cleaned_results = [];
try {
// This _may_ only work for Active Directory?
$result = ldap_read($conn, '', '(objectClass=*)'/* , ['supportedControl']*/);
$results = ldap_get_entries($conn, $result);
$cleaned_results = $this->ldap_results_cleaner($results);
//$this->line(print_r($cleaned_results,true));
$default_naming_contexts = $cleaned_results[0]['namingcontexts'];
$this->info("Default Naming Contexts:");
$this->info(implode(", ", $default_naming_contexts));
//okay, great - now how do we display those results? I have no idea.
} catch (\Exception $e) {
$this->error("Unable to get base naming contexts - here's what we *did* get:");
$this->line(print_r($cleaned_results, true));
}
$result = ldap_read($conn, '', '(objectClass=*)'/* , ['supportedControl']*/);
$results = ldap_get_entries($conn, $result);
$cleaned_results = $this->ldap_results_cleaner($results);
$this->line(print_r($cleaned_results,true));
//okay, great - now how do we display those results? I have no idea.
// I don't see why this throws an Exception for Google LDAP, but I guess we ought to try and catch it?
$this->debugout("I guess we're trying to do the ldap search here, but sometimes it takes too long?");
$this->comment("I guess we're trying to do the ldap search here, but sometimes it takes too long?");
$this->debugout("Base DN is: ".$settings->ldap_basedn." and filter is: ".parenthesized_filter($settings->ldap_filter));
$search_results = ldap_search($conn, $settings->ldap_basedn, parenthesized_filter($settings->ldap_filter));
$entries = ldap_get_entries($conn, $search_results);
$this->info("Printing first 10 results: ");
$pretty_data = array_slice($this->ldap_results_cleaner($entries), 0, 10);
//print_r($data);
$headers = [];
foreach ($pretty_data as $row) {
//populate headers
foreach ($row as $key => $value) {
//skip objectsid and objectguid because it junks up output
if ($key == "objectsid" || $key == "objectguid") {
continue;
}
if (!in_array($key, $headers)) {
$headers[] = $key;
}
}
for($i=0;$i<10;$i++) {
$this->info($search_results[$i]);
}
$table = [];
//repeat again to populate table
foreach ($pretty_data as $row) {
$newrow = [];
foreach ($headers as $header) {
if (is_array(@$row[$header])) {
$newrow[] = "[" . implode(", ", $row[$header]) . "]";
} else {
$newrow[] = @$row[$header];
}
}
$table[] = $newrow;
}
$this->table($headers, $table);
} catch (\Exception $e) {
$this->error("WARNING: Exception caught during Authed bind to $username - ".$e->getMessage());
return false;
} finally {
ldap_close($conn);
}
});
}
@@ -585,7 +477,7 @@ class LdapTroubleshooter extends Command
{
if(!(function_exists('pcntl_sigtimedwait') && function_exists('posix_getpid') && function_exists('pcntl_fork') && function_exists('posix_kill') && function_exists('pcntl_wifsignaled'))) {
// POSIX functions needed for forking aren't present, just run the function inline (ignoring timeout)
$this->line('WARNING: Unable to execute POSIX fork() commands, timeout may not be respected');
$this->info('WARNING: Unable to execute POSIX fork() commands, timeout may not be respected');
return $function();
} else {
$parent_pid = posix_getpid();
@@ -622,6 +514,4 @@ class LdapTroubleshooter extends Command
}
}
}

View File

@@ -96,7 +96,7 @@ class MoveUploadsToNewDisk extends Command
$private_uploads['assets'] = glob('storage/private_uploads/assets'."/*.*");
$private_uploads['signatures'] = glob('storage/private_uploads/signatures'."/*.*");
$private_uploads['audits'] = glob('storage/private_uploads/audits'."/*.*");
$private_uploads['assetmodels'] = glob('storage/private_uploads/models'."/*.*");
$private_uploads['assetmodels'] = glob('storage/private_uploads/assetmodels'."/*.*");
$private_uploads['imports'] = glob('storage/private_uploads/imports'."/*.*");
$private_uploads['licenses'] = glob('storage/private_uploads/licenses'."/*.*");
$private_uploads['users'] = glob('storage/private_uploads/users'."/*.*");

View File

@@ -4,7 +4,7 @@ namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\CustomField;
use Illuminate\Support\Facades\Schema;
use Schema;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\Command;
@@ -51,7 +51,8 @@ class PaveIt extends Command
}
// List all the tables in the database so we don't have to worry about missing some as the app grows
$tables = Schema::getTables();
$tables = DB::connection()->getDoctrineSchemaManager()->listTableNames();
$except_tables = [
'oauth_access_tokens',
'oauth_clients',
@@ -59,9 +60,6 @@ class PaveIt extends Command
'migrations',
'settings',
'users',
'telescope_entries',
'telescope_entries_tags',
'telescope_monitoring',
];
// We only need to find out what these are so we can nuke these columns on the assets table.
@@ -69,15 +67,14 @@ class PaveIt extends Command
foreach ($custom_fields as $custom_field) {
$this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
if (Schema::hasColumn('assets', $custom_field->db_column)) {
Schema::table('assets', function ($table) use ($custom_field) {
if (\Schema::hasColumn('assets', $custom_field->db_column)) {
\Schema::table('assets', function ($table) use ($custom_field) {
$table->dropColumn($custom_field->db_column);
});
}
}
foreach ($tables as $table_obj) {
$table = $table_obj['name'];
foreach ($tables as $table) {
if (in_array($table, $except_tables)) {
$this->info($table. ' is SKIPPED.');
} else {
@@ -87,8 +84,8 @@ class PaveIt extends Command
}
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
DB::statement('delete from oauth_clients WHERE id > 2');
DB::statement('delete from oauth_access_tokens WHERE user_id > 2');
\DB::statement('delete from oauth_clients WHERE id > 2');
\DB::statement('delete from oauth_access_tokens WHERE id > 2');
}
}

View File

@@ -62,19 +62,19 @@ class Purge extends Command
$assetcount = $assets->count();
$this->info($assets->count().' assets purged.');
$asset_assoc = 0;
$maintenances = 0;
$asset_maintenances = 0;
foreach ($assets as $asset) {
$this->info('- Asset "'.$asset->display_name.'" deleted.');
$this->info('- Asset "'.$asset->present()->name().'" deleted.');
$asset_assoc += $asset->assetlog()->count();
$asset->assetlog()->forceDelete();
$maintenances += $asset->maintenances()->count();
$asset->maintenances()->forceDelete();
$asset_maintenances += $asset->assetmaintenances()->count();
$asset->assetmaintenances()->forceDelete();
$asset->forceDelete();
}
$this->info($asset_assoc.' corresponding log records purged.');
$this->info($maintenances.' corresponding maintenance records purged.');
$this->info($asset_maintenances.' corresponding maintenance records purged.');
$locations = Location::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($locations->count().' locations purged.');

View File

@@ -0,0 +1,157 @@
<?php
namespace App\Console\Commands;
use App\LegacyEncrypter\McryptEncrypter;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\Setting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class RecryptFromMcrypt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:legacy-recrypt
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command allows upgrading users to de-encrypt their deprecated mcrypt encrypted fields and re-encrypt them using the current OpenSSL encryption.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Check and see if they have a legacy app key listed in their .env
// If not, we can try to use the current APP_KEY if looks like it's old
$legacy_key = env('LEGACY_APP_KEY');
$key_parts = explode(':', $legacy_key);
$legacy_cipher = env('LEGACY_CIPHER', 'rijndael-256');
$errors = [];
if (! $legacy_key) {
$this->error('ERROR: You do not have a LEGACY_APP_KEY set in your .env file. Please locate your old APP_KEY and ADD a line to your .env file like: LEGACY_APP_KEY=YOUR_OLD_APP_KEY');
return false;
}
// Do some basic legacy app key length checks
if (strlen($legacy_key) == 32) {
$legacy_length_check = true;
} elseif (array_key_exists('1', $key_parts) && (strlen($key_parts[1]) == 44)) {
$legacy_key = base64_decode($key_parts[1], true);
$legacy_length_check = true;
} else {
$legacy_length_check = false;
}
// Check that the app key is 32 characters
if ($legacy_length_check === true) {
$this->comment('INFO: Your LEGACY_APP_KEY looks correct. Okay to continue.');
} else {
$this->error('ERROR: Your LEGACY_APP_KEY is not the correct length (32 characters or base64 followed by 44 characters for later versions). Please locate your old APP_KEY and use that as your LEGACY_APP_KEY in your .env file to continue.');
return false;
}
$this->error('================================!!!! WARNING !!!!================================');
$this->error('================================!!!! WARNING !!!!================================');
$this->comment("This tool will attempt to decrypt your old Snipe-IT (mcrypt, now deprecated) encrypted data and re-encrypt it using OpenSSL. \n\nYou should only continue if you have backed up any and all old APP_KEYs and have backed up your data.");
$force = ($this->option('force')) ? true : false;
if ($force || ($this->confirm('Are you SURE you wish to continue?'))) {
$backup_file = 'backups/env-backups/'.'app_key-'.date('Y-m-d-gis');
try {
Storage::disk('local')->put($backup_file, 'APP_KEY: '.config('app.key'));
Storage::disk('local')->append($backup_file, 'LEGACY_APP_KEY: '.$legacy_key);
} catch (\Exception $e) {
$this->info('WARNING: Could not backup app keys');
}
if ($legacy_cipher) {
$mcrypter = new McryptEncrypter($legacy_key, $legacy_cipher);
} else {
$mcrypter = new McryptEncrypter($legacy_key);
}
$settings = Setting::getSettings();
if ($settings->ldap_pword == '') {
$this->comment('INFO: No LDAP password found. Skipping... ');
} else {
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
$settings->ldap_pword = Crypt::encrypt($decrypted_ldap_pword);
$settings->save();
}
/** @var CustomField[] $custom_fields */
$custom_fields = CustomField::where('field_encrypted', '=', 1)->get();
$this->comment('INFO: Retrieving encrypted custom fields...');
$query = Asset::withTrashed();
foreach ($custom_fields as $custom_field) {
$this->comment('FIELD TO RECRYPT: '.$custom_field->name.' ('.$custom_field->db_column.')');
$query->orWhereNotNull($custom_field->db_column);
}
// Get all assets with a value in any of the fields that were encrypted
/** @var Asset[] $assets */
$assets = $query->get();
$bar = $this->output->createProgressBar(count($assets));
foreach ($assets as $asset) {
foreach ($custom_fields as $encrypted_field) {
$columnName = $encrypted_field->db_column;
// Make sure the value isn't null
if ($asset->{$columnName} != '') {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
$asset->{$columnName} = Crypt::encrypt($decrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
}
}
}
$asset->save();
$bar->advance();
}
$bar->finish();
if (count($errors) > 0) {
$this->comment("\n\n");
$this->error("The decrypter encountered some errors: \n");
foreach ($errors as $error) {
$this->error($error);
}
}
}
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Actionlog;
use Illuminate\Console\Command;
class RemoveInvalidUploadDeleteActionLogItems extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:remove-invalid-upload-delete-action-log-items';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Permanently remove invalid "upload deleted" action log items that have a null filename. This command can potentially result in deleted files being "resurrected" in the UI.';
/**
* Execute the console command.
*/
public function handle()
{
$invalidLogs = Actionlog::query()
->where('action_type', 'upload deleted')
->whereNull('filename')
->withTrashed()
->get();
$this->info("{$invalidLogs->count()} invalid log items found.");
if ($invalidLogs->count() === 0) {
return 0;
}
$this->table(['ID', 'Action Type', 'Item Type', 'Item ID', 'Created At', 'Deleted At'], $invalidLogs->map(fn($log) => [
$log->id,
$log->action_type,
$log->item_type,
$log->item_id,
$log->created_at,
$log->deleted_at,
])->toArray());
if ($this->confirm("Do you wish to remove {$invalidLogs->count()} log items?")) {
$invalidLogs->each(fn($log) => $log->forceDelete());
}
return 0;
}
}

View File

@@ -5,7 +5,6 @@ namespace App\Console\Commands;
use Illuminate\Console\Command;
use ZipArchive;
use Illuminate\Support\Facades\Log;
use enshrined\svgSanitize\Sanitizer;
class SQLStreamer {
private $input;
@@ -243,17 +242,13 @@ class RestoreFromBackup extends Command
$private_dirs = [
'storage/private_uploads/accessories',
'storage/private_uploads/assetmodels' => 'storage/private_uploads/models', //this was changed from assetmodels => models Aug 10 2025
'storage/private_uploads/asset_maintenances' => 'storage/private_uploads/maintenances', //this was changed from asset_maintenances => maintenances Aug 10 2025
'storage/private_uploads/maintenances', //but let 'maintenances' take precedence
'storage/private_uploads/models', //and let 'models' take precedence
'storage/private_uploads/assetmodels',
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
'storage/private_uploads/audits',
'storage/private_uploads/components',
'storage/private_uploads/consumables',
'storage/private_uploads/eula-pdfs',
'storage/private_uploads/imports',
'storage/private_uploads/locations',
'storage/private_uploads/licenses',
'storage/private_uploads/signatures',
'storage/private_uploads/users',
@@ -264,10 +259,9 @@ class RestoreFromBackup extends Command
];
$public_dirs = [
'public/uploads/accessories',
// 'public/uploads/assetmodels' => 'public/uploads/models', //according to git, this was _never_ a thing... (see below)
'public/uploads/maintenances',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/avatars',
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
'public/uploads/categories',
'public/uploads/companies',
'public/uploads/components',
@@ -275,7 +269,7 @@ class RestoreFromBackup extends Command
'public/uploads/departments',
'public/uploads/locations',
'public/uploads/manufacturers',
'public/uploads/models', // ...it's been this way for 9 years (as of late 2025)
'public/uploads/models',
'public/uploads/suppliers',
];
@@ -288,27 +282,14 @@ class RestoreFromBackup extends Command
'public/uploads/favicon-uploaded.*',
];
$all_files = $private_dirs + $public_dirs;
$sqlfiles = [];
$sqlfile_indices = [];
$interesting_files = [];
$boring_files = [];
$unsafe_files = [];
$good_extensions = config('filesystems.allowed_upload_extensions_array');
$private_extensions = array_merge($good_extensions, ["csv", "key"]); //add csv, and 'key'
$public_extensions = array_diff($good_extensions, ["xml"]); //remove xml
$sanitizer = new Sanitizer();
/**
* TODO: I _hate_ the "continue 3" thing we keep doing here
* I think a better approach might be to have the "each file" stuff be in a method on this class, and the
* boring_files and interesting_files be properties on it that we fill out. Then, in that method, we could
* just do a 'return' once the file is actually handled (yay or nay). We could also start to break out some of
* the _other_ things that we do into their own methods too? But I don't care about that as much.
*/
for ($i = 0; $i < $za->numFiles; $i++) {
$stat_results = $za->statIndex($i);
// echo "index: $i\n";
@@ -323,7 +304,7 @@ class RestoreFromBackup extends Command
// skip macOS resource fork files (?!?!?!)
if (strpos($raw_path, '__MACOSX') !== false && strpos($raw_path, '._') !== false) {
//print "SKIPPING macOS Resource fork file: $raw_path\n";
// $boring_files[] = $raw_path; //stop adding this to the boring files list; it's just confusing
$boring_files[] = $raw_path;
continue;
}
if (@pathinfo($raw_path, PATHINFO_EXTENSION) == 'sql') {
@@ -332,70 +313,41 @@ class RestoreFromBackup extends Command
$sqlfile_indices[] = $i;
continue;
}
if ($raw_path[-1] == '/') {
//last character is '/' - this is a directory, and we don't need it, and we don't need to warn about it
continue;
}
if (in_array(basename($raw_path), [".gitkeep", ".gitignore", ".DS_Store"])) {
//skip these boring files silently without reporting on them; they're stupid
continue;
}
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
foreach (['public' => $public_dirs, 'private' => $private_dirs] as $purpose => $dirs) {
$allowed_extensions = match ($purpose) {
'public' => $public_extensions,
'private' => $private_extensions,
};
foreach ($dirs as $dir => $destdir) {
if (is_int($dir)) {
$dir = $destdir;
}
$last_pos = strrpos($raw_path, $dir . '/');
if ($last_pos !== false) {
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
//the CSV bit, below, is because we store CSV files as "blahcsv" - without an extension
if (!in_array($extension, $allowed_extensions) && !($dir == "storage/private_uploads/imports" && substr($raw_path, -3) == "csv" && $extension == "")) {
$unsafe_files[] = $raw_path;
Log::debug($raw_path . ' from directory ' . $dir . ' is being skipped');
} else {
if ($dir != $destdir) {
Log::debug("Getting ready to save file $raw_path to new directory $destdir");
}
$interesting_files[$raw_path] = ['dest' => $destdir, 'index' => $i];
}
continue 3;
foreach (array_merge($private_dirs, $public_dirs) as $dir) {
$last_pos = strrpos($raw_path, $dir . '/');
if ($last_pos !== false) {
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
$interesting_files[$raw_path] = ['dest' => $dir, 'index' => $i];
continue 2;
if ($last_pos + strlen($dir) + 1 == strlen($raw_path)) {
// we don't care about that; we just want files with the appropriate prefix
//print("FOUND THE EXACT DIRECTORY: $dir AT: $raw_path!!!\n");
}
}
}
foreach (['public' => $public_files, 'private' => $private_files] as $purpose => $files) {
$allowed_extensions = match ($purpose) {
'public' => $public_extensions,
'private' => $private_extensions,
};
foreach ($files as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {
$file = substr($file, 0, -1); //trim last character (which should be the wildcard)
$good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico',];
foreach (array_merge($private_files, $public_files) as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {
$file = substr($file, 0, -1); //trim last character (which should be the wildcard)
}
$last_pos = strrpos($raw_path, $file); // no trailing slash!
if ($last_pos !== false) {
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
if (!in_array($extension, $good_extensions)) {
$this->warn('Potentially unsafe file ' . $raw_path . ' is being skipped');
$boring_files[] = $raw_path;
continue 2;
}
$last_pos = strrpos($raw_path, $file); // no trailing slash!
if ($last_pos !== false) {
if (!in_array($extension, $allowed_extensions)) {
// gathering potentially unsafe files here to return at exit
$unsafe_files[] = $raw_path;
Log::debug('Potentially unsafe file ' . $raw_path . ' is being skipped');
$boring_files[] = $raw_path;
continue 3;
}
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//no wildcards found in $file, process 'normally'
if ($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
$interesting_files[$raw_path] = ['dest' => dirname($file), 'index' => $i];
continue 3;
}
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//no wildcards found in $file, process 'normally'
if ($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
$interesting_files[$raw_path] = ['dest' => dirname($file), 'index' => $i];
continue 2;
}
}
}
@@ -420,7 +372,6 @@ class RestoreFromBackup extends Command
if ($this->option('sanitize-guess-prefix')) {
$prefix = SQLStreamer::guess_prefix($sql_contents);
$this->line($prefix);
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitize your SQL.");
}
@@ -532,25 +483,18 @@ class RestoreFromBackup extends Command
}
foreach ($interesting_files as $pretty_file_name => $file_details) {
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
$migrated_file_name = $file_details['dest'] . '/' . basename($pretty_file_name);
if (strcasecmp(substr($pretty_file_name, -4), ".svg") === 0) {
$svg_contents = $za->getFromIndex($file_details['index']);
$cleaned_svg = $sanitizer->sanitize($svg_contents);
file_put_contents($migrated_file_name, $cleaned_svg);
} else {
$fp = $za->getStream($ugly_file_name);
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
if (!is_dir($file_details['dest'])) {
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
}
$migrated_file = fopen($migrated_file_name, 'w');
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
fwrite($migrated_file, $buffer);
}
fclose($migrated_file);
fclose($fp);
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
$fp = $za->getStream($ugly_file_name);
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
if (!is_dir($file_details['dest'])) {
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
}
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
fwrite($migrated_file, $buffer);
}
fclose($migrated_file);
fclose($fp);
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
if ($bar) {
$bar->advance();
}
@@ -561,11 +505,6 @@ class RestoreFromBackup extends Command
} else {
$this->info(count($interesting_files).' files were succesfully transferred');
}
if (count($unsafe_files) > 0) {
foreach ($unsafe_files as $unsafe_file) {
$this->warn('Potentially unsafe file '.$unsafe_file.' was skipped');
}
}
foreach ($boring_files as $boring_file) {
$this->warn($boring_file.' was skipped.');
}

View File

@@ -64,46 +64,28 @@ class SendAcceptanceReminder extends Command
->groupBy(function($item) {
return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : '';
});
$no_email_list= [];
foreach($unacceptedAssetGroups as $unacceptedAssetGroup) {
// The [0] is weird, but it allows for the item_count to work and grabs the appropriate info for each user.
// Collapsing and flattening the collection doesn't work above.
$acceptance = $unacceptedAssetGroup[0]['acceptance'];
$locale = $acceptance->assignedTo?->locale;
$email = $acceptance->assignedTo?->email;
if(!$email){
$no_email_list[] = [
'id' => $acceptance->assignedTo?->id,
'name' => $acceptance->assignedTo?->display_name,
];
} else {
$count++;
$this->info($acceptance->assignedTo?->present()->fullName().' has no email address.');
}
$item_count = $unacceptedAssetGroup->count();
if ($locale && $email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))->locale($locale));
} elseif ($email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count)));
}
$count++;
}
$this->info($count.' users notified.');
$headers = ['ID', 'Name'];
$rows = [];
foreach ($no_email_list as $user) {
$rows[] = [$user['id'], $user['name']];
}
if (!empty($rows)) {
$this->info("The following users do not have an email address:");
$this->table($headers, $rows);
}
return 0;
}

View File

@@ -2,14 +2,13 @@
namespace App\Console\Commands;
use App\Helpers\Helper;
use App\Mail\ExpiringAssetsMail;
use App\Mail\ExpiringLicenseMail;
use App\Models\Asset;
use App\Models\License;
use App\Models\Recipients\AlertRecipient;
use App\Models\Setting;
use App\Notifications\ExpiringAssetsNotification;
use App\Notifications\ExpiringLicenseNotification;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendExpirationAlerts extends Command
{
@@ -43,83 +42,28 @@ class SendExpirationAlerts extends Command
public function handle()
{
$settings = Setting::getSettings();
$alert_interval = $settings->alert_interval;
$threshold = $settings->alert_interval;
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))
->map(fn($item) => trim($item)) // Trim each email
->filter(fn($item) => !empty($item))
->all();
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new AlertRecipient($item);
});
// Expiring Assets
$assets = Asset::getExpiringWarrantyOrEol($alert_interval);
$assets = Asset::getExpiringWarrantee($threshold);
if ($assets->count() > 0) {
Mail::to($recipients)->send(new ExpiringAssetsMail($assets, $alert_interval));
$this->table(
[
trans('general.id'),
trans('admin/hardware/form.tag'),
trans('admin/hardware/form.model'),
trans('general.model_no'),
trans('general.purchase_date'),
trans('admin/hardware/form.eol_rate'),
trans('admin/hardware/form.eol_date'),
trans('admin/hardware/form.warranty_expires'),
],
$assets->map(fn($item) =>
[
trans('general.id') => $item->id,
trans('admin/hardware/form.tag') => $item->asset_tag,
trans('admin/hardware/form.model') => $item->model->name,
trans('general.model_no') => $item->model->model_number,
trans('general.purchase_date') => $item->purchase_date_formatted,
trans('admin/hardware/form.eol_rate') => $item->model->eol,
trans('admin/hardware/form.eol_date') => $item->eol_date ? $item->eol_formatted_date .' ('.$item->eol_diff_for_humans.')' : '',
trans('admin/hardware/form.warranty_expires') => $item->warranty_expires ? $item->warranty_expires_formatted_date .' ('.$item->warranty_expires_diff_for_humans.')' : '',
])
);
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count' => $assets->count(), 'threshold' => $threshold]));
\Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
}
// Expiring licenses
$licenses = License::query()->ExpiringLicenses($alert_interval)
->with('manufacturer','category')
->orderBy('expiration_date', 'ASC')
->orderBy('termination_date', 'ASC')
->get();
$licenses = License::getExpiringLicenses($threshold);
if ($licenses->count() > 0) {
Mail::to($recipients)->send(new ExpiringLicenseMail($licenses, $alert_interval));
$this->table(
[
trans('general.id'),
trans('general.name'),
trans('general.purchase_date'),
trans('admin/licenses/form.expiration'),
trans('mail.expires'),
trans('admin/licenses/form.termination_date'),
trans('mail.terminates')],
$licenses->map(fn($item) => [
trans('general.id') => $item->id,
trans('general.name') => $item->name,
trans('general.purchase_date') => $item->purchase_date_formatted,
trans('admin/licenses/form.expiration') => $item->expires_formatted_date,
trans('mail.expires') => $item->expires_diff_for_humans,
trans('admin/licenses/form.termination_date') => $item->terminates_formatted_date,
trans('mail.terminates') => $item->terminates_diff_for_humans
])
);
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold]));
\Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
}
// Send a message even if the count is 0
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count' => $assets->count(), 'threshold' => $alert_interval]));
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $alert_interval]));
} else {
if ($settings->alert_email == '') {
$this->error('Could not send email. No alert email configured in settings');

View File

@@ -2,12 +2,13 @@
namespace App\Console\Commands;
use App\Mail\SendUpcomingAuditMail;
use App\Models\Asset;
use App\Models\Recipients\AlertRecipient;
use App\Models\Setting;
use App\Notifications\SendUpcomingAuditNotification;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendUpcomingAuditReport extends Command
{
@@ -47,42 +48,19 @@ class SendUpcomingAuditReport extends Command
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval);
$assets = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'asc')->get();
$this->info($assets->count() . ' assets must be audited in on or before ' . $interval_date . ' is deadline');
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'desc')->get();
$this->info($assets->count().' assets must be audited in on or before '.$interval_date.' is deadline');
if ((count($assets) !== 0) && ($assets->count() > 0) && ($settings->alert_email != '')) {
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))
->map(fn($item) => trim($item))
->filter(fn($item) => !empty($item))
->all();
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item) {
return new AlertRecipient($item);
});
$this->info('Sending Admin SendUpcomingAuditNotification to: '.$settings->alert_email);
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
$this->info('Sending Admin SendUpcomingAuditNotification to: ' . $settings->alert_email);
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets, $settings->audit_warning_days));
$this->table(
[
trans('general.id'),
trans('general.name'),
trans('general.last_audit'),
trans('general.next_audit_date'),
trans('mail.Days'),
trans('mail.supplier'),
trans('mail.assigned_to'),
],
$assets->map(fn($item) => [
trans('general.id') => $item->id,
trans('general.name') => $item->display_name,
trans('general.last_audit') => $item->last_audit_formatted_date,
trans('general.next_audit_date') => $item->next_audit_formatted_date,
trans('mail.Days') => round($item->next_audit_diff_in_days),
trans('mail.supplier') => $item->supplier ? $item->supplier->name : '',
trans('mail.assigned_to') => $item->assignedTo ? $item->assignedTo->display_name : '',
])
);
}
}

View File

@@ -1,51 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Helpers\Helper;
use Illuminate\Console\Command;
class TestLocationsFMCS extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:test-locations-fmcs {--location_id=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Test for company ID inconsistencies if FullMultipleCompanySupport with scoped locations will be used.';
/**
* Execute the console command.
*/
public function handle()
{
$this->info('This script checks for company ID inconsistencies if Full Multiple Company Support with scoped locations will be used.');
$this->info('This could take a few moments if have a very large dataset.');
$this->newLine();
// if parameter location_id is set, only test this location
$location_id = null;
if ($this->option('location_id')) {
$location_id = $this->option('location_id');
}
$mismatched = Helper::test_locations_fmcs(true, $location_id);
$this->warn(trans_choice('admin/settings/message.location_scoping.mismatch', count($mismatched)));
$this->newLine();
$this->info('Edit your locations to associate them with the correct company.');
$header = ['Type', 'ID', 'Name', 'Checkout Type', 'Company ID', 'Item Company', 'Item Location', 'Location Company', 'Location Company ID'];
sort($mismatched);
$this->table($header, $mismatched);
}
}

View File

@@ -5,7 +5,6 @@ namespace App\Console;
use App\Console\Commands\ImportLocations;
use App\Console\Commands\ReEncodeCustomFieldNames;
use App\Console\Commands\RestoreDeletedUsers;
use App\Models\Setting;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -19,14 +18,12 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
if(Setting::getSettings()?->alerts_enabled === 1) {
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkin')->daily();
$schedule->command('snipeit:upcoming-audits')->daily();
}
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkin')->daily();
$schedule->command('snipeit:backup')->weekly();
$schedule->command('backup:clean')->daily();
$schedule->command('snipeit:upcoming-audits')->daily();
$schedule->command('auth:clear-resets')->everyFifteenMinutes();
$schedule->command('saml:clear_expired_nonces')->weekly();
}

View File

@@ -28,7 +28,7 @@ class CheckoutableCheckedIn
$this->checkedOutTo = $checkedOutTo;
$this->checkedInBy = $checkedInBy;
$this->note = $note;
$this->action_date = $action_date ?? date('Y-m-d H:i:s');
$this->action_date = $action_date ?? date('Y-m-d');
$this->originalValues = $originalValues;
}
}

28
app/Events/NoteAdded.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NoteAdded
{
use Dispatchable, SerializesModels;
public $itemNoteAddedOn;
public $note;
public $noteAddedBy;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($itemNoteAddedOn, User $noteAddedBy, $note)
{
$this->itemNoteAddedOn = $itemNoteAddedOn;
$this->note = $note;
$this->noteAddedBy = $noteAddedBy;
}
}

View File

@@ -1,9 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class AssetNotRequestable extends Exception
{
}

View File

@@ -11,7 +11,6 @@ use Illuminate\Support\Facades\Log;
use Throwable;
use JsonException;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
class Handler extends ExceptionHandler
{
@@ -94,7 +93,7 @@ class Handler extends ExceptionHandler
// Handle API requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date)
if ($e instanceof InvalidFormatException) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('validation.date', ['attribute' => 'date'])), config('app.validation_status_code'));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('validation.date', ['attribute' => 'date'])), 200);
}
// Handle API requests that fail because the model doesn't exist
@@ -108,66 +107,21 @@ class Handler extends ExceptionHandler
$statusCode = $e->getStatusCode();
// API throttle requests are handled in the RouteServiceProvider configureRateLimiting() method, so we don't need to handle them here
switch ($e->getStatusCode()) {
case '404':
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
case '429':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Too many requests'), 429);
case '405':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
default:
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
}
}
// This handles API validation exceptions that happen at the Form Request level, so they
// never even get to the controller where we normally nicely format JSON responses
if ($e instanceof ValidationException) {
$response = $this->invalidJson($request, $e);
return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), config('app.validation_status_code'));
}
}
// This is traaaaash but it handles models that are not found while using route model binding :(
// The only alternative is to set that at *each* route, which is crazypants
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
$ids = method_exists($e, 'getIds') ? $e->getIds() : [];
if (in_array('bulkedit', $ids, true)) {
$error_array = session()->get('bulk_asset_errors');
return redirect()
->route('hardware.index')
->withErrors($error_array, 'bulk_asset_errors')
->withInput();
}
// This gets the MVC model name from the exception and formats in a way that's less fugly
$model_name = trim(strtolower(implode(" ", preg_split('/(?=[A-Z])/', last(explode('\\', $e->getModel()))))));
$route = str_plural(strtolower(last(explode('\\', $e->getModel())))).'.index';
// Sigh.
if ($route == 'assets.index') {
$route = 'hardware.index';
} elseif ($route == 'reporttemplates.index') {
$route = 'reports/custom';
} elseif ($route == 'assetmodels.index') {
$route = 'models.index';
} elseif ($route == 'predefinedkits.index') {
$route = 'kits.index';
} elseif ($route == 'assetmaintenances.index') {
$route = 'maintenances.index';
} elseif ($route === 'licenseseats.index') {
$route = 'licenses.index';
} elseif (($route === 'customfieldsets.index') || ($route === 'customfields.index')) {
$route = 'fields.index';
}
return redirect()
->route($route)
->withError(trans('general.generic_model_not_found', ['model' => $model_name]));
}
if ($this->isHttpException($e) && (isset($statusCode)) && ($statusCode == '404' )) {
@@ -198,7 +152,7 @@ class Handler extends ExceptionHandler
protected function invalidJson($request, ValidationException $exception)
{
return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors()), config('app.validation_status_code'));
return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors()), 200);
}
@@ -220,9 +174,8 @@ class Handler extends ExceptionHandler
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
}
}

View File

@@ -1,10 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class UserDoestExistException extends Exception
{
}

View File

@@ -12,8 +12,6 @@ use App\Models\Depreciation;
use App\Models\Setting;
use App\Models\Statuslabel;
use App\Models\License;
use App\Models\Location;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
use Carbon\Carbon;
@@ -95,7 +93,7 @@ class Helper
$Parsedown->setSafeMode(true);
if ($str) {
return $Parsedown->text(strip_tags($str));
return $Parsedown->text($str);
}
}
@@ -105,7 +103,7 @@ class Helper
$Parsedown->setSafeMode(true);
if ($str) {
return $Parsedown->line(strip_tags($str));
return $Parsedown->line($str);
}
}
@@ -435,34 +433,6 @@ class Helper
return $colors[$index];
}
/**
* Check if a string has any RTL characters
* @param $value
* @return bool
*/
public static function hasRtl($string) {
$rtlChar = '/[\x{0590}-\x{083F}]|[\x{08A0}-\x{08FF}]|[\x{FB1D}-\x{FDFF}]|[\x{FE70}-\x{FEFF}]/u';
return preg_match($rtlChar, $string) != 0;
}
// is chinese, japanese or korean language
public static function isCjk($string) {
return Helper::isChinese($string) || Helper::isJapanese($string) || Helper::isKorean($string);
}
public static function isChinese($string) {
return preg_match("/\p{Han}+/u", $string);
}
public static function isJapanese($string) {
return preg_match('/[\x{4E00}-\x{9FBF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}]/u', $string);
}
public static function isKorean($string) {
return preg_match('/[\x{3130}-\x{318F}\x{AC00}-\x{D7AF}]/u', $string);
}
/**
* Increases or decreases the brightness of a color by a percentage of the current brightness.
*
@@ -751,8 +721,8 @@ class Helper
// The check and message that the user is still using the deprecated version
$deprecations = [
'ms_teams_deprecated' => array(
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows') && (Setting::getSettings()->webhook_selected === 'microsoft'),
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Dec 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows'),
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Jan 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
];
// if item of concern is being used and its being used with the deprecated values return the notification array.
@@ -898,49 +868,7 @@ class Helper
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'image/jpeg') || ($filetype == 'image/jpg') || ($filetype == 'image/png') || ($filetype == 'image/bmp') || ($filetype == 'image/gif') || ($filetype == 'image/avif') || ($filetype == 'image/webp')) {
return $filetype;
}
return false;
}
/**
* Check if the file is a video, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [B. Wetherington] [<bwetherington@grokability.com>]
* @since [v8.1.18]
*/
public static function checkUploadIsVideo($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'video/mp4') || ($filetype == 'video/quicktime') || ($filetype == 'video/mpeg') || ($filetype == 'video/ogg') || ($filetype == 'video/webm') || ($filetype == 'video/x-msvide')) {
return $filetype;
}
return false;
}
/**
* Check if the file is audio, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*/
public static function checkUploadIsAudio($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'audio/mpeg') || ($filetype == 'audio/ogg')) {
if (($filetype == 'image/jpeg') || ($filetype == 'image/jpg') || ($filetype == 'image/png') || ($filetype == 'image/bmp') || ($filetype == 'image/gif') || ($filetype == 'image/avif')) {
return $filetype;
}
@@ -967,12 +895,6 @@ class Helper
public static function selectedPermissionsArray($permissions, $selected_arr = [])
{
$permissions_arr = [];
if (is_array($permissions)) {
$permissions = json_encode($permissions);
}
// Set default to empty JSON if the value is null
$permissions = json_decode($permissions ?? '{}', JSON_OBJECT_AS_ARRAY);
foreach ($permissions as $permission) {
for ($x = 0; $x < count($permission); $x++) {
@@ -983,13 +905,13 @@ class Helper
if (is_array($selected_arr)) {
if (array_key_exists($permission_name, $selected_arr)) {
$permissions_arr[$permission_name] = (int) $selected_arr[$permission_name];
$permissions_arr[$permission_name] = $selected_arr[$permission_name];
} else {
$permissions_arr[$permission_name] = 0;
$permissions_arr[$permission_name] = '0';
}
} else {
$permissions_arr[$permission_name] = 0;
$permissions_arr[$permission_name] = '0';
}
}
}
@@ -1225,42 +1147,22 @@ class Helper
'webp' => 'far fa-image',
'avif' => 'far fa-image',
'svg' => 'fas fa-vector-square',
// word
'doc' => 'far fa-file-word',
'docx' => 'far fa-file-word',
// Excel
'xls' => 'far fa-file-excel',
'xlsx' => 'far fa-file-excel',
'ods' => 'far fa-file-excel',
// Presentation
'ppt' => 'far fa-file-powerpoint',
'odp' => 'far fa-file-powerpoint',
// archive
'zip' => 'fas fa-file-archive',
'rar' => 'fas fa-file-archive',
//Text
'odt' => 'far fa-file-alt',
'txt' => 'far fa-file-alt',
'rtf' => 'far fa-file-alt',
'xml' => 'fas fa-code',
// Misc
'pdf' => 'far fa-file-pdf',
'lic' => 'far fa-save',
// video
'mov' => 'fa-solid fa-video',
'mp4' => 'fa-solid fa-video',
// audio
'ogg' => 'fa-solid fa-file-audio',
'mp3' => 'fa-solid fa-file-audio',
'wav' => 'fa-solid fa-file-audio',
];
if ($extension && array_key_exists($extension, $allowedExtensionMap)) {
@@ -1404,24 +1306,25 @@ class Helper
switch ($item) {
case 'asset':
return 'fas fa-barcode';
break;
case 'accessory':
return 'fas fa-keyboard';
break;
case 'component':
return 'fas fa-hdd';
break;
case 'consumable':
return 'fas fa-tint';
break;
case 'license':
return 'far fa-save';
break;
case 'location':
return 'fas fa-map-marker-alt';
break;
case 'user':
return 'fas fa-user';
case 'supplier':
return 'fa-solid fa-store';
case 'manufacturer':
return 'fa-solid fa-building';
case 'category':
return 'fa-solid fa-table-columns';
break;
}
}
@@ -1571,168 +1474,59 @@ class Helper
}
static public function getRedirectOption($request, $id, $table, $item_id = null) : RedirectResponse
static public function getRedirectOption($request, $id, $table, $item_id = null)
{
$redirect_option = Session::get('redirect_option') ?? $request->redirect_option;
$checkout_to_type = Session::get('checkout_to_type') ?? null;
$checkedInFrom = Session::get('checkedInFrom');
$other_redirect = Session::get('other_redirect');
$backUrl = Session::pull('back_url', route('home'));
// return to previous page
if ($redirect_option === 'back') {
return redirect()->to($backUrl);
}
$redirect_option = Session::get('redirect_option');
$checkout_to_type = Session::get('checkout_to_type');
// return to index
if ($redirect_option == 'index') {
return match ($table) {
'Assets' => redirect()->route('hardware.index'),
'Users' => redirect()->route('users.index'),
'Licenses' => redirect()->route('licenses.index'),
'Accessories' => redirect()->route('accessories.index'),
'Components' => redirect()->route('components.index'),
'Consumables' => redirect()->route('consumables.index'),
};
switch ($table) {
case "Assets":
return route('hardware.index');
case "Users":
return route('users.index');
case "Licenses":
return route('licenses.index');
case "Accessories":
return route('accessories.index');
case "Components":
return route('components.index');
case "Consumables":
return route('consumables.index');
}
}
// return to thing being assigned
if ($redirect_option == 'item') {
return match ($table) {
'Assets' => redirect()->route('hardware.show', $id ?? $item_id),
'Users' => redirect()->route('users.show', $id ?? $item_id),
'Licenses' => redirect()->route('licenses.show', $id ?? $item_id),
'Accessories' => redirect()->route('accessories.show', $id ?? $item_id),
'Components' => redirect()->route('components.show', $id ?? $item_id),
'Consumables' => redirect()->route('consumables.show', $id ?? $item_id),
};
switch ($table) {
case "Assets":
return route('hardware.show', $id ?? $item_id);
case "Users":
return route('users.show', $id ?? $item_id);
case "Licenses":
return route('licenses.show', $id ?? $item_id);
case "Accessories":
return route('accessories.show', $id ?? $item_id);
case "Components":
return route('components.show', $id ?? $item_id);
case "Consumables":
return route('consumables.show', $id ?? $item_id);
}
}
// return to assignment target
if ($redirect_option == 'target') {
return match ($checkout_to_type) {
'user' => redirect()->route('users.show', $request->assigned_user ?? $checkedInFrom),
'location' => redirect()->route('locations.show', $request->assigned_location ?? $checkedInFrom),
'asset' => redirect()->route('hardware.show', $request->assigned_asset ?? $checkedInFrom),
};
switch ($checkout_to_type) {
case 'user':
return route('users.show', ['user' => $request->assigned_user]);
case 'location':
return route('locations.show', ['location' => $request->assigned_location]);
case 'asset':
return route('hardware.show', ['hardware' => $request->assigned_asset]);
}
}
// return to somewhere else
if ($redirect_option == 'other_redirect') {
return match ($other_redirect) {
'audit' => redirect()->route('assets.audit.due'),
'model' => redirect()->route('models.show', $request->model_id),
};
}
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
}
/**
* Check for inconsistencies before activating scoped locations with FullMultipleCompanySupport
* If there are locations with different companies than related objects unforseen problems could arise
*
* @author T. Regnery <tobias.regnery@gmail.com>
* @since 7.0
*
* @param $artisan when false, bail out on first inconsistent entry
* @param $location_id when set, only test this specific location
* @param $new_company_id in case of updating a location, this is the newly requested company_id
* @return string []
*/
static public function test_locations_fmcs($artisan, $location_id = null, $new_company_id = null) {
$mismatched = [];
if ($location_id) {
$location = Location::find($location_id);
if ($location) {
$locations = collect([])->push(Location::find($location_id));
}
} else {
$locations = Location::all();
}
// Bail out early if there are no locations
if ($locations->count() == 0) {
return [];
}
foreach($locations as $location) {
// in case of an update of a single location, use the newly requested company_id
if ($new_company_id) {
$location_company = $new_company_id;
} else {
$location_company = $location->company_id;
}
// Depending on the relationship, we must use different operations to retrieve the objects
$keywords_relation = [
'many' => [
'accessories',
'assets',
'assignedAccessories',
'assignedAssets',
'components',
'consumables',
'rtd_assets',
'users',
],
'one' => [
'manager',
'parent',
]];
// In case of a single location, the children must be checked as well, because we don't walk every location
if ($location_id) {
$keywords_relation['many'][] = 'children';
}
foreach ($keywords_relation as $relation => $keywords) {
foreach($keywords as $keyword) {
if ($relation == 'many') {
$items = $location->{$keyword}->all();
} else {
$items = collect([])->push($location->$keyword);
}
$count = 0;
foreach ($items as $item) {
if ($item && $item->company_id != $location_company) {
$mismatched[] = [
class_basename(get_class($item)),
$item->id,
$item->name ?? $item->asset_tag ?? $item->serial ?? $item->username,
$item->assigned_type ? str_replace('App\\Models\\', '', $item->assigned_type) : null,
$item->company_id ?? null,
$item->company->name ?? null,
// $item->defaultLoc->id ?? null,
// $item->defaultLoc->name ?? null,
// $item->defaultLoc->company->id ?? null,
// $item->defaultLoc->company->name ?? null,
$item->location->name ?? null,
$item->location->company->name ?? null,
$location_company ?? null,
];
$count++;
// Bail early if this is not being run via artisan
if ((!$artisan) && ($count > 0)) {
return $mismatched;
}
}
}
}
}
}
return $mismatched;
}
}

View File

@@ -16,7 +16,6 @@ class IconHelper
case 'clone':
return 'far fa-clone';
case 'delete':
case 'upload deleted':
return 'fas fa-trash';
case 'create':
return 'fa-solid fa-plus';
@@ -44,8 +43,6 @@ class IconHelper
return 'fa-regular fa-envelope';
case 'phone':
return 'fa-solid fa-phone';
case 'mobile':
return 'fas fa-mobile-screen-button';
case 'long-arrow-right':
return 'fas fa-long-arrow-alt-right';
case 'download':
@@ -62,8 +59,6 @@ class IconHelper
return 'fas fa-cog';
case 'angle-left':
return 'fas fa-angle-left';
case 'angle-right':
return 'fas fa-angle-right';
case 'warning':
return 'fas fa-exclamation-triangle';
case 'kits':
@@ -154,7 +149,6 @@ class IconHelper
case 'location':
return 'fas fa-map-marker-alt';
case 'superadmin':
case 'admin':
return 'fas fa-crown';
case 'print':
return 'fa-solid fa-print';
@@ -190,8 +184,6 @@ class IconHelper
return 'fa-regular fa-id-card';
case 'department' :
return 'fa-solid fa-building-user';
case 'home' :
return 'fa-solid fa-house';
case 'note':
case 'notes':
return 'fas fa-sticky-note';

View File

@@ -16,84 +16,38 @@ class StorageHelper
$disk = config('filesystems.default');
}
switch (config("filesystems.disks.$disk.driver")) {
case 'local':
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
case 'local':
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
case 's3':
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
case 's3':
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
default:
return Storage::disk($disk)->download($filename);
default:
return Storage::disk($disk)->download($filename);
}
}
public static function getMediaType($file_with_path) {
// Get the file extension and determine the media type
if (Storage::exists($file_with_path)) {
$fileinfo = pathinfo($file_with_path);
$extension = strtolower($fileinfo['extension']);
switch ($extension) {
case 'avif':
case 'jpg':
case 'png':
case 'gif':
case 'svg':
case 'webp':
return 'image';
case 'pdf':
return 'pdf';
case 'mp3':
case 'wav':
case 'ogg':
return 'audio';
case 'mp4':
case 'webm':
case 'mov':
return 'video';
case 'doc':
case 'docx':
return 'document';
case 'txt':
return 'text';
case 'xls':
case 'xlsx':
case 'ods':
return 'spreadsheet';
default:
return $extension; // Default for unknown types
}
}
return null;
}
/**
* This determines the file types that should be allowed inline and checks their fileinfo extension
* to determine that they are safe to display inline.
*
* @author <A. Gianotto> [<snipe@snipe.net]>
* @since v7.0.14
* @param $file_with_path
* @since v7.0.14
* @param $file_with_path
* @return bool
*/
public static function allowSafeInline($file_with_path)
{
public static function allowSafeInline($file_with_path) {
$allowed_inline = [
'avif',
'gif',
'gif',
'jpg',
'mov',
'mp3',
'mp4',
'ogg',
'pdf',
'png',
'svg',
'wav',
'webm',
'jpg',
'gif',
'svg',
'avif',
'webp',
'png',
];
@@ -105,24 +59,10 @@ class StorageHelper
}
public static function getFiletype($file_with_path)
{
// The file exists and is allowed to be displayed inline
if (Storage::exists($file_with_path)) {
return pathinfo($file_with_path, PATHINFO_EXTENSION);
}
return null;
}
/**
* Decide whether to show the file inline or download it.
*/
public static function showOrDownloadFile($file, $filename)
{
public static function showOrDownloadFile($file, $filename) {
$headers = [];

View File

@@ -77,30 +77,13 @@ class AccessoriesController extends Controller
$accessory->supplier_id = request('supplier_id');
$accessory->notes = request('notes');
if ($request->has('use_cloned_image')) {
$cloned_model_img = Accessory::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
$new_image = 'accessories/'.$new_image_name;
Storage::disk('public')->copy('accessories/'.$cloned_model_img->image, $new_image);
$accessory->image = $new_image_name;
}
} else {
$accessory = $request->handleImages($accessory);
}
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
}
$accessory = $request->handleImages($accessory);
session()->put(['redirect_option' => $request->get('redirect_option')]);
// Was the accessory created?
if ($accessory->save()) {
// Redirect to the new accessory page
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
@@ -112,10 +95,16 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
*/
public function edit(Accessory $accessory) : View | RedirectResponse
public function edit($accessoryId = null) : View | RedirectResponse
{
$this->authorize('update', Accessory::class);
return view('accessories.edit')->with('item', $accessory)->with('category_type', 'accessory');
if ($item = Accessory::find($accessoryId)) {
$this->authorize($item);
return view('accessories.edit', compact('item'))->with('category_type', 'accessory');
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
/**
@@ -125,18 +114,24 @@ class AccessoriesController extends Controller
* @param int $accessoryId
* @since [v6.0]
*/
public function getClone(Accessory $accessory) : View | RedirectResponse
public function getClone($accessoryId = null) : View | RedirectResponse
{
$this->authorize('create', Accessory::class);
$cloned = clone $accessory;
$accessory_to_clone = $accessory;
$cloned->id = null;
$cloned->deleted_at = '';
// Check if the asset exists
if (is_null($accessory_to_clone = Accessory::find($accessoryId))) {
// Redirect to the asset management page
return redirect()->route('accessories.index')
->with('error', trans('admin/accessories/message.does_not_exist', ['id' => $accessoryId]));
}
$accessory = clone $accessory_to_clone;
$accessory->id = null;
$accessory->location_id = null;
return view('accessories/edit')
->with('cloned_model', $accessory_to_clone)
->with('item', $cloned);
->with('item', $accessory);
}
@@ -147,9 +142,9 @@ class AccessoriesController extends Controller
* @param ImageUploadRequest $request
* @param int $accessoryId
*/
public function update(ImageUploadRequest $request, Accessory $accessory) : RedirectResponse
public function update(ImageUploadRequest $request, $accessoryId = null) : RedirectResponse
{
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessory->id)) {
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId)) {
$this->authorize($accessory);
@@ -185,8 +180,7 @@ class AccessoriesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($accessory->save()) {
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.update.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.update.success'));
}
} else {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
@@ -203,15 +197,15 @@ class AccessoriesController extends Controller
*/
public function destroy($accessoryId) : RedirectResponse
{
if (is_null($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId))) {
if (is_null($accessory = Accessory::find($accessoryId))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
$this->authorize($accessory);
if ($accessory->checkouts_count > 0) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/general.delete_disabled'));
if ($accessory->hasUsers() > 0) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()]));
}
if ($accessory->image) {
@@ -237,13 +231,14 @@ class AccessoriesController extends Controller
* @see AccessoriesController::getDataView() method that generates the JSON response
* @since [v1.0]
*/
public function show(Accessory $accessory) : View | RedirectResponse
public function show($accessoryID = null) : View | RedirectResponse
{
$accessory->loadCount('checkouts as checkouts_count');
$accessory->load(['adminuser' => fn($query) => $query->withTrashed()]);
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
$this->authorize('view', $accessory);
return view('accessories.view', compact('accessory'));
if (isset($accessory->id)) {
return view('accessories.view', compact('accessory'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', ['id' => $accessoryID]));
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace App\Http\Controllers\Accessories;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Accessory;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
class AccessoriesFilesController extends Controller
{
/**
* Validates and stores files associated with a accessory.
*
* @param UploadFileRequest $request
* @param int $accessoryId
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @todo Switch to using the AssetFileRequest form request validator.
*/
public function store(UploadFileRequest $request, $accessoryId = null) : RedirectResponse
{
if (config('app.lock_passwords')) {
return redirect()->route('accessories.show', ['accessory'=>$accessoryId])->with('error', trans('general.feature_disabled'));
}
$accessory = Accessory::find($accessoryId);
if (isset($accessory->id)) {
$this->authorize('accessories.files', $accessory);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/accessories')) {
Storage::makeDirectory('private_uploads/accessories', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/accessories/', 'accessory-'.$accessory->id, $file);
//Log the upload to the log
$accessory->logUpload($file_name, e($request->input('notes')));
}
return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->route('accessories.show', $accessory->id)->with('error', trans('general.no_files_uploaded'));
}
// Prepare the error message
return redirect()->route('accessories.index')
->with('error', trans('general.file_does_not_exist'));
}
/**
* Deletes the selected accessory file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $accessoryId
* @param int $fileId
*/
public function destroy($accessoryId = null, $fileId = null) : RedirectResponse
{
$accessory = Accessory::find($accessoryId);
// the asset is valid
if (isset($accessory->id)) {
$this->authorize('update', $accessory);
$log = Actionlog::find($fileId);
// Remove the file if one exists
if (Storage::exists('accessories/'.$log->filename)) {
try {
Storage::delete('accessories/'.$log->filename);
} catch (\Exception $e) {
Log::debug($e);
}
}
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
// Redirect to the licence management page
return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist'));
}
/**
* Allows the selected file to be viewed.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.4]
* @param int $accessoryId
* @param int $fileId
*/
public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
{
// the accessory is valid
if ($accessory = Accessory::find($accessoryId)) {
$this->authorize('view', $accessory);
$this->authorize('accessories.files', $accessory);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
$file = 'private_uploads/accessories/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found'));
}
}
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.log_record_not_found'));
}
return redirect()->route('accessories.index')->with('error', trans('general.file_not_found'));
}
}

View File

@@ -7,6 +7,7 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use \Illuminate\Contracts\View\View;
@@ -29,17 +30,9 @@ class AccessoryCheckinController extends Controller
}
$accessory = Accessory::find($accessory_user->accessory_id);
//based on what the accessory is checked out to the target redirect option will be displayed accordingly.
$target_option = match ($accessory_user->assigned_type) {
'App\Models\Asset' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.asset')]),
'App\Models\Location' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.location')]),
default => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.user')]),
};
$this->authorize('checkin', $accessory);
return view('accessories/checkin', compact('accessory', 'target_option'))->with('backto', $backto);
return view('accessories/checkin', compact('accessory'))->with('backto', $backto);
}
/**
@@ -58,14 +51,8 @@ class AccessoryCheckinController extends Controller
$accessory = Accessory::find($accessory_checkout->accessory_id);
session()->put('checkedInFrom', $accessory_checkout->assigned_to);
session()->put('checkout_to_type', match ($accessory_checkout->assigned_type) {
'App\Models\User' => 'user',
'App\Models\Location' => 'location',
'App\Models\Asset' => 'asset',
});
$this->authorize('checkin', $accessory);
$checkin_hours = date('H:i:s');
$checkin_at = date('Y-m-d H:i:s');
if ($request->filled('checkin_at')) {
@@ -78,8 +65,7 @@ class AccessoryCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.checkin.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.checkin.success'));
}
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));

View File

@@ -71,7 +71,6 @@ class AccessoryCheckoutController extends Controller
$this->authorize('checkout', $accessory);
$target = $this->determineCheckoutTarget();
session()->put(['checkout_to_type' => $target]);
$accessory->checkout_qty = $request->input('checkout_qty', 1);
@@ -98,7 +97,7 @@ class AccessoryCheckoutController extends Controller
// Redirect to the new accessory page
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))
->with('success', trans('admin/accessories/message.checkout.success'));
}
}

View File

@@ -7,24 +7,30 @@ use App\Events\CheckoutDeclined;
use App\Events\ItemAccepted;
use App\Events\ItemDeclined;
use App\Http\Controllers\Controller;
use App\Mail\CheckoutAcceptanceResponseMail;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Company;
use App\Models\Contracts\Acceptable;
use App\Models\Setting;
use App\Models\User;
use App\Models\AssetModel;
use App\Models\Accessory;
use App\Models\License;
use App\Models\Component;
use App\Models\Consumable;
use App\Notifications\AcceptanceAssetAcceptedNotification;
use App\Notifications\AcceptanceAssetAcceptedToUserNotification;
use App\Notifications\AcceptanceAssetDeclinedNotification;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Http\Controllers\SettingsController;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Log;
use App\Helpers\Helper;
class AcceptanceController extends Controller
{
@@ -75,10 +81,6 @@ class AcceptanceController extends Controller
public function store(Request $request, $id) : RedirectResponse
{
$acceptance = CheckoutAcceptance::find($id);
$assigned_user = User::find($acceptance->assigned_to_id);
$settings = Setting::getSettings();
$sig_filename='';
if (is_null($acceptance)) {
return redirect()->route('account.accept')->with('error', trans('admin/hardware/message.does_not_exist'));
@@ -101,99 +103,142 @@ class AcceptanceController extends Controller
}
/**
* Check for the signature directory
* Get the signature and save it
*/
if (! Storage::exists('private_uploads/signatures')) {
Storage::makeDirectory('private_uploads/signatures', 775);
}
/**
* Check for the eula-pdfs directory
*/
if (! Storage::exists('private_uploads/eula-pdfs')) {
Storage::makeDirectory('private_uploads/eula-pdfs', 775);
}
$item = $acceptance->checkoutable_type::find($acceptance->checkoutable_id);
// If signatures are required, make sure we have one
if (Setting::getSettings()->require_accept_signature == '1') {
// The item was accepted, check for a signature
if ($request->filled('signature_output')) {
$sig_filename = 'siglog-' . Str::uuid() . '-' . date('Y-m-d-his') . '.png';
$data_uri = $request->input('signature_output');
$encoded_image = explode(',', $data_uri);
$decoded_image = base64_decode($encoded_image[1]);
Storage::put('private_uploads/signatures/' . $sig_filename, (string)$decoded_image);
// No image data is present, kick them back.
// This mostly only applies to users on super-duper crapola browsers *cough* IE *cough*
} else {
return redirect()->back()->with('error', trans('general.shitty_browser'));
}
}
// Convert PDF logo to base64 for TCPDF
// This is needed for TCPDF to properly embed the image if it's a png and the cache isn't writable
$encoded_logo = null;
if ($settings->acceptance_pdf_logo) {
$encoded_logo = base64_encode(file_get_contents(public_path() . '/uploads/' . $settings->acceptance_pdf_logo));
}
// Get the data array ready for the notifications and PDF generation
$data = [
'item_tag' => $item->asset_tag,
'item_name' => $item->name, // this handles licenses seats, which don't have a 'name' field
'item_model' => $item->model?->name,
'item_serial' => $item->serial,
'item_status' => $item->assetstatus?->name,
'eula' => $item->getEula(),
'note' => $request->input('note'),
'check_out_date' => Helper::getFormattedDateObject($acceptance->created_at, 'datetime', false),
'accepted_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false),
'declined_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false),
'assigned_to' => $assigned_user->display_name,
'email' => $assigned_user->email,
'employee_num' => $assigned_user->employee_num,
'site_name' => $settings->site_name,
'company_name' => $item->company?->name?? $settings->site_name,
'signature' => (($sig_filename && array_key_exists('1', $encoded_image))) ? $encoded_image[1] : null,
'logo' => ($encoded_logo) ?? null,
'date_settings' => $settings->date_display_format,
'admin' => auth()->user()->present()?->fullName,
'qty' => $acceptance->qty ?? 1,
];
$display_model = '';
$pdf_view_route = '';
$pdf_filename = 'accepted-eula-'.date('Y-m-d-h-i-s').'.pdf';
$sig_filename='';
if ($request->input('asset_acceptance') == 'accepted') {
/**
* Check for the eula-pdfs directory
*/
if (! Storage::exists('private_uploads/eula-pdfs')) {
Storage::makeDirectory('private_uploads/eula-pdfs', 775);
}
$pdf_filename = 'accepted-'.$acceptance->checkoutable_id.'-'.$acceptance->display_checkoutable_type.'-eula-'.date('Y-m-d-h-i-s').'.pdf';
if (Setting::getSettings()->require_accept_signature == '1') {
// Check if the signature directory exists, if not create it
if (!Storage::exists('private_uploads/signatures')) {
Storage::makeDirectory('private_uploads/signatures', 775);
}
// Generate the PDF content
$pdf_content = $acceptance->generateAcceptancePdf($data, $acceptance);
Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf_content);
// The item was accepted, check for a signature
if ($request->filled('signature_output')) {
$sig_filename = 'siglog-' . Str::uuid() . '-' . date('Y-m-d-his') . '.png';
$data_uri = $request->input('signature_output');
$encoded_image = explode(',', $data_uri);
$decoded_image = base64_decode($encoded_image[1]);
Storage::put('private_uploads/signatures/' . $sig_filename, (string)$decoded_image);
// Log the acceptance
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note'));
// Send the PDF to the signing user
if (($request->input('send_copy') == '1') && ($assigned_user->email !='')) {
// Add the attachment for the signing user into the $data array
$data['file'] = $pdf_filename;
try {
$assigned_user->notify((new AcceptanceAssetAcceptedToUserNotification($data))->locale($assigned_user->locale));
} catch (\Exception $e) {
Log::warning($e);
// No image data is present, kick them back.
// This mostly only applies to users on super-duper crapola browsers *cough* IE *cough*
} else {
return redirect()->back()->with('error', trans('general.shitty_browser'));
}
}
// this is horrible
switch($acceptance->checkoutable_type){
case 'App\Models\Asset':
$pdf_view_route ='account.accept.accept-asset-eula';
$asset_model = AssetModel::find($item->model_id);
if (!$asset_model) {
return redirect()->back()->with('error', trans('admin/models/message.does_not_exist'));
}
$display_model = $asset_model->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Accessory':
$pdf_view_route ='account.accept.accept-accessory-eula';
$accessory = Accessory::find($item->id);
$display_model = $accessory->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\LicenseSeat':
$pdf_view_route ='account.accept.accept-license-eula';
$license = License::find($item->license_id);
$display_model = $license->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Component':
$pdf_view_route ='account.accept.accept-component-eula';
$component = Component::find($item->id);
$display_model = $component->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Consumable':
$pdf_view_route ='account.accept.accept-consumable-eula';
$consumable = Consumable::find($item->id);
$display_model = $consumable->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
}
// if ($acceptance->checkoutable_type == 'App\Models\Asset') {
// $pdf_view_route ='account.accept.accept-asset-eula';
// $asset_model = AssetModel::find($item->model_id);
// $display_model = $asset_model->name;
// $assigned_to = User::find($item->assigned_to)->present()->fullName;
//
// } elseif ($acceptance->checkoutable_type== 'App\Models\Accessory') {
// $pdf_view_route ='account.accept.accept-accessory-eula';
// $accessory = Accessory::find($item->id);
// $display_model = $accessory->name;
// $assigned_to = User::find($item->assignedTo);
//
// }
/**
* Gather the data for the PDF. We fire this whether there is a signature required or not,
* since we want the moment-in-time proof of what the EULA was when they accepted it.
*/
$branding_settings = SettingsController::getPDFBranding();
if (is_null($branding_settings->logo)){
$path_logo = "";
} else {
$path_logo = public_path() . '/uploads/' . $branding_settings->logo;
}
$data = [
'item_tag' => $item->asset_tag,
'item_model' => $display_model,
'item_serial' => $item->serial,
'item_status' => $item->assetstatus?->name,
'eula' => $item->getEula(),
'note' => $request->input('note'),
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format('Y-m-d'),
'assigned_to' => $assigned_to,
'company_name' => $branding_settings->site_name,
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
'logo' => $path_logo,
'date_settings' => $branding_settings->date_display_format,
];
if ($pdf_view_route!='') {
Log::debug($pdf_filename.' is the filename, and the route was specified.');
$pdf = Pdf::loadView($pdf_view_route, $data);
Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf->output());
}
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note'));
try {
$acceptance->notify((new AcceptanceAssetAcceptedNotification($data))->locale(Setting::getSettings()->locale));
$acceptance->notify(new AcceptanceAssetAcceptedNotification($data));
} catch (\Exception $e) {
Log::warning($e);
}
@@ -201,43 +246,97 @@ class AcceptanceController extends Controller
$return_msg = trans('admin/users/message.accepted');
// Item was declined
} else {
for ($i = 0; $i < ($acceptance->qty ?? 1); $i++) {
$acceptance->decline($sig_filename, $request->input('note'));
/**
* Check for the eula-pdfs directory
*/
if (! Storage::exists('private_uploads/eula-pdfs')) {
Storage::makeDirectory('private_uploads/eula-pdfs', 775);
}
if (Setting::getSettings()->require_accept_signature == '1') {
// Check if the signature directory exists, if not create it
if (!Storage::exists('private_uploads/signatures')) {
Storage::makeDirectory('private_uploads/signatures', 775);
}
// The item was accepted, check for a signature
if ($request->filled('signature_output')) {
$sig_filename = 'siglog-' . Str::uuid() . '-' . date('Y-m-d-his') . '.png';
$data_uri = $request->input('signature_output');
$encoded_image = explode(',', $data_uri);
$decoded_image = base64_decode($encoded_image[1]);
Storage::put('private_uploads/signatures/' . $sig_filename, (string)$decoded_image);
// No image data is present, kick them back.
// This mostly only applies to users on super-duper crapola browsers *cough* IE *cough*
} else {
return redirect()->back()->with('error', trans('general.shitty_browser'));
}
}
// Format the data to send the declined notification
$branding_settings = SettingsController::getPDFBranding();
// This is the most horriblest
switch($acceptance->checkoutable_type){
case 'App\Models\Asset':
$asset_model = AssetModel::find($item->model_id);
$display_model = $asset_model->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Accessory':
$accessory = Accessory::find($item->id);
$display_model = $accessory->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\LicenseSeat':
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Component':
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Consumable':
$consumable = Consumable::find($item->id);
$display_model = $consumable->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
}
$data = [
'item_tag' => $item->asset_tag,
'item_model' => $display_model,
'item_serial' => $item->serial,
'item_status' => $item->assetstatus?->name,
'note' => $request->input('note'),
'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'),
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
'assigned_to' => $assigned_to,
'company_name' => $branding_settings->site_name,
'date_settings' => $branding_settings->date_display_format,
];
if ($pdf_view_route!='') {
Log::debug($pdf_filename.' is the filename, and the route was specified.');
$pdf = Pdf::loadView($pdf_view_route, $data);
Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf->output());
}
$acceptance->decline($sig_filename, $request->input('note'));
$acceptance->notify(new AcceptanceAssetDeclinedNotification($data));
Log::debug('New event acceptance.');
event(new CheckoutDeclined($acceptance));
$return_msg = trans('admin/users/message.declined');
}
// Send an email notification if one is requested
if ($acceptance->alert_on_response_id) {
try {
$recipient = User::find($acceptance->alert_on_response_id);
if ($recipient) {
Log::debug('Attempting to send email acceptance.');
Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
$acceptance,
$recipient,
$request->input('asset_acceptance') === 'accepted',
));
Log::debug('Send email notification sucess on checkout acceptance response.');
}
} catch (Exception $e) {
Log::error($e->getMessage());
Log::warning($e);
}
}
return redirect()->to('account/accept')->with('success', $return_msg);
}
}

View File

@@ -3,13 +3,11 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Models\Actionlog;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use \Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class ActionlogController extends Controller
{
public function displaySig($filename) : RedirectResponse | Response | bool
@@ -41,29 +39,17 @@ class ActionlogController extends Controller
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
{
$this->authorize('view', \App\Models\Asset::class);
if ($actionlog = Actionlog::where('filename', $filename)->with('user')->with('target')->firstOrFail()) {
$this->authorize('view', $actionlog->target);
$this->authorize('view', $actionlog->user);
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/' . $filename, now()->addMinutes(5)));
}
if (Storage::exists('private_uploads/eula-pdfs/' . $filename)) {
if (request()->input('inline') == 'true') {
return response()->file(config('app.private_uploads') . '/eula-pdfs/' . $filename);
}
return response()->download(config('app.private_uploads') . '/eula-pdfs/' . $filename);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
}
return redirect()->back()->with('error', trans('general.record_not_found'));
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
}
}

View File

@@ -11,9 +11,12 @@ use App\Http\Requests\StoreAccessoryRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
use App\Models\Company;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use App\Models\AccessoryCheckout;
@@ -23,25 +26,11 @@ class AccessoriesController extends Controller
use CheckInOutRequest;
/**
* List accessories
* Display a listing of the resource.
*
* @group Accessories
* @queryParam search string A search term to filter results by. Example: keyboard
* @queryParam filter[<fieldname>] string A field to filter by. Example
* @queryParam company_id int Filter by company ID. Example: 1
* @queryParam category_id int Filter by category ID. Example: 1
* @queryParam manufacturer_id int Filter by manufacturer ID. Example: 1
* @queryParam supplier_id int Filter by supplier ID. Example: 1
* @queryParam location_id int Filter by location ID. Example: 1
* @queryParam notes string Filter by notes. Example: For office use only
* @queryParam offset int The number of items to skip before starting to collect the result set. Example: 0
* @queryParam limit int The number of items to return. Example: 50
* @queryParam sort string The field to sort by. Example: created_at
* @queryParam order string The order to sort by. Example: desc
*
* @return \Illuminate\Http\Response
*@since [v4.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
@@ -65,15 +54,6 @@ class AccessoriesController extends Controller
'notes',
'checkouts_count',
'qty',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'company',
'location',
'category',
'supplier',
'manufacturer',
];
@@ -81,25 +61,12 @@ class AccessoriesController extends Controller
->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier', 'adminuser')
->withCount('checkouts as checkouts_count');
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
if ($request->filled('search')) {
$accessories = $accessories->TextSearch($request->input('search'));
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$accessories->ByFilter($filter);
} elseif ($request->filled('search')) {
$accessories->TextSearch($request->input('search'));
}
if ($request->filled('company_id')) {
$accessories->where('accessories.company_id', '=', $request->input('company_id'));
$accessories->where('company_id', '=', $request->input('company_id'));
}
if ($request->filled('category_id')) {
@@ -162,13 +129,9 @@ class AccessoriesController extends Controller
/**
* Create Accessory
* Store a newly created resource in storage.
*
* @group Accessories
* @bodyParam name string required The name of the accessory. Example: Apple Bluetooth Keyboard
* @bodyParam qty int required The number of accessories to create. Example: 10
* @bodyParam category_id int required The ID of the category to assign this accessory to. Example: 1
* @bodyParam image file No-example
* @param \App\Http\Requests\ImageUploadRequest $request
* @return \Illuminate\Http\JsonResponse
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
@@ -188,9 +151,8 @@ class AccessoriesController extends Controller
}
/**
* Show Accessory
* Display the specified resource.
*
* @group Accessories
* @param int $id
* @return array
* @author [A. Gianotto] [<snipe@snipe.net>]
@@ -205,11 +167,26 @@ class AccessoriesController extends Controller
}
/**
* Display the specified resource.
*
* @param int $id
* @return array
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function accessory_detail($id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::findOrFail($id);
return (new AccessoriesTransformer)->transformAccessory($accessory);
}
/**
* Show Accessory Checkouts
* Get the list of checkouts for a specific accessory
*
* @group Accessories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
@@ -239,9 +216,8 @@ class AccessoriesController extends Controller
/**
* Update accessory.
* Update the specified resource in storage.
*
* @group Accessories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
@@ -263,9 +239,8 @@ class AccessoriesController extends Controller
}
/**
* Delete Accessory
* Remove the specified resource from storage.
*
* @group Accessories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
@@ -274,11 +249,11 @@ class AccessoriesController extends Controller
public function destroy($id)
{
$this->authorize('delete', Accessory::class);
$accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id);
$accessory = Accessory::findOrFail($id);
$this->authorize($accessory);
if ($accessory->checkouts_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/general.delete_disabled')));
if ($accessory->hasUsers() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()])));
}
$accessory->delete();
@@ -288,12 +263,11 @@ class AccessoriesController extends Controller
/**
* Checkout Accessory
* Save the Accessory checkout information.
*
* If Slack is enabled and/or asset acceptance is enabled, it will also
* trigger a Slack message and send an email.
*
* @group Accessories
* @param int $accessoryId
* @return \Illuminate\Http\JsonResponse
* @author [A. Gianotto] [<snipe@snipe.net>]
@@ -314,43 +288,32 @@ class AccessoriesController extends Controller
'note' => $request->input('note'),
]);
$accessory_checkout->created_by = auth()->id();
$accessory_checkout->save();
$payload = [
'accessory_id' => $accessory->id,
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
'created_by' => auth()->id(),
'pivot' => $accessory_checkout->id,
];
}
// Set this value to be able to pass the qty through to the event
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('admin/accessories/message.checkout.success')));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
}
/**
* Checkin Accessory
* Check in the item so that it can be checked out again to someone else
*
* @group Accessories
* @uses Accessory::checkin_email() to determine if an email can and should be sent
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @param int $accessoryUserId
* @param string $backto
* @return JsonResponse
* @uses Accessory::checkin_email() to determine if an email can and should be sent
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return \Illuminate\Http\RedirectResponse
* @internal param int $accessoryId
*/
public function checkin(Request $request, $accessoryUserId = null)
{
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryUserId))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist', ['id' => $accessoryUserId])));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
}
$accessory = Accessory::find($accessory_checkout->accessory_id);
@@ -364,14 +327,7 @@ class AccessoriesController extends Controller
$user = User::find($accessory_checkout->assigned_to);
}
$payload = [
'accessory_id' => $accessory->id,
'note' => $request->input('note'),
'created_by' => auth()->id(),
'pivot' => $accessory_checkout->id,
];
return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('admin/accessories/message.checkin.success')));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkin.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkin.error')));
@@ -380,10 +336,8 @@ class AccessoriesController extends Controller
/**
* Selectlist
* Gets a paginated collection for the select2 menus
*
* @group Accessories
* @queryParam search string A search term to filter results by name. No-example
* @see \App\Http\Transformers\SelectlistTransformer
*
*/

View File

@@ -0,0 +1,200 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\StorageHelper;
use Illuminate\Support\Facades\Storage;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This class controls file related actions related
* to assets for the Snipe-IT Asset Management application.
*
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
*
* @version v1.0
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
class AssetFilesController extends Controller
{
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param int $assetId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function store(UploadFileRequest $request, $assetId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
// Make sure we are allowed to update this asset
$this->authorize('update', $asset);
if ($request->hasFile('file')) {
// If the file storage directory doesn't exist; create it
if (! Storage::exists('private_uploads/assets')) {
Storage::makeDirectory('private_uploads/assets', 775);
}
// Loop over the attached files and add them to the asset
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
$asset->logUpload($file_name, e($request->get('notes')));
}
// All done - report success
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.upload.success')));
}
// We only reach here if no files were included in the POST, so tell the user this
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.upload.nofiles')), 500);
}
/**
* List the files for an asset.
*
* @param int $assetId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function list($assetId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
// Check that there are some uploads on this asset that can be listed
if ($asset->uploads->count() > 0) {
$files = array();
foreach ($asset->uploads as $upload) {
array_push($files, $upload);
}
// Give the list of files back to the user
return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/hardware/message.upload.success')));
}
// There are no files.
return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/hardware/message.upload.success')));
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error')), 500);
}
/**
* Check for permissions and display the file.
*
* @param int $assetId
* @param int $fileId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function show($assetId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
// Check that the file being requested exists for the asset
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.no_match', ['id' => $fileId])), 404);
}
// Form the full filename with path
$file = 'private_uploads/assets/'.$log->filename;
Log::debug('Checking for '.$file);
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
// Check the file actually exists on the filesystem
if (! Storage::exists($file)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.does_not_exist', ['id' => $fileId])), 404);
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error', ['id' => $fileId])), 500);
}
/**
* Delete the associated file
*
* @param int $assetId
* @param int $fileId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function destroy($assetId = null, $fileId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
$rel_path = 'private_uploads/assets';
// the asset is valid
if (isset($asset->id)) {
$this->authorize('update', $asset);
// Check for the file
$log = Actionlog::find($fileId);
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
// Delete the record of the file
$log->delete();
// All deleting done - notify the user of success
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.deletefile.success')), 200);
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
}
}

View File

@@ -4,11 +4,11 @@ namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\MaintenancesTransformer;
use App\Http\Transformers\AssetMaintenancesTransformer;
use App\Models\Asset;
use App\Models\Maintenance;
use App\Models\AssetMaintenance;
use App\Models\Company;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
@@ -18,24 +18,13 @@ use Illuminate\Http\JsonResponse;
*
* @version v2.0
*/
class MaintenancesController extends Controller
class AssetMaintenancesController extends Controller
{
/**
* List Maintenances
* Generates the JSON response for asset maintenances listing view.
*
* @group Maintenances
* @queryParam search string Search term to filter results. Example: repair
* @queryParam asset_id integer Filter by exact asset ID. Example: 1
* @queryParam supplier_id integer Filter by exact supplier ID. Example: 1
* @queryParam created_by integer Filter by exact user ID who created the maintenance. Example
* @queryParam url string Filter by exact URL. Example: http://example.com
* @queryParam asset_maintenance_type string Filter by exact maintenance type. Example: repair
* @queryParam sort string Column to sort results by. Allowed values: id, name, asset_maintenance_time, asset_maintenance_type, cost, start_date, completion_date, notes, asset_tag, asset_name, serial, created_by, supplier, location, is_warranty, status_label. Default: created_at. Example: name
* @queryParam order string Order of sorted results. Allowed values: asc, desc. Default: desc. Example: asc
* @queryParam offset integer Offset/starting position of the results. Default: 0. Example: 0
* @queryParam limit integer Limit the number of results returned. Default: 25. Maximum: 100. Example: 50
* @see MaintenancesController::getIndex() method that generates view
* @see AssetMaintenancesController::getIndex() method that generates view
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
@@ -44,7 +33,7 @@ class MaintenancesController extends Controller
{
$this->authorize('view', Asset::class);
$maintenances = Maintenance::select('maintenances.*')
$maintenances = AssetMaintenance::select('asset_maintenances.*')
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'adminuser');
if ($request->filled('search')) {
@@ -56,15 +45,11 @@ class MaintenancesController extends Controller
}
if ($request->filled('supplier_id')) {
$maintenances->where('maintenances.supplier_id', '=', $request->input('supplier_id'));
$maintenances->where('asset_maintenances.supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('created_by')) {
$maintenances->where('maintenances.created_by', '=', $request->input('created_by'));
}
if ($request->filled('url')) {
$maintenances->where('maintenances.url', '=', $request->input('url'));
$maintenances->where('asset_maintenances.created_by', '=', $request->input('created_by'));
}
if ($request->filled('asset_maintenance_type')) {
@@ -78,7 +63,7 @@ class MaintenancesController extends Controller
$allowed_columns = [
'id',
'name',
'title',
'asset_maintenance_time',
'asset_maintenance_type',
'cost',
@@ -90,7 +75,6 @@ class MaintenancesController extends Controller
'serial',
'created_by',
'supplier',
'location',
'is_warranty',
'status_label',
];
@@ -114,9 +98,6 @@ class MaintenancesController extends Controller
case 'serial':
$maintenances = $maintenances->OrderByAssetSerial($order);
break;
case 'location':
$maintenances = $maintenances->OrderLocationName($order);
break;
case 'status_label':
$maintenances = $maintenances->OrderStatusName($order);
break;
@@ -127,33 +108,31 @@ class MaintenancesController extends Controller
$total = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get();
return (new MaintenancesTransformer())->transformMaintenances($maintenances, $total);
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total);
}
/**
* Create Maintenance
* Validates and stores the new asset maintenance
*
* @group Maintenances
* @see MaintenancesController::getCreate() method for the form
* @see AssetMaintenancesController::getCreate() method for the form
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
*/
public function store(ImageUploadRequest $request) : JsonResponse | array
public function store(Request $request) : JsonResponse | array
{
$this->authorize('update', Asset::class);
// create a new model instance
$maintenance = new Maintenance();
$maintenance = new AssetMaintenance();
$maintenance->fill($request->all());
$maintenance->created_by = auth()->id();
$maintenance = $request->handleImages($maintenance);
// Was the asset maintenance created?
if ($maintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.create.success')));
}
@@ -162,9 +141,8 @@ class MaintenancesController extends Controller
}
/**
* Update Maintenance
* Validates and stores an update to an asset maintenance
*
* @group Maintenances
* @author A. Gianotto <snipe@snipe.net>
* @param int $id
* @param int $request
@@ -175,11 +153,11 @@ class MaintenancesController extends Controller
{
$this->authorize('update', Asset::class);
if ($maintenance = Maintenance::with('asset')->find($id)) {
if ($maintenance = AssetMaintenance::with('asset')->find($id)) {
// Can this user manage this asset?
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
}
// The asset this miantenance is attached to is not valid or has been deleted
@@ -190,57 +168,55 @@ class MaintenancesController extends Controller
$maintenance->fill($request->all());
if ($maintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.edit.success')));
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.edit.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/maintenances/general.maintenance'), 'id' => $id])));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id])));
}
/**
* Delete Maintenance
* Delete an asset maintenance
*
* @group Maintenances
* @author A. Gianotto <snipe@snipe.net>
* @param int $maintenanceId
* @param int $assetMaintenanceId
* @version v1.0
* @since [v4.0]
*/
public function destroy($maintenanceId) : JsonResponse | array
public function destroy($assetMaintenanceId) : JsonResponse | array
{
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
$maintenance = Maintenance::findOrFail($maintenanceId);
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
$maintenance->delete();
$assetMaintenance->delete();
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.delete.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success')));
}
/**
* View Maintenance
* View an asset maintenance
*
* @group Maintenances
* @author A. Gianotto <snipe@snipe.net>
* @param int $maintenanceId
* @param int $assetMaintenanceId
* @version v1.0
* @since [v4.0]
*/
public function show($maintenanceId) : JsonResponse | array
public function show($assetMaintenanceId) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$maintenance = Maintenance::findOrFail($maintenanceId);
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
if (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot view a maintenance for that asset'));
}
return (new MaintenancesTransformer())->transformMaintenance($maintenance);
return (new AssetMaintenancesTransformer())->transformAssetMaintenance($assetMaintenance);
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\StorageHelper;
use Illuminate\Support\Facades\Storage;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\AssetModel;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use App\Http\Transformers\AssetModelsTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This class controls file related actions related
* to assets for the Snipe-IT Asset Management application.
*
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
*
* @version v1.0
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
class AssetModelFilesController extends Controller
{
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param int $assetModelId
* @since [v7.0.12]
* @author [r-xyz]
*/
public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// Make sure we are allowed to update this asset
$this->authorize('update', $assetModel);
if ($request->hasFile('file')) {
// If the file storage directory doesn't exist; create it
if (! Storage::exists('private_uploads/assetmodels')) {
Storage::makeDirectory('private_uploads/assetmodels', 775);
}
// Loop over the attached files and add them to the asset
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file);
$assetModel->logUpload($file_name, e($request->get('notes')));
}
// All done - report success
return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success')));
}
// We only reach here if no files were included in the POST, so tell the user this
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500);
}
/**
* List the files for an asset.
*
* @param int $assetmodel
* @since [v7.0.12]
* @author [r-xyz]
*/
public function list($assetmodel_id) : JsonResponse | array
{
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
$this->authorize('view', $assetmodel);
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
}
/**
* Check for permissions and display the file.
*
* @param int $assetModelId
* @param int $fileId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v7.0.12]
* @author [r-xyz]
*/
public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('view', $assetModel);
// Check that the file being requested exists for the asset
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404);
}
// Form the full filename with path
$file = 'private_uploads/assetmodels/'.$log->filename;
Log::debug('Checking for '.$file);
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
// Check the file actually exists on the filesystem
if (! Storage::exists($file)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404);
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500);
}
/**
* Delete the associated file
*
* @param int $assetModelId
* @param int $fileId
* @since [v7.0.12]
* @author [r-xyz]
*/
public function destroy($assetModelId = null, $fileId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
$rel_path = 'private_uploads/assetmodels';
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('update', $assetModel);
// Check for the file
$log = Actionlog::find($fileId);
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
// Delete the record of the file
$log->delete();
// All deleting done - notify the user of success
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200);
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
}
}

View File

@@ -25,9 +25,8 @@ use Illuminate\Http\JsonResponse;
class AssetModelsController extends Controller
{
/**
* List Models
* Display a listing of the resource.
*
* @group Models
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
@@ -47,20 +46,10 @@ class AssetModelsController extends Controller
'manufacturer',
'requestable',
'assets_count',
'assets_assigned_count',
'assets_archived_count',
'remaining',
'category',
'fieldset',
'deleted_at',
'updated_at',
'require_serial',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'manufacturer',
'category',
];
$assetmodels = AssetModel::select([
@@ -80,54 +69,14 @@ class AssetModelsController extends Controller
'models.fieldset_id',
'models.deleted_at',
'models.updated_at',
'models.require_serial'
])
->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues', 'adminuser')
->withCount('assets as assets_count')
->withCount('availableAssets as remaining')
->withCount('assignedAssets as assets_assigned_count')
->withCount('archivedAssets as assets_archived_count');
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$assetmodels->ByFilter($filter);
} elseif ($request->filled('search')) {
$assetmodels->TextSearch($request->input('search'));
}
->withCount('assets as assets_count');
if ($request->input('status')=='deleted') {
$assetmodels->onlyTrashed();
}
if ($request->filled('name')) {
$assetmodels = $assetmodels->where('models.name', '=', $request->input('name'));
}
if ($request->filled('model_number')) {
$assetmodels = $assetmodels->where('models.model_number', '=', $request->input('model_number'));
}
if ($request->input('requestable') == 'true') {
$assetmodels = $assetmodels->where('models.requestable', '=', '1');
} elseif ($request->input('requestable') == 'false') {
$assetmodels = $assetmodels->where('models.requestable', '=', '0');
}
if ($request->filled('notes')) {
$assetmodels = $assetmodels->where('models.notes', '=', $request->input('notes'));
}
if ($request->filled('category_id')) {
$assetmodels = $assetmodels->where('models.category_id', '=', $request->input('category_id'));
}
@@ -173,9 +122,8 @@ class AssetModelsController extends Controller
/**
* Create Model
* Store a newly created resource in storage.
*
* @group Models
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\StoreAssetModelRequest $request
@@ -188,7 +136,7 @@ class AssetModelsController extends Controller
$assetmodel = $request->handleImages($assetmodel);
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
@@ -196,12 +144,11 @@ class AssetModelsController extends Controller
}
/**
* Show Model
* Display the specified resource.
*
* @group Models
* @urlParam id int required The ID of the model.
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
*/
public function show($id) : array
{
@@ -212,13 +159,11 @@ class AssetModelsController extends Controller
}
/**
* List Assets in Model
* Display the specified resource's assets
*
* @group Models
* @urlParam id int required The ID of the model.
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @param int $id
*/
public function assets($id) : array
{
@@ -230,12 +175,12 @@ class AssetModelsController extends Controller
/**
* Update Model
* Update the specified resource in storage.
*
* @group Models
* @urlParam id int required The ID of the model.
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(StoreAssetModelRequest $request, $id) : JsonResponse
@@ -244,7 +189,7 @@ class AssetModelsController extends Controller
$assetmodel = AssetModel::findOrFail($id);
$assetmodel->fill($request->all());
$assetmodel = $request->handleImages($assetmodel);
/**
* Allow custom_fieldset_id to override and populate fieldset_id.
* This is stupid, but required for legacy API support.
@@ -259,19 +204,18 @@ class AssetModelsController extends Controller
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
}
/**
* Delete Model
* Remove the specified resource from storage.
*
* @group Models
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @urlParam id int required The ID of the model.
* @param int $id
*/
public function destroy($id) : JsonResponse
{
@@ -297,9 +241,8 @@ class AssetModelsController extends Controller
}
/**
* Selectlist of Models
* Gets a paginated collection for the select2 menus
*
* @group Models
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer

View File

@@ -6,7 +6,6 @@ use App\Events\CheckoutableCheckedIn;
use App\Http\Requests\StoreAssetRequest;
use App\Http\Requests\UpdateAssetRequest;
use App\Http\Traits\MigratesLegacyAssetLocations;
use App\Http\Transformers\ComponentsTransformer;
use App\Models\AccessoryCheckout;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
@@ -35,7 +34,6 @@ use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use App\View\Label;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
/**
@@ -50,32 +48,10 @@ class AssetsController extends Controller
use MigratesLegacyAssetLocations;
/**
* List Assets
* Returns JSON listing of all assets
*
* @group Assets
* @queryParam action string The action to filter by: audits, checkins. No-example
* @queryParam upcoming_status string The status to filter by: `due`, `overdue`, `due-or-overdue`. No-example
* @queryParam status string The asset status to filter by: `RTD`, `Deployed`, `Pending`, `Undeployable`, `Archived`, `Requestable`. No-example
* @queryParam status_id int The asset status ID to filter by. No-example
* @queryParam asset_tag string The asset tag to filter by. No-example
* @queryParam serial string The serial number to filter by. No-example
* @queryParam requestable boolean Filter by requestable assets. No-example
* @queryParam model_id int The model ID or IDs to filter by. No-example
* @queryParam category_id int The category ID to filter by. No-example
* @queryParam location_id int The location ID to filter by. No-example
* @queryParam rtd_location_id int The RTD location ID to filter by. No-example
* @queryParam supplier_id int The supplier ID to filter by. No-example
* @queryParam asset_eol_date date The asset end-of-life date to filter by. No-example
* @queryParam assigned_to int The user ID or department ID the asset is assigned to. No-example
* @queryParam assigned_type string The type of assignment, either 'user' or 'asset' or 'location'. Example: user
* @queryParam company_id int The company ID to filter by. No-example
* @queryParam manufacturer_id int The manufacturer ID to filter by. No-example
* @queryParam depreciation_id int The depreciation ID to filter by. No-example
* @queryParam byod boolean Filter by BYOD assets. Example: true
* @queryParam order_number string The order number to filter by. Example: No-example
* @queryParam search string A text string to search for. Example: No-example
* @queryParam filter string A JSON object of key/value pairs to filter by. No-example
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
*/
public function index(Request $request, $action = null, $upcoming_status = null) : JsonResponse | array
@@ -137,40 +113,17 @@ class AssetsController extends Controller
'byod',
'asset_eol_date',
'requestable',
'jobtitle',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'company',
'model',
'location',
'rtd_location',
'category',
'status_label',
'manufacturer',
'supplier',
'jobtitle',
'assigned_to',
'created_by',
];
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[] = $field->db_column_name();
}
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
}
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[] = $field->db_column_name();
}
$assets = Asset::select('assets.*')
@@ -186,7 +139,6 @@ class AssetsController extends Controller
'model.category',
'model.manufacturer',
'model.fieldset',
'model.depreciation',
'supplier'
); // it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
@@ -345,15 +297,9 @@ class AssetsController extends Controller
if ($request->input('requestable') == 'true') {
$assets->where('assets.requestable', '=', '1');
}
if ($request->filled('model_id')) {
// If model_id is already an array, just use it as-is
if (is_array($request->input('model_id'))) {
$assets->InModelList($request->input('model_id'));
} else {
// Otherwise, turn it into an array
$assets->InModelList([$request->input('model_id')]);
}
$assets->InModelList([$request->input('model_id')]);
}
if ($request->filled('category_id')) {
@@ -442,9 +388,6 @@ class AssetsController extends Controller
case 'assigned_to':
$assets->OrderAssigned($order);
break;
case 'jobtitle':
$assets->OrderByJobTitle($order);
break;
case 'created_by':
$assets->OrderByCreatedByName($order);
break;
@@ -492,15 +435,20 @@ class AssetsController extends Controller
}]);
}
/**
* Here we're just determining which Transformer (via $transformer) to use based on the
* variables we set earlier on in this method - we default to AssetsTransformer.
*/
return (new $transformer)->transformAssets($assets, $total, $request);
}
/**
* Lookup by Tag
* Returns JSON with information about an asset (by tag) for detail view.
*
* @group Assets
* @urlParam $tag string required The asset tag. Example: 1
* @param string $tag
* @since [v4.2.1]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
@@ -533,43 +481,25 @@ class AssetsController extends Controller
}
/**
* Lookup by Serial
* Returns JSON with information about an asset (by serial) for detail view.
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @urlParam $serial string
* @param string $serial
* @since [v4.2.1]
* @return \Illuminate\Http\JsonResponse
*/
public function showBySerial(Request $request, $serial): JsonResponse | array
{
$this->authorize('index', Asset::class);
$assets = Asset::where('serial', $serial)->with([
'assetstatus',
'assignedTo',
'company',
'defaultLoc',
'location',
'model.category',
'model.depreciation',
'model.fieldset',
'model.manufacturer',
'supplier',
]);
$assets = Asset::where('serial', $serial)->with('assetstatus')->with('assignedTo');
// Check if they've passed ?deleted=true
if ($request->input('deleted', 'false') == 'true') {
$assets = $assets->withTrashed();
}
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
if (($assets) && ($assets->count()) > 0) {
return (new AssetsTransformer)->transformAssets($assets, $total);
if (($assets = $assets->get()) && ($assets->count()) > 0) {
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
// If there are 0 results, return the "no such asset" response
@@ -577,11 +507,10 @@ class AssetsController extends Controller
}
/**
* Show Asset
* Returns JSON with information about an asset for detail view.
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @queryParam $assetId int required The asset ID. Example: 1
* @param int $assetId
* @since [v4.0]
* @return \Illuminate\Http\JsonResponse
*/
@@ -598,13 +527,6 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
* Licenses Assigned to Asset
*
* @group Assets
* @queryParam $assetId int required The asset ID. Example: 1
* @return \Illuminate\Http\JsonResponse
*/
public function licenses(Request $request, $id): array
{
$this->authorize('view', Asset::class);
@@ -617,12 +539,8 @@ class AssetsController extends Controller
/**
* Selectlist
* Gets a paginated collection for the select2 menus
*
* @group Assets
* @queryParam $search string String to search on. No-example
* @queryParam $companyId int Company ID to filter by. No-example
* @queryParam $assetStatusType string The asset status type to filter by: `RTD`. No-example
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
@@ -638,12 +556,7 @@ class AssetsController extends Controller
'assets.assigned_to',
'assets.assigned_type',
'assets.status_id',
])->with('model', 'assetstatus', 'assignedTo')
->NotArchived();
if ((Setting::getSettings()->full_multiple_companies_support=='1') && ($request->filled('companyId'))) {
$assets->where('assets.company_id', $request->input('companyId'));
}
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
$assets = $assets->RTD();
@@ -653,6 +566,7 @@ class AssetsController extends Controller
$assets = $assets->AssignedSearch($request->input('search'));
}
$assets = $assets->paginate(50);
// Loop through and set some custom properties for the transformer to use.
@@ -664,7 +578,7 @@ class AssetsController extends Controller
$asset->use_text = $asset->present()->fullName;
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
$asset->use_text .= ' → ' . $asset->assigned->display_name;
$asset->use_text .= ' → ' . $asset->assigned->getFullNameAttribute();
}
@@ -680,9 +594,8 @@ class AssetsController extends Controller
/**
* Create Asset
* Accepts a POST request to create a new asset
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param \App\Http\Requests\ImageUploadRequest $request
* @since [v4.0]
@@ -766,9 +679,7 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
// below is what we want the _eventual_ return to look like - in a more standardized format.
// return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
@@ -776,15 +687,8 @@ class AssetsController extends Controller
/**
* Update Asset
* Accepts a POST request to update an asset
*
* @group Assets
* @queryParam model_id int The model ID to associate the asset with. Example: 1
* @queryParam company_id int The company ID to associate the asset with. Example: 1
* @queryParam rtd_location_id int The RTD location ID to associate the asset with. Example: 1
* @queryParam last_audit_date date The last audit date for the asset. Example: 2023-12-31
* @queryParam image file The image file to upload for the asset. Example: (binary)
* @param \App\Http\Requests\UpdateAssetRequest $request
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
@@ -861,13 +765,9 @@ class AssetsController extends Controller
}
if ($problems_updating_encrypted_custom_fields) {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
// Below is the *correct* return since it uses the transformer, but we have to use the old, flat return for now until we can update Jamf2Snipe and Kanji2Snipe
// return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.encrypted_warning')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.encrypted_warning')));
} else {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
// Below is the *correct* return since it uses the transformer, but we have to use the old, flat return for now until we can update Jamf2Snipe and Kanji2Snipe
/// return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.success')));
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
@@ -875,10 +775,8 @@ class AssetsController extends Controller
/**
* Delete Asset
* Delete a given asset (mark as deleted).
*
* @group Assets
* @param int $id
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
@@ -912,9 +810,8 @@ class AssetsController extends Controller
/**
* Restore Asset
* Restore a soft-deleted asset.
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v5.1.18]
@@ -941,9 +838,8 @@ class AssetsController extends Controller
}
/**
* Checkout by Asset Tag
* Checkout an asset by its tag.
*
* @group Assets
* @author [N. Butler]
* @param string $tag
* @since [v6.0.5]
@@ -957,9 +853,8 @@ class AssetsController extends Controller
}
/**
* Checkout Asset
* Checkout an asset
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
@@ -1033,9 +928,8 @@ class AssetsController extends Controller
/**
* Checkin Asset
* Checkin an asset
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
@@ -1119,9 +1013,8 @@ class AssetsController extends Controller
}
/**
* Checkin by Asset Tag
* Checkin an asset by asset tag
*
* @group Assets
* @author [A. Janes] [<ajanes@adagiohealth.org>]
* @since [v6.0]
*/
@@ -1144,14 +1037,13 @@ class AssetsController extends Controller
/**
* Audit Asset
* Mark an asset as audited
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @since [v4.0]
*/
public function audit(Request $request, Asset $asset): JsonResponse
public function audit(Request $request): JsonResponse
{
$this->authorize('audit', Asset::class);
@@ -1159,15 +1051,36 @@ class AssetsController extends Controller
$settings = Setting::getSettings();
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
// Allow the asset tag to be passed in the payload (legacy method)
if ($request->filled('asset_tag')) {
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
// No tag passed - return an error
if (!$request->filled('asset_tag')) {
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag' => '',
'error' => trans('admin/hardware/message.no_tag'),
], trans('admin/hardware/message.no_tag')), 200);
}
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
if ($asset) {
$originalValues = $asset->getRawOriginal();
/**
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
* the audit log entry we're creating through this controller.
*
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
* will bypass normal model-level validation that's usually handled at the observer )
*
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
* which manually invokes Watson Validating to make sure the asset's model is valid.
*
* @see \App\Observers\AssetObserver::updating()
*/
$asset->unsetEventDispatcher();
$asset->next_audit_date = $dt;
if ($request->filled('next_audit_date')) {
@@ -1182,97 +1095,40 @@ class AssetsController extends Controller
$asset->last_audit_date = date('Y-m-d H:i:s');
// Set up the payload for re-display in the API response
$payload = [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
'note' => $request->input('note'),
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
];
/**
* Update custom fields in the database.
* Validation for these fields is handled through the AssetRequest form request
* $model = AssetModel::find($request->get('model_id'));
*/
if (($asset->model) && ($asset->model->fieldset)) {
$payload['custom_fields'] = [];
foreach ($asset->model->fieldset->fields as $field) {
if (($field->display_audit=='1') && ($request->has($field->db_column))) {
if ($field->field_encrypted == '1') {
if (Gate::allows('assets.view.encrypted_custom_fields')) {
if (is_array($request->input($field->db_column))) {
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
} else {
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
}
}
} else {
if (is_array($request->input($field->db_column))) {
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
} else {
$asset->{$field->db_column} = $request->input($field->db_column);
}
}
$payload['custom_fields'][$field->db_column] = $request->input($field->db_column);
}
}
}
// Invoke the validation to see if the audit will complete successfully
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
// Validate the rest of the data before we turn off the event dispatcher
if ($asset->isInvalid()) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag' => $asset->asset_tag], $asset->getErrors()));
}
/**
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
* the audit log entry we're creating through this controller.
*
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
* will bypass normal model-level validation that's usually handled at the observer)
*
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
* which manually invokes Watson Validating to make sure the asset's model is valid.
*
* @see \App\Observers\AssetObserver::updating()
* @see \App\Models\Asset::save()
*/
$asset->unsetEventDispatcher();
/**
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
* We have to invoke this manually because of the unsetEventDispatcher() above.)
*/
if ($asset->isValid() && $asset->save()) {
$asset->logAudit(request('note'), request('location_id'), null, $originalValues);
return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('admin/hardware/message.audit.success')));
$asset->logAudit(request('note'), request('location_id'));
return response()->json(Helper::formatStandardApiResponse('success', [
'asset_tag' => e($asset->asset_tag),
'note' => e($request->input('note')),
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
], trans('admin/hardware/message.audit.success')));
}
// Asset failed validation or was not able to be saved
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag' => e($asset->asset_tag),
'error' => $asset->getErrors()->first(),
], trans('admin/hardware/message.audit.error', ['error' => $asset->getErrors()->first()])), 200);
}
// No matching asset for the asset tag that was passed.
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag' => e($request->input('asset_tag')),
'error' => trans('admin/hardware/message.audit.error'),
], trans('admin/hardware/message.audit.error', ['error' => trans('admin/hardware/message.does_not_exist')])), 200);
}
/**
* Show Requestable Assets
* Returns JSON listing of all requestable assets
*
* @group Assets
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
@@ -1359,41 +1215,18 @@ class AssetsController extends Controller
}
/**
* List Assets Assigned to Asset
*
* @group Assets
*/
public function assignedAssets(Request $request, Asset $asset) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $asset);
$query = Asset::where([
'assigned_to' => $asset->id,
'assigned_type' => Asset::class,
]);
$total = $query->count();
$assets = $query->applyOffsetAndLimit($total)->get();
return (new AssetsTransformer)->transformAssets($assets, $total);
return [];
// to do
}
/**
* List Accessories Assigned to Asset
*
* @group Assets
*/
public function assignedAccessories(Request $request, Asset $asset) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $asset);
$accessory_checkouts = AccessoryCheckout::AssetsAssigned()
->where('assigned_to', $asset->id)
->with('adminuser')
->with('accessories');
$accessory_checkouts = AccessoryCheckout::AssetsAssigned()->with('adminuser')->with('accessories');
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
$limit = app('api_limit_value');
@@ -1402,30 +1235,11 @@ class AssetsController extends Controller
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
}
/**
* List Components Assigned to Asset
*
* @group Assets
*/
public function assignedComponents(Request $request, Asset $asset): JsonResponse|array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $asset);
$asset->loadCount('components');
$total = $asset->components_count;
$components = $asset->load(['components' => fn($query) => $query->applyOffsetAndLimit($total)])->components;
return (new ComponentsTransformer)->transformComponents($components, $total);
}
/**
* Generate Label by Asset Tag
*
* @group Assets
* Generate asset labels by tag
*
* @author [Nebelkreis] [https://github.com/NebelKreis]
*
* @param Request $request Contains asset_tags array of asset tags to generate labels for
* @return JsonResponse Returns base64 encoded PDF on success, error message on failure
*/

View File

@@ -15,9 +15,8 @@ use Illuminate\Support\Facades\Storage;
class CategoriesController extends Controller
{
/**
* List Categories
* Display a listing of the resource.
*
* @group Categories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
@@ -39,10 +38,7 @@ class CategoriesController extends Controller
'consumables_count',
'components_count',
'licenses_count',
'created_at',
'updated_at',
'image',
'notes',
];
$categories = Category::select([
@@ -56,29 +52,11 @@ class CategoriesController extends Controller
'require_acceptance',
'checkin_email',
'image',
'notes',
])
->with('adminuser')
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count');
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count');
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$categories->ByFilter($filter);
} elseif ($request->filled('search')) {
$categories->TextSearch($request->input('search'));
}
/*
* This checks to see if we should override the Admin Setting to show archived assets in list.
* We don't currently use it within the Snipe-IT GUI, but will be useful for API integrations where they
@@ -92,6 +70,10 @@ class CategoriesController extends Controller
$categories = $categories->withCount('showableAssets as assets_count');
}
if ($request->filled('search')) {
$categories = $categories->TextSearch($request->input('search'));
}
if ($request->filled('name')) {
$categories->where('name', '=', $request->input('name'));
}
@@ -149,9 +131,8 @@ class CategoriesController extends Controller
/**
* Create Category
* Store a newly created resource in storage.
*
* @group Categories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
@@ -173,9 +154,8 @@ class CategoriesController extends Controller
}
/**
* Show Category
* Display the specified resource.
*
* @group Categories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
@@ -190,9 +170,8 @@ class CategoriesController extends Controller
/**
* Update Category
* Update the specified resource in storage.
*
* @group Categories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
@@ -221,9 +200,8 @@ class CategoriesController extends Controller
}
/**
* Delete Category
* Remove the specified resource from storage.
*
* @group Categories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
@@ -232,7 +210,7 @@ class CategoriesController extends Controller
public function destroy($id) : JsonResponse
{
$this->authorize('delete', Category::class);
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($id);
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count')->findOrFail($id);
if (! $category->isDeletable()) {
return response()->json(
@@ -246,9 +224,8 @@ class CategoriesController extends Controller
/**
* Selectlist
* Gets a paginated collection for the select2 menus
*
* @group Categories
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer

View File

@@ -1,54 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
use App\Exceptions\AssetNotRequestable;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\JsonResponse;
use Exception;
class CheckoutRequest extends Controller
{
/**
* Store Asset Request
*
* @group Account
*/
public function store(Asset $asset): JsonResponse
{
try {
CreateCheckoutRequestAction::run($asset, auth()->user());
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.success')));
} catch (AssetNotRequestable $e) {
return response()->json(Helper::formatStandardApiResponse('error', 'Asset is not requestable'));
} catch (AuthorizationException $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
} catch (Exception $e) {
report($e);
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
}
}
/**
* Cancel Asset Request
*
* @group Account
*/
public function destroy(Asset $asset): JsonResponse
{
try {
CancelCheckoutRequestAction::run($asset, auth()->user());
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled')));
} catch (AuthorizationException $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
} catch (Exception $e) {
report($e);
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
}
}
}

View File

@@ -15,9 +15,8 @@ use Illuminate\Http\JsonResponse;
class CompaniesController extends Controller
{
/**
* List Companies
* Display a listing of the resource.
*
* @group Companies
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
@@ -39,15 +38,11 @@ class CompaniesController extends Controller
'accessories_count',
'consumables_count',
'components_count',
'notes',
];
$companies = Company::withCount(['assets as assets_count' => function ($query) {
$query->AssetsForShow();
}])
->with('adminuser')
->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
}])->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
if ($request->filled('search')) {
$companies->TextSearch($request->input('search'));
@@ -91,12 +86,11 @@ class CompaniesController extends Controller
/**
* Create Company
* Store a newly created resource in storage.
*
* @group Companies
* @param \App\Http\Requests\ImageUploadRequest $request
*@since [v4.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
*/
public function store(ImageUploadRequest $request) : JsonResponse
{
@@ -114,37 +108,33 @@ class CompaniesController extends Controller
}
/**
* Show Company
* Display the specified resource.
*
* @group Companies
* @param int $id
* @since [v4.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
*/
public function show($id) : array
{
$this->authorize('view', Company::class);
$company = Company::findOrFail($id);
$this->authorize('view', $company);
return (new CompaniesTransformer)->transformCompany($company);
}
/**
* Update Company
* Update the specified resource in storage.
*
* @group Companies
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
* @param int $id
*@author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function update(ImageUploadRequest $request, $id) : JsonResponse
{
$this->authorize('update', Company::class);
$company = Company::findOrFail($id);
$this->authorize('update', $company);
$company->fill($request->all());
$company = $request->handleImages($company);
@@ -158,12 +148,11 @@ class CompaniesController extends Controller
}
/**
* Delete Company
* Remove the specified resource from storage.
*
* @group Companies
* @param int $id
* @since [v4.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
*/
public function destroy($id) : JsonResponse
{
@@ -182,9 +171,8 @@ class CompaniesController extends Controller
}
/**
* Selectlist
* Gets a paginated collection for the select2 menus
*
* @group Companies
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
@@ -199,7 +187,6 @@ class CompaniesController extends Controller
'companies.image',
]);
if ($request->filled('search')) {
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
}

Some files were not shown because too many files have changed in this diff Show More