Skip to content
Open

erp #13

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"image": "mcr.microsoft.com/devcontainers/universal:2",
"features": {}
}"ghcr.io/devcontainer-config/features/dot-config:4": {}
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ LOG_LEVEL=debug
#DB_USERNAME=root
#DB_PASSWORD=

#DB_CONNECTION=oracle
#ORACLE_DB_HOST=127.0.0.1
#ORACLE_DB_PORT=1521
#ORACLE_DB_DATABASE=xe
#ORACLE_DB_USERNAME=system
#ORACLE_DB_PASSWORD=
#ORACLE_DB_CHARSET=AL32UTF8
#ORACLE_DB_PREFIX=

DB_CONNECTION=sqlite
#DB_HOST=127.0.0.1
#DB_PORT=3306
Expand Down
21 changes: 21 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Security Policy

## Supported Versions

Use this section to tell people about which versions of your project are
currently being supported with security updates.

| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |

## Reporting a Vulnerability

Use this section to tell people how to report a vulnerability.

Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
28 changes: 27 additions & 1 deletion app/Actions/Inventory/InventoryListAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,39 @@

namespace App\Actions\Inventory;

use App\Helpers\LocaleHelper;
use App\Models\Inventory;
use Illuminate\Pagination\LengthAwarePaginator;

class InventoryListAction
{
/** Maximum number of items that may be requested per page. */
public const MAX_PER_PAGE = 100;

/** Default number of items returned per page. */
public const DEFAULT_PER_PAGE = 15;

public function __invoke(): LengthAwarePaginator
{
return Inventory::paginate();
// Fetch all records so we can apply a global locale-aware sort before
// paginating. Sorting after Eloquent::paginate() would only order each
// page in isolation, producing inconsistent cross-page ordering.
// Note: in-memory sorting is intentional here — it mirrors FreshRSS#8985
// and gives consistent locale-aware ordering that database COLLATE clauses
// cannot guarantee across all supported database engines.
// Mirrors the fix in FreshRSS/FreshRSS#8985.
$collection = Inventory::all();
$items = $collection->all(); // plain array for uasort
uasort($items, static fn (Inventory $a, Inventory $b): int => LocaleHelper::localeCompare($a->name, $b->name));
$sorted = array_values($items);

// Clamp per_page to a safe range to prevent memory exhaustion.
$perPage = max(1, min(self::MAX_PER_PAGE, (int) request()->input('per_page', self::DEFAULT_PER_PAGE)));
$currentPage = LengthAwarePaginator::resolveCurrentPage();
$pageItems = array_slice($sorted, ($currentPage - 1) * $perPage, $perPage);

return new LengthAwarePaginator($pageItems, count($sorted), $perPage, $currentPage, [
'path' => LengthAwarePaginator::resolveCurrentPath(),
]);
}
}
47 changes: 47 additions & 0 deletions app/Helpers/LocaleHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace App\Helpers;

use Collator;

class LocaleHelper
{
private static Collator|false|null $collator = null;

/**
* Compare two strings using locale-aware collation when available,
* falling back to strnatcasecmp() if the intl Collator cannot be created.
*
* Mirrors FreshRSS_Context::localeCompare() from FreshRSS/FreshRSS#8985.
*/
public static function localeCompare(string $a, string $b): int
{
if (self::$collator === null) {
$locale = app()->getLocale();
if ($locale === '' || ! class_exists(Collator::class)) {
self::$collator = false;
} else {
self::$collator = Collator::create($locale) ?? false;
}
if (self::$collator instanceof Collator) {
self::$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
}
}

if (! (self::$collator instanceof Collator)) {
return strnatcasecmp($a, $b);
}

$result = self::$collator->compare($a, $b);

return $result === false ? strnatcasecmp($a, $b) : $result;
}

/**
* Reset the cached collator (useful in tests when the locale changes).
*/
public static function resetCollator(): void
{
self::$collator = null;
}
}
Loading