feat: Backend REST API Grundstruktur
🔐 Auth Routes: - POST /register, /login, /refresh, /logout - GET /me 👥 Contacts Routes: - CRUD + /activities, /deals - /import, /export (DSGVO Art. 20) 💰 Deals Routes: - CRUD + /pipeline (Kanban View) - /move, /won, /lost - /forecast 📝 Activities Routes: - CRUD + /upcoming - /complete 📊 Pipelines Routes: - CRUD + /stages ✨ Features: - CORS Middleware - Error Handler - Request Logger - API Documentation Endpoint
This commit is contained in:
135
src/main.ts
135
src/main.ts
@@ -1,14 +1,28 @@
|
||||
import { Application } from "@oak/oak";
|
||||
import "@std/dotenv/load";
|
||||
|
||||
// Routes
|
||||
import { authRouter } from "./routes/auth.ts";
|
||||
import { contactsRouter } from "./routes/contacts.ts";
|
||||
import { dealsRouter } from "./routes/deals.ts";
|
||||
import { activitiesRouter } from "./routes/activities.ts";
|
||||
import { pipelinesRouter } from "./routes/pipelines.ts";
|
||||
|
||||
const app = new Application();
|
||||
const PORT = parseInt(Deno.env.get("PORT") || "8000");
|
||||
|
||||
// ============================================
|
||||
// MIDDLEWARE
|
||||
// ============================================
|
||||
|
||||
// CORS Middleware
|
||||
app.use(async (ctx, next) => {
|
||||
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
|
||||
ctx.response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
const origin = ctx.request.headers.get("origin") || "*";
|
||||
ctx.response.headers.set("Access-Control-Allow-Origin", 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");
|
||||
ctx.response.headers.set("Access-Control-Max-Age", "86400");
|
||||
|
||||
if (ctx.request.method === "OPTIONS") {
|
||||
ctx.response.status = 204;
|
||||
@@ -23,9 +37,34 @@ app.use(async (ctx, next) => {
|
||||
const start = Date.now();
|
||||
await next();
|
||||
const ms = Date.now() - start;
|
||||
console.log(`${ctx.request.method} ${ctx.request.url.pathname} - ${ctx.response.status} (${ms}ms)`);
|
||||
const status = ctx.response.status;
|
||||
const statusColor = status >= 500 ? "\x1b[31m" : status >= 400 ? "\x1b[33m" : "\x1b[32m";
|
||||
console.log(`${statusColor}${status}\x1b[0m ${ctx.request.method} ${ctx.request.url.pathname} - ${ms}ms`);
|
||||
});
|
||||
|
||||
// Error Handler
|
||||
app.use(async (ctx, next) => {
|
||||
try {
|
||||
await next();
|
||||
} catch (err) {
|
||||
console.error("Error:", err);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = {
|
||||
success: false,
|
||||
error: {
|
||||
code: "INTERNAL_ERROR",
|
||||
message: Deno.env.get("NODE_ENV") === "development"
|
||||
? err.message
|
||||
: "An internal error occurred",
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// SYSTEM ROUTES
|
||||
// ============================================
|
||||
|
||||
// Health Check
|
||||
app.use(async (ctx, next) => {
|
||||
if (ctx.request.url.pathname === "/health") {
|
||||
@@ -33,7 +72,7 @@ app.use(async (ctx, next) => {
|
||||
status: "ok",
|
||||
service: "pulse-crm-backend",
|
||||
version: "0.1.0",
|
||||
timestamp: new Date().toISOString()
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -46,36 +85,94 @@ app.use(async (ctx, next) => {
|
||||
ctx.response.body = {
|
||||
name: "Pulse CRM API",
|
||||
version: "1.0.0",
|
||||
docs: "/api/v1/docs",
|
||||
description: "Der Herzschlag deines Business",
|
||||
endpoints: {
|
||||
auth: "/api/v1/auth/*",
|
||||
contacts: "/api/v1/contacts/*",
|
||||
companies: "/api/v1/companies/*",
|
||||
deals: "/api/v1/deals/*",
|
||||
pipelines: "/api/v1/pipelines/*",
|
||||
activities: "/api/v1/activities/*",
|
||||
users: "/api/v1/users/*"
|
||||
}
|
||||
auth: {
|
||||
"POST /api/v1/auth/register": "Register new user",
|
||||
"POST /api/v1/auth/login": "Login",
|
||||
"POST /api/v1/auth/refresh": "Refresh token",
|
||||
"POST /api/v1/auth/logout": "Logout",
|
||||
"GET /api/v1/auth/me": "Get current user",
|
||||
},
|
||||
contacts: {
|
||||
"GET /api/v1/contacts": "List contacts",
|
||||
"GET /api/v1/contacts/:id": "Get contact",
|
||||
"POST /api/v1/contacts": "Create contact",
|
||||
"PUT /api/v1/contacts/:id": "Update contact",
|
||||
"DELETE /api/v1/contacts/:id": "Delete contact",
|
||||
},
|
||||
deals: {
|
||||
"GET /api/v1/deals": "List deals",
|
||||
"GET /api/v1/deals/pipeline": "Get pipeline view",
|
||||
"GET /api/v1/deals/:id": "Get deal",
|
||||
"POST /api/v1/deals": "Create deal",
|
||||
"PUT /api/v1/deals/:id": "Update deal",
|
||||
"POST /api/v1/deals/:id/move": "Move to stage",
|
||||
"POST /api/v1/deals/:id/won": "Mark as won",
|
||||
"POST /api/v1/deals/:id/lost": "Mark as lost",
|
||||
},
|
||||
activities: {
|
||||
"GET /api/v1/activities": "List activities",
|
||||
"GET /api/v1/activities/upcoming": "Upcoming tasks",
|
||||
"POST /api/v1/activities": "Create activity",
|
||||
"POST /api/v1/activities/:id/complete": "Complete task",
|
||||
},
|
||||
pipelines: {
|
||||
"GET /api/v1/pipelines": "List pipelines",
|
||||
"POST /api/v1/pipelines": "Create pipeline",
|
||||
"PUT /api/v1/pipelines/:id/stages": "Update stages",
|
||||
},
|
||||
},
|
||||
};
|
||||
return;
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// 404 Handler
|
||||
// ============================================
|
||||
// API ROUTES
|
||||
// ============================================
|
||||
|
||||
app.use(authRouter.routes());
|
||||
app.use(authRouter.allowedMethods());
|
||||
|
||||
app.use(contactsRouter.routes());
|
||||
app.use(contactsRouter.allowedMethods());
|
||||
|
||||
app.use(dealsRouter.routes());
|
||||
app.use(dealsRouter.allowedMethods());
|
||||
|
||||
app.use(activitiesRouter.routes());
|
||||
app.use(activitiesRouter.allowedMethods());
|
||||
|
||||
app.use(pipelinesRouter.routes());
|
||||
app.use(pipelinesRouter.allowedMethods());
|
||||
|
||||
// ============================================
|
||||
// 404 HANDLER
|
||||
// ============================================
|
||||
|
||||
app.use((ctx) => {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = {
|
||||
success: false,
|
||||
error: {
|
||||
code: "NOT_FOUND",
|
||||
message: "Endpoint not found"
|
||||
}
|
||||
message: `Endpoint ${ctx.request.method} ${ctx.request.url.pathname} not found`,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
console.log(`🚀 Pulse CRM Backend starting on port ${PORT}...`);
|
||||
console.log(`📚 API Docs: http://localhost:${PORT}/api/v1`);
|
||||
console.log(`❤️ Health: http://localhost:${PORT}/health`);
|
||||
// ============================================
|
||||
// START SERVER
|
||||
// ============================================
|
||||
|
||||
console.log("");
|
||||
console.log(" 🫀 Pulse CRM Backend");
|
||||
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("");
|
||||
|
||||
await app.listen({ port: PORT });
|
||||
|
||||
Reference in New Issue
Block a user