feat(deploy): run shared toolkit socket
Lint and Build Checks / Lint, Test, and Build (push) Failing after 364h2m28s
Lint and Build Checks / Lint, Test, and Build (push) Failing after 364h2m28s
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
VITE_TOOLKIT_SOCKET_URL=http://bone-kingdom.tabitha.tealthrone:5174
|
||||||
@@ -21,18 +21,23 @@ steps:
|
|||||||
|
|
||||||
validate:
|
validate:
|
||||||
image: node:24-bookworm
|
image: node:24-bookworm
|
||||||
|
environment:
|
||||||
|
VITE_TOOLKIT_SOCKET_URL: http://bone-kingdom.tabitha.tealthrone:5174
|
||||||
commands:
|
commands:
|
||||||
- npm ci
|
- npm ci
|
||||||
- npm run lint
|
- npm run lint
|
||||||
- npm run test -- --runInBand
|
- npm run test -- --runInBand
|
||||||
- npm run web:build
|
- npm run web:build
|
||||||
|
- npm run deploy:frontend-health
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
image: node:24-bookworm
|
image: node:24-bookworm
|
||||||
environment:
|
environment:
|
||||||
DEPLOY_TARGET_URL: http://bone-kingdom.tabitha.tealthrone/
|
DEPLOY_TARGET_URL: http://bone-kingdom.tabitha.tealthrone/
|
||||||
|
DEPLOY_TOOLKIT_SOCKET_URL: http://bone-kingdom.tabitha.tealthrone:5174
|
||||||
DEPLOY_HOST: 100.108.87.106
|
DEPLOY_HOST: 100.108.87.106
|
||||||
DEPLOY_USER: Tabitha
|
DEPLOY_USER: Tabitha
|
||||||
|
DEPLOY_REPO_PATH: /Users/Tabitha/.openclaw/workspace_father/blb-throne-of-the-bone-king
|
||||||
DEPLOY_PATH: /Users/Tabitha/.openclaw/workspace_father/blb-throne-of-the-bone-king/dist/renderer
|
DEPLOY_PATH: /Users/Tabitha/.openclaw/workspace_father/blb-throne-of-the-bone-king/dist/renderer
|
||||||
DEPLOY_SSH_PRIVATE_KEY:
|
DEPLOY_SSH_PRIVATE_KEY:
|
||||||
from_secret: deploy_ssh_private_key
|
from_secret: deploy_ssh_private_key
|
||||||
@@ -46,8 +51,19 @@ steps:
|
|||||||
- ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
|
- ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
|
||||||
- ssh "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p '$DEPLOY_PATH'"
|
- ssh "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p '$DEPLOY_PATH'"
|
||||||
- rsync -az --delete dist/renderer/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/"
|
- rsync -az --delete dist/renderer/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/"
|
||||||
|
- rsync -az package.json package-lock.json "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_REPO_PATH/"
|
||||||
|
- rsync -az --delete socket/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_REPO_PATH/socket/"
|
||||||
|
- rsync -az --delete scripts/deploy/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_REPO_PATH/scripts/deploy/"
|
||||||
|
- rsync -az --ignore-existing src/data/toolkit/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_REPO_PATH/src/data/toolkit/"
|
||||||
|
- rsync -az --ignore-existing src/assets/images/uploads/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_REPO_PATH/src/assets/images/uploads/"
|
||||||
|
- rsync -az --ignore-existing src/assets/music/uploads/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_REPO_PATH/src/assets/music/uploads/"
|
||||||
|
- ssh "$DEPLOY_USER@$DEPLOY_HOST" "cd '$DEPLOY_REPO_PATH' && TOOLKIT_SOCKET_HOST=0.0.0.0 TOOLKIT_SOCKET_PORT=5174 TOOLKIT_SOCKET_CORS_ORIGIN='http://127.0.0.1:5173,http://localhost:5173,http://bone-kingdom.tabitha.tealthrone' sh scripts/deploy/restart-toolkit-socket.sh '$DEPLOY_REPO_PATH'"
|
||||||
|
- curl -fsS "$DEPLOY_TOOLKIT_SOCKET_URL/health" >/tmp/bone-kingdom-socket-health.json
|
||||||
|
- grep -q '"ok":true' /tmp/bone-kingdom-socket-health.json
|
||||||
- curl -fsS "$DEPLOY_TARGET_URL" >/tmp/bone-kingdom-index.html
|
- curl -fsS "$DEPLOY_TARGET_URL" >/tmp/bone-kingdom-index.html
|
||||||
- grep -q '<div id="root">' /tmp/bone-kingdom-index.html
|
- grep -q '<div id="root">' /tmp/bone-kingdom-index.html
|
||||||
|
- curl -fsS "${DEPLOY_TARGET_URL%/}/health" >/tmp/bone-kingdom-frontend-health.json
|
||||||
|
- grep -q '"ok":true' /tmp/bone-kingdom-frontend-health.json
|
||||||
depends_on:
|
depends_on:
|
||||||
- validate
|
- validate
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ __tests__/ # Jest tests
|
|||||||
- Change Log content is sourced from `src/data/toolkit/changelog.json`.
|
- Change Log content is sourced from `src/data/toolkit/changelog.json`.
|
||||||
- Credits Toolkit data is sourced from `src/data/toolkit/credits.json` and synced through the socket server.
|
- Credits Toolkit data is sourced from `src/data/toolkit/credits.json` and synced through the socket server.
|
||||||
- Toolkit Images and Audio records can upload files through the socket into `src/assets/images/uploads/` and `src/assets/music/uploads/`.
|
- Toolkit Images and Audio records can upload files through the socket into `src/assets/images/uploads/` and `src/assets/music/uploads/`.
|
||||||
|
- Deployed Toolkit authoring uses `http://bone-kingdom.tabitha.tealthrone:5174` and intentionally runs from the shared repo workspace so edits update development JSON/assets while coding continues.
|
||||||
- Toolkit seed datasets live under `src/data/toolkit/` and are exported through `src/data/index.ts`.
|
- Toolkit seed datasets live under `src/data/toolkit/` and are exported through `src/data/index.ts`.
|
||||||
- Shared data interfaces live under `src/data/types/`.
|
- Shared data interfaces live under `src/data/types/`.
|
||||||
|
|
||||||
@@ -132,6 +133,8 @@ npm run web
|
|||||||
npm run dev:toolkit
|
npm run dev:toolkit
|
||||||
npm run dev:all
|
npm run dev:all
|
||||||
npm run dev:clear
|
npm run dev:clear
|
||||||
|
npm run deploy:frontend-health
|
||||||
|
npm run deploy:toolkit
|
||||||
npm run electron:dev
|
npm run electron:dev
|
||||||
npm run web:build
|
npm run web:build
|
||||||
npm run electron:build
|
npm run electron:build
|
||||||
|
|||||||
@@ -124,6 +124,27 @@ Socket health check:
|
|||||||
curl http://127.0.0.1:5174/health
|
curl http://127.0.0.1:5174/health
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Shared Deploy Toolkit Socket
|
||||||
|
|
||||||
|
The hosted browser app at `http://bone-kingdom.tabitha.tealthrone/` is built with `VITE_TOOLKIT_SOCKET_URL=http://bone-kingdom.tabitha.tealthrone:5174`. That socket is intentionally restarted from the shared repo workspace instead of from `dist/`, so Toolkit edits made by another person update the development JSON and upload folders directly:
|
||||||
|
|
||||||
|
- JSON content: `src/data/toolkit/`
|
||||||
|
- Uploaded images: `src/assets/images/uploads/`
|
||||||
|
- Uploaded audio: `src/assets/music/uploads/`
|
||||||
|
|
||||||
|
Woodpecker deploys the static renderer, restarts the shared Toolkit socket with `scripts/deploy/restart-toolkit-socket.sh`, and validates both:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://bone-kingdom.tabitha.tealthrone/health
|
||||||
|
curl http://bone-kingdom.tabitha.tealthrone:5174/health
|
||||||
|
```
|
||||||
|
|
||||||
|
For a manual shared-socket restart on the host:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TOOLKIT_SOCKET_HOST=0.0.0.0 npm run deploy:toolkit
|
||||||
|
```
|
||||||
|
|
||||||
## Assets
|
## Assets
|
||||||
|
|
||||||
Image assets live in `src/assets/images/`. Fonts live in `src/assets/fonts/`. Music lives in `src/assets/music/`. Browser SCSS entrypoints live in `src/assets/scss/`.
|
Image assets live in `src/assets/images/`. Fonts live in `src/assets/fonts/`. Music lives in `src/assets/music/`. Browser SCSS entrypoints live in `src/assets/scss/`.
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { getToolkitSocketUrl } from '../src/socket/toolkitSocketClient';
|
||||||
|
|
||||||
|
const originalToolkitSocketUrl = process.env.TOOLKIT_SOCKET_URL;
|
||||||
|
const originalLocation = Object.getOwnPropertyDescriptor(
|
||||||
|
globalThis,
|
||||||
|
'location',
|
||||||
|
);
|
||||||
|
|
||||||
|
function setTestLocation(hostname: string, protocol = 'http:') {
|
||||||
|
Object.defineProperty(globalThis, 'location', {
|
||||||
|
configurable: true,
|
||||||
|
value: {
|
||||||
|
hostname,
|
||||||
|
protocol,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreTestLocation() {
|
||||||
|
if (originalLocation == null) {
|
||||||
|
Reflect.deleteProperty(globalThis, 'location');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(globalThis, 'location', originalLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('toolkit socket client configuration', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
if (originalToolkitSocketUrl == null) {
|
||||||
|
delete process.env.TOOLKIT_SOCKET_URL;
|
||||||
|
} else {
|
||||||
|
process.env.TOOLKIT_SOCKET_URL = originalToolkitSocketUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreTestLocation();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses explicit toolkit socket URL configuration first', () => {
|
||||||
|
process.env.TOOLKIT_SOCKET_URL = 'http://socket.example.test:5174';
|
||||||
|
setTestLocation('bone-kingdom.tabitha.tealthrone');
|
||||||
|
|
||||||
|
expect(getToolkitSocketUrl()).toBe('http://socket.example.test:5174');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses local socket defaults for localhost development', () => {
|
||||||
|
delete process.env.TOOLKIT_SOCKET_URL;
|
||||||
|
setTestLocation('localhost');
|
||||||
|
|
||||||
|
expect(getToolkitSocketUrl()).toBe('http://127.0.0.1:5174');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('derives the deployed socket URL from the current browser host', () => {
|
||||||
|
delete process.env.TOOLKIT_SOCKET_URL;
|
||||||
|
setTestLocation('bone-kingdom.tabitha.tealthrone');
|
||||||
|
|
||||||
|
expect(getToolkitSocketUrl()).toBe(
|
||||||
|
'http://bone-kingdom.tabitha.tealthrone:5174',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -178,6 +178,7 @@ Canonical list lives in `manifest.llm.json` → `knownGaps`. Current highlights:
|
|||||||
- `wiki/` currently stores reference images and loose wiki/design assets
|
- `wiki/` currently stores reference images and loose wiki/design assets
|
||||||
- Toolkit JSON files now contain starter data based on the current wiki schemas; Credits, Changelog, and Legal live under `src/data/toolkit/`
|
- Toolkit JSON files now contain starter data based on the current wiki schemas; Credits, Changelog, and Legal live under `src/data/toolkit/`
|
||||||
- Toolkit media uploads are written by the local socket server under `src/assets/images/uploads/` and `src/assets/music/uploads/`
|
- Toolkit media uploads are written by the local socket server under `src/assets/images/uploads/` and `src/assets/music/uploads/`
|
||||||
|
- Deployed Toolkit authoring uses a shared socket at `http://bone-kingdom.tabitha.tealthrone:5174` that runs from the repo workspace and writes to development JSON/assets
|
||||||
|
|
||||||
## Notes for LLMs
|
## Notes for LLMs
|
||||||
- **Read `manifest.llm.json` for task routing** — use `taskRoutes` to pick the narrowest file set
|
- **Read `manifest.llm.json` for task routing** — use `taskRoutes` to pick the narrowest file set
|
||||||
@@ -189,6 +190,7 @@ Canonical list lives in `manifest.llm.json` → `knownGaps`. Current highlights:
|
|||||||
- **Persistence is platform-split** — extend `persistStorage.ts` / `persistStorage.web.ts`, not stores directly
|
- **Persistence is platform-split** — extend `persistStorage.ts` / `persistStorage.web.ts`, not stores directly
|
||||||
- `src/data/AGENTS.md` contains extra rules for JSON content and Toolkit seed data
|
- `src/data/AGENTS.md` contains extra rules for JSON content and Toolkit seed data
|
||||||
- Data consumers should import from `src/data/index.ts`; keep `src/data/types/` aligned with JSON schema changes
|
- Data consumers should import from `src/data/index.ts`; keep `src/data/types/` aligned with JSON schema changes
|
||||||
|
- Keep the deployed Toolkit socket tied to the shared repo workspace; do not move it to static `dist/` output or it will stop updating editable JSON/assets for parallel work
|
||||||
- Before committing or pushing meaningful deliverables, update `src/data/toolkit/changelog.json`; Husky bumps build metadata but does not write release notes
|
- Before committing or pushing meaningful deliverables, update `src/data/toolkit/changelog.json`; Husky bumps build metadata but does not write release notes
|
||||||
- iOS Pods and `node_modules/` are not committed; run `pod install` after pulling native dep changes
|
- iOS Pods and `node_modules/` are not committed; run `pod install` after pulling native dep changes
|
||||||
- `dist/` build artifacts are gitignored
|
- `dist/` build artifacts are gitignored
|
||||||
|
|||||||
+17
-46
@@ -54,12 +54,7 @@
|
|||||||
"purpose": "Human-facing overview for setup, commands, architecture, toolkit behavior, and validation."
|
"purpose": "Human-facing overview for setup, commands, architecture, toolkit behavior, and validation."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalReadOrder": [
|
"globalReadOrder": ["llm.txt", "manifest.llm.json", "AGENTS.md", "README.md"],
|
||||||
"llm.txt",
|
|
||||||
"manifest.llm.json",
|
|
||||||
"AGENTS.md",
|
|
||||||
"README.md"
|
|
||||||
],
|
|
||||||
"globalReadOrderSemantics": "Broad orientation order. Task routers should prefer taskRoutes read arrays to avoid over-reading.",
|
"globalReadOrderSemantics": "Broad orientation order. Task routers should prefer taskRoutes read arrays to avoid over-reading.",
|
||||||
"documentationFiles": [
|
"documentationFiles": [
|
||||||
{
|
{
|
||||||
@@ -122,6 +117,8 @@
|
|||||||
"entry": "socket/server.mjs",
|
"entry": "socket/server.mjs",
|
||||||
"devCommand": "npm run dev:toolkit",
|
"devCommand": "npm run dev:toolkit",
|
||||||
"healthUrl": "http://127.0.0.1:5174/health",
|
"healthUrl": "http://127.0.0.1:5174/health",
|
||||||
|
"deployCommand": "npm run deploy:toolkit",
|
||||||
|
"deployHealthUrl": "http://bone-kingdom.tabitha.tealthrone:5174/health",
|
||||||
"primary": true
|
"primary": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -167,6 +164,8 @@
|
|||||||
"dev:toolkit": "node socket/server.mjs",
|
"dev:toolkit": "node socket/server.mjs",
|
||||||
"dev:all": "concurrently -k -n app,toolkit -c blue,magenta \"npm run dev\" \"npm run dev:toolkit\"",
|
"dev:all": "concurrently -k -n app,toolkit -c blue,magenta \"npm run dev\" \"npm run dev:toolkit\"",
|
||||||
"dev:clear": "node socket/clear.mjs",
|
"dev:clear": "node socket/clear.mjs",
|
||||||
|
"deploy:frontend-health": "node scripts/deploy/write-frontend-health.mjs",
|
||||||
|
"deploy:toolkit": "sh scripts/deploy/restart-toolkit-socket.sh",
|
||||||
"electron:dev": "npm run electron:compile && cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://127.0.0.1:5173 concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"",
|
"electron:dev": "npm run electron:compile && cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://127.0.0.1:5173 concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"",
|
||||||
"electron:build": "npm run web:build && npm run electron:compile",
|
"electron:build": "npm run web:build && npm run electron:compile",
|
||||||
"electron:compile": "tsc -p tsconfig.electron.json",
|
"electron:compile": "tsc -p tsconfig.electron.json",
|
||||||
@@ -178,11 +177,7 @@
|
|||||||
},
|
},
|
||||||
"architecture": {
|
"architecture": {
|
||||||
"rootComponent": "App.tsx",
|
"rootComponent": "App.tsx",
|
||||||
"componentTree": [
|
"componentTree": ["SafeAreaProvider", "GameViewport", "AppNavigator"],
|
||||||
"SafeAreaProvider",
|
|
||||||
"GameViewport",
|
|
||||||
"AppNavigator"
|
|
||||||
],
|
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"type": "zustand-store with animated fade transitions",
|
"type": "zustand-store with animated fade transitions",
|
||||||
"routes": "src/navigation/routes.ts",
|
"routes": "src/navigation/routes.ts",
|
||||||
@@ -237,13 +232,7 @@
|
|||||||
"androidDefault",
|
"androidDefault",
|
||||||
"phoneLarge"
|
"phoneLarge"
|
||||||
],
|
],
|
||||||
"volumeKeys": [
|
"volumeKeys": ["master", "music", "ambience", "speech", "sfx"]
|
||||||
"master",
|
|
||||||
"music",
|
|
||||||
"ambience",
|
|
||||||
"speech",
|
|
||||||
"sfx"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"screens": [
|
"screens": [
|
||||||
"src/screens/HomeScreen.tsx",
|
"src/screens/HomeScreen.tsx",
|
||||||
@@ -313,11 +302,7 @@
|
|||||||
"hook": "src/hooks/useBackgroundMusic.ts",
|
"hook": "src/hooks/useBackgroundMusic.ts",
|
||||||
"webHook": "src/hooks/useBackgroundMusic.web.ts",
|
"webHook": "src/hooks/useBackgroundMusic.web.ts",
|
||||||
"defaultTrack": "src/assets/music/throne-of-the-bone-king.mp3",
|
"defaultTrack": "src/assets/music/throne-of-the-bone-king.mp3",
|
||||||
"volumeControls": [
|
"volumeControls": ["master", "music", "mute"]
|
||||||
"master",
|
|
||||||
"music",
|
|
||||||
"mute"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"toolkit": {
|
"toolkit": {
|
||||||
"screen": "src/screens/ToolkitScreen.tsx",
|
"screen": "src/screens/ToolkitScreen.tsx",
|
||||||
@@ -358,10 +343,7 @@
|
|||||||
"webEntry": "src/web/main.tsx",
|
"webEntry": "src/web/main.tsx",
|
||||||
"electronMain": "electron/main.ts",
|
"electronMain": "electron/main.ts",
|
||||||
"electronPreload": "electron/preload.ts",
|
"electronPreload": "electron/preload.ts",
|
||||||
"buildOutputs": [
|
"buildOutputs": ["dist/renderer/", "dist/electron/"]
|
||||||
"dist/renderer/",
|
|
||||||
"dist/electron/"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"taskRoutes": {
|
"taskRoutes": {
|
||||||
"ui-screen": {
|
"ui-screen": {
|
||||||
@@ -416,11 +398,7 @@
|
|||||||
"Electron main/preload compile to dist/electron/.",
|
"Electron main/preload compile to dist/electron/.",
|
||||||
"Vite emits renderer assets to dist/renderer/."
|
"Vite emits renderer assets to dist/renderer/."
|
||||||
],
|
],
|
||||||
"verify": [
|
"verify": ["npm run lint", "npm run web:build", "npm run electron:build"],
|
||||||
"npm run lint",
|
|
||||||
"npm run web:build",
|
|
||||||
"npm run electron:build"
|
|
||||||
],
|
|
||||||
"update": [
|
"update": [
|
||||||
"manifest.llm.json when targets, build outputs, or dev commands change"
|
"manifest.llm.json when targets, build outputs, or dev commands change"
|
||||||
]
|
]
|
||||||
@@ -465,9 +443,7 @@
|
|||||||
"Keep Changelog, Credits, and Legal JSON under src/data/toolkit/.",
|
"Keep Changelog, Credits, and Legal JSON under src/data/toolkit/.",
|
||||||
"Update src/data/toolkit/changelog.json before committing or pushing meaningful deliverables unless explicitly skipped."
|
"Update src/data/toolkit/changelog.json before committing or pushing meaningful deliverables unless explicitly skipped."
|
||||||
],
|
],
|
||||||
"verify": [
|
"verify": ["npm run test -- --runInBand"]
|
||||||
"npm run test -- --runInBand"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"realtime": {
|
"realtime": {
|
||||||
"description": "Socket.IO server/client wiring for Toolkit connection status and content sync.",
|
"description": "Socket.IO server/client wiring for Toolkit connection status and content sync.",
|
||||||
@@ -516,9 +492,7 @@
|
|||||||
"<target source>",
|
"<target source>",
|
||||||
"<related __tests__ file>"
|
"<related __tests__ file>"
|
||||||
],
|
],
|
||||||
"verify": [
|
"verify": ["npm run test -- --runInBand"]
|
||||||
"npm run test -- --runInBand"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"docs": {
|
"docs": {
|
||||||
"description": "README, AGENTS, llm.txt, manifest, docs, or wiki maintenance.",
|
"description": "README, AGENTS, llm.txt, manifest, docs, or wiki maintenance.",
|
||||||
@@ -627,14 +601,13 @@
|
|||||||
"No JSON Schema validator exists yet for manifest.llm.json.",
|
"No JSON Schema validator exists yet for manifest.llm.json.",
|
||||||
"Toolkit content panels use socket-backed structured authoring forms for current JSON fields.",
|
"Toolkit content panels use socket-backed structured authoring forms for current JSON fields.",
|
||||||
"Toolkit audio and image resources can upload local files through the Toolkit socket into src/assets uploads folders.",
|
"Toolkit audio and image resources can upload local files through the Toolkit socket into src/assets uploads folders.",
|
||||||
|
"The deployed Toolkit socket is expected to run from the shared repo workspace so deployed browser authoring updates development JSON and uploaded assets.",
|
||||||
"Toolkit JSON schemas are first-pass wiki-derived interfaces and should remain flexible while authoring forms stabilize.",
|
"Toolkit JSON schemas are first-pass wiki-derived interfaces and should remain flexible while authoring forms stabilize.",
|
||||||
"Native android/ and ios/ folders are not currently present in this checkout.",
|
"Native android/ and ios/ folders are not currently present in this checkout.",
|
||||||
"No database exists yet; editable content is intentionally JSON-backed until schemas stabilize."
|
"No database exists yet; editable content is intentionally JSON-backed until schemas stabilize."
|
||||||
],
|
],
|
||||||
"verificationBaseline": {
|
"verificationBaseline": {
|
||||||
"logicOrStoreChanges": [
|
"logicOrStoreChanges": ["npm run test -- --runInBand"],
|
||||||
"npm run test -- --runInBand"
|
|
||||||
],
|
|
||||||
"uiChanges": [
|
"uiChanges": [
|
||||||
"npm run lint",
|
"npm run lint",
|
||||||
"npm run test -- --runInBand",
|
"npm run test -- --runInBand",
|
||||||
@@ -648,12 +621,10 @@
|
|||||||
"socketChanges": [
|
"socketChanges": [
|
||||||
"npm run test -- --runInBand",
|
"npm run test -- --runInBand",
|
||||||
"npm run dev:toolkit",
|
"npm run dev:toolkit",
|
||||||
"curl http://127.0.0.1:5174/health"
|
"curl http://127.0.0.1:5174/health",
|
||||||
],
|
"curl http://bone-kingdom.tabitha.tealthrone:5174/health"
|
||||||
"mobileChanges": [
|
|
||||||
"npm run lint",
|
|
||||||
"npm run test -- --runInBand"
|
|
||||||
],
|
],
|
||||||
|
"mobileChanges": ["npm run lint", "npm run test -- --runInBand"],
|
||||||
"docsChanges": [
|
"docsChanges": [
|
||||||
"node -e \"JSON.parse(require('fs').readFileSync('manifest.llm.json','utf8')); console.log('manifest ok')\"",
|
"node -e \"JSON.parse(require('fs').readFileSync('manifest.llm.json','utf8')); console.log('manifest ok')\"",
|
||||||
"npm run lint"
|
"npm run lint"
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
"dev:all": "concurrently -k -n app,toolkit -c blue,magenta \"npm run dev\" \"npm run dev:toolkit\"",
|
"dev:all": "concurrently -k -n app,toolkit -c blue,magenta \"npm run dev\" \"npm run dev:toolkit\"",
|
||||||
"dev:clear": "node socket/clear.mjs",
|
"dev:clear": "node socket/clear.mjs",
|
||||||
"dev:toolkit": "node socket/server.mjs",
|
"dev:toolkit": "node socket/server.mjs",
|
||||||
|
"deploy:frontend-health": "node scripts/deploy/write-frontend-health.mjs",
|
||||||
|
"deploy:toolkit": "sh scripts/deploy/restart-toolkit-socket.sh",
|
||||||
"electron:build": "npm run web:build && npm run electron:compile",
|
"electron:build": "npm run web:build && npm run electron:compile",
|
||||||
"electron:compile": "tsc -p tsconfig.electron.json",
|
"electron:compile": "tsc -p tsconfig.electron.json",
|
||||||
"electron:dev": "npm run electron:compile && cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://127.0.0.1:5173 concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"",
|
"electron:dev": "npm run electron:compile && cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://127.0.0.1:5173 concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"",
|
||||||
|
|||||||
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
repo_path="${1:-$(pwd)}"
|
||||||
|
socket_host="${TOOLKIT_SOCKET_HOST:-0.0.0.0}"
|
||||||
|
socket_port="${TOOLKIT_SOCKET_PORT:-5174}"
|
||||||
|
socket_cors_origin="${TOOLKIT_SOCKET_CORS_ORIGIN:-http://127.0.0.1:5173,http://localhost:5173,http://bone-kingdom.tabitha.tealthrone}"
|
||||||
|
pid_file="$repo_path/.toolkit-socket.pid"
|
||||||
|
log_directory="$repo_path/logs"
|
||||||
|
log_file="$log_directory/toolkit-socket.log"
|
||||||
|
|
||||||
|
cd "$repo_path"
|
||||||
|
|
||||||
|
if [ ! -f package.json ] || [ ! -f socket/server.mjs ]; then
|
||||||
|
echo "[deploy:toolkit] $repo_path is not a Bone Kingdom repo checkout."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$log_directory" \
|
||||||
|
"$repo_path/src/assets/images/uploads" \
|
||||||
|
"$repo_path/src/assets/music/uploads"
|
||||||
|
|
||||||
|
if [ ! -d "$repo_path/node_modules/socket.io" ]; then
|
||||||
|
echo "[deploy:toolkit] node_modules missing socket.io; installing workspace dependencies."
|
||||||
|
npm install --no-audit --no-fund
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$pid_file" ]; then
|
||||||
|
existing_pid="$(cat "$pid_file")"
|
||||||
|
|
||||||
|
if [ -n "$existing_pid" ] && kill -0 "$existing_pid" 2>/dev/null; then
|
||||||
|
echo "[deploy:toolkit] stopping existing socket process $existing_pid"
|
||||||
|
kill -TERM "$existing_pid"
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[deploy:toolkit] starting socket on $socket_host:$socket_port"
|
||||||
|
|
||||||
|
TOOLKIT_SOCKET_HOST="$socket_host" \
|
||||||
|
TOOLKIT_SOCKET_PORT="$socket_port" \
|
||||||
|
TOOLKIT_SOCKET_CORS_ORIGIN="$socket_cors_origin" \
|
||||||
|
nohup node socket/server.mjs >"$log_file" 2>&1 &
|
||||||
|
|
||||||
|
socket_pid="$!"
|
||||||
|
printf '%s\n' "$socket_pid" > "$pid_file"
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
if ! kill -0 "$socket_pid" 2>/dev/null; then
|
||||||
|
echo "[deploy:toolkit] socket failed to start; recent log output follows."
|
||||||
|
tail -n 80 "$log_file" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
curl -fsS "http://127.0.0.1:$socket_port/health" >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[deploy:toolkit] socket running as $socket_pid"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { mkdir, writeFile } from 'node:fs/promises';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const packageJson = require('../../package.json');
|
||||||
|
|
||||||
|
const outputPath = process.argv[2] ?? 'dist/renderer/health';
|
||||||
|
const commit =
|
||||||
|
process.env.CI_COMMIT_SHA ??
|
||||||
|
process.env.VITE_LAST_SUCCESSFUL_COMMIT ??
|
||||||
|
'unknown';
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
ok: true,
|
||||||
|
service: 'blb-frontend',
|
||||||
|
version: packageJson.version,
|
||||||
|
commit,
|
||||||
|
generatedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await mkdir(path.dirname(outputPath), { recursive: true });
|
||||||
|
await writeFile(outputPath, `${JSON.stringify(payload)}\n`, 'utf8');
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Toolkit Socket
|
||||||
|
|
||||||
|
The Toolkit socket is a local/shared Socket.IO authoring server for browser-only Toolkit workflows.
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev:toolkit
|
||||||
|
curl http://127.0.0.1:5174/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Default local settings:
|
||||||
|
|
||||||
|
- host: `127.0.0.1`
|
||||||
|
- port: `5174`
|
||||||
|
- CORS origin: `http://127.0.0.1:5173`
|
||||||
|
|
||||||
|
## Shared Deployment
|
||||||
|
|
||||||
|
The deployed browser app at `http://bone-kingdom.tabitha.tealthrone/` connects to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://bone-kingdom.tabitha.tealthrone:5174
|
||||||
|
```
|
||||||
|
|
||||||
|
Woodpecker restarts this socket from the shared repo workspace with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TOOLKIT_SOCKET_HOST=0.0.0.0 \
|
||||||
|
TOOLKIT_SOCKET_PORT=5174 \
|
||||||
|
TOOLKIT_SOCKET_CORS_ORIGIN='http://127.0.0.1:5173,http://localhost:5173,http://bone-kingdom.tabitha.tealthrone' \
|
||||||
|
sh scripts/deploy/restart-toolkit-socket.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Running from the repo workspace is intentional. Toolkit edits made from the deployed browser update the same source files used during development:
|
||||||
|
|
||||||
|
- `src/data/toolkit/*.json`
|
||||||
|
- `src/assets/images/uploads/`
|
||||||
|
- `src/assets/music/uploads/`
|
||||||
|
|
||||||
|
The deploy script syncs socket code and scripts, but uses `--ignore-existing` for Toolkit JSON and uploaded assets so live content edits are not overwritten by routine deploys.
|
||||||
|
|||||||
+49
-21
@@ -3,6 +3,7 @@ import 'dotenv/config';
|
|||||||
import { watch } from 'node:fs';
|
import { watch } from 'node:fs';
|
||||||
import { mkdir, writeFile } from 'node:fs/promises';
|
import { mkdir, writeFile } from 'node:fs/promises';
|
||||||
import { createServer } from 'node:http';
|
import { createServer } from 'node:http';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
@@ -11,6 +12,8 @@ import { Server } from 'socket.io';
|
|||||||
import contentStore from './contentStore.cjs';
|
import contentStore from './contentStore.cjs';
|
||||||
|
|
||||||
const repositoryRoot = fileURLToPath(new URL('..', import.meta.url));
|
const repositoryRoot = fileURLToPath(new URL('..', import.meta.url));
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const packageJson = require('../package.json');
|
||||||
const defaultWebOrigin = 'http://127.0.0.1:5173';
|
const defaultWebOrigin = 'http://127.0.0.1:5173';
|
||||||
const host = process.env.TOOLKIT_SOCKET_HOST ?? '127.0.0.1';
|
const host = process.env.TOOLKIT_SOCKET_HOST ?? '127.0.0.1';
|
||||||
const port = Number.parseInt(process.env.TOOLKIT_SOCKET_PORT ?? '5174', 10);
|
const port = Number.parseInt(process.env.TOOLKIT_SOCKET_PORT ?? '5174', 10);
|
||||||
@@ -25,29 +28,46 @@ if (Number.isNaN(port)) {
|
|||||||
throw new Error('TOOLKIT_SOCKET_PORT must be a valid number.');
|
throw new Error('TOOLKIT_SOCKET_PORT must be a valid number.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const socketHealthStartedAt = Date.now();
|
||||||
|
const socketHealthStartedAtIso = new Date(socketHealthStartedAt).toISOString();
|
||||||
|
|
||||||
|
function createSocketHealthPayload() {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
service: 'blb-toolkit-socket',
|
||||||
|
version: packageJson.version,
|
||||||
|
startedAt: socketHealthStartedAtIso,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
uptimeSeconds: Math.max(
|
||||||
|
0,
|
||||||
|
Math.round((Date.now() - socketHealthStartedAt) / 1000),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeJsonResponse(response, statusCode, body) {
|
||||||
|
response.writeHead(statusCode, {
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
'Access-Control-Allow-Methods': 'GET, HEAD, OPTIONS',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Cache-Control': 'no-store',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
});
|
||||||
|
response.end(`${JSON.stringify(body)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
const httpServer = createServer((request, response) => {
|
const httpServer = createServer((request, response) => {
|
||||||
if (request.url === '/health') {
|
const pathname = request.url?.split('?')[0];
|
||||||
response.writeHead(200, {
|
|
||||||
'Content-Type': 'application/json',
|
if (pathname === '/health') {
|
||||||
});
|
writeJsonResponse(response, 200, createSocketHealthPayload());
|
||||||
response.end(
|
|
||||||
JSON.stringify({
|
|
||||||
ok: true,
|
|
||||||
service: 'blb-toolkit-socket',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.writeHead(404, {
|
writeJsonResponse(response, 404, {
|
||||||
'Content-Type': 'application/json',
|
ok: false,
|
||||||
|
service: 'blb-toolkit-socket',
|
||||||
});
|
});
|
||||||
response.end(
|
|
||||||
JSON.stringify({
|
|
||||||
ok: false,
|
|
||||||
service: 'blb-toolkit-socket',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const io = new Server(httpServer, {
|
const io = new Server(httpServer, {
|
||||||
@@ -73,7 +93,9 @@ const assetUploadConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function getErrorMessage(error) {
|
function getErrorMessage(error) {
|
||||||
return error instanceof Error ? error.message : 'Unknown toolkit content error.';
|
return error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: 'Unknown toolkit content error.';
|
||||||
}
|
}
|
||||||
|
|
||||||
function createContentError(resource, error) {
|
function createContentError(resource, error) {
|
||||||
@@ -237,7 +259,10 @@ io.on('connection', socket => {
|
|||||||
const resource = payload?.resource;
|
const resource = payload?.resource;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const changedContent = await createContentRecord(resource, payload?.record);
|
const changedContent = await createContentRecord(
|
||||||
|
resource,
|
||||||
|
payload?.record,
|
||||||
|
);
|
||||||
|
|
||||||
respond?.({
|
respond?.({
|
||||||
ok: true,
|
ok: true,
|
||||||
@@ -259,7 +284,10 @@ io.on('connection', socket => {
|
|||||||
const resource = payload?.resource;
|
const resource = payload?.resource;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const changedContent = await updateContentRecord(resource, payload?.record);
|
const changedContent = await updateContentRecord(
|
||||||
|
resource,
|
||||||
|
payload?.record,
|
||||||
|
);
|
||||||
|
|
||||||
respond?.({
|
respond?.({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"id": "deployed-toolkit-socket-workspace",
|
||||||
|
"version": "0.0.5-20260624-local",
|
||||||
|
"createdAt": "2026-06-24",
|
||||||
|
"title": "Deployed Toolkit Socket Workspace",
|
||||||
|
"summary": "Prepared deployments to run a shared Toolkit socket against the development workspace data and assets.",
|
||||||
|
"changes": [
|
||||||
|
"Added deploy-aware Toolkit socket URL selection for built browser clients.",
|
||||||
|
"Added Woodpecker deploy steps to restart the Toolkit socket and validate frontend and socket health endpoints.",
|
||||||
|
"Added a shared workspace socket restart script that writes Toolkit JSON and uploaded media into the repo source folders."
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "toolkit-structured-media-authoring",
|
"id": "toolkit-structured-media-authoring",
|
||||||
"version": "0.0.2-20260624-local",
|
"version": "0.0.2-20260624-local",
|
||||||
|
|||||||
@@ -13,6 +13,17 @@ type NodeProcess = {
|
|||||||
env?: Partial<Record<string, string>>;
|
env?: Partial<Record<string, string>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BrowserLocation = {
|
||||||
|
hostname: string;
|
||||||
|
protocol: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BrowserGlobal = typeof globalThis & {
|
||||||
|
location?: BrowserLocation;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare const __TOOLKIT_SOCKET_URL__: string | undefined;
|
||||||
|
|
||||||
export type ToolkitContentResource = EditableToolkitResource;
|
export type ToolkitContentResource = EditableToolkitResource;
|
||||||
export type ToolkitAssetKind = 'audio' | 'image';
|
export type ToolkitAssetKind = 'audio' | 'image';
|
||||||
|
|
||||||
@@ -79,9 +90,36 @@ function getMaybeProcess() {
|
|||||||
return typeof process === 'undefined' ? undefined : (process as NodeProcess);
|
return typeof process === 'undefined' ? undefined : (process as NodeProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getConfiguredToolkitSocketUrl() {
|
||||||
|
if (
|
||||||
|
typeof __TOOLKIT_SOCKET_URL__ === 'string' &&
|
||||||
|
__TOOLKIT_SOCKET_URL__.length > 0
|
||||||
|
) {
|
||||||
|
return __TOOLKIT_SOCKET_URL__;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getMaybeProcess()?.env?.TOOLKIT_SOCKET_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBrowserToolkitSocketUrl() {
|
||||||
|
const location = (globalThis as BrowserGlobal).location;
|
||||||
|
|
||||||
|
if (location == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.hostname === '127.0.0.1' || location.hostname === 'localhost') {
|
||||||
|
return defaultToolkitSocketUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${location.protocol}//${location.hostname}:5174`;
|
||||||
|
}
|
||||||
|
|
||||||
export function getToolkitSocketUrl() {
|
export function getToolkitSocketUrl() {
|
||||||
return (
|
return (
|
||||||
getMaybeProcess()?.env?.TOOLKIT_SOCKET_URL ?? defaultToolkitSocketUrl
|
getConfiguredToolkitSocketUrl() ??
|
||||||
|
getBrowserToolkitSocketUrl() ??
|
||||||
|
defaultToolkitSocketUrl
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +141,9 @@ export function getToolkitSocket() {
|
|||||||
return toolkitSocket;
|
return toolkitSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isToolkitContentPayload(value: unknown): value is ToolkitContentPayload {
|
function isToolkitContentPayload(
|
||||||
|
value: unknown,
|
||||||
|
): value is ToolkitContentPayload {
|
||||||
if (value == null || typeof value !== 'object') {
|
if (value == null || typeof value !== 'object') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -117,9 +157,7 @@ function isToolkitContentPayload(value: unknown): value is ToolkitContentPayload
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseToolkitContentResponse(
|
function parseToolkitContentResponse(response: unknown): ToolkitContentPayload {
|
||||||
response: unknown,
|
|
||||||
): ToolkitContentPayload {
|
|
||||||
if (response == null || typeof response !== 'object') {
|
if (response == null || typeof response !== 'object') {
|
||||||
throw new Error('Toolkit content response was empty.');
|
throw new Error('Toolkit content response was empty.');
|
||||||
}
|
}
|
||||||
@@ -172,7 +210,10 @@ function parseToolkitAssetUploadResponse(
|
|||||||
throw new Error(uploadResponse.error ?? 'Toolkit asset upload failed.');
|
throw new Error(uploadResponse.error ?? 'Toolkit asset upload failed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uploadResponse.ok === true && isToolkitAssetUploadPayload(uploadResponse)) {
|
if (
|
||||||
|
uploadResponse.ok === true &&
|
||||||
|
isToolkitAssetUploadPayload(uploadResponse)
|
||||||
|
) {
|
||||||
return uploadResponse;
|
return uploadResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+87
-22
@@ -1,7 +1,8 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
import type { ServerResponse } from 'node:http';
|
||||||
|
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig, loadEnv, type PluginOption } from 'vite';
|
||||||
|
|
||||||
import packageJson from './package.json';
|
import packageJson from './package.json';
|
||||||
|
|
||||||
@@ -17,25 +18,89 @@ const platformExtensions = [
|
|||||||
'.json',
|
'.json',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default defineConfig({
|
const frontendHealthStartedAt = Date.now();
|
||||||
define: {
|
const frontendHealthStartedAtIso = new Date(
|
||||||
'import.meta.env.VITE_APP_VERSION': JSON.stringify(packageJson.version),
|
frontendHealthStartedAt,
|
||||||
},
|
).toISOString();
|
||||||
plugins: [react()],
|
|
||||||
resolve: {
|
function createFrontendHealthPayload() {
|
||||||
alias: [{ find: /^react-native$/, replacement: 'react-native-web' }],
|
return {
|
||||||
dedupe: ['react', 'react-dom', 'react-native-web'],
|
ok: true,
|
||||||
extensions: platformExtensions,
|
service: 'blb-frontend',
|
||||||
},
|
version: packageJson.version,
|
||||||
optimizeDeps: {
|
startedAt: frontendHealthStartedAtIso,
|
||||||
exclude: ['react-native-safe-area-context'],
|
timestamp: new Date().toISOString(),
|
||||||
},
|
uptimeSeconds: Math.max(
|
||||||
build: {
|
0,
|
||||||
outDir: path.resolve(__dirname, 'dist/renderer'),
|
Math.round((Date.now() - frontendHealthStartedAt) / 1000),
|
||||||
emptyOutDir: true,
|
),
|
||||||
},
|
};
|
||||||
server: {
|
}
|
||||||
host: '127.0.0.1',
|
|
||||||
port: 5173,
|
function writeJsonResponse(
|
||||||
},
|
response: ServerResponse,
|
||||||
|
statusCode: number,
|
||||||
|
body: object,
|
||||||
|
) {
|
||||||
|
response.writeHead(statusCode, {
|
||||||
|
'Cache-Control': 'no-store',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
});
|
||||||
|
response.end(`${JSON.stringify(body)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function frontendHealthPlugin(): PluginOption {
|
||||||
|
return {
|
||||||
|
name: 'blb-frontend-health',
|
||||||
|
configurePreviewServer(server) {
|
||||||
|
server.middlewares.use((request, response, next) => {
|
||||||
|
if (request.url?.split('?')[0] !== '/health') {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeJsonResponse(response, 200, createFrontendHealthPayload());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use((request, response, next) => {
|
||||||
|
if (request.url?.split('?')[0] !== '/health') {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeJsonResponse(response, 200, createFrontendHealthPayload());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig(({ mode }) => {
|
||||||
|
const env = loadEnv(mode, __dirname, '');
|
||||||
|
const toolkitSocketUrl =
|
||||||
|
env.VITE_TOOLKIT_SOCKET_URL ?? env.TOOLKIT_SOCKET_URL ?? '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
define: {
|
||||||
|
__TOOLKIT_SOCKET_URL__: JSON.stringify(toolkitSocketUrl),
|
||||||
|
'import.meta.env.VITE_APP_VERSION': JSON.stringify(packageJson.version),
|
||||||
|
},
|
||||||
|
plugins: [frontendHealthPlugin(), react()],
|
||||||
|
resolve: {
|
||||||
|
alias: [{ find: /^react-native$/, replacement: 'react-native-web' }],
|
||||||
|
dedupe: ['react', 'react-dom', 'react-native-web'],
|
||||||
|
extensions: platformExtensions,
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
exclude: ['react-native-safe-area-context'],
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
outDir: path.resolve(__dirname, 'dist/renderer'),
|
||||||
|
emptyOutDir: true,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 5173,
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user