feat: add external API for publishing posts with image URL and scheduling

- New POST /api/v1/publish endpoint: download image from URL, create post and schedule it per page
- New GET /api/v1/pages endpoint: list available pages for external callers
- API key auth via X-API-Key header, configurable from admin settings (stored in DB)
- New app_config table (migration included) to store the external API key
- Admin-only settings section (desktop + mobile) to set/revoke the key
- Fallback to EXTERNAL_API_KEY env var if no DB key is configured

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Sonnet 4.6 committed 2026-05-06 13:12:27 +02:00
1 parent 9e6fe9fe31
commit 391acfdb83
9 files changed
+539 -2

No files matched your search

+39
View File
@@ -16,6 +16,7 @@ import { freeSoundService } from "./services/freesound";
import { analyticsRouter } from "./routes/analytics";
import { reelsRouter } from "./routes/reels";
import { remotionRouter } from "./routes/remotion";
import { externalRouter } from "./routes/external";
import { insertAudioTrackSchema } from "@shared/schema";
import * as musicMetadata from "music-metadata";
@@ -382,6 +383,44 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// External API (clé API, pas de session)
app.use("/api/v1", externalRouter);
// Paramètres de l'API externe (admin seulement)
app.get("/api/settings/external-api", requireAdmin, async (req, res) => {
try {
const config = await storage.getAppConfig();
res.json({ configured: !!(config?.externalApiKey) });
} catch (error) {
console.error("Error fetching app config:", error);
res.status(500).json({ error: "Erreur lors de la récupération de la configuration" });
}
});
app.post("/api/settings/external-api", requireAdmin, async (req, res) => {
try {
const { apiKey } = req.body;
if (!apiKey || typeof apiKey !== "string" || apiKey.trim() === "") {
return res.status(400).json({ error: "La clé API est requise" });
}
await storage.upsertAppConfig({ externalApiKey: apiKey.trim() });
res.json({ success: true, configured: true });
} catch (error) {
console.error("Error saving app config:", error);
res.status(500).json({ error: "Erreur lors de la sauvegarde de la configuration" });
}
});
app.delete("/api/settings/external-api", requireAdmin, async (req, res) => {
try {
await storage.upsertAppConfig({ externalApiKey: null });
res.json({ success: true, configured: false });
} catch (error) {
console.error("Error deleting app config:", error);
res.status(500).json({ error: "Erreur lors de la suppression de la clé" });
}
});
// Analytics Routes
app.use("/api/analytics", requireAuth, analyticsRouter);