mirror of
https://github.com/linsa-io/linsa.git
synced 2026-01-11 22:40:32 +01:00
Update flow.toml to start web and desktop dev servers with port management and cleanup logic; enhance desktop preload and main process to resolve preload path, set web URLs, and expose shell functions; improve README with load fallback info and environment variables; add external logs configuration to worker package.
This commit is contained in:
70
flow.toml
70
flow.toml
@@ -365,9 +365,75 @@ shortcuts = ["d"]
|
|||||||
[[tasks]]
|
[[tasks]]
|
||||||
name = "desktop"
|
name = "desktop"
|
||||||
command = """
|
command = """
|
||||||
pnpm --filter @linsa/desktop dev
|
set -euo pipefail
|
||||||
|
|
||||||
|
LINS_WEB_PORT=5625
|
||||||
|
ONEFOCUS_WEB_PORT=5615
|
||||||
|
ONEFOCUS_ROOT="${ONEFOCUS_ROOT:-/Users/nikiv/org/1f/1f}"
|
||||||
|
|
||||||
|
if [ ! -d "$ONEFOCUS_ROOT" ]; then
|
||||||
|
echo "1focus repo not found at $ONEFOCUS_ROOT"
|
||||||
|
echo "Set ONEFOCUS_ROOT to the 1focus repo root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
kill_port() {
|
||||||
|
local port="$1"
|
||||||
|
if lsof -iTCP:"$port" -sTCP:LISTEN -P -n >/dev/null 2>&1; then
|
||||||
|
lsof -tiTCP:"$port" -sTCP:LISTEN | xargs kill -9 2>/dev/null || true
|
||||||
|
sleep 0.5
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_port() {
|
||||||
|
local port="$1"
|
||||||
|
for i in $(seq 1 40); do
|
||||||
|
if nc -z 127.0.0.1 "$port" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 0.25
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_port "$LINS_WEB_PORT"
|
||||||
|
kill_port "$ONEFOCUS_WEB_PORT"
|
||||||
|
|
||||||
|
LINS_WEB_PID=""
|
||||||
|
ONE_WEB_PID=""
|
||||||
|
LINS_DESK_PID=""
|
||||||
|
ONE_DESK_PID=""
|
||||||
|
|
||||||
|
echo "Starting Linsa web dev server on :$LINS_WEB_PORT..."
|
||||||
|
(cd packages/web && pnpm dev 2>&1 | while IFS= read -r line; do echo "[linsa:web] $line"; done) &
|
||||||
|
LINS_WEB_PID=$!
|
||||||
|
|
||||||
|
echo "Starting 1focus web dev server on :$ONEFOCUS_WEB_PORT..."
|
||||||
|
(cd "$ONEFOCUS_ROOT/packages/web" && pnpm dev 2>&1 | while IFS= read -r line; do echo "[1f:web] $line"; done) &
|
||||||
|
ONE_WEB_PID=$!
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
for pid in "$LINS_WEB_PID" "$ONE_WEB_PID" "$LINS_DESK_PID" "$ONE_DESK_PID"; do
|
||||||
|
if [ -n "$pid" ]; then
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
wait_for_port "$LINS_WEB_PORT"
|
||||||
|
wait_for_port "$ONEFOCUS_WEB_PORT"
|
||||||
|
|
||||||
|
echo "Starting Linsa Electron..."
|
||||||
|
(pnpm --filter @linsa/desktop dev 2>&1 | while IFS= read -r line; do echo "[linsa:desktop] $line"; done) &
|
||||||
|
LINS_DESK_PID=$!
|
||||||
|
|
||||||
|
echo "Starting 1focus Electron..."
|
||||||
|
(cd "$ONEFOCUS_ROOT" && pnpm dev:desktop 2>&1 | while IFS= read -r line; do echo "[1f:desktop] $line"; done) &
|
||||||
|
ONE_DESK_PID=$!
|
||||||
|
|
||||||
|
wait "$LINS_DESK_PID" "$ONE_DESK_PID"
|
||||||
"""
|
"""
|
||||||
description = "Start the Electron desktop app (electron-vite dev)."
|
description = "Run Linsa + 1focus desktop apps with their web dev servers."
|
||||||
dependencies = ["node", "pnpm"]
|
dependencies = ["node", "pnpm"]
|
||||||
shortcuts = ["desk"]
|
shortcuts = ["desk"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
# Linsa desktop
|
# Linsa desktop
|
||||||
|
|
||||||
Electron shell that mirrors the same structure we use in the `as` project: `electron-vite` bundling the main, preload, and React renderer, plus a small Jazz schema for storing folders locally.
|
Electron shell that mirrors the same structure we use in the `as` project: `electron-vite` bundling the main, preload, and React renderer. The window loads the Linsa web app by default (dev URL: `http://localhost:5625`) and falls back to the bundled renderer if the web app cannot be reached.
|
||||||
|
|
||||||
## Running locally
|
## Running locally
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm install
|
pnpm install
|
||||||
|
pnpm --filter @linsa/web dev
|
||||||
pnpm --filter @linsa/desktop dev
|
pnpm --filter @linsa/desktop dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
By default the Electron shell loads the existing web app. Override the target with `WEB_DEV_URL` or `WEB_URL` when needed.
|
||||||
|
|
||||||
Set a Jazz Cloud key to sync state instead of keeping it only on the device:
|
Set a Jazz Cloud key to sync state instead of keeping it only on the device:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -18,9 +21,15 @@ echo "VITE_JAZZ_API_KEY=your_jazz_key" >> .env
|
|||||||
# echo "VITE_JAZZ_PEER=ws://localhost:4200" >> .env
|
# echo "VITE_JAZZ_PEER=ws://localhost:4200" >> .env
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Optional environment
|
||||||
|
|
||||||
|
- `WEB_URL` / `WEB_DEV_URL` – where to load the web app from.
|
||||||
|
- `VITE_JAZZ_API_KEY` / `VITE_JAZZ_PEER` – used by the fallback renderer for sync.
|
||||||
|
|
||||||
## What it does
|
## What it does
|
||||||
|
|
||||||
- Uses `electron-vite` to bundle `main`, `preload`, and the React renderer.
|
- Uses `electron-vite` to bundle `main`, `preload`, and the React renderer.
|
||||||
|
- Loads the web client first and falls back to the bundled renderer if needed.
|
||||||
- Wraps the renderer with `JazzReactProvider` (storage in IndexedDB) and a simple Jazz schema to keep track of folders you want scanned.
|
- Wraps the renderer with `JazzReactProvider` (storage in IndexedDB) and a simple Jazz schema to keep track of folders you want scanned.
|
||||||
- Opens an OS folder picker (via the preload bridge) to add/remove code folders.
|
- Opens an OS folder picker (via the preload bridge) to add/remove code folders.
|
||||||
- Scans those folders for git repos and lets you open them in VS Code, Terminal, or Finder.
|
- Scans those folders for git repos and lets you open them in VS Code, Terminal, or Finder.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { fileURLToPath } from "node:url";
|
|||||||
import { readdir, stat } from "node:fs/promises";
|
import { readdir, stat } from "node:fs/promises";
|
||||||
import { app, BrowserWindow, dialog, ipcMain, shell } from "electron";
|
import { app, BrowserWindow, dialog, ipcMain, shell } from "electron";
|
||||||
|
|
||||||
|
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
interface GitRepo {
|
interface GitRepo {
|
||||||
@@ -50,13 +52,24 @@ async function findGitRepos(dir: string, maxDepth = 4): Promise<GitRepo[]> {
|
|||||||
return repos;
|
return repos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolvePreload() {
|
||||||
|
const candidates = [
|
||||||
|
join(__dirname, "../preload/index.js"),
|
||||||
|
join(__dirname, "../preload/index.cjs"),
|
||||||
|
join(__dirname, "../preload/index.mjs"),
|
||||||
|
];
|
||||||
|
|
||||||
|
return candidates.find((path) => existsSync(path));
|
||||||
|
}
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
const preloadJs = join(__dirname, "../preload/index.js");
|
const preloadPath = resolvePreload();
|
||||||
const preloadCjs = join(__dirname, "../preload/index.cjs");
|
const webDevUrl =
|
||||||
const preloadMjs = join(__dirname, "../preload/index.mjs");
|
process.env.WEB_DEV_URL ??
|
||||||
const preloadPath = [preloadJs, preloadCjs, preloadMjs].find((p) =>
|
process.env.VITE_WEB_DEV_URL ??
|
||||||
existsSync(p),
|
"http://localhost:5625";
|
||||||
);
|
const webProdUrl = process.env.WEB_URL ?? process.env.VITE_WEB_URL;
|
||||||
|
const targetUrl = webProdUrl ?? webDevUrl;
|
||||||
|
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
width: 1000,
|
width: 1000,
|
||||||
@@ -79,16 +92,22 @@ function createWindow() {
|
|||||||
return { action: "deny" };
|
return { action: "deny" };
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.ELECTRON_RENDERER_URL) {
|
mainWindow
|
||||||
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL);
|
.loadURL(targetUrl)
|
||||||
mainWindow.webContents.openDevTools();
|
.catch(() => mainWindow.loadFile(join(__dirname, "../renderer/index.html")));
|
||||||
} else {
|
|
||||||
mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
|
if (!webProdUrl) {
|
||||||
|
mainWindow.webContents.openDevTools({ mode: "bottom" });
|
||||||
}
|
}
|
||||||
|
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipcMain.handle("shell:open-external", async (_event, url: string) => {
|
||||||
|
if (!url) return;
|
||||||
|
await shell.openExternal(url);
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle("dialog:pick-folder", async (): Promise<string | null> => {
|
ipcMain.handle("dialog:pick-folder", async (): Promise<string | null> => {
|
||||||
const result = await dialog.showOpenDialog({
|
const result = await dialog.showOpenDialog({
|
||||||
title: "Select code folder",
|
title: "Select code folder",
|
||||||
@@ -102,6 +121,8 @@ ipcMain.handle("dialog:pick-folder", async (): Promise<string | null> => {
|
|||||||
return result.filePaths[0];
|
return result.filePaths[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("app:get-version", () => app.getVersion());
|
||||||
|
|
||||||
ipcMain.handle("repos:scan", async (_event, folders: string[]): Promise<GitRepo[]> => {
|
ipcMain.handle("repos:scan", async (_event, folders: string[]): Promise<GitRepo[]> => {
|
||||||
const allRepos: GitRepo[] = [];
|
const allRepos: GitRepo[] = [];
|
||||||
|
|
||||||
@@ -121,15 +142,23 @@ ipcMain.handle("repos:scan", async (_event, folders: string[]): Promise<GitRepo[
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("shell:show-in-folder", async (_event, path: string) => {
|
ipcMain.handle("shell:show-in-folder", async (_event, path: string) => {
|
||||||
|
if (!path) return;
|
||||||
|
shell.showItemInFolder(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("shell:show-path", async (_event, path: string) => {
|
||||||
|
if (!path) return;
|
||||||
shell.showItemInFolder(path);
|
shell.showItemInFolder(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("shell:open-in-editor", async (_event, path: string) => {
|
ipcMain.handle("shell:open-in-editor", async (_event, path: string) => {
|
||||||
|
if (!path) return;
|
||||||
const { exec } = await import("node:child_process");
|
const { exec } = await import("node:child_process");
|
||||||
exec(`code "${path}"`);
|
exec(`code "${path}"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("shell:open-in-terminal", async (_event, path: string) => {
|
ipcMain.handle("shell:open-in-terminal", async (_event, path: string) => {
|
||||||
|
if (!path) return;
|
||||||
const { exec } = await import("node:child_process");
|
const { exec } = await import("node:child_process");
|
||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
exec(`open -a Terminal "${path}"`);
|
exec(`open -a Terminal "${path}"`);
|
||||||
|
|||||||
@@ -6,10 +6,24 @@ export interface GitRepo {
|
|||||||
lastModified: number;
|
lastModified: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DesktopAPI {
|
||||||
|
openExternal: (url: string) => Promise<void>;
|
||||||
|
showPath: (path: string) => Promise<void>;
|
||||||
|
showInFolder: (path: string) => Promise<void>;
|
||||||
|
pickFolder: () => Promise<string | null>;
|
||||||
|
getVersion: () => Promise<string>;
|
||||||
|
scanRepos: (folders: string[]) => Promise<GitRepo[]>;
|
||||||
|
openInEditor: (path: string) => Promise<void>;
|
||||||
|
openInTerminal: (path: string) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("electronAPI", {
|
contextBridge.exposeInMainWorld("electronAPI", {
|
||||||
pickFolder: () => ipcRenderer.invoke("dialog:pick-folder") as Promise<string | null>,
|
openExternal: (url: string) => ipcRenderer.invoke("shell:open-external", url),
|
||||||
scanRepos: (folders: string[]) => ipcRenderer.invoke("repos:scan", folders) as Promise<GitRepo[]>,
|
showPath: (path: string) => ipcRenderer.invoke("shell:show-path", path),
|
||||||
showInFolder: (path: string) => ipcRenderer.invoke("shell:show-in-folder", path),
|
showInFolder: (path: string) => ipcRenderer.invoke("shell:show-in-folder", path),
|
||||||
|
pickFolder: () => ipcRenderer.invoke("dialog:pick-folder") as Promise<string | null>,
|
||||||
|
getVersion: () => ipcRenderer.invoke("app:get-version") as Promise<string>,
|
||||||
|
scanRepos: (folders: string[]) => ipcRenderer.invoke("repos:scan", folders) as Promise<GitRepo[]>,
|
||||||
openInEditor: (path: string) => ipcRenderer.invoke("shell:open-in-editor", path),
|
openInEditor: (path: string) => ipcRenderer.invoke("shell:open-in-editor", path),
|
||||||
openInTerminal: (path: string) => ipcRenderer.invoke("shell:open-in-terminal", path),
|
openInTerminal: (path: string) => ipcRenderer.invoke("shell:open-in-terminal", path),
|
||||||
});
|
} satisfies DesktopAPI);
|
||||||
|
|||||||
16
packages/desktop/src/renderer/env.d.ts
vendored
16
packages/desktop/src/renderer/env.d.ts
vendored
@@ -1,20 +1,8 @@
|
|||||||
interface GitRepo {
|
import type { DesktopAPI } from "../preload";
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
lastModified: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ElectronAPI {
|
|
||||||
pickFolder: () => Promise<string | null>;
|
|
||||||
scanRepos: (folders: string[]) => Promise<GitRepo[]>;
|
|
||||||
showInFolder: (path: string) => Promise<void>;
|
|
||||||
openInEditor: (path: string) => Promise<void>;
|
|
||||||
openInTerminal: (path: string) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
electronAPI: ElectronAPI;
|
electronAPI: DesktopAPI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"wrangler": "^4.53.0"
|
"wrangler": "^4.53.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@1focus/logs": "file:/Users/nikiv/lang/ts/lib/1focus/packages/logs",
|
||||||
"drizzle-orm": "^0.45.0",
|
"drizzle-orm": "^0.45.0",
|
||||||
"drizzle-zod": "^0.8.3",
|
"drizzle-zod": "^0.8.3",
|
||||||
"hono": "^4.10.4",
|
"hono": "^4.10.4",
|
||||||
|
|||||||
@@ -116,6 +116,23 @@ Authentication:
|
|||||||
- `PATCH /api/v1/admin/browser-sessions/:sessionId`
|
- `PATCH /api/v1/admin/browser-sessions/:sessionId`
|
||||||
- `DELETE /api/v1/admin/browser-sessions/:sessionId`
|
- `DELETE /api/v1/admin/browser-sessions/:sessionId`
|
||||||
|
|
||||||
|
### External Logs
|
||||||
|
|
||||||
|
To forward logs into 1focus Logs for the `linsa` server, set these secrets/vars in the worker:
|
||||||
|
|
||||||
|
- `FOCUS_LOGS_API_KEY` (required)
|
||||||
|
- `FOCUS_LOGS_SERVER` (optional, default: `linsa`)
|
||||||
|
- `FOCUS_LOGS_ENDPOINT` (optional, default: `https://1focus.app/api/logs`)
|
||||||
|
|
||||||
|
Then send a log:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8787/api/v1/admin/logs" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $ADMIN_API_KEY" \
|
||||||
|
-d '{"message":"Hello from linsa","level":"info"}'
|
||||||
|
```
|
||||||
|
|
||||||
### Example (create chat thread)
|
### Example (create chat thread)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Hono, type Context, type MiddlewareHandler } from "hono"
|
import { Hono, type Context, type MiddlewareHandler } from "hono"
|
||||||
import { cors } from "hono/cors"
|
import { cors } from "hono/cors"
|
||||||
import { eq } from "drizzle-orm"
|
import { eq } from "drizzle-orm"
|
||||||
|
import { createLogsClient, type LogPayload, type LogsWriteResult } from "@1focus/logs"
|
||||||
import {
|
import {
|
||||||
browser_session_tabs,
|
browser_session_tabs,
|
||||||
browser_sessions,
|
browser_sessions,
|
||||||
@@ -17,6 +18,9 @@ type Env = {
|
|||||||
ADMIN_API_KEY?: string
|
ADMIN_API_KEY?: string
|
||||||
DATABASE_URL?: string
|
DATABASE_URL?: string
|
||||||
HYPERDRIVE?: Hyperdrive
|
HYPERDRIVE?: Hyperdrive
|
||||||
|
FOCUS_LOGS_API_KEY?: string
|
||||||
|
FOCUS_LOGS_ENDPOINT?: string
|
||||||
|
FOCUS_LOGS_SERVER?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Hono app
|
// Create a new Hono app
|
||||||
@@ -62,6 +66,40 @@ const parseBody = async (c: Context<AppEnv>) => {
|
|||||||
return (await c.req.json().catch(() => ({}))) as Record<string, unknown>
|
return (await c.req.json().catch(() => ({}))) as Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||||
|
Boolean(value) && typeof value === "object" && !Array.isArray(value)
|
||||||
|
|
||||||
|
const getLogsClient = (env: Env) => {
|
||||||
|
const apiKey = env.FOCUS_LOGS_API_KEY?.trim()
|
||||||
|
if (!apiKey) return null
|
||||||
|
const endpoint = env.FOCUS_LOGS_ENDPOINT?.trim() || undefined
|
||||||
|
const server = env.FOCUS_LOGS_SERVER?.trim() || "linsa"
|
||||||
|
return createLogsClient({
|
||||||
|
apiKey,
|
||||||
|
server,
|
||||||
|
endpoint,
|
||||||
|
defaultSource: "linsa-worker",
|
||||||
|
timeoutMs: 3000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const logTo1Focus = async (
|
||||||
|
c: Context<AppEnv>,
|
||||||
|
payload: LogPayload,
|
||||||
|
awaitResult = false,
|
||||||
|
): Promise<LogsWriteResult | null> => {
|
||||||
|
const client = getLogsClient(c.env)
|
||||||
|
if (!client) return null
|
||||||
|
|
||||||
|
const promise = client.log(payload)
|
||||||
|
if (!awaitResult && c.executionCtx) {
|
||||||
|
c.executionCtx.waitUntil(promise)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
const parseInteger = (value: unknown) => {
|
const parseInteger = (value: unknown) => {
|
||||||
const numberValue =
|
const numberValue =
|
||||||
typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN
|
typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN
|
||||||
@@ -127,9 +165,55 @@ app.get("/", (c) => {
|
|||||||
// Example API endpoint
|
// Example API endpoint
|
||||||
app.get("/api/v1/hello", (c) => {
|
app.get("/api/v1/hello", (c) => {
|
||||||
const name = c.req.query("name") || "World"
|
const name = c.req.query("name") || "World"
|
||||||
|
void logTo1Focus(c, {
|
||||||
|
message: "hello endpoint called",
|
||||||
|
level: "info",
|
||||||
|
meta: { name },
|
||||||
|
})
|
||||||
return c.json({ message: `Hello, ${name}!` })
|
return c.json({ message: `Hello, ${name}!` })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Manual log write (admin-only)
|
||||||
|
app.post("/api/v1/admin/logs", async (c) => {
|
||||||
|
const body = await parseBody(c)
|
||||||
|
const message = typeof body.message === "string" ? body.message.trim() : ""
|
||||||
|
if (!message) {
|
||||||
|
return c.json({ error: "message is required" }, 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaInput = isRecord(body.meta) ? body.meta : {}
|
||||||
|
const meta = {
|
||||||
|
...metaInput,
|
||||||
|
path: c.req.path,
|
||||||
|
method: c.req.method,
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload: LogPayload = {
|
||||||
|
message,
|
||||||
|
level: typeof body.level === "string" ? (body.level as LogPayload["level"]) : "info",
|
||||||
|
source: typeof body.source === "string" ? body.source : undefined,
|
||||||
|
timestamp:
|
||||||
|
typeof body.timestamp === "number" || typeof body.timestamp === "string"
|
||||||
|
? body.timestamp
|
||||||
|
: undefined,
|
||||||
|
meta,
|
||||||
|
attributes: isRecord(body.attributes) ? body.attributes : undefined,
|
||||||
|
resource: isRecord(body.resource) ? body.resource : undefined,
|
||||||
|
scope: isRecord(body.scope) ? body.scope : undefined,
|
||||||
|
traceId: typeof body.traceId === "string" ? body.traceId : undefined,
|
||||||
|
spanId: typeof body.spanId === "string" ? body.spanId : undefined,
|
||||||
|
parentSpanId: typeof body.parentSpanId === "string" ? body.parentSpanId : undefined,
|
||||||
|
traceFlags: typeof body.traceFlags === "number" ? body.traceFlags : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await logTo1Focus(c, payload, true)
|
||||||
|
if (!result) {
|
||||||
|
return c.json({ error: "Logging not configured" }, 503)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({ result }, result.ok ? 200 : 502)
|
||||||
|
})
|
||||||
|
|
||||||
// Canvas endpoints
|
// Canvas endpoints
|
||||||
app.post("/api/v1/admin/canvas", async (c) => {
|
app.post("/api/v1/admin/canvas", async (c) => {
|
||||||
const body = await parseBody(c)
|
const body = await parseBody(c)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ packages:
|
|||||||
- packages/*
|
- packages/*
|
||||||
|
|
||||||
onlyBuiltDependencies:
|
onlyBuiltDependencies:
|
||||||
|
- electron
|
||||||
- esbuild
|
- esbuild
|
||||||
- sharp
|
- sharp
|
||||||
- workerd
|
- workerd
|
||||||
|
|||||||
Reference in New Issue
Block a user