Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
51ccdeb
test: add comprehensive tests for compound route resolution
vcnainala Feb 2, 2026
f77120a
fix(security): add generic language files for auth and password reset
vcnainala Feb 2, 2026
ecb3872
fix(security): use generic error message for unverified accounts in A…
vcnainala Feb 2, 2026
c58d7c2
fix(security): use generic error messages for member invitation flows
vcnainala Feb 2, 2026
31ea48e
fix(security): remove owner.email filter from public API endpoints
vcnainala Feb 2, 2026
bf54a1f
feat(assets): add university partner logos
vcnainala Feb 2, 2026
cbe474c
feat(faqs): redesign FAQ component with updated content
vcnainala Feb 2, 2026
214fa2d
feat(welcome): redesign partner cards section with noBS initiative co…
vcnainala Feb 2, 2026
85ceb25
feat(footer): add reusable Footer component
vcnainala Feb 2, 2026
a3e60d8
feat(legal): redesign Privacy Policy and Terms of Service pages
vcnainala Feb 2, 2026
72cda52
refactor(about): replace inline footer with shared Footer component
vcnainala Feb 2, 2026
3ee8d9f
style(structure-search): update button styling for consistency
vcnainala Feb 2, 2026
ab51020
fix(snapshots): add error handling and auto-skip for failed spectra i…
vcnainala Feb 2, 2026
b636286
build: compile frontend assets with snapshot error handling fixes
vcnainala Feb 2, 2026
3fad6c5
Merge branch 'development' into fix/snapshot-error-handling
vcnainala Feb 9, 2026
fcd5c5f
Merge branch 'development' into fix/snapshot-error-handling
vcnainala May 5, 2026
cee9526
feat(ui): add shared public site header and navigation utilities
vcnainala Jun 3, 2026
dc04482
feat(pages): add dedicated FAQs Inertia page and route
vcnainala Jun 3, 2026
31ab123
refactor(ui): adopt shared header and refresh public-facing pages
vcnainala Jun 3, 2026
ecd2736
refactor(search): improve unified search and structure editor UX
vcnainala Jun 3, 2026
4846166
style(ui): polish authenticated app layout header controls
vcnainala Jun 3, 2026
bb9c21c
test: add feature coverage for public Inertia pages
vcnainala Jun 3, 2026
86534ee
chore(build): update frontend lockfiles and Vite openchemlib config
vcnainala Jun 3, 2026
41f1df9
chore(ops): raise PHP memory limits and document Horizon in Sail
vcnainala Jun 3, 2026
34c905f
fix(compounds): repair missing compound info during processing
vcnainala Jun 3, 2026
ef191ae
fix(backup): harden postgres dump job error handling
vcnainala Jun 3, 2026
3923bc8
fix(auth): align project and study policies with member access
vcnainala Jun 3, 2026
c54567e
feat(search): add catalog text search API with legacy route support
vcnainala Jun 3, 2026
1fe41fe
feat(search): add public text search Inertia page and client API helpers
vcnainala Jun 3, 2026
dee9ada
refactor(public): refresh public project, study, and compound pages
vcnainala Jun 3, 2026
eec3e35
feat(dashboard): improve workspace filters and project listing UX
vcnainala Jun 3, 2026
edb8583
fix(projects): resolve owner viewer_role when not on members pivot
vcnainala Jun 3, 2026
80c680a
fix(tests): align member and login tests with generic error responses
vcnainala Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ BROADCAST_CONNECTION=log
CACHE_STORE=file
QUEUE_CONNECTION=redis

# Horizon workers start automatically with Sail (see docker/8.4/supervisord.conf).
# After changing queue/redis settings, run: ./vendor/bin/sail artisan horizon:terminate

SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
Expand Down
4 changes: 2 additions & 2 deletions app/Actions/Project/AddProjectMember.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected function validate($project, string $email, ?string $role)
'email' => $email,
'role' => $role,
], $this->rules(), [
'email.exists' => __('We were unable to find a registered user with this email address.'),
'email.exists' => __('Unable to add member with this email address.'),
])->after(
$this->ensureUserIsNotAlreadyOnProject($project, $email)
)->validateWithBag('addModelMember');
Expand Down Expand Up @@ -80,7 +80,7 @@ protected function ensureUserIsNotAlreadyOnProject($project, string $email)
$validator->errors()->addIf(
$project->hasUserWithEmail($email),
'email',
__('This user already belongs to the project.')
__('Unable to add member with this email address.')
);
};
}
Expand Down
2 changes: 1 addition & 1 deletion app/Actions/Project/InviteProjectMember.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class InviteProjectMember
*/
public function invite($user, $project, string $email, ?string $role = null, ?string $message = null)
{
// Gate::forUser($user)->authorize('addProjectMember', $project);
Gate::forUser($user)->authorize('addProjectMember', $project);

$this->validate($project, $email, $role, $message);

Expand Down
29 changes: 2 additions & 27 deletions app/Console/Commands/RefreshNmriumInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Http\Controllers\StudyController;
use App\Models\Study;
use App\Support\Nmr\JcampDatasetClassifier;
use App\Support\Nmr\NmriumSpectraInfoInspector;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
Expand Down Expand Up @@ -75,7 +76,7 @@ public function handle(): int
}

