- offer to create tag when none is found while adding a link from cli - fix tag display in search
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.");
|
||
};
|