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

+25
View File
@@ -37,6 +37,8 @@ import {
audioTracks,
type AudioTrack,
type InsertAudioTrack,
appConfig,
type AppConfig,
} from "@shared/schema";
import { db } from "./db";
import { eq, and, gte, lte, desc, asc, isNull, inArray } from "drizzle-orm";
@@ -126,6 +128,10 @@ export interface IStorage {
getFreesoundConfig(userId: string): Promise<FreesoundConfig | undefined>;
createFreesoundConfig(config: InsertFreesoundConfig): Promise<FreesoundConfig>;
updateFreesoundConfig(userId: string, config: Partial<InsertFreesoundConfig>): Promise<FreesoundConfig>;
// App Config
getAppConfig(): Promise<AppConfig | undefined>;
upsertAppConfig(data: Partial<Pick<AppConfig, 'externalApiKey'>>): Promise<AppConfig>;
}
export class DatabaseStorage implements IStorage {
@@ -584,6 +590,25 @@ export class DatabaseStorage implements IStorage {
.returning();
return updated;
}
// App Config
async getAppConfig(): Promise<AppConfig | undefined> {
const [config] = await db.select().from(appConfig).limit(1);
return config || undefined;
}
async upsertAppConfig(data: Partial<Pick<AppConfig, 'externalApiKey'>>): Promise<AppConfig> {
const existing = await this.getAppConfig();
if (existing) {
const [updated] = await db.update(appConfig)
.set({ ...data, updatedAt: new Date() })
.where(eq(appConfig.id, existing.id))
.returning();
return updated;
}
const [created] = await db.insert(appConfig).values(data).returning();
return created;
}
}
export const storage = new DatabaseStorage();