Files
KanbanGit/tests/Fixtures/auth-server.mjs
Twyco 802294d6a2
CI/CD / test_and_check_app (push) Canceled after 0s
CI/CD / Publish immutable image (push) Canceled after 0s
CI/CD / Publish immutable image (pull_request) Canceled after 0s
CI/CD / test_and_check_app (pull_request) Canceled after 6s
feat(KAN-21): rebuild login verification and two-factor flows
2026-09-11 14:18:23 +02:00

120 lines
4.1 KiB
JavaScript

import { fileURLToPath } from 'node:url';
import vue from '@vitejs/plugin-vue';
import tailwind from '@tailwindcss/vite';
import { createServer } from 'vite';
// Isolated Inertia transport fixture; real Fortify sessions are covered by PHPUnit.
const server = await createServer({
configFile: false,
cacheDir: 'node_modules/.vite/auth-fixture',
plugins: [vue(), tailwind()],
resolve: {
alias: {
'@': fileURLToPath(new URL('../../resources/js', import.meta.url)),
},
},
server: { host: '127.0.0.1', port: 4177, strictPort: true, hmr: false },
appType: 'custom',
});
server.middlewares.use(async (request, response, next) => {
const url = new URL(request.url, 'http://localhost');
const pages = {
'/login': 'Login',
'/two-factor-challenge': 'TwoFactorChallenge',
'/email/verify': 'VerifyEmail',
'/email/verification-notification': 'VerifyEmail',
'/dashboard': 'Destination',
'/logout': 'Login',
'/forgot-password': 'Destination',
'/': 'Destination',
};
if (!(url.pathname in pages)) {
return next();
}
const mutation = request.method === 'POST';
const result =
request.headers.cookie?.match(/auth_test_result=(\w+)/)?.[1] ??
'success';
if (mutation && ['server', 'expired', 'limited'].includes(result)) {
response.statusCode = { server: 503, expired: 419, limited: 429 }[
result
];
response.setHeader('Content-Type', 'text/html');
response.end('<h1>Simulated failed response</h1>');
return;
}
let body = '';
if (mutation) {
for await (const chunk of request) {
body += chunk;
}
}
const submitted = body ? JSON.parse(body) : {};
let path = url.pathname;
let errors = {};
let status;
if (mutation && result === 'validation') {
path = new URL(request.headers.referer).pathname;
const field =
path === '/login'
? 'email'
: 'recovery_code' in submitted
? 'recovery_code'
: 'code';
errors = { [field]: 'These details could not be verified.' };
} else if (mutation) {
if (path === '/email/verification-notification') {
path = '/email/verify';
status = 'verification-link-sent';
} else if (path === '/logout') {
path = '/login';
} else {
path =
result === 'challenge' ? '/two-factor-challenge' : '/dashboard';
}
}
const page = {
component: pages[path],
url: path,
version: null,
clearHistory: false,
encryptHistory: false,
props: {
name: 'KanbanGit',
errors,
status,
canResetPassword: !url.searchParams.has('no-reset'),
auth: {
user:
path === '/email/verify'
? {
id: 1,
name: 'Alex Morgan',
email: 'alex.morgan@example.test',
}
: null,
},
},
};
response.setHeader('Vary', 'X-Inertia');
if (request.headers['x-inertia']) {
response.setHeader('X-Inertia', 'true');
response.setHeader('Content-Type', 'application/json');
response.end(JSON.stringify(page));
return;
}
const escaped = JSON.stringify(page)
.replaceAll('&', '&amp;')
.replaceAll('"', '&quot;')
.replaceAll('<', '&lt;');
const html =
'<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head><body><div id="app" data-page="' +
escaped +
'"></div><script type="module" src="/tests/Fixtures/auth-preview.mjs"></script></body></html>';
response.setHeader('Content-Type', 'text/html');
response.end(await server.transformIndexHtml(request.url, html));
});
await server.listen();
console.log('Auth fixture ready at http://127.0.0.1:4177/login');