mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-24 23:56:40 +02:00
feat(crm): add standalone dev environment and test token generator
- docker-compose.crm-dev.yml: isolierte Testumgebung (PostgreSQL + Redis + CRM) - scripts/generate-token.ts: generiert Test-JWTs fuer curl-basiertes API-Testing - scripts/init-schema.sql: erstellt app_crm Schema beim DB-Start - keys/: Test-RSA-Schluessel (nur fuer lokale Entwicklung!) - Fix: multiSchema previewFeature entfernt (deprecated) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8783d01fc0
commit
094db465cb
6 changed files with 203 additions and 3 deletions
70
packages/crm-service/docker-compose.crm-dev.yml
Normal file
70
packages/crm-service/docker-compose.crm-dev.yml
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# ============================================================
|
||||
# CRM-Service - Standalone Entwicklungsumgebung
|
||||
# ============================================================
|
||||
# Startet PostgreSQL + Redis + CRM isoliert (ohne Core/Frontend)
|
||||
#
|
||||
# Starten: docker compose -f docker-compose.crm-dev.yml up -d
|
||||
# Stoppen: docker compose -f docker-compose.crm-dev.yml down
|
||||
# Logs: docker compose -f docker-compose.crm-dev.yml logs -f crm
|
||||
# Reset DB: docker compose -f docker-compose.crm-dev.yml down -v
|
||||
|
||||
services:
|
||||
crm-postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: crm-dev-postgres
|
||||
environment:
|
||||
POSTGRES_USER: insight
|
||||
POSTGRES_PASSWORD: devpassword
|
||||
POSTGRES_DB: platform_core
|
||||
ports:
|
||||
- "15432:5432"
|
||||
volumes:
|
||||
- crm-pg-data:/var/lib/postgresql/data
|
||||
- ./scripts/init-schema.sql:/docker-entrypoint-initdb.d/01-init-schema.sql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U insight -d platform_core"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
crm-redis:
|
||||
image: redis:7-alpine
|
||||
container_name: crm-dev-redis
|
||||
ports:
|
||||
- "16379:6379"
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
crm:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: development
|
||||
container_name: crm-dev-service
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- APP_PORT=3100
|
||||
- DATABASE_URL=postgresql://insight:devpassword@crm-postgres:5432/platform_core?schema=app_crm
|
||||
- DATABASE_URL_DIRECT=postgresql://insight:devpassword@crm-postgres:5432/platform_core?schema=app_crm
|
||||
- REDIS_HOST=crm-redis
|
||||
- REDIS_PORT=6379
|
||||
- JWT_PUBLIC_KEY_PATH=/app/keys/jwt-public.pem
|
||||
- JWT_ISSUER=insight-platform
|
||||
- CORS_ORIGINS=http://localhost:3100,http://127.0.0.1:3100
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
- ./keys/jwt-public.pem:/app/keys/jwt-public.pem:ro
|
||||
ports:
|
||||
- "3100:3100"
|
||||
depends_on:
|
||||
crm-postgres:
|
||||
condition: service_healthy
|
||||
crm-redis:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
crm-pg-data:
|
||||
27
packages/crm-service/keys/jwt-private.pem
Normal file
27
packages/crm-service/keys/jwt-private.pem
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAwyZKQNL8oGoKlRyZzNtHXiLgDE8Zw+7Ll8XUsgI93CZBPEbL
|
||||
UCh6C2AZOaar5YdfgD6acX5PQxHnn28x8cAhLDhXcutX+riyJ0Mq+yNnuJrAt645
|
||||
Yp1pbDRseePm+Z6OnCbKnULvfqautWBRfBDFmAPfF91/tnQPb6CtC7iUVjNpBuK0
|
||||
eX5UnbSxI5IgDk8tLyG/j8IufVQnNRN7t9oSpvS0wKQpKjMX4hGcESd/zyO0gnam
|
||||
47mT8v2x7noS6NGZncB2RSmajMvvYx/mZxLk7kf1MRiLUlM+BOUw/hh7Sod0gOLa
|
||||
S9iLhe+sgu54Tc9cgU2+QY/9EHMqUiT7jxeuewIDAQABAoIBAA+iB5y9yiMHm6Vq
|
||||
Tx/MCSGPZadWxtedT43u+STxbQcvwVYUzcC0HWK/5gVqRqbye0IIwdKJrcvPqWBq
|
||||
RfP50i56rPa1x6x8Ezl19gF8SpjNPNb6C/rMZV+Xq4DwMtaYTxBPQzfp3smkjKZW
|
||||
+vAXX9zXoo2E3vA8x+fHVVV1Cd0FyUvcVdeLT16eEqsOfS347KfUDCQ5Nv63xpj9
|
||||
dGXVOOmUwWY8pCmW+mz15q7Y1stegCV/XMv7vuxyhFpD1W29NyLvJ8pzKhQxN+xS
|
||||
/ivE0L+pq+PeQ9/4MERt6q5xp5TQy0lj4kxjnvv+5FkZj2s4KVUr5F0b6vkOhvip
|
||||
4P9rV6ECgYEA9zxVtI7SBNsRcIo7gRwdy+L0kz6qYWRUpZ+hc8OnZjvtw8BcfzwO
|
||||
XnlGFUTSfMaPwzJc7Ny/uKHpCovnszUzzgJ0FMv+mLKLU8Noaay2lFRc9zrGef2S
|
||||
+2bdjTZYjRLt1S7nczowuGesvMJqic30ic0t7xgUZ4AhWCVSgue9pcMCgYEAyhFG
|
||||
BE+mzkCRcLh9U4mkbXOKsy3SHBhgFpAUPjY9dlnIFpjOITDLS07Wd3NQ7u72qQw/
|
||||
dDtq17LsoUzpkzcMDVSUUWawfdYwza0IaGfnprECtk38pj0MtRSDJfT8AQQvzdSC
|
||||
ZAhMFdfa9ZqVQ/1jbU+2aJ2esFXUZ20Mg7VH8OkCgYEAr/pKJtLhuoMTzr8Vy7hv
|
||||
nQhWfdhE/j2j4p/VE8lYBfTyMDtjm0zsDWLU956dFCNhgNcAPbiC3rCgZ9ldermL
|
||||
vj8Q0RzCg33SnjSgxVciPkIuSeuUCpDrZfa6DCF6ti+bCfrw05u/wgJJebIIkz39
|
||||
qXhaEa1aGLGjClLGgFbjLnECgYBAdvpLbc3dhyPfPjxdZlAayv245Clf5Tbie9y5
|
||||
bDx3gXUgIGfClvqEcAZj3Vo4n+v5SnsD7eDMJ7zuSMdLvAgQSKd4wLYVrzuqokVI
|
||||
ab2xpE8lMgQkVN2093JPrbSn7loB5IYku7DqVw73w/VS14fc281p2r9BqmA1Dskr
|
||||
S65D4QKBgHOpjCDmRqzrl+EDOjRZinXHWZqm/lX5pBFBxtTGYstbg7gY4D6nLhsR
|
||||
mCbYQr7WOf+ouQYkg5ck82K/0Gu46rjkpBcIdbnW1wC9iE1iJxd5u96iEcJNu2oM
|
||||
H5gx73pkmO4rVnInv67VYeDrGHGd/aix6b5nSDaYg5Ct1yENlzCD
|
||||
-----END RSA PRIVATE KEY-----
|
||||
9
packages/crm-service/keys/jwt-public.pem
Normal file
9
packages/crm-service/keys/jwt-public.pem
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwyZKQNL8oGoKlRyZzNtH
|
||||
XiLgDE8Zw+7Ll8XUsgI93CZBPEbLUCh6C2AZOaar5YdfgD6acX5PQxHnn28x8cAh
|
||||
LDhXcutX+riyJ0Mq+yNnuJrAt645Yp1pbDRseePm+Z6OnCbKnULvfqautWBRfBDF
|
||||
mAPfF91/tnQPb6CtC7iUVjNpBuK0eX5UnbSxI5IgDk8tLyG/j8IufVQnNRN7t9oS
|
||||
pvS0wKQpKjMX4hGcESd/zyO0gnam47mT8v2x7noS6NGZncB2RSmajMvvYx/mZxLk
|
||||
7kf1MRiLUlM+BOUw/hh7Sod0gOLaS9iLhe+sgu54Tc9cgU2+QY/9EHMqUiT7jxeu
|
||||
ewIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
|
@ -6,9 +6,8 @@
|
|||
// ============================================================
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "../node_modules/.prisma/crm-client"
|
||||
previewFeatures = ["multiSchema"]
|
||||
provider = "prisma-client-js"
|
||||
output = "../node_modules/.prisma/crm-client"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
|
|
|
|||
93
packages/crm-service/scripts/generate-token.ts
Normal file
93
packages/crm-service/scripts/generate-token.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Test-Token-Generator fuer CRM-Service Entwicklung.
|
||||
*
|
||||
* Generiert ein gueltiges JWT mit konfigurierbaren Claims.
|
||||
*
|
||||
* Verwendung:
|
||||
* npx ts-node scripts/generate-token.ts
|
||||
* npx ts-node scripts/generate-token.ts --role TENANT_ADMIN
|
||||
* npx ts-node scripts/generate-token.ts --expiry 60 (60 Minuten)
|
||||
*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
function base64url(data: Buffer | string): string {
|
||||
const buf = typeof data === 'string' ? Buffer.from(data) : data;
|
||||
return buf.toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
||||
}
|
||||
|
||||
function generateToken(options: {
|
||||
role?: string;
|
||||
tenantId?: string;
|
||||
tenantSlug?: string;
|
||||
userId?: string;
|
||||
email?: string;
|
||||
expiryMinutes?: number;
|
||||
}): string {
|
||||
const privateKeyPath = path.join(__dirname, '..', 'keys', 'jwt-private.pem');
|
||||
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const expiryMinutes = options.expiryMinutes ?? 60;
|
||||
|
||||
const header = {
|
||||
alg: 'RS256',
|
||||
typ: 'JWT',
|
||||
};
|
||||
|
||||
const payload = {
|
||||
sub: options.userId ?? 'test-user-00000000-0000-0000-0000-000000000001',
|
||||
email: options.email ?? 'testuser@insight.local',
|
||||
role: options.role ?? 'TENANT_ADMIN',
|
||||
tenantId: options.tenantId ?? 'test-tenant-00000000-0000-0000-0000-000000000001',
|
||||
tenantSlug: options.tenantSlug ?? 'test_tenant',
|
||||
jti: crypto.randomUUID(),
|
||||
iss: 'insight-platform',
|
||||
iat: now,
|
||||
exp: now + expiryMinutes * 60,
|
||||
};
|
||||
|
||||
const headerB64 = base64url(JSON.stringify(header));
|
||||
const payloadB64 = base64url(JSON.stringify(payload));
|
||||
const signingInput = `${headerB64}.${payloadB64}`;
|
||||
|
||||
const sign = crypto.createSign('RSA-SHA256');
|
||||
sign.update(signingInput);
|
||||
const signature = sign.sign(privateKey);
|
||||
const signatureB64 = base64url(signature);
|
||||
|
||||
return `${signingInput}.${signatureB64}`;
|
||||
}
|
||||
|
||||
// CLI args parsen
|
||||
const args = process.argv.slice(2);
|
||||
const getArg = (name: string): string | undefined => {
|
||||
const idx = args.indexOf(`--${name}`);
|
||||
return idx >= 0 && idx + 1 < args.length ? args[idx + 1] : undefined;
|
||||
};
|
||||
|
||||
const token = generateToken({
|
||||
role: getArg('role'),
|
||||
tenantId: getArg('tenantId'),
|
||||
tenantSlug: getArg('tenantSlug'),
|
||||
userId: getArg('userId'),
|
||||
email: getArg('email'),
|
||||
expiryMinutes: getArg('expiry') ? parseInt(getArg('expiry')!, 10) : undefined,
|
||||
});
|
||||
|
||||
console.log('\n=== INSIGHT CRM Test-Token ===\n');
|
||||
console.log('Token:');
|
||||
console.log(token);
|
||||
console.log('\n--- Verwendung mit curl ---\n');
|
||||
console.log(`export CRM_TOKEN="${token}"\n`);
|
||||
console.log('# Kontakte auflisten:');
|
||||
console.log('curl -s http://localhost:3100/api/v1/crm/contacts -H "Authorization: Bearer $CRM_TOKEN" | jq\n');
|
||||
console.log('# Kontakt erstellen:');
|
||||
console.log(`curl -s -X POST http://localhost:3100/api/v1/crm/contacts \\
|
||||
-H "Authorization: Bearer $CRM_TOKEN" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{"type":"PERSON","firstName":"Max","lastName":"Mustermann","email":"max@firma.de","companyName":"Firma GmbH"}' | jq\n`);
|
||||
console.log('# Health-Check (kein Token noetig):');
|
||||
console.log('curl -s http://localhost:3100/health | jq\n');
|
||||
2
packages/crm-service/scripts/init-schema.sql
Normal file
2
packages/crm-service/scripts/init-schema.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
-- Erstellt das app_crm Schema fuer die CRM-Tabellen
|
||||
CREATE SCHEMA IF NOT EXISTS app_crm;
|
||||
Loading…
Add table
Reference in a new issue