@nextsparkjs/plugin-social-media-publisher 0.1.0-beta.44 → 0.1.0-beta.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/social/connect/callback/docs.md +155 -0
- package/api/social/connect/callback/presets.ts +47 -0
- package/api/social/connect/docs.md +135 -0
- package/api/social/connect/presets.ts +48 -0
- package/api/social/disconnect/docs.md +119 -0
- package/api/social/disconnect/presets.ts +34 -0
- package/api/social/publish/docs.md +169 -0
- package/api/social/publish/presets.ts +78 -0
- package/package.json +2 -2
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# OAuth Callback API
|
|
2
|
+
|
|
3
|
+
Handles OAuth redirect from Facebook after user authorization.
|
|
4
|
+
|
|
5
|
+
## Endpoint
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
GET /api/v1/plugin/social-media-publisher/social/connect/callback
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Description
|
|
12
|
+
|
|
13
|
+
This endpoint receives the OAuth redirect from Facebook, exchanges the authorization code for access tokens, encrypts them, and stores connected accounts in the database.
|
|
14
|
+
|
|
15
|
+
## Query Parameters
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Description |
|
|
18
|
+
|-----------|------|-------------|
|
|
19
|
+
| `code` | string | Authorization code from Facebook |
|
|
20
|
+
| `state` | string | CSRF token with embedded clientId, platform, mode |
|
|
21
|
+
| `error` | string | (Optional) Error if user denied permission |
|
|
22
|
+
| `error_description` | string | (Optional) Error description |
|
|
23
|
+
|
|
24
|
+
### State Format
|
|
25
|
+
|
|
26
|
+
The state parameter contains embedded values:
|
|
27
|
+
```
|
|
28
|
+
{randomState}&platform={platform}&clientId={clientId}&mode={mode}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Response
|
|
32
|
+
|
|
33
|
+
This endpoint returns HTML pages that communicate with the parent window via postMessage.
|
|
34
|
+
|
|
35
|
+
### Success Response
|
|
36
|
+
|
|
37
|
+
Returns HTML page that:
|
|
38
|
+
1. Displays success message
|
|
39
|
+
2. Sends postMessage to opener window:
|
|
40
|
+
```javascript
|
|
41
|
+
{
|
|
42
|
+
type: 'oauth-success',
|
|
43
|
+
platform: 'instagram_business',
|
|
44
|
+
connectedCount: 1
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
3. Closes popup after 2 seconds
|
|
48
|
+
|
|
49
|
+
### Preview Mode Response
|
|
50
|
+
|
|
51
|
+
When `mode=preview` in state, returns account data without saving:
|
|
52
|
+
```javascript
|
|
53
|
+
{
|
|
54
|
+
type: 'oauth-preview',
|
|
55
|
+
platform: 'instagram_business',
|
|
56
|
+
accounts: [
|
|
57
|
+
{
|
|
58
|
+
platform: 'instagram_business',
|
|
59
|
+
platformAccountId: 'ig_123',
|
|
60
|
+
username: '@myaccount',
|
|
61
|
+
accessToken: '...',
|
|
62
|
+
tokenExpiresAt: '2024-03-15T10:30:00Z',
|
|
63
|
+
permissions: ['instagram_basic', 'instagram_content_publish'],
|
|
64
|
+
accountMetadata: {
|
|
65
|
+
followersCount: 5000,
|
|
66
|
+
profilePictureUrl: '...'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Error Response
|
|
74
|
+
|
|
75
|
+
Returns HTML page that:
|
|
76
|
+
1. Displays error message
|
|
77
|
+
2. Sends postMessage to opener:
|
|
78
|
+
```javascript
|
|
79
|
+
{
|
|
80
|
+
type: 'oauth-error',
|
|
81
|
+
error: 'user_cancelled',
|
|
82
|
+
errorDescription: 'You cancelled the authorization process'
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
3. Closes popup after 3 seconds
|
|
86
|
+
|
|
87
|
+
## Error Types
|
|
88
|
+
|
|
89
|
+
| Error Type | Description |
|
|
90
|
+
|------------|-------------|
|
|
91
|
+
| `user_cancelled` | User cancelled OAuth flow |
|
|
92
|
+
| `app_not_authorized` | App not authorized by Facebook |
|
|
93
|
+
| `meta_server_error` | Facebook server temporarily unavailable |
|
|
94
|
+
| `missing_client` | Client ID not in state |
|
|
95
|
+
| `missing_code` | Authorization code not provided |
|
|
96
|
+
| `no_instagram_accounts` | No Instagram Business accounts found |
|
|
97
|
+
| `callback_exception` | Server error during processing |
|
|
98
|
+
|
|
99
|
+
## Account Types
|
|
100
|
+
|
|
101
|
+
### Instagram Business
|
|
102
|
+
|
|
103
|
+
Fetches Instagram accounts linked to Facebook Pages:
|
|
104
|
+
- Requires Facebook Page with linked Instagram Business Account
|
|
105
|
+
- Returns profile picture, followers count, media count
|
|
106
|
+
- Uses Page access token for Instagram Graph API
|
|
107
|
+
|
|
108
|
+
### Facebook Page
|
|
109
|
+
|
|
110
|
+
Fetches Facebook Pages the user manages:
|
|
111
|
+
- Returns page name, category, fan count
|
|
112
|
+
- Uses Page access token for publishing
|
|
113
|
+
|
|
114
|
+
## Token Handling
|
|
115
|
+
|
|
116
|
+
1. Authorization code exchanged for user access token
|
|
117
|
+
2. User token exchanged for long-lived Page tokens (60 days)
|
|
118
|
+
3. Tokens encrypted with AES-256-GCM before storage
|
|
119
|
+
4. Expiration tracked for auto-refresh on publish
|
|
120
|
+
|
|
121
|
+
## Database Storage
|
|
122
|
+
|
|
123
|
+
Accounts stored in `clients_social_platforms` table:
|
|
124
|
+
- Linked to client via `parentId`
|
|
125
|
+
- Upsert on `platformAccountId` conflict
|
|
126
|
+
- Audit log entry created for each connection
|
|
127
|
+
|
|
128
|
+
## Frontend Integration
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// Open OAuth popup
|
|
132
|
+
const popup = window.open(
|
|
133
|
+
'/api/v1/plugin/social-media-publisher/social/connect?platform=instagram_business&clientId=xxx',
|
|
134
|
+
'oauth',
|
|
135
|
+
'width=600,height=700'
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
// Listen for callback
|
|
139
|
+
window.addEventListener('message', (event) => {
|
|
140
|
+
if (event.origin !== window.location.origin) return
|
|
141
|
+
|
|
142
|
+
if (event.data.type === 'oauth-success') {
|
|
143
|
+
console.log(`Connected ${event.data.connectedCount} accounts`)
|
|
144
|
+
refreshAccountList()
|
|
145
|
+
} else if (event.data.type === 'oauth-error') {
|
|
146
|
+
console.error(event.data.errorDescription)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Related APIs
|
|
152
|
+
|
|
153
|
+
- [Connect](/api/v1/plugin/social-media-publisher/social/connect) - Initiate OAuth flow
|
|
154
|
+
- [Publish](/api/v1/plugin/social-media-publisher/social/publish) - Publish content
|
|
155
|
+
- [Disconnect](/api/v1/plugin/social-media-publisher/social/disconnect) - Disconnect account
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Presets for OAuth Callback
|
|
3
|
+
*
|
|
4
|
+
* OAuth redirect handler - typically not called directly
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineApiEndpoint } from '@nextsparkjs/core/types/api-presets'
|
|
8
|
+
|
|
9
|
+
export default defineApiEndpoint({
|
|
10
|
+
endpoint: '/api/v1/plugin/social-media-publisher/social/connect/callback',
|
|
11
|
+
summary: 'OAuth callback handler for social media connections (browser redirect)',
|
|
12
|
+
presets: [
|
|
13
|
+
{
|
|
14
|
+
id: 'callback-success',
|
|
15
|
+
title: 'Callback with Code',
|
|
16
|
+
description: 'OAuth callback with authorization code (browser redirect)',
|
|
17
|
+
method: 'GET',
|
|
18
|
+
params: {
|
|
19
|
+
code: '{{authorizationCode}}',
|
|
20
|
+
state: '{{state}}&platform=instagram_business&clientId={{clientId}}'
|
|
21
|
+
},
|
|
22
|
+
tags: ['oauth', 'callback']
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'callback-preview',
|
|
26
|
+
title: 'Callback Preview Mode',
|
|
27
|
+
description: 'OAuth callback in preview mode (returns data without saving)',
|
|
28
|
+
method: 'GET',
|
|
29
|
+
params: {
|
|
30
|
+
code: '{{authorizationCode}}',
|
|
31
|
+
state: '{{state}}&platform=instagram_business&clientId={{clientId}}&mode=preview'
|
|
32
|
+
},
|
|
33
|
+
tags: ['oauth', 'callback', 'preview']
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'callback-error',
|
|
37
|
+
title: 'Callback with Error',
|
|
38
|
+
description: 'OAuth callback when user denies permission',
|
|
39
|
+
method: 'GET',
|
|
40
|
+
params: {
|
|
41
|
+
error: 'access_denied',
|
|
42
|
+
error_description: 'User cancelled the authorization'
|
|
43
|
+
},
|
|
44
|
+
tags: ['oauth', 'callback', 'error']
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
})
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Social Media Connect API
|
|
2
|
+
|
|
3
|
+
Initiate OAuth flow and connect Facebook Pages or Instagram Business accounts.
|
|
4
|
+
|
|
5
|
+
## Endpoints
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
GET /api/v1/plugin/social-media-publisher/social/connect
|
|
9
|
+
POST /api/v1/plugin/social-media-publisher/social/connect (deprecated)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## GET - Initiate OAuth Flow
|
|
13
|
+
|
|
14
|
+
Redirects user to Facebook OAuth authorization page to connect their social media accounts.
|
|
15
|
+
|
|
16
|
+
### Query Parameters
|
|
17
|
+
|
|
18
|
+
| Parameter | Type | Required | Default | Description |
|
|
19
|
+
|-----------|------|----------|---------|-------------|
|
|
20
|
+
| `platform` | string | No | instagram_business | Platform to connect |
|
|
21
|
+
| `clientId` | string | Yes | - | Client ID to associate accounts with |
|
|
22
|
+
| `state` | string | No | - | Random state for CSRF protection |
|
|
23
|
+
| `mode` | string | No | save | "preview" returns data without saving |
|
|
24
|
+
|
|
25
|
+
### Supported Platforms
|
|
26
|
+
|
|
27
|
+
- `instagram_business` - Instagram Business Account (requires linked Facebook Page)
|
|
28
|
+
- `facebook_page` - Facebook Page
|
|
29
|
+
|
|
30
|
+
### Example Request
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
GET /api/v1/plugin/social-media-publisher/social/connect?platform=instagram_business&clientId=client_123&state=abc123
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Response
|
|
37
|
+
|
|
38
|
+
Redirects to Facebook OAuth authorization URL:
|
|
39
|
+
```
|
|
40
|
+
https://www.facebook.com/v18.0/dialog/oauth?client_id=...&redirect_uri=...&scope=...&state=...
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### OAuth Scopes Requested
|
|
44
|
+
|
|
45
|
+
**For Instagram Business:**
|
|
46
|
+
- `instagram_basic` - Basic account info
|
|
47
|
+
- `instagram_content_publish` - Publish content
|
|
48
|
+
- `instagram_manage_comments` - Manage comments
|
|
49
|
+
- `pages_show_list` - List Facebook Pages
|
|
50
|
+
- `pages_read_engagement` - Read engagement metrics
|
|
51
|
+
|
|
52
|
+
**For Facebook Pages:**
|
|
53
|
+
- `pages_show_list` - List Facebook Pages
|
|
54
|
+
- `pages_manage_posts` - Publish posts
|
|
55
|
+
- `pages_read_engagement` - Read engagement
|
|
56
|
+
|
|
57
|
+
## POST - Handle OAuth Callback (Deprecated)
|
|
58
|
+
|
|
59
|
+
Use `/api/v1/plugin/social-media-publisher/social/connect/callback` instead.
|
|
60
|
+
|
|
61
|
+
This endpoint receives the authorization code and exchanges it for access tokens.
|
|
62
|
+
|
|
63
|
+
### Request Body
|
|
64
|
+
|
|
65
|
+
| Field | Type | Required | Description |
|
|
66
|
+
|-------|------|----------|-------------|
|
|
67
|
+
| `code` | string | Yes | Authorization code from OAuth |
|
|
68
|
+
| `state` | string | No | State for CSRF validation |
|
|
69
|
+
| `platform` | string | Yes | Platform type |
|
|
70
|
+
|
|
71
|
+
### Success Response (200)
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"success": true,
|
|
76
|
+
"message": "Successfully connected 1 instagram_business account(s)",
|
|
77
|
+
"accounts": [
|
|
78
|
+
{
|
|
79
|
+
"id": "acc_123",
|
|
80
|
+
"platform": "instagram_business",
|
|
81
|
+
"accountName": "@myaccount",
|
|
82
|
+
"permissions": ["instagram_basic", "instagram_content_publish"],
|
|
83
|
+
"metadata": {
|
|
84
|
+
"followersCount": 5000,
|
|
85
|
+
"mediaCount": 150
|
|
86
|
+
},
|
|
87
|
+
"connectedAt": "2024-01-15T10:30:00Z"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Error Responses
|
|
94
|
+
|
|
95
|
+
| Status | Error | Description |
|
|
96
|
+
|--------|-------|-------------|
|
|
97
|
+
| 400 | Missing clientId | clientId parameter required |
|
|
98
|
+
| 400 | Invalid platform | Platform must be facebook_page or instagram_business |
|
|
99
|
+
| 401 | Authentication required | User not logged in |
|
|
100
|
+
| 404 | No Instagram accounts | No Instagram Business accounts found |
|
|
101
|
+
| 500 | OAuth failed | Failed to exchange code for token |
|
|
102
|
+
|
|
103
|
+
## Setup
|
|
104
|
+
|
|
105
|
+
Configure Facebook OAuth credentials:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# plugins/social-media-publisher/.env
|
|
109
|
+
FACEBOOK_CLIENT_ID=your_app_id
|
|
110
|
+
FACEBOOK_CLIENT_SECRET=your_app_secret
|
|
111
|
+
NEXT_PUBLIC_APP_URL=https://your-app.com
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## OAuth Flow
|
|
115
|
+
|
|
116
|
+
1. Frontend calls GET with clientId and platform
|
|
117
|
+
2. User redirected to Facebook OAuth
|
|
118
|
+
3. User authorizes requested permissions
|
|
119
|
+
4. Facebook redirects to callback endpoint
|
|
120
|
+
5. Callback exchanges code for tokens
|
|
121
|
+
6. Tokens encrypted and stored in database
|
|
122
|
+
7. Popup closes and parent window notified
|
|
123
|
+
|
|
124
|
+
## Security
|
|
125
|
+
|
|
126
|
+
- All access tokens are encrypted with AES-256-GCM
|
|
127
|
+
- CSRF protection via state parameter
|
|
128
|
+
- Token expiration tracked for auto-refresh
|
|
129
|
+
- Audit logs created for all connections
|
|
130
|
+
|
|
131
|
+
## Related APIs
|
|
132
|
+
|
|
133
|
+
- [OAuth Callback](/api/v1/plugin/social-media-publisher/social/connect/callback) - OAuth redirect handler
|
|
134
|
+
- [Publish](/api/v1/plugin/social-media-publisher/social/publish) - Publish content
|
|
135
|
+
- [Disconnect](/api/v1/plugin/social-media-publisher/social/disconnect) - Disconnect account
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Presets for Social Media Connect
|
|
3
|
+
*
|
|
4
|
+
* OAuth connection for Facebook Pages and Instagram Business
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineApiEndpoint } from '@nextsparkjs/core/types/api-presets'
|
|
8
|
+
|
|
9
|
+
export default defineApiEndpoint({
|
|
10
|
+
endpoint: '/api/v1/plugin/social-media-publisher/social/connect',
|
|
11
|
+
summary: 'Connect Facebook Pages and Instagram Business accounts via OAuth',
|
|
12
|
+
presets: [
|
|
13
|
+
{
|
|
14
|
+
id: 'connect-instagram',
|
|
15
|
+
title: 'Connect Instagram',
|
|
16
|
+
description: 'Initiate OAuth for Instagram Business',
|
|
17
|
+
method: 'GET',
|
|
18
|
+
params: {
|
|
19
|
+
platform: 'instagram_business',
|
|
20
|
+
clientId: '{{clientId}}'
|
|
21
|
+
},
|
|
22
|
+
tags: ['oauth', 'instagram']
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'connect-facebook',
|
|
26
|
+
title: 'Connect Facebook',
|
|
27
|
+
description: 'Initiate OAuth for Facebook Page',
|
|
28
|
+
method: 'GET',
|
|
29
|
+
params: {
|
|
30
|
+
platform: 'facebook_page',
|
|
31
|
+
clientId: '{{clientId}}'
|
|
32
|
+
},
|
|
33
|
+
tags: ['oauth', 'facebook']
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'preview-mode',
|
|
37
|
+
title: 'Preview Mode',
|
|
38
|
+
description: 'Get account data without saving',
|
|
39
|
+
method: 'GET',
|
|
40
|
+
params: {
|
|
41
|
+
platform: '{{platform}}',
|
|
42
|
+
clientId: '{{clientId}}',
|
|
43
|
+
mode: 'preview'
|
|
44
|
+
},
|
|
45
|
+
tags: ['oauth', 'preview']
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
})
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Social Media Disconnect API
|
|
2
|
+
|
|
3
|
+
Disconnect (deactivate) a social media account.
|
|
4
|
+
|
|
5
|
+
## Endpoint
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
POST /api/v1/plugin/social-media-publisher/social/disconnect
|
|
9
|
+
DELETE /api/v1/plugin/social-media-publisher/social/disconnect/:accountId
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Authentication
|
|
13
|
+
|
|
14
|
+
Requires dual authentication (session or API key).
|
|
15
|
+
|
|
16
|
+
**Headers:**
|
|
17
|
+
```
|
|
18
|
+
Authorization: Bearer <session-token>
|
|
19
|
+
# OR
|
|
20
|
+
x-api-key: <api-key>
|
|
21
|
+
x-team-id: <team-id>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## POST - Disconnect Account
|
|
25
|
+
|
|
26
|
+
Marks a social media account as inactive (soft delete).
|
|
27
|
+
|
|
28
|
+
### Request Body
|
|
29
|
+
|
|
30
|
+
| Field | Type | Required | Description |
|
|
31
|
+
|-------|------|----------|-------------|
|
|
32
|
+
| `accountId` | string | Yes | Social account UUID |
|
|
33
|
+
|
|
34
|
+
### Example Request
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"accountId": "123e4567-e89b-12d3-a456-426614174000"
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Success Response (200)
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"success": true,
|
|
47
|
+
"accountId": "123e4567-e89b-12d3-a456-426614174000",
|
|
48
|
+
"message": "Successfully disconnected @myaccount (instagram_business)"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## DELETE - Disconnect Account (Alternative)
|
|
53
|
+
|
|
54
|
+
Alternative method using account ID in URL path.
|
|
55
|
+
|
|
56
|
+
### Example Request
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
DELETE /api/v1/plugin/social-media-publisher/social/disconnect/123e4567-e89b-12d3-a456-426614174000
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Success Response (200)
|
|
63
|
+
|
|
64
|
+
Same as POST method.
|
|
65
|
+
|
|
66
|
+
## Error Responses
|
|
67
|
+
|
|
68
|
+
| Status | Error | Description |
|
|
69
|
+
|--------|-------|-------------|
|
|
70
|
+
| 400 | Validation failed | Invalid request body |
|
|
71
|
+
| 400 | Invalid account ID format | UUID format invalid |
|
|
72
|
+
| 400 | Account already disconnected | Account is already inactive |
|
|
73
|
+
| 401 | Authentication required | Not authenticated |
|
|
74
|
+
| 404 | Account not found | Account doesn't exist or no access |
|
|
75
|
+
| 500 | Failed to disconnect | Server error during operation |
|
|
76
|
+
|
|
77
|
+
### Example Error
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"error": "Account already disconnected",
|
|
82
|
+
"message": "This account is already inactive."
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Behavior
|
|
87
|
+
|
|
88
|
+
1. **Soft Delete**: Account is marked as `isActive = false`, not deleted
|
|
89
|
+
2. **Tokens Preserved**: Encrypted tokens remain in database for potential reconnection
|
|
90
|
+
3. **Audit Log**: Disconnect event logged with timestamp and user info
|
|
91
|
+
4. **Idempotent**: Returns error if already disconnected
|
|
92
|
+
|
|
93
|
+
## Audit Logging
|
|
94
|
+
|
|
95
|
+
Disconnect events are logged:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"action": "account_disconnected",
|
|
100
|
+
"details": {
|
|
101
|
+
"platform": "instagram_business",
|
|
102
|
+
"accountName": "@myaccount",
|
|
103
|
+
"success": true,
|
|
104
|
+
"disconnectedAt": "2024-01-15T10:30:00Z"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Reconnecting
|
|
110
|
+
|
|
111
|
+
To reconnect a disconnected account:
|
|
112
|
+
1. Use the [Connect](/api/v1/plugin/social-media-publisher/social/connect) endpoint
|
|
113
|
+
2. OAuth flow will update the existing record
|
|
114
|
+
3. Account becomes active again with fresh tokens
|
|
115
|
+
|
|
116
|
+
## Related APIs
|
|
117
|
+
|
|
118
|
+
- [Connect](/api/v1/plugin/social-media-publisher/social/connect) - Connect accounts
|
|
119
|
+
- [Publish](/api/v1/plugin/social-media-publisher/social/publish) - Publish content
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Presets for Social Media Disconnect
|
|
3
|
+
*
|
|
4
|
+
* Disconnect (deactivate) social media accounts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineApiEndpoint } from '@nextsparkjs/core/types/api-presets'
|
|
8
|
+
|
|
9
|
+
export default defineApiEndpoint({
|
|
10
|
+
endpoint: '/api/v1/plugin/social-media-publisher/social/disconnect',
|
|
11
|
+
summary: 'Disconnect and deactivate social media accounts',
|
|
12
|
+
presets: [
|
|
13
|
+
{
|
|
14
|
+
id: 'disconnect-post',
|
|
15
|
+
title: 'Disconnect Account (POST)',
|
|
16
|
+
description: 'Disconnect account using POST method',
|
|
17
|
+
method: 'POST',
|
|
18
|
+
payload: {
|
|
19
|
+
accountId: '{{accountId}}'
|
|
20
|
+
},
|
|
21
|
+
tags: ['write', 'disconnect']
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'disconnect-delete',
|
|
25
|
+
title: 'Disconnect Account (DELETE)',
|
|
26
|
+
description: 'Disconnect account using DELETE method with path param',
|
|
27
|
+
method: 'DELETE',
|
|
28
|
+
pathParams: {
|
|
29
|
+
accountId: '{{accountId}}'
|
|
30
|
+
},
|
|
31
|
+
tags: ['write', 'disconnect']
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
})
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Social Media Publish API
|
|
2
|
+
|
|
3
|
+
Publish content to connected Instagram Business or Facebook Page accounts.
|
|
4
|
+
|
|
5
|
+
## Endpoint
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
POST /api/v1/plugin/social-media-publisher/social/publish
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Authentication
|
|
12
|
+
|
|
13
|
+
Requires dual authentication (session or API key).
|
|
14
|
+
|
|
15
|
+
**Headers:**
|
|
16
|
+
```
|
|
17
|
+
Authorization: Bearer <session-token>
|
|
18
|
+
# OR
|
|
19
|
+
x-api-key: <api-key>
|
|
20
|
+
x-team-id: <team-id>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Request Body
|
|
24
|
+
|
|
25
|
+
| Field | Type | Required | Description |
|
|
26
|
+
|-------|------|----------|-------------|
|
|
27
|
+
| `accountId` | string | Yes | Social platform account ID |
|
|
28
|
+
| `platform` | string | Yes | Platform type (instagram_business, facebook_page) |
|
|
29
|
+
| `imageUrl` | string | No* | Single image URL |
|
|
30
|
+
| `imageUrls` | string[] | No* | Multiple image URLs for carousel (2-10) |
|
|
31
|
+
| `caption` | string | No | Post caption/message |
|
|
32
|
+
|
|
33
|
+
*Instagram requires at least one image. Facebook supports text-only posts.
|
|
34
|
+
|
|
35
|
+
### Example: Single Image Post
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"accountId": "acc_123",
|
|
40
|
+
"platform": "instagram_business",
|
|
41
|
+
"imageUrl": "https://example.com/photo.jpg",
|
|
42
|
+
"caption": "Check out our new product! #launch"
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Example: Carousel Post
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"accountId": "acc_123",
|
|
51
|
+
"platform": "instagram_business",
|
|
52
|
+
"imageUrls": [
|
|
53
|
+
"https://example.com/photo1.jpg",
|
|
54
|
+
"https://example.com/photo2.jpg",
|
|
55
|
+
"https://example.com/photo3.jpg"
|
|
56
|
+
],
|
|
57
|
+
"caption": "Swipe through our collection!"
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Example: Text-Only Post (Facebook)
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"accountId": "acc_456",
|
|
66
|
+
"platform": "facebook_page",
|
|
67
|
+
"caption": "Exciting news coming soon!"
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Success Response (200)
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"success": true,
|
|
76
|
+
"platform": "instagram_business",
|
|
77
|
+
"postId": "17895695668004550",
|
|
78
|
+
"postUrl": "https://www.instagram.com/p/ABC123/",
|
|
79
|
+
"message": "Successfully published to instagram_business"
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Error Responses
|
|
84
|
+
|
|
85
|
+
| Status | Error | Description |
|
|
86
|
+
|--------|-------|-------------|
|
|
87
|
+
| 400 | Validation failed | Invalid request body |
|
|
88
|
+
| 400 | Platform requires image | Instagram requires at least one image |
|
|
89
|
+
| 400 | Invalid image URL | Image URL format invalid |
|
|
90
|
+
| 400 | Invalid caption | Caption exceeds platform limit |
|
|
91
|
+
| 401 | Authentication required | Not authenticated |
|
|
92
|
+
| 403 | Account inactive | Account disconnected, needs reconnection |
|
|
93
|
+
| 403 | Token expired | Token refresh failed, reconnect account |
|
|
94
|
+
| 404 | Account not found | Account doesn't exist or no access |
|
|
95
|
+
| 500 | Publishing failed | API error during publishing |
|
|
96
|
+
|
|
97
|
+
### Example Error
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"error": "Publishing failed",
|
|
102
|
+
"platform": "instagram_business",
|
|
103
|
+
"details": "Invalid image URL",
|
|
104
|
+
"errorDetails": { ... }
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Platform Requirements
|
|
109
|
+
|
|
110
|
+
### Instagram Business
|
|
111
|
+
|
|
112
|
+
- **Image Required**: Yes (single or carousel)
|
|
113
|
+
- **Carousel**: 2-10 images
|
|
114
|
+
- **Caption Limit**: 2,200 characters
|
|
115
|
+
- **Image Formats**: JPEG, PNG
|
|
116
|
+
- **Image Size**: Max 8MB per image
|
|
117
|
+
- **Aspect Ratio**: 4:5 to 1.91:1
|
|
118
|
+
|
|
119
|
+
### Facebook Page
|
|
120
|
+
|
|
121
|
+
- **Image Required**: No (text-only allowed)
|
|
122
|
+
- **Carousel**: 2-10 images
|
|
123
|
+
- **Caption Limit**: 63,206 characters
|
|
124
|
+
- **Image Formats**: JPEG, PNG, GIF
|
|
125
|
+
- **Image Size**: Max 4MB per image
|
|
126
|
+
|
|
127
|
+
## Token Auto-Refresh
|
|
128
|
+
|
|
129
|
+
The endpoint automatically handles token refresh:
|
|
130
|
+
|
|
131
|
+
1. Checks token expiration before publishing
|
|
132
|
+
2. If expiring within 10 minutes, refreshes token
|
|
133
|
+
3. Uses Meta's fb_exchange_token endpoint
|
|
134
|
+
4. Re-encrypts new token in database
|
|
135
|
+
5. Blocks publish if refresh fails (prevents wasted API calls)
|
|
136
|
+
|
|
137
|
+
## Audit Logging
|
|
138
|
+
|
|
139
|
+
All publish attempts are logged to `audit_logs` table:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"action": "post_published",
|
|
144
|
+
"details": {
|
|
145
|
+
"platform": "instagram_business",
|
|
146
|
+
"accountName": "@myaccount",
|
|
147
|
+
"success": true,
|
|
148
|
+
"postId": "17895695668004550",
|
|
149
|
+
"postType": "carousel",
|
|
150
|
+
"imageCount": 3,
|
|
151
|
+
"caption": "...",
|
|
152
|
+
"publishedAt": "2024-01-15T10:30:00Z"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Image URL Requirements
|
|
158
|
+
|
|
159
|
+
Image URLs must be:
|
|
160
|
+
- Publicly accessible
|
|
161
|
+
- HTTPS protocol
|
|
162
|
+
- Valid image extension or content-type
|
|
163
|
+
- Not behind authentication
|
|
164
|
+
- Reachable from Meta's servers
|
|
165
|
+
|
|
166
|
+
## Related APIs
|
|
167
|
+
|
|
168
|
+
- [Connect](/api/v1/plugin/social-media-publisher/social/connect) - Connect accounts
|
|
169
|
+
- [Disconnect](/api/v1/plugin/social-media-publisher/social/disconnect) - Disconnect accounts
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Presets for Social Media Publish
|
|
3
|
+
*
|
|
4
|
+
* Publish content to Facebook Pages and Instagram Business
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineApiEndpoint } from '@nextsparkjs/core/types/api-presets'
|
|
8
|
+
|
|
9
|
+
export default defineApiEndpoint({
|
|
10
|
+
endpoint: '/api/v1/plugin/social-media-publisher/social/publish',
|
|
11
|
+
summary: 'Publish photos, carousels, and text posts to social media',
|
|
12
|
+
presets: [
|
|
13
|
+
{
|
|
14
|
+
id: 'instagram-photo',
|
|
15
|
+
title: 'Instagram Photo',
|
|
16
|
+
description: 'Publish single photo to Instagram',
|
|
17
|
+
method: 'POST',
|
|
18
|
+
payload: {
|
|
19
|
+
accountId: '{{accountId}}',
|
|
20
|
+
platform: 'instagram_business',
|
|
21
|
+
imageUrl: '{{imageUrl}}',
|
|
22
|
+
caption: 'Check out our latest post!'
|
|
23
|
+
},
|
|
24
|
+
tags: ['write', 'instagram', 'photo']
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'instagram-carousel',
|
|
28
|
+
title: 'Instagram Carousel',
|
|
29
|
+
description: 'Publish multi-image carousel to Instagram',
|
|
30
|
+
method: 'POST',
|
|
31
|
+
payload: {
|
|
32
|
+
accountId: '{{accountId}}',
|
|
33
|
+
platform: 'instagram_business',
|
|
34
|
+
imageUrls: ['{{imageUrl1}}', '{{imageUrl2}}', '{{imageUrl3}}'],
|
|
35
|
+
caption: 'Swipe through our collection!'
|
|
36
|
+
},
|
|
37
|
+
tags: ['write', 'instagram', 'carousel']
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'facebook-photo',
|
|
41
|
+
title: 'Facebook Photo',
|
|
42
|
+
description: 'Publish photo to Facebook Page',
|
|
43
|
+
method: 'POST',
|
|
44
|
+
payload: {
|
|
45
|
+
accountId: '{{accountId}}',
|
|
46
|
+
platform: 'facebook_page',
|
|
47
|
+
imageUrl: '{{imageUrl}}',
|
|
48
|
+
caption: 'New post on our page!'
|
|
49
|
+
},
|
|
50
|
+
tags: ['write', 'facebook', 'photo']
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'facebook-text',
|
|
54
|
+
title: 'Facebook Text Post',
|
|
55
|
+
description: 'Publish text-only post to Facebook Page',
|
|
56
|
+
method: 'POST',
|
|
57
|
+
payload: {
|
|
58
|
+
accountId: '{{accountId}}',
|
|
59
|
+
platform: 'facebook_page',
|
|
60
|
+
caption: 'Exciting news coming soon!'
|
|
61
|
+
},
|
|
62
|
+
tags: ['write', 'facebook', 'text']
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'facebook-carousel',
|
|
66
|
+
title: 'Facebook Carousel',
|
|
67
|
+
description: 'Publish multi-image carousel to Facebook Page',
|
|
68
|
+
method: 'POST',
|
|
69
|
+
payload: {
|
|
70
|
+
accountId: '{{accountId}}',
|
|
71
|
+
platform: 'facebook_page',
|
|
72
|
+
imageUrls: ['{{imageUrl1}}', '{{imageUrl2}}'],
|
|
73
|
+
caption: 'Check out our photo gallery!'
|
|
74
|
+
},
|
|
75
|
+
tags: ['write', 'facebook', 'carousel']
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextsparkjs/plugin-social-media-publisher",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.46",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./plugin.config.ts",
|
|
6
6
|
"requiredPlugins": [],
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"react": "^19.0.0",
|
|
11
11
|
"react-dom": "^19.0.0",
|
|
12
12
|
"zod": "^4.0.0",
|
|
13
|
-
"@nextsparkjs/core": "0.1.0-beta.
|
|
13
|
+
"@nextsparkjs/core": "0.1.0-beta.46"
|
|
14
14
|
},
|
|
15
15
|
"nextspark": {
|
|
16
16
|
"type": "plugin",
|