Skip to content

Commit 1589c09

Browse files
Merge pull request #1260 from NFDI4Chem/development
Development
2 parents f2c85bb + 2f13b41 commit 1589c09

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+4350
-1988
lines changed

.env.example

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ ORCID_CLIENT_SECRET=
104104
ORCID_REDIRECT_URL=http://localhost/auth/login/orcid/callback
105105
ORCID_ENVIRONMENT=sandbox
106106

107+
NFDIAAI_CLIENT_ID=
108+
NFDIAAI_CLIENT_SECRET=
109+
NFDIAAI_REDIRECT_URL="${APP_URL}/auth/login/regapp/callback"
110+
107111
TELESCOPE_ENABLED=false
108112

109113
#DATACITE Properties
@@ -117,4 +121,6 @@ NMRKIT_URL=https://nodejs.nmrxiv.org
117121
CAS_URL=https://commonchemistry.cas.org
118122
PUBCHEM_URL=https://pubchem.ncbi.nlm.nih.gov
119123
COMMON_CHEMISTRY_URL=https://commonchemistry.cas.org
120-
CHEMISTRY_STANDARDIZE_URL=https://api.cheminf.studio/latest/chem/standardize
124+
CHEMISTRY_STANDARDIZE_URL=https://api.cheminf.studio/latest/chem/standardize
125+
126+
BACKUP_KEEP_DAYS=7

.github/workflows/dev-build.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@ env:
1616
REPOSITORY_NAMESPACE: nfdi4chem
1717

