Thank you so much for your help and speedy response @vikaspotluri123 and @Cathy_Sarisky!
Ok I used the following to generate the needed token and attempt to upload the .json file:
const fs = require('fs');
const jwt = require('jsonwebtoken');
const axios = require('axios');
const FormData = require('form-data');
const path = require('path');
const GHOST_URL = "https://yourdomain.com/folder"; // 🔹 Replace with your Ghost URL
const ADMIN_API_KEY = "your_admin_api_key"; // 🔹 Replace with your actual Admin API Key
const IMPORT_FILE_PATH = "/absolute/path/to/import.json"; // 🔹 Replace with your actual file path
// 🔴 Ensure Required Variables Are Set
if (!GHOST_URL || !ADMIN_API_KEY || !IMPORT_FILE_PATH) {
console.error("❌ Missing required values. Update the script with your Ghost URL, Admin API Key, and JSON file path.");
process.exit(1);
}
// ✅ Extract API Key ID and Secret
const [id, secret] = ADMIN_API_KEY.split(':');
if (!id || !secret) {
console.error("❌ Invalid Admin API Key format. Expected format: {id}:{secret}");
process.exit(1);
}
// ✅ Generate JWT Token
const createJwtToken = () => {
const now = Math.floor(Date.now() / 1000);
return jwt.sign(
{ exp: now + 300, iat: now, aud: "/admin/" }, // Payload
Buffer.from(secret, 'hex'),
{ keyid: id, algorithm: "HS256", header: { typ: "JWT" } }
);
};
// ✅ Upload JSON to Ghost Admin API
const uploadJsonToGhost = async () => {
try {
console.log(`📂 Reading import file from: ${IMPORT_FILE_PATH}`);
if (!fs.existsSync(IMPORT_FILE_PATH)) {
throw new Error(`❌ File not found: ${IMPORT_FILE_PATH}`);
}
const token = createJwtToken();
console.log(`🔑 Generated JWT Token`);
const apiUrl = `${GHOST_URL}/ghost/api/admin/db/`;
const formData = new FormData();
formData.append("importfile", fs.createReadStream(IMPORT_FILE_PATH));
console.log(`📡 Uploading import.json to ${apiUrl}...`);
const response = await axios.post(apiUrl, formData, {
headers: {
Authorization: `Ghost ${token}`,
...formData.getHeaders(),
},
});
console.log("✅ Import successful!", response.data);
} catch (error) {
console.error("❌ Import failed:", error.response?.data || error.message);
}
};
// ✅ Run the Upload Function
uploadJsonToGhost();
The token is generated and upload is attempted; however, I got this error:
❌ Import failed: {
errors: [
{
message: 'You do not have permission to importContent db',
context: null,
type: 'NoPermissionError',
details: null,
property: null,
help: null,
code: null,
id: 'b0e96b20-04e3-11f0-86fc-cf66509cf4ac',
ghostErrorCode: null
}
]
}
Am I able to upload a single .json file in this way? Before I was attempting to read my .json file and upload the posts and tag associations via the admin api (
api.posts.add … ), but that only uploaded the posts and did not retain the tag associations.
What I am trying to solve is this: I need to programmatically upload posts such that the tag-post associations are retained. My hope was that I could achieve this by uploading the single .json file in one go because when I programmatically do this for each post added via admin api, a new id is set such that it is no longer the id in my .json file and so the tag associations are lost. Manually uploading works great and tag associations are retained then, but again I need to do this via the terminal for my use-case.
Let me know if that makes sense or if I can given further information to clarify.
Thank you again for your help!