Spaces:
Sleeping
Sleeping
| import { chromium } from 'playwright'; | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| const DIR = process.env.STATE_DIR || '/data'; | |
| const COOKIE_FILE = path.join(DIR, 'state.json'); | |
| const LOG_DIR = path.join(DIR, 'log'); | |
| /** | |
| * Launch Playwright, sign in to n8n if needed, refresh cookie, | |
| * save a screenshot, return status object. | |
| */ | |
| export async function runLogin() { | |
| let browser; | |
| try { | |
| /* ---------- ensure writable paths exist ---------- */ | |
| fs.mkdirSync(LOG_DIR, { recursive: true }); // also creates DIR | |
| browser = await chromium.launch({ args: ['--no-sandbox'] }); | |
| /* ---------- reuse cookie ---------- */ | |
| const hasCookie = fs.existsSync(COOKIE_FILE); | |
| const context = await browser.newContext({ | |
| storageState: hasCookie ? COOKIE_FILE : undefined, | |
| ignoreHTTPSErrors: true, // in case your n8n URL uses self-signed cert | |
| }); | |
| 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 */ | |
| await context.storageState({ path: COOKIE_FILE }); | |
| const ts = new Date().toISOString().replace(/[:T]/g, '-').split('.')[0]; | |
| const shot = path.join(LOG_DIR, `overview-${ts}.png`); | |
| await page.screenshot({ path: shot, fullPage: true }); | |
| await context.tracing.stop({ path: path.join(DIR, 'trace.zip') }); | |
| return { ok: true, screenshot: shot, loggedIn: needSignIn ? 'fresh' : 'cookie' }; | |
| } catch (err) { | |
| console.error('❌ runLogin error:', err); | |
| return { ok: false, error: err.message }; | |
| } finally { | |
| if (browser) await browser.close(); | |
| } | |
| } | |