mirror of
https://github.com/gitroomhq/postiz-agent.git
synced 2026-09-14 13:46:24 +08:00
feat: add posts:settings command to patch a post's provider settings
Add `postiz posts:settings <id> --settings '<json>'` calling PUT /public/v1/posts/:id/settings to patch a post's provider-specific settings without recreating it. Modeled on the existing posts:status command. Settings are merged server-side (only passed keys change); only DRAFT/QUEUE posts can be updated. Also document that posts:list responses now include each post's current `settings` (returned as a JSON string). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,14 @@ All notable changes to the Postiz CLI will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `posts:settings` - Update a post's provider settings via `PUT /public/v1/posts/:id/settings` (merged — only the keys you pass change; unpublished DRAFT/QUEUE posts only).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `posts:list` responses now include each post's current `settings`.
|
||||||
|
|
||||||
## [1.0.0] - 2026-02-13
|
## [1.0.0] - 2026-02-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ postiz posts:list --startDate "2024-01-01T00:00:00Z" --endDate "2024-12-31T23:59
|
|||||||
postiz posts:list --customer "customer-id"
|
postiz posts:list --customer "customer-id"
|
||||||
```
|
```
|
||||||
|
|
||||||
Defaults to last 30 days to next 30 days if dates not specified.
|
Defaults to last 30 days to next 30 days if dates not specified. Each returned post includes its current `settings` (returned as a JSON string — `JSON.parse` it). The intended workflow is `posts:list` (read current settings) → `posts:settings` (patch them).
|
||||||
|
|
||||||
**Delete post**
|
**Delete post**
|
||||||
```bash
|
```bash
|
||||||
@@ -198,6 +198,14 @@ postiz posts:status <post-id> --status schedule
|
|||||||
|
|
||||||
Move a scheduled post back to a draft, or promote a draft into the publishing queue. Switching to `draft` also terminates any workflow that's already running for the post, so it won't publish. Switching to `schedule` queues the post for publishing at its stored date.
|
Move a scheduled post back to a draft, or promote a draft into the publishing queue. Switching to `draft` also terminates any workflow that's already running for the post, so it won't publish. Switching to `schedule` queues the post for publishing at its stored date.
|
||||||
|
|
||||||
|
**Update a post's provider-specific settings**
|
||||||
|
```bash
|
||||||
|
postiz posts:settings <post-id> --settings '{"content_posting_method":"DIRECT_POST"}'
|
||||||
|
postiz posts:settings <post-id> --settings '{"subreddit":[{"value":{"subreddit":"/r/selfhosted","title":"My title","type":"self","is_flair_required":true}}]}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Patches a post's settings server-side. The backend **merges** the object — only the keys you pass change, everything else is preserved — so pass a partial object, not the full settings blob. Only **DRAFT/QUEUE** (unpublished) posts can be updated; published posts are rejected. Pass the **main post id**, not a comment id. Do **not** include `__type` — the backend adds it automatically from the integration.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Analytics
|
### Analytics
|
||||||
@@ -560,6 +568,7 @@ The CLI interacts with these Postiz API endpoints:
|
|||||||
| `/public/v1/posts` | POST | Create a post |
|
| `/public/v1/posts` | POST | Create a post |
|
||||||
| `/public/v1/posts` | GET | List posts |
|
| `/public/v1/posts` | GET | List posts |
|
||||||
| `/public/v1/posts/:id` | DELETE | Delete a post |
|
| `/public/v1/posts/:id` | DELETE | Delete a post |
|
||||||
|
| `/public/v1/posts/:id/settings` | PUT | Update a post's provider settings (merged; unpublished only) |
|
||||||
| `/public/v1/posts/:id/missing` | GET | Get missing content from provider |
|
| `/public/v1/posts/:id/missing` | GET | Get missing content from provider |
|
||||||
| `/public/v1/posts/:id/release-id` | PUT | Update release ID for a post |
|
| `/public/v1/posts/:id/release-id` | PUT | Update release ID for a post |
|
||||||
| `/public/v1/integrations` | GET | List integrations (optional `?group=` filter) |
|
| `/public/v1/integrations` | GET | List integrations (optional `?group=` filter) |
|
||||||
@@ -679,6 +688,7 @@ postiz posts:list # List posts
|
|||||||
postiz posts:delete <id> # Delete post
|
postiz posts:delete <id> # Delete post
|
||||||
postiz posts:status <id> --status draft # Move to draft (stops workflow)
|
postiz posts:status <id> --status draft # Move to draft (stops workflow)
|
||||||
postiz posts:status <id> --status schedule # Queue draft for publishing
|
postiz posts:status <id> --status schedule # Queue draft for publishing
|
||||||
|
postiz posts:settings <id> --settings '{}' # Patch a post's settings (merged; DRAFT/QUEUE only)
|
||||||
postiz upload <file> # Upload media
|
postiz upload <file> # Upload media
|
||||||
|
|
||||||
# Analytics
|
# Analytics
|
||||||
|
|||||||
@@ -194,6 +194,8 @@ postiz posts:create --json post.json
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# List posts (defaults to last 30 days to next 30 days)
|
# List posts (defaults to last 30 days to next 30 days)
|
||||||
|
# Each returned post includes its current `settings` (as a JSON string — JSON.parse it).
|
||||||
|
# Workflow: run posts:list to read a post's current settings, then posts:settings to patch them.
|
||||||
postiz posts:list
|
postiz posts:list
|
||||||
|
|
||||||
# List posts in date range
|
# List posts in date range
|
||||||
@@ -205,6 +207,12 @@ postiz posts:delete <post-id>
|
|||||||
# Change post status (draft ↔ schedule)
|
# Change post status (draft ↔ schedule)
|
||||||
postiz posts:status <post-id> --status draft # Move back to draft, terminates any running publish workflow
|
postiz posts:status <post-id> --status draft # Move back to draft, terminates any running publish workflow
|
||||||
postiz posts:status <post-id> --status schedule # Promote a draft into the publishing queue (uses the post's stored date)
|
postiz posts:status <post-id> --status schedule # Promote a draft into the publishing queue (uses the post's stored date)
|
||||||
|
|
||||||
|
# Update a post's provider-specific settings (merged — only the keys you pass change)
|
||||||
|
# Only DRAFT/QUEUE (unpublished) posts can be updated. Pass the MAIN post id, not a comment id.
|
||||||
|
# Do NOT include __type — the backend adds it automatically from the integration.
|
||||||
|
postiz posts:settings <post-id> --settings '{"content_posting_method":"DIRECT_POST"}' # Switch a TikTok draft to direct publishing
|
||||||
|
postiz posts:settings <post-id> --settings '{"subreddit":[{"value":{"subreddit":"/r/selfhosted","title":"My title","type":"self","is_flair_required":true}}]}' # Set a Reddit post's subreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Analytics
|
### Analytics
|
||||||
@@ -749,6 +757,7 @@ https://clawhub.ai/nevo-david/agent-media
|
|||||||
9. **Required settings** - Some platforms require specific settings (Reddit needs title, YouTube needs title)
|
9. **Required settings** - Some platforms require specific settings (Reddit needs title, YouTube needs title)
|
||||||
10. **Media MIME types** - CLI auto-detects from file extension, ensure correct extension
|
10. **Media MIME types** - CLI auto-detects from file extension, ensure correct extension
|
||||||
11. **Analytics returns `{"missing": true}`** - The post was published but the platform didn't return a post ID. Run `posts:missing <post-id>` to get available content, then `posts:connect <post-id> --release-id "<id>"` to link it. Analytics will work after connecting.
|
11. **Analytics returns `{"missing": true}`** - The post was published but the platform didn't return a post ID. Run `posts:missing <post-id>` to get available content, then `posts:connect <post-id> --release-id "<id>"` to link it. Analytics will work after connecting.
|
||||||
|
12. **`posts:settings` merges** - Only the keys you pass change; everything else on the post is preserved, so pass a partial object, not the full settings blob. Only **DRAFT/QUEUE** (unpublished) posts can be updated — published posts are rejected. Pass the **main post id**, not a comment id. Never include `__type` — the backend adds it automatically from the integration.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -781,6 +790,7 @@ postiz posts:list # List posts
|
|||||||
postiz posts:delete <id> # Delete post
|
postiz posts:delete <id> # Delete post
|
||||||
postiz posts:status <id> --status draft # Move to draft (stops workflow)
|
postiz posts:status <id> --status draft # Move to draft (stops workflow)
|
||||||
postiz posts:status <id> --status schedule # Queue draft for publishing
|
postiz posts:status <id> --status schedule # Queue draft for publishing
|
||||||
|
postiz posts:settings <id> --settings '{}' # Patch a post's settings (merged; DRAFT/QUEUE only)
|
||||||
postiz upload <file> # Upload media
|
postiz upload <file> # Upload media
|
||||||
|
|
||||||
# Analytics
|
# Analytics
|
||||||
|
|||||||
@@ -157,6 +157,13 @@ export class PostizAPI {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updatePostSettings(postId: string, settings: Record<string, any>) {
|
||||||
|
return this.request(`/public/v1/posts/${postId}/settings`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify({ settings }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async getAnalytics(integrationId: string, date: string) {
|
async getAnalytics(integrationId: string, date: string) {
|
||||||
return this.request(`/public/v1/analytics/${integrationId}?date=${encodeURIComponent(date)}`, {
|
return this.request(`/public/v1/analytics/${integrationId}?date=${encodeURIComponent(date)}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|||||||
@@ -205,6 +205,39 @@ export async function changePostStatus(args: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updatePostSettings(args: any) {
|
||||||
|
const config = getConfig();
|
||||||
|
const api = new PostizAPI(config);
|
||||||
|
|
||||||
|
if (!args.id) {
|
||||||
|
console.error('❌ Post ID is required');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args.settings) {
|
||||||
|
console.error('❌ --settings is required');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let settings: any;
|
||||||
|
try {
|
||||||
|
settings = JSON.parse(args.settings);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('❌ Failed to parse settings JSON:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await api.updatePostSettings(args.id, settings);
|
||||||
|
console.log(`✅ Post ${args.id} settings updated`);
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
return result;
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('❌ Failed to update post settings:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function deletePost(args: any) {
|
export async function deletePost(args: any) {
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
const api = new PostizAPI(config);
|
const api = new PostizAPI(config);
|
||||||
|
|||||||
+26
-1
@@ -1,6 +1,6 @@
|
|||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import { createPost, listPosts, deletePost, getMissingContent, connectPost, changePostStatus } from './commands/posts';
|
import { createPost, listPosts, deletePost, getMissingContent, connectPost, changePostStatus, updatePostSettings } from './commands/posts';
|
||||||
import { listIntegrations, listGroups, getIntegrationSettings, triggerIntegrationTool } from './commands/integrations';
|
import { listIntegrations, listGroups, getIntegrationSettings, triggerIntegrationTool } from './commands/integrations';
|
||||||
import { getAnalytics, getPostAnalytics } from './commands/analytics';
|
import { getAnalytics, getPostAnalytics } from './commands/analytics';
|
||||||
import { uploadFile } from './commands/upload';
|
import { uploadFile } from './commands/upload';
|
||||||
@@ -202,6 +202,31 @@ yargs(hideBin(process.argv))
|
|||||||
},
|
},
|
||||||
changePostStatus as any
|
changePostStatus as any
|
||||||
)
|
)
|
||||||
|
.command(
|
||||||
|
'posts:settings <id>',
|
||||||
|
'Update a post\'s provider-specific settings (merged; only unpublished draft/scheduled posts)',
|
||||||
|
(yargs: Argv) => {
|
||||||
|
return yargs
|
||||||
|
.positional('id', {
|
||||||
|
describe: 'Post ID',
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
.option('settings', {
|
||||||
|
describe: 'Partial settings as a JSON string — only the keys you pass change; do not include __type',
|
||||||
|
type: 'string',
|
||||||
|
demandOption: true,
|
||||||
|
})
|
||||||
|
.example(
|
||||||
|
'$0 posts:settings post-123 --settings \'{"content_posting_method":"DIRECT_POST"}\'',
|
||||||
|
'Switch a TikTok draft to direct publishing'
|
||||||
|
)
|
||||||
|
.example(
|
||||||
|
'$0 posts:settings post-123 --settings \'{"subreddit":[{"value":{"subreddit":"/r/selfhosted","title":"My title","type":"self","is_flair_required":true}}]}\'',
|
||||||
|
'Set a Reddit post\'s subreddit'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
updatePostSettings as any
|
||||||
|
)
|
||||||
.command(
|
.command(
|
||||||
'posts:connect <id>',
|
'posts:connect <id>',
|
||||||
'Connect a post to its published content by updating the release ID',
|
'Connect a post to its published content by updating the release ID',
|
||||||
|
|||||||
Reference in New Issue
Block a user