Skip to content

chore: Centralize all DEBUG checks in debugLog #108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 36 additions & 39 deletions src/lib/coordination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Server } from 'http'
import express from 'express'
import { AddressInfo } from 'net'
import { unlinkSync } from 'fs'
import { log, debugLog, DEBUG, setupOAuthCallbackServerWithLongPoll } from './utils'
import { log, debugLog, setupOAuthCallbackServerWithLongPoll } from './utils'

export type AuthCoordinator = {
initializeAuth: () => Promise<{ server: Server; waitForAuthCode: () => Promise<string>; skipBrowserAuth: boolean }>
Expand All @@ -18,10 +18,10 @@ export type AuthCoordinator = {
export async function isPidRunning(pid: number): Promise<boolean> {
try {
process.kill(pid, 0) // Doesn't kill the process, just checks if it exists
if (DEBUG) debugLog(`Process ${pid} is running`)
debugLog(`Process ${pid} is running`)
return true
} catch (err) {
if (DEBUG) debugLog(`Process ${pid} is not running`, err)
debugLog(`Process ${pid} is not running`, err)
return false
}
}
Expand All @@ -32,30 +32,29 @@ export async function isPidRunning(pid: number): Promise<boolean> {
* @returns True if the lockfile is valid, false otherwise
*/
export async function isLockValid(lockData: LockfileData): Promise<boolean> {
if (DEBUG) debugLog('Checking if lockfile is valid', lockData)
debugLog('Checking if lockfile is valid', lockData)

// Check if the lockfile is too old (over 30 minutes)
const MAX_LOCK_AGE = 30 * 60 * 1000 // 30 minutes
if (Date.now() - lockData.timestamp > MAX_LOCK_AGE) {
log('Lockfile is too old')
if (DEBUG)
debugLog('Lockfile is too old', {
age: Date.now() - lockData.timestamp,
maxAge: MAX_LOCK_AGE,
})
debugLog('Lockfile is too old', {
age: Date.now() - lockData.timestamp,
maxAge: MAX_LOCK_AGE,
})
return false
}

// Check if the process is still running
if (!(await isPidRunning(lockData.pid))) {
log('Process from lockfile is not running')
if (DEBUG) debugLog('Process from lockfile is not running', { pid: lockData.pid })
debugLog('Process from lockfile is not running', { pid: lockData.pid })
return false
}

// Check if the endpoint is accessible
try {
if (DEBUG) debugLog('Checking if endpoint is accessible', { port: lockData.port })
debugLog('Checking if endpoint is accessible', { port: lockData.port })

const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 1000)
Expand All @@ -67,11 +66,11 @@ export async function isLockValid(lockData: LockfileData): Promise<boolean> {
clearTimeout(timeout)

const isValid = response.status === 200 || response.status === 202
if (DEBUG) debugLog(`Endpoint check result: ${isValid ? 'valid' : 'invalid'}`, { status: response.status })
debugLog(`Endpoint check result: ${isValid ? 'valid' : 'invalid'}`, { status: response.status })
return isValid
} catch (error) {
log(`Error connecting to auth server: ${(error as Error).message}`)
if (DEBUG) debugLog('Error connecting to auth server', error)
debugLog('Error connecting to auth server', error)
return false
}
}
Expand All @@ -90,11 +89,11 @@ export async function waitForAuthentication(port: number): Promise<boolean> {
attempts++
const url = `http://127.0.0.1:${port}/wait-for-auth`
log(`Querying: ${url}`)
if (DEBUG) debugLog(`Poll attempt ${attempts}`)
debugLog(`Poll attempt ${attempts}`)

try {
const response = await fetch(url)
if (DEBUG) debugLog(`Poll response status: ${response.status}`)
debugLog(`Poll response status: ${response.status}`)

if (response.status === 200) {
// Auth completed, but we don't return the code anymore
Expand All @@ -103,21 +102,21 @@ export async function waitForAuthentication(port: number): Promise<boolean> {
} else if (response.status === 202) {
// Continue polling
log(`Authentication still in progress`)
if (DEBUG) debugLog(`Will retry in 1s`)
debugLog(`Will retry in 1s`)
await new Promise((resolve) => setTimeout(resolve, 1000))
} else {
log(`Unexpected response status: ${response.status}`)
return false
}
} catch (fetchError) {
if (DEBUG) debugLog(`Fetch error during poll`, fetchError)
debugLog(`Fetch error during poll`, fetchError)
// If we can't connect, we'll try again after a delay
await new Promise((resolve) => setTimeout(resolve, 2000))
}
}
} catch (error) {
log(`Error waiting for authentication: ${(error as Error).message}`)
if (DEBUG) debugLog(`Error waiting for authentication`, error)
debugLog(`Error waiting for authentication`, error)
return false
}
}
Expand All @@ -136,16 +135,16 @@ export function createLazyAuthCoordinator(serverUrlHash: string, callbackPort: n
initializeAuth: async () => {
// If auth has already been initialized, return the existing state
if (authState) {
if (DEBUG) debugLog('Auth already initialized, reusing existing state')
debugLog('Auth already initialized, reusing existing state')
return authState
}

log('Initializing auth coordination on-demand')
if (DEBUG) debugLog('Initializing auth coordination on-demand', { serverUrlHash, callbackPort })
debugLog('Initializing auth coordination on-demand', { serverUrlHash, callbackPort })

// Initialize auth using the existing coordinateAuth logic
authState = await coordinateAuth(serverUrlHash, callbackPort, events)
if (DEBUG) debugLog('Auth coordination completed', { skipBrowserAuth: authState.skipBrowserAuth })
debugLog('Auth coordination completed', { skipBrowserAuth: authState.skipBrowserAuth })
return authState
},
}
Expand All @@ -163,17 +162,15 @@ export async function coordinateAuth(
callbackPort: number,
events: EventEmitter,
): Promise<{ server: Server; waitForAuthCode: () => Promise<string>; skipBrowserAuth: boolean }> {
if (DEBUG) debugLog('Coordinating authentication', { serverUrlHash, callbackPort })
debugLog('Coordinating authentication', { serverUrlHash, callbackPort })

// Check for a lockfile (disabled on Windows for the time being)
const lockData = process.platform === 'win32' ? null : await checkLockfile(serverUrlHash)

if (DEBUG) {
if (process.platform === 'win32') {
debugLog('Skipping lockfile check on Windows')
} else {
debugLog('Lockfile check result', { found: !!lockData, lockData })
}
if (process.platform === 'win32') {
debugLog('Skipping lockfile check on Windows')
} else {
debugLog('Lockfile check result', { found: !!lockData, lockData })
}

// If there's a valid lockfile, try to use the existing auth process
Expand All @@ -182,7 +179,7 @@ export async function coordinateAuth(

try {
// Try to wait for the authentication to complete
if (DEBUG) debugLog('Waiting for authentication from other instance')
debugLog('Waiting for authentication from other instance')
const authCompleted = await waitForAuthentication(lockData.port)

if (authCompleted) {
Expand All @@ -191,7 +188,7 @@ export async function coordinateAuth(
// Setup a dummy server - the client will use tokens directly from disk
const dummyServer = express().listen(0) // Listen on any available port
const dummyPort = (dummyServer.address() as AddressInfo).port
if (DEBUG) debugLog('Started dummy server', { port: dummyPort })
debugLog('Started dummy server', { port: dummyPort })

// This shouldn't actually be called in normal operation, but provide it for API compatibility
const dummyWaitForAuthCode = () => {
Expand All @@ -210,11 +207,11 @@ export async function coordinateAuth(
}
} catch (error) {
log(`Error waiting for authentication: ${error}`)
if (DEBUG) debugLog('Error waiting for authentication', error)
debugLog('Error waiting for authentication', error)
}

// If we get here, the other process didn't complete auth successfully
if (DEBUG) debugLog('Other instance did not complete auth successfully, deleting lockfile')
debugLog('Other instance did not complete auth successfully, deleting lockfile')
await deleteLockfile(serverUrlHash)
} else if (lockData) {
// Invalid lockfile, delete it
Expand All @@ -223,7 +220,7 @@ export async function coordinateAuth(
}

// Create our own lockfile
if (DEBUG) debugLog('Setting up OAuth callback server', { port: callbackPort })
debugLog('Setting up OAuth callback server', { port: callbackPort })
const { server, waitForAuthCode, authCompletedPromise } = setupOAuthCallbackServerWithLongPoll({
port: callbackPort,
path: '/oauth/callback',
Expand All @@ -233,7 +230,7 @@ export async function coordinateAuth(
// Get the actual port the server is running on
const address = server.address() as AddressInfo
const actualPort = address.port
if (DEBUG) debugLog('OAuth callback server running', { port: actualPort })
debugLog('OAuth callback server running', { port: actualPort })

log(`Creating lockfile for server ${serverUrlHash} with process ${process.pid} on port ${actualPort}`)
await createLockfile(serverUrlHash, process.pid, actualPort)
Expand All @@ -245,7 +242,7 @@ export async function coordinateAuth(
await deleteLockfile(serverUrlHash)
} catch (error) {
log(`Error cleaning up lockfile: ${error}`)
if (DEBUG) debugLog('Error cleaning up lockfile', error)
debugLog('Error cleaning up lockfile', error)
}
}

Expand All @@ -254,19 +251,19 @@ export async function coordinateAuth(
// Synchronous version for 'exit' event since we can't use async here
const configPath = getConfigFilePath(serverUrlHash, 'lock.json')
unlinkSync(configPath)
if (DEBUG) console.error(`[DEBUG] Removed lockfile on exit: ${configPath}`)
debugLog(`Removed lockfile on exit: ${configPath}`)
} catch (error) {
if (DEBUG) console.error(`[DEBUG] Error removing lockfile on exit:`, error)
debugLog(`Error removing lockfile on exit:`, error)
}
})

// Also handle SIGINT separately
process.once('SIGINT', async () => {
if (DEBUG) debugLog('Received SIGINT signal, cleaning up')
debugLog('Received SIGINT signal, cleaning up')
await cleanupHandler()
})

if (DEBUG) debugLog('Auth coordination complete, returning primary instance handlers')
debugLog('Auth coordination complete, returning primary instance handlers')
return {
server,
waitForAuthCode,
Expand Down
Loading
Loading