1 | type Linkedin = { |
2 | token: string |
3 | apiVersion: string |
4 | } |
5 |
|
6 | type Base64 = string |
7 |
|
8 | async function initializeImageUpload(resource: Linkedin, owner: string) { |
9 | const endpoint = 'https://api.linkedin.com/rest/images?action=initializeUpload' |
10 | const response = await fetch(endpoint, { |
11 | method: 'POST', |
12 | headers: { |
13 | Authorization: `Bearer ${resource.token}`, |
14 | 'X-Restli-Protocol-Version': '2.0.0', |
15 | 'Content-Type': 'application/json', |
16 | 'LinkedIn-Version': `${resource.apiVersion}` |
17 | }, |
18 | body: JSON.stringify({ |
19 | initializeUploadRequest: { |
20 | owner |
21 | } |
22 | }) |
23 | }) |
24 |
|
25 | if (!response.ok) { |
26 | throw new Error(`HTTP error! status: ${response.status}`) |
27 | } |
28 |
|
29 | const data = await response.json() |
30 | return data.value |
31 | } |
32 |
|
33 | async function uploadImage(uploadUrl: string, image: Base64) { |
34 | const buffer = Buffer.from(image, 'base64') |
35 |
|
36 | const response = await fetch(uploadUrl, { |
37 | method: 'PUT', |
38 | headers: { |
39 | 'Content-Type': 'application/octet-stream' |
40 | }, |
41 | body: buffer |
42 | }) |
43 |
|
44 | if (!response.ok) { |
45 | throw new Error(`HTTP error! status: ${response.status}`) |
46 | } |
47 | } |
48 |
|
49 | async function uploadThumbnail(resource: Linkedin, owner: string, thumbnail: Base64) { |
50 | const uploadData = await initializeImageUpload(resource, owner) |
51 | const uploadUrl = uploadData.uploadUrl |
52 | const imageUrn = uploadData.image |
53 |
|
54 | await uploadImage(uploadUrl, thumbnail) |
55 |
|
56 | return imageUrn |
57 | } |
58 |
|
59 | export async function main( |
60 | resource: Linkedin, |
61 | postData: { |
62 | author: string |
63 | commentary: string |
64 | visibility: 'CONNECTIONS' | 'PUBLIC' | 'LOGGED_IN' | 'CONTAINER' |
65 | distribution: { |
66 | feedDistribution: 'NONE' | 'MAIN_FEED' |
67 | } |
68 | content?: { |
69 | article: { |
70 | source: string |
71 | title: string |
72 | description?: string |
73 | thumbnail?: string |
74 | thumbnailAltText?: string |
75 | } |
76 | } |
77 | isReshareDisabledByAuthor: boolean |
78 | }, |
79 | thumbnail?: Base64 |
80 | ) { |
81 | if (postData.content?.article && thumbnail) { |
82 | const thumbnailUrn = await uploadThumbnail(resource, postData.author, thumbnail) |
83 | postData.content.article.thumbnail = thumbnailUrn |
84 | } |
85 |
|
86 | |
87 | const endpoint = 'https://api.linkedin.com/rest/posts' |
88 |
|
89 | const response = await fetch(endpoint, { |
90 | method: 'POST', |
91 | headers: { |
92 | Authorization: `Bearer ${resource.token}`, |
93 | 'X-Restli-Protocol-Version': '2.0.0', |
94 | 'LinkedIn-Version': `${resource.apiVersion}`, |
95 | 'Content-Type': 'application/json' |
96 | }, |
97 | body: JSON.stringify({ ...postData, lifecycleState: 'PUBLISHED' }) |
98 | }) |
99 |
|
100 | if (!response.ok) { |
101 | throw new Error(`HTTP error! status: ${response.status}`) |
102 | } |
103 |
|
104 | const postId = response.headers.get('x-restli-id') |
105 | const data = await response.json() |
106 |
|
107 | return { ...data, postId } |
108 | } |
109 |
|