fix: script portability and npm security updates

Scripts:
- stop.sh: replace Linux-only fuser with cross-platform lsof fallback
- start.sh: parameterize port (APP_PORT) and container name (dynamic lookup)
- app-dev-start.sh: cross-platform stat (GNU -c / BSD -f) and setpriv/su fallback
- deploy-compose.sh: parameterize Docker registry via DOCKER_REGISTRY env var
- harden-postgres.sh: make DB_USER and DB_NAME configurable via env vars

NPM security:
- next: 15.5.12 → 15.5.15 (fixes HTTP request smuggling CVE)
- nodemailer: 8.0.1 → 8.0.5 (fixes SMTP command injection CVEs)
- lodash-es: add pnpm override to force >=4.18.0 (fixes code injection + prototype pollution)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 14:06:58 +02:00
parent e4bf121b33
commit 78d50b78d3
9 changed files with 102 additions and 76 deletions
+1 -1
View File
@@ -34,7 +34,7 @@
"dompurify": "^3.3.3", "dompurify": "^3.3.3",
"exceljs": "^4.4.0", "exceljs": "^4.4.0",
"framer-motion": "^12.38.0", "framer-motion": "^12.38.0",
"next": "^15.1.7", "next": "^15.5.15",
"next-auth": "^5.0.0-beta.25", "next-auth": "^5.0.0-beta.25",
"otpauth": "^9.5.0", "otpauth": "^9.5.0",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
+2 -1
View File
@@ -49,7 +49,8 @@
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"flatted": "^3.4.2", "flatted": "^3.4.2",
"picomatch": "^4.0.4" "picomatch": "^4.0.4",
"lodash-es": "^4.18.0"
} }
}, },
"packageManager": "pnpm@9.14.2", "packageManager": "pnpm@9.14.2",
+1 -1
View File
@@ -28,7 +28,7 @@
"@trpc/server": "^11.0.0", "@trpc/server": "^11.0.0",
"@types/nodemailer": "^7.0.11", "@types/nodemailer": "^7.0.11",
"ioredis": "^5.10.0", "ioredis": "^5.10.0",
"nodemailer": "^8.0.1", "nodemailer": "^8.0.5",
"openai": "^6.27.0", "openai": "^6.27.0",
"otpauth": "^9.5.0", "otpauth": "^9.5.0",
"pino": "^10.3.1", "pino": "^10.3.1",
+57 -56
View File
@@ -7,6 +7,7 @@ settings:
overrides: overrides:
flatted: ^3.4.2 flatted: ^3.4.2
picomatch: ^4.0.4 picomatch: ^4.0.4
lodash-es: ^4.18.0
importers: importers:
@@ -66,7 +67,7 @@ importers:
version: 4.3.2(react@19.2.4) version: 4.3.2(react@19.2.4)
'@sentry/nextjs': '@sentry/nextjs':
specifier: ^10.45.0 specifier: ^10.45.0
version: 10.45.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(next@15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.105.4) version: 10.45.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(next@15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.105.4)
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^5.62.16 specifier: ^5.62.16
version: 5.90.21(react@19.2.4) version: 5.90.21(react@19.2.4)
@@ -98,11 +99,11 @@ importers:
specifier: ^12.38.0 specifier: ^12.38.0
version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
next: next:
specifier: ^15.1.7 specifier: ^15.5.15
version: 15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
next-auth: next-auth:
specifier: ^5.0.0-beta.25 specifier: ^5.0.0-beta.25
version: 5.0.0-beta.30(next@15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) version: 5.0.0-beta.30(next@15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
otpauth: otpauth:
specifier: ^9.5.0 specifier: ^9.5.0
version: 9.5.0 version: 9.5.0
@@ -210,8 +211,8 @@ importers:
specifier: ^5.10.0 specifier: ^5.10.0
version: 5.10.0 version: 5.10.0
nodemailer: nodemailer:
specifier: ^8.0.1 specifier: ^8.0.5
version: 8.0.1 version: 8.0.5
openai: openai:
specifier: ^6.27.0 specifier: ^6.27.0
version: 6.27.0(zod@3.25.76) version: 6.27.0(zod@3.25.76)
@@ -1049,53 +1050,53 @@ packages:
'@napi-rs/wasm-runtime@0.2.12': '@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
'@next/env@15.5.12': '@next/env@15.5.15':
resolution: {integrity: sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==} resolution: {integrity: sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg==}
'@next/swc-darwin-arm64@15.5.12': '@next/swc-darwin-arm64@15.5.15':
resolution: {integrity: sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==} resolution: {integrity: sha512-6PvFO2Tzt10GFK2Ro9tAVEtacMqRmTarYMFKAnV2vYMdwWc73xzmDQyAV7SwEdMhzmiRoo7+m88DuiXlJlGeaw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@next/swc-darwin-x64@15.5.12': '@next/swc-darwin-x64@15.5.15':
resolution: {integrity: sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==} resolution: {integrity: sha512-G+YNV+z6FDZTp/+IdGyIMFqalBTaQSnvAA+X/hrt+eaTRFSznRMz9K7rTmzvM6tDmKegNtyzgufZW0HwVzEqaQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@next/swc-linux-arm64-gnu@15.5.12': '@next/swc-linux-arm64-gnu@15.5.15':
resolution: {integrity: sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==} resolution: {integrity: sha512-eVkrMcVIBqGfXB+QUC7jjZ94Z6uX/dNStbQFabewAnk13Uy18Igd1YZ/GtPRzdhtm7QwC0e6o7zOQecul4iC1w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-musl@15.5.12': '@next/swc-linux-arm64-musl@15.5.15':
resolution: {integrity: sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==} resolution: {integrity: sha512-RwSHKMQ7InLy5GfkY2/n5PcFycKA08qI1VST78n09nN36nUPqCvGSMiLXlfUmzmpQpF6XeBYP2KRWHi0UW3uNg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-x64-gnu@15.5.12': '@next/swc-linux-x64-gnu@15.5.15':
resolution: {integrity: sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==} resolution: {integrity: sha512-nplqvY86LakS+eeiuWsNWvfmK8pFcOEW7ZtVRt4QH70lL+0x6LG/m1OpJ/tvrbwjmR8HH9/fH2jzW1GlL03TIg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-musl@15.5.12': '@next/swc-linux-x64-musl@15.5.15':
resolution: {integrity: sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==} resolution: {integrity: sha512-eAgl9NKQ84/sww0v81DQINl/vL2IBxD7sMybd0cWRw6wqgouVI53brVRBrggqBRP/NWeIAE1dm5cbKYoiMlqDQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-win32-arm64-msvc@15.5.12': '@next/swc-win32-arm64-msvc@15.5.15':
resolution: {integrity: sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==} resolution: {integrity: sha512-GJVZC86lzSquh0MtvZT+L7G8+jMnJcldloOjA8Kf3wXvBrvb6OGe2MzPuALxFshSm/IpwUtD2mIoof39ymf52A==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@next/swc-win32-x64-msvc@15.5.12': '@next/swc-win32-x64-msvc@15.5.15':
resolution: {integrity: sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==} resolution: {integrity: sha512-nFucjVdwlFqxh/JG3hWSJ4p8+YJV7Ii8aPDuBQULB6DzUF4UNZETXLfEUk+oI2zEznWWULPt7MeuTE6xtK1HSA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@@ -3352,8 +3353,8 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'} engines: {node: '>=10'}
lodash-es@4.17.23: lodash-es@4.18.1:
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
lodash.defaults@4.2.0: lodash.defaults@4.2.0:
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
@@ -3525,8 +3526,8 @@ packages:
nodemailer: nodemailer:
optional: true optional: true
next@15.5.12: next@15.5.15:
resolution: {integrity: sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==} resolution: {integrity: sha512-VSqCrJwtLVGwAVE0Sb/yikrQfkwkZW9p+lL/J4+xe+G3ZA+QnWPqgcfH1tDUEuk9y+pthzzVFp4L/U8JerMfMQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -3573,8 +3574,8 @@ packages:
node-releases@2.0.27: node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
nodemailer@8.0.1: nodemailer@8.0.5:
resolution: {integrity: sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg==} resolution: {integrity: sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
normalize-path@3.0.0: normalize-path@3.0.0:
@@ -5224,30 +5225,30 @@ snapshots:
'@tybys/wasm-util': 0.10.1 '@tybys/wasm-util': 0.10.1
optional: true optional: true
'@next/env@15.5.12': {} '@next/env@15.5.15': {}
'@next/swc-darwin-arm64@15.5.12': '@next/swc-darwin-arm64@15.5.15':
optional: true optional: true
'@next/swc-darwin-x64@15.5.12': '@next/swc-darwin-x64@15.5.15':
optional: true optional: true
'@next/swc-linux-arm64-gnu@15.5.12': '@next/swc-linux-arm64-gnu@15.5.15':
optional: true optional: true
'@next/swc-linux-arm64-musl@15.5.12': '@next/swc-linux-arm64-musl@15.5.15':
optional: true optional: true
'@next/swc-linux-x64-gnu@15.5.12': '@next/swc-linux-x64-gnu@15.5.15':
optional: true optional: true
'@next/swc-linux-x64-musl@15.5.12': '@next/swc-linux-x64-musl@15.5.15':
optional: true optional: true
'@next/swc-win32-arm64-msvc@15.5.12': '@next/swc-win32-arm64-msvc@15.5.15':
optional: true optional: true
'@next/swc-win32-x64-msvc@15.5.12': '@next/swc-win32-x64-msvc@15.5.15':
optional: true optional: true
'@noble/hashes@2.0.1': {} '@noble/hashes@2.0.1': {}
@@ -5928,7 +5929,7 @@ snapshots:
'@sentry/core@10.45.0': {} '@sentry/core@10.45.0': {}
'@sentry/nextjs@10.45.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(next@15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.105.4)': '@sentry/nextjs@10.45.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(next@15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.105.4)':
dependencies: dependencies:
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/semantic-conventions': 1.40.0
@@ -5941,7 +5942,7 @@ snapshots:
'@sentry/react': 10.45.0(react@19.2.4) '@sentry/react': 10.45.0(react@19.2.4)
'@sentry/vercel-edge': 10.45.0 '@sentry/vercel-edge': 10.45.0
'@sentry/webpack-plugin': 5.1.1(webpack@5.105.4) '@sentry/webpack-plugin': 5.1.1(webpack@5.105.4)
next: 15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: 15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
rollup: 4.59.0 rollup: 4.59.0
stacktrace-parser: 0.1.11 stacktrace-parser: 0.1.11
transitivePeerDependencies: transitivePeerDependencies:
@@ -7784,7 +7785,7 @@ snapshots:
kapsule@1.16.3: kapsule@1.16.3:
dependencies: dependencies:
lodash-es: 4.17.23 lodash-es: 4.18.1
keyv@4.5.4: keyv@4.5.4:
dependencies: dependencies:
@@ -7824,7 +7825,7 @@ snapshots:
dependencies: dependencies:
p-locate: 5.0.0 p-locate: 5.0.0
lodash-es@4.17.23: {} lodash-es@4.18.1: {}
lodash.defaults@4.2.0: {} lodash.defaults@4.2.0: {}
@@ -7951,15 +7952,15 @@ snapshots:
neo-async@2.6.2: {} neo-async@2.6.2: {}
next-auth@5.0.0-beta.30(next@15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4): next-auth@5.0.0-beta.30(next@15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4):
dependencies: dependencies:
'@auth/core': 0.41.0 '@auth/core': 0.41.0
next: 15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: 15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react: 19.2.4 react: 19.2.4
next@15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): next@15.5.15(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies: dependencies:
'@next/env': 15.5.12 '@next/env': 15.5.15
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001776 caniuse-lite: 1.0.30001776
postcss: 8.4.31 postcss: 8.4.31
@@ -7967,14 +7968,14 @@ snapshots:
react-dom: 19.2.4(react@19.2.4) react-dom: 19.2.4(react@19.2.4)
styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4)
optionalDependencies: optionalDependencies:
'@next/swc-darwin-arm64': 15.5.12 '@next/swc-darwin-arm64': 15.5.15
'@next/swc-darwin-x64': 15.5.12 '@next/swc-darwin-x64': 15.5.15
'@next/swc-linux-arm64-gnu': 15.5.12 '@next/swc-linux-arm64-gnu': 15.5.15
'@next/swc-linux-arm64-musl': 15.5.12 '@next/swc-linux-arm64-musl': 15.5.15
'@next/swc-linux-x64-gnu': 15.5.12 '@next/swc-linux-x64-gnu': 15.5.15
'@next/swc-linux-x64-musl': 15.5.12 '@next/swc-linux-x64-musl': 15.5.15
'@next/swc-win32-arm64-msvc': 15.5.12 '@next/swc-win32-arm64-msvc': 15.5.15
'@next/swc-win32-x64-msvc': 15.5.12 '@next/swc-win32-x64-msvc': 15.5.15
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
'@playwright/test': 1.58.2 '@playwright/test': 1.58.2
sharp: 0.34.5 sharp: 0.34.5
@@ -8004,7 +8005,7 @@ snapshots:
node-releases@2.0.27: {} node-releases@2.0.27: {}
nodemailer@8.0.1: {} nodemailer@8.0.5: {}
normalize-path@3.0.0: {} normalize-path@3.0.0: {}
+2 -2
View File
@@ -2,8 +2,8 @@
# Remove SUPERUSER from the application database user # Remove SUPERUSER from the application database user
# Run after initial setup: bash scripts/harden-postgres.sh # Run after initial setup: bash scripts/harden-postgres.sh
DB_USER="capakraken" DB_USER="${DB_USER:-capakraken}"
DB_NAME="capakraken" DB_NAME="${DB_NAME:-capakraken}"
echo "Hardening PostgreSQL for $DB_USER..." echo "Hardening PostgreSQL for $DB_USER..."
+12 -6
View File
@@ -2,6 +2,9 @@
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
APP_PORT="${APP_PORT:-3100}"
APP_CONTAINER="${APP_CONTAINER:-$(docker compose --profile full ps -q app 2>/dev/null | head -1)}"
echo "Starting CapaKraken..." echo "Starting CapaKraken..."
# 1. Start Docker services # 1. Start Docker services
@@ -19,20 +22,23 @@ for i in {1..30}; do
done done
# 3. Start the web app in Docker for a stable lifecycle # 3. Start the web app in Docker for a stable lifecycle
echo " Starting app container on port 3100..." echo " Starting app container on port ${APP_PORT}..."
docker compose --profile full up -d app docker compose --profile full up -d app
# Resolve container name after start (docker compose generates it from project dir + service)
APP_CONTAINER="$(docker compose --profile full ps -q app 2>/dev/null | head -1)"
# 4. Wait for server to be ready # 4. Wait for server to be ready
# Allow up to 90s: prisma generate + migrate deploy + next dev compilation # Allow up to 90s: prisma generate + migrate deploy + next dev compilation
echo " Waiting for server (up to 90s)..." echo " Waiting for server (up to 90s)..."
for i in {1..90}; do for i in {1..90}; do
if curl -sf http://localhost:3100/api/health > /dev/null 2>&1; then if curl -sf "http://localhost:${APP_PORT}/api/health" > /dev/null 2>&1; then
echo "" echo ""
echo "CapaKraken is running!" echo "CapaKraken is running!"
curl -s http://localhost:3100/api/ready | python3 -m json.tool 2>/dev/null || curl -s http://localhost:3100/api/ready curl -s "http://localhost:${APP_PORT}/api/ready" | python3 -m json.tool 2>/dev/null || curl -s "http://localhost:${APP_PORT}/api/ready"
echo "" echo ""
echo " URL: http://localhost:3100" echo " URL: http://localhost:${APP_PORT}"
echo " Logs: docker logs -f capakraken-app-1" echo " Logs: docker logs -f ${APP_CONTAINER}"
exit 0 exit 0
fi fi
# Print progress every 10s # Print progress every 10s
@@ -43,5 +49,5 @@ for i in {1..90}; do
done done
echo "ERROR: Server failed to start within 90 seconds" echo "ERROR: Server failed to start within 90 seconds"
echo "Check logs: docker logs --tail 100 capakraken-app-1" echo "Check logs: docker logs --tail 100 ${APP_CONTAINER}"
exit 1 exit 1
+5 -1
View File
@@ -16,8 +16,12 @@ if [ -f /tmp/capakraken-dev.pid ]; then
rm -f /tmp/capakraken-dev.pid rm -f /tmp/capakraken-dev.pid
fi fi
# Also kill anything on port 3100 # Also kill anything on port 3100 (cross-platform: lsof works on Linux + macOS)
if command -v lsof >/dev/null 2>&1; then
lsof -ti:3100 2>/dev/null | xargs kill 2>/dev/null || true
elif command -v fuser >/dev/null 2>&1; then
fuser -k 3100/tcp 2>/dev/null || true fuser -k 3100/tcp 2>/dev/null || true
fi
# 2. Stop Docker services (keep data volumes) # 2. Stop Docker services (keep data volumes)
echo " Stopping app, PostgreSQL and Redis..." echo " Stopping app, PostgreSQL and Redis..."
+2 -1
View File
@@ -32,8 +32,9 @@ echo "App env file: ${APP_ENV_FILE}"
echo "App image: ${APP_IMAGE}" echo "App image: ${APP_IMAGE}"
echo "Migrator image: ${MIGRATOR_IMAGE}" echo "Migrator image: ${MIGRATOR_IMAGE}"
DOCKER_REGISTRY="${DOCKER_REGISTRY:-ghcr.io}"
if [ -n "${GHCR_USERNAME:-}" ] && [ -n "${GHCR_TOKEN:-}" ]; then if [ -n "${GHCR_USERNAME:-}" ] && [ -n "${GHCR_TOKEN:-}" ]; then
printf '%s\n' "${GHCR_TOKEN}" | docker login ghcr.io -u "${GHCR_USERNAME}" --password-stdin printf '%s\n' "${GHCR_TOKEN}" | docker login "${DOCKER_REGISTRY}" -u "${GHCR_USERNAME}" --password-stdin
fi fi
docker compose -f "${COMPOSE_FILE}" config -q docker compose -f "${COMPOSE_FILE}" config -q
+14 -1
View File
@@ -17,15 +17,28 @@ pnpm --filter @capakraken/db db:migrate:deploy
pnpm check:exports pnpm check:exports
pnpm check:imports pnpm check:imports
repo_home="/tmp/capakraken-dev-home"
# Cross-platform stat: GNU stat uses -c, BSD/macOS stat uses -f
if stat -c '%u' /app >/dev/null 2>&1; then
repo_uid="$(stat -c '%u' /app)" repo_uid="$(stat -c '%u' /app)"
repo_gid="$(stat -c '%g' /app)" repo_gid="$(stat -c '%g' /app)"
repo_home="/tmp/capakraken-dev-home" else
repo_uid="$(stat -f '%u' /app)"
repo_gid="$(stat -f '%g' /app)"
fi
mkdir -p /app/apps/web/.next mkdir -p /app/apps/web/.next
mkdir -p "$repo_home/.config/pnpm" mkdir -p "$repo_home/.config/pnpm"
chown -R "$repo_uid:$repo_gid" /app/apps/web/.next chown -R "$repo_uid:$repo_gid" /app/apps/web/.next
chown -R "$repo_uid:$repo_gid" "$repo_home" chown -R "$repo_uid:$repo_gid" "$repo_home"
# Cross-platform privilege drop: setpriv (Linux) or su (macOS/BSD)
if command -v setpriv >/dev/null 2>&1; then
exec setpriv --reuid="$repo_uid" --regid="$repo_gid" --clear-groups \ exec setpriv --reuid="$repo_uid" --regid="$repo_gid" --clear-groups \
env HOME="$repo_home" XDG_CONFIG_HOME="$repo_home/.config" \ env HOME="$repo_home" XDG_CONFIG_HOME="$repo_home/.config" \
pnpm --filter @capakraken/web exec next dev -H 0.0.0.0 -p 3100 pnpm --filter @capakraken/web exec next dev -H 0.0.0.0 -p 3100
else
exec su -s /bin/sh "#${repo_uid}" -c \
"HOME='$repo_home' XDG_CONFIG_HOME='$repo_home/.config' pnpm --filter @capakraken/web exec next dev -H 0.0.0.0 -p 3100"
fi