From f47e4dea07da8f4a6b3220eb173610ba14895ea9 Mon Sep 17 00:00:00 2001 From: Sagar Date: Wed, 19 Nov 2025 11:26:16 +0100 Subject: [PATCH 01/11] fix: the cms api base url is now accessed through the config. --- .../RelationManagers/EntriesRelationManager.php | 8 ++++---- app/Filament/Dashboard/Resources/MoleculeResource.php | 2 +- .../RelationManagers/MoleculesRelationManager.php | 2 +- .../RelationManagers/RelatedRelationManager.php | 2 +- .../RelationManagers/MoleculesRelationManager.php | 2 +- .../RelationManagers/EntriesRelationManager.php | 8 ++++---- .../RelationManagers/MoleculesRelationManager.php | 2 +- app/Livewire/MoleculeDepict2d.php | 4 ++-- config/services.php | 4 ++++ resources/views/molecule.blade.php | 4 ++-- 10 files changed, 21 insertions(+), 17 deletions(-) diff --git a/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php b/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php index 9cef396f..b3e5e5e4 100644 --- a/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php +++ b/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php @@ -99,21 +99,21 @@ public function infolist(Infolist $infolist): Infolist ]) ->schema([ ImageEntry::make('parent_canonical_smiles')->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->parent_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->parent_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('canonical_smiles')->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('standardized_canonical_smiles')->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->standardized_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->standardized_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) @@ -133,7 +133,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/MoleculeResource.php b/app/Filament/Dashboard/Resources/MoleculeResource.php index 3c7041a6..11894e10 100644 --- a/app/Filament/Dashboard/Resources/MoleculeResource.php +++ b/app/Filament/Dashboard/Resources/MoleculeResource.php @@ -90,7 +90,7 @@ public static function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php index b297563e..b47e153b 100644 --- a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php +++ b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php @@ -26,7 +26,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php index c890aa7d..ae132fdc 100644 --- a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php +++ b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php @@ -26,7 +26,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php b/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php index 2052b6c5..a9359be7 100644 --- a/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php +++ b/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php @@ -40,7 +40,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php index ce7075ec..291a988e 100644 --- a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php +++ b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php @@ -94,21 +94,21 @@ public function infolist(Infolist $infolist): Infolist ]) ->schema([ ImageEntry::make('parent_canonical_smiles')->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->parent_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->parent_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('canonical_smiles')->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('standardized_canonical_smiles')->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->standardized_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->standardized_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) @@ -128,7 +128,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php index 27247b19..107cda4a 100644 --- a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php +++ b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php @@ -35,7 +35,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; }) ->width(200) ->height(200) diff --git a/app/Livewire/MoleculeDepict2d.php b/app/Livewire/MoleculeDepict2d.php index 76b5666a..bc5d1478 100644 --- a/app/Livewire/MoleculeDepict2d.php +++ b/app/Livewire/MoleculeDepict2d.php @@ -28,13 +28,13 @@ class MoleculeDepict2d extends Component #[Computed] public function source() { - return env('CM_PUBLIC_API').'depict/2D?smiles='.urlencode($this->smiles).'&height='.$this->height.'&width='.$this->width.'&toolkit='.$this->toolkit.'&CIP='.$this->CIP; + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($this->smiles).'&height='.$this->height.'&width='.$this->width.'&toolkit='.$this->toolkit.'&CIP='.$this->CIP; } #[Computed] public function preview() { - return env('CM_PUBLIC_API').'depict/2D?smiles='.urlencode($this->smiles); + return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($this->smiles); } public function downloadMolFile($toolkit) diff --git a/config/services.php b/config/services.php index ad1a400b..f1158ed6 100644 --- a/config/services.php +++ b/config/services.php @@ -49,4 +49,8 @@ 'redirect' => env('NFDI_REDIRECT_URL'), ], + 'cheminf' => [ + 'api_url' => env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/'), + ], + ]; diff --git a/resources/views/molecule.blade.php b/resources/views/molecule.blade.php index 058806a6..637ab628 100644 --- a/resources/views/molecule.blade.php +++ b/resources/views/molecule.blade.php @@ -34,7 +34,7 @@ + content="{{ config('services.cheminf.api_url') . 'depict/2D?smiles=' . urlencode($molecule->canonical_smiles) . '&height=630&width=1200&toolkit=cdk' ?? asset('img/coconut-og-image.png') }}"> @@ -56,7 +56,7 @@ and found in the collection{{ $molecule->collections->count() > 1 ? 's' : '' }}: {{ implode(', ', $molecule->collections->pluck('title')->toArray()) }} @endif"> + content="{{ config('services.cheminf.api_url') . 'depict/2D?smiles=' . urlencode($molecule->canonical_smiles) . '&height=630&width=1200&toolkit=cdk' ?? asset('img/coconut-og-image.png') }}"> From 1a389dd56332b2db38d15ed7db972a549373dbff Mon Sep 17 00:00:00 2001 From: Sagar Date: Wed, 19 Nov 2025 11:27:06 +0100 Subject: [PATCH 02/11] fix: changed the trigger branch in the workflow. --- .github/workflows/dev-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index d25cf0c8..7525fa1c 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -13,7 +13,7 @@ name : Development - Build COCONUT image and push to Docker Hub on: push: - branches: [development] + branches: [fix-access-env-variables] env: REPOSITORY_NAME: coconut From 713b54c59697732ed6f855222f71e0bd828a5cc3 Mon Sep 17 00:00:00 2001 From: Sagar Date: Wed, 19 Nov 2025 13:11:40 +0100 Subject: [PATCH 03/11] fix: audit vulnerability. --- composer.lock | 95 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/composer.lock b/composer.lock index 340106f4..3b49658b 100644 --- a/composer.lock +++ b/composer.lock @@ -11797,16 +11797,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -11819,7 +11819,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -11844,7 +11844,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -11860,7 +11860,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/error-handler", @@ -12228,16 +12228,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.2.6", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "6023ec7607254c87c5e69fb3558255aca440d72b" + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6023ec7607254c87c5e69fb3558255aca440d72b", - "reference": "6023ec7607254c87c5e69fb3558255aca440d72b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", "shasum": "" }, "require": { @@ -12254,6 +12254,7 @@ "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", "symfony/cache": "^6.4.12|^7.1.5", + "symfony/clock": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", @@ -12286,7 +12287,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.2.6" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" }, "funding": [ { @@ -12297,12 +12298,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-09T08:14:01+00:00" + "time": "2025-11-08T16:41:12+00:00" }, { "name": "symfony/http-kernel", @@ -12500,16 +12505,16 @@ }, { "name": "symfony/mime", - "version": "v7.2.6", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "706e65c72d402539a072d0d6ad105fff6c161ef1" + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/706e65c72d402539a072d0d6ad105fff6c161ef1", - "reference": "706e65c72d402539a072d0d6ad105fff6c161ef1", + "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", "shasum": "" }, "require": { @@ -12564,7 +12569,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.2.6" + "source": "https://github.com/symfony/mime/tree/v7.3.4" }, "funding": [ { @@ -12575,12 +12580,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-27T13:34:41+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/polyfill-ctype", @@ -12741,7 +12750,7 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -12804,7 +12813,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -12815,6 +12824,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -12824,7 +12837,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -12885,7 +12898,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -12896,6 +12909,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -12905,7 +12922,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -12966,7 +12983,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -12977,6 +12994,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -12986,7 +13007,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -13046,7 +13067,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -13057,6 +13078,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -13066,16 +13091,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -13122,7 +13147,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -13133,12 +13158,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-uuid", From f29c8b1a3e79536122c8a19efe7d8667ef58684c Mon Sep 17 00:00:00 2001 From: Sagar Date: Wed, 19 Nov 2025 14:19:59 +0100 Subject: [PATCH 04/11] chore: refactored to use env variables to be served from config. --- app/Console/Commands/GenerateCoordinates.php | 2 +- .../FetchCitationMetadataAuto.php | 6 +++--- .../SampleLocationsRelationManager.php | 2 +- app/Filament/Dashboard/Resources/ReportResource.php | 2 +- app/Helper.php | 6 +++--- app/Jobs/ImportEntry.php | 6 +++--- app/Jobs/ImportEntryAuto.php | 6 +++--- app/Jobs/ProcessEntry.php | 2 +- app/Jobs/ProcessEntryBatch.php | 2 +- app/Models/Molecule.php | 2 +- .../PostPublishJobFailedNotification.php | 2 +- app/Notifications/PrePublishJobFailedNotification.php | 2 +- app/Notifications/ReportAssignedNotification.php | 2 +- app/Notifications/ReportStatusChangedNotification.php | 4 ++-- app/Notifications/ReportSubmittedNotification.php | 4 ++-- config/filesystems.php | 2 +- config/services.php | 11 +++++++++++ resources/views/components/tawk-chat.blade.php | 2 +- .../views/forms/components/organisms-table.blade.php | 2 +- 19 files changed, 39 insertions(+), 28 deletions(-) diff --git a/app/Console/Commands/GenerateCoordinates.php b/app/Console/Commands/GenerateCoordinates.php index a0a55489..51c25fb9 100644 --- a/app/Console/Commands/GenerateCoordinates.php +++ b/app/Console/Commands/GenerateCoordinates.php @@ -71,7 +71,7 @@ public function handle() $canonical_smiles = $mol->canonical_smiles; // Build endpoints. - $apiUrl = env('API_URL', 'https://api.cheminf.studio/latest/'); + $apiUrl = config('services.cheminf.internal_api_url'); $d2Endpoint = $apiUrl.'convert/mol2D?smiles='.urlencode($canonical_smiles).'&toolkit=rdkit'; $d3Endpoint = $apiUrl.'convert/mol3D?smiles='.urlencode($canonical_smiles).'&toolkit=rdkit'; diff --git a/app/Console/Commands/SubmissionsAutoProcess/FetchCitationMetadataAuto.php b/app/Console/Commands/SubmissionsAutoProcess/FetchCitationMetadataAuto.php index dd5ead55..3d8fc55a 100644 --- a/app/Console/Commands/SubmissionsAutoProcess/FetchCitationMetadataAuto.php +++ b/app/Console/Commands/SubmissionsAutoProcess/FetchCitationMetadataAuto.php @@ -146,7 +146,7 @@ private function fetchCitationFromAPIs($doi): ?array $citationResponse = null; // Try EuropePMC first - $europemcUrl = env('EUROPEPMC_WS_API'); + $europemcUrl = config('services.citation.europepmc_url'); $europemcParams = [ 'query' => 'DOI:'.$doi, 'format' => 'json', @@ -161,14 +161,14 @@ private function fetchCitationFromAPIs($doi): ?array $citationResponse = $this->formatCitationResponse($europemcResponse['resultList']['result'][0], 'europemc'); } else { // Try CrossRef - $crossrefUrl = env('CROSSREF_WS_API').$doi; + $crossrefUrl = config('services.citation.crossref_url').$doi; $response = $this->makeRequest($crossrefUrl); $crossrefResponse = ($response && method_exists($response, 'json')) ? $response->json() : null; if ($crossrefResponse && isset($crossrefResponse['message'])) { $citationResponse = $this->formatCitationResponse($crossrefResponse['message'], 'crossref'); } else { // Try DataCite as last resort - $dataciteUrl = env('DATACITE_WS_API').$doi; + $dataciteUrl = config('services.citation.datacite_url').$doi; $response = $this->makeRequest($dataciteUrl); $dataciteResponse = ($response && method_exists($response, 'json')) ? $response->json() : null; if ($dataciteResponse && isset($dataciteResponse['data'])) { diff --git a/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/SampleLocationsRelationManager.php b/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/SampleLocationsRelationManager.php index 159a9fd9..454c0438 100644 --- a/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/SampleLocationsRelationManager.php +++ b/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/SampleLocationsRelationManager.php @@ -38,7 +38,7 @@ public function table(Table $table): Table ]) ->actions([ Tables\Actions\Action::make('edit') - ->url(fn (SampleLocation $record) => env('APP_URL').'/dashboard/sample-locations/'.$record->id.'/edit') + ->url(fn (SampleLocation $record) => config('app.url').'/dashboard/sample-locations/'.$record->id.'/edit') // ->color('info') ->icon('heroicon-m-pencil-square'), // Tables\Actions\EditAction::make(), diff --git a/app/Filament/Dashboard/Resources/ReportResource.php b/app/Filament/Dashboard/Resources/ReportResource.php index 12b8be91..daeddcf3 100644 --- a/app/Filament/Dashboard/Resources/ReportResource.php +++ b/app/Filament/Dashboard/Resources/ReportResource.php @@ -228,7 +228,7 @@ public static function form(Form $form): Form }), Action::make('viewCompoundPage') ->color('info') - ->url(fn (string $operation, $record): string => $operation === 'create' ? env('APP_URL').'/compounds/'.request()->compound_id : env('APP_URL').'/compounds/'.$record->mol_ids) + ->url(fn (string $operation, $record): string => $operation === 'create' ? config('app.url').'/compounds/'.request()->compound_id : config('app.url').'/compounds/'.$record->mol_ids) ->openUrlInNewTab() ->hidden(function (Get $get, string $operation) { return ! $get('type'); diff --git a/app/Helper.php b/app/Helper.php index 4f36c30b..ef527c7f 100644 --- a/app/Helper.php +++ b/app/Helper.php @@ -75,7 +75,7 @@ function doiRegxMatch($doi) function fetchDOICitation($doi) { $citationResponse = null; - $europemcUrl = env('EUROPEPMC_WS_API'); + $europemcUrl = config('services.citation.europepmc_url'); $europemcParams = [ 'query' => 'DOI:'.$doi, 'format' => 'json', @@ -89,13 +89,13 @@ function fetchDOICitation($doi) $citationResponse = formatCitationResponse($europemcResponse['resultList']['result'][0], 'europemc'); } else { // fetch citation from CrossRef - $crossrefUrl = env('CROSSREF_WS_API').$doi; + $crossrefUrl = config('services.citation.crossref_url').$doi; $crossrefResponse = makeRequest($crossrefUrl); if ($crossrefResponse && isset($crossrefResponse['message'])) { $citationResponse = formatCitationResponse($crossrefResponse['message'], 'crossref'); } else { // fetch citation from DataCite - $dataciteUrl = env('DATACITE_WS_API').$doi; + $dataciteUrl = config('services.citation.datacite_url').$doi; $dataciteResponse = makeRequest($dataciteUrl); if ($dataciteResponse && isset($dataciteResponse['data'])) { $citationResponse = formatCitationResponse($dataciteResponse['data'], 'datacite'); diff --git a/app/Jobs/ImportEntry.php b/app/Jobs/ImportEntry.php index d411a162..3eb2605a 100644 --- a/app/Jobs/ImportEntry.php +++ b/app/Jobs/ImportEntry.php @@ -282,7 +282,7 @@ public function fetchDOICitation($doi, $molecule) $citationResponse = null; if ($citation->wasRecentlyCreated || $citation->title == '') { // fetch citation from EuropePMC - $europemcUrl = env('EUROPEPMC_WS_API'); + $europemcUrl = config('services.citation.europepmc_url'); $europemcParams = [ 'query' => 'DOI:'.$doi, 'format' => 'json', @@ -296,14 +296,14 @@ public function fetchDOICitation($doi, $molecule) $citationResponse = $this->formatCitationResponse($europemcResponse['resultList']['result'][0], 'europemc'); } else { // fetch citation from CrossRef - $crossrefUrl = env('CROSSREF_WS_API').$doi; + $crossrefUrl = config('services.citation.crossref_url').$doi; $response = $this->makeRequest($crossrefUrl); $crossrefResponse = $response ? $response->json() : null; if ($crossrefResponse && isset($crossrefResponse['message'])) { $citationResponse = $this->formatCitationResponse($crossrefResponse['message'], 'crossref'); } else { // fetch citation from DataCite - $dataciteUrl = env('DATACITE_WS_API').$doi; + $dataciteUrl = config('services.citation.datacite_url').$doi; $response = $this->makeRequest($dataciteUrl); $dataciteResponse = $response ? $response->json() : null; if ($dataciteResponse && isset($dataciteResponse['data'])) { diff --git a/app/Jobs/ImportEntryAuto.php b/app/Jobs/ImportEntryAuto.php index 59845b99..93d22916 100644 --- a/app/Jobs/ImportEntryAuto.php +++ b/app/Jobs/ImportEntryAuto.php @@ -363,7 +363,7 @@ public function fetchDOICitation($doi, $molecule) $citationResponse = null; if ($citation->wasRecentlyCreated || $citation->title == '') { // fetch citation from EuropePMC - $europemcUrl = env('EUROPEPMC_WS_API'); + $europemcUrl = config('services.citation.europepmc_url'); $europemcParams = [ 'query' => 'DOI:'.$doi, 'format' => 'json', @@ -377,14 +377,14 @@ public function fetchDOICitation($doi, $molecule) $citationResponse = $this->formatCitationResponse($europemcResponse['resultList']['result'][0], 'europemc'); } else { // fetch citation from CrossRef - $crossrefUrl = env('CROSSREF_WS_API').$doi; + $crossrefUrl = config('services.citation.crossref_url').$doi; $response = $this->makeRequest($crossrefUrl); $crossrefResponse = $response ? $response->json() : null; if ($crossrefResponse && isset($crossrefResponse['message'])) { $citationResponse = $this->formatCitationResponse($crossrefResponse['message'], 'crossref'); } else { // fetch citation from DataCite - $dataciteUrl = env('DATACITE_WS_API').$doi; + $dataciteUrl = config('services.citation.datacite_url').$doi; $response = $this->makeRequest($dataciteUrl); $dataciteResponse = $response ? $response->json() : null; if ($dataciteResponse && isset($dataciteResponse['data'])) { diff --git a/app/Jobs/ProcessEntry.php b/app/Jobs/ProcessEntry.php index 5c17ea83..7fe84406 100644 --- a/app/Jobs/ProcessEntry.php +++ b/app/Jobs/ProcessEntry.php @@ -55,7 +55,7 @@ public function handle(): void $has_stereocenters = false; $is_invalid = false; $error_code = -1; - $API_URL = env('API_URL', 'https://api.cheminf.studio/latest/'); + $API_URL = config('services.cheminf.internal_api_url'); $ENDPOINT = $API_URL.'chem/coconut/pre-processing?smiles='.urlencode($canonical_smiles).'&_3d_mol=false&descriptors=false'; try { diff --git a/app/Jobs/ProcessEntryBatch.php b/app/Jobs/ProcessEntryBatch.php index 12d79a89..c36f229b 100644 --- a/app/Jobs/ProcessEntryBatch.php +++ b/app/Jobs/ProcessEntryBatch.php @@ -121,7 +121,7 @@ public function processEntry(Entry $entry): void $is_cis_trans = false; $is_invalid = false; $error_code = -1; - $API_URL = env('API_URL', 'https://api.cheminf.studio/latest/'); + $API_URL = config('services.cheminf.internal_api_url'); $ENDPOINT = $API_URL.'chem/coconut/pre-processing?smiles='.urlencode($canonical_smiles).'&_3d_mol=false&descriptors=false'; try { diff --git a/app/Models/Molecule.php b/app/Models/Molecule.php index db9b3b91..d03f01b4 100644 --- a/app/Models/Molecule.php +++ b/app/Models/Molecule.php @@ -303,7 +303,7 @@ public function getSchema($type = 'bioschemas') $moleculeSchema->identifier($this->identifier) ->name($this->name) - ->url(env('APP_URL').'/compound/'.$this->identifier) + ->url(config('app.url').'/compound/'.$this->identifier) ->inChI($this->standard_inchi) ->inChIKey($this->standard_inchi_key) ->iupacName($this->iupac_name) diff --git a/app/Notifications/PostPublishJobFailedNotification.php b/app/Notifications/PostPublishJobFailedNotification.php index 58c822e6..c5683b27 100644 --- a/app/Notifications/PostPublishJobFailedNotification.php +++ b/app/Notifications/PostPublishJobFailedNotification.php @@ -43,7 +43,7 @@ public function toMail(object $notifiable): MailMessage $timestamp = $this->event->errorDetails['timestamp']; // Create a dashboard URL for admins to check logs - $dashboardUrl = url(env('APP_URL').'/dashboard'); + $dashboardUrl = url(config('app.url').'/dashboard'); $mailMessage = (new MailMessage) ->subject('Coconut: Post Publish Job Failed - '.$jobName) diff --git a/app/Notifications/PrePublishJobFailedNotification.php b/app/Notifications/PrePublishJobFailedNotification.php index daa8850d..69671a05 100644 --- a/app/Notifications/PrePublishJobFailedNotification.php +++ b/app/Notifications/PrePublishJobFailedNotification.php @@ -43,7 +43,7 @@ public function toMail(object $notifiable): MailMessage $timestamp = $this->event->errorDetails['timestamp']; // Create a dashboard URL for admins to check logs - $dashboardUrl = url(env('APP_URL').'/dashboard'); + $dashboardUrl = url(config('app.url').'/dashboard'); $mailMessage = (new MailMessage) ->subject('Coconut: Pre Publish Job Failed - '.$jobName) diff --git a/app/Notifications/ReportAssignedNotification.php b/app/Notifications/ReportAssignedNotification.php index 1278b549..f29cf456 100644 --- a/app/Notifications/ReportAssignedNotification.php +++ b/app/Notifications/ReportAssignedNotification.php @@ -37,7 +37,7 @@ public function via(object $notifiable): array */ public function toMail(object $notifiable) { - $url = url(env('APP_URL').'/dashboard/reports/'.$this->event->report->id.'/edit'); + $url = url(config('app.url').'/dashboard/reports/'.$this->event->report->id.'/edit'); return (new ReportAssignedMail($this->event, $notifiable, 'curator', $url)) ->to($notifiable->email); diff --git a/app/Notifications/ReportStatusChangedNotification.php b/app/Notifications/ReportStatusChangedNotification.php index e93f69ba..610e9f22 100644 --- a/app/Notifications/ReportStatusChangedNotification.php +++ b/app/Notifications/ReportStatusChangedNotification.php @@ -41,9 +41,9 @@ public function via(object $notifiable): array public function toMail(object $notifiable) { if ($notifiable->can('update', $this->event->report)) { - $url = url(env('APP_URL').'/dashboard/reports/'.$this->event->report->id.'/edit'); + $url = url(config('app.url').'/dashboard/reports/'.$this->event->report->id.'/edit'); } else { - $url = url(env('APP_URL').'/dashboard/reports/'.$this->event->report->id); + $url = url(config('app.url').'/dashboard/reports/'.$this->event->report->id); } return (new ReportStatusChangedMail($this->event, $notifiable, $this->mail_to, $url)) diff --git a/app/Notifications/ReportSubmittedNotification.php b/app/Notifications/ReportSubmittedNotification.php index 0d99e770..246ef6c9 100644 --- a/app/Notifications/ReportSubmittedNotification.php +++ b/app/Notifications/ReportSubmittedNotification.php @@ -41,9 +41,9 @@ public function via(object $notifiable): array public function toMail(object $notifiable) { if ($notifiable->can('update', $this->event->report)) { - $url = url(env('APP_URL').'/dashboard/reports/'.$this->event->report->id.'/edit'); + $url = url(config('app.url').'/dashboard/reports/'.$this->event->report->id.'/edit'); } else { - $url = url(env('APP_URL').'/dashboard/reports/'.$this->event->report->id); + $url = url(config('app.url').'/dashboard/reports/'.$this->event->report->id); } return (new ReportSubmittedMail($this->event, $notifiable, $this->mail_to, $url)) diff --git a/config/filesystems.php b/config/filesystems.php index 6a645054..54016361 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -39,7 +39,7 @@ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => config('app.url').'/storage', 'visibility' => 'public', 'throw' => false, ], diff --git a/config/services.php b/config/services.php index f1158ed6..d25f80cf 100644 --- a/config/services.php +++ b/config/services.php @@ -51,6 +51,17 @@ 'cheminf' => [ 'api_url' => env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/'), + 'internal_api_url' => env('API_URL', 'https://api.cheminf.studio/latest/'), + ], + + 'citation' => [ + 'europepmc_url' => env('EUROPEPMC_WS_API'), + 'crossref_url' => env('CROSSREF_WS_API'), + 'datacite_url' => env('DATACITE_WS_API'), + ], + + 'tawk' => [ + 'url' => env('TAWK_URL'), ], ]; diff --git a/resources/views/components/tawk-chat.blade.php b/resources/views/components/tawk-chat.blade.php index fa7f24b3..b4416c64 100644 --- a/resources/views/components/tawk-chat.blade.php +++ b/resources/views/components/tawk-chat.blade.php @@ -5,7 +5,7 @@ var s1 = document.createElement("script"), s0 = document.getElementsByTagName("script")[0]; s1.async = true; - s1.src = '{{ env('TAWK_URL') }}'; + s1.src = '{{ config('services.tawk.url') }}'; s1.charset = 'UTF-8'; s1.setAttribute('crossorigin', '*'); s0.parentNode.insertBefore(s1, s0); diff --git a/resources/views/forms/components/organisms-table.blade.php b/resources/views/forms/components/organisms-table.blade.php index 7403be0f..f3758ea6 100644 --- a/resources/views/forms/components/organisms-table.blade.php +++ b/resources/views/forms/components/organisms-table.blade.php @@ -14,7 +14,7 @@

