194 lines
4.8 KiB
JavaScript
194 lines
4.8 KiB
JavaScript
import inquirer from 'inquirer';
|
||
import { loadConfig } from '../config.js';
|
||
import { initDirectusClient, createItem, searchItems } from '../directus/client.js';
|
||
import { promptForMultipleRelations } from '../directus/relationHelpers.js';
|
||
|
||
const ASSOCIATED_MEDIA_TYPES = ['artists', 'books', 'movies', 'shows', 'genres'];
|
||
const BLOCK_COLLECTIONS = [
|
||
'youtube_player',
|
||
'github_banner',
|
||
'npm_banner',
|
||
'rss_banner',
|
||
'calendar_banner',
|
||
'forgejo_banner'
|
||
];
|
||
|
||
export const addPost = async () => {
|
||
const config = await loadConfig();
|
||
|
||
initDirectusClient(config);
|
||
|
||
const { title, description, content, featured } = await inquirer.prompt([
|
||
{
|
||
name: 'title',
|
||
message: '📝 Title:',
|
||
validate: (input) => !!input || 'Title is required'
|
||
},
|
||
{
|
||
name: 'description',
|
||
message: '🗒 Description:',
|
||
default: ''
|
||
},
|
||
{
|
||
name: 'content',
|
||
message: '📄 Content:',
|
||
default: ''
|
||
},
|
||
{
|
||
type: 'confirm',
|
||
name: 'featured',
|
||
message: '⭐ Featured?',
|
||
default: false
|
||
}
|
||
]);
|
||
|
||
let tagIds = [];
|
||
|
||
while (true) {
|
||
const { query } = await inquirer.prompt({
|
||
name: 'query',
|
||
message: '🏷 Search for tags (or leave blank to finish):'
|
||
});
|
||
const trimmedQuery = query.trim();
|
||
|
||
if (!trimmedQuery) break;
|
||
|
||
const tags = await searchItems('tags', trimmedQuery);
|
||
|
||
if (!tags.length) {
|
||
console.warn(`⚠️ No tags found matching "${trimmedQuery}"`);
|
||
|
||
continue;
|
||
}
|
||
|
||
const { selected } = await inquirer.prompt({
|
||
type: 'checkbox',
|
||
name: 'selected',
|
||
message: '✔ Select tags to add:',
|
||
choices: tags.map((tag) => ({ name: tag.name, value: tag.id }))
|
||
});
|
||
|
||
tagIds.push(...selected);
|
||
|
||
const { again } = await inquirer.prompt({
|
||
type: 'confirm',
|
||
name: 'again',
|
||
message: 'Search and select more tags?',
|
||
default: false
|
||
});
|
||
|
||
if (!again) break;
|
||
}
|
||
|
||
const selectedBlocks = [];
|
||
const { includeBlocks } = await inquirer.prompt({
|
||
type: 'confirm',
|
||
name: 'includeBlocks',
|
||
message: '➕ Add blocks?',
|
||
default: false
|
||
});
|
||
|
||
if (includeBlocks) {
|
||
while (true) {
|
||
const { collection } = await inquirer.prompt({
|
||
type: 'list',
|
||
name: 'collection',
|
||
message: '🧱 Choose a block collection (or Cancel to finish):',
|
||
choices: [...BLOCK_COLLECTIONS, new inquirer.Separator(), 'Cancel']
|
||
});
|
||
|
||
if (collection === 'Cancel') break;
|
||
|
||
const { query } = await inquirer.prompt({
|
||
name: 'query',
|
||
message: `🔍 Search ${collection}:`
|
||
});
|
||
const results = await searchItems(collection, query);
|
||
|
||
if (!results.length) {
|
||
console.warn(`⚠️ No items found in "${collection}" matching "${query}"`);
|
||
|
||
continue;
|
||
}
|
||
|
||
const { itemId } = await inquirer.prompt({
|
||
type: 'list',
|
||
name: 'itemId',
|
||
message: `Select an item from ${collection}:`,
|
||
choices: results.map((item) => ({
|
||
name: item.title || item.name || item.id,
|
||
value: item.id
|
||
}))
|
||
});
|
||
|
||
selectedBlocks.push({ collection, item: itemId });
|
||
|
||
const { again } = await inquirer.prompt({
|
||
type: 'confirm',
|
||
name: 'again',
|
||
message: '➕ Add another block?',
|
||
default: false
|
||
});
|
||
|
||
if (!again) break;
|
||
}
|
||
}
|
||
|
||
const associatedMediaPayload = {};
|
||
const { includeMedia } = await inquirer.prompt({
|
||
type: 'confirm',
|
||
name: 'includeMedia',
|
||
message: '➕ Add associated media?',
|
||
default: false
|
||
});
|
||
|
||
if (includeMedia) {
|
||
for (const mediaType of ASSOCIATED_MEDIA_TYPES) {
|
||
const { query } = await inquirer.prompt({
|
||
name: 'query',
|
||
message: `🔎 Search for ${mediaType} to associate (or leave blank to skip):`
|
||
});
|
||
|
||
if (!query.trim()) continue;
|
||
|
||
const matches = await searchItems(mediaType, query.trim());
|
||
|
||
if (!matches.length) {
|
||
console.warn(`⚠️ No ${mediaType} found matching "${query.trim()}"`);
|
||
|
||
continue;
|
||
}
|
||
|
||
const { selected } = await inquirer.prompt({
|
||
type: 'checkbox',
|
||
name: 'selected',
|
||
message: `✔ Select ${mediaType} to associate:`,
|
||
choices: matches.map((m) => ({
|
||
name: m.name_string || m.title || m.name || m.label || m.id,
|
||
value: m.id
|
||
}))
|
||
});
|
||
|
||
if (selected.length)
|
||
associatedMediaPayload[`${mediaType}`] = selected.map((id) => ({
|
||
[`${mediaType}_id`]: id
|
||
}));
|
||
}
|
||
}
|
||
|
||
const media = await promptForMultipleRelations('media', 'Associated media');
|
||
const payload = {
|
||
title,
|
||
description,
|
||
content,
|
||
featured,
|
||
date: new Date().toISOString(),
|
||
post_tags: tagIds.map((tagId) => ({ tags_id: tagId })),
|
||
blocks: selectedBlocks,
|
||
...associatedMediaPayload
|
||
};
|
||
|
||
await createItem('posts', payload);
|
||
|
||
console.log('✅ Post created successfully.');
|
||
};
|