mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
2 files changed
+64
-16
No files matched your search
@@ -10,6 +10,9 @@ Preferred communication style: Simple, everyday language.
|
|||||||
|
|
||||||
## Recent Changes
|
## Recent Changes
|
||||||
|
|
||||||
|
### October 28, 2025
|
||||||
|
- **Facebook Video Story Publishing Fix**: Fixed critical bug preventing video story publication. Facebook Graph API requires a 3-phase upload process for video stories: (1) START phase - initialize upload session and get `video_id` + `upload_url`, (2) UPLOAD phase - POST video to rupload.facebook.com endpoint with `file_url` header and OAuth authorization, (3) FINISH phase - finalize and publish story with `video_id`. Previous implementation incorrectly attempted direct upload to `/video_stories` endpoint with `file_url` parameter, causing "#100 The parameter upload_phase is required" error. Fixed by implementing proper 3-phase workflow using remote URL upload method (file_url header), avoiding need for binary chunked uploads.
|
||||||
|
|
||||||
### October 17, 2025
|
### October 17, 2025
|
||||||
- **Docker Deployment Configuration**: Configured Docker Compose for private server deployment with PostgreSQL and application on same network. PostgreSQL exposed on port 4523:4523 for external access. Application runs on internal port 5555 (accessible via Nginx reverse proxy). Both PostgreSQL and app are on nginx_default external network and internal network for communication. Created comprehensive DOCKER.md documentation with setup instructions, network architecture explanation, and troubleshooting guide.
|
- **Docker Deployment Configuration**: Configured Docker Compose for private server deployment with PostgreSQL and application on same network. PostgreSQL exposed on port 4523:4523 for external access. Application runs on internal port 5555 (accessible via Nginx reverse proxy). Both PostgreSQL and app are on nginx_default external network and internal network for communication. Created comprehensive DOCKER.md documentation with setup instructions, network architecture explanation, and troubleshooting guide.
|
||||||
|
|
||||||
|
|||||||
+61
-16
@@ -361,27 +361,72 @@ export class FacebookService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async publishVideoStory(post: Post, page: SocialPage, media: Media): Promise<string> {
|
private async publishVideoStory(post: Post, page: SocialPage, media: Media): Promise<string> {
|
||||||
// Video stories can be published directly to the video_stories endpoint
|
// Video stories require a 3-phase upload process:
|
||||||
|
// 1. START phase - Initialize the upload and get upload_url
|
||||||
|
// 2. UPLOAD phase - Upload the video to upload_url using file_url header
|
||||||
|
// 3. FINISH phase - Finalize and publish the story
|
||||||
|
|
||||||
const videoUrl = media.originalUrl;
|
const videoUrl = media.originalUrl;
|
||||||
|
const accessToken = page.accessToken!;
|
||||||
const params = new URLSearchParams({
|
|
||||||
access_token: page.accessToken!,
|
// Phase 1: START - Initialize the upload
|
||||||
file_url: videoUrl,
|
const startParams = new URLSearchParams({
|
||||||
|
access_token: accessToken,
|
||||||
|
upload_phase: 'start',
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = `${this.baseUrl}/${page.pageId}/video_stories?${params.toString()}`;
|
const startUrl = `${this.baseUrl}/${page.pageId}/video_stories?${startParams.toString()}`;
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const startResponse = await fetch(startUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!startResponse.ok) {
|
||||||
const error = await response.json() as FacebookError;
|
const error = await startResponse.json() as FacebookError;
|
||||||
throw new Error(`Facebook API error publishing video story: ${error.error.message} (code: ${error.error.code})`);
|
throw new Error(`Facebook API error (START phase): ${error.error.message} (code: ${error.error.code})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json() as { success: boolean; post_id: string };
|
const startData = await startResponse.json() as { video_id: string; upload_url: string };
|
||||||
return data.post_id;
|
const videoId = startData.video_id;
|
||||||
|
const uploadUrl = startData.upload_url;
|
||||||
|
|
||||||
|
// Phase 2: UPLOAD - Upload the video using the upload_url
|
||||||
|
// IMPORTANT: file_url must be passed as a header, not a query parameter
|
||||||
|
const uploadResponse = await fetch(uploadUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `OAuth ${accessToken}`,
|
||||||
|
'file_url': videoUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadResponse.ok) {
|
||||||
|
const errorText = await uploadResponse.text();
|
||||||
|
throw new Error(`Facebook API error (UPLOAD phase): ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 3: FINISH - Finalize and publish the story
|
||||||
|
const finishParams = new URLSearchParams({
|
||||||
|
access_token: accessToken,
|
||||||
|
upload_phase: 'finish',
|
||||||
|
video_id: videoId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const finishUrl = `${this.baseUrl}/${page.pageId}/video_stories?${finishParams.toString()}`;
|
||||||
|
|
||||||
|
const finishResponse = await fetch(finishUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!finishResponse.ok) {
|
||||||
|
const error = await finishResponse.json() as FacebookError;
|
||||||
|
throw new Error(`Facebook API error (FINISH phase): ${error.error.message} (code: ${error.error.code})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finishData = await finishResponse.json() as { success: boolean; post_id?: string };
|
||||||
|
|
||||||
|
// Return the post_id from the finish response
|
||||||
|
return finishData.post_id || videoId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user