Compare commits

...

66 Commits

Author SHA1 Message Date
github-actions[bot]
7110556ef0 Merge pull request #11729 from firefly-iii/release-1771170628
🤖 Automatically merge the PR into the develop branch.
2026-02-15 16:50:35 +01:00
JC5
3e1b703dfc 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 16:50:28 +01:00
James Cole
fce0750509 Remove unnecessary package. 2026-02-15 16:43:35 +01:00
github-actions[bot]
f5385d0229 Merge pull request #11727 from firefly-iii/release-1771154935
🤖 Automatically merge the PR into the develop branch.
2026-02-15 12:29:02 +01:00
JC5
8599c4a3c1 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 12:28:55 +01:00
James Cole
d210e7dcdd Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-15 12:24:46 +01:00
James Cole
8de9e0ba29 Remove checks for null 2026-02-15 12:24:33 +01:00
github-actions[bot]
7fa9e79f2a Merge pull request #11726 from firefly-iii/release-1771154592
🤖 Automatically merge the PR into the develop branch.
2026-02-15 12:23:20 +01:00
JC5
4a5281fd80 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 12:23:13 +01:00
James Cole
ad60974430 Update command so it is really the first attempt to connect. 2026-02-15 12:19:10 +01:00
James Cole
195794881b Catch nulls in dates. 2026-02-15 12:11:25 +01:00
James Cole
3e6a997dd5 Catch preferences with no date info. 2026-02-15 12:09:57 +01:00
James Cole
2eedfd9f26 Merge branch 'main' into develop 2026-02-15 12:06:20 +01:00
James Cole
9ec0515bb6 Fix string pointer 2026-02-15 12:06:05 +01:00
James Cole
6b197eecb9 Remove PR update rule from mergify configuration
Removed the rule for automatically updating PRs before merging.

Signed-off-by: James Cole <james@firefly-iii.org>
2026-02-15 11:58:07 +01:00
github-actions[bot]
591c970882 Merge pull request #11725 from firefly-iii/release-1771152972
🤖 Automatically merge the PR into the develop branch.
2026-02-15 11:56:22 +01:00
JC5
15d91dbe1b 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 11:56:12 +01:00
James Cole
6ff87bf447 Add command that first verifies the database connection. Saves some time booting up. 2026-02-15 11:51:04 +01:00
James Cole
147ce154d8 Remove unused file. 2026-02-15 11:25:26 +01:00
James Cole
2c8be33000 Clean up routes and API calls. 2026-02-15 11:25:12 +01:00
github-actions[bot]
b472890c84 Merge pull request #11724 from firefly-iii/release-1771138148
🤖 Automatically merge the PR into the develop branch.
2026-02-15 07:49:18 +01:00
JC5
2fabcf5193 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 07:49:08 +01:00
James Cole
5ed4e7aa79 Clean up kernel files and middleware. 2026-02-15 07:41:31 +01:00
James Cole
973caad7e4 Clean up providers 2026-02-15 07:27:28 +01:00
github-actions[bot]
d273503a15 Merge pull request #11723 from firefly-iii/release-1771134641
🤖 Automatically merge the PR into the develop branch.
2026-02-15 06:50:49 +01:00
JC5
cc149adb5d 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 06:50:41 +01:00
James Cole
ac8bcb786b Fix https://github.com/firefly-iii/firefly-iii/issues/11720 2026-02-15 06:18:52 +01:00
github-actions[bot]
642deefba5 Merge pull request #11718 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 20:40:32 +01:00
github-actions[bot]
5b13b64fd7 Merge pull request #11717 from firefly-iii/release-1771098019
🤖 Automatically merge the PR into the develop branch.
2026-02-14 20:40:27 +01:00
JC5
653a64d0a8 🤖 Auto commit for release 'v6.4.21' on 2026-02-14 2026-02-14 20:40:19 +01:00
github-actions[bot]
c9dcdc90ec Merge pull request #11716 from firefly-iii/release-1771097648
🤖 Automatically merge the PR into the develop branch.
2026-02-14 20:34:19 +01:00
JC5
1f04888331 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 20:34:08 +01:00
James Cole
28c21ecb7e Update changelog 2026-02-14 20:29:19 +01:00
github-actions[bot]
7382030e61 Merge pull request #11711 from firefly-iii/release-1771075473
🤖 Automatically merge the PR into the develop branch.
2026-02-14 14:24:39 +01:00
JC5
a573e19dbc 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 14:24:33 +01:00
James Cole
b152a2c8d9 Fix #11710 2026-02-14 14:20:24 +01:00
github-actions[bot]
7e14c36fc0 Merge pull request #11709 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 13:38:49 +01:00
github-actions[bot]
58bfc7f8ce Merge pull request #11708 from firefly-iii/release-1771072714
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:38:43 +01:00
JC5
6b8c005108 🤖 Auto commit for release 'v6.4.20' on 2026-02-14 2026-02-14 13:38:34 +01:00
James Cole
ceb660fd02 Fix missing setting. Restore 8.4 for the moment. Very professional. 2026-02-14 13:33:29 +01:00
James Cole
7fac1e8614 Remove statistics. 2026-02-14 13:25:39 +01:00
github-actions[bot]
809e34f5fe Merge pull request #11707 from firefly-iii/release-1771071314
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:15:20 +01:00
JC5
73ca57c8c4 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 13:15:14 +01:00
James Cole
63088ffeb1 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-14 13:10:54 +01:00
James Cole
4e88ebd52d Make Firefly III PHP 8.5 only. 2026-02-14 13:10:41 +01:00
github-actions[bot]
85fd38d146 Merge pull request #11706 from firefly-iii/release-1771070819
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:07:16 +01:00
JC5
e83d7051eb 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 13:06:59 +01:00
James Cole
567f2dae17 Remove problematic comment repetition by Mago. 2026-02-14 13:02:48 +01:00
github-actions[bot]
c231ae4016 Merge pull request #11705 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 12:55:26 +01:00
github-actions[bot]
fb13a4cdcb Merge pull request #11704 from firefly-iii/release-1771070112
🤖 Automatically merge the PR into the develop branch.
2026-02-14 12:55:21 +01:00
JC5
724042e1d8 🤖 Auto commit for release 'v6.4.19' on 2026-02-14 2026-02-14 12:55:12 +01:00
James Cole
84a30c3c8f Missing ltter. 2026-02-14 12:49:16 +01:00
github-actions[bot]
6c9dac831a Merge pull request #11703 from firefly-iii/release-1771069573
🤖 Automatically merge the PR into the develop branch.
2026-02-14 12:46:20 +01:00
JC5
e605ddb779 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 12:46:13 +01:00
James Cole
4b19ed8f07 Add changelog. 2026-02-14 12:41:28 +01:00
James Cole
57bd8e09d4 Fix #11702 2026-02-14 10:31:01 +01:00
github-actions[bot]
32f1a7c9c2 Merge pull request #11701 from firefly-iii/release-1771057524
🤖 Automatically merge the PR into the develop branch.
2026-02-14 09:25:32 +01:00
JC5
fc5b0db43f 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 09:25:24 +01:00
James Cole
c2721f3f48 Fix https://github.com/firefly-iii/firefly-iii/issues/11700 2026-02-14 08:18:53 +01:00
James Cole
96291c9bce Fix https://github.com/firefly-iii/firefly-iii/issues/11684 2026-02-14 08:17:19 +01:00
James Cole
ab9400aaee Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-13 08:05:02 +01:00
James Cole
31d1ee11cb Fix https://github.com/firefly-iii/firefly-iii/issues/11694 2026-02-13 08:04:39 +01:00
github-actions[bot]
694dc3816c Merge pull request #11699 from firefly-iii/release-1770965983
🤖 Automatically merge the PR into the develop branch.
2026-02-13 07:59:49 +01:00
JC5
c647fa7b94 🤖 Auto commit for release 'develop' on 2026-02-13 2026-02-13 07:59:43 +01:00
James Cole
022d59bfdd Fix https://github.com/firefly-iii/firefly-iii/issues/11688 2026-02-13 07:33:30 +01:00
James Cole
057b82b471 fix rename of rules when bill name changes. 2026-02-11 07:00:59 +01:00
56 changed files with 1061 additions and 764 deletions

View File