- + Edit From 29fbdf7f69e1fbeb2a50b8d51f214639726e3e36 Mon Sep 17 00:00:00 2001 From: Sagar Date: Wed, 19 Nov 2025 14:22:41 +0100 Subject: [PATCH 05/11] chore: revered the triggering branch to development. --- .github/workflows/dev-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 7525fa1c..d25cf0c8 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -13,7 +13,7 @@ name : Development - Build COCONUT image and push to Docker Hub on: push: - branches: [fix-access-env-variables] + branches: [development] env: REPOSITORY_NAME: coconut From 9fa62488c54b6596367ffdbe6dba67b47114659c Mon Sep 17 00:00:00 2001 From: Sagar Date: Sun, 23 Nov 2025 12:18:44 +0100 Subject: [PATCH 06/11] feat: cms client to handle all the requests. --- app/Services/CmsClient.php | 74 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 app/Services/CmsClient.php diff --git a/app/Services/CmsClient.php b/app/Services/CmsClient.php new file mode 100644 index 00000000..dc51aee6 --- /dev/null +++ b/app/Services/CmsClient.php @@ -0,0 +1,74 @@ +internalUrl = config('services.cheminf.internal_api_url'); + $this->publicUrl = config('services.cheminf.api_url'); + $this->authToken = config('services.cheminf.internal_token'); + } + + /** + * Make a GET request to the CMS API + * + * @return \Illuminate\Http\Client\Response + */ + public function get(string $endpoint, array $params = []) + { + return $this->makeRequest('GET', $endpoint, $params); + } + + /** + * Make a POST request to the CMS API + * + * @return \Illuminate\Http\Client\Response + */ + public function post(string $endpoint, array $data = []) + { + return $this->makeRequest('POST', $endpoint, $data); + } + + /** + * Make the actual HTTP request + * + * @return \Illuminate\Http\Client\Response + */ + private function makeRequest(string $method, string $endpoint, array $data = []) + { + $request = Http::timeout(120); + + // Add authentication token if available + if ($this->authToken) { + $request = $request->withHeaders([ + 'Authorization' => 'Bearer '.$this->authToken, + ]); + } + + $url = $this->internalUrl.ltrim($endpoint, '/'); + + return match (strtoupper($method)) { + 'GET' => $request->get($url, $data), + 'POST' => $request->post($url, $data), + default => throw new \InvalidArgumentException("Unsupported HTTP method: {$method}"), + }; + } + + /** + * Get the public URL for the CMS API (for frontend use) + */ + public function getPublicUrl(): string + { + return $this->publicUrl; + } +} From b62191528bb72b12fa59af358deaf6c5dc0954fd Mon Sep 17 00:00:00 2001 From: Sagar Date: Sun, 23 Nov 2025 12:19:26 +0100 Subject: [PATCH 07/11] feat: controller to handle front end requests. --- app/Http/Controllers/CmsProxyController.php | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 app/Http/Controllers/CmsProxyController.php diff --git a/app/Http/Controllers/CmsProxyController.php b/app/Http/Controllers/CmsProxyController.php new file mode 100644 index 00000000..fa49fe87 --- /dev/null +++ b/app/Http/Controllers/CmsProxyController.php @@ -0,0 +1,58 @@ +cmsClient = $cmsClient; + } + + /** + * Generic proxy for CMS API requests + * CSRF protection is automatically applied by Laravel's web middleware + * + * @return \Illuminate\Http\Response + */ + public function proxy(Request $request) + { + $endpoint = $request->input('endpoint'); + $params = $request->except(['endpoint', '_token']); + $method = $request->method(); + + $response = match ($method) { + 'GET' => $this->cmsClient->get($endpoint, $params), + 'POST' => $this->cmsClient->post($endpoint, $params), + default => abort(405, 'Method not allowed'), + }; + + return response($response->body()) + ->header('Content-Type', $response->header('Content-Type')) + ->header('Cache-Control', 'public, max-age=86400') + ->setStatusCode($response->status()); + } + + /** + * Proxy for 2D depiction - used by img tags + * No CSRF protection (called directly by browsers via img src) + * Rate limited via route middleware to prevent abuse + * + * @return \Illuminate\Http\Response + */ + public function depict2D(Request $request) + { + $params = $request->only(['smiles', 'height', 'width', 'toolkit', 'CIP']); + + $response = $this->cmsClient->get('depict/2D', $params); + + return response($response->body()) + ->header('Content-Type', $response->header('Content-Type') ?? 'image/svg+xml') + ->header('Cache-Control', 'public, max-age=86400'); + } +} From 48edf63af31f52f2e8ff10c2612203f473641e08 Mon Sep 17 00:00:00 2001 From: Sagar Date: Sun, 23 Nov 2025 12:20:09 +0100 Subject: [PATCH 08/11] feat: route and service config setup. --- config/services.php | 2 ++ routes/web.php | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/config/services.php b/config/services.php index d25f80cf..9073b4d1 100644 --- a/config/services.php +++ b/config/services.php @@ -52,6 +52,8 @@ 'cheminf' => [ 'api_url' => env('CM_PUBLIC_API', 'https://api.cheminf.studio/latest/'), 'internal_api_url' => env('API_URL', 'https://api.cheminf.studio/latest/'), + 'internal_token' => env('CMS_INTERNAL_AUTH_TOKEN'), + 'rate_limit' => env('CMS_RATE_LIMIT', 200), ], 'citation' => [ diff --git a/routes/web.php b/routes/web.php index d62ed6c9..d0bc41cd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,6 +2,7 @@ use App\Http\Controllers\ApplicationController; use App\Http\Controllers\Auth\SocialController; +use App\Http\Controllers\CmsProxyController; use App\Http\Controllers\CollectionController; use App\Http\Controllers\MoleculeController; use App\Livewire\About; @@ -43,6 +44,17 @@ Route::get('/collections', CollectionList::class)->name('collections.index'); +// CMS API Proxy - CSRF protected for generic proxy +Route::match(['GET', 'POST'], '/cms-proxy', [CmsProxyController::class, 'proxy'])->name('cms.proxy'); + +// CMS Depict endpoint - No CSRF needed (used in img tags) +// Rate limited per minute based on CMS_RATE_LIMIT env variable (default: 200, 0 = no limit) +$rateLimit = (int) config('services.cheminf.rate_limit', 200); +$depictRoute = Route::get('/cms/depict2d', [CmsProxyController::class, 'depict2D'])->name('cms.depict2d'); +if ($rateLimit > 0) { + $depictRoute->middleware('throttle:'.$rateLimit.',1'); +} + // Compound pages Route::get('compound/coconut_id/{id}', MoleculeController::class)->name('old_compound'); Route::get('compounds/{id}', MoleculeController::class)->name('compound'); From c044628faace8e3c5d14e06f77f4843283c31295 Mon Sep 17 00:00:00 2001 From: Sagar Date: Sun, 23 Nov 2025 12:22:48 +0100 Subject: [PATCH 09/11] feat: modified the cms api access for backend. --- app/Console/Commands/GenerateCoordinates.php | 24 ++++++++++---------- app/Jobs/ProcessEntry.php | 13 +++++++---- app/Jobs/ProcessEntryBatch.php | 13 +++++++---- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/app/Console/Commands/GenerateCoordinates.php b/app/Console/Commands/GenerateCoordinates.php index 51c25fb9..155e5348 100644 --- a/app/Console/Commands/GenerateCoordinates.php +++ b/app/Console/Commands/GenerateCoordinates.php @@ -5,9 +5,9 @@ use App\Models\Collection; use App\Models\Molecule; use App\Models\Structure; +use App\Services\CmsClient; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Throwable; @@ -63,21 +63,18 @@ public function handle() $progressBar->start(); // Process molecules in chunks using the static list of IDs. - $moleculeIds->chunk(10000)->each(function ($idsChunk) use ($progressBar) { + $cmsClient = app(CmsClient::class); + + $moleculeIds->chunk(10000)->each(function ($idsChunk) use ($progressBar, $cmsClient) { $mols = Molecule::whereIn('id', $idsChunk)->select('id', 'canonical_smiles')->get(); $data = []; foreach ($mols as $mol) { $id = $mol->id; $canonical_smiles = $mol->canonical_smiles; - // Build endpoints. - $apiUrl = config('services.cheminf.internal_api_url'); - $d2Endpoint = $apiUrl.'convert/mol2D?smiles='.urlencode($canonical_smiles).'&toolkit=rdkit'; - $d3Endpoint = $apiUrl.'convert/mol3D?smiles='.urlencode($canonical_smiles).'&toolkit=rdkit'; - // Fetch coordinates from API. - $d2 = $this->fetchFromApi($d2Endpoint, $canonical_smiles); - $d3 = $this->fetchFromApi($d3Endpoint, $canonical_smiles); + $d2 = $this->fetchFromApi($cmsClient, 'convert/mol2D', $canonical_smiles); + $d3 = $this->fetchFromApi($cmsClient, 'convert/mol3D', $canonical_smiles); // Accumulate data for batch insertion. $data[] = [ @@ -103,9 +100,9 @@ public function handle() /** * Make an HTTP GET request with basic retry/backoff handling (e.g. 429 Too Many Requests). * - * @return mixed (array|null) Returns the JSON-decoded response or null on failure. + * @return mixed (string|null) Returns the response body or null on failure. */ - private function fetchFromApi(string $endpoint, string $smiles) + private function fetchFromApi(CmsClient $cmsClient, string $endpoint, string $smiles) { $maxRetries = 3; $attempt = 0; @@ -113,7 +110,10 @@ private function fetchFromApi(string $endpoint, string $smiles) while ($attempt < $maxRetries) { try { - $response = Http::timeout(600)->get($endpoint); + $response = $cmsClient->get($endpoint, [ + 'smiles' => $smiles, + 'toolkit' => 'rdkit', + ], false); if ($response->successful()) { return $response->body(); diff --git a/app/Jobs/ProcessEntry.php b/app/Jobs/ProcessEntry.php index 7fe84406..6b6ef5a2 100644 --- a/app/Jobs/ProcessEntry.php +++ b/app/Jobs/ProcessEntry.php @@ -4,6 +4,7 @@ use App\Enums\ReportStatus; use App\Events\PrePublishJobFailed; +use App\Services\CmsClient; use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -11,7 +12,6 @@ use Illuminate\Http\Client\RequestException; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; class ProcessEntry implements ShouldQueue @@ -55,11 +55,16 @@ public function handle(): void $has_stereocenters = false; $is_invalid = false; $error_code = -1; - $API_URL = config('services.cheminf.internal_api_url'); - $ENDPOINT = $API_URL.'chem/coconut/pre-processing?smiles='.urlencode($canonical_smiles).'&_3d_mol=false&descriptors=false'; + + $cmsClient = app(CmsClient::class); try { - $response = Http::timeout(600)->get($ENDPOINT); + $response = $cmsClient->get('chem/coconut/pre-processing', [ + 'smiles' => $canonical_smiles, + '_3d_mol' => 'false', + 'descriptors' => 'false', + ], false); + if ($response->successful()) { $data = $response->json(); if (array_key_exists('original', $data)) { diff --git a/app/Jobs/ProcessEntryBatch.php b/app/Jobs/ProcessEntryBatch.php index c36f229b..a2dac9b5 100644 --- a/app/Jobs/ProcessEntryBatch.php +++ b/app/Jobs/ProcessEntryBatch.php @@ -5,6 +5,7 @@ use App\Enums\ReportStatus; use App\Events\PrePublishJobFailed; use App\Models\Entry; +use App\Services\CmsClient; use Exception; use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; @@ -13,7 +14,6 @@ use Illuminate\Http\Client\RequestException; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; class ProcessEntryBatch implements ShouldQueue @@ -121,11 +121,16 @@ public function processEntry(Entry $entry): void $is_cis_trans = false; $is_invalid = false; $error_code = -1; - $API_URL = config('services.cheminf.internal_api_url'); - $ENDPOINT = $API_URL.'chem/coconut/pre-processing?smiles='.urlencode($canonical_smiles).'&_3d_mol=false&descriptors=false'; + + $cmsClient = app(CmsClient::class); try { - $response = Http::timeout(600)->get($ENDPOINT); + $response = $cmsClient->get('chem/coconut/pre-processing', [ + 'smiles' => $canonical_smiles, + '_3d_mol' => 'false', + 'descriptors' => 'false', + ], false); + if ($response->successful()) { $data = $response->json(); if (array_key_exists('original', $data)) { From 56b1bba3f867f9fe06f1a6607c088de1066dac4f Mon Sep 17 00:00:00 2001 From: Sagar Date: Sun, 23 Nov 2025 12:23:19 +0100 Subject: [PATCH 10/11] feat: modified the cms access for frontend. --- .../EntriesRelationManager.php | 8 +++---- .../Dashboard/Resources/MoleculeResource.php | 2 +- .../MoleculesRelationManager.php | 2 +- .../RelatedRelationManager.php | 2 +- .../MoleculesRelationManager.php | 2 +- .../EntriesRelationManager.php | 8 +++---- .../MoleculesRelationManager.php | 2 +- app/Helper.php | 21 +++++++++++++++++++ app/Livewire/MoleculeDepict2d.php | 4 ++-- resources/views/molecule.blade.php | 4 ++-- 10 files changed, 38 insertions(+), 17 deletions(-) diff --git a/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php b/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php index b3e5e5e4..7d1569a2 100644 --- a/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php +++ b/app/Filament/Dashboard/Resources/CollectionResource/RelationManagers/EntriesRelationManager.php @@ -99,21 +99,21 @@ public function infolist(Infolist $infolist): Infolist ]) ->schema([ ImageEntry::make('parent_canonical_smiles')->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->parent_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->parent_canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('canonical_smiles')->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('standardized_canonical_smiles')->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->standardized_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->standardized_canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) @@ -133,7 +133,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/MoleculeResource.php b/app/Filament/Dashboard/Resources/MoleculeResource.php index 11894e10..46b31332 100644 --- a/app/Filament/Dashboard/Resources/MoleculeResource.php +++ b/app/Filament/Dashboard/Resources/MoleculeResource.php @@ -90,7 +90,7 @@ public static function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php index b47e153b..88516d97 100644 --- a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php +++ b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/MoleculesRelationManager.php @@ -26,7 +26,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php index ae132fdc..2599165b 100644 --- a/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php +++ b/app/Filament/Dashboard/Resources/MoleculeResource/RelationManagers/RelatedRelationManager.php @@ -26,7 +26,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php b/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php index a9359be7..5b8c03fc 100644 --- a/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php +++ b/app/Filament/Dashboard/Resources/OrganismResource/RelationManagers/MoleculesRelationManager.php @@ -40,7 +40,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php index 291a988e..6fb4b5ae 100644 --- a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php +++ b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/EntriesRelationManager.php @@ -94,21 +94,21 @@ public function infolist(Infolist $infolist): Infolist ]) ->schema([ ImageEntry::make('parent_canonical_smiles')->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->parent_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->parent_canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('canonical_smiles')->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) ->ring(5) ->defaultImageUrl(url('/images/placeholder.png')), ImageEntry::make('standardized_canonical_smiles')->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->standardized_canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->standardized_canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) @@ -128,7 +128,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) diff --git a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php index 107cda4a..d4834703 100644 --- a/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php +++ b/app/Filament/Dashboard/Resources/ReportResource/RelationManagers/MoleculesRelationManager.php @@ -35,7 +35,7 @@ public function table(Table $table): Table ImageColumn::make('structure')->square() ->label('Structure') ->state(function ($record) { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($record->canonical_smiles).'&height=300&width=300&CIP=true&toolkit=cdk'; + return getDepictUrl($record->canonical_smiles, 300, 300, 'cdk', true); }) ->width(200) ->height(200) diff --git a/app/Helper.php b/app/Helper.php index ef527c7f..03c37da7 100644 --- a/app/Helper.php +++ b/app/Helper.php @@ -638,3 +638,24 @@ function handleJobFailure( $batchId ); } + +/** + * Generate a URL for 2D molecule depiction through the CMS proxy + * + * @param string $smiles The SMILES string + * @param int $height Image height (default: 300) + * @param int $width Image width (default: 300) + * @param string $toolkit Toolkit to use: cdk, rdkit, openbabel (default: cdk) + * @param bool $CIP Whether to include CIP labels (default: true) + * @return string The proxy URL + */ +function getDepictUrl(string $smiles, int $height = 300, int $width = 300, string $toolkit = 'cdk', bool $CIP = true): string +{ + return route('cms.depict2d', [ + 'smiles' => $smiles, + 'height' => $height, + 'width' => $width, + 'toolkit' => $toolkit, + 'CIP' => $CIP ? 'true' : 'false', + ]); +} diff --git a/app/Livewire/MoleculeDepict2d.php b/app/Livewire/MoleculeDepict2d.php index bc5d1478..49a96a74 100644 --- a/app/Livewire/MoleculeDepict2d.php +++ b/app/Livewire/MoleculeDepict2d.php @@ -28,13 +28,13 @@ class MoleculeDepict2d extends Component #[Computed] public function source() { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($this->smiles).'&height='.$this->height.'&width='.$this->width.'&toolkit='.$this->toolkit.'&CIP='.$this->CIP; + return getDepictUrl($this->smiles, $this->height, $this->width, $this->toolkit, $this->CIP); } #[Computed] public function preview() { - return config('services.cheminf.api_url').'depict/2D?smiles='.urlencode($this->smiles); + return getDepictUrl($this->smiles); } public function downloadMolFile($toolkit) diff --git a/resources/views/molecule.blade.php b/resources/views/molecule.blade.php index 637ab628..46111b5e 100644 --- a/resources/views/molecule.blade.php +++ b/resources/views/molecule.blade.php @@ -34,7 +34,7 @@ + content="{{ getDepictUrl($molecule->canonical_smiles, 630, 1200, 'cdk', true) ?? asset('img/coconut-og-image.png') }}"> @@ -56,7 +56,7 @@ and found in the collection{{ $molecule->collections->count() > 1 ? 's' : '' }}: {{ implode(', ', $molecule->collections->pluck('title')->toArray()) }} @endif"> + content="{{ getDepictUrl($molecule->canonical_smiles, 630, 1200, 'cdk', true) ?? asset('img/coconut-og-image.png') }}"> From 98caa6c6bc9f229c292bcc0cd4fb4b670b51544e Mon Sep 17 00:00:00 2001 From: Sagar Date: Sun, 23 Nov 2025 12:23:46 +0100 Subject: [PATCH 11/11] feat: added example env content. --- .env.example | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.env.example b/.env.example index 2d7f02fd..c3d44a68 100644 --- a/.env.example +++ b/.env.example @@ -57,3 +57,13 @@ VITE_PUSHER_HOST="${PUSHER_HOST}" VITE_PUSHER_PORT="${PUSHER_PORT}" VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +# CMS (ChemInformatics Microservice) API Configuration +# API_URL: Internal/Docker network URL for backend requests +# CM_PUBLIC_API: Public URL for frontend/proxy requests +# CMS_INTERNAL_AUTH_TOKEN: Authentication token to bypass rate limits +# CMS_RATE_LIMIT: Maximum requests per minute to /cms/depict2d endpoint (default: 200, set to 0 to disable) +API_URL=https://api.cheminf.studio/latest/ +CM_PUBLIC_API=https://api.cheminf.studio/latest/ +CMS_INTERNAL_AUTH_TOKEN= +CMS_RATE_LIMIT=200