feat(auth): Implementiere vollständiges Auth-System
- JWT Access + Refresh Tokens mit djwt - Argon2 Password Hashing (OWASP konfig) - Rate Limiting für Auth-Endpoints - Rollen-basierte Zugriffskontrolle (owner, admin, manager, user) - DSGVO Audit Logging - Email-Verifizierung (Struktur) - Passwort-Reset Flow - Multi-Device Logout Neue Dateien: - src/types/index.ts - TypeScript Interfaces - src/db/connection.ts - PostgreSQL Pool - src/services/password.ts - Argon2 Hashing - src/services/jwt.ts - Token Generation - src/services/audit.ts - DSGVO Audit Log - src/middleware/auth.ts - Auth Middleware - src/repositories/user.ts - User DB Queries - src/repositories/organization.ts - Org DB Queries - src/utils/response.ts - API Response Helpers Task: #8 Authentifizierung & Benutzerverwaltung
This commit is contained in:
71
src/main.ts
71
src/main.ts
@@ -8,8 +8,12 @@ import { dealsRouter } from "./routes/deals.ts";
|
||||
import { activitiesRouter } from "./routes/activities.ts";
|
||||
import { pipelinesRouter } from "./routes/pipelines.ts";
|
||||
|
||||
// Database
|
||||
import { checkHealth as checkDbHealth } from "./db/connection.ts";
|
||||
|
||||
const app = new Application();
|
||||
const PORT = parseInt(Deno.env.get("PORT") || "8000");
|
||||
const NODE_ENV = Deno.env.get("NODE_ENV") || "development";
|
||||
|
||||
// ============================================
|
||||
// MIDDLEWARE
|
||||
@@ -17,8 +21,15 @@ const PORT = parseInt(Deno.env.get("PORT") || "8000");
|
||||
|
||||
// CORS Middleware
|
||||
app.use(async (ctx, next) => {
|
||||
const origin = ctx.request.headers.get("origin") || "*";
|
||||
ctx.response.headers.set("Access-Control-Allow-Origin", origin);
|
||||
const allowedOrigins = Deno.env.get("CORS_ORIGINS")?.split(",") || ["*"];
|
||||
const origin = ctx.request.headers.get("origin");
|
||||
|
||||
if (origin && (allowedOrigins.includes("*") || allowedOrigins.includes(origin))) {
|
||||
ctx.response.headers.set("Access-Control-Allow-Origin", origin);
|
||||
} else if (allowedOrigins.includes("*")) {
|
||||
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
|
||||
}
|
||||
|
||||
ctx.response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH");
|
||||
ctx.response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
||||
ctx.response.headers.set("Access-Control-Allow-Credentials", "true");
|
||||
@@ -32,7 +43,16 @@ app.use(async (ctx, next) => {
|
||||
await next();
|
||||
});
|
||||
|
||||
// Logger Middleware
|
||||
// Security Headers
|
||||
app.use(async (ctx, next) => {
|
||||
ctx.response.headers.set("X-Content-Type-Options", "nosniff");
|
||||
ctx.response.headers.set("X-Frame-Options", "DENY");
|
||||
ctx.response.headers.set("X-XSS-Protection", "1; mode=block");
|
||||
ctx.response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
|
||||
await next();
|
||||
});
|
||||
|
||||
// Request Logger
|
||||
app.use(async (ctx, next) => {
|
||||
const start = Date.now();
|
||||
await next();
|
||||
@@ -48,14 +68,18 @@ app.use(async (ctx, next) => {
|
||||
await next();
|
||||
} catch (err) {
|
||||
console.error("Error:", err);
|
||||
ctx.response.status = 500;
|
||||
|
||||
const status = err.status || 500;
|
||||
ctx.response.status = status;
|
||||
ctx.response.body = {
|
||||
success: false,
|
||||
error: {
|
||||
code: "INTERNAL_ERROR",
|
||||
message: Deno.env.get("NODE_ENV") === "development"
|
||||
code: status === 500 ? "INTERNAL_ERROR" : "ERROR",
|
||||
message: NODE_ENV === "development"
|
||||
? err.message
|
||||
: "An internal error occurred",
|
||||
: status === 500
|
||||
? "An internal error occurred"
|
||||
: err.message,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -65,20 +89,35 @@ app.use(async (ctx, next) => {
|
||||
// SYSTEM ROUTES
|
||||
// ============================================
|
||||
|
||||
// Health Check
|
||||
// Health Check (includes DB status)
|
||||
app.use(async (ctx, next) => {
|
||||
if (ctx.request.url.pathname === "/health") {
|
||||
const dbHealthy = await checkDbHealth();
|
||||
|
||||
ctx.response.status = dbHealthy ? 200 : 503;
|
||||
ctx.response.body = {
|
||||
status: "ok",
|
||||
status: dbHealthy ? "ok" : "degraded",
|
||||
service: "pulse-crm-backend",
|
||||
version: "0.1.0",
|
||||
timestamp: new Date().toISOString(),
|
||||
checks: {
|
||||
database: dbHealthy ? "ok" : "error",
|
||||
},
|
||||
};
|
||||
return;
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// Liveness probe (simple check)
|
||||
app.use(async (ctx, next) => {
|
||||
if (ctx.request.url.pathname === "/live") {
|
||||
ctx.response.body = { status: "ok" };
|
||||
return;
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// API Info
|
||||
app.use(async (ctx, next) => {
|
||||
if (ctx.request.url.pathname === "/api" || ctx.request.url.pathname === "/api/v1") {
|
||||
@@ -86,12 +125,17 @@ app.use(async (ctx, next) => {
|
||||
name: "Pulse CRM API",
|
||||
version: "1.0.0",
|
||||
description: "Der Herzschlag deines Business",
|
||||
documentation: "/api/v1/docs",
|
||||
endpoints: {
|
||||
auth: {
|
||||
"POST /api/v1/auth/register": "Register new user",
|
||||
"POST /api/v1/auth/register": "Register new user & organization",
|
||||
"POST /api/v1/auth/login": "Login",
|
||||
"POST /api/v1/auth/refresh": "Refresh token",
|
||||
"POST /api/v1/auth/logout": "Logout",
|
||||
"POST /api/v1/auth/refresh": "Refresh access token",
|
||||
"POST /api/v1/auth/logout": "Logout (revoke token)",
|
||||
"POST /api/v1/auth/logout-all": "Logout from all devices",
|
||||
"POST /api/v1/auth/forgot-password": "Request password reset",
|
||||
"POST /api/v1/auth/reset-password": "Reset password with token",
|
||||
"POST /api/v1/auth/verify-email": "Verify email address",
|
||||
"GET /api/v1/auth/me": "Get current user",
|
||||
},
|
||||
contacts: {
|
||||
@@ -100,6 +144,8 @@ app.use(async (ctx, next) => {
|
||||
"POST /api/v1/contacts": "Create contact",
|
||||
"PUT /api/v1/contacts/:id": "Update contact",
|
||||
"DELETE /api/v1/contacts/:id": "Delete contact",
|
||||
"POST /api/v1/contacts/import": "Import contacts (CSV)",
|
||||
"GET /api/v1/contacts/export": "Export contacts (DSGVO)",
|
||||
},
|
||||
deals: {
|
||||
"GET /api/v1/deals": "List deals",
|
||||
@@ -173,6 +219,7 @@ console.log(" ====================");
|
||||
console.log(` 📡 Server: http://localhost:${PORT}`);
|
||||
console.log(` 📚 API: http://localhost:${PORT}/api/v1`);
|
||||
console.log(` ❤️ Health: http://localhost:${PORT}/health`);
|
||||
console.log(` 🔧 Mode: ${NODE_ENV}`);
|
||||
console.log("");
|
||||
|
||||
await app.listen({ port: PORT });
|
||||
|
||||
Reference in New Issue
Block a user