naystack 1.8.12 → 1.8.13
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/README.md +189 -31
- package/dist/auth/index.cjs.js +158 -18
- package/dist/auth/index.d.mts +3 -1
- package/dist/auth/index.d.ts +3 -1
- package/dist/auth/index.esm.js +158 -18
- package/dist/auth/instagram/index.cjs.js +158 -18
- package/dist/auth/instagram/index.d.mts +6 -1
- package/dist/auth/instagram/index.d.ts +6 -1
- package/dist/auth/instagram/index.esm.js +158 -18
- package/dist/auth/instagram/route.cjs.js +158 -18
- package/dist/auth/instagram/route.d.mts +3 -1
- package/dist/auth/instagram/route.d.ts +3 -1
- package/dist/auth/instagram/route.esm.js +158 -18
- package/dist/auth/instagram/utils.cjs.js +2 -2
- package/dist/auth/instagram/utils.d.mts +13 -2
- package/dist/auth/instagram/utils.d.ts +13 -2
- package/dist/auth/instagram/utils.esm.js +2 -2
- package/dist/file/setup.d.mts +13 -5
- package/dist/file/setup.d.ts +13 -5
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/socials/index.cjs.js +298 -72
- package/dist/socials/index.d.mts +6 -4
- package/dist/socials/index.d.ts +6 -4
- package/dist/socials/index.esm.js +296 -72
- package/dist/socials/instagram/getters.cjs.js +76 -25
- package/dist/socials/instagram/getters.d.mts +56 -6
- package/dist/socials/instagram/getters.d.ts +56 -6
- package/dist/socials/instagram/getters.esm.js +74 -25
- package/dist/socials/instagram/setters.cjs.js +192 -14
- package/dist/socials/instagram/setters.d.mts +43 -3
- package/dist/socials/instagram/setters.d.ts +43 -3
- package/dist/socials/instagram/setters.esm.js +191 -14
- package/dist/socials/instagram/types.d.mts +48 -11
- package/dist/socials/instagram/types.d.ts +48 -11
- package/dist/socials/instagram/utils.cjs.js +28 -15
- package/dist/socials/instagram/utils.d.mts +7 -18
- package/dist/socials/instagram/utils.d.ts +7 -18
- package/dist/socials/instagram/utils.esm.js +26 -14
- package/dist/socials/instagram/webhook.cjs.js +1 -1
- package/dist/socials/instagram/webhook.esm.js +1 -1
- package/dist/socials/meta/container.cjs.js +82 -0
- package/dist/socials/meta/container.d.mts +38 -0
- package/dist/socials/meta/container.d.ts +38 -0
- package/dist/socials/meta/container.esm.js +57 -0
- package/dist/socials/meta/request.cjs.js +60 -0
- package/dist/socials/meta/request.d.mts +50 -0
- package/dist/socials/meta/request.d.ts +50 -0
- package/dist/socials/meta/request.esm.js +34 -0
- package/dist/socials/meta/types.cjs.js +18 -0
- package/dist/socials/meta/types.d.mts +47 -0
- package/dist/socials/meta/types.d.ts +47 -0
- package/dist/socials/meta/types.esm.js +0 -0
- package/dist/socials/{meta-webhook.cjs.js → meta/webhook.cjs.js} +4 -4
- package/dist/socials/{meta-webhook.esm.js → meta/webhook.esm.js} +1 -1
- package/dist/socials/threads/getters.cjs.js +36 -11
- package/dist/socials/threads/getters.d.mts +23 -2
- package/dist/socials/threads/getters.d.ts +23 -2
- package/dist/socials/threads/getters.esm.js +35 -11
- package/dist/socials/threads/setters.cjs.js +150 -44
- package/dist/socials/threads/setters.d.mts +25 -34
- package/dist/socials/threads/setters.d.ts +25 -34
- package/dist/socials/threads/setters.esm.js +149 -41
- package/dist/socials/threads/types.d.mts +40 -1
- package/dist/socials/threads/types.d.ts +40 -1
- package/dist/socials/threads/utils.cjs.js +29 -11
- package/dist/socials/threads/utils.d.mts +8 -17
- package/dist/socials/threads/utils.d.ts +8 -17
- package/dist/socials/threads/utils.esm.js +27 -10
- package/dist/socials/threads/webhook.cjs.js +1 -1
- package/dist/socials/threads/webhook.esm.js +1 -1
- package/package.json +1 -1
- /package/dist/socials/{meta-webhook.d.mts → meta/webhook.d.mts} +0 -0
- /package/dist/socials/{meta-webhook.d.ts → meta/webhook.d.ts} +0 -0
package/dist/auth/index.esm.js
CHANGED
|
@@ -571,40 +571,177 @@ async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
|
-
var getInstagramAuthorizationURL = (token) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
574
|
+
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
575
575
|
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
576
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope
|
|
576
|
+
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${token}&redirect_uri=${getEnv("NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */)}#weblink`;
|
|
577
577
|
|
|
578
|
-
// src/socials/
|
|
579
|
-
function
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
method: postData ? "POST" : "GET",
|
|
585
|
-
body: JSON.stringify(postData),
|
|
586
|
-
headers: {
|
|
587
|
-
Authorization: `Bearer ${token}`,
|
|
588
|
-
"Content-Type": "application/json"
|
|
578
|
+
// src/socials/meta/request.ts
|
|
579
|
+
function createGraphClient(baseURL) {
|
|
580
|
+
const buildURL = (token, path, params = {}) => {
|
|
581
|
+
const search = new URLSearchParams();
|
|
582
|
+
for (const [key, value] of Object.entries(params)) {
|
|
583
|
+
if (value !== void 0) search.set(key, String(value));
|
|
589
584
|
}
|
|
590
|
-
|
|
585
|
+
search.set("access_token", token);
|
|
586
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
587
|
+
};
|
|
588
|
+
const request = async (token, path, options = {}) => {
|
|
589
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
590
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
591
|
+
headers: {
|
|
592
|
+
Authorization: `Bearer ${token}`,
|
|
593
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
594
|
+
},
|
|
595
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
596
|
+
});
|
|
597
|
+
return response.json().catch(() => null);
|
|
598
|
+
};
|
|
599
|
+
return { buildURL, request };
|
|
600
|
+
}
|
|
601
|
+
function readGraphID(label, result) {
|
|
602
|
+
if (result?.error) {
|
|
603
|
+
console.error(`[naystack] ${label} failed: ${result.error.message}`);
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
return result?.id || null;
|
|
591
607
|
}
|
|
592
608
|
|
|
609
|
+
// src/socials/instagram/utils.ts
|
|
610
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
611
|
+
var getInstagramData = client.request;
|
|
612
|
+
|
|
593
613
|
// src/socials/instagram/getters.ts
|
|
594
614
|
var getInstagramUser = (token, id, fields) => {
|
|
595
615
|
return getInstagramData(token, id || "me", {
|
|
596
|
-
|
|
616
|
+
params: {
|
|
617
|
+
fields: fields ? fields.join(",") : "username,followers_count,media_count"
|
|
618
|
+
}
|
|
597
619
|
});
|
|
598
620
|
};
|
|
621
|
+
var getInstagramContainerStatus = async (token, id) => {
|
|
622
|
+
const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
|
|
623
|
+
if (!result?.status_code) return null;
|
|
624
|
+
return {
|
|
625
|
+
status: result.status_code,
|
|
626
|
+
error: result.status_code === "ERROR" ? result.status : void 0
|
|
627
|
+
};
|
|
628
|
+
};
|
|
599
629
|
|
|
600
|
-
// src/socials/meta
|
|
630
|
+
// src/socials/meta/container.ts
|
|
631
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
632
|
+
async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
633
|
+
const deadline = Date.now() + timeoutMS;
|
|
634
|
+
let state = await getState();
|
|
635
|
+
if (!state) {
|
|
636
|
+
await sleep(2e3);
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
|
|
640
|
+
await sleep(intervalMS);
|
|
641
|
+
state = await getState();
|
|
642
|
+
}
|
|
643
|
+
return state;
|
|
644
|
+
}
|
|
645
|
+
function createPublisher(platform) {
|
|
646
|
+
const isReady = async (token, id, label, wait) => {
|
|
647
|
+
const state = await waitForContainer(
|
|
648
|
+
() => platform.getStatus(token, id),
|
|
649
|
+
wait
|
|
650
|
+
);
|
|
651
|
+
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
652
|
+
console.error(
|
|
653
|
+
`[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
|
|
654
|
+
);
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
return true;
|
|
658
|
+
};
|
|
659
|
+
return {
|
|
660
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
661
|
+
publish: async (token, params, wait) => {
|
|
662
|
+
const containerID = await platform.createContainer(token, params);
|
|
663
|
+
if (!containerID) return null;
|
|
664
|
+
if (!await isReady(token, containerID, "container", wait)) return null;
|
|
665
|
+
return platform.publish(token, containerID);
|
|
666
|
+
},
|
|
667
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
668
|
+
createChildren: async (token, items, wait) => {
|
|
669
|
+
const children = [];
|
|
670
|
+
for (const item of items) {
|
|
671
|
+
const childID = await platform.createContainer(token, {
|
|
672
|
+
...item,
|
|
673
|
+
is_carousel_item: true
|
|
674
|
+
});
|
|
675
|
+
if (!childID) return null;
|
|
676
|
+
if (!await isReady(token, childID, "carousel item", wait))
|
|
677
|
+
return null;
|
|
678
|
+
children.push(childID);
|
|
679
|
+
}
|
|
680
|
+
return children;
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/socials/instagram/setters.ts
|
|
686
|
+
var publisher = createPublisher({
|
|
687
|
+
name: "Instagram",
|
|
688
|
+
getStatus: getInstagramContainerStatus,
|
|
689
|
+
createContainer: async (token, params) => readGraphID(
|
|
690
|
+
"Instagram media container",
|
|
691
|
+
await getInstagramData(token, "me/media", {
|
|
692
|
+
params,
|
|
693
|
+
method: "POST"
|
|
694
|
+
})
|
|
695
|
+
),
|
|
696
|
+
publish: async (token, creationID) => readGraphID(
|
|
697
|
+
"Instagram publish",
|
|
698
|
+
await getInstagramData(token, "me/media_publish", {
|
|
699
|
+
params: { creation_id: creationID },
|
|
700
|
+
method: "POST"
|
|
701
|
+
})
|
|
702
|
+
)
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
// src/socials/meta/webhook.ts
|
|
601
706
|
import { NextResponse as NextResponse6 } from "next/server";
|
|
602
707
|
|
|
708
|
+
// src/socials/threads/utils.ts
|
|
709
|
+
var client2 = createGraphClient("https://graph.threads.net/v1.0");
|
|
710
|
+
var getThreadsData = client2.request;
|
|
711
|
+
|
|
712
|
+
// src/socials/threads/getters.ts
|
|
713
|
+
var getThreadsContainerStatus = async (token, id) => {
|
|
714
|
+
const result = await getThreadsData(token, id, { params: { fields: "status,error_message" } });
|
|
715
|
+
if (!result?.status) return null;
|
|
716
|
+
return { status: result.status, error: result.error_message };
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// src/socials/threads/setters.ts
|
|
720
|
+
var publisher2 = createPublisher({
|
|
721
|
+
name: "Threads",
|
|
722
|
+
getStatus: getThreadsContainerStatus,
|
|
723
|
+
createContainer: async (token, params) => readGraphID(
|
|
724
|
+
"Threads container",
|
|
725
|
+
await getThreadsData(token, "me/threads", {
|
|
726
|
+
params,
|
|
727
|
+
method: "POST"
|
|
728
|
+
})
|
|
729
|
+
),
|
|
730
|
+
publish: async (token, creationID) => readGraphID(
|
|
731
|
+
"Threads publish",
|
|
732
|
+
await getThreadsData(token, "me/threads_publish", {
|
|
733
|
+
params: { creation_id: creationID },
|
|
734
|
+
method: "POST"
|
|
735
|
+
})
|
|
736
|
+
)
|
|
737
|
+
});
|
|
738
|
+
|
|
603
739
|
// src/auth/instagram/route.ts
|
|
604
740
|
var getInstagramRoute = ({
|
|
605
741
|
redirectURL,
|
|
606
742
|
errorRedirectURL,
|
|
607
|
-
onUser
|
|
743
|
+
onUser,
|
|
744
|
+
scopes
|
|
608
745
|
}) => {
|
|
609
746
|
const handleError2 = (message) => NextResponse7.redirect(`${errorRedirectURL}?error=${message}`);
|
|
610
747
|
return async (req) => {
|
|
@@ -614,7 +751,10 @@ var getInstagramRoute = ({
|
|
|
614
751
|
if (error) return handleError2(error);
|
|
615
752
|
if (!accessCode) {
|
|
616
753
|
if (!stateToken) return handleError2("Invalid request");
|
|
617
|
-
return NextResponse7.redirect(
|
|
754
|
+
return NextResponse7.redirect(
|
|
755
|
+
getInstagramAuthorizationURL(stateToken, scopes),
|
|
756
|
+
302
|
|
757
|
+
);
|
|
618
758
|
}
|
|
619
759
|
if (!stateToken) return handleError2("Invalid request");
|
|
620
760
|
const instagramData = await getLongLivedToken(
|
|
@@ -136,40 +136,177 @@ async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
-
var getInstagramAuthorizationURL = (token) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
139
|
+
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
140
140
|
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
141
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope
|
|
141
|
+
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${token}&redirect_uri=${getEnv("NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */)}#weblink`;
|
|
142
142
|
|
|
143
|
-
// src/socials/
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
method: postData ? "POST" : "GET",
|
|
150
|
-
body: JSON.stringify(postData),
|
|
151
|
-
headers: {
|
|
152
|
-
Authorization: `Bearer ${token}`,
|
|
153
|
-
"Content-Type": "application/json"
|
|
143
|
+
// src/socials/meta/request.ts
|
|
144
|
+
function createGraphClient(baseURL) {
|
|
145
|
+
const buildURL = (token, path, params = {}) => {
|
|
146
|
+
const search = new URLSearchParams();
|
|
147
|
+
for (const [key, value] of Object.entries(params)) {
|
|
148
|
+
if (value !== void 0) search.set(key, String(value));
|
|
154
149
|
}
|
|
155
|
-
|
|
150
|
+
search.set("access_token", token);
|
|
151
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
152
|
+
};
|
|
153
|
+
const request = async (token, path, options = {}) => {
|
|
154
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
155
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
156
|
+
headers: {
|
|
157
|
+
Authorization: `Bearer ${token}`,
|
|
158
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
159
|
+
},
|
|
160
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
161
|
+
});
|
|
162
|
+
return response.json().catch(() => null);
|
|
163
|
+
};
|
|
164
|
+
return { buildURL, request };
|
|
156
165
|
}
|
|
166
|
+
function readGraphID(label, result) {
|
|
167
|
+
if (result?.error) {
|
|
168
|
+
console.error(`[naystack] ${label} failed: ${result.error.message}`);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return result?.id || null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/socials/instagram/utils.ts
|
|
175
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
176
|
+
var getInstagramData = client.request;
|
|
157
177
|
|
|
158
178
|
// src/socials/instagram/getters.ts
|
|
159
179
|
var getInstagramUser = (token, id, fields) => {
|
|
160
180
|
return getInstagramData(token, id || "me", {
|
|
161
|
-
|
|
181
|
+
params: {
|
|
182
|
+
fields: fields ? fields.join(",") : "username,followers_count,media_count"
|
|
183
|
+
}
|
|
162
184
|
});
|
|
163
185
|
};
|
|
186
|
+
var getInstagramContainerStatus = async (token, id) => {
|
|
187
|
+
const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
|
|
188
|
+
if (!result?.status_code) return null;
|
|
189
|
+
return {
|
|
190
|
+
status: result.status_code,
|
|
191
|
+
error: result.status_code === "ERROR" ? result.status : void 0
|
|
192
|
+
};
|
|
193
|
+
};
|
|
164
194
|
|
|
165
|
-
// src/socials/meta
|
|
195
|
+
// src/socials/meta/container.ts
|
|
196
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
197
|
+
async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
198
|
+
const deadline = Date.now() + timeoutMS;
|
|
199
|
+
let state = await getState();
|
|
200
|
+
if (!state) {
|
|
201
|
+
await sleep(2e3);
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
|
|
205
|
+
await sleep(intervalMS);
|
|
206
|
+
state = await getState();
|
|
207
|
+
}
|
|
208
|
+
return state;
|
|
209
|
+
}
|
|
210
|
+
function createPublisher(platform) {
|
|
211
|
+
const isReady = async (token, id, label, wait) => {
|
|
212
|
+
const state = await waitForContainer(
|
|
213
|
+
() => platform.getStatus(token, id),
|
|
214
|
+
wait
|
|
215
|
+
);
|
|
216
|
+
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
217
|
+
console.error(
|
|
218
|
+
`[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
|
|
219
|
+
);
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
};
|
|
224
|
+
return {
|
|
225
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
226
|
+
publish: async (token, params, wait) => {
|
|
227
|
+
const containerID = await platform.createContainer(token, params);
|
|
228
|
+
if (!containerID) return null;
|
|
229
|
+
if (!await isReady(token, containerID, "container", wait)) return null;
|
|
230
|
+
return platform.publish(token, containerID);
|
|
231
|
+
},
|
|
232
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
233
|
+
createChildren: async (token, items, wait) => {
|
|
234
|
+
const children = [];
|
|
235
|
+
for (const item of items) {
|
|
236
|
+
const childID = await platform.createContainer(token, {
|
|
237
|
+
...item,
|
|
238
|
+
is_carousel_item: true
|
|
239
|
+
});
|
|
240
|
+
if (!childID) return null;
|
|
241
|
+
if (!await isReady(token, childID, "carousel item", wait))
|
|
242
|
+
return null;
|
|
243
|
+
children.push(childID);
|
|
244
|
+
}
|
|
245
|
+
return children;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/socials/instagram/setters.ts
|
|
251
|
+
var publisher = createPublisher({
|
|
252
|
+
name: "Instagram",
|
|
253
|
+
getStatus: getInstagramContainerStatus,
|
|
254
|
+
createContainer: async (token, params) => readGraphID(
|
|
255
|
+
"Instagram media container",
|
|
256
|
+
await getInstagramData(token, "me/media", {
|
|
257
|
+
params,
|
|
258
|
+
method: "POST"
|
|
259
|
+
})
|
|
260
|
+
),
|
|
261
|
+
publish: async (token, creationID) => readGraphID(
|
|
262
|
+
"Instagram publish",
|
|
263
|
+
await getInstagramData(token, "me/media_publish", {
|
|
264
|
+
params: { creation_id: creationID },
|
|
265
|
+
method: "POST"
|
|
266
|
+
})
|
|
267
|
+
)
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// src/socials/meta/webhook.ts
|
|
166
271
|
var import_server2 = require("next/server");
|
|
167
272
|
|
|
273
|
+
// src/socials/threads/utils.ts
|
|
274
|
+
var client2 = createGraphClient("https://graph.threads.net/v1.0");
|
|
275
|
+
var getThreadsData = client2.request;
|
|
276
|
+
|
|
277
|
+
// src/socials/threads/getters.ts
|
|
278
|
+
var getThreadsContainerStatus = async (token, id) => {
|
|
279
|
+
const result = await getThreadsData(token, id, { params: { fields: "status,error_message" } });
|
|
280
|
+
if (!result?.status) return null;
|
|
281
|
+
return { status: result.status, error: result.error_message };
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/socials/threads/setters.ts
|
|
285
|
+
var publisher2 = createPublisher({
|
|
286
|
+
name: "Threads",
|
|
287
|
+
getStatus: getThreadsContainerStatus,
|
|
288
|
+
createContainer: async (token, params) => readGraphID(
|
|
289
|
+
"Threads container",
|
|
290
|
+
await getThreadsData(token, "me/threads", {
|
|
291
|
+
params,
|
|
292
|
+
method: "POST"
|
|
293
|
+
})
|
|
294
|
+
),
|
|
295
|
+
publish: async (token, creationID) => readGraphID(
|
|
296
|
+
"Threads publish",
|
|
297
|
+
await getThreadsData(token, "me/threads_publish", {
|
|
298
|
+
params: { creation_id: creationID },
|
|
299
|
+
method: "POST"
|
|
300
|
+
})
|
|
301
|
+
)
|
|
302
|
+
});
|
|
303
|
+
|
|
168
304
|
// src/auth/instagram/route.ts
|
|
169
305
|
var getInstagramRoute = ({
|
|
170
306
|
redirectURL,
|
|
171
307
|
errorRedirectURL,
|
|
172
|
-
onUser
|
|
308
|
+
onUser,
|
|
309
|
+
scopes
|
|
173
310
|
}) => {
|
|
174
311
|
const handleError = (message) => import_server3.NextResponse.redirect(`${errorRedirectURL}?error=${message}`);
|
|
175
312
|
return async (req) => {
|
|
@@ -179,7 +316,10 @@ var getInstagramRoute = ({
|
|
|
179
316
|
if (error) return handleError(error);
|
|
180
317
|
if (!accessCode) {
|
|
181
318
|
if (!stateToken) return handleError("Invalid request");
|
|
182
|
-
return import_server3.NextResponse.redirect(
|
|
319
|
+
return import_server3.NextResponse.redirect(
|
|
320
|
+
getInstagramAuthorizationURL(stateToken, scopes),
|
|
321
|
+
302
|
|
322
|
+
);
|
|
183
323
|
}
|
|
184
324
|
if (!stateToken) return handleError("Invalid request");
|
|
185
325
|
const instagramData = await getLongLivedToken(
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as next_server from 'next/server';
|
|
2
|
+
import { InstagramScope } from './utils.mjs';
|
|
2
3
|
export { getInstagramAuthorizationURL, getRefreshedInstagramAccessToken } from './utils.mjs';
|
|
3
4
|
import { InstagramUser } from '../../socials/instagram/types.mjs';
|
|
5
|
+
import '../../socials/meta/container.mjs';
|
|
6
|
+
import '../../socials/meta/types.mjs';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Options for initializing Instagram OAuth via {@link setupInstagramAuth}.
|
|
@@ -8,6 +11,7 @@ import { InstagramUser } from '../../socials/instagram/types.mjs';
|
|
|
8
11
|
* @property onUser - Called with `(instagramUser, appUserId, accessToken)` after successful OAuth. Return a string to show as error (redirects to `errorRedirectURL`); return `void` on success.
|
|
9
12
|
* @property redirectURL - Where to redirect after successful Instagram auth.
|
|
10
13
|
* @property errorRedirectURL - Where to redirect on error (with `?error=` query param).
|
|
14
|
+
* @property scopes - Permissions to request. Default: `["instagram_business_basic"]`. Add `"instagram_business_content_publish"` to publish on the user's behalf.
|
|
11
15
|
*
|
|
12
16
|
* @category Auth
|
|
13
17
|
*/
|
|
@@ -15,6 +19,7 @@ interface SetupInstagramAuthOptions {
|
|
|
15
19
|
onUser: (data: InstagramUser, id: number | null, accessToken: string) => Promise<string | void>;
|
|
16
20
|
redirectURL: string;
|
|
17
21
|
errorRedirectURL: string;
|
|
22
|
+
scopes?: InstagramScope[];
|
|
18
23
|
}
|
|
19
24
|
/**
|
|
20
25
|
* Initializes Instagram OAuth. Returns a GET handler for the OAuth callback and a helper to refresh long-lived tokens.
|
|
@@ -50,4 +55,4 @@ declare function setupInstagramAuth(props: SetupInstagramAuthOptions): {
|
|
|
50
55
|
GET: (req: next_server.NextRequest) => Promise<next_server.NextResponse<unknown>>;
|
|
51
56
|
};
|
|
52
57
|
|
|
53
|
-
export { type SetupInstagramAuthOptions, setupInstagramAuth };
|
|
58
|
+
export { InstagramScope, type SetupInstagramAuthOptions, setupInstagramAuth };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as next_server from 'next/server';
|
|
2
|
+
import { InstagramScope } from './utils.js';
|
|
2
3
|
export { getInstagramAuthorizationURL, getRefreshedInstagramAccessToken } from './utils.js';
|
|
3
4
|
import { InstagramUser } from '../../socials/instagram/types.js';
|
|
5
|
+
import '../../socials/meta/container.js';
|
|
6
|
+
import '../../socials/meta/types.js';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Options for initializing Instagram OAuth via {@link setupInstagramAuth}.
|
|
@@ -8,6 +11,7 @@ import { InstagramUser } from '../../socials/instagram/types.js';
|
|
|
8
11
|
* @property onUser - Called with `(instagramUser, appUserId, accessToken)` after successful OAuth. Return a string to show as error (redirects to `errorRedirectURL`); return `void` on success.
|
|
9
12
|
* @property redirectURL - Where to redirect after successful Instagram auth.
|
|
10
13
|
* @property errorRedirectURL - Where to redirect on error (with `?error=` query param).
|
|
14
|
+
* @property scopes - Permissions to request. Default: `["instagram_business_basic"]`. Add `"instagram_business_content_publish"` to publish on the user's behalf.
|
|
11
15
|
*
|
|
12
16
|
* @category Auth
|
|
13
17
|
*/
|
|
@@ -15,6 +19,7 @@ interface SetupInstagramAuthOptions {
|
|
|
15
19
|
onUser: (data: InstagramUser, id: number | null, accessToken: string) => Promise<string | void>;
|
|
16
20
|
redirectURL: string;
|
|
17
21
|
errorRedirectURL: string;
|
|
22
|
+
scopes?: InstagramScope[];
|
|
18
23
|
}
|
|
19
24
|
/**
|
|
20
25
|
* Initializes Instagram OAuth. Returns a GET handler for the OAuth callback and a helper to refresh long-lived tokens.
|
|
@@ -50,4 +55,4 @@ declare function setupInstagramAuth(props: SetupInstagramAuthOptions): {
|
|
|
50
55
|
GET: (req: next_server.NextRequest) => Promise<next_server.NextResponse<unknown>>;
|
|
51
56
|
};
|
|
52
57
|
|
|
53
|
-
export { type SetupInstagramAuthOptions, setupInstagramAuth };
|
|
58
|
+
export { InstagramScope, type SetupInstagramAuthOptions, setupInstagramAuth };
|
|
@@ -108,40 +108,177 @@ async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
var getInstagramAuthorizationURL = (token) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
111
|
+
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
112
112
|
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
113
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope
|
|
113
|
+
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${token}&redirect_uri=${getEnv("NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */)}#weblink`;
|
|
114
114
|
|
|
115
|
-
// src/socials/
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
method: postData ? "POST" : "GET",
|
|
122
|
-
body: JSON.stringify(postData),
|
|
123
|
-
headers: {
|
|
124
|
-
Authorization: `Bearer ${token}`,
|
|
125
|
-
"Content-Type": "application/json"
|
|
115
|
+
// src/socials/meta/request.ts
|
|
116
|
+
function createGraphClient(baseURL) {
|
|
117
|
+
const buildURL = (token, path, params = {}) => {
|
|
118
|
+
const search = new URLSearchParams();
|
|
119
|
+
for (const [key, value] of Object.entries(params)) {
|
|
120
|
+
if (value !== void 0) search.set(key, String(value));
|
|
126
121
|
}
|
|
127
|
-
|
|
122
|
+
search.set("access_token", token);
|
|
123
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
124
|
+
};
|
|
125
|
+
const request = async (token, path, options = {}) => {
|
|
126
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
127
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
128
|
+
headers: {
|
|
129
|
+
Authorization: `Bearer ${token}`,
|
|
130
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
131
|
+
},
|
|
132
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
133
|
+
});
|
|
134
|
+
return response.json().catch(() => null);
|
|
135
|
+
};
|
|
136
|
+
return { buildURL, request };
|
|
128
137
|
}
|
|
138
|
+
function readGraphID(label, result) {
|
|
139
|
+
if (result?.error) {
|
|
140
|
+
console.error(`[naystack] ${label} failed: ${result.error.message}`);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return result?.id || null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/socials/instagram/utils.ts
|
|
147
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
148
|
+
var getInstagramData = client.request;
|
|
129
149
|
|
|
130
150
|
// src/socials/instagram/getters.ts
|
|
131
151
|
var getInstagramUser = (token, id, fields) => {
|
|
132
152
|
return getInstagramData(token, id || "me", {
|
|
133
|
-
|
|
153
|
+
params: {
|
|
154
|
+
fields: fields ? fields.join(",") : "username,followers_count,media_count"
|
|
155
|
+
}
|
|
134
156
|
});
|
|
135
157
|
};
|
|
158
|
+
var getInstagramContainerStatus = async (token, id) => {
|
|
159
|
+
const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
|
|
160
|
+
if (!result?.status_code) return null;
|
|
161
|
+
return {
|
|
162
|
+
status: result.status_code,
|
|
163
|
+
error: result.status_code === "ERROR" ? result.status : void 0
|
|
164
|
+
};
|
|
165
|
+
};
|
|
136
166
|
|
|
137
|
-
// src/socials/meta
|
|
167
|
+
// src/socials/meta/container.ts
|
|
168
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
169
|
+
async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
170
|
+
const deadline = Date.now() + timeoutMS;
|
|
171
|
+
let state = await getState();
|
|
172
|
+
if (!state) {
|
|
173
|
+
await sleep(2e3);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
|
|
177
|
+
await sleep(intervalMS);
|
|
178
|
+
state = await getState();
|
|
179
|
+
}
|
|
180
|
+
return state;
|
|
181
|
+
}
|
|
182
|
+
function createPublisher(platform) {
|
|
183
|
+
const isReady = async (token, id, label, wait) => {
|
|
184
|
+
const state = await waitForContainer(
|
|
185
|
+
() => platform.getStatus(token, id),
|
|
186
|
+
wait
|
|
187
|
+
);
|
|
188
|
+
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
189
|
+
console.error(
|
|
190
|
+
`[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
|
|
191
|
+
);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
198
|
+
publish: async (token, params, wait) => {
|
|
199
|
+
const containerID = await platform.createContainer(token, params);
|
|
200
|
+
if (!containerID) return null;
|
|
201
|
+
if (!await isReady(token, containerID, "container", wait)) return null;
|
|
202
|
+
return platform.publish(token, containerID);
|
|
203
|
+
},
|
|
204
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
205
|
+
createChildren: async (token, items, wait) => {
|
|
206
|
+
const children = [];
|
|
207
|
+
for (const item of items) {
|
|
208
|
+
const childID = await platform.createContainer(token, {
|
|
209
|
+
...item,
|
|
210
|
+
is_carousel_item: true
|
|
211
|
+
});
|
|
212
|
+
if (!childID) return null;
|
|
213
|
+
if (!await isReady(token, childID, "carousel item", wait))
|
|
214
|
+
return null;
|
|
215
|
+
children.push(childID);
|
|
216
|
+
}
|
|
217
|
+
return children;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/socials/instagram/setters.ts
|
|
223
|
+
var publisher = createPublisher({
|
|
224
|
+
name: "Instagram",
|
|
225
|
+
getStatus: getInstagramContainerStatus,
|
|
226
|
+
createContainer: async (token, params) => readGraphID(
|
|
227
|
+
"Instagram media container",
|
|
228
|
+
await getInstagramData(token, "me/media", {
|
|
229
|
+
params,
|
|
230
|
+
method: "POST"
|
|
231
|
+
})
|
|
232
|
+
),
|
|
233
|
+
publish: async (token, creationID) => readGraphID(
|
|
234
|
+
"Instagram publish",
|
|
235
|
+
await getInstagramData(token, "me/media_publish", {
|
|
236
|
+
params: { creation_id: creationID },
|
|
237
|
+
method: "POST"
|
|
238
|
+
})
|
|
239
|
+
)
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// src/socials/meta/webhook.ts
|
|
138
243
|
import { NextResponse as NextResponse2 } from "next/server";
|
|
139
244
|
|
|
245
|
+
// src/socials/threads/utils.ts
|
|
246
|
+
var client2 = createGraphClient("https://graph.threads.net/v1.0");
|
|
247
|
+
var getThreadsData = client2.request;
|
|
248
|
+
|
|
249
|
+
// src/socials/threads/getters.ts
|
|
250
|
+
var getThreadsContainerStatus = async (token, id) => {
|
|
251
|
+
const result = await getThreadsData(token, id, { params: { fields: "status,error_message" } });
|
|
252
|
+
if (!result?.status) return null;
|
|
253
|
+
return { status: result.status, error: result.error_message };
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/socials/threads/setters.ts
|
|
257
|
+
var publisher2 = createPublisher({
|
|
258
|
+
name: "Threads",
|
|
259
|
+
getStatus: getThreadsContainerStatus,
|
|
260
|
+
createContainer: async (token, params) => readGraphID(
|
|
261
|
+
"Threads container",
|
|
262
|
+
await getThreadsData(token, "me/threads", {
|
|
263
|
+
params,
|
|
264
|
+
method: "POST"
|
|
265
|
+
})
|
|
266
|
+
),
|
|
267
|
+
publish: async (token, creationID) => readGraphID(
|
|
268
|
+
"Threads publish",
|
|
269
|
+
await getThreadsData(token, "me/threads_publish", {
|
|
270
|
+
params: { creation_id: creationID },
|
|
271
|
+
method: "POST"
|
|
272
|
+
})
|
|
273
|
+
)
|
|
274
|
+
});
|
|
275
|
+
|
|
140
276
|
// src/auth/instagram/route.ts
|
|
141
277
|
var getInstagramRoute = ({
|
|
142
278
|
redirectURL,
|
|
143
279
|
errorRedirectURL,
|
|
144
|
-
onUser
|
|
280
|
+
onUser,
|
|
281
|
+
scopes
|
|
145
282
|
}) => {
|
|
146
283
|
const handleError = (message) => NextResponse3.redirect(`${errorRedirectURL}?error=${message}`);
|
|
147
284
|
return async (req) => {
|
|
@@ -151,7 +288,10 @@ var getInstagramRoute = ({
|
|
|
151
288
|
if (error) return handleError(error);
|
|
152
289
|
if (!accessCode) {
|
|
153
290
|
if (!stateToken) return handleError("Invalid request");
|
|
154
|
-
return NextResponse3.redirect(
|
|
291
|
+
return NextResponse3.redirect(
|
|
292
|
+
getInstagramAuthorizationURL(stateToken, scopes),
|
|
293
|
+
302
|
|
294
|
+
);
|
|
155
295
|
}
|
|
156
296
|
if (!stateToken) return handleError("Invalid request");
|
|
157
297
|
const instagramData = await getLongLivedToken(
|