onairos 2.0.7 → 2.0.9

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.
@@ -0,0 +1,168 @@
1
+ import React, { Component } from 'react';
2
+
3
+ class PinterestConnector extends Component {
4
+ constructor(props) {
5
+ super(props);
6
+ this.state = {
7
+ connected: false,
8
+ open: false,
9
+ isConnecting: false,
10
+ };
11
+ this.handleClose = this.handleClose.bind(this);
12
+ this.handleOpen = this.handleOpen.bind(this);
13
+ this.pinterestConnect = this.pinterestConnect.bind(this);
14
+ this.setConnected = this.setConnected.bind(this);
15
+ this.setDisconnected = this.setDisconnected.bind(this);
16
+ }
17
+
18
+ setConnected() {
19
+ this.setState({ connected: true });
20
+ if (this.props.onConnectionChange) {
21
+ this.props.onConnectionChange('Pinterest', true);
22
+ }
23
+ this.handleClose();
24
+ }
25
+
26
+ setDisconnected() {
27
+ this.updateConnections('Remove', 'Pinterest').then(() => {
28
+ this.setState({ connected: false });
29
+ if (this.props.onConnectionChange) {
30
+ this.props.onConnectionChange('Pinterest', false);
31
+ }
32
+ this.handleClose();
33
+ }).catch((error) => {
34
+ console.error('Error removing Pinterest connection:', error);
35
+ });
36
+ }
37
+
38
+ async updateConnections(updateType, newConnection) {
39
+ const jsonData = {
40
+ session: {
41
+ username: localStorage.getItem("username") || this.props.username
42
+ },
43
+ updateType,
44
+ newConnection
45
+ };
46
+
47
+ try {
48
+ const response = await fetch('https://api2.onairos.uk/connections/update', {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ body: JSON.stringify(jsonData),
54
+ });
55
+ return await response.json();
56
+ } catch (error) {
57
+ console.error('UpdateConnections error:', error);
58
+ throw error;
59
+ }
60
+ }
61
+
62
+ handleOpen() {
63
+ this.setState({ open: true });
64
+ }
65
+
66
+ handleClose() {
67
+ this.setState({ open: false });
68
+ if (this.props.onClose) {
69
+ this.props.onClose();
70
+ }
71
+ }
72
+
73
+ async pinterestConnect() {
74
+ this.setState({ isConnecting: true });
75
+
76
+ const jsonData = {
77
+ session: {
78
+ username: localStorage.getItem("username") || this.props.username
79
+ },
80
+ };
81
+
82
+ try {
83
+ const response = await fetch('https://api2.onairos.uk/pinterest/authorize', {
84
+ method: 'POST',
85
+ headers: {
86
+ 'Content-Type': 'application/json',
87
+ },
88
+ body: JSON.stringify(jsonData),
89
+ });
90
+
91
+ const result = await response.json();
92
+
93
+ if (result.pinterestURL) {
94
+ window.location.href = result.pinterestURL;
95
+ } else {
96
+ console.error('No Pinterest URL received');
97
+ this.setState({ isConnecting: false });
98
+ }
99
+ } catch (error) {
100
+ console.error('Pinterest connection error:', error);
101
+ this.setState({ isConnecting: false });
102
+ }
103
+ }
104
+
105
+ render() {
106
+ const { open = this.props.open || this.state.open } = this.props;
107
+
108
+ if (!open) return null;
109
+
110
+ return (
111
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
112
+ <div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-hidden">
113
+ <div className="p-6">
114
+ <h2 className="text-xl font-bold text-gray-900 mb-4">
115
+ Grant Onairos Access to your Pinterest Account?
116
+ </h2>
117
+
118
+ <div className="space-y-4 text-gray-700">
119
+ <p>
120
+ Grant Permission to your Pinterest Account, so we can build your Data Models.
121
+ </p>
122
+
123
+ <div>
124
+ <p className="font-medium mb-2">We will access your Pinterest:</p>
125
+ <ul className="list-disc ml-6 space-y-1">
126
+ <li>Basic Profile Information</li>
127
+ <li>Boards and Pins</li>
128
+ <li>Saved and Liked Pins</li>
129
+ <li>Following and Followers</li>
130
+ </ul>
131
+ </div>
132
+
133
+ <p>
134
+ We will delete all the data used once your Model is Created
135
+ </p>
136
+
137
+ <p className="text-sm">
138
+ <a href="https://onairos.uk/privacy-policy" className="text-blue-600 hover:underline">
139
+ Onairos Privacy Policy
140
+ </a>{' '}
141
+ - Your creative interests and preferences are securely processed.
142
+ </p>
143
+ </div>
144
+
145
+ <div className="flex space-x-3 mt-6">
146
+ <button
147
+ onClick={this.handleClose}
148
+ disabled={this.state.isConnecting}
149
+ className="flex-1 px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 disabled:opacity-50"
150
+ >
151
+ Disagree
152
+ </button>
153
+ <button
154
+ onClick={this.pinterestConnect}
155
+ disabled={this.state.isConnecting}
156
+ className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
157
+ >
158
+ {this.state.isConnecting ? 'Connecting...' : 'Agree'}
159
+ </button>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ );
165
+ }
166
+ }
167
+
168
+ export default PinterestConnector;
@@ -0,0 +1,292 @@
1
+ # OAuth Connectors
2
+
3
+ This directory contains OAuth connector components that handle authentication flows for various social media and service platforms. Each connector follows the same pattern established in the original YoutubeInfo schema.
4
+
5
+ ## Available Connectors
6
+
7
+ - **YoutubeConnector** - YouTube OAuth integration
8
+ - **LinkedInConnector** - LinkedIn OAuth integration
9
+ - **InstagramConnector** - Instagram OAuth integration
10
+ - **PinterestConnector** - Pinterest OAuth integration
11
+ - **RedditConnector** - Reddit OAuth integration
12
+ - **GmailConnector** - Gmail OAuth integration
13
+
14
+ ## Architecture
15
+
16
+ ### Component Structure
17
+
18
+ Each connector follows this consistent pattern:
19
+
20
+ ```javascript
21
+ class PlatformConnector extends Component {
22
+ constructor(props) {
23
+ super(props);
24
+ this.state = {
25
+ connected: false,
26
+ open: false,
27
+ isConnecting: false,
28
+ };
29
+ }
30
+
31
+ // Connection management methods
32
+ setConnected() { /* Handle successful connection */ }
33
+ setDisconnected() { /* Handle disconnection */ }
34
+
35
+ // API integration
36
+ async updateConnections(updateType, newConnection) { /* Update server */ }
37
+ async platformConnect() { /* Initiate OAuth flow */ }
38
+
39
+ // UI handlers
40
+ handleOpen() { /* Open dialog */ }
41
+ handleClose() { /* Close dialog */ }
42
+
43
+ render() { /* Render OAuth consent dialog */ }
44
+ }
45
+ ```
46
+
47
+ ### Props Interface
48
+
49
+ All connectors accept these props:
50
+
51
+ - `open` (boolean) - Controls dialog visibility
52
+ - `onClose` (function) - Called when dialog is closed
53
+ - `onConnectionChange` (function) - Called when connection status changes
54
+ - `username` (string) - User identifier for API calls
55
+
56
+ ### API Integration
57
+
58
+ Each connector integrates with the Onairos API:
59
+
60
+ - **Authorization Endpoint**: `https://api2.onairos.uk/{platform}/authorize`
61
+ - **Connection Management**: `https://api2.onairos.uk/connections/update`
62
+
63
+ ## Usage
64
+
65
+ ### Basic Implementation
66
+
67
+ ```javascript
68
+ import { YoutubeConnector } from './components/connectors';
69
+
70
+ function MyComponent() {
71
+ const [showYouTube, setShowYouTube] = useState(false);
72
+
73
+ const handleConnectionChange = (platform, isConnected) => {
74
+ console.log(`${platform} connection status:`, isConnected);
75
+ };
76
+
77
+ return (
78
+ <>
79
+ <button onClick={() => setShowYouTube(true)}>
80
+ Connect YouTube
81
+ </button>
82
+
83
+ <YoutubeConnector
84
+ open={showYouTube}
85
+ onClose={() => setShowYouTube(false)}
86
+ onConnectionChange={handleConnectionChange}
87
+ username="user@example.com"
88
+ />
89
+ </>
90
+ );
91
+ }
92
+ ```
93
+
94
+ ### Integration with UniversalOnboarding
95
+
96
+ The connectors are integrated into the `UniversalOnboarding` component:
97
+
98
+ ```javascript
99
+ // Platform configuration
100
+ const platforms = [
101
+ { name: 'YouTube', icon: '📺', color: 'bg-red-500', connector: 'youtube' },
102
+ { name: 'LinkedIn', icon: '💼', color: 'bg-blue-700', connector: 'linkedin' },
103
+ // ... other platforms
104
+ ];
105
+
106
+ // Toggle handler
107
+ const handleToggle = (platformName, connectorType) => {
108
+ if (isCurrentlyConnected) {
109
+ // Disconnect logic
110
+ } else {
111
+ // Open OAuth dialog
112
+ setActiveConnector(connectorType);
113
+ }
114
+ };
115
+
116
+ // Render connectors
117
+ <YoutubeConnector
118
+ open={activeConnector === 'youtube'}
119
+ onClose={() => setActiveConnector(null)}
120
+ onConnectionChange={handleConnectionChange}
121
+ username={username}
122
+ />
123
+ ```
124
+
125
+ ## OAuth Flow
126
+
127
+ ### 1. User Interaction
128
+ - User clicks platform toggle in UniversalOnboarding
129
+ - Connector dialog opens with consent information
130
+
131
+ ### 2. Authorization Request
132
+ ```javascript
133
+ const jsonData = {
134
+ session: {
135
+ username: localStorage.getItem("username") || this.props.username
136
+ },
137
+ };
138
+
139
+ const response = await fetch('https://api2.onairos.uk/youtube/authorize', {
140
+ method: 'POST',
141
+ headers: { 'Content-Type': 'application/json' },
142
+ body: JSON.stringify(jsonData),
143
+ });
144
+
145
+ const result = await response.json();
146
+ window.location.href = result.youtubeURL; // Redirect to OAuth
147
+ ```
148
+
149
+ ### 3. Connection Management
150
+ ```javascript
151
+ const updateConnections = async (updateType, newConnection) => {
152
+ const jsonData = {
153
+ session: { username: this.props.username },
154
+ updateType, // 'Add' or 'Remove'
155
+ newConnection // Platform name
156
+ };
157
+
158
+ await fetch('https://api2.onairos.uk/connections/update', {
159
+ method: 'POST',
160
+ headers: { 'Content-Type': 'application/json' },
161
+ body: JSON.stringify(jsonData),
162
+ });
163
+ };
164
+ ```
165
+
166
+ ## Data Access Permissions
167
+
168
+ ### YouTube
169
+ - Basic Account Info
170
+ - Liked and Watched Videos
171
+ - Subscribed Channels and Playlist Videos
172
+
173
+ ### LinkedIn
174
+ - Basic Profile Information
175
+ - Professional Experience
176
+ - Network Connections
177
+ - Posts and Activity
178
+
179
+ ### Instagram
180
+ - Basic Profile Information
181
+ - Posts and Stories
182
+ - Liked Content
183
+ - Following and Followers
184
+
185
+ ### Pinterest
186
+ - Basic Profile Information
187
+ - Boards and Pins
188
+ - Saved and Liked Pins
189
+ - Following and Followers
190
+
191
+ ### Reddit
192
+ - Basic Profile Information
193
+ - Posts and Comments
194
+ - Upvoted and Saved Content
195
+ - Subscribed Subreddits
196
+
197
+ ### Gmail
198
+ - Basic Account Information
199
+ - Email Metadata (subjects, dates, senders)
200
+ - Email Categories and Labels
201
+ - Communication Patterns
202
+ - **Note**: Email content is NOT accessed
203
+
204
+ ## Security & Privacy
205
+
206
+ ### Data Handling
207
+ - All data is processed securely according to platform policies
208
+ - Data is deleted after model creation
209
+ - Only metadata is accessed for email services
210
+
211
+ ### Compliance
212
+ - **Google Services**: Complies with [Google API Services User Data Policy](https://policies.google.com/privacy)
213
+ - **Meta Platforms**: Complies with [Meta Platform Policy](https://developers.facebook.com/policy)
214
+ - **General**: Follows [Onairos Privacy Policy](https://onairos.uk/privacy-policy)
215
+
216
+ ### API Security
217
+ - All requests use HTTPS
218
+ - User authentication via session tokens
219
+ - Server-side OAuth token management
220
+
221
+ ## Error Handling
222
+
223
+ ### Connection Errors
224
+ ```javascript
225
+ try {
226
+ const response = await fetch(endpoint, options);
227
+ const result = await response.json();
228
+
229
+ if (result.platformURL) {
230
+ window.location.href = result.platformURL;
231
+ } else {
232
+ console.error('No OAuth URL received');
233
+ this.setState({ isConnecting: false });
234
+ }
235
+ } catch (error) {
236
+ console.error('Connection error:', error);
237
+ this.setState({ isConnecting: false });
238
+ }
239
+ ```
240
+
241
+ ### State Management
242
+ - Loading states prevent multiple simultaneous requests
243
+ - Error states provide user feedback
244
+ - Connection states persist across sessions
245
+
246
+ ## Styling
247
+
248
+ ### Design System
249
+ - Uses Tailwind CSS for consistent styling
250
+ - Modal overlays with backdrop blur
251
+ - Responsive design for mobile/desktop
252
+ - Accessible color contrast and focus states
253
+
254
+ ### UI Components
255
+ - Fixed overlay with centered modal
256
+ - Clear consent information with bullet points
257
+ - Prominent action buttons (Agree/Disagree)
258
+ - Loading states with disabled buttons
259
+
260
+ ## Testing
261
+
262
+ ### Manual Testing
263
+ 1. Click platform toggle in UniversalOnboarding
264
+ 2. Verify consent dialog opens with correct information
265
+ 3. Test "Disagree" button closes dialog
266
+ 4. Test "Agree" button initiates OAuth flow
267
+ 5. Verify connection state updates correctly
268
+
269
+ ### Integration Testing
270
+ - Test with different username formats
271
+ - Verify API error handling
272
+ - Test connection/disconnection flows
273
+ - Validate state persistence
274
+
275
+ ## Future Enhancements
276
+
277
+ ### Additional Platforms
278
+ - Twitter/X integration
279
+ - TikTok integration
280
+ - Facebook integration
281
+ - Discord integration
282
+
283
+ ### Enhanced Features
284
+ - Connection status indicators
285
+ - Data usage analytics
286
+ - Granular permission controls
287
+ - Batch connection management
288
+
289
+ ### Performance
290
+ - Lazy loading of connector components
291
+ - Connection state caching
292
+ - Optimized API calls
@@ -0,0 +1,168 @@
1
+ import React, { Component } from 'react';
2
+
3
+ class RedditConnector extends Component {
4
+ constructor(props) {
5
+ super(props);
6
+ this.state = {
7
+ connected: false,
8
+ open: false,
9
+ isConnecting: false,
10
+ };
11
+ this.handleClose = this.handleClose.bind(this);
12
+ this.handleOpen = this.handleOpen.bind(this);
13
+ this.redditConnect = this.redditConnect.bind(this);
14
+ this.setConnected = this.setConnected.bind(this);
15
+ this.setDisconnected = this.setDisconnected.bind(this);
16
+ }
17
+
18
+ setConnected() {
19
+ this.setState({ connected: true });
20
+ if (this.props.onConnectionChange) {
21
+ this.props.onConnectionChange('Reddit', true);
22
+ }
23
+ this.handleClose();
24
+ }
25
+
26
+ setDisconnected() {
27
+ this.updateConnections('Remove', 'Reddit').then(() => {
28
+ this.setState({ connected: false });
29
+ if (this.props.onConnectionChange) {
30
+ this.props.onConnectionChange('Reddit', false);
31
+ }
32
+ this.handleClose();
33
+ }).catch((error) => {
34
+ console.error('Error removing Reddit connection:', error);
35
+ });
36
+ }
37
+
38
+ async updateConnections(updateType, newConnection) {
39
+ const jsonData = {
40
+ session: {
41
+ username: localStorage.getItem("username") || this.props.username
42
+ },
43
+ updateType,
44
+ newConnection
45
+ };
46
+
47
+ try {
48
+ const response = await fetch('https://api2.onairos.uk/connections/update', {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ body: JSON.stringify(jsonData),
54
+ });
55
+ return await response.json();
56
+ } catch (error) {
57
+ console.error('UpdateConnections error:', error);
58
+ throw error;
59
+ }
60
+ }
61
+
62
+ handleOpen() {
63
+ this.setState({ open: true });
64
+ }
65
+
66
+ handleClose() {
67
+ this.setState({ open: false });
68
+ if (this.props.onClose) {
69
+ this.props.onClose();
70
+ }
71
+ }
72
+
73
+ async redditConnect() {
74
+ this.setState({ isConnecting: true });
75
+
76
+ const jsonData = {
77
+ session: {
78
+ username: localStorage.getItem("username") || this.props.username
79
+ },
80
+ };
81
+
82
+ try {
83
+ const response = await fetch('https://api2.onairos.uk/reddit/authorize', {
84
+ method: 'POST',
85
+ headers: {
86
+ 'Content-Type': 'application/json',
87
+ },
88
+ body: JSON.stringify(jsonData),
89
+ });
90
+
91
+ const result = await response.json();
92
+
93
+ if (result.redditURL) {
94
+ window.location.href = result.redditURL;
95
+ } else {
96
+ console.error('No Reddit URL received');
97
+ this.setState({ isConnecting: false });
98
+ }
99
+ } catch (error) {
100
+ console.error('Reddit connection error:', error);
101
+ this.setState({ isConnecting: false });
102
+ }
103
+ }
104
+
105
+ render() {
106
+ const { open = this.props.open || this.state.open } = this.props;
107
+
108
+ if (!open) return null;
109
+
110
+ return (
111
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
112
+ <div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-hidden">
113
+ <div className="p-6">
114
+ <h2 className="text-xl font-bold text-gray-900 mb-4">
115
+ Grant Onairos Access to your Reddit Account?
116
+ </h2>
117
+
118
+ <div className="space-y-4 text-gray-700">
119
+ <p>
120
+ Grant Permission to your Reddit Account, so we can build your Data Models.
121
+ </p>
122
+
123
+ <div>
124
+ <p className="font-medium mb-2">We will access your Reddit:</p>
125
+ <ul className="list-disc ml-6 space-y-1">
126
+ <li>Basic Profile Information</li>
127
+ <li>Posts and Comments</li>
128
+ <li>Upvoted and Saved Content</li>
129
+ <li>Subscribed Subreddits</li>
130
+ </ul>
131
+ </div>
132
+
133
+ <p>
134
+ We will delete all the data used once your Model is Created
135
+ </p>
136
+
137
+ <p className="text-sm">
138
+ <a href="https://onairos.uk/privacy-policy" className="text-blue-600 hover:underline">
139
+ Onairos Privacy Policy
140
+ </a>{' '}
141
+ - Your Reddit activity helps us understand your interests and preferences.
142
+ </p>
143
+ </div>
144
+
145
+ <div className="flex space-x-3 mt-6">
146
+ <button
147
+ onClick={this.handleClose}
148
+ disabled={this.state.isConnecting}
149
+ className="flex-1 px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 disabled:opacity-50"
150
+ >
151
+ Disagree
152
+ </button>
153
+ <button
154
+ onClick={this.redditConnect}
155
+ disabled={this.state.isConnecting}
156
+ className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
157
+ >
158
+ {this.state.isConnecting ? 'Connecting...' : 'Agree'}
159
+ </button>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ );
165
+ }
166
+ }
167
+
168
+ export default RedditConnector;