Add admin panel

This commit is contained in:
2025-06-07 13:24:57 +09:00
parent 7915ed7a44
commit c57a37c9dd
14 changed files with 1138 additions and 145 deletions

View File

@ -26,6 +26,13 @@ interface Config {
baseUrl: string;
};
// Admin Panel Configuration
admin: {
port: number;
username?: string;
password?: string;
};
// File paths
paths: {
projectRoot: string;
@ -34,6 +41,7 @@ interface Config {
publicDir: string;
podcastAudioDir: string;
frontendBuildDir: string;
adminBuildDir: string;
feedUrlsFile: string;
};
}
@ -78,6 +86,12 @@ function createConfig(): Config {
baseUrl: getOptionalEnv("PODCAST_BASE_URL", "https://your-domain.com"),
},
admin: {
port: parseInt(getOptionalEnv("ADMIN_PORT", "3001")),
username: import.meta.env["ADMIN_USERNAME"],
password: import.meta.env["ADMIN_PASSWORD"],
},
paths: {
projectRoot,
dataDir,
@ -85,6 +99,7 @@ function createConfig(): Config {
publicDir,
podcastAudioDir: path.join(publicDir, "podcast_audio"),
frontendBuildDir: path.join(projectRoot, "frontend", "dist"),
adminBuildDir: path.join(projectRoot, "admin-panel", "dist"),
feedUrlsFile: path.join(projectRoot, getOptionalEnv("FEED_URLS_FILE", "feed_urls.txt")),
},
};

View File

@ -183,6 +183,71 @@ export async function getAllFeeds(): Promise<Feed[]> {
}
}
export async function getAllFeedsIncludingInactive(): Promise<Feed[]> {
try {
const stmt = db.prepare(
"SELECT * FROM feeds ORDER BY created_at DESC",
);
const rows = stmt.all() as any[];
return rows.map((row) => ({
id: row.id,
url: row.url,
title: row.title,
description: row.description,
lastUpdated: row.last_updated,
createdAt: row.created_at,
active: Boolean(row.active),
}));
} catch (error) {
console.error("Error getting all feeds including inactive:", error);
throw error;
}
}
export async function deleteFeed(feedId: string): Promise<boolean> {
try {
// Start transaction
db.exec("BEGIN TRANSACTION");
// Delete all episodes for articles belonging to this feed
const deleteEpisodesStmt = db.prepare(`
DELETE FROM episodes
WHERE article_id IN (
SELECT id FROM articles WHERE feed_id = ?
)
`);
deleteEpisodesStmt.run(feedId);
// Delete all articles for this feed
const deleteArticlesStmt = db.prepare("DELETE FROM articles WHERE feed_id = ?");
deleteArticlesStmt.run(feedId);
// Delete the feed itself
const deleteFeedStmt = db.prepare("DELETE FROM feeds WHERE id = ?");
const result = deleteFeedStmt.run(feedId);
db.exec("COMMIT");
return result.changes > 0;
} catch (error) {
db.exec("ROLLBACK");
console.error("Error deleting feed:", error);
throw error;
}
}
export async function toggleFeedActive(feedId: string, active: boolean): Promise<boolean> {
try {
const stmt = db.prepare("UPDATE feeds SET active = ? WHERE id = ?");
const result = stmt.run(active ? 1 : 0, feedId);
return result.changes > 0;
} catch (error) {
console.error("Error toggling feed active status:", error);
throw error;
}
}
// Article management functions
export async function saveArticle(
article: Omit<Article, "id" | "discoveredAt">,