@@ -402,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.93.1",
"version": "v3.94.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a"
"reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/883b20fb38c7866de9844ab6d0a205c423bde2d4",
"reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4",
"shasum": ""
},
"require": {
@@ -428,7 +428,7 @@
"react/event-loop": "^1.5",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0",
"symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
"symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
@@ -442,18 +442,18 @@
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.32",
"justinrainbow/json-schema": "^6.6",
"facile-it/paraunit": "^1.3.1 || ^2.7.1",
"infection/infection": "^0.32.3",
"justinrainbow/json-schema": "^6.6.4",
"keradus/cli-executor": "^2.3",
"mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.9",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.48",
"php-coveralls/php-coveralls": "^2.9.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.7",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.7",
"phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.51",
"symfony/polyfill-php85": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0",
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0"
"symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.4",
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.1"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@@ -494,7 +494,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.93.1"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.0"
},
"funding": [
{
@@ -502,7 +502,7 @@
"type": "github"
}
],
"time": "2026-01-28T23:50:50+00:00"
"time": "2026-02-11T16:44:33+00:00"
},
{
"name": "psr/container",
@@ -1185,29 +1185,29 @@
},
{
"name": "sebastian/diff",
"version": "7.0.0",
"version": "8.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "7ab1ea946c012266ca32390913653d844ecd085f"
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f",
"reference": "7ab1ea946c012266ca32390913653d844ecd085f",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
"shasum": ""
},
"require": {
"php": ">=8.3"
"php": ">=8.4"
},
"require-dev": {
"phpunit/phpunit": "^12.0",
"phpunit/phpunit": "^13.0",
"symfony/process": "^7.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "7.0-dev"
"dev-main": "8.0-dev"
}
},
"autoload": {
@@ -1240,15 +1240,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/7.0.0"
"source": "https://github.com/sebastianbergmann/diff/tree/8.0.0"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/diff",
"type": "tidelift"
}
],
"time": "2025-02-07T04:55:46+00:00"
"time": "2026-02-06T04:42:27+00:00"
},
{
"name": "symfony/console",

7
.github/mergify.yml vendored
View File

@@ -1,11 +1,4 @@
pull_request_rules:
- name: Make sure PR are up to date before merging
description: This automatically updates PRs when they are out-of-date with the
base branch to avoid semantic conflicts (next step is using a merge
queue).
conditions: []
actions:
update:
- name: Close all on main
conditions:
- base=main

View File

@@ -63,6 +63,9 @@ class UpdateRequest extends FormRequest
{
/** @var TransactionCurrency $currency */
$currency = $this->route()->parameter('currency_code');
if (is_string($currency)) {
$currency = TransactionCurrency::whereCode($currency)->first();
}
return [
'name' => sprintf('min:1|max:255|unique:transaction_currencies,name,%d', $currency->id),

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Console\Commands\Tools\VerifiesDatabaseConnectionTrait;
use Illuminate\Console\Command;
use PDO;
use PDOException;
@@ -32,6 +33,7 @@ use PDOException;
class CreatesDatabase extends Command
{
use ShowsFriendlyMessages;
use VerifiesDatabaseConnectionTrait;
protected $description = 'Tries to create the database if it doesn\'t exist yet.';
@@ -39,21 +41,27 @@ class CreatesDatabase extends Command
public function handle(): int
{
if ('mysql' !== env('DB_CONNECTION')) { // @phpstan-ignore larastan.noEnvCallsOutsideOfConfig */
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
$connected = $this->verifyDatabaseConnection();
if (!$connected) {
$this->friendlyError('Failed to connect to the database. Is it up?');
return Command::FAILURE;
}
if ('mysql' !== config('database.default')) { // @phpstan-ignore larastan.noEnvCallsOutsideOfConfig */
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', config('database.default')));
return 0;
}
// try to set up a raw connection:
$exists = false;
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST'), env('DB_PORT'));
$exists = false;
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST'), env('DB_PORT'));
if ('' !== (string) env('DB_SOCKET')) {
$dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET'));
}
$this->friendlyLine(sprintf('DSN is %s', $dsn));
$options = [
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
@@ -71,7 +79,7 @@ class CreatesDatabase extends Command
// only continue when no error.
// with PDO, try to list DB's (
/** @var array $stmt */
$stmt = $pdo->query('SHOW DATABASES;');
$stmt = $pdo->query('SHOW DATABASES;');
// slightly more complex but less error-prone.
foreach ($stmt as $row) {
$name = $row['Database'] ?? false;

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* VerifiesDatabaseConnection.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Tools;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
class VerifiesDatabaseConnection extends Command
{
use ShowsFriendlyMessages;
use VerifiesDatabaseConnectionTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:verify-database-connection';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command tries to connect to the database.';
/**
* Execute the console command.
*/
public function handle(): int
{
$connected = $this->verifyDatabaseConnection();
if ($connected) {
$this->friendlyPositive('Connected to the database.');
return Command::SUCCESS;
}
$this->friendlyError('Failed to connect to the database. Is it up?');
return Command::FAILURE;
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/*
* VerifiesDatabaseConnectionTrait.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Tools;
use Exception;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
trait VerifiesDatabaseConnectionTrait
{
protected function verifyDatabaseConnection(): bool
{
$loops = 30;
$loop = 0;
$queries = ['pgsql' => 'SELECT * FROM pg_catalog.pg_tables;', 'sqlite' => 'SELECT name FROM sqlite_schema;', 'mysql' => 'SHOW TABLES;'];
$default = config('database.default');
if (!array_key_exists($default, $queries)) {
$this->friendlyWarning(sprintf('Cannot validate database connection for "%s"', $default));
return true;
}
$query = $queries[$default];
$connected = false;
Log::debug(sprintf('Connecting to database "%s"...', config('database.default')));
while (!$connected && $loop < $loops) {
try {
DB::select($query);
$connected = true;
} catch (QueryException $e) {
Log::error(sprintf('Loop #%d: connection failed: %s', $loop, $e->getMessage()));
$this->friendlyWarning(sprintf('Database connection attempt #%d failed. Sleep for 10 seconds...', $loop + 1));
sleep(10);
} catch (Exception $e) {
Log::error(sprintf('Loop #%d: not connected yet because of a %s: %s', $loop, get_class($e), $e->getMessage()));
$this->friendlyWarning(sprintf('Database connection attempt #%d failed. Sleep for 10 seconds...', $loop + 1));
sleep(10);
}
++$loop;
}
return $connected;
}
}

View File

@@ -94,6 +94,7 @@ class UpgradesDatabase extends Command
private function callInitialCommands(): void
{
$this->call('firefly-iii:verify-database-connection');
$this->call('migrate', ['--seed' => true, '--force' => true, '--no-interaction' => true]);
$this->call('upgrade:600-pgsql-sequences');
$this->call('upgrade:480-decrypt-all');

View File

@@ -1,69 +0,0 @@
<?php
/**
* Kernel.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Log;
use Override;
/**
* File to make sure commands work.
*/
class Kernel extends ConsoleKernel
{
/**
* Register the commands for the application.
*/
#[Override]
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
/**
* Define the application's command schedule.
*/
#[Override]
protected function schedule(Schedule $schedule): void
{
$schedule->call(static function (): void {
Log::error('Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://docs.firefly-iii.org/');
echo "\n";
echo '------------';
echo "\n";
echo wordwrap('Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions here:');
echo "\n";
echo 'https://docs.firefly-iii.org/';
echo "\n\n";
echo 'Disable this cron job!';
echo "\n";
echo '------------';
echo "\n";
})->daily();
}
}

View File

@@ -1,8 +1,10 @@
<?php
declare(strict_types=1);
/*
* api-noauth.php
* Copyright (c) 2021 james@firefly-iii.org
* UpdatedExistingBill.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,20 +22,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;
use Illuminate\Support\Facades\Route;
// Cron job API routes:
use FireflyIII\Http\Middleware\AcceptHeaders;
use FireflyIII\Events\Event;
use FireflyIII\Models\Bill;
use Illuminate\Queue\SerializesModels;
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => '',
'as' => 'api.v1.cron.',
'middleware' => [AcceptHeaders::class],
],
static function (): void {
Route::get('{cliToken}', ['uses' => 'CronController@cron', 'as' => 'index']);
}
);
class UpdatedExistingBill extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public Bill $bill,
public array $oldData
) {}
}

View File

@@ -36,13 +36,13 @@ class ConvertsAmountToPrimaryAmount
$primaryAmountField = $params->primaryAmountField;
if (!Amount::convertToPrimary($params->user)) {
Log::debug(sprintf(
'User does not want to do conversion, no need to convert "%s" and store it in field "%s" for %s #%d.',
$params->amountField,
$params->primaryAmountField,
get_class($params->model),
$params->model->id
));
// Log::debug(sprintf(
// 'User does not want to do conversion, no need to convert "%s" and store it in field "%s" for %s #%d.',
// $params->amountField,
// $params->primaryAmountField,
// get_class($params->model),
// $params->model->id
// ));
$params->model->{$primaryAmountField} = null;
$params->model->saveQuietly();

View File

@@ -115,6 +115,7 @@ class ConfigurationController extends Controller
FireflyConfig::set('enable_external_map', $data['enable_external_map']);
FireflyConfig::set('enable_external_rates', $data['enable_external_rates']);
FireflyConfig::set('allow_webhooks', $data['allow_webhooks']);
FireflyConfig::set('enable_batch_processing', $data['enable_batch_processing']);
FireflyConfig::set('valid_url_protocols', $data['valid_url_protocols']);
FireflyConfig::set('is_demo_site', $data['is_demo_site']);

View File

@@ -190,6 +190,14 @@ class LoginController extends Controller
*/
public function showLoginForm(Request $request): Factory|Redirector|RedirectResponse|View
{
if ('remote_user_guard' === config('auth.defaults.guard')) {
$message = sprintf(
'Firefly III is configured to use the "remote user guard", but was unable to link you to a user. Are you sure the "%s" header is in place?',
config('auth.guard_header')
);
return view('errors.error', ['message' => $message]);
}
Log::channel('audit')->info('Show login form (1.1).');
$count = DB::table('users')->count();

View File

@@ -80,6 +80,14 @@ abstract class Controller extends BaseController
View::share('FF_VERSION', config('firefly.version'));
View::share('FF_BUILD_TIME', config('firefly.build_time'));
// this breaks when running < php 8.5 and is totally intentional.
// $input = ' James is cool';
// $output = $input
// |> trim(...)
// |> (fn (string $string) => str_replace(' ', '-', $string))
// |> (fn (string $string) => str_replace(['.', '/', '…'], '', $string))
// |> strtolower(...);
// is webhooks enabled?
View::share(
'featuringWebhooks',

View File

@@ -103,10 +103,11 @@ class IndexController extends Controller
$firstJournal = $this->repository->firstNull();
$startPeriod = $firstJournal instanceof TransactionJournal ? $firstJournal->date : new Carbon();
$endPeriod = clone $end;
// limit to 3 years for the time being.
if (now()->diffInYears($startPeriod, true) > 3) {
$startPeriod = now()->subYears(3);
$endPeriod->endOfDay();
// limit to 6 years for the time being.
$max = 6;
if (now()->diffInYears($startPeriod, true) > $max) {
$startPeriod = now()->subYears($max);
}
$periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod);

View File

@@ -1,65 +0,0 @@
<?php
/**
* Kernel.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http;
use FireflyIII\Http\Middleware\Authenticate;
use FireflyIII\Http\Middleware\Binder;
use FireflyIII\Http\Middleware\InstallationId;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\TrimStrings;
use FireflyIII\Http\Middleware\TrustProxies;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\View\Middleware\ShareErrorsFromSession;
/**
* Class Kernel
*/
class Kernel extends HttpKernel
{
protected $middleware = [
// SecureHeaders::class,
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
TrustProxies::class,
InstallationId::class,
];
protected $middlewareAliases = [
'auth' => Authenticate::class,
'auth.basic' => AuthenticateWithBasicAuth::class,
'bindings' => Binder::class,
'can' => Authorize::class,
'guest' => RedirectIfAuthenticated::class,
'throttle' => ThrottleRequests::class,
];
protected $middlewarePriority = [StartFireflySession::class, ShareErrorsFromSession::class, Authenticate::class, Binder::class, Authorize::class];
}

View File

@@ -82,22 +82,25 @@ class Authenticate
protected function authenticate($request, array $guards)
{
if (0 === count($guards)) {
// go for default guard:
// @noinspection PhpUndefinedMethodInspection
if ($this->auth->check()) {
// do an extra check on user object.
/** @noinspection PhpUndefinedMethodInspection */
/** @var User $user */
$user = $this->auth->authenticate();
Log::debug('in Authenticate::authenticate() with zero guards.');
// There are no guards defined, go for the default guard:
if (auth()->check()) {
Log::debug('User is authenticated.');
$user = auth()->user();
$this->validateBlockedUser($user, $guards);
}
return;
}
// @noinspection PhpUndefinedMethodInspection
return $this->auth->authenticate();
$this->auth->authenticate();
if (!$this->auth->check()) {
throw new AuthenticationException('The user is not logged in but must be.', $guards);
}
}
exit('five');
foreach ($guards as $guard) {
exit('six');
if ('api' !== $guard) {
$this->auth->guard($guard)->authenticate();
}
@@ -111,6 +114,7 @@ class Authenticate
}
}
exit('seven');
// this is a massive hack, but if the handler has the oauth exception
// at this point we can report its error instead of a generic one.
$message = 'Unauthenticated.';
@@ -143,5 +147,6 @@ class Authenticate
// @phpstan-ignore-line (thinks function is undefined)
throw new AuthenticationException('Blocked account.', $guards);
}
Log::debug(sprintf('User #%d is not blocked.', $user->id));
}
}

View File

@@ -32,7 +32,7 @@ use Override;
/**
* Class StartFireflySession.
*/
class StartFireflySession extends StartSession
class StartFireflyIIISession extends StartSession
{
/**
* Store the current URL for the request if necessary.

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* UpdatesRulesForChangedBill.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Listeners\Model\Bill;
use FireflyIII\Events\Model\Bill\UpdatedExistingBill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
class UpdatesRulesForChangedBill implements ShouldQueue
{
public function handle(UpdatedExistingBill $event): void
{
// update rule actions.
if ($event->bill->name !== $event->oldData['name']) {
$this->updateBillTriggersAndActions($event->bill, $event->oldData);
}
}
private function updateBillTriggersAndActions(Bill $bill, array $oldData): void
{
Log::debug(sprintf('Now in updateBillTriggersAndActions(#%d)', $bill->id));
$repository = app(RuleRepositoryInterface::class);
$repository->setUser($bill->user);
$rules = $repository->getAll();
/** @var Rule $rule */
foreach ($rules as $rule) {
$this->updateRule($bill, $rule, $oldData);
}
}
private function updateRule(Bill $bill, Rule $rule, array $oldData): void
{
$triggers = ['bill_is', 'bill_ends', 'bill_starts', 'bill_contains'];
/** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) {
if (in_array($trigger->trigger_type, $triggers, true) && $trigger->trigger_value === $oldData['name']) {
Log::debug(sprintf('Updated trigger #%d in rule #%d to new subscription name', $trigger->id, $rule->id));
$trigger->trigger_value = $bill->name;
$trigger->save();
}
}
/** @var RuleAction $action */
foreach ($rule->ruleActions as $action) {
if ('link_to_bill' === $action->action_type && $action->action_value === $oldData['name']) {
Log::debug(sprintf('Updated action #%d in rule #%d to new subscription name', $action->id, $rule->id));
$action->action_value = $bill->name;
$action->save();
}
}
}
}

View File

@@ -104,7 +104,9 @@ trait SupportsGroupProcessingTrait
$repository->deleteStatisticsForType(Category::class, $objects->categories, $dates);
$repository->deleteStatisticsForType(Tag::class, $objects->tags, $dates);
// remove if no stuff present:
// remove generic statistics:
$repository->deleteStatisticsForPrefix('all_', $dates);
// remove for no tag, no cat, etc.
if (0 === $objects->budgets->count()) {
Log::debug('No budgets, delete "no_category" stats.');

View File

@@ -149,12 +149,10 @@ class RecalculatesPrimaryCurrencyAmounts
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id)
->where(static function (Builder $q): void {
$q
->whereNotNull('native_amount')
->orWhereNotNull('native_foreign_amount')
->orWhere('native_amount', '!=', '')
->orWhere('native_foreign_amount', '!=', '')
;
$q->whereNotNull('native_amount')->orWhereNotNull('native_foreign_amount');
if ('pgsql' !== config('database.default')) {
$q->orWhere('native_amount', '!=', '')->orWhere('native_foreign_amount', '!=', '');
}
})
->update(['native_amount' => null, 'native_foreign_amount' => null])
;

View File

@@ -34,10 +34,6 @@ use Laravel\Passport\Passport;
*/
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
// 'FireflyIII\Model' => 'FireflyIII\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*

View File

@@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Providers;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use Illuminate\Session\SessionManager;
use Illuminate\Support\ServiceProvider;
use Override;
@@ -43,7 +43,7 @@ class FireflySessionProvider extends ServiceProvider
$this->registerSessionDriver();
$this->app->singleton(StartFireflySession::class);
$this->app->singleton(StartFireflyIIISession::class);
}
/**

View File

@@ -42,20 +42,18 @@ class RouteServiceProvider extends ServiceProvider
#[Override]
public function boot(): void
{
$this->routes(function (): void {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'))
;
Route::prefix('api/v1/cron')
->middleware('api_basic')
->namespace($this->namespace)
->group(base_path('routes/api-noauth.php'))
;
Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
});
// $this->routes(function (): void {
// Route::prefix('api')
// ->middleware('api')
// ->namespace($this->namespace)
// ->group(base_path('routes/api.php'))
// ;
// Route::prefix('api/v1/cron')
// ->middleware('api_basic')
// ->namespace($this->namespace)
// ->group(base_path('routes/api-noauth.php'))
// ;
// Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
// });
}
}

View File

@@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Providers;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use Illuminate\Session\SessionServiceProvider as BaseSessionServiceProvider;
use Override;
@@ -42,6 +42,6 @@ class SessionServiceProvider extends BaseSessionServiceProvider
$this->registerSessionDriver();
$this->app->singleton(StartFireflySession::class);
$this->app->singleton(StartFireflyIIISession::class);
}
}

View File

@@ -96,7 +96,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
foreach ($set as $bill) {
if ($bill->order !== $current) {
$bill->order = $current;
$bill->save();
$bill->saveQuietly();
}
++$current;
}
@@ -613,7 +613,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
public function setOrder(Bill $bill, int $order): void
{
$bill->order = $order;
$bill->save();
$bill->saveQuietly();
}
/**

View File

@@ -49,6 +49,9 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
#[Override]
public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection
{
Log::debug(sprintf('Collect all statistics where type starts with "%s"', $prefix));
Log::debug(sprintf('Between %s and %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
return $this->userGroup
->periodStatistics()
->where('type', 'LIKE', sprintf('%s%%', $prefix))
@@ -156,6 +159,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
int $count,
string $amount
): PeriodStatistic {
Log::debug(sprintf('Store as type "%s"', sprintf('%s_%s', $prefix, $type)));
$stat = new PeriodStatistic();
$stat->transaction_currency_id = $currencyId;
$stat->user_group_id = $this->getUserGroup()->id;

View File

@@ -102,7 +102,7 @@ class RuleRepository implements RuleRepositoryInterface, UserGroupInterface
{
return $this->user
->rules()
->with(['ruleGroup'])
->with(['ruleGroup', 'ruleTriggers', 'ruleActions'])
->get()
;
}

View File

@@ -37,6 +37,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Rules\UniqueIban;
use FireflyIII\Support\NullArrayObject;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Support\Facades\Log;
use Safe\Exceptions\JsonException;
@@ -227,7 +228,12 @@ trait JournalServiceTrait
$set = array_unique($set);
Log::debug('End of loop.');
Log::debug(sprintf('Total nr. of tags: %d', count($tags)), $tags);
$journal->tags()->sync($set);
try {
$journal->tags()->sync($set);
} catch (UniqueConstraintViolationException $e) {
Log::error(sprintf('Firefly III could not sync tags: %s', $e->getMessage()));
}
}
/**

View File

@@ -24,12 +24,12 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Update;
use FireflyIII\Events\Model\Bill\UpdatedExistingBill;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\Bill;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
use FireflyIII\Services\Internal\Support\BillServiceTrait;
use FireflyIII\Support\Facades\Amount;
@@ -50,6 +50,7 @@ class BillUpdateService
public function update(Bill $bill, array $data): Bill
{
$this->user = $bill->user;
$oldData = $bill->toArray();
if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) {
$factory = app(TransactionCurrencyFactory::class);
@@ -68,13 +69,6 @@ class BillUpdateService
$bill = $this->updateBillProperties($bill, $data);
$bill->save();
$bill->refresh();
// old values
$oldData = [
'name' => $bill->name,
'amount_min' => $bill->amount_min,
'amount_max' => $bill->amount_max,
'transaction_currency_name' => $bill->transactionCurrency->name,
];
// update note:
if (array_key_exists('notes', $data)) {
$this->updateNote($bill, (string) $data['notes']);
@@ -90,12 +84,6 @@ class BillUpdateService
}
}
// update rule actions.
if (array_key_exists('name', $data)) {
$this->updateBillActions($bill, $oldData['name'], $data['name']);
$this->updateBillTriggers($bill, $oldData, $data);
}
// update using name:
if (array_key_exists('object_group_title', $data)) {
$objectGroupTitle = $data['object_group_title'] ?? '';
@@ -127,6 +115,7 @@ class BillUpdateService
$bill->objectGroups()->sync([]);
$bill->save();
}
event(new UpdatedExistingBill($bill, $oldData));
return $bill;
}
@@ -181,39 +170,6 @@ class BillUpdateService
return $bill;
}
private function updateBillTriggers(Bill $bill, array $oldData, array $newData): void
{
Log::debug(sprintf('Now in updateBillTriggers(%d, "%s")', $bill->id, $bill->name));
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$repository->setUser($bill->user);
$rules = $repository->getRulesForBill($bill);
if (0 === $rules->count()) {
Log::debug('Found no rules.');
return;
}
Log::debug(sprintf('Found %d rules', $rules->count()));
$fields = [
'name' => 'description_contains',
'amount_min' => 'amount_more',
'amount_max' => 'amount_less',
'transaction_currency_name' => 'currency_is',
];
foreach ($fields as $field => $ruleTriggerKey) {
if (!array_key_exists($field, $newData)) {
continue;
}
if ($oldData[$field] === $newData[$field]) {
Log::debug(sprintf('Field %s is unchanged ("%s"), continue.', $field, $oldData[$field]));
continue;
}
$this->updateRules($rules, $ruleTriggerKey, $oldData[$field], $newData[$field]);
}
}
private function updateOrder(Bill $bill, int $oldOrder, int $newOrder): void
{
if ($newOrder > $oldOrder) {

View File

@@ -31,6 +31,7 @@ use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
/**
@@ -40,6 +41,7 @@ class RemoteUserGuard implements Guard
{
protected Application $application;
protected ?User $user = null;
private $tried = false;
/**
* Create a new authentication guard.
@@ -49,13 +51,19 @@ class RemoteUserGuard implements Guard
Application $app
) {
$app->get('request');
// Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri()));
Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $app->get('request')?->getMethod(), $app->get('request')?->getRequestUri()));
$this->application = $app;
}
public function authenticate(): void
{
// Log::debug(sprintf('Now at %s', __METHOD__));
$this->tried = true;
Log::debug(sprintf('Now at %s', __METHOD__));
if (App::runningInConsole()) {
Log::debug('Running in console, will not authenticate.');
return;
}
if ($this->user instanceof User) {
Log::debug(sprintf('%s is found: #%d, "%s".', $this->user::class, $this->user->id, $this->user->email));
@@ -70,10 +78,14 @@ class RemoteUserGuard implements Guard
$userID = request()->server($header) ?? apache_request_headers()[$header] ?? null;
}
// test value for development
// $userID = 'james@firefly';
if (null === $userID || '' === $userID) {
Log::error(sprintf('No user in header "%s".', $header));
throw new FireflyException('The guard header was unexpectedly empty. See the logs.');
// throw new FireflyException('The guard header was unexpectedly empty. See the logs.');
return;
}
Log::debug(sprintf('User ID found in header is "%s"', $userID));
@@ -145,8 +157,18 @@ class RemoteUserGuard implements Guard
public function user(): ?User
{
if (App::runningInConsole()) {
Log::debug('Running in console, will not authenticate.');
return null;
}
if (false === $this->tried) {
Log::debug('Have not tried authentication, do it now.');
$this->authenticate();
}
// Log::debug(sprintf('Now at %s', __METHOD__));
$user = $this->user;
if (!$user instanceof User) {
Log::debug('User is NULL');

View File

@@ -35,20 +35,8 @@ class Calculator
{
public const int DEFAULT_INTERVAL = 1;
private static ?SplObjectStorage $intervalMap = null; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private static array $intervals = [];
private static ?SplObjectStorage $intervalMap = null;
private static array $intervals = [];
public function isAvailablePeriodicity(Periodicity $periodicity): bool
{

View File

@@ -114,6 +114,38 @@ class ExportDataGenerator
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
public function __construct()
{
$this->accounts = new Collection();

View File

@@ -37,7 +37,6 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Navigation;
use FireflyIII\Support\Facades\Steam;
@@ -80,44 +79,8 @@ trait PeriodOverview
protected TagRepositoryInterface $tagRepository;
protected JournalRepositoryInterface $journalRepos;
protected PeriodStatisticRepositoryInterface $periodStatisticRepo;
private Collection $statistics; // temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
private array $transactions; // temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
private Collection $statistics;
private array $transactions;
/**
* This method returns "period entries", so nov-2015, dec-2015, etc. (this depends on the users session range)
@@ -178,6 +141,34 @@ trait PeriodOverview
return $entries;
}
protected function getGenericPeriod(string $type, string $period, Carbon $start, Carbon $end): array
{
$return = [
'title' => Navigation::periodShow($start, $period),
'route' => route('transactions.index', [$type, $start->format('Y-m-d'), $end->format('Y-m-d')]),
'total_transactions' => 0,
];
$setTypes = [
'withdrawal' => 'spent',
'expenses' => 'spent',
'deposit' => 'earned',
'revenue' => 'earned',
'transfer' => 'transferred',
'transfers' => 'transferred',
];
if (!array_key_exists($type, $setTypes)) {
throw new FireflyException(sprintf('[c] Cannot deal with type "%s"', $type));
}
$setType = $setTypes[$type];
$this->transactions = [];
$set = $this->getSingleGenericPeriodByType($start, $end, $type);
$return['total_transactions'] += $set['count'];
$return[$setType] = $set;
return $return;
}
/**
* Same as above, but for lists that involve transactions without a budget.
*
@@ -263,60 +254,18 @@ trait PeriodOverview
*/
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
{
$range = Navigation::getViewRange(true);
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('transactions-period-entries');
$cache->addProperty($transactionType);
if ($cache->has()) {
return $cache->get();
}
$this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class);
$range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
/** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range);
$entries = [];
$spent = [];
$earned = [];
$transferred = [];
// collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end);
$genericSet = $collector->getExtractedJournals();
$loops = 0;
$dates = Navigation::blockPeriods($start, $end, $range);
$entries = [];
$this->statistics = $this->periodStatisticRepo->allInRangeForPrefix('all_', $start, $end);
Log::debug(sprintf('Collected %d statistics', $this->statistics->count()));
foreach ($dates as $currentDate) {
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
if ($loops < 10) {
// set to correct array
if ('expenses' === $transactionType || 'withdrawal' === $transactionType) {
$spent = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
if ('revenue' === $transactionType || 'deposit' === $transactionType) {
$earned = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
if ('transfer' === $transactionType || 'transfers' === $transactionType) {
$transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
}
$entries[] = [
'title' => $title,
'route' => route('transactions.index', [
$transactionType,
$currentDate['start']->format('Y-m-d'),
$currentDate['end']->format('Y-m-d'),
]),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
++$loops;
$entries[] = $this->getGenericPeriod($transactionType, $currentDate['period'], $currentDate['start'], $currentDate['end']);
}
return $entries;
@@ -363,10 +312,11 @@ trait PeriodOverview
return new Collection();
}
Log::debug(sprintf('Now in filterStatistics("%s")', $type));
return $this->statistics->filter(
static fn (PeriodStatistic $statistic): bool => $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type
);
return $this->statistics->filter(static function (PeriodStatistic $statistic) use ($start, $end, $type): bool {
return $statistic->start->isSameSecond($start) && $statistic->end->isSameSecond($end) && $statistic->type === $type;
});
}
private function filterTransactionsByType(TransactionTypeEnum $type, Carbon $start, Carbon $end): array
@@ -428,21 +378,63 @@ trait PeriodOverview
return [$start, $end];
}
private function getSingleGenericPeriodByType(Carbon $start, Carbon $end, string $type): array
{
$filterType = sprintf('all_%s', $type);
$statistics = $this->filterStatistics($start, $end, $filterType);
$types = config(sprintf('firefly.transactionTypesByType.%s', $type));
// nothing found, regenerate them.
if (0 === $statistics->count()) {
if (0 === count($this->transactions)) {
// get collection!
// collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end);
$this->transactions = $collector->getExtractedJournals();
Log::debug(sprintf('Going to group %d found journal(s)', count($types)));
}
$grouped = $this->groupByCurrency($this->filterJournalsByDate($this->transactions, $start, $end));
$this->saveGroupedForPrefix('all', $start, $end, $type, $grouped);
return $grouped;
}
$grouped = ['count' => 0];
/** @var PeriodStatistic $statistic */
foreach ($statistics as $statistic) {
$id = (int) $statistic->transaction_currency_id;
$currency = Amount::getTransactionCurrencyById($id);
$grouped[$id] = [
'amount' => (string) $statistic->amount,
'count' => (int) $statistic->count,
'currency_id' => $currency->id,
'currency_name' => $currency->name,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
$grouped['count'] += (int) $statistic->count;
}
return $grouped;
}
private function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array
{
Log::debug(sprintf(
'Now in getSingleModelPeriodByType(%s #%d, %s %s, %s)',
$model::class,
$model->id,
$start->format('Y-m-d'),
$end->format('Y-m-d'),
$start->format('Y-m-d H:i:s.u'),
$end->format('Y-m-d H:i:s.u'),
$type
));
$statistics = $this->filterStatistics($start, $end, $type);
// nothing found, regenerate them.
if (0 === $statistics->count()) {
Log::debug(sprintf('Found nothing in this period for type "%s"', $type));
Log::debug(sprintf('Found nothing between %s and %s for type "%s"', $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'), $type));
if (0 === count($this->transactions)) {
switch ($model::class) {
default:
@@ -467,7 +459,7 @@ trait PeriodOverview
switch ($type) {
default:
throw new FireflyException(sprintf('Cannot deal with category period type %s', $type));
throw new FireflyException(sprintf('Cannot deal with type %s', $type));
case 'spent':
$result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end);
@@ -529,7 +521,7 @@ trait PeriodOverview
switch ($model) {
default:
throw new FireflyException(sprintf('Cannot deal with model of type "%s"', $model));
throw new FireflyException(sprintf('[b] Cannot deal with model of type "%s"', $model));
case 'budget':
// get all expenses without a budget.

View File

@@ -53,6 +53,22 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private readonly bool $convertToPrimary; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -66,6 +82,22 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $currencies = [];
private array $currencyIds = [];
private array $ids = [];

View File

@@ -53,6 +53,22 @@ class BudgetLimitEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $currencies = [];
private array $currencyIds = [];
private Carbon $end;

View File

@@ -55,6 +55,22 @@ class PiggyBankEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $accounts = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -68,6 +84,22 @@ class PiggyBankEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $amounts = [];
private Collection $collection;
private array $currencies = [];

View File

@@ -50,6 +50,22 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $accountIds = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -63,6 +79,22 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private Collection $collection;
private array $currencies = [];
private array $groupIds = [];

View File

@@ -59,6 +59,22 @@ class SubscriptionEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private readonly bool $convertToPrimary;
private ?Carbon $end = null;
private array $mappedObjects = [];

View File

@@ -79,6 +79,38 @@ class TransactionGroupEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
public function __construct()
{
$this->dateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];

View File

@@ -55,6 +55,22 @@ class WebhookEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $ids = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -68,6 +84,22 @@ class WebhookEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $responses = [];
private array $triggers = [];
private array $webhookDeliveries = [];

View File

@@ -51,8 +51,8 @@ class ObjectGroupTransformer extends AbstractTransformer
return [
'id' => (string) $objectGroup->id,
'created_at' => $objectGroup->created_at?->toAtomString(),
'updated_at' => $objectGroup->updated_at?->toAtomString(),
'created_at' => $objectGroup->created_at->toAtomString(),
'updated_at' => $objectGroup->updated_at->toAtomString(),
'title' => $objectGroup->title,
'order' => $objectGroup->order,
'links' => [['rel' => 'self', 'uri' => '/object_groups/'.$objectGroup->id]],

View File

@@ -66,8 +66,8 @@ class PiggyBankEventTransformer extends AbstractTransformer
return [
'id' => (string) $event->id,
'created_at' => $event->created_at?->toAtomString(),
'updated_at' => $event->updated_at?->toAtomString(),
'created_at' => $event->created_at->toAtomString(),
'updated_at' => $event->updated_at->toAtomString(),
'amount' => $amount,
'pc_amount' => $primaryAmount,

View File

@@ -32,11 +32,9 @@ use FireflyIII\Http\Middleware\IsAdmin;
use FireflyIII\Http\Middleware\Range;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\SecureHeaders;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\TrustProxies;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use FireflyIII\Http\Middleware\VerifyCsrfToken;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
@@ -47,7 +45,6 @@ use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance;
use Illuminate\Foundation\Http\Middleware\TrimStrings;
use Illuminate\Http\Middleware\HandleCors;
use Illuminate\Http\Middleware\ValidatePostSize;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
@@ -92,115 +89,99 @@ if (!function_exists('stringIsEqual')) {
$app = Application::configure(basePath: dirname(__DIR__))
->withRouting(
web : __DIR__ . '/../routes/web.php',
api : __DIR__ . '/../routes/api.php',
commands: __DIR__ . '/../routes/console.php',
health : '/up',
)
->withMiddleware(function (Middleware $middleware): void {
// overrule the standard middleware
$middleware->use(
[
InvokeDeferredCallbacks::class,
\Illuminate\Http\Middleware\TrustProxies::class, // use the DEFAULT middleware for this.
HandleCors::class,
PreventRequestsDuringMaintenance::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
SecureHeaders::class,
TrustProxies::class,
SecureHeaders::class, // is a Firefly III specific middleware class.
]
);
// overrule the web group
// append and extend the default "web" middleware
// to include our own custom "StartFireflyIIISession" class.
// this class in turns contains a better "previous URL" feature.
// See https://laravel.com/docs/12.x/middleware for the default list.
$middleware->group('web',
[
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
StartFireflyIIISession::class, // this is different.
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
Binder::class, // this is also different.
CreateFreshApiToken::class,
]
);
// new group?
$middleware->appendToGroup('binders-only',
[
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
Binder::class,
]);
//
$middleware->appendToGroup('user-not-logged-in', [
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Binder::class,
RedirectIfAuthenticated::class,
]);
// the default API group only contains "substitute bindings" middleware
// so here we replace the entire API group and add more sensible stuff.
$middleware->group('api',
[
AcceptHeaders::class,
// EnsureFrontendRequestsAreStateful::class,
'auth:api',
Binder::class,
]
);
$middleware->appendToGroup('api_basic', [AcceptHeaders::class, Binder::class]);
// more
$middleware->appendToGroup('user-logged-in-no-2fa', [
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Binder::class,
Authenticate::class,
]);
// simple auth
// "simple auth" means the user must be logged in and present,
// but does not have to be 2FA authenticated. This is so all users
// can always log out, for example.
$middleware->appendToGroup('user-simple-auth', [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Binder::class,
Authenticate::class,
]);
// user full auth
// This middleware is added for all routes where the user MUST have full authentication.
// this includes 2FA etc.
// incidentally, this group also includes the range middleware and the message thing.
$middleware->appendToGroup('user-full-auth', [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Authenticate::class,
MFAMiddleware::class,
Range::class,
Binder::class,
InterestingMessage::class,
CreateFreshApiToken::class,
]);
// admin
// This middleware is added to ensure that the user is not only logged in and
// authenticated (with MFA and everything), but also admin.
$middleware->appendToGroup('admin', [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Authenticate::class,
// AuthenticateTwoFactor::class,
MFAMiddleware::class,
IsAdmin::class,
Range::class,
Binder::class,
CreateFreshApiToken::class,
InterestingMessage::class,
]);
// api
$middleware->appendToGroup('api', [AcceptHeaders::class, EnsureFrontendRequestsAreStateful::class, 'auth:api,sanctum', Binder::class]);
// api basic,
$middleware->appendToGroup('api_basic', [AcceptHeaders::class, Binder::class]);
// if the user is not logged in, this group applies.
// on top of everything else of course.
$middleware->appendToGroup('user-not-logged-in', [
Installer::class,
RedirectIfAuthenticated::class,
]);
// the "binders only" group does not need or ask for authentication
// it just makes sure strings from routes are bound to objects if possible.
$middleware->group('binders-only',
[
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
Binder::class,
]);
// $middleware->priority([StartFireflyIIISession::class, ShareErrorsFromSession::class, Authenticate::class, Binder::class, Authorize::class]);
})
->withEvents(discover: [
__DIR__ . '/../app/Listeners',
@@ -225,16 +206,6 @@ $app = Application::configure(basePath: dirname(__DIR__))
|
*/
$app->singleton(
Kernel::class,
FireflyIII\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
FireflyIII\Console\Kernel::class
);
$app->singleton(
ExceptionHandler::class,
Handler::class

71
bootstrap/providers.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
/*
* providers.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use FireflyIII\Providers\AccountServiceProvider;
use FireflyIII\Providers\AdminServiceProvider;
use FireflyIII\Providers\AppServiceProvider;
use FireflyIII\Providers\AttachmentServiceProvider;
use FireflyIII\Providers\BillServiceProvider;
use FireflyIII\Providers\BudgetServiceProvider;
use FireflyIII\Providers\CategoryServiceProvider;
use FireflyIII\Providers\CurrencyServiceProvider;
use FireflyIII\Providers\FireflyServiceProvider;
use FireflyIII\Providers\JournalServiceProvider;
use FireflyIII\Providers\PiggyBankServiceProvider;
use FireflyIII\Providers\RecurringServiceProvider;
use FireflyIII\Providers\RouteServiceProvider;
use FireflyIII\Providers\RuleGroupServiceProvider;
use FireflyIII\Providers\RuleServiceProvider;
use FireflyIII\Providers\SearchServiceProvider;
use FireflyIII\Providers\TagServiceProvider;
use TwigBridge\ServiceProvider;
return [
// Package Service Providers...
// Application Service Providers...
AppServiceProvider::class,
FireflyIII\Providers\AuthServiceProvider::class,
// FireflyIII\Providers\BroadcastServiceProvider::class,
// EventServiceProvider::class,
RouteServiceProvider::class,
// own stuff:
PragmaRX\Google2FALaravel\ServiceProvider::class,
ServiceProvider::class,
// More service providers.
AccountServiceProvider::class,
AttachmentServiceProvider::class,
BillServiceProvider::class,
BudgetServiceProvider::class,
CategoryServiceProvider::class,
CurrencyServiceProvider::class,
FireflyServiceProvider::class,
JournalServiceProvider::class,
PiggyBankServiceProvider::class,
RuleServiceProvider::class,
RuleGroupServiceProvider::class,
SearchServiceProvider::class,
TagServiceProvider::class,
AdminServiceProvider::class,
RecurringServiceProvider::class,
];

View File

@@ -3,6 +3,55 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## v6.4.21
### Added
- The ability to undo the recording of a database migration, which may help with database issues. [See the docs](https://docs.firefly-iii.org/references/faq/firefly-iii/using/#i-get-errors-about-missing-tables-how-do-i-fix-this)
- Added debug logs to file permission checks.
### Fixed
- View range issue for subscription overview
- Amount log entries were recorded for the transaction group, not the journal
- Subscriptions were not being renamed in rules when their names were changed
- [Issue 11688](https://github.com/firefly-iii/firefly-iii/issues/11688) (Token endpoints returning 401 unauthorized) reported by @molnarti
- [Issue 11694](https://github.com/firefly-iii/firefly-iii/issues/11694) (Foreign currency amount is always positive in transfers) reported by @SledgehammerPL
- [Issue 11684](https://github.com/firefly-iii/firefly-iii/issues/11684) (Transaction summary duplicated after more than 10 rows) reported by @jkmf
- [Issue 11700](https://github.com/firefly-iii/firefly-iii/issues/11700) (Duplicate entry for key 'tag_transaction_journal_tag_id_transaction_journal_id_unique') reported by @beatbesmer
- [Issue 11702](https://github.com/firefly-iii/firefly-iii/issues/11702) (Can't enable displaying primary currency when using Postgres) reported by @absdjfh
- [Issue 11710](https://github.com/firefly-iii/firefly-iii/issues/11710) (Firefly throws error on startup and when trying to use importer) reported by @avee87
## v6.4.20
### Added
- The ability to undo the recording of a database migration, which may help with database issues. [See the docs](https://docs.firefly-iii.org/references/faq/firefly-iii/using/#i-get-errors-about-missing-tables-how-do-i-fix-this)
- Added debug logs to file permission checks.
### Fixed
- View range issue for subscription overview
- Amount log entries were recorded for the transaction group, not the journal
- Subscriptions were not being renamed in rules when their names were changed
- [Issue 11688](https://github.com/firefly-iii/firefly-iii/issues/11688) (Token endpoints returning 401 unauthorized) reported by @molnarti
- [Issue 11694](https://github.com/firefly-iii/firefly-iii/issues/11694) (Foreign currency amount is always positive in transfers) reported by @SledgehammerPL
- [Issue 11684](https://github.com/firefly-iii/firefly-iii/issues/11684) (Transaction summary duplicated after more than 10 rows) reported by @jkmf
- [Issue 11700](https://github.com/firefly-iii/firefly-iii/issues/11700) (Duplicate entry for key 'tag_transaction_journal_tag_id_transaction_journal_id_unique') reported by @beatbesmer
- [Issue 11702](https://github.com/firefly-iii/firefly-iii/issues/11702) (Can't enable displaying primary currency when using Postgres) reported by @absdjfh
## v6.4.19
### Added
- The ability to undo the recording of a database migration, which may help with database issues. [See the docs](https://docs.firefly-iii.org/references/faq/firefly-iii/using/#i-get-errors-about-missing-tables-how-do-i-fix-this)
- Added debug logs to file permission checks
### Fixed
- View range issue for subscription overview
- Amount log entries were recorded for the transaction group, not the journal
- Subscriptions were not being renamed in rules when their names were changed
- [Issue 11688](https://github.com/firefly-iii/firefly-iii/issues/11688) (Token endpoints returning 401 unauthorized) reported by @molnarti
- [Issue 11694](https://github.com/firefly-iii/firefly-iii/issues/11694) (Foreign currency amount is always positive in transfers) reported by @SledgehammerPL
- [Issue 11684](https://github.com/firefly-iii/firefly-iii/issues/11684) (Transaction summary duplicated after more than 10 rows) reported by @jkmf
- [Issue 11700](https://github.com/firefly-iii/firefly-iii/issues/11700) (Duplicate entry for key 'tag_transaction_journal_tag_id_transaction_journal_id_unique') reported by @beatbesmer
- [Issue 11702](https://github.com/firefly-iii/firefly-iii/issues/11702) (Can't enable displaying primary currency when using Postgres) reported by @absdjfh
## v6.4.18
### Fixed

View File

@@ -34,9 +34,6 @@
"transfers",
"management"
],
"platform": {
"php": "8.4"
},
"license": "AGPL-3.0-or-later",
"homepage": "https://github.com/firefly-iii/firefly-iii",
"type": "project",
@@ -93,7 +90,6 @@
"laravel-notification-channels/pushover": "^4.0",
"laravel/framework": "^12",
"laravel/passport": "^12.0",
"laravel/sanctum": "^4.1",
"laravel/slack-notification-channel": "^3.3",
"laravel/ui": "^4.2",
"league/commonmark": "^2",

175
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c4e7dd2df7bae96ea6e4df82530411b9",
"content-hash": "6c4181d945517372c00358f3828806bc",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -2233,69 +2233,6 @@
},
"time": "2026-02-06T12:17:10+00:00"
},
{
"name": "laravel/sanctum",
"version": "v4.3.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/sanctum.git",
"reference": "e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/sanctum/zipball/e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76",
"reference": "e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/console": "^11.0|^12.0|^13.0",
"illuminate/contracts": "^11.0|^12.0|^13.0",
"illuminate/database": "^11.0|^12.0|^13.0",
"illuminate/support": "^11.0|^12.0|^13.0",
"php": "^8.2",
"symfony/console": "^7.0|^8.0"
},
"require-dev": {
"mockery/mockery": "^1.6",
"orchestra/testbench": "^9.15|^10.8|^11.0",
"phpstan/phpstan": "^1.10"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Laravel\\Sanctum\\SanctumServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Sanctum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
"keywords": [
"auth",
"laravel",
"sanctum"
],
"support": {
"issues": "https://github.com/laravel/sanctum/issues",
"source": "https://github.com/laravel/sanctum"
},
"time": "2026-02-07T17:19:31+00:00"
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.9",
@@ -3894,16 +3831,16 @@
},
{
"name": "nette/utils",
"version": "v4.1.2",
"version": "v4.1.3",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
"reference": "f76b5dc3d6c6d3043c8d937df2698515b99cbaf5"
"reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/f76b5dc3d6c6d3043c8d937df2698515b99cbaf5",
"reference": "f76b5dc3d6c6d3043c8d937df2698515b99cbaf5",
"url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe",
"reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe",
"shasum": ""
},
"require": {
@@ -3915,8 +3852,10 @@
},
"require-dev": {
"jetbrains/phpstorm-attributes": "^1.2",
"nette/phpstan-rules": "^1.0",
"nette/tester": "^2.5",
"phpstan/phpstan": "^2.0@stable",
"phpstan/extension-installer": "^1.4@stable",
"phpstan/phpstan": "^2.1@stable",
"tracy/tracy": "^2.9"
},
"suggest": {
@@ -3977,9 +3916,9 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v4.1.2"
"source": "https://github.com/nette/utils/tree/v4.1.3"
},
"time": "2026-02-03T17:21:09+00:00"
"time": "2026-02-13T03:05:33+00:00"
},
{
"name": "nunomaduro/collision",
@@ -5120,16 +5059,16 @@
},
{
"name": "predis/predis",
"version": "v3.3.0",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/predis/predis.git",
"reference": "153097374b39a2f737fe700ebcd725642526cdec"
"reference": "1183f5732e6b10efd33f64984a96726eaecb59aa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/predis/predis/zipball/153097374b39a2f737fe700ebcd725642526cdec",
"reference": "153097374b39a2f737fe700ebcd725642526cdec",
"url": "https://api.github.com/repos/predis/predis/zipball/1183f5732e6b10efd33f64984a96726eaecb59aa",
"reference": "1183f5732e6b10efd33f64984a96726eaecb59aa",
"shasum": ""
},
"require": {
@@ -5171,7 +5110,7 @@
],
"support": {
"issues": "https://github.com/predis/predis/issues",
"source": "https://github.com/predis/predis/tree/v3.3.0"
"source": "https://github.com/predis/predis/tree/v3.4.0"
},
"funding": [
{
@@ -5179,7 +5118,7 @@
"type": "github"
}
],
"time": "2025-11-24T17:48:50+00:00"
"time": "2026-02-11T17:30:28+00:00"
},
{
"name": "psr/cache",
@@ -9646,16 +9585,16 @@
},
{
"name": "thecodingmachine/safe",
"version": "v3.3.0",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/thecodingmachine/safe.git",
"reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236"
"reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thecodingmachine/safe/zipball/2cdd579eeaa2e78e51c7509b50cc9fb89a956236",
"reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236",
"url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19",
"reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19",
"shasum": ""
},
"require": {
@@ -9765,7 +9704,7 @@
"description": "PHP core functions that throw exceptions instead of returning FALSE on error",
"support": {
"issues": "https://github.com/thecodingmachine/safe/issues",
"source": "https://github.com/thecodingmachine/safe/tree/v3.3.0"
"source": "https://github.com/thecodingmachine/safe/tree/v3.4.0"
},
"funding": [
{
@@ -9776,12 +9715,16 @@
"url": "https://github.com/shish",
"type": "github"
},
{
"url": "https://github.com/silasjoisten",
"type": "github"
},
{
"url": "https://github.com/staabm",
"type": "github"
}
],
"time": "2025-05-14T06:15:44+00:00"
"time": "2026-02-04T18:08:13+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@@ -10530,16 +10473,16 @@
},
{
"name": "fruitcake/laravel-debugbar",
"version": "v4.0.7",
"version": "v4.0.8",
"source": {
"type": "git",
"url": "https://github.com/fruitcake/laravel-debugbar.git",
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be"
"reference": "ad7a5b11c11bf7773c9acd04d0fe0d79a229b1c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/a9cc62c81cd0bda4ca7410229487638d7df786be",
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be",
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/ad7a5b11c11bf7773c9acd04d0fe0d79a229b1c7",
"reference": "ad7a5b11c11bf7773c9acd04d0fe0d79a229b1c7",
"shasum": ""
},
"require": {
@@ -10616,7 +10559,7 @@
],
"support": {
"issues": "https://github.com/fruitcake/laravel-debugbar/issues",
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.7"
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.8"
},
"funding": [
{
@@ -10628,7 +10571,7 @@
"type": "github"
}
],
"time": "2026-02-06T20:53:50+00:00"
"time": "2026-02-14T13:26:03+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@@ -11198,16 +11141,16 @@
},
{
"name": "php-debugbar/php-debugbar",
"version": "v3.3.1",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/php-debugbar/php-debugbar.git",
"reference": "afdaa2e56aca9d56b5bb2bad041bd2f6002017cf"
"reference": "e50d470344b62a033a76d3d10a803b04c8e3be69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/afdaa2e56aca9d56b5bb2bad041bd2f6002017cf",
"reference": "afdaa2e56aca9d56b5bb2bad041bd2f6002017cf",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/e50d470344b62a033a76d3d10a803b04c8e3be69",
"reference": "e50d470344b62a033a76d3d10a803b04c8e3be69",
"shasum": ""
},
"require": {
@@ -11284,7 +11227,7 @@
],
"support": {
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.3.1"
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.4.0"
},
"funding": [
{
@@ -11296,7 +11239,7 @@
"type": "github"
}
],
"time": "2026-02-06T21:09:38+00:00"
"time": "2026-02-14T14:10:26+00:00"
},
{
"name": "php-debugbar/symfony-bridge",
@@ -11414,11 +11357,11 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.38",
"version": "2.1.39",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629",
"reference": "dfaf1f530e1663aa167bc3e52197adb221582629",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224",
"reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224",
"shasum": ""
},
"require": {
@@ -11463,25 +11406,25 @@
"type": "github"
}
],
"time": "2026-01-30T17:12:46+00:00"
"time": "2026-02-11T14:48:56+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
"version": "2.0.3",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
"reference": "468e02c9176891cc901143da118f09dc9505fc2f"
"reference": "6b5571001a7f04fa0422254c30a0017ec2f2cacc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f",
"reference": "468e02c9176891cc901143da118f09dc9505fc2f",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/6b5571001a7f04fa0422254c30a0017ec2f2cacc",
"reference": "6b5571001a7f04fa0422254c30a0017ec2f2cacc",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.1.15"
"phpstan/phpstan": "^2.1.39"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -11506,29 +11449,32 @@
"MIT"
],
"description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
"keywords": [
"static analysis"
],
"support": {
"issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3"
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.4"
},
"time": "2025-05-14T10:56:57+00:00"
"time": "2026-02-09T13:21:14+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "2.0.8",
"version": "2.0.10",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "1ed9e626a37f7067b594422411539aa807190573"
"reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1ed9e626a37f7067b594422411539aa807190573",
"reference": "1ed9e626a37f7067b594422411539aa807190573",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f",
"reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.1.29"
"phpstan/phpstan": "^2.1.39"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -11554,11 +11500,14 @@
"MIT"
],
"description": "Extra strict and opinionated rules for PHPStan",
"keywords": [
"static analysis"
],
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.8"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.10"
},
"time": "2026-01-27T08:10:25+00:00"
"time": "2026-02-11T14:17:32+00:00"
},
{
"name": "phpunit/php-code-coverage",

View File

@@ -22,59 +22,18 @@
declare(strict_types=1);
use FireflyIII\Providers\AccountServiceProvider;
use FireflyIII\Providers\AdminServiceProvider;
use FireflyIII\Providers\AppServiceProvider;
use FireflyIII\Providers\AttachmentServiceProvider;
use FireflyIII\Providers\BillServiceProvider;
use FireflyIII\Providers\BudgetServiceProvider;
use FireflyIII\Providers\CategoryServiceProvider;
use FireflyIII\Providers\CurrencyServiceProvider;
use FireflyIII\Providers\EventServiceProvider;
use FireflyIII\Providers\FireflyServiceProvider;
use FireflyIII\Providers\JournalServiceProvider;
use FireflyIII\Providers\PiggyBankServiceProvider;
use FireflyIII\Providers\RecurringServiceProvider;
use FireflyIII\Providers\RouteServiceProvider;
use FireflyIII\Providers\RuleGroupServiceProvider;
use FireflyIII\Providers\RuleServiceProvider;
use FireflyIII\Providers\SearchServiceProvider;
use FireflyIII\Providers\SessionServiceProvider;
use FireflyIII\Providers\TagServiceProvider;
use FireflyIII\Support\Facades\AccountForm;
use FireflyIII\Support\Facades\CurrencyForm;
use FireflyIII\Support\Facades\ExpandedForm;
use FireflyIII\Support\Facades\PiggyBankForm;
use FireflyIII\Support\Facades\RuleForm;
use Illuminate\Auth\AuthServiceProvider;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider;
use Illuminate\Broadcasting\BroadcastServiceProvider;
use Illuminate\Bus\BusServiceProvider;
use Illuminate\Cache\CacheServiceProvider;
use Illuminate\Cookie\CookieServiceProvider;
use Illuminate\Database\DatabaseServiceProvider;
use Illuminate\Encryption\EncryptionServiceProvider;
use Illuminate\Filesystem\FilesystemServiceProvider;
use Illuminate\Foundation\Providers\ConsoleSupportServiceProvider;
use Illuminate\Foundation\Providers\FoundationServiceProvider;
use Illuminate\Hashing\HashServiceProvider;
use Illuminate\Mail\MailServiceProvider;
use Illuminate\Notifications\NotificationServiceProvider;
use Illuminate\Pagination\PaginationServiceProvider;
use Illuminate\Pipeline\PipelineServiceProvider;
use Illuminate\Queue\QueueServiceProvider;
use Illuminate\Redis\RedisServiceProvider;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;
use Illuminate\Translation\TranslationServiceProvider;
use Illuminate\Validation\ValidationServiceProvider;
use Illuminate\View\ViewServiceProvider;
use Spatie\Html\Facades\Html;
use TwigBridge\ServiceProvider;
return [
'name' => envNonEmpty('APP_NAME', 'Firefly III'),
@@ -86,61 +45,6 @@ return [
'fallback_locale' => 'en_US',
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',
'providers' => [
// Laravel Framework Service Providers...
AuthServiceProvider::class,
BroadcastServiceProvider::class,
BusServiceProvider::class,
CacheServiceProvider::class,
ConsoleSupportServiceProvider::class,
CookieServiceProvider::class,
DatabaseServiceProvider::class,
EncryptionServiceProvider::class,
FilesystemServiceProvider::class,
FoundationServiceProvider::class,
HashServiceProvider::class,
MailServiceProvider::class,
NotificationServiceProvider::class,
PaginationServiceProvider::class,
PipelineServiceProvider::class,
QueueServiceProvider::class,
RedisServiceProvider::class,
PasswordResetServiceProvider::class,
SessionServiceProvider::class,
TranslationServiceProvider::class,
ValidationServiceProvider::class,
ViewServiceProvider::class,
// Package Service Providers...
// Application Service Providers...
AppServiceProvider::class,
FireflyIII\Providers\AuthServiceProvider::class,
// FireflyIII\Providers\BroadcastServiceProvider::class,
// EventServiceProvider::class,
RouteServiceProvider::class,
// own stuff:
PragmaRX\Google2FALaravel\ServiceProvider::class,
ServiceProvider::class,
// More service providers.
AccountServiceProvider::class,
AttachmentServiceProvider::class,
BillServiceProvider::class,
BudgetServiceProvider::class,
CategoryServiceProvider::class,
CurrencyServiceProvider::class,
FireflyServiceProvider::class,
JournalServiceProvider::class,
PiggyBankServiceProvider::class,
RuleServiceProvider::class,
RuleGroupServiceProvider::class,
SearchServiceProvider::class,
TagServiceProvider::class,
AdminServiceProvider::class,
RecurringServiceProvider::class,
],
'aliases' => [
'Auth' => Auth::class,
'Route' => Route::class,

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2026-02-10',
'build_time' => 1770751756,
'version' => 'develop/2026-02-15',
'build_time' => 1771170494,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

86
package-lock.json generated
View File

@@ -3814,9 +3814,9 @@
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4240,6 +4240,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/body-parser/node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/bonjour-service": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz",
@@ -4578,9 +4594,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001769",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
"integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
"version": "1.0.30001770",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz",
"integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==",
"dev": true,
"funding": [
{
@@ -6191,6 +6207,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -7118,9 +7150,9 @@
}
},
"node_modules/i18next": {
"version": "25.8.4",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.4.tgz",
"integrity": "sha512-a9A0MnUjKvzjEN/26ZY1okpra9kA8MEwzYEz1BNm+IyxUKPRH6ihf0p7vj8YvULwZHKHl3zkJ6KOt4hewxBecQ==",
"version": "25.8.7",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.7.tgz",
"integrity": "sha512-ttxxc5+67S/0hhoeVdEgc1lRklZhdfcUSEPp1//uUG2NB88X3667gRsDar+ZWQFdysnOsnb32bcoMsa4mtzhkQ==",
"funding": [
{
"type": "individual",
@@ -9717,9 +9749,9 @@
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -11033,9 +11065,9 @@
}
},
"node_modules/terser-webpack-plugin/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11801,9 +11833,9 @@
"license": "BSD-2-Clause"
},
"node_modules/webpack": {
"version": "5.105.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.1.tgz",
"integrity": "sha512-Gdj3X74CLJJ8zy4URmK42W7wTZUJrqL+z8nyGEr4dTN0kb3nVs+ZvjbTOqRYPD7qX4tUmwyHL9Q9K6T1seW6Yw==",
"version": "5.105.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz",
"integrity": "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11922,9 +11954,9 @@
}
},
"node_modules/webpack-dev-middleware/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12039,9 +12071,9 @@
}
},
"node_modules/webpack-dev-server/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12159,9 +12191,9 @@
}
},
"node_modules/webpack/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -22,8 +22,8 @@
"not_enough_currencies": "Not enough currencies",
"not_enough_currencies_enabled": "If you have just one currency enabled, there is no need to add exchange rates.",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
"webhook_stored_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID} (\"{title}\")<\/a> has been stored.",
"webhook_updated_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID}<\/a> (\"{title}\") has been updated.",
"webhook_stored_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID} (\"{title}\")<\/a> er blevet gemt.",
"webhook_updated_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID}<\/a> (\"{title}\") er blevet opdateret.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"transaction_new_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> has been stored.",
"transaction_journal_information": "Transaction information",
@@ -31,7 +31,7 @@
"apply_rules_checkbox": "Apply rules",
"fire_webhooks_checkbox": "Fire webhooks",
"no_budget_pointer": "Det ser ud til, at du ikke har oprettet budgetter endnu. Du burde oprette nogle p\u00e5 <a href=\"\/budgets\">budgetsiden<\/a>. Budgetter kan hj\u00e6lpe dig med at holde styr p\u00e5 udgifter.",
"no_bill_pointer": "You seem to have no subscription yet. You should create some on the <a href=\"subscriptions\">subscription<\/a>-page. Subscriptions can help you keep track of expenses.",
"no_bill_pointer": "Det ser ikke ud til, at du har nogle budgetter endnu. Du burde oprette nogle p\u00e5 <a href=\"\/budgets\">budgetsiden<\/a>. Budgetter kan hj\u00e6lpe dig med at holde styr p\u00e5 udgifter.",
"source_account": "Kildekonto",
"hidden_fields_preferences": "You can enable more transaction options in your <a href=\"preferences\">preferences<\/a>.",
"destination_account": "Destinationskonto",
@@ -107,42 +107,42 @@
"multi_account_warning_withdrawal": "Husk, at kildekontoen for efterf\u00f8lgende opdelinger vil blive overstyret af hvad der er defineret i den f\u00f8rste opdeling af tilbagetr\u00e6kningen.",
"multi_account_warning_deposit": "Husk, at destinationskontoen for efterf\u00f8lgende opdelinger vil blive tilsidesat af hvad der er defineret i den f\u00f8rste opsplitning af depositummet.",
"multi_account_warning_transfer": "Husk p\u00e5, at kilden + destination konto for efterf\u00f8lgende opdelinger vil blive overstyret af hvad der er defineret i den f\u00f8rste opdeling af overf\u00f8rslen.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "Efter enhver begivenhed",
"webhook_trigger_STORE_TRANSACTION": "Efter oprettelse af transaktion",
"webhook_trigger_UPDATE_TRANSACTION": "Efter opdatering af transaktion",
"webhook_trigger_DESTROY_TRANSACTION": "Efter sletning af transaktion",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_trigger_STORE_BUDGET": "Efter budgetoprettelse",
"webhook_trigger_UPDATE_BUDGET": "Efter budgetopdatering",
"webhook_trigger_DESTROY_BUDGET": "Efter sletning af budget",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Efter budgetteret bel\u00f8b \u00e6ndring",
"webhook_response_TRANSACTIONS": "Transaktionsdetaljer",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "Relevante oplysninger",
"webhook_response_ACCOUNTS": "Kontodetaljer",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "Ingen detaljer",
"webhook_delivery_JSON": "JSON",
"actions": "Handlinger",
"meta_data": "Meta data",
"webhook_messages": "Webhook-besked",
"inactive": "Inactive",
"no_webhook_messages": "Der er ingen webhook-beskeder",
"inspect": "Inspect",
"inspect": "Unders\u00f8g",
"create_new_webhook": "Opret ny webhook",
"webhooks": "Webhooks",
"webhook_trigger_form_help": "Indicate on what event the webhook will trigger",
"webhook_response_form_help": "Indicate what the webhook must submit to the URL.",
"webhook_trigger_form_help": "Indik\u00e9r p\u00e5 hvilken begivenhed webhook vil udl\u00f8se",
"webhook_response_form_help": "Angiv, hvad webhook skal indsende til URL.",
"webhook_delivery_form_help": "Hvilket format webhook skal levere data i.",
"webhook_active_form_help": "Webhooken skal v\u00e6re aktiv, ellers vil den ikke blive kaldt.",
"edit_webhook_js": "Rediger webhook \"{title}\"",
"webhook_was_triggered": "The webhook was triggered on the indicated transaction. Please wait for results to appear.",
"webhook_was_triggered": "Webhooken blev udl\u00f8st p\u00e5 den angivne transaktion. Vent venligst p\u00e5 at resultaterne vises.",
"view_message": "Vis besked",
"view_attempts": "Vis mislykkede fors\u00f8g",
"message_content_title": "Webhook-beskedindhold",
"message_content_help": "This is the content of the message that was sent (or tried) using this webhook.",
"message_content_help": "Dette er indholdet af den besked, der blev sendt (eller fors\u00f8gt) ved hj\u00e6lp af denne webhook.",
"attempt_content_title": "Webhook-fors\u00f8g",
"attempt_content_help": "These are all the unsuccessful attempts of this webhook message to submit to the configured URL. After some time, Firefly III will stop trying.",
"attempt_content_help": "Disse er alle de mislykkede fors\u00f8g p\u00e5 at sende denne webhook-meddelelse til den konfigurerede URL. Efter et stykke tid vil Firefly III stoppe med at fors\u00f8ge.",
"no_attempts": "Der er ingen mislykkede fors\u00f8g. Det er en god ting!",
"webhook_attempt_at": "Attempt at {moment}",
"logs": "Logs",
"webhook_attempt_at": "Fors\u00f8g p\u00e5 {moment}",
"logs": "Logfiler",
"response": "Svar",
"visit_webhook_url": "Bes\u00f8g webhook-URL",
"reset_webhook_secret": "Nulstil webhook-hemmelighed",

View File

@@ -107,18 +107,18 @@
"multi_account_warning_withdrawal": "Tenga en cuenta que la cuenta de origen de las divisiones posteriores ser\u00e1 anulada por lo que se defina en la primera divisi\u00f3n del gasto.",
"multi_account_warning_deposit": "Tenga en cuenta que la cuenta de destino de las divisiones posteriores ser\u00e1 anulada por lo que se defina en la primera divisi\u00f3n del retiro.",
"multi_account_warning_transfer": "Tenga en cuenta que la cuenta de origen + destino de divisiones posteriores ser\u00e1 anulada por lo que se defina en la primera divisi\u00f3n de la transferencia.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "Despu\u00e9s de cualquier evento",
"webhook_trigger_STORE_TRANSACTION": "Despu\u00e9s de crear la transacci\u00f3n",
"webhook_trigger_UPDATE_TRANSACTION": "Despu\u00e9s de actualizar la transacci\u00f3n",
"webhook_trigger_DESTROY_TRANSACTION": "Despu\u00e9s de eliminar la transacci\u00f3n",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_STORE_BUDGET": "Despu\u00e9s de crear un presupuesto",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Detalles de la transacci\u00f3n",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_ACCOUNTS": "Detalles de la cuenta",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "Sin detalles",
"webhook_delivery_JSON": "JSON",
"actions": "Acciones",
"meta_data": "Meta Datos",

View File

@@ -62,7 +62,7 @@
"destination_account_reconciliation": "\uc870\uc815 \uac70\ub798\uc758 \ubaa9\uc801\uc9c0 \uacc4\uc815\uc740 \ud3b8\uc9d1\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",
"source_account_reconciliation": "\uc870\uc815 \uac70\ub798\uc758 \ucd9c\ubc1c\uc9c0 \uacc4\uc815\uc740 \ud3b8\uc9d1\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",
"budget": "\uc608\uc0b0",
"bill": "Subscription",
"bill": "\uad6c\ub3c5",
"you_create_withdrawal": "\ucd9c\uae08\uc744 \uc0dd\uc131\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4.",
"you_create_transfer": "\uc804\uc1a1\uc744 \uc0dd\uc131\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4.",
"you_create_deposit": "\uc785\uae08\uc744 \uc0dd\uc131\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4.",

View File

@@ -184,11 +184,14 @@
{{ formatAmountBySymbol(transaction.amount, transaction.currency_symbol, transaction.currency_decimal_places, false) }}
{% endif %}
{% if transaction.source_account_id != account.id %}
{{ formatAmountBySymbol(transaction.amount*-1, transaction.currency_symbol, transaction.currency_decimal_places, false) }}
{{ formatAmountBySymbol(transaction.amount*-1, transaction.currency_symbol, transaction.currency_decimal_places, false) }}
{% endif %}
{# foreign amount of transfer #}
{% if null != transaction.foreign_amount %}
{% if null != transaction.foreign_amount and transaction.source_account_id == account.id %}
({{ formatAmountBySymbol(transaction.foreign_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_decimal_places, false) }})
{% endif %}
{% if null != transaction.foreign_amount and transaction.source_account_id != account.id %}
({{ formatAmountBySymbol(transaction.foreign_amount*-1, transaction.foreign_currency_symbol, transaction.foreign_currency_decimal_places, false) }})
{% endif %}

View File

@@ -21,6 +21,9 @@
*/
declare(strict_types=1);
use FireflyIII\Http\Middleware\AcceptHeaders;
use FireflyIII\Http\Middleware\Binder;
use Illuminate\Support\Facades\Route;
use function Safe\define;
@@ -38,6 +41,19 @@ if (!defined('DATEFORMAT')) {
define('DATEFORMAT', '(19|20)[0-9]{2}-?[0-9]{2}-?[0-9]{2}');
}
// API route for cron
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => 'v1',
'as' => 'api.v1.cron.',
'middleware' => [Binder::class, AcceptHeaders::class],
],
static function (): void {
Route::get('cron/{cliToken}', ['uses' => 'CronController@cron', 'as' => 'index'])->withoutMiddleware(['api']);
}
);
// Autocomplete controllers
Route::group(
[

View File

@@ -73,24 +73,24 @@ Route::group(
}
);
Route::group(
['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System', 'as' => 'cron.', 'prefix' => 'cron'],
static function (): void {
Route::get('run/{cliToken}', ['uses' => 'CronController@cron', 'as' => 'cron']);
}
);
// Route::group(
// ['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System', 'as' => 'cron.', 'prefix' => 'cron'],
// static function (): void {
// Route::get('run/{cliToken}', ['uses' => 'CronController@cron', 'as' => 'cron']);
// }
// );
Route::group(
['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System'],
['middleware' => ['binders-only'], 'namespace' => 'FireflyIII\Http\Controllers\System'],
static function (): void {
// Route::get('offline', static fn () => view('errors.offline'));
Route::get('health', ['uses' => 'HealthcheckController@check', 'as' => 'healthcheck']);
Route::get('health', ['uses' => 'HealthcheckController@check', 'as' => 'healthcheck'])->withoutMiddleware(['web']);
}
);
// These routes only work when the user is NOT logged in.
Route::group(
['middleware' => 'user-not-logged-in', 'namespace' => 'FireflyIII\Http\Controllers'],
['middleware' => ['user-not-logged-in'], 'namespace' => 'FireflyIII\Http\Controllers'],
static function (): void {
// Authentication Routes...
Route::get('login', ['uses' => 'Auth\LoginController@showLoginForm', 'as' => 'login']);
@@ -128,7 +128,7 @@ Route::group(
// For the two factor routes, the user must be logged in, but NOT 2FA. Account confirmation does not matter here.
Route::group(
['middleware' => 'user-logged-in-no-2fa', 'prefix' => 'two-factor', 'as' => 'two-factor.', 'namespace' => 'FireflyIII\Http\Controllers\Auth'],
['middleware' => 'user-simple-auth', 'prefix' => 'two-factor', 'as' => 'two-factor.', 'namespace' => 'FireflyIII\Http\Controllers\Auth'],
static function (): void {
Route::post('submit', ['uses' => 'TwoFactorController@submitMFA', 'as' => 'submit']);
Route::get('lost', ['uses' => 'TwoFactorController@lostTwoFactor', 'as' => 'lost']); // can be removed when v2 is live.