1818
jobs:
19+
lint-security:
20+
name: Lint & Security (reusable)
21+
uses: ./.github/workflows/lint-security-check.yml
22+
permissions:
23+
contents: read
24+
security-events: write
25+
with:
26+
run_php: true
27+
run_js: true
28+
run_secrets: true
29+
1930
# Build and publish Docker images for the development environment
2031
setup-build-publish-deploy:
2132
name: Build & deploy to development
2233
runs-on: ubuntu-latest
23-
# Tip: gate deploys on tests with `needs: php-unit-test` once a tests job exists
34+
needs: [lint-security]
35+
2436
# Environment provides secrets and protection rules
2537
environment:
2638
name: Dev
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Lint and Security Checks
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
run_php:
7+
description: "Run PHP lint & security job"
8+
required: false
9+
type: boolean
10+
default: true
11+
run_js:
12+
description: "Run JS lint & security job"
13+
required: false
14+
type: boolean
15+
default: true
16+
run_secrets:
17+
description: "Run secret leakage detection job"
18+
required: false
19+
type: boolean
20+
default: true
21+
22+
permissions:
23+
contents: read
24+
security-events: write
25+
26+
concurrency:
27+
group: lint-security-${{ github.ref }}
28+
cancel-in-progress: true
29+
30+
jobs:
31+
# Lints PHP code style (Pint) and audits Composer dependencies for vulnerabilities
32+
php:
33+
if: ${{ inputs.run_php }}
34+
name: PHP Lint & Security
35+
runs-on: ubuntu-latest
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v4
39+
40+
- name: Setup PHP
41+
uses: shivammathur/setup-php@v2
42+
with:
43+
php-version: '8.3'
44+
coverage: none
45+
tools: composer
46+
extensions: mbstring, intl, pdo, pdo_mysql, pdo_pgsql
47+
ini-values: memory_limit=512M
48+
49+
- name: Cache Composer dependencies
50+
uses: actions/cache@v4
51+
with:
52+
path: |
53+
~/.composer/cache/files
54+
~/.cache/composer/files
55+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
56+
restore-keys: |
57+
${{ runner.os }}-composer-
58+
- name: Validate composer.json
59+
run: composer validate --no-check-publish --strict
60+
61+
- name: Install PHP dependencies
62+
run: composer install --no-interaction --prefer-dist --no-progress
63+
64+
- name: PHP code style (Pint)
65+
run: vendor/bin/pint --test
66+
67+
- name: Composer security audit
68+
run: composer audit --no-interaction --locked
69+
70+
# Runs JS/Vue linting (ESLint), formatting check (Prettier), and npm security audit
71+
js:
72+
if: ${{ inputs.run_js }}
73+
name: JS Lint & Security
74+
runs-on: ubuntu-latest
75+
steps:
76+
- name: Checkout
77+
uses: actions/checkout@v4
78+
79+
- name: Setup Node.js
80+
uses: actions/setup-node@v4
81+
with:
82+
node-version: '20'
83+
cache: 'npm'
84+
85+
- name: Install Node dependencies
86+
run: npm ci
87+
88+
- name: ESLint
89+
run: npx eslint --ext .js,.vue --ignore-path .gitignore resources/js
90+
91+
- name: Prettier (check)
92+
run: npx prettier resources/js --check
93+
94+
- name: npm audit (high+)
95+
run: npm audit --audit-level=high
96+
97+
# Scans the repository for hard-coded secrets using Gitleaks
98+
secrets:
99+
if: ${{ inputs.run_secrets }}
100+
name: Secret Leakage Scan
101+
runs-on: ubuntu-latest
102+
steps:
103+
- name: Checkout
104+
uses: actions/checkout@v4
105+
106+
- name: Install Gitleaks
107+
run: |
108+
set -e
109+
GITLEAKS_VERSION=8.18.4
110+
curl -sSL -o gitleaks.tar.gz "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
111+
tar -xzf gitleaks.tar.gz gitleaks
112+
sudo mv gitleaks /usr/local/bin/gitleaks
113+
gitleaks version
114+
115+
- name: Gitleaks scan
116+
id: gitleaks
117+
run: |
118+
set +e
119+
gitleaks detect --source . --no-git --redact --report-format sarif --report-path gitleaks.sarif
120+
code=$?
121+
echo "$code" > gitleaks-exit-code.txt
122+
if [ ! -f gitleaks.sarif ]; then
123+
echo "Gitleaks did not produce SARIF file; creating empty stub." >&2
124+
printf '%s\n' '{' \
125+
' "version": "2.1.0",' \
126+
' "runs": [' \
127+
' {' \
128+
' "tool": { "driver": { "name": "Gitleaks", "informationUri": "https://github.com/gitleaks/gitleaks" } },' \
129+
' "results": []' \
130+
' }' \
131+
' ]' \
132+
'}' > gitleaks.sarif
133+
fi
134+
# Always succeed here; we fail after uploading SARIF for annotations
135+
exit 0
136+
137+
- name: Upload SARIF (code scanning)
138+
if: always()
139+
uses: github/codeql-action/upload-sarif@v3
140+
with:
141+
sarif_file: gitleaks.sarif
142+
- name: Fail if Gitleaks found leaks
143+
run: |
144+
code=$(cat gitleaks-exit-code.txt || echo 0)
145+
if [ "$code" -ne 0 ]; then
146+
echo "Gitleaks detected potential secrets (exit code $code). Failing job after SARIF upload." >&2
147+
exit 1
148+
fi

.github/workflows/pr-lint.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: PR Lint & Security
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
6+
branches:
7+
- development
8+
- main
9+
10+
permissions:
11+
contents: read
12+
security-events: write
13+
14+
jobs:
15+
lint-security:
16+
name: Lint & Security (PR)
17+
uses: ./.github/workflows/lint-security-check.yml
18+
with:
19+
run_php: true
20+
run_js: true
21+
run_secrets: true

.github/workflows/prod-build.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,22 @@ env:
2020
REPOSITORY_NAMESPACE: nfdi4chem
2121

2222
jobs:
23+
lint-security:
24+
name: Lint & Security (reusable)
25+
uses: ./.github/workflows/lint-security-check.yml
26+
permissions:
27+
contents: read
28+
security-events: write
29+
with:
30+
run_php: true
31+
run_js: true
32+
run_secrets: true
33+
2334
# Guard: confirm input and authorize actor
2435
guard:
2536
name: Access control and confirmation
2637
runs-on: ubuntu-latest
38+
needs: [lint-security]
2739
steps:
2840
- name: Validate actor and confirmation
2941
shell: bash

