Spaces:
Sleeping
Sleeping
| import { chromium } from 'playwright'; | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| /** | |
| * Launch Playwright, sign in to n8n if needed, refresh cookie, | |
| * save a screenshot, return status object. | |
| */ | |
| export async function runLogin() { | |
| let browser; | |
| try { | |
| browser = await chromium.launch({ args: ['--no-sandbox'] }); | |
| /* ---------- reuse cookie ---------- */ | |
| const hasState = fs.existsSync('/opt/state.json'); | |
| const context = hasState | |
| ? await browser.newContext({ storageState: '/opt/state.json' }) | |
| : await browser.newContext(); | |
| await context.tracing.start({ screenshots: true, snapshots: true }); | |
| const page = await context.newPage(); | |
| const baseUrl = process.env.N8N_URL || 'http://localhost:5678/'; | |
| await page.goto(baseUrl, { waitUntil: 'networkidle', timeout: 60000 }); | |
| /* ---------- BASIC AUTH (optional) ---------- */ | |
| const basicUserInput = page.locator('input[type="text"]'); | |
| if (process.env.BASIC_USER && await basicUserInput.count()) { | |
| await basicUserInput.fill(process.env.BASIC_USER); | |
| await page.fill('input[type="password"]', process.env.BASIC_PASS || ''); | |
| await page.press('input[type="password"]', 'Enter'); | |
| } | |
| /* ---------- detect current page ---------- */ | |
| const signInForm = page.locator('[data-test-id="signin-form"]'); | |
| const overviewTab = page.getByRole('link', { name: /^overview$/i }); | |
| let needSignIn = false; | |
| try { | |
| await Promise.race([ | |
| signInForm.waitFor({ state: 'visible', timeout: 45000 }), | |
| overviewTab.waitFor({ state: 'visible', timeout: 45000 }), | |
| ]); | |
| needSignIn = await signInForm.count() > 0; | |
| } catch (_) { | |
| needSignIn = false; // neither selector appeared | |
| } | |
| /* ---------- perform sign-in ---------- */ | |
| if (needSignIn) { | |
| const email = page.locator( | |
| 'input[name="emailOrLdapLoginId"], input[name="email"]' | |
| ).first(); | |
| const pass = page.locator('input[name="password"]').first(); | |
| await email.fill(process.env.N8N_EMAIL || ''); | |
| await pass.fill(process.env.N8N_PASSWORD || ''); | |
| await page.getByRole('button', { name: /sign in/i }).click(); | |
| await overviewTab.waitFor({ state: 'visible', timeout: 60000 }); | |
| } | |
| /* ---------- save cookie & screenshot ---------- */ | |
| await context.storageState({ path: '/opt/state.json' }); | |
| try { | |
| const dir = '/opt/log'; | |
| fs.mkdirSync(dir, { recursive: true }); | |
| const ts = new Date().toISOString().replace(/[:T]/g, '-').split('.')[0]; | |
| const file = path.join(dir, `overview-${ts}.png`); | |
| await page.screenshot({ path: file, fullPage: true }); | |
| console.log('📸 Screenshot saved ->', file); | |
| } catch (e) { | |
| console.warn('⚠️ Could not save screenshot:', e.message); | |
| } | |
| await context.tracing.stop({ path: '/opt/trace.zip' }); | |
| return { ok: true, loggedIn: needSignIn ? 'fresh' : 'cookie' }; | |
| } catch (err) { | |
| console.error('❌ runLogin error:', err); | |
| return { ok: false, error: err.message }; | |
| } finally { | |
| if (browser) await browser.close(); | |
| } | |
| } | |