$jcampBackfilled = $this->backfillUnparsedJcampDatasets($study);
if (! $this->needsRefresh($spectra)) {
if (! NmriumSpectraInfoInspector::needsRefresh($spectra)) {
if ($jcampBackfilled > 0) {
$this->info(sprintf('study %d %s — info OK; classified %d JCAMP-only file(s)', $study->id, $study->name, $jcampBackfilled));
$touched++;
Expand Down Expand Up @@ -237,32 +238,6 @@ protected function spectrumKey(array $spectrum): ?string
return null;
}

/**
* Heuristically decide whether at least one spectrum in the stored
* payload is missing real metadata. We treat `info` as "needs refresh"
* when it is empty, missing the `experiment` key, or has been corrupted
* with raw `{im, re}` payloads from the legacy SpectraEditor bug.
*
* @param array<int, array<string, mixed>> $spectra
*/
protected function needsRefresh(array $spectra): bool
{
foreach ($spectra as $spec) {
$info = $spec['info'] ?? null;
if (! is_array($info) || empty($info)) {
return true;
}
if (isset($info['im']) || isset($info['re'])) {
return true;
}
if (! array_key_exists('experiment', $info) && ! array_key_exists('nucleus', $info)) {
return true;
}
}

return false;
}

/**
* Re-derive `dataset.type` for every dataset of the study using the same
* spectrum-to-dataset matching the controller does on save.
Expand Down
88 changes: 88 additions & 0 deletions app/Console/Commands/RepairMissingCompoundInfoCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace App\Console\Commands;

use App\Support\Molecules\MoleculeEnrichmentInspector;
use App\Support\Nmr\NmriumSpectraInfoInspector;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;

class RepairMissingCompoundInfoCommand extends Command
{
protected $signature = 'nmrxiv:repair-missing-compound-info
{--molecule-limit=10 : Max molecules to enrich per run}
{--nmrium : Only repair NMRium spectrum metadata}
{--molecules : Only enrich molecule records}';

protected $description = 'Detect missing compound / spectrum metadata and run the appropriate repair commands';

public function handle(): int
{
$onlyNmrium = (bool) $this->option('nmrium');
$onlyMolecules = (bool) $this->option('molecules');
$runBoth = ! $onlyNmrium && ! $onlyMolecules;

$exitCode = self::SUCCESS;

if ($runBoth || $onlyMolecules) {
$exitCode = max($exitCode, $this->repairMoleculesIfNeeded());
}

if ($runBoth || $onlyNmrium) {
$exitCode = max($exitCode, $this->repairNmriumIfNeeded());
}

return $exitCode;
}

protected function repairMoleculesIfNeeded(): int
{
$pending = MoleculeEnrichmentInspector::needingEnrichmentQuery()->count();

if ($pending === 0) {
$this->line('No molecules with missing compound metadata.');

return self::SUCCESS;
}

$limit = max(1, (int) $this->option('molecule-limit'));

$this->info(sprintf(
'Found %d molecule(s) with missing compound metadata; enriching up to %d.',
$pending,
min($pending, $limit)
));

return Artisan::call('nmrxiv:molecules-clean', [
'--force' => true,
'--limit' => min($pending, $limit),
], $this->output);
}

protected function repairNmriumIfNeeded(): int
{
if (rtrim((string) config('external-links.nmrkit_url'), '/') === '') {
$this->warn('NMRKIT_URL is not configured; skipping NMRium metadata repair.');

return self::SUCCESS;
}

[$studyId, $pending] = NmriumSpectraInfoInspector::firstStudyNeedingRefresh();

if ($pending === 0) {
$this->line('No studies with missing NMRium spectrum metadata.');

return self::SUCCESS;
}

$this->info(sprintf(
'Found %d study/studies with missing NMRium spectrum metadata; refreshing study %d.',
$pending,
$studyId
));

return Artisan::call('nmrxiv:refresh-nmrium-info', [
'--study' => $studyId,
], $this->output);
}
}
4 changes: 2 additions & 2 deletions app/Http/Controllers/API/Auth/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ public function login(Request $request): JsonResponse

if (! $user->hasVerifiedEmail()) {
return response()->json([
'message' => 'Account is not yet verified. Please verify your email address by clicking on the link we just emailed to you.',
], 403);
'message' => 'Invalid login details',
], 401);
}

$token = $user->createToken('auth_token')->plainTextToken;
Expand Down
Loading
Loading