diff --git a/docker-compose-demo.yml b/docker-compose-demo.yml new file mode 100644 index 00000000..b1e5310b --- /dev/null +++ b/docker-compose-demo.yml @@ -0,0 +1,41 @@ +# ============================================================================= +# Veritas Kanban — Docker Compose (DEMO for Product Hunt) +# ============================================================================= +# Usage: +# docker compose -f docker-compose-demo.yml up --build -d +# docker compose -f docker-compose-demo.yml down +# docker compose -f docker-compose-demo.yml logs -f +# ============================================================================= + +services: + veritas-kanban-demo: + build: + context: . + dockerfile: Dockerfile + container_name: veritas-kanban-demo + # IMPORTANT: must match Dockerfile WORKDIR for relative CMD (node dist/index.js) + working_dir: /app/server + ports: + - '3099:3001' + environment: + - NODE_ENV=production + - PORT=3001 + - DATA_DIR=/app/data + - VERITAS_ADMIN_KEY=demo-admin-key-for-product-hunt-2026 + - VERITAS_AUTH_LOCALHOST_BYPASS=true + - VERITAS_AUTH_LOCALHOST_ROLE=admin + - CORS_ORIGINS=http://localhost:3099 + volumes: + # Separate data volume for demo instance + - kanban-demo-data:/app/data + restart: unless-stopped + healthcheck: + test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3001/health'] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + +volumes: + kanban-demo-data: + driver: local diff --git a/docker-compose.yml b/docker-compose.yml index 39abcc07..36dc36a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,24 +13,23 @@ services: build: context: . dockerfile: Dockerfile - container_name: veritas-kanban + container_name: veritas-kanban-demo # IMPORTANT: Must match Dockerfile WORKDIR (/app/server) for correct path resolution - # Services use process.cwd()/.. to locate project root; mismatched WORKDIR causes EACCES errors - working_dir: /app + working_dir: /app/server ports: - - '3001:3001' + # Demo instance port (do NOT use production 3001) + - '3099:3001' environment: - NODE_ENV=production - PORT=3001 - DATA_DIR=/app/data - # Reverse proxy support: set when running behind nginx, Caddy, Traefik, etc. - # - TRUST_PROXY=1 - # Auth: set API keys for production use - # - API_KEYS=key1:admin,key2:viewer - # - JWT_SECRET=your-secret-here + # Demo auth key (Arcade walkthrough) + - VERITAS_ADMIN_KEY=demo-admin-key-for-product-hunt-2026 + # Host -> container requests come from bridge IP (not 127.0.0.1), so keep API key enabled. + - VERITAS_AUTH_LOCALHOST_BYPASS=false volumes: - # Persist task data across container restarts - - kanban-data:/app/data + # Persist demo data across container restarts + - kanban-demo-data:/app/data restart: unless-stopped healthcheck: test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3001/health'] @@ -40,5 +39,5 @@ services: start_period: 10s volumes: - kanban-data: + kanban-demo-data: driver: local diff --git a/seed-demo-data.sh b/seed-demo-data.sh new file mode 100755 index 00000000..fcd5aa88 --- /dev/null +++ b/seed-demo-data.sh @@ -0,0 +1,267 @@ +#!/bin/bash +# ============================================================================= +# Seed Demo Data for Product Hunt Demo +# ============================================================================= +# Creates realistic, impressive demo data for Veritas Kanban v3.0 +# Port: 3099 (demo instance) +# ============================================================================= + +set -euo pipefail + +API_BASE="http://localhost:3099/api" + +echo "🌱 Seeding Veritas Kanban demo data..." + +# ----------------------------------------------------------------------------- +# Helper function to create tasks +# ----------------------------------------------------------------------------- +create_task() { + local title="$1" + local description="$2" + local project="$3" + local status="$4" + local priority="$5" + local type="$6" + + local payload + payload=$(cat <&2 + echo "$resp" | head -c 400 >&2 + exit 1 + fi + sleep 5 + continue + fi + + echo " ✗ Failed: $title" >&2 + echo "$resp" | head -c 800 >&2 + exit 1 + done +} + +# ----------------------------------------------------------------------------- +# BRAINMELD TASKS +# ----------------------------------------------------------------------------- +echo "" +echo "📦 Creating BrainMeld tasks..." + +create_task \ + "Implement AI-powered document summarization" \ + "Add GPT-4 integration for automatic document summarization. Extract key insights and create markdown summaries." \ + "brainmeld" \ + "done" \ + "high" \ + "feature" + +create_task \ + "Design knowledge graph visualization" \ + "Create interactive D3.js visualization showing connections between documents, tags, and concepts." \ + "brainmeld" \ + "done" \ + "medium" \ + "design" + +create_task \ + "Add semantic search with vector embeddings" \ + "Integrate Pinecone for vector search. Generate embeddings for all documents and enable natural language queries." \ + "brainmeld" \ + "in-progress" \ + "high" \ + "feature" + +create_task \ + "Build collaborative annotation system" \ + "Allow multiple users to highlight and comment on shared documents in real-time." \ + "brainmeld" \ + "in-progress" \ + "medium" \ + "feature" + +create_task \ + "Research RAG architecture patterns" \ + "Evaluate different retrieval-augmented generation approaches for knowledge Q&A system." \ + "brainmeld" \ + "todo" \ + "high" \ + "research" + +create_task \ + "Optimize document parsing for large PDFs" \ + "Current parser struggles with 100+ page PDFs. Profile and optimize performance." \ + "brainmeld" \ + "todo" \ + "medium" \ + "bug" + +# ----------------------------------------------------------------------------- +# DEALMELD TASKS +# ----------------------------------------------------------------------------- +echo "" +echo "🤝 Creating DealMeld tasks..." + +create_task \ + "Build digital sales room template system" \ + "Create customizable templates for different deal types (SaaS, consulting, enterprise)." \ + "dealmeld" \ + "done" \ + "high" \ + "feature" + +create_task \ + "Add document engagement analytics" \ + "Track which pages prospects view, time spent, and engagement patterns." \ + "dealmeld" \ + "done" \ + "high" \ + "feature" + +create_task \ + "Implement e-signature integration" \ + "Integrate DocuSign and HelloSign for in-app contract signing." \ + "dealmeld" \ + "in-progress" \ + "high" \ + "feature" + +create_task \ + "Design stakeholder collaboration features" \ + "Enable multiple decision-makers on buyer side to collaborate within the deal room." \ + "dealmeld" \ + "todo" \ + "medium" \ + "design" + +create_task \ + "Research mutual action plan best practices" \ + "Study how top sales teams structure MAPs. Interview 10+ sales leaders." \ + "dealmeld" \ + "todo" \ + "low" \ + "research" + +# ----------------------------------------------------------------------------- +# MESSAGEMELD TASKS +# ----------------------------------------------------------------------------- +echo "" +echo "💬 Creating MessageMeld tasks..." + +create_task \ + "Add Discord and Telegram support" \ + "Extend cross-platform messaging to Discord and Telegram in addition to existing platforms." \ + "messagemeld" \ + "done" \ + "high" \ + "feature" + +create_task \ + "Fix message sync race condition" \ + "Occasional duplicate messages when multiple platforms receive the same message simultaneously." \ + "messagemeld" \ + "in-progress" \ + "high" \ + "bug" + +create_task \ + "Build unified notification system" \ + "Aggregate notifications from all platforms into single intelligent feed." \ + "messagemeld" \ + "todo" \ + "medium" \ + "feature" + +create_task \ + "Design thread unification UI" \ + "Show how conversations across platforms can be merged into coherent threads." \ + "messagemeld" \ + "todo" \ + "medium" \ + "design" + +# ----------------------------------------------------------------------------- +# INFRASTRUCTURE TASKS +# ----------------------------------------------------------------------------- +echo "" +echo "⚙️ Creating Infrastructure tasks..." + +create_task \ + "Migrate to Kubernetes for production deployment" \ + "Move from Docker Compose to k8s for better scaling and orchestration." \ + "infrastructure" \ + "done" \ + "high" \ + "feature" + +create_task \ + "Set up CI/CD pipeline with GitHub Actions" \ + "Automate testing, building, and deployment for all projects." \ + "infrastructure" \ + "done" \ + "high" \ + "feature" + +create_task \ + "Implement comprehensive monitoring and alerting" \ + "Deploy Prometheus, Grafana, and PagerDuty for full observability." \ + "infrastructure" \ + "in-progress" \ + "high" \ + "feature" + +create_task \ + "Audit security vulnerabilities across all services" \ + "Run Snyk, Trivy, and manual penetration testing. Remediate all high/critical findings." \ + "infrastructure" \ + "todo" \ + "high" \ + "research" + +create_task \ + "Optimize database query performance" \ + "Several slow queries identified in production. Add indexes and optimize N+1 queries." \ + "infrastructure" \ + "todo" \ + "medium" \ + "bug" + +echo "" +echo "✅ Task seeding complete!" +echo "" +echo "📋 Summary:" +echo " • BrainMeld: 6 tasks" +echo " • DealMeld: 5 tasks" +echo " • MessageMeld: 4 tasks" +echo " • Infrastructure: 5 tasks" +echo " • Total: 20 tasks" +echo "" diff --git a/server/src/routes/tool-policies.ts b/server/src/routes/tool-policies.ts index 7190dfcb..c091db62 100644 --- a/server/src/routes/tool-policies.ts +++ b/server/src/routes/tool-policies.ts @@ -3,6 +3,9 @@ * GitHub Issue: #110 * * CRUD operations for role-based tool access policies. + * + * NOTE: The responseEnvelopeMiddleware auto-wraps all res.json() calls in + * { success, data, meta }. Do NOT manually wrap responses here. */ import express from 'express'; @@ -51,15 +54,7 @@ router.get( '/', asyncHandler(async (req, res) => { const policies = await toolPolicyService.listPolicies(); - - res.json({ - success: true, - data: policies, - meta: { - timestamp: new Date().toISOString(), - count: policies.length, - }, - }); + res.json(policies); }) ); @@ -71,27 +66,14 @@ router.get( '/:role', asyncHandler(async (req, res) => { const { role } = RoleParamSchema.parse(req.params); - const policy = await toolPolicyService.getToolPolicy(role); if (!policy) { - res.status(404).json({ - success: false, - error: `Tool policy not found for role: ${role}`, - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.status(404).json({ error: `Tool policy not found for role: ${role}` }); return; } - res.json({ - success: true, - data: policy, - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.json(policy); }) ); @@ -103,17 +85,8 @@ router.post( '/', asyncHandler(async (req, res) => { const policy = ToolPolicySchema.parse(req.body); - await toolPolicyService.savePolicy(policy); - - res.status(201).json({ - success: true, - data: policy, - meta: { - timestamp: new Date().toISOString(), - message: `Tool policy created for role: ${policy.role}`, - }, - }); + res.status(201).json(policy); }) ); @@ -127,41 +100,19 @@ router.put( const { role } = RoleParamSchema.parse(req.params); const policyData = ToolPolicySchema.parse(req.body); - // Ensure the role in the URL matches the role in the body if (policyData.role.toLowerCase() !== role.toLowerCase()) { - res.status(400).json({ - success: false, - error: 'Role in URL does not match role in request body', - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.status(400).json({ error: 'Role in URL does not match role in request body' }); return; } - // Check if policy exists const existing = await toolPolicyService.getToolPolicy(role); if (!existing) { - res.status(404).json({ - success: false, - error: `Tool policy not found for role: ${role}`, - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.status(404).json({ error: `Tool policy not found for role: ${role}` }); return; } await toolPolicyService.savePolicy(policyData); - - res.json({ - success: true, - data: policyData, - meta: { - timestamp: new Date().toISOString(), - message: `Tool policy updated for role: ${role}`, - }, - }); + res.json(policyData); }) ); @@ -173,16 +124,8 @@ router.delete( '/:role', asyncHandler(async (req, res) => { const { role } = RoleParamSchema.parse(req.params); - await toolPolicyService.deletePolicy(role); - - res.json({ - success: true, - meta: { - timestamp: new Date().toISOString(), - message: `Tool policy deleted for role: ${role}`, - }, - }); + res.json({ deleted: role }); }) ); @@ -195,20 +138,8 @@ router.post( asyncHandler(async (req, res) => { const { role } = RoleParamSchema.parse(req.params); const { tool } = z.object({ tool: z.string().min(1) }).parse(req.body); - const allowed = await toolPolicyService.validateToolAccess(role, tool); - - res.json({ - success: true, - data: { - role, - tool, - allowed, - }, - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.json({ role, tool, allowed }); }) ); @@ -218,39 +149,17 @@ router.use( (err: Error, req: express.Request, res: express.Response, _next: express.NextFunction) => { log.error({ err, path: req.path, method: req.method }, 'Tool policy route error'); - // Zod validation errors if (err instanceof z.ZodError) { - res.status(400).json({ - success: false, - error: 'Validation failed', - details: err.errors, - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.status(400).json({ error: 'Validation failed', details: err.errors }); return; } - // Validation errors from service if (err.name === 'ValidationError') { - res.status(400).json({ - success: false, - error: err.message, - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.status(400).json({ error: err.message }); return; } - // Generic errors - res.status(500).json({ - success: false, - error: 'Internal server error', - meta: { - timestamp: new Date().toISOString(), - }, - }); + res.status(500).json({ error: 'Internal server error' }); } );