fix: prevent Docker build failure when pnpm lockfile is missing (#130)

* fix: make docker build resilient when lockfile is absent

* test: update e2e credentials for secure admin seed policy
This commit is contained in:
nyk 2026-03-04 08:33:09 +07:00 committed by GitHub
parent 90b712ea29
commit 2111f03542
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 28 additions and 10 deletions

View File

@ -1,5 +1,5 @@
AUTH_USER=testadmin
AUTH_PASS=testpass123
AUTH_PASS=testpass1234!
API_KEY=test-api-key-e2e-12345
AUTH_SECRET=test-legacy-secret
MC_ALLOW_ANY_HOST=1

View File

@ -3,14 +3,19 @@ RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
COPY package.json ./
COPY . .
# better-sqlite3 requires native compilation tools
RUN apt-get update && apt-get install -y python3 make g++ --no-install-recommends && rm -rf /var/lib/apt/lists/*
RUN pnpm install --frozen-lockfile
RUN if [ -f pnpm-lock.yaml ]; then \
pnpm install --frozen-lockfile; \
else \
echo "WARN: pnpm-lock.yaml not found in build context; running non-frozen install"; \
pnpm install --no-frozen-lockfile; \
fi
FROM base AS build
COPY --from=deps /app/node_modules ./node_modules
COPY . .
COPY --from=deps /app ./
RUN pnpm build
FROM node:20-slim AS runtime

View File

@ -99,6 +99,13 @@ rm -rf node_modules
pnpm install
```
### "pnpm-lock.yaml not found" during Docker build
If your deployment context omits `pnpm-lock.yaml`, Docker build now falls back to
`pnpm install --no-frozen-lockfile`.
For reproducible builds, include `pnpm-lock.yaml` in the build context.
### "Invalid ELF header" or "Mach-O" errors
The native binary was compiled on a different platform. Rebuild:

View File

@ -6,6 +6,8 @@ import { test, expect } from '@playwright/test'
*/
test.describe('CSRF Origin Validation (Issue #20)', () => {
const TEST_PASS = 'testpass1234!'
test('POST with mismatched Origin is rejected', async ({ request }) => {
const res = await request.post('/api/auth/login', {
data: { username: 'test', password: 'test' },
@ -21,7 +23,7 @@ test.describe('CSRF Origin Validation (Issue #20)', () => {
test('POST with matching Origin is allowed', async ({ request }) => {
const res = await request.post('/api/auth/login', {
data: { username: 'testadmin', password: 'testpass123' },
data: { username: 'testadmin', password: TEST_PASS },
headers: {
'origin': 'http://127.0.0.1:3005',
'host': '127.0.0.1:3005'
@ -33,7 +35,7 @@ test.describe('CSRF Origin Validation (Issue #20)', () => {
test('POST without Origin header is allowed (non-browser client)', async ({ request }) => {
const res = await request.post('/api/auth/login', {
data: { username: 'testadmin', password: 'testpass123' },
data: { username: 'testadmin', password: TEST_PASS },
})
// No Origin = non-browser client, should be allowed through CSRF check
expect(res.status()).not.toBe(403)

View File

@ -6,6 +6,8 @@ import { test, expect } from '@playwright/test'
*/
test.describe('Login Flow', () => {
const TEST_PASS = 'testpass1234!'
test('login page loads', async ({ page }) => {
await page.goto('/login')
await expect(page).toHaveURL(/\/login/)
@ -18,7 +20,7 @@ test.describe('Login Flow', () => {
test('login API returns session cookie on success', async ({ request }) => {
const res = await request.post('/api/auth/login', {
data: { username: 'testadmin', password: 'testpass123' },
data: { username: 'testadmin', password: TEST_PASS },
headers: { 'x-forwarded-for': '10.88.88.1' }
})
expect(res.status()).toBe(200)
@ -39,7 +41,7 @@ test.describe('Login Flow', () => {
test('session cookie grants API access', async ({ request }) => {
// Login to get a session
const loginRes = await request.post('/api/auth/login', {
data: { username: 'testadmin', password: 'testpass123' },
data: { username: 'testadmin', password: TEST_PASS },
headers: { 'x-forwarded-for': '10.88.88.2' }
})
expect(loginRes.status()).toBe(200)

View File

@ -6,6 +6,8 @@ import { test, expect } from '@playwright/test'
*/
test.describe('Login Rate Limiting (Issue #8)', () => {
const TEST_PASS = 'testpass1234!'
test('blocks login after 5 rapid failed attempts', async ({ request }) => {
const results: number[] = []
@ -25,7 +27,7 @@ test.describe('Login Rate Limiting (Issue #8)', () => {
test('successful login is not blocked for fresh IP', async ({ request }) => {
const res = await request.post('/api/auth/login', {
data: { username: 'testadmin', password: 'testpass123' },
data: { username: 'testadmin', password: TEST_PASS },
headers: { 'x-real-ip': '10.88.88.88' }
})
// Should succeed (200) or at least not be rate limited