Features: - Add LinkedIn OAuth integration and auto-posting functionality - Add scheduler service for automated post publishing - Add metadata field to generated_posts for LinkedIn URLs - Add privacy policy page for LinkedIn API compliance - Add company management features and employee accounts - Add license key system for company registrations Fixes: - Fix timezone issues (use UTC consistently across app) - Fix datetime serialization errors in database operations - Fix scheduling timezone conversion (local time to UTC) - Fix import errors (get_database -> db) Infrastructure: - Update Docker setup to use port 8001 (avoid conflicts) - Add SSL support with nginx-proxy and Let's Encrypt - Add LinkedIn setup documentation - Add migration scripts for schema updates Services: - Add linkedin_service.py for LinkedIn API integration - Add scheduler_service.py for background job processing - Add storage_service.py for Supabase Storage - Add email_service.py improvements - Add encryption utilities for token storage Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
218 lines
6.0 KiB
Markdown
218 lines
6.0 KiB
Markdown
# LinkedIn Auto-Posting Setup Guide
|
|
|
|
This guide walks you through setting up the LinkedIn auto-posting feature.
|
|
|
|
## Overview
|
|
|
|
Employees can link their LinkedIn accounts to automatically post scheduled content directly to LinkedIn, eliminating manual copy-paste work.
|
|
|
|
## Prerequisites
|
|
|
|
1. A LinkedIn Developer account
|
|
2. Access to your server's environment variables
|
|
3. HTTPS domain (required for OAuth)
|
|
|
|
## Step 1: Create LinkedIn App
|
|
|
|
1. Go to [LinkedIn Developers](https://www.linkedin.com/developers/apps)
|
|
2. Click "Create app"
|
|
3. Fill in app details:
|
|
- **App name**: Your app name (e.g., "LinkedInWorkflow Auto-Poster")
|
|
- **LinkedIn Page**: Select your company page
|
|
- **App logo**: Upload a logo (optional)
|
|
4. Check the agreement and create the app
|
|
|
|
## Step 2: Configure OAuth Settings
|
|
|
|
1. In your app dashboard, go to the **Auth** tab
|
|
2. Add OAuth 2.0 redirect URLs:
|
|
```
|
|
https://yourdomain.com/settings/linkedin/callback
|
|
```
|
|
3. Request the following **OAuth 2.0 scopes**:
|
|
- `openid` - For user identity
|
|
- `profile` - For user profile data
|
|
- `email` - For user email
|
|
- `w_member_social` - **CRITICAL** - For posting on user's behalf
|
|
|
|
Note: `w_member_social` requires app review by LinkedIn. Submit for review if not already approved.
|
|
|
|
4. Note your **Client ID** and **Client Secret** (found in Auth tab)
|
|
|
|
## Step 3: Generate Encryption Key
|
|
|
|
Run the helper script to generate a secure encryption key:
|
|
|
|
```bash
|
|
python scripts/generate_encryption_key.py
|
|
```
|
|
|
|
Copy the generated key for the next step.
|
|
|
|
## Step 4: Configure Environment Variables
|
|
|
|
Add these variables to your `.env` file:
|
|
|
|
```env
|
|
# LinkedIn API (Custom OAuth for auto-posting)
|
|
LINKEDIN_CLIENT_ID=your_client_id_here
|
|
LINKEDIN_CLIENT_SECRET=your_client_secret_here
|
|
LINKEDIN_REDIRECT_URI=https://yourdomain.com/settings/linkedin/callback
|
|
|
|
# Token Encryption
|
|
ENCRYPTION_KEY=your_generated_fernet_key_here
|
|
```
|
|
|
|
⚠️ **Security**: Never commit these values to git! Keep them secure.
|
|
|
|
## Step 5: Run Database Migration
|
|
|
|
Apply the database migration to create the `linkedin_accounts` table:
|
|
|
|
```bash
|
|
# Using psql
|
|
psql $DATABASE_URL -f config/migrate_add_linkedin_accounts.sql
|
|
|
|
# Or via Supabase dashboard SQL editor
|
|
# Copy and paste the contents of config/migrate_add_linkedin_accounts.sql
|
|
```
|
|
|
|
## Step 6: Install Dependencies
|
|
|
|
Install the new dependencies:
|
|
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
## Step 7: Restart Application
|
|
|
|
Restart your application to load the new environment variables:
|
|
|
|
```bash
|
|
# If using systemd
|
|
sudo systemctl restart linkedinworkflow
|
|
|
|
# If using docker
|
|
docker-compose restart
|
|
|
|
# If running directly
|
|
# Stop the current process and restart
|
|
```
|
|
|
|
## Step 8: Test the Integration
|
|
|
|
1. Log in as an employee user
|
|
2. Go to **Settings** (`/settings`)
|
|
3. Click **"Mit LinkedIn verbinden"**
|
|
4. Complete the LinkedIn OAuth flow
|
|
5. Verify the account shows as connected
|
|
6. Schedule a test post
|
|
7. Wait for the scheduled time (or manually trigger the scheduler)
|
|
8. Verify the post appears on LinkedIn
|
|
|
|
## Troubleshooting
|
|
|
|
### OAuth Errors
|
|
|
|
**"Invalid redirect_uri"**
|
|
- Make sure the redirect URI in your .env matches exactly with the one configured in LinkedIn app settings
|
|
- Include the protocol (`https://`) and no trailing slash
|
|
|
|
**"Insufficient permissions"**
|
|
- Ensure `w_member_social` scope is requested and approved
|
|
- Some scopes require LinkedIn app review
|
|
|
|
### Token Errors
|
|
|
|
**"Token expired"**
|
|
- LinkedIn tokens typically last 60 days
|
|
- The system will attempt to refresh automatically
|
|
- If refresh fails, user needs to reconnect
|
|
|
|
**"Encryption error"**
|
|
- Verify `ENCRYPTION_KEY` is set in environment
|
|
- Never change the encryption key after storing tokens (or all tokens become unreadable)
|
|
|
|
### Posting Errors
|
|
|
|
**"Rate limit exceeded"**
|
|
- LinkedIn has rate limits on posting
|
|
- The system will fall back to email notification
|
|
- Wait before retrying
|
|
|
|
**"Image upload failed"**
|
|
- Check that the image URL is publicly accessible
|
|
- Supabase storage URLs must be publicly readable
|
|
- System will post without image if upload fails
|
|
|
|
## Rate Limits
|
|
|
|
LinkedIn API has the following rate limits (as of 2024):
|
|
|
|
- **Posts**: ~100 per day per user
|
|
- **API calls**: Varies by endpoint
|
|
|
|
Plan your posting schedule accordingly.
|
|
|
|
## Security Best Practices
|
|
|
|
1. **Never log tokens**: Tokens are encrypted at rest and should never be logged in plaintext
|
|
2. **HTTPS only**: OAuth requires HTTPS in production
|
|
3. **Secure cookies**: Session cookies use httponly, secure, and samesite flags
|
|
4. **Token rotation**: Encourage users to reconnect periodically
|
|
5. **Audit logging**: Monitor for suspicious activity in `linkedin_accounts.last_error`
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────┐
|
|
│ Employee │
|
|
│ (Settings Page) │
|
|
└────────┬────────┘
|
|
│ 1. OAuth Flow
|
|
▼
|
|
┌─────────────────────────┐
|
|
│ linkedin_accounts table │
|
|
│ (encrypted tokens) │
|
|
└────────┬────────────────┘
|
|
│ 2. Scheduler checks
|
|
▼
|
|
┌─────────────────────────┐
|
|
│ LinkedIn API Service │
|
|
│ (UGC Posts API) │
|
|
└─────────────────────────┘
|
|
```
|
|
|
|
## Monitoring
|
|
|
|
Key metrics to monitor:
|
|
|
|
- **Token expiry**: Check `linkedin_accounts.token_expires_at`
|
|
- **API errors**: Check `linkedin_accounts.last_error`
|
|
- **Success rate**: Compare scheduled posts vs. published posts
|
|
- **Fallback rate**: How often email fallback is used
|
|
|
|
Query to check accounts needing attention:
|
|
|
|
```sql
|
|
SELECT
|
|
user_id,
|
|
linkedin_name,
|
|
last_error,
|
|
last_error_at,
|
|
token_expires_at
|
|
FROM linkedin_accounts
|
|
WHERE
|
|
(last_error IS NOT NULL OR token_expires_at < NOW() + INTERVAL '7 days')
|
|
AND is_active = true;
|
|
```
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
1. Check logs for detailed error messages
|
|
2. Review LinkedIn API documentation
|
|
3. Verify environment configuration
|
|
4. Test with a single user account first
|