diff --git a/README.md b/README.md index 900b4d6..87632bc 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Open http://localhost:3000. The app requires login by default; you will see the Camera and geolocation in the browser require a **secure context** (HTTPS) when you open the app from your phone. To test Share live from a device on your LAN without buying a domain or cert: -1. Generate a self-signed cert (once). Use your machine’s LAN IP so the phone can use it: +1. Generate a self-signed cert (once). Use your machine's LAN IP so the phone can use it: ```bash chmod +x scripts/gen-dev-cert.sh ./scripts/gen-dev-cert.sh 192.168.1.123 @@ -34,7 +34,7 @@ Camera and geolocation in the browser require a **secure context** (HTTPS) when npm run dev ``` -3. On your phone, open **https://192.168.1.123:3000** (same IP you passed above). Accept the browser’s “untrusted certificate” warning once (e.g. Advanced → Proceed). Then log in and use Share live; camera and location will work. +3. On your phone, open **https://192.168.1.123:3000** (same IP you passed above). Accept the browser's “untrusted certificate” warning once (e.g. Advanced → Proceed). Then log in and use Share live; camera and location will work. Without the certs, `npm run dev` still runs over HTTP as before. @@ -48,7 +48,7 @@ The **Share live** feature uses WebRTC for real-time video streaming from mobile - **Mediasoup** server (runs automatically in the Nuxt process) - **mediasoup-client** (browser library, included automatically) -**Streaming from a phone on your LAN:** The server auto-detects your machine’s LAN IP (from network interfaces) and uses it for WebRTC. Open **https://:3000** on both phone and laptop (same IP as for your dev cert). To override (e.g. Docker or multiple NICs), set `MEDIASOUP_ANNOUNCED_IP`. Ensure firewall allows UDP/TCP ports 40000–49999 on the server. +**Streaming from a phone on your LAN:** The server auto-detects your machine's LAN IP (from network interfaces) and uses it for WebRTC. Open **https://:3000** on both phone and laptop (same IP as for your dev cert). To override (e.g. Docker or multiple NICs), set `MEDIASOUP_ANNOUNCED_IP`. Ensure firewall allows UDP/TCP ports 40000–49999 on the server. See [docs/live-streaming.md](docs/live-streaming.md) for architecture details. @@ -65,7 +65,7 @@ See [docs/live-streaming.md](docs/live-streaming.md) for architecture details. - **Devices**: Manage cameras/devices via the API (`/api/devices`) or the Members/Cameras UI. Each device needs `name`, `device_type`, `lat`, `lng`, `stream_url`, and `source_type` (`mjpeg` or `hls`). - **Environment**: No required env vars for basic run. For production, set `HOST=0.0.0.0` and `PORT` as needed (e.g. in Docker/Helm). - **Authentication**: The login page always offers password sign-in (local). Optionally set `BOOTSTRAP_EMAIL` and `BOOTSTRAP_PASSWORD` before the first run to create the first admin; otherwise a default admin is created and its credentials are printed in the terminal. To also show an OIDC sign-in button, configure `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, and optionally `OIDC_LABEL`, `OIDC_REDIRECT_URI`. See [docs/auth.md](docs/auth.md) for provider-specific examples. -- **Bootstrap admin** (when using local auth): The server initializes the database and runs bootstrap at startup. On first run (no users in the database), it creates the first admin. If you set `BOOTSTRAP_EMAIL` and `BOOTSTRAP_PASSWORD` before starting, that account is created. If you don’t set them, a default admin is created (identifier: `admin`) with a random password and the credentials are printed in the terminal—copy them and sign in at `/login`, then change the password or add users via Members. Use **Members** to change roles (admin, leader, member). Only admins can change roles; admins and leaders can edit POIs. +- **Bootstrap admin** (when using local auth): The server initializes the database and runs bootstrap at startup. On first run (no users in the database), it creates the first admin. If you set `BOOTSTRAP_EMAIL` and `BOOTSTRAP_PASSWORD` before starting, that account is created. If you don't set them, a default admin is created (identifier: `admin`) with a random password and the credentials are printed in the terminal—copy them and sign in at `/login`, then change the password or add users via Members. Use **Members** to change roles (admin, leader, member). Only admins can change roles; admins and leaders can edit POIs. - **Database**: SQLite file at `data/kestrelos.db` (created automatically). Contains users, sessions, and POIs. ## Docker diff --git a/test/e2e/global-setup.js b/test/e2e/global-setup.js index e5323f9..f8dac93 100644 --- a/test/e2e/global-setup.js +++ b/test/e2e/global-setup.js @@ -1,72 +1,50 @@ -/** - * Global setup for E2E tests. - * Runs once before all tests. - */ - import { existsSync, mkdirSync } from 'node:fs' import { join, dirname } from 'node:path' import { fileURLToPath } from 'node:url' import { execSync } from 'node:child_process' -const _dirname = dirname(fileURLToPath(import.meta.url)) -const projectRoot = join(_dirname, '../../..') +const projectRoot = join(dirname(fileURLToPath(import.meta.url)), '../../..') const devCertsDir = join(projectRoot, '.dev-certs') const devKey = join(devCertsDir, 'key.pem') const devCert = join(devCertsDir, 'cert.pem') -// Import server modules (ES modules) const { getDb } = await import('../../server/utils/db.js') const { hashPassword } = await import('../../server/utils/password.js') const { TEST_ADMIN } = await import('./fixtures/users.js') -function ensureDevCerts() { - if (existsSync(devKey) && existsSync(devCert)) { - return // Certs already exist - } +const ensureDevCerts = () => { + if (existsSync(devKey) && existsSync(devCert)) return - // Create .dev-certs directory mkdirSync(devCertsDir, { recursive: true }) - - // Generate self-signed cert for localhost/127.0.0.1 - const SAN = 'subjectAltName=IP:127.0.0.1,DNS:localhost' try { execSync( - `openssl req -x509 -newkey rsa:2048 -keyout "${devKey}" -out "${devCert}" -days 365 -nodes -subj "/CN=localhost" -addext "${SAN}"`, - { cwd: projectRoot, stdio: 'inherit' }, + `openssl req -x509 -newkey rsa:2048 -keyout "${devKey}" -out "${devCert}" -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName=IP:127.0.0.1,DNS:localhost"`, + { cwd: projectRoot, stdio: process.env.CI ? 'pipe' : 'inherit' }, ) - console.log('[test] Generated .dev-certs/key.pem and .dev-certs/cert.pem') } catch (error) { throw new Error(`Failed to generate dev certificates: ${error.message}`) } } -async function globalSetup() { +export default async function globalSetup() { ensureDevCerts() let retries = 3 - let lastError = null while (retries > 0) { try { const { get, run } = await getDb() - const existingUser = await get('SELECT id FROM users WHERE identifier = ?', [TEST_ADMIN.identifier]) + const existing = await get('SELECT id FROM users WHERE identifier = ?', [TEST_ADMIN.identifier]) - if (!existingUser) { - const id = crypto.randomUUID() - const now = new Date().toISOString() + if (!existing) { await run( 'INSERT INTO users (id, identifier, password_hash, role, created_at, auth_provider, oidc_issuer, oidc_sub) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', - [id, TEST_ADMIN.identifier, hashPassword(TEST_ADMIN.password), TEST_ADMIN.role, now, 'local', null, null], + [crypto.randomUUID(), TEST_ADMIN.identifier, hashPassword(TEST_ADMIN.password), TEST_ADMIN.role, new Date().toISOString(), 'local', null, null], ) - console.log(`[test] Created test admin user: ${TEST_ADMIN.identifier}`) - } - else { - console.log(`[test] Test admin user already exists: ${TEST_ADMIN.identifier}`) } return } catch (error) { - lastError = error if (error.message?.includes('SQLITE_BUSY') || error.message?.includes('database is locked')) { retries-- if (retries > 0) { @@ -77,7 +55,4 @@ async function globalSetup() { throw error } } - throw lastError } - -export default globalSetup