.github/workflows/test.yml

Lines changed: 0 additions & 52 deletions
This file was deleted.

.phpunit.cache/test-results

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Jobs\CleanupBackupsJob;
6+
use Illuminate\Console\Command;
7+
8+
class CleanupBackups extends Command
9+
{
10+
/**
11+
* The name and signature of the console command.
12+
*
13+
* @var string
14+
*/
15+
protected $signature = 'nmrxiv:backup-cleanup';
16+
17+
/**
18+
* The console command description.
19+
*
20+
* @var string
21+
*/
22+
protected $description = 'Cleanup of postgres backups from ceph and retain only last 7 backups.';
23+
24+
/**
25+
* Execute the console command.
26+
*/
27+
public function handle(): void
28+
{
29+
CleanupBackupsJob::dispatch();
30+
}
31+
}

app/Http/Controllers/ApplicationController.php

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,63 @@ class ApplicationController extends Controller
2121
public function compounds(Request $request)
2222
{
2323
$query = $request->get('query');
24-
$limit = $request->get('limit') ? $limit : 24;
24+
$limit = $request->get('limit') ? $request->get('limit') : 24;
2525
$page = $request->query('page');
2626
$tagType = $request->query('tagType') ? $request->query('tagType') : null;
2727

2828
return Inertia::render('Public/Compounds', compact(['query', 'limit', 'page', 'tagType']));
2929
}
3030

31+
/**
32+
* Resolve compound by ID and render the appropriate view
33+
*
34+
* @return Inertia\Inertia
35+
*/
36+
public function resolveCompound(Request $request, $identifier)
37+
{
38+
$resolvedModel = resolveIdentifier($identifier);
39+
$namespace = $resolvedModel['namespace'];
40+
$model = $resolvedModel['model'];
41+
42+
if ($model && $namespace === 'Molecule') {
43+
// Redirect to spectra page with compound parameter for now
44+
// This maintains the current compound viewing functionality
45+
return redirect('/spectra?compound='.substr($identifier, 1));
46+
} else {
47+
abort(404, 'Compound not found');
48+
}
49+
}
50+
51+
/**
52+
* Resolve sample by ID and render the appropriate view
53+
*
54+
* @return Inertia\Inertia
55+
*/
56+
public function resolveSample(Request $request, $identifier)
57+
{
58+
return $this->resolve($request, $identifier);
59+
}
60+
61+
/**
62+
* Resolve project by ID and render the appropriate view
63+
*
64+
* @return Inertia\Inertia
65+
*/
66+
public function resolveProject(Request $request, $identifier)
67+
{
68+
return $this->resolve($request, $identifier);
69+
}
70+
71+
/**
72+
* Resolve dataset by ID and render the appropriate view
73+
*
74+
* @return Inertia\Inertia
75+
*/
76+
public function resolveDataset(Request $request, $identifier)
77+
{
78+
return $this->resolve($request, $identifier);
79+
}
80+
3181
/**
3282
* Resolve the incoming request into right models and render the
3383
* inertia view

app/Http/Middleware/HandleInertiaRequests.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public function share(Request $request)
6363
'twitter' => (env('TWITTER_CLIENT_ID') !== null && env('TWITTER_CLIENT_ID') !== ''),
6464
'github' => (env('GITHUB_CLIENT_ID') !== null && env('GITHUB_CLIENT_ID') !== ''),
6565
'orcid' => (env('ORCID_CLIENT_ID') !== null && env('ORCID_CLIENT_ID') !== ''),
66+
'nfdiaai' => (env('NFDIAAI_CLIENT_ID') !== null && env('NFDIAAI_CLIENT_ID') !== ''),
6667
'config.announcements' => Schema::hasTable('announcements') ? Announcement::active() : null,
6768
'url' => env('APP_URL'),
6869
'nmriumURL' => env('NMRIUM_URL'),

0 commit comments

Comments
 (0)