feat: API Key Auth + erweiterte MCP Tools + Dokumentation
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
- API Key Middleware für alle /api/ Endpoints (X-API-Key, Bearer, Query) - API Key Management Routes (generate, revoke, status) - MCP Tools erweitert: 12 Tools (war 3) inkl. bulk ops, dashboard, search - Audit Logging für alle API/MCP Aktionen - docs/API-REFERENCE.md - vollständige API Dokumentation - docs/ARCHITECTURE.md - Architektur-Übersicht - Vorbereitung für NotiBot-Integration
This commit is contained in:
@@ -16,6 +16,7 @@ const recurrenceService = new RecurrenceService(storage);
|
||||
const gamificationService = new GamificationService(storage);
|
||||
|
||||
import { setupAuth, hashPassword, comparePassword } from "./auth.js";
|
||||
import { apiKeyAuth } from "./api-key-auth.js";
|
||||
|
||||
function isAdmin(req: any, res: any, next: any) {
|
||||
if (req.isAuthenticated() && req.user.role === 'admin') {
|
||||
@@ -27,6 +28,10 @@ function isAdmin(req: any, res: any, next: any) {
|
||||
export async function registerRoutes(app: Express): Promise<Server> {
|
||||
setupAuth(app);
|
||||
|
||||
// API Key auth middleware - must be after session setup
|
||||
// Allows external tools to authenticate via X-API-Key header
|
||||
app.use("/api", apiKeyAuth());
|
||||
|
||||
// Update user schedule
|
||||
app.patch("/api/user/schedule", async (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
@@ -838,6 +843,62 @@ ${activeTasksWithLabels.slice(0, 5).map(t => `- [${t.priority}] [${t.label}] ${t
|
||||
}
|
||||
});
|
||||
|
||||
// --- API Key Management Routes ---
|
||||
|
||||
// Get current API key (masked)
|
||||
app.get("/api/user/api-key", async (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
const user = req.user as User;
|
||||
if (user.apiKey) {
|
||||
// Show first 8 and last 4 chars
|
||||
const masked = user.apiKey.substring(0, 8) + "..." + user.apiKey.substring(user.apiKey.length - 4);
|
||||
res.json({ hasKey: true, maskedKey: masked });
|
||||
} else {
|
||||
res.json({ hasKey: false, maskedKey: null });
|
||||
}
|
||||
});
|
||||
|
||||
// Generate new API key
|
||||
app.post("/api/user/api-key", async (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
const user = req.user as User;
|
||||
const crypto = await import("crypto");
|
||||
const newKey = "tf_" + crypto.randomBytes(32).toString("hex");
|
||||
|
||||
await storage.updateUserApiKey(user.id, newKey);
|
||||
|
||||
await storage.createAuditLog({
|
||||
userId: user.id,
|
||||
action: "CREATE",
|
||||
entityType: "API_KEY",
|
||||
entityId: user.id,
|
||||
details: { message: "API key generated" },
|
||||
source: "USER"
|
||||
});
|
||||
|
||||
// Return full key ONCE (user must save it)
|
||||
res.json({ apiKey: newKey, message: "Save this key - it won't be shown again in full." });
|
||||
});
|
||||
|
||||
// Revoke API key
|
||||
app.delete("/api/user/api-key", async (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
const user = req.user as User;
|
||||
|
||||
await storage.updateUserApiKey(user.id, null);
|
||||
|
||||
await storage.createAuditLog({
|
||||
userId: user.id,
|
||||
action: "DELETE",
|
||||
entityType: "API_KEY",
|
||||
entityId: user.id,
|
||||
details: { message: "API key revoked" },
|
||||
source: "USER"
|
||||
});
|
||||
|
||||
res.json({ message: "API key revoked" });
|
||||
});
|
||||
|
||||
// Health check endpoint
|
||||
app.get("/api/health", (req, res) => {
|
||||
res.status(200).json({ status: "ok", timestamp: new Date().toISOString() });
|
||||
|
||||
Reference in New Issue
Block a user