File size: 3,129 Bytes
eb6572b
 
 
 
44cec99
 
 
f384533
eb6572b
 
 
 
 
 
 
c9132c5
 
 
eb6572b
 
 
44cec99
c9132c5
 
 
 
 
eb6572b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44cec99
 
c9132c5
44cec99
c9132c5
 
 
 
eb6572b
c9132c5
eb6572b
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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();
  }
}