Skip to content
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
2 changes: 1 addition & 1 deletion api/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"@std/streams": "jsr:@std/streams@^1.1.1"
},
"name": "@01edu/api",
"version": "0.2.8",
"version": "0.2.9",
"license": "MIT",
"exports": {
"./context": "./context.ts",
Expand Down
14 changes: 14 additions & 0 deletions api/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ export const DEVTOOL_URL: string = ENV('DEVTOOL_URL', '')
*/
export const DEVTOOL_ACCESS_TOKEN: string = ENV('DEVTOOL_ACCESS_TOKEN', '')

/**
* A boolean flag indicating whether the developer tools integration is enabled.
*
* @example
* ```ts
* import { WITH_DEVTOOLS } from '@01edu/api/env';
*
* if (WITH_DEVTOOLS) {
* // Initialize or enable developer tools integrations
* }
* ```
*/
export const WITH_DEVTOOLS: boolean = truthy('WITH_DEVTOOLS')

/**
* Disable query debug instrumentation when set in the environment.
*/
Expand Down
84 changes: 77 additions & 7 deletions api/local_ipc_client.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { TextLineStream } from '@std/streams/text-line-stream'
import { PORT, WITH_DEVTOOLS } from './env.ts'

export const defaultSocketPath: string = Deno.build.os === 'windows'
? '\\\\.\\pipe\\01-devtools'
: `${Deno.env.get('XDG_RUNTIME_DIR') || '/tmp'}/01-devtools/01-devtools.sock`

const encoder = new TextEncoder()
const decoder = new TextDecoder()

async function sendCommand(
socketPath: string,
Expand All @@ -28,22 +30,90 @@ async function sendCommand(

export let devtoolsPort: number | null = null

export interface RegisterPayload {
projectId: string
name?: string
url: string
sqlEndpoint?: string | null
const getGitRepoName = async () => {
try {
const command = new Deno.Command('git', {
args: ['remote', 'get-url', 'origin'],
})
const { success, stdout } = await command.output()
if (!success) return null
const url = decoder.decode(stdout).trim()
return url.replace(/\.git$/, '').split(/[/:]/).pop() ?? null
} catch {
// Fail silently
}
return null
}

export async function register(
payload: RegisterPayload,
const getDirectoryName = () =>
Deno.cwd().replaceAll('\\', '/').split('/').pop() ?? 'project'

export async function registerToDevtools(
socketPath = defaultSocketPath,
): Promise<null | void> {
if (!WITH_DEVTOOLS) return

const checkRunning = await new Deno.Command('docker', {
args: ['ps', '-q', '-f', 'name=devtools', '-f', 'status=running'],
stdout: 'piped',
stderr: 'null',
}).output()

const isRunning = decoder.decode(checkRunning.stdout).trim().length > 0

if (!isRunning) {
await new Deno.Command('docker', {
args: ['rm', '-f', 'devtools'],
stdout: 'null',
stderr: 'null',
}).output()

const socketDir = `${Deno.env.get('XDG_RUNTIME_DIR') ?? '/tmp'}/01-devtools`

await Deno.mkdir(socketDir, { recursive: true })

const { success } = await new Deno.Command('docker', {
args: [
'run',
'-d',
'--rm',
'--name',
'devtools',
'--network',
'host',
'-e',
'GEMINI_API_KEY',
'-v',
`${socketDir}:/tmp/01-devtools`,
'-v',
'devtools-db:/app/db',
'ghcr.io/01-edu/devtools:latest-dev',
],
stdout: 'inherit',
stderr: 'inherit',
}).output()

if (!success) return null
}

const name = (await getGitRepoName()) ?? getDirectoryName()
const projectId = name.toLowerCase().replace(/[^a-z0-9-_]/g, '')
const url = `localhost:${PORT}`
const sqlEndpoint = `http://${url}/api/sql`

const payload = {
projectId,
name,
url,
sqlEndpoint,
}

const res = await sendCommand(
socketPath,
`register/${JSON.stringify(payload)}`,
)
if (!res) return null

devtoolsPort = res.port as number
console.info(`DevTools is running on: http://localhost:${devtoolsPort}`)
}