server be & fe

This commit is contained in:
azykov@mail.ru
2026-09-29 13:50:01 +03:00
commit 657009bd6c
56 changed files with 6162 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
import { randomBytes } from 'node:crypto';
import { describe, expect, it } from 'vitest';
import type { AccessKeyView } from '@cardy/shared';
import { loadConfig } from '../src/config.js';
import { SUPERADMIN, testApp } from './helpers.js';
const baseEnv = {
KEY_ENCRYPTION_SECRET: randomBytes(32).toString('base64'),
SUPERADMIN_EMAIL: 'Root@Example.com',
};
describe('loadConfig', () => {
it('applies defaults', () => {
const config = loadConfig(baseEnv);
expect(config).toMatchObject({ superadminEmail: 'root@example.com', superadminGroup: 'default', host: '127.0.0.1', port: 10002 });
expect(config.publicApiUrl).toBeUndefined();
expect(config.adminProxySecret).toBeUndefined();
});
it('validates values', () => {
expect(loadConfig({ ...baseEnv, PUBLIC_API_URL: 'https://dev.aggtaa.com/api/' }).publicApiUrl).toBe('https://dev.aggtaa.com/api');
expect(() => loadConfig({ ...baseEnv, PUBLIC_API_URL: 'nope' })).toThrow();
expect(() => loadConfig({ ...baseEnv, SUPERADMIN_EMAIL: '' })).toThrow();
});
});
describe('reverse proxy integration', () => {
const proxied = {
'remote-email': SUPERADMIN,
'x-cardy-admin': '1',
'x-forwarded-proto': 'https',
'x-forwarded-host': 'dev.aggtaa.com',
'x-forwarded-prefix': '/api',
};
it('derives the deep link server and cookie path from X-Forwarded-* headers', async () => {
const { app } = await testApp({ publicApiUrl: undefined });
const key = (await app.inject({ method: 'POST', url: `/admin/users/${SUPERADMIN}/keys`, headers: proxied, payload: {} })).json() as AccessKeyView;
expect(key.deepLink).toBe(`cardy://login?server=${encodeURIComponent('https://dev.aggtaa.com/api')}&key=${key.key}`);
const me = await app.inject({ url: '/admin/me', headers: proxied });
const cookie = String(me.headers['set-cookie']);
expect(cookie).toMatch(/Path=\/api\/admin/);
expect(cookie).toMatch(/Secure/);
});
it('ignores a malformed X-Forwarded-Prefix', async () => {
const { app } = await testApp({ publicApiUrl: undefined });
const key = (
await app.inject({
method: 'POST',
url: `/admin/users/${SUPERADMIN}/keys`,
headers: { ...proxied, 'x-forwarded-prefix': '/api"><script>' },
payload: {},
})
).json() as AccessKeyView;
expect(key.deepLink).toContain(`server=${encodeURIComponent('https://dev.aggtaa.com')}&key=`);
});
it('rejects admin requests without the proxy secret when configured', async () => {
const { app } = await testApp({ adminProxySecret: 's3cret' });
const headers = { 'remote-email': SUPERADMIN };
expect((await app.inject({ url: '/admin/me', headers })).statusCode).toBe(401);
expect((await app.inject({ url: '/admin/me', headers: { ...headers, 'x-cardy-proxy-secret': 'wrong' } })).statusCode).toBe(401);
expect((await app.inject({ url: '/admin/me', headers: { ...headers, 'x-cardy-proxy-secret': 's3cret' } })).statusCode).toBe(200);
});
it('does not answer CORS preflight for admin paths', async () => {
const { app } = await testApp();
const res = await app.inject({
method: 'OPTIONS',
url: '/admin/me',
headers: { origin: 'https://evil.example', 'access-control-request-method': 'GET' },
});
expect(res.headers['access-control-allow-origin']).toBeUndefined();
});
});