mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-27 22:36:06 +00:00
Merge pull request #96 from Lifeforge-app/fix/cli
Add Features to Connect Pocketbase in Local Network & Optimize Debugging
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import { PB_BINARY_PATH, PB_DIR, PB_KWARGS } from '@/constants/db'
|
||||
import { PB_BINARY_PATH, PB_DIR, PB_KWARGS, PB_HOST, PB_PORT } from '@/constants/db'
|
||||
import executeCommand from '@/utils/commands'
|
||||
import { checkPortInUse, delay, killExistingProcess } from '@/utils/helpers'
|
||||
import { checkAddressInUse, checkPortInUse, delay, killExistingProcess } from '@/utils/helpers'
|
||||
import logger from '@/utils/logger'
|
||||
import chalk from 'chalk'
|
||||
|
||||
/**
|
||||
* Service command configurations
|
||||
@@ -17,22 +18,16 @@ interface ServiceConfig {
|
||||
export const SERVICE_COMMANDS: Record<string, ServiceConfig> = {
|
||||
db: {
|
||||
command: async () => {
|
||||
const killedProcess = killExistingProcess('./pocketbase serve')
|
||||
|
||||
if (killedProcess) {
|
||||
await delay(2000)
|
||||
}
|
||||
|
||||
if (checkPortInUse(8090)) {
|
||||
logger.error(
|
||||
'No Pocketbase instance found running, but port 8090 is already in use.'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
if (checkAddressInUse(PB_HOST, PB_PORT)) {
|
||||
logger.error(
|
||||
`Database address ${chalk.blue(`${PB_HOST}:${PB_PORT}`)} is already in use.`
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (!fs.existsSync(PB_BINARY_PATH)) {
|
||||
logger.error(
|
||||
`PocketBase binary does not exist: ${PB_BINARY_PATH}. Please run "bun forge db init" to initialize the database.`
|
||||
`PocketBase binary does not exist: ${chalk.blue(PB_BINARY_PATH)}. Please run "bun forge db init" to initialize the database.`
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import chalk from 'chalk'
|
||||
import concurrently from 'concurrently'
|
||||
|
||||
import { PROJECTS } from '@/commands/project/constants/projects'
|
||||
@@ -30,7 +31,9 @@ export async function startSingleService(
|
||||
|
||||
const cwd = config.cwd instanceof Function ? config.cwd() : config.cwd
|
||||
|
||||
executeCommand(command, { cwd }, extraArgs)
|
||||
logger.debug(`Current Working Directory: ${chalk.blue(cwd)}`)
|
||||
|
||||
executeCommand(command, { cwd, stdio: 'inherit' }, extraArgs)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,10 +26,17 @@ export const PB_BINARY_PATH = path.resolve(
|
||||
process.env.PB_BINARY_PATH || `${PB_DIR}/pocketbase`
|
||||
)
|
||||
|
||||
// Remove http:// prefix if present
|
||||
export const PB_URL = getEnvVar('PB_HOST').replace(/^http:\/\//, '')
|
||||
|
||||
// Extract host from PB_HOST
|
||||
export const [PB_HOST, PB_PORT] = PB_URL.split(':')
|
||||
|
||||
export const PB_KWARGS = [
|
||||
`--dir=${PB_DATA_DIR}`,
|
||||
`--migrationsDir=${PB_MIGRATIONS_DIR}`,
|
||||
'--automigrate=0'
|
||||
'--automigrate=0',
|
||||
`--http ${PB_URL || 'localhost:8090'}`
|
||||
]
|
||||
|
||||
// Straightaway exit if PB_DIR is not accessible (skip in Docker mode)
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ROOT_DIR } from '@/constants/constants'
|
||||
|
||||
import logger from './logger'
|
||||
import { addDependency, removeDependency } from './packageJson'
|
||||
import chalk from 'chalk'
|
||||
|
||||
interface CommandExecutionOptions {
|
||||
stdio?: IOType | [IOType, IOType, IOType]
|
||||
@@ -34,12 +35,12 @@ export default function executeCommand(
|
||||
cmd = typeof command === 'function' ? command() : command
|
||||
} catch (error) {
|
||||
logger.error(`Failed to generate command.`)
|
||||
logger.debug(`Error details: ${error}`)
|
||||
logger.debug(`Error details: ${chalk.grey(String(error))}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug(`Executing: ${cmd}`)
|
||||
logger.debug(`Executing command ${chalk.blue(cmd)} with arguments: ${chalk.blue(_arguments.length ? _arguments.join(' ') : `${chalk.red('none')}`)}`)
|
||||
|
||||
const [toBeExecuted, ...args] = cmd.split(' ')
|
||||
|
||||
@@ -50,14 +51,12 @@ export default function executeCommand(
|
||||
stdio: options.stdio ?? 'pipe'
|
||||
})
|
||||
|
||||
if (logger.level === 'debug') {
|
||||
if (result.stdout) {
|
||||
logger.debug(result.stdout.toString())
|
||||
}
|
||||
if (result.stdout) {
|
||||
logger.debug(chalk.grey(result.stdout.toString()))
|
||||
}
|
||||
|
||||
if (result.stderr) {
|
||||
logger.debug(result.stderr.toString())
|
||||
}
|
||||
if (result.stderr) {
|
||||
logger.debug(chalk.grey(result.stderr.toString()))
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
@@ -69,7 +68,7 @@ export default function executeCommand(
|
||||
}
|
||||
|
||||
if (!options.stdio || options.stdio === 'pipe') {
|
||||
logger.debug(`Completed: ${cmd}`)
|
||||
logger.debug(`Command Completed: ${chalk.blue(cmd)}, exit code: ${chalk.blue(String(result.status))}`)
|
||||
}
|
||||
|
||||
return result.stdout?.toString().trim() || ''
|
||||
@@ -78,8 +77,8 @@ export default function executeCommand(
|
||||
throw error
|
||||
}
|
||||
|
||||
logger.error(`Command execution failed: ${cmd}`)
|
||||
logger.debug(`Error details: ${error}`)
|
||||
logger.error(`Command execution failed: ${chalk.blue(cmd)}`)
|
||||
logger.debug(`Error details: ${chalk.grey(String(error))}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
@@ -113,7 +112,7 @@ export function installPackage(
|
||||
fs.rmSync(targetDir, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
logger.debug(`Installing ${fullName} from registry...`)
|
||||
logger.debug(`Installing ${chalk.blue(fullName)} from registry...`)
|
||||
|
||||
executeCommand(`bun add ${fullName}@latest --ignore-scripts`, {
|
||||
cwd: ROOT_DIR
|
||||
@@ -122,12 +121,11 @@ export function installPackage(
|
||||
const installedPath = path.join(ROOT_DIR, 'node_modules', fullName)
|
||||
|
||||
if (!fs.existsSync(installedPath)) {
|
||||
logger.error(`Failed to find installed package at ${installedPath}`)
|
||||
logger.error(`Failed to find installed package at ${chalk.blue(installedPath)}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
logger.debug(`Copying ${fullName} to ${targetDir}...`)
|
||||
|
||||
logger.debug(`Copying ${chalk.blue(fullName)} to ${chalk.blue(targetDir)}...`)
|
||||
fs.cpSync(installedPath, targetDir, { recursive: true, dereference: true })
|
||||
|
||||
// Add to target package.json (apps or locales)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import chalk from 'chalk'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import executeCommand from './commands'
|
||||
@@ -60,7 +61,7 @@ export function getEnvVar(varName: string, fallback?: string): string {
|
||||
return fallback
|
||||
}
|
||||
|
||||
logger.error(`Missing required environment variable: ${varName}`)
|
||||
logger.error(`Missing required environment variable: ${chalk.red(varName)}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -77,7 +78,9 @@ export function killExistingProcess(
|
||||
if (typeof processKeywordOrPID === 'number') {
|
||||
process.kill(processKeywordOrPID)
|
||||
|
||||
logger.debug(`Killed process with PID: ${String(processKeywordOrPID)}`)
|
||||
logger.debug(
|
||||
`Killed process with PID: ${chalk.blue(String(processKeywordOrPID))}`
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -92,7 +95,7 @@ export function killExistingProcess(
|
||||
})
|
||||
|
||||
logger.debug(
|
||||
`Killed process matching keyword: ${processKeywordOrPID} (PID: ${serverInstance})`
|
||||
`Killed process matching keyword: ${chalk.blue(processKeywordOrPID)} (PID: ${chalk.blue(serverInstance)})`
|
||||
)
|
||||
|
||||
return parseInt(serverInstance, 10)
|
||||
@@ -122,6 +125,60 @@ export function checkPortInUse(port: number): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific port is currently in use.
|
||||
*
|
||||
* @param port - The port number to check
|
||||
* @returns True if the port is in use, false otherwise
|
||||
*/
|
||||
export function checkAddressInUse(address: string, port: string): boolean {
|
||||
logger.debug(
|
||||
`Checking if address ${chalk.blue(address)}:${chalk.blue(port)} is in use...`
|
||||
)
|
||||
|
||||
try {
|
||||
executeCommand('nc', { exitOnError: false }, ['-zv', address, port])
|
||||
|
||||
const ssOutput = executeCommand('ss', { exitOnError: false }, [
|
||||
'-tlnp',
|
||||
'src',
|
||||
`${address}:${port}`
|
||||
])
|
||||
|
||||
const lines = ssOutput.trim().split('\n')
|
||||
|
||||
if (lines.length > 1) {
|
||||
const processLine = lines[1]
|
||||
|
||||
const processMatch = processLine.match(
|
||||
/users:\(\("([^"]+)",pid=(\d+),fd=\d+\)\)/
|
||||
)
|
||||
|
||||
if (processMatch) {
|
||||
const processName = processMatch[1]
|
||||
|
||||
const pid = processMatch[2]
|
||||
|
||||
logger.error(
|
||||
`Address ${chalk.blue(address)}:${chalk.blue(port)} is in use by process: ${chalk.blue(processName)} (PID: ${chalk.blue(pid)})`
|
||||
)
|
||||
} else {
|
||||
logger.error(
|
||||
`Address ${chalk.blue(address)}:${chalk.blue(port)} is in use, but could not parse process info.`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
logger.error(
|
||||
`Address ${chalk.blue(address)}:${chalk.blue(port)} is in use, but no process info found.`
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a promise that resolves after the specified delay.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user