-
Notifications
You must be signed in to change notification settings - Fork 1
Development Guide
npm run devThis starts the Vite dev server with integrated backend API:
- Frontend + Backend: http://localhost:5173
- Backend routes (
/auth/*,/dqm/*) are handled by Vite plugin
# Vite dev server (same as npm run dev)
npm run dev:client
# Standalone Express server with Redis (port 3001)
npm run dev:servercrownpeak-dqm-react-component/
├── src/ # React component source
│ ├── components/
│ │ ├── auth/ # Authentication components
│ │ ├── sidebar/ # Sidebar UI components
│ │ ├── cards/ # Analysis result cards
│ │ ├── renderers/ # HTML rendering
│ │ └── common/ # Shared components
│ ├── utils/ # Utilities
│ ├── types.ts # TypeScript types
│ ├── DQMSidebar.tsx # Main component
│ ├── index.ts # Library entry point
│ ├── App.tsx # Dev test harness
│ └── main.tsx # Dev app entry
│ ├── html-pages/ # Widget entrypoint, loaders, declarations
│
├── server/ # Backend API
│ ├── routes/ # API endpoints
│ │ ├── auth.ts # Authentication
│ │ └── dqm.ts # DQM proxy
│ ├── services/ # Business logic
│ │ ├── sessionStore.ts # Session management
│ │ └── dqmClient.ts # DQM API client
│ ├── middleware/ # Express middleware
│ ├── config.ts # Configuration
│ ├── types.ts # TypeScript types
│ └── index.ts # Express app
│
├── dist/ # Build output
│ ├── index.js # Library ESM
│ ├── index.cjs # Library CommonJS
│ ├── index.d.ts # Library type declarations
│ ├── dqm-widget.esm.js # Widget bundle (ESM)
│ ├── dqm-widget.iife.js # Widget bundle (IIFE)
│ └── dqm-widget.d.ts # Widget type declarations
│ └── server/ # Compiled server code
│
├── test/ # Demo pages for widget bundles
├── public/ # Landing page for demos
├── vite.config.ts # Vite configuration
├── tsconfig.json # TypeScript base config
├── tsconfig.lib.json # Library build config
├── tsconfig.server.json # Server build config
└── package.json # Dependencies & scripts
The component supports two modes:
<DQMSidebar
config={{
authBackendUrl: '', // Dev: empty (same origin) | Prod: 'https://your-backend.com'
useLocalStorage: true,
}}
/>- All API calls proxied through backend
- Session token stored in localStorage
- Real credentials handled server-side only
<DQMSidebar
config={{
apiKey: 'YOUR_API_KEY',
websiteId: 'YOUR_WEBSITE_ID',
}}
/>- Direct communication with Crownpeak DQM API
- Credentials in props or localStorage
- No backend required
- Start dev server:
npm run dev - Open http://localhost:5173
- Click "Login" and enter credentials
- Backend validates and issues session token
- All API calls go through http://localhost:5173 (same origin)
- Update
src/App.tsx:
<DQMSidebar
config={{
apiKey: 'YOUR_API_KEY',
websiteId: 'YOUR_WEBSITE_ID',
}}
/>- Start frontend only:
npm run dev:client - No backend needed, direct API calls
npm run build:libOutput: dist/index.{js,cjs,d.ts}
npm run build:widgetOutput: dist/dqm-widget.{esm.js,iife.js,d.ts} (fully bundled, Shadow DOM-safe)
npm run build:serverOutput: dist/server/
npm run buildRuns library + widget + backend + auth UI builds.
npm pack
# Creates: crownpeak-dqm-react-component-1.0.0.tgznpm run build:widget
npm run serve:widgetServes dist/ plus demo pages via serve.json rewrites:
-
/test/demo-iife.htmlfor IIFE bundle -
/test/demo-esm.htmlfor ESM bundle -
/test/demo-dynamic.htmlfor lazy loading -
/test/widget-standalone.htmlfor full standalone demo
# Not needed in dev mode - backend is integrated via Vite plugin
# VITE_BACKEND_URL=http://localhost:3001# Only for npm run dev:server (standalone Express with Redis)
PORT=3001
CORS_ORIGINS=http://localhost:5173,http://localhost:3000
DQM_API_BASE_URL=https://api.crownpeak.net/dqm-cms/v1
JWT_SECRET=your-secret-keycurl -X POST http://localhost:5173/auth/login \
-H "Content-Type: application/json" \
-d '{
"apiKey": "YOUR_API_KEY",
"websiteId": "YOUR_WEBSITE_ID"
}'Response:
{
"sessionToken": "abc123...",
"websiteId": "YOUR_WEBSITE_ID"
}curl -X POST http://localhost:5173/dqm/assets \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"html": "<html><body><h1>Test</h1></body></html>",
"url": "https://example.com"
}'curl -X GET http://localhost:5173/dqm/assets/ASSET_ID \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"Backend logs all requests automatically. Check terminal output:
[2025-10-31T10:00:00.000Z] POST /auth/login
[Auth] User logged in successfully (websiteId: test_id)
[SessionStore] Created session: abc12345... (expires in 1440 minutes)
curl -X GET http://localhost:5173/auth/session \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"Add to server/routes/auth.ts:
authRouter.get('/sessions/count', (req, res) => {
res.json({ count: sessionStore.count() });
});Problem: Frontend can't reach backend
Solution: Add frontend URL to CORS_ORIGINS in .env:
CORS_ORIGINS=http://localhost:5173,http://localhost:3000Problem: Error: listen EADDRINUSE: address already in use :::5173
Solution: Kill process or change port:
lsof -ti:5173 | xargs kill -9
# For standalone server (port 3001):
# lsof -ti:3001 | xargs kill -9Problem: 401 Unauthorized after some time
Solution: Session TTL is 24 hours. Login again or extend TTL in server/config.ts:
session: {
ttl: 48 * 60 * 60 * 1000, // 48 hours
}Problem: Type errors in server code
Solution: Ensure @types packages installed:
npm install --save-dev @types/express @types/cors @types/node- Build server:
npm run build:server - Copy
dist/server/to production - Set environment variables
- Install production dependencies only
- Start:
node dist/server/index.js
Replace server/services/sessionStore.ts with Redis:
import { createClient } from 'redis';
const redis = createClient({
url: process.env.REDIS_URL
});
await redis.connect();
export const sessionStore = {
create: async (apiKey, websiteId) => {
const token = generateToken();
await redis.setEx(`session:${token}`, 86400, JSON.stringify({
apiKey, websiteId, createdAt: Date.now()
}));
return token;
},
get: async (token) => {
const data = await redis.get(`session:${token}`);
return data ? JSON.parse(data) : null;
},
delete: async (token) => {
await redis.del(`session:${token}`);
}
};npm install express-rate-limitimport rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/auth/login', limiter);MIT