diff --git a/backend/api/pages.js b/backend/api/pages.js new file mode 100644 index 0000000..d4faac1 --- /dev/null +++ b/backend/api/pages.js @@ -0,0 +1,156 @@ +import express from 'express'; +// BOSA SDK instance (initialized in server.js) +let bosa = null; +export const initPagesAPI = (bosaInstance) => { + bosa = bosaInstance; +}; +export const createPagesRouter = () => { + const router = express.Router(); + router.post('/', async (req, res) => { + try { + const { app_name, route_path, page_config } = req.body; + if (!app_name || !route_path || !page_config) { + bosa?.log?.warn('CreatePage: Missing required fields'); + return res.status(400).json({ error: 'Missing required fields' }); + } + if (!bosa) { + return res.status(500).json({ error: 'BOSA SDK not initialized' }); + } + // Check if page already exists + const existing = await bosa.db + .query('wb_pages') + .where('app_name', '=', app_name) + .where('route_path', '=', route_path) + .first(); + if (existing) { + bosa.log?.warn(`CreatePage: Page already exists | App: ${app_name} | Route: ${route_path}`); + return res.status(409).json({ error: 'Page already exists' }); + } + // Insert using BOSA SDK + const id = await bosa.db.query('wb_pages').insert({ + app_name, + route_path, + page_config: JSON.stringify(page_config), + version: 1, + }); + bosa.log?.info(`CreatePage: Page created | ID: ${id} | App: ${app_name}`); + res.status(201).json({ + id, + app_name, + route_path, + page_config, + version: 1, + }); + } + catch (error) { + bosa?.log?.error(`CreatePage: Failed | Error: ${error.message} | App: ${req.body.app_name}`); + res.status(500).json({ error: error.message }); + } + }); + router.get('/:id', async (req, res) => { + try { + if (!bosa) { + return res.status(500).json({ error: 'BOSA SDK not initialized' }); + } + const page = await bosa.db + .query('wb_pages') + .where('id', '=', Number(req.params.id)) + .first(); + if (!page) { + bosa.log?.warn(`GetPage: Page not found | ID: ${req.params.id}`); + return res.status(404).json({ error: 'Page not found' }); + } + const result = page; + res.json({ + ...result, + page_config: JSON.parse(result.page_config), + }); + } + catch (error) { + bosa?.log?.error(`GetPage: Failed | ID: ${req.params.id} | Error: ${error.message}`); + res.status(500).json({ error: error.message }); + } + }); + router.put('/:id', async (req, res) => { + try { + if (!bosa) { + return res.status(500).json({ error: 'BOSA SDK not initialized' }); + } + const { page_config } = req.body; + if (!page_config) { + bosa.log?.warn(`UpdatePage: Missing page_config | ID: ${req.params.id}`); + return res.status(400).json({ error: 'Missing page_config' }); + } + const existing = await bosa.db + .query('wb_pages') + .where('id', '=', Number(req.params.id)) + .first(); + if (!existing) { + bosa.log?.warn(`UpdatePage: Page not found | ID: ${req.params.id}`); + return res.status(404).json({ error: 'Page not found' }); + } + const newVersion = (existing.version || 1) + 1; + await bosa.db + .query('wb_pages') + .where('id', '=', Number(req.params.id)) + .update({ + page_config: JSON.stringify(page_config), + version: newVersion, + }); + bosa.log?.info(`UpdatePage: Page updated | ID: ${req.params.id} | Version: ${newVersion}`); + res.json({ + id: Number(req.params.id), + page_config, + version: newVersion, + }); + } + catch (error) { + bosa?.log?.error(`UpdatePage: Failed | ID: ${req.params.id} | Error: ${error.message}`); + res.status(500).json({ error: error.message }); + } + }); + router.delete('/:id', async (req, res) => { + try { + if (!bosa) { + return res.status(500).json({ error: 'BOSA SDK not initialized' }); + } + const deleted = await bosa.db + .query('wb_pages') + .where('id', '=', Number(req.params.id)) + .delete(); + if (deleted === 0) { + bosa.log?.warn(`DeletePage: Page not found | ID: ${req.params.id}`); + return res.status(404).json({ error: 'Page not found' }); + } + bosa.log?.info(`DeletePage: Page deleted | ID: ${req.params.id}`); + res.status(204).send(); + } + catch (error) { + bosa?.log?.error(`DeletePage: Failed | ID: ${req.params.id} | Error: ${error.message}`); + res.status(500).json({ error: error.message }); + } + }); + router.get('/', async (req, res) => { + try { + if (!bosa) { + return res.status(500).json({ error: 'BOSA SDK not initialized' }); + } + const appName = req.query.app_name; + let query = bosa.db.query('wb_pages'); + if (appName) { + query = query.where('app_name', '=', appName); + } + const pages = await query.get(); + const results = pages.map((page) => ({ + ...page, + page_config: JSON.parse(page.page_config), + })); + res.json(results); + } + catch (error) { + bosa?.log?.error(`ListPages: Failed | App: ${req.query.app_name} | Error: ${error.message}`); + res.status(500).json({ error: error.message }); + } + }); + return router; +}; diff --git a/package.json b/package.json index 15f9798..5be1dc1 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "dev:frontend": "vite", "dev:all": "concurrently \"npm run dev\" \"npm run dev:frontend\"", "start": "node server.js", - "build": "tsc && vite build", + "build": "npm run build:backend && npm run build:frontend", + "build:backend": "tsc -p tsconfig.backend.json", "build:frontend": "vite build", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix", diff --git a/scripts/deploy_wb.bat b/scripts/deploy_wb.bat index 159a5c1..d80ecaa 100644 --- a/scripts/deploy_wb.bat +++ b/scripts/deploy_wb.bat @@ -48,9 +48,19 @@ echo [INFO] Source: %SOURCE_DIR% echo [INFO] Target: %TARGET_DIR% echo. -REM Build the frontend first -echo [INFO] Building frontend... +REM Build backend and frontend +echo [INFO] Building backend (TypeScript)... cd /d "%SOURCE_DIR%" +call npm run build:backend +if errorlevel 1 ( + echo [ERROR] Backend build failed! + pause + exit /b 1 +) +echo [OK] Backend build completed +echo. + +echo [INFO] Building frontend... call npm run build:frontend if errorlevel 1 ( echo [ERROR] Frontend build failed! @@ -82,10 +92,14 @@ if exist "%SOURCE_DIR%\package.json" ( echo [OK] Copied package.json ) -REM Copy backend directory +REM Copy backend directory (compiled JS files only, exclude .ts files) if exist "%SOURCE_DIR%\backend" ( xcopy /E /I /Y "%SOURCE_DIR%\backend" "%TARGET_DIR%\backend" >nul - echo [OK] Copied backend directory + REM Remove TypeScript source files from target + for /r "%TARGET_DIR%\backend" %%f in (*.ts) do ( + if not "%%~nxf"=="*.test.ts" del "%%f" + ) + echo [OK] Copied backend directory (JS files) ) REM Copy migrations directory diff --git a/tsconfig.backend.json b/tsconfig.backend.json new file mode 100644 index 0000000..9f4c3d8 --- /dev/null +++ b/tsconfig.backend.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./backend", + "rootDir": "./backend", + "module": "ESNext", + "moduleResolution": "node", + "allowImportingTsExtensions": false, + "jsx": "preserve", + "lib": ["ES2020"], + "noUnusedLocals": false, + "noUnusedParameters": false + }, + "include": ["backend/**/*"], + "exclude": ["node_modules", "dist", "backend/**/*.test.ts"] +} +