From 304964f443a25c21253c68bafe54ecd0fe317fc5 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Mon, 16 Sep 2024 20:41:32 -0400 Subject: [PATCH 1/6] feat: add horizon --- app/Providers/HorizonServiceProvider.php | 36 ++++ composer.json | 1 + composer.lock | 81 ++++++++- config/app.php | 1 + config/horizon.php | 213 +++++++++++++++++++++++ docker-compose.coolify.yml | 5 - 6 files changed, 331 insertions(+), 6 deletions(-) create mode 100644 app/Providers/HorizonServiceProvider.php create mode 100644 config/horizon.php diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php new file mode 100644 index 00000000..9811982b --- /dev/null +++ b/app/Providers/HorizonServiceProvider.php @@ -0,0 +1,36 @@ +email, [ + // + ]); + }); + } +} diff --git a/composer.json b/composer.json index 31a6b425..ddf0ad65 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "kreait/laravel-firebase": "^5.2", "lab404/laravel-impersonate": "^1.7", "laravel/framework": "^11.0", + "laravel/horizon": "^5.28", "laravel/jetstream": "^5.0", "laravel/pulse": "^1.0@beta", "laravel/sanctum": "^4.0", diff --git a/composer.lock b/composer.lock index de07ca08..d59dd1a5 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "39021f80bc99a9800375c9a9abffd273", + "content-hash": "9feddce02f2064b50e8e57190d02dc79", "packages": [ { "name": "amphp/amp", @@ -4816,6 +4816,85 @@ }, "time": "2024-09-13T13:36:30+00:00" }, + { + "name": "laravel/horizon", + "version": "v5.28.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/horizon.git", + "reference": "9d2c4eaeb11408384401f8a7d1b0ea4c76554f3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/horizon/zipball/9d2c4eaeb11408384401f8a7d1b0ea4c76554f3f", + "reference": "9d2c4eaeb11408384401f8a7d1b0ea4c76554f3f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcntl": "*", + "ext-posix": "*", + "illuminate/contracts": "^9.21|^10.0|^11.0", + "illuminate/queue": "^9.21|^10.0|^11.0", + "illuminate/support": "^9.21|^10.0|^11.0", + "nesbot/carbon": "^2.17|^3.0", + "php": "^8.0", + "ramsey/uuid": "^4.0", + "symfony/console": "^6.0|^7.0", + "symfony/error-handler": "^6.0|^7.0", + "symfony/process": "^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0|^8.0|^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.0|^10.4", + "predis/predis": "^1.1|^2.0" + }, + "suggest": { + "ext-redis": "Required to use the Redis PHP driver.", + "predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Horizon\\HorizonServiceProvider" + ], + "aliases": { + "Horizon": "Laravel\\Horizon\\Horizon" + } + } + }, + "autoload": { + "psr-4": { + "Laravel\\Horizon\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Dashboard and code-driven configuration for Laravel queues.", + "keywords": [ + "laravel", + "queue" + ], + "support": { + "issues": "https://github.com/laravel/horizon/issues", + "source": "https://github.com/laravel/horizon/tree/v5.28.1" + }, + "time": "2024-09-04T14:06:50+00:00" + }, { "name": "laravel/jetstream", "version": "v5.2.0", diff --git a/config/app.php b/config/app.php index e994cff7..564cb0f8 100644 --- a/config/app.php +++ b/config/app.php @@ -192,6 +192,7 @@ App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, + App\Providers\HorizonServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\FortifyServiceProvider::class, App\Providers\JetstreamServiceProvider::class, diff --git a/config/horizon.php b/config/horizon.php new file mode 100644 index 00000000..5101f6f0 --- /dev/null +++ b/config/horizon.php @@ -0,0 +1,213 @@ + env('HORIZON_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | Horizon Path + |-------------------------------------------------------------------------- + | + | This is the URI path where Horizon will be accessible from. Feel free + | to change this path to anything you like. Note that the URI will not + | affect the paths of its internal API that aren't exposed to users. + | + */ + + 'path' => env('HORIZON_PATH', 'horizon'), + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Connection + |-------------------------------------------------------------------------- + | + | This is the name of the Redis connection where Horizon will store the + | meta information required for it to function. It includes the list + | of supervisors, failed jobs, job metrics, and other information. + | + */ + + 'use' => 'default', + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Prefix + |-------------------------------------------------------------------------- + | + | This prefix will be used when storing all Horizon data in Redis. You + | may modify the prefix when you are running multiple installations + | of Horizon on the same server so that they don't have problems. + | + */ + + 'prefix' => env( + 'HORIZON_PREFIX', + Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:' + ), + + /* + |-------------------------------------------------------------------------- + | Horizon Route Middleware + |-------------------------------------------------------------------------- + | + | These middleware will get attached onto each Horizon route, giving you + | the chance to add your own middleware to this list or change any of + | the existing middleware. Or, you can simply stick with this list. + | + */ + + 'middleware' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Queue Wait Time Thresholds + |-------------------------------------------------------------------------- + | + | This option allows you to configure when the LongWaitDetected event + | will be fired. Every connection / queue combination may have its + | own, unique threshold (in seconds) before this event is fired. + | + */ + + 'waits' => [ + 'redis:default' => 60, + ], + + /* + |-------------------------------------------------------------------------- + | Job Trimming Times + |-------------------------------------------------------------------------- + | + | Here you can configure for how long (in minutes) you desire Horizon to + | persist the recent and failed jobs. Typically, recent jobs are kept + | for one hour while all failed jobs are stored for an entire week. + | + */ + + 'trim' => [ + 'recent' => 60, + 'pending' => 60, + 'completed' => 60, + 'recent_failed' => 10080, + 'failed' => 10080, + 'monitored' => 10080, + ], + + /* + |-------------------------------------------------------------------------- + | Silenced Jobs + |-------------------------------------------------------------------------- + | + | Silencing a job will instruct Horizon to not place the job in the list + | of completed jobs within the Horizon dashboard. This setting may be + | used to fully remove any noisy jobs from the completed jobs list. + | + */ + + 'silenced' => [ + // App\Jobs\ExampleJob::class, + ], + + /* + |-------------------------------------------------------------------------- + | Metrics + |-------------------------------------------------------------------------- + | + | Here you can configure how many snapshots should be kept to display in + | the metrics graph. This will get used in combination with Horizon's + | `horizon:snapshot` schedule to define how long to retain metrics. + | + */ + + 'metrics' => [ + 'trim_snapshots' => [ + 'job' => 24, + 'queue' => 24, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Fast Termination + |-------------------------------------------------------------------------- + | + | When this option is enabled, Horizon's "terminate" command will not + | wait on all of the workers to terminate unless the --wait option + | is provided. Fast termination can shorten deployment delay by + | allowing a new instance of Horizon to start while the last + | instance will continue to terminate each of its workers. + | + */ + + 'fast_termination' => false, + + /* + |-------------------------------------------------------------------------- + | Memory Limit (MB) + |-------------------------------------------------------------------------- + | + | This value describes the maximum amount of memory the Horizon master + | supervisor may consume before it is terminated and restarted. For + | configuring these limits on your workers, see the next section. + | + */ + + 'memory_limit' => 64, + + /* + |-------------------------------------------------------------------------- + | Queue Worker Configuration + |-------------------------------------------------------------------------- + | + | Here you may define the queue worker settings used by your application + | in all environments. These supervisors and settings handle all your + | queued jobs and will be provisioned by Horizon during deployment. + | + */ + + 'defaults' => [ + 'supervisor-1' => [ + 'connection' => 'redis', + 'queue' => ['default'], + 'balance' => 'auto', + 'autoScalingStrategy' => 'time', + 'maxProcesses' => 1, + 'maxTime' => 0, + 'maxJobs' => 0, + 'memory' => 128, + 'tries' => 1, + 'timeout' => 60, + 'nice' => 0, + ], + ], + + 'environments' => [ + 'production' => [ + 'supervisor-1' => [ + 'maxProcesses' => 10, + 'balanceMaxShift' => 1, + 'balanceCooldown' => 3, + ], + ], + + 'local' => [ + 'supervisor-1' => [ + 'maxProcesses' => 3, + ], + ], + ], +]; diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml index 0e16a22b..e3160746 100644 --- a/docker-compose.coolify.yml +++ b/docker-compose.coolify.yml @@ -37,8 +37,3 @@ services: PHP_FPM_POOL_NAME: "loger_queue" depends_on: - loger - horizon: - image: freesgen/loger - command: ["php", "/var/www/html/artisan", "horizon"] - environment: - PHP_FPM_POOL_NAME: "loger_horizon" From a89e608e1cedf4d9c0a069cd09c6a8eca7ca2ca5 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Thu, 3 Oct 2024 09:00:23 -0400 Subject: [PATCH 2/6] chore: add update --- app/Console/Commands/CheckUpdate.php | 32 ++++++++++++++ app/Console/Commands/UpdateVersion.php | 60 ++++++++++++++++++++++++++ components.d.ts | 2 + version.json | 3 ++ 4 files changed, 97 insertions(+) create mode 100644 app/Console/Commands/CheckUpdate.php create mode 100644 app/Console/Commands/UpdateVersion.php create mode 100644 version.json diff --git a/app/Console/Commands/CheckUpdate.php b/app/Console/Commands/CheckUpdate.php new file mode 100644 index 00000000..a08dbb79 --- /dev/null +++ b/app/Console/Commands/CheckUpdate.php @@ -0,0 +1,32 @@ + [ + 'method' => 'GET', + 'header' => [ + 'User-Agent: PHP' + ] + ] + ])), true); + $latestVersion = $latestRelease['tag_name']; + + if (version_compare($latestVersion, $currentVersion, '>')) { + // Trigger the update process if the latest version is newer + Artisan::call('update:run', ['--version' => $latestVersion, '--zipUrl' => $latestRelease['zipball_url']]); + } + } +} diff --git a/app/Console/Commands/UpdateVersion.php b/app/Console/Commands/UpdateVersion.php new file mode 100644 index 00000000..64a51631 --- /dev/null +++ b/app/Console/Commands/UpdateVersion.php @@ -0,0 +1,60 @@ +option('version'); + $zipUrl = $this->option('zipUrl'); + + $this->info("Starting update to version $version..."); + + // Download the ZIP file from the GitHub release + $updatePath = storage_path('app/update-package.zip'); + $this->downloadUpdate($zipUrl, $updatePath); + + // Unzip the update package + $zip = new ZipArchive; + if ($zip->open($updatePath) === TRUE) { + $zip->extractTo(base_path()); + $zip->close(); + $this->info('Update files extracted successfully.'); + + // Run migrations + Artisan::call('migrate --force'); + $this->info('Migrations completed.'); + + // Clear cache + Artisan::call('cache:clear'); + Artisan::call('config:cache'); + $this->info('Cache cleared and configurations rebuilt.'); + + // Update the version file + file_put_contents(base_path('version.json'), json_encode(['version' => $version])); + $this->info("Version updated to $version successfully."); + } else { + $this->error('Failed to extract the update package.'); + } + } + + private function downloadUpdate($url, $path) + { + $fileContent = file_get_contents($url); + file_put_contents($path, $fileContent); + $this->info('Update package downloaded.'); + } +} diff --git a/components.d.ts b/components.d.ts index 2222e11f..2a9ca025 100644 --- a/components.d.ts +++ b/components.d.ts @@ -32,6 +32,7 @@ declare module 'vue' { IMdiLock: typeof import('~icons/mdi/lock')['default'] IMdiMinus: typeof import('~icons/mdi/minus')['default'] IMdiMoney: typeof import('~icons/mdi/money')['default'] + IMdiPencil: typeof import('~icons/mdi/pencil')['default'] IMdiPlus: typeof import('~icons/mdi/plus')['default'] IMdiSearch: typeof import('~icons/mdi/search')['default'] IMdiSort: typeof import('~icons/mdi/sort')['default'] @@ -40,5 +41,6 @@ declare module 'vue' { IMdiSync: typeof import('~icons/mdi/sync')['default'] IMdiTimerStarOutline: typeof import('~icons/mdi/timer-star-outline')['default'] IMdiTrash: typeof import('~icons/mdi/trash')['default'] + IMdiWallet: typeof import('~icons/mdi/wallet')['default'] } } diff --git a/version.json b/version.json new file mode 100644 index 00000000..b8bfb0ca --- /dev/null +++ b/version.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} From 88ced3a88f8887c0caf46eee12894e7bb5085473 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Thu, 3 Oct 2024 09:01:17 -0400 Subject: [PATCH 3/6] fix: budget fixes --- .../js/Components/atoms/BalanceInput.vue | 18 ++++++---------- resources/js/Components/atoms/InputMoney.vue | 4 +--- .../Components/molecules/MoneyPresenter.vue | 2 +- resources/js/Pages/Auth/Register.vue | 6 +----- .../Pages/Dashboard/Partials/BudgetWidget.vue | 12 +++++------ .../Dashboard/Partials/DashboardDrafts.vue | 1 + resources/js/Pages/Finance/Account.vue | 19 +++++------------ resources/js/Pages/Finance/Budget.vue | 4 ++-- .../Finance/Partials/BudgetCategories.vue | 3 ++- .../domains/budget/components/BudgetItem.vue | 2 +- resources/js/domains/budget/useBudget.ts | 18 +++++++++++----- .../components/AccountsLedger.vue | 21 ++++++++++++++++++- .../components/ExpenseChartWidgetRow.vue | 3 +-- .../components/TransactionModal.vue | 1 - 14 files changed, 60 insertions(+), 54 deletions(-) diff --git a/resources/js/Components/atoms/BalanceInput.vue b/resources/js/Components/atoms/BalanceInput.vue index 699f25ed..10053e78 100644 --- a/resources/js/Components/atoms/BalanceInput.vue +++ b/resources/js/Components/atoms/BalanceInput.vue @@ -12,21 +12,15 @@ const emit = defineEmits(['move']); - const props = defineProps({ - value: { - type: Number - }, - formatter: { - type: Function, - default() { + const props = withDefaults(defineProps<{ + value: number | string; + formatter: Function; + category: ICategory; + }>(), { + formatter: () => { return (value: string) => { return value } - } - }, - category: { - type: Object, - required: true } }) diff --git a/resources/js/Components/atoms/InputMoney.vue b/resources/js/Components/atoms/InputMoney.vue index f440f9d9..7e5c101b 100644 --- a/resources/js/Components/atoms/InputMoney.vue +++ b/resources/js/Components/atoms/InputMoney.vue @@ -1,9 +1,7 @@ diff --git a/resources/js/Pages/Auth/Register.vue b/resources/js/Pages/Auth/Register.vue index 1fe7b1d5..d62206e4 100644 --- a/resources/js/Pages/Auth/Register.vue +++ b/resources/js/Pages/Auth/Register.vue @@ -30,11 +30,7 @@ const submit = (formData) => { password_confirmation: formData.confirmPassword, terms: true, })) - .post(route("register"), { - onFinish: () => { - form.reset("password", "password_confirmation"); - }, - }); + .post(route("register")); }; const fixedEmail = ref(null); diff --git a/resources/js/Pages/Dashboard/Partials/BudgetWidget.vue b/resources/js/Pages/Dashboard/Partials/BudgetWidget.vue index ca03daaf..7fe854e9 100644 --- a/resources/js/Pages/Dashboard/Partials/BudgetWidget.vue +++ b/resources/js/Pages/Dashboard/Partials/BudgetWidget.vue @@ -141,8 +141,8 @@ fetchBudgetAlerts().then(data => { @action="router.visit('/budgets')" >
-
-
+
+
{ Total
-

+

{{variance}} % @@ -174,7 +174,7 @@ fetchBudgetAlerts().then(data => {

-
+
{ For spending
- {{ formatMoney(currentBudget.forSpending) }} / {{ formatMoney(currentBudget.total) }} + {{ formatMoney(currentBudget.forSpending) }} ({{ progress }}%)
@@ -207,7 +207,7 @@ fetchBudgetAlerts().then(data => { For savings
- {{ formatMoney(currentBudget.forSavings) }} / {{ formatMoney(currentBudget.total) }} + {{ formatMoney(currentBudget.forSavings) }} ({{ progress }}%)
diff --git a/resources/js/Pages/Dashboard/Partials/DashboardDrafts.vue b/resources/js/Pages/Dashboard/Partials/DashboardDrafts.vue index 1047e61f..66cac4fc 100644 --- a/resources/js/Pages/Dashboard/Partials/DashboardDrafts.vue +++ b/resources/js/Pages/Dashboard/Partials/DashboardDrafts.vue @@ -17,6 +17,7 @@ const isLoadingDrafts = ref(false); const selected = defineModel('selected'); const emit = defineEmits(['re-loaded']); + const fetchTransactions = async () => { const url = `/api/finance/transactions?filter[status]=draft&limit=10&relationships=linked`; return axios.get(url).then(({ data }) => { diff --git a/resources/js/Pages/Finance/Account.vue b/resources/js/Pages/Finance/Account.vue index ca261ac9..5274087b 100644 --- a/resources/js/Pages/Finance/Account.vue +++ b/resources/js/Pages/Finance/Account.vue @@ -145,22 +145,13 @@ const isCreditCard = computed(() => { const payCreditCard = () => { const accountId = page.accountId const debt = Math.abs(selectedAccount.value?.balance ?? 0); - const transaction = currentBillingCycle.value; - - openModal({ + openTransactionModal({ mode: TRANSFER, - data: { + transactionData: { counter_account_id: accountId ?? "", - due: debt, + total: debt, description: `Payment of ${selectedAccount.value?.name}`, - account_id: props.accounts?.find?.((account) => account.balance > debt)?.id, - documents: [transaction], - resourceId: transaction?.id, - title: `Payment of ${transaction?.name}`, - defaultConcept: `Payment of ${transaction?.name}`, - transaction: transaction, - endpoint: `/api/billing-cycles/${currentBillingCycle.value.id}/payments/`, - paymentMethod: paymentMethods[0], + account_id: props.accounts.find((account) => account.balance > debt)?.id }, }) } @@ -224,7 +215,7 @@ const selectedTabName = computed(() => { Pay credit card diff --git a/resources/js/Pages/Finance/Budget.vue b/resources/js/Pages/Finance/Budget.vue index f7054d49..23946243 100644 --- a/resources/js/Pages/Finance/Budget.vue +++ b/resources/js/Pages/Finance/Budget.vue @@ -29,7 +29,7 @@ import { ICategory } from "@/domains/transactions/models"; const props = defineProps({ budgets: { - type: Array, + type: Object, required: true, }, distribution: { @@ -219,7 +219,7 @@ const readyToAssignLeft = computed(() => {
-

+

{{ formatMoney(accountTotal) }} {{ formatMoney(available) }} = ({{ formatMoney(accountTotal - available)}})

diff --git a/resources/js/Pages/Finance/Partials/BudgetCategories.vue b/resources/js/Pages/Finance/Partials/BudgetCategories.vue index 4c84a4ec..f542e924 100644 --- a/resources/js/Pages/Finance/Partials/BudgetCategories.vue +++ b/resources/js/Pages/Finance/Partials/BudgetCategories.vue @@ -28,6 +28,7 @@ const { budgets } = toRefs(props); const { visibleCategories, filters, + selectedBudgetIds, setSelectedBudget, assignBudget, moveBudget @@ -116,7 +117,7 @@ const handleBudgetMovement = (budgetMovementData: any) => { (); const emit = defineEmits(['removed', 'edit', 'assign', 'move']); diff --git a/resources/js/domains/budget/useBudget.ts b/resources/js/domains/budget/useBudget.ts index 67c85420..bc9db1c4 100644 --- a/resources/js/domains/budget/useBudget.ts +++ b/resources/js/domains/budget/useBudget.ts @@ -1,6 +1,6 @@ import { cloneDeep } from "lodash"; -import { computed, watch, reactive, toRefs, Ref } from "vue"; +import { computed, watch, reactive, toRefs, Ref, nextTick } from "vue"; import { getCategoriesTotals, getGroupTotals, InflowCategories } from './index'; import { IBudgetCategory } from "./models/budget"; import { format } from "date-fns"; @@ -238,7 +238,9 @@ export const useBudget = (budgets: Ref>) => { // @ts-ignore:: its ok not to be ok BudgetState.filters[filter] = filterName == filter ? !value : false ; }) - setVisibleCategories(); + nextTick(() => { + setVisibleCategories() + }) } const assignBudget = (assign: AssignBudgetProps) => { @@ -273,7 +275,9 @@ export const useBudget = (budgets: Ref>) => { } } setBudgetState(getBudget(BudgetState.data)); - setVisibleCategories() + nextTick(() => { + setVisibleCategories() + }) } const moveBudget = (movementData: BudgetMovementProps) => { @@ -323,7 +327,7 @@ export const useBudget = (budgets: Ref>) => { const filterConditions = { - overspent: (category: IBudgetCategory) => category.hasOverspent, + overspent: (category: IBudgetCategory) => {return (!category.subCategories && category.available < 0 ) || category.hasOverspent}, funded: (category: IBudgetCategory) => category.hasFunded, underFunded: (category: IBudgetCategory) => category.hasUnderFunded, } @@ -339,7 +343,11 @@ function getVisibleCategories(budgetData: Record, filterName?: Filt const visibleGroups = data.reduce((groups: any, group: IBudgetCategory) => { const groupHasFilter = group.name != 'Inflow' && evaluateFilterCondition(group, filterName) if (groupHasFilter) { - group.subCategories = group.subCategories?.filter(subCategory => filterName ? evaluateFilterCondition(subCategory, filterName) : true) + console.log(`Grupo: ${group.name}`); + group.subCategories = group.subCategories?.filter(subCategory => { + console.log(`${group.name}: ${subCategory.name}`, subCategory); + return filterName ? evaluateFilterCondition(subCategory, filterName) : true + }) groups.push(group) } return groups diff --git a/resources/js/domains/transactions/components/AccountsLedger.vue b/resources/js/domains/transactions/components/AccountsLedger.vue index ea531481..5a6dad1f 100644 --- a/resources/js/domains/transactions/components/AccountsLedger.vue +++ b/resources/js/domains/transactions/components/AccountsLedger.vue @@ -1,6 +1,6 @@