naystack 1.8.12 → 1.8.14
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 +34 -0
- package/dist/socials/meta/types.d.mts +51 -0
- package/dist/socials/meta/types.d.ts +51 -0
- package/dist/socials/meta/types.esm.js +9 -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
|
@@ -125,40 +125,177 @@ async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
var getInstagramAuthorizationURL = (token) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
128
|
+
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
129
129
|
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
130
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope
|
|
130
|
+
)}&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`;
|
|
131
131
|
|
|
132
|
-
// src/socials/
|
|
133
|
-
function
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
method: postData ? "POST" : "GET",
|
|
139
|
-
body: JSON.stringify(postData),
|
|
140
|
-
headers: {
|
|
141
|
-
Authorization: `Bearer ${token}`,
|
|
142
|
-
"Content-Type": "application/json"
|
|
132
|
+
// src/socials/meta/request.ts
|
|
133
|
+
function createGraphClient(baseURL) {
|
|
134
|
+
const buildURL = (token, path, params = {}) => {
|
|
135
|
+
const search = new URLSearchParams();
|
|
136
|
+
for (const [key, value] of Object.entries(params)) {
|
|
137
|
+
if (value !== void 0) search.set(key, String(value));
|
|
143
138
|
}
|
|
144
|
-
|
|
139
|
+
search.set("access_token", token);
|
|
140
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
141
|
+
};
|
|
142
|
+
const request = async (token, path, options = {}) => {
|
|
143
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
144
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
145
|
+
headers: {
|
|
146
|
+
Authorization: `Bearer ${token}`,
|
|
147
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
148
|
+
},
|
|
149
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
150
|
+
});
|
|
151
|
+
return response.json().catch(() => null);
|
|
152
|
+
};
|
|
153
|
+
return { buildURL, request };
|
|
145
154
|
}
|
|
155
|
+
function readGraphID(label, result) {
|
|
156
|
+
if (result?.error) {
|
|
157
|
+
console.error(`[naystack] ${label} failed: ${result.error.message}`);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return result?.id || null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/socials/instagram/utils.ts
|
|
164
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
165
|
+
var getInstagramData = client.request;
|
|
146
166
|
|
|
147
167
|
// src/socials/instagram/getters.ts
|
|
148
168
|
var getInstagramUser = (token, id, fields) => {
|
|
149
169
|
return getInstagramData(token, id || "me", {
|
|
150
|
-
|
|
170
|
+
params: {
|
|
171
|
+
fields: fields ? fields.join(",") : "username,followers_count,media_count"
|
|
172
|
+
}
|
|
151
173
|
});
|
|
152
174
|
};
|
|
175
|
+
var getInstagramContainerStatus = async (token, id) => {
|
|
176
|
+
const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
|
|
177
|
+
if (!result?.status_code) return null;
|
|
178
|
+
return {
|
|
179
|
+
status: result.status_code,
|
|
180
|
+
error: result.status_code === "ERROR" ? result.status : void 0
|
|
181
|
+
};
|
|
182
|
+
};
|
|
153
183
|
|
|
154
|
-
// src/socials/meta
|
|
184
|
+
// src/socials/meta/container.ts
|
|
185
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
186
|
+
async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
187
|
+
const deadline = Date.now() + timeoutMS;
|
|
188
|
+
let state = await getState();
|
|
189
|
+
if (!state) {
|
|
190
|
+
await sleep(2e3);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
|
|
194
|
+
await sleep(intervalMS);
|
|
195
|
+
state = await getState();
|
|
196
|
+
}
|
|
197
|
+
return state;
|
|
198
|
+
}
|
|
199
|
+
function createPublisher(platform) {
|
|
200
|
+
const isReady = async (token, id, label, wait) => {
|
|
201
|
+
const state = await waitForContainer(
|
|
202
|
+
() => platform.getStatus(token, id),
|
|
203
|
+
wait
|
|
204
|
+
);
|
|
205
|
+
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
206
|
+
console.error(
|
|
207
|
+
`[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
|
|
208
|
+
);
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
};
|
|
213
|
+
return {
|
|
214
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
215
|
+
publish: async (token, params, wait) => {
|
|
216
|
+
const containerID = await platform.createContainer(token, params);
|
|
217
|
+
if (!containerID) return null;
|
|
218
|
+
if (!await isReady(token, containerID, "container", wait)) return null;
|
|
219
|
+
return platform.publish(token, containerID);
|
|
220
|
+
},
|
|
221
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
222
|
+
createChildren: async (token, items, wait) => {
|
|
223
|
+
const children = [];
|
|
224
|
+
for (const item of items) {
|
|
225
|
+
const childID = await platform.createContainer(token, {
|
|
226
|
+
...item,
|
|
227
|
+
is_carousel_item: true
|
|
228
|
+
});
|
|
229
|
+
if (!childID) return null;
|
|
230
|
+
if (!await isReady(token, childID, "carousel item", wait))
|
|
231
|
+
return null;
|
|
232
|
+
children.push(childID);
|
|
233
|
+
}
|
|
234
|
+
return children;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/socials/instagram/setters.ts
|
|
240
|
+
var publisher = createPublisher({
|
|
241
|
+
name: "Instagram",
|
|
242
|
+
getStatus: getInstagramContainerStatus,
|
|
243
|
+
createContainer: async (token, params) => readGraphID(
|
|
244
|
+
"Instagram media container",
|
|
245
|
+
await getInstagramData(token, "me/media", {
|
|
246
|
+
params,
|
|
247
|
+
method: "POST"
|
|
248
|
+
})
|
|
249
|
+
),
|
|
250
|
+
publish: async (token, creationID) => readGraphID(
|
|
251
|
+
"Instagram publish",
|
|
252
|
+
await getInstagramData(token, "me/media_publish", {
|
|
253
|
+
params: { creation_id: creationID },
|
|
254
|
+
method: "POST"
|
|
255
|
+
})
|
|
256
|
+
)
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// src/socials/meta/webhook.ts
|
|
155
260
|
var import_server2 = require("next/server");
|
|
156
261
|
|
|
262
|
+
// src/socials/threads/utils.ts
|
|
263
|
+
var client2 = createGraphClient("https://graph.threads.net/v1.0");
|
|
264
|
+
var getThreadsData = client2.request;
|
|
265
|
+
|
|
266
|
+
// src/socials/threads/getters.ts
|
|
267
|
+
var getThreadsContainerStatus = async (token, id) => {
|
|
268
|
+
const result = await getThreadsData(token, id, { params: { fields: "status,error_message" } });
|
|
269
|
+
if (!result?.status) return null;
|
|
270
|
+
return { status: result.status, error: result.error_message };
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// src/socials/threads/setters.ts
|
|
274
|
+
var publisher2 = createPublisher({
|
|
275
|
+
name: "Threads",
|
|
276
|
+
getStatus: getThreadsContainerStatus,
|
|
277
|
+
createContainer: async (token, params) => readGraphID(
|
|
278
|
+
"Threads container",
|
|
279
|
+
await getThreadsData(token, "me/threads", {
|
|
280
|
+
params,
|
|
281
|
+
method: "POST"
|
|
282
|
+
})
|
|
283
|
+
),
|
|
284
|
+
publish: async (token, creationID) => readGraphID(
|
|
285
|
+
"Threads publish",
|
|
286
|
+
await getThreadsData(token, "me/threads_publish", {
|
|
287
|
+
params: { creation_id: creationID },
|
|
288
|
+
method: "POST"
|
|
289
|
+
})
|
|
290
|
+
)
|
|
291
|
+
});
|
|
292
|
+
|
|
157
293
|
// src/auth/instagram/route.ts
|
|
158
294
|
var getInstagramRoute = ({
|
|
159
295
|
redirectURL,
|
|
160
296
|
errorRedirectURL,
|
|
161
|
-
onUser
|
|
297
|
+
onUser,
|
|
298
|
+
scopes
|
|
162
299
|
}) => {
|
|
163
300
|
const handleError = (message) => import_server3.NextResponse.redirect(`${errorRedirectURL}?error=${message}`);
|
|
164
301
|
return async (req) => {
|
|
@@ -168,7 +305,10 @@ var getInstagramRoute = ({
|
|
|
168
305
|
if (error) return handleError(error);
|
|
169
306
|
if (!accessCode) {
|
|
170
307
|
if (!stateToken) return handleError("Invalid request");
|
|
171
|
-
return import_server3.NextResponse.redirect(
|
|
308
|
+
return import_server3.NextResponse.redirect(
|
|
309
|
+
getInstagramAuthorizationURL(stateToken, scopes),
|
|
310
|
+
302
|
|
311
|
+
);
|
|
172
312
|
}
|
|
173
313
|
if (!stateToken) return handleError("Invalid request");
|
|
174
314
|
const instagramData = await getLongLivedToken(
|
|
@@ -2,12 +2,14 @@ import { NextRequest, NextResponse } from 'next/server';
|
|
|
2
2
|
import { SetupInstagramAuthOptions } from './index.mjs';
|
|
3
3
|
import './utils.mjs';
|
|
4
4
|
import '../../socials/instagram/types.mjs';
|
|
5
|
+
import '../../socials/meta/container.mjs';
|
|
6
|
+
import '../../socials/meta/types.mjs';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Returns the GET route handler for Instagram OAuth callback.
|
|
8
10
|
* @param options - SetupInstagramAuthOptions (onUser, redirect URLs)
|
|
9
11
|
* @returns Async route handler for the OAuth callback
|
|
10
12
|
*/
|
|
11
|
-
declare const getInstagramRoute: ({ redirectURL, errorRedirectURL, onUser, }: SetupInstagramAuthOptions) => (req: NextRequest) => Promise<NextResponse<unknown>>;
|
|
13
|
+
declare const getInstagramRoute: ({ redirectURL, errorRedirectURL, onUser, scopes, }: SetupInstagramAuthOptions) => (req: NextRequest) => Promise<NextResponse<unknown>>;
|
|
12
14
|
|
|
13
15
|
export { getInstagramRoute };
|
|
@@ -2,12 +2,14 @@ import { NextRequest, NextResponse } from 'next/server';
|
|
|
2
2
|
import { SetupInstagramAuthOptions } from './index.js';
|
|
3
3
|
import './utils.js';
|
|
4
4
|
import '../../socials/instagram/types.js';
|
|
5
|
+
import '../../socials/meta/container.js';
|
|
6
|
+
import '../../socials/meta/types.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Returns the GET route handler for Instagram OAuth callback.
|
|
8
10
|
* @param options - SetupInstagramAuthOptions (onUser, redirect URLs)
|
|
9
11
|
* @returns Async route handler for the OAuth callback
|
|
10
12
|
*/
|
|
11
|
-
declare const getInstagramRoute: ({ redirectURL, errorRedirectURL, onUser, }: SetupInstagramAuthOptions) => (req: NextRequest) => Promise<NextResponse<unknown>>;
|
|
13
|
+
declare const getInstagramRoute: ({ redirectURL, errorRedirectURL, onUser, scopes, }: SetupInstagramAuthOptions) => (req: NextRequest) => Promise<NextResponse<unknown>>;
|
|
12
14
|
|
|
13
15
|
export { getInstagramRoute };
|
|
@@ -101,40 +101,177 @@ async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
var getInstagramAuthorizationURL = (token) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
104
|
+
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
105
105
|
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
106
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope
|
|
106
|
+
)}&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`;
|
|
107
107
|
|
|
108
|
-
// src/socials/
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
method: postData ? "POST" : "GET",
|
|
115
|
-
body: JSON.stringify(postData),
|
|
116
|
-
headers: {
|
|
117
|
-
Authorization: `Bearer ${token}`,
|
|
118
|
-
"Content-Type": "application/json"
|
|
108
|
+
// src/socials/meta/request.ts
|
|
109
|
+
function createGraphClient(baseURL) {
|
|
110
|
+
const buildURL = (token, path, params = {}) => {
|
|
111
|
+
const search = new URLSearchParams();
|
|
112
|
+
for (const [key, value] of Object.entries(params)) {
|
|
113
|
+
if (value !== void 0) search.set(key, String(value));
|
|
119
114
|
}
|
|
120
|
-
|
|
115
|
+
search.set("access_token", token);
|
|
116
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
117
|
+
};
|
|
118
|
+
const request = async (token, path, options = {}) => {
|
|
119
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
120
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
121
|
+
headers: {
|
|
122
|
+
Authorization: `Bearer ${token}`,
|
|
123
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
124
|
+
},
|
|
125
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
126
|
+
});
|
|
127
|
+
return response.json().catch(() => null);
|
|
128
|
+
};
|
|
129
|
+
return { buildURL, request };
|
|
121
130
|
}
|
|
131
|
+
function readGraphID(label, result) {
|
|
132
|
+
if (result?.error) {
|
|
133
|
+
console.error(`[naystack] ${label} failed: ${result.error.message}`);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return result?.id || null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/socials/instagram/utils.ts
|
|
140
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
141
|
+
var getInstagramData = client.request;
|
|
122
142
|
|
|
123
143
|
// src/socials/instagram/getters.ts
|
|
124
144
|
var getInstagramUser = (token, id, fields) => {
|
|
125
145
|
return getInstagramData(token, id || "me", {
|
|
126
|
-
|
|
146
|
+
params: {
|
|
147
|
+
fields: fields ? fields.join(",") : "username,followers_count,media_count"
|
|
148
|
+
}
|
|
127
149
|
});
|
|
128
150
|
};
|
|
151
|
+
var getInstagramContainerStatus = async (token, id) => {
|
|
152
|
+
const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
|
|
153
|
+
if (!result?.status_code) return null;
|
|
154
|
+
return {
|
|
155
|
+
status: result.status_code,
|
|
156
|
+
error: result.status_code === "ERROR" ? result.status : void 0
|
|
157
|
+
};
|
|
158
|
+
};
|
|
129
159
|
|
|
130
|
-
// src/socials/meta
|
|
160
|
+
// src/socials/meta/container.ts
|
|
161
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
162
|
+
async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
163
|
+
const deadline = Date.now() + timeoutMS;
|
|
164
|
+
let state = await getState();
|
|
165
|
+
if (!state) {
|
|
166
|
+
await sleep(2e3);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
|
|
170
|
+
await sleep(intervalMS);
|
|
171
|
+
state = await getState();
|
|
172
|
+
}
|
|
173
|
+
return state;
|
|
174
|
+
}
|
|
175
|
+
function createPublisher(platform) {
|
|
176
|
+
const isReady = async (token, id, label, wait) => {
|
|
177
|
+
const state = await waitForContainer(
|
|
178
|
+
() => platform.getStatus(token, id),
|
|
179
|
+
wait
|
|
180
|
+
);
|
|
181
|
+
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
182
|
+
console.error(
|
|
183
|
+
`[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
|
|
184
|
+
);
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
191
|
+
publish: async (token, params, wait) => {
|
|
192
|
+
const containerID = await platform.createContainer(token, params);
|
|
193
|
+
if (!containerID) return null;
|
|
194
|
+
if (!await isReady(token, containerID, "container", wait)) return null;
|
|
195
|
+
return platform.publish(token, containerID);
|
|
196
|
+
},
|
|
197
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
198
|
+
createChildren: async (token, items, wait) => {
|
|
199
|
+
const children = [];
|
|
200
|
+
for (const item of items) {
|
|
201
|
+
const childID = await platform.createContainer(token, {
|
|
202
|
+
...item,
|
|
203
|
+
is_carousel_item: true
|
|
204
|
+
});
|
|
205
|
+
if (!childID) return null;
|
|
206
|
+
if (!await isReady(token, childID, "carousel item", wait))
|
|
207
|
+
return null;
|
|
208
|
+
children.push(childID);
|
|
209
|
+
}
|
|
210
|
+
return children;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/socials/instagram/setters.ts
|
|
216
|
+
var publisher = createPublisher({
|
|
217
|
+
name: "Instagram",
|
|
218
|
+
getStatus: getInstagramContainerStatus,
|
|
219
|
+
createContainer: async (token, params) => readGraphID(
|
|
220
|
+
"Instagram media container",
|
|
221
|
+
await getInstagramData(token, "me/media", {
|
|
222
|
+
params,
|
|
223
|
+
method: "POST"
|
|
224
|
+
})
|
|
225
|
+
),
|
|
226
|
+
publish: async (token, creationID) => readGraphID(
|
|
227
|
+
"Instagram publish",
|
|
228
|
+
await getInstagramData(token, "me/media_publish", {
|
|
229
|
+
params: { creation_id: creationID },
|
|
230
|
+
method: "POST"
|
|
231
|
+
})
|
|
232
|
+
)
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// src/socials/meta/webhook.ts
|
|
131
236
|
import { NextResponse as NextResponse2 } from "next/server";
|
|
132
237
|
|
|
238
|
+
// src/socials/threads/utils.ts
|
|
239
|
+
var client2 = createGraphClient("https://graph.threads.net/v1.0");
|
|
240
|
+
var getThreadsData = client2.request;
|
|
241
|
+
|
|
242
|
+
// src/socials/threads/getters.ts
|
|
243
|
+
var getThreadsContainerStatus = async (token, id) => {
|
|
244
|
+
const result = await getThreadsData(token, id, { params: { fields: "status,error_message" } });
|
|
245
|
+
if (!result?.status) return null;
|
|
246
|
+
return { status: result.status, error: result.error_message };
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// src/socials/threads/setters.ts
|
|
250
|
+
var publisher2 = createPublisher({
|
|
251
|
+
name: "Threads",
|
|
252
|
+
getStatus: getThreadsContainerStatus,
|
|
253
|
+
createContainer: async (token, params) => readGraphID(
|
|
254
|
+
"Threads container",
|
|
255
|
+
await getThreadsData(token, "me/threads", {
|
|
256
|
+
params,
|
|
257
|
+
method: "POST"
|
|
258
|
+
})
|
|
259
|
+
),
|
|
260
|
+
publish: async (token, creationID) => readGraphID(
|
|
261
|
+
"Threads publish",
|
|
262
|
+
await getThreadsData(token, "me/threads_publish", {
|
|
263
|
+
params: { creation_id: creationID },
|
|
264
|
+
method: "POST"
|
|
265
|
+
})
|
|
266
|
+
)
|
|
267
|
+
});
|
|
268
|
+
|
|
133
269
|
// src/auth/instagram/route.ts
|
|
134
270
|
var getInstagramRoute = ({
|
|
135
271
|
redirectURL,
|
|
136
272
|
errorRedirectURL,
|
|
137
|
-
onUser
|
|
273
|
+
onUser,
|
|
274
|
+
scopes
|
|
138
275
|
}) => {
|
|
139
276
|
const handleError = (message) => NextResponse3.redirect(`${errorRedirectURL}?error=${message}`);
|
|
140
277
|
return async (req) => {
|
|
@@ -144,7 +281,10 @@ var getInstagramRoute = ({
|
|
|
144
281
|
if (error) return handleError(error);
|
|
145
282
|
if (!accessCode) {
|
|
146
283
|
if (!stateToken) return handleError("Invalid request");
|
|
147
|
-
return NextResponse3.redirect(
|
|
284
|
+
return NextResponse3.redirect(
|
|
285
|
+
getInstagramAuthorizationURL(stateToken, scopes),
|
|
286
|
+
302
|
|
287
|
+
);
|
|
148
288
|
}
|
|
149
289
|
if (!stateToken) return handleError("Invalid request");
|
|
150
290
|
const instagramData = await getLongLivedToken(
|
|
@@ -112,9 +112,9 @@ async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
var getInstagramAuthorizationURL = (token) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
115
|
+
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
116
116
|
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
117
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope
|
|
117
|
+
)}&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`;
|
|
118
118
|
// Annotate the CommonJS export names for ESM import in node:
|
|
119
119
|
0 && (module.exports = {
|
|
120
120
|
getInstagramAuthorizationURL,
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permissions requestable during Instagram Login OAuth. `instagram_business_basic`
|
|
3
|
+
* is enough to read a profile; publishing needs `instagram_business_content_publish`.
|
|
4
|
+
*
|
|
5
|
+
* Widening the scopes only affects users who authorize afterwards — tokens already
|
|
6
|
+
* stored keep the permissions they were granted, so existing users must reconnect.
|
|
7
|
+
*
|
|
8
|
+
* @category Auth
|
|
9
|
+
*/
|
|
10
|
+
type InstagramScope = "instagram_business_basic" | "instagram_business_content_publish" | "instagram_business_manage_messages" | "instagram_business_manage_comments" | "instagram_business_manage_insights";
|
|
1
11
|
/**
|
|
2
12
|
* Exchanges a long-lived Instagram token for a refreshed token.
|
|
3
13
|
* @param token - Current long-lived access token
|
|
@@ -29,6 +39,7 @@ declare function getLongLivedToken(code: string, redirectURL: string, clientId:
|
|
|
29
39
|
* simply built on the server instead of shipped in the client bundle.
|
|
30
40
|
*
|
|
31
41
|
* @param token - The state token to embed in the authorization URL (typically the user's session or access token).
|
|
42
|
+
* @param scopes - Permissions to request. Default: `["instagram_business_basic"]`.
|
|
32
43
|
* @returns The full Instagram OAuth authorization URL.
|
|
33
44
|
*
|
|
34
45
|
* @example
|
|
@@ -41,6 +52,6 @@ declare function getLongLivedToken(code: string, redirectURL: string, clientId:
|
|
|
41
52
|
*
|
|
42
53
|
* @category Auth
|
|
43
54
|
*/
|
|
44
|
-
declare const getInstagramAuthorizationURL: (token: string) => string;
|
|
55
|
+
declare const getInstagramAuthorizationURL: (token: string, scopes?: InstagramScope[]) => string;
|
|
45
56
|
|
|
46
|
-
export { getInstagramAuthorizationURL, getLongLivedToken, getRefreshedInstagramAccessToken };
|
|
57
|
+
export { type InstagramScope, getInstagramAuthorizationURL, getLongLivedToken, getRefreshedInstagramAccessToken };
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permissions requestable during Instagram Login OAuth. `instagram_business_basic`
|
|
3
|
+
* is enough to read a profile; publishing needs `instagram_business_content_publish`.
|
|
4
|
+
*
|
|
5
|
+
* Widening the scopes only affects users who authorize afterwards — tokens already
|
|
6
|
+
* stored keep the permissions they were granted, so existing users must reconnect.
|
|
7
|
+
*
|
|
8
|
+
* @category Auth
|
|
9
|
+
*/
|
|
10
|
+
type InstagramScope = "instagram_business_basic" | "instagram_business_content_publish" | "instagram_business_manage_messages" | "instagram_business_manage_comments" | "instagram_business_manage_insights";
|
|
1
11
|
/**
|
|
2
12
|
* Exchanges a long-lived Instagram token for a refreshed token.
|
|
3
13
|
* @param token - Current long-lived access token
|
|
@@ -29,6 +39,7 @@ declare function getLongLivedToken(code: string, redirectURL: string, clientId:
|
|
|
29
39
|
* simply built on the server instead of shipped in the client bundle.
|
|
30
40
|
*
|
|
31
41
|
* @param token - The state token to embed in the authorization URL (typically the user's session or access token).
|
|
42
|
+
* @param scopes - Permissions to request. Default: `["instagram_business_basic"]`.
|
|
32
43
|
* @returns The full Instagram OAuth authorization URL.
|
|
33
44
|
*
|
|
34
45
|
* @example
|
|
@@ -41,6 +52,6 @@ declare function getLongLivedToken(code: string, redirectURL: string, clientId:
|
|
|
41
52
|
*
|
|
42
53
|
* @category Auth
|
|
43
54
|
*/
|
|
44
|
-
declare const getInstagramAuthorizationURL: (token: string) => string;
|
|
55
|
+
declare const getInstagramAuthorizationURL: (token: string, scopes?: InstagramScope[]) => string;
|
|
45
56
|
|
|
46
|
-
export { getInstagramAuthorizationURL, getLongLivedToken, getRefreshedInstagramAccessToken };
|
|
57
|
+
export { type InstagramScope, getInstagramAuthorizationURL, getLongLivedToken, getRefreshedInstagramAccessToken };
|
|
@@ -84,9 +84,9 @@ async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
var getInstagramAuthorizationURL = (token) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
87
|
+
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
88
88
|
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
89
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope
|
|
89
|
+
)}&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`;
|
|
90
90
|
export {
|
|
91
91
|
getInstagramAuthorizationURL,
|
|
92
92
|
getLongLivedToken,
|
package/dist/file/setup.d.mts
CHANGED
|
@@ -5,6 +5,12 @@ import '@aws-sdk/client-s3';
|
|
|
5
5
|
/**
|
|
6
6
|
* Options for setting up file upload via {@link setupFileUpload}.
|
|
7
7
|
*
|
|
8
|
+
* Every callback receives the same `{ type, userId, data }` context. **`data` is
|
|
9
|
+
* optional** — it is whatever the caller passed as `config.data` to
|
|
10
|
+
* `useFileUpload`, and the client only appends the form field when a value is
|
|
11
|
+
* given, so the route parses it to `undefined` otherwise. Narrow it before use;
|
|
12
|
+
* destructuring it unguarded throws at runtime.
|
|
13
|
+
*
|
|
8
14
|
* @property getKey - Optional. Given `{ type, userId, data }`, returns the S3 object key for the upload. If omitted, a UUID is generated.
|
|
9
15
|
* @property processFile - Optional. Given the uploaded `file` and `{ type, userId, data }`, returns the blob to
|
|
10
16
|
* store in its place — e.g. re-encoding an image, or stripping metadata. Runs after authentication and before the
|
|
@@ -22,23 +28,23 @@ interface SetupFileUploadOptions {
|
|
|
22
28
|
getKey?: (data: {
|
|
23
29
|
type: string;
|
|
24
30
|
userId: number;
|
|
25
|
-
data
|
|
31
|
+
data?: object;
|
|
26
32
|
}) => Promise<string>;
|
|
27
33
|
processFile?: (file: File, data: {
|
|
28
34
|
type: string;
|
|
29
35
|
userId: number;
|
|
30
|
-
data
|
|
36
|
+
data?: object;
|
|
31
37
|
}) => Promise<Blob>;
|
|
32
38
|
putOptions?: (data: {
|
|
33
39
|
type: string;
|
|
34
40
|
userId: number;
|
|
35
|
-
data
|
|
41
|
+
data?: object;
|
|
36
42
|
}) => PutOverrides;
|
|
37
43
|
onUpload: (data: {
|
|
38
44
|
url: string | null;
|
|
39
45
|
type: string;
|
|
40
46
|
userId: number;
|
|
41
|
-
data
|
|
47
|
+
data?: object;
|
|
42
48
|
}) => Promise<object>;
|
|
43
49
|
}
|
|
44
50
|
/**
|
|
@@ -65,7 +71,9 @@ interface SetupFileUploadOptions {
|
|
|
65
71
|
* export const { PUT } = setupFileUpload({
|
|
66
72
|
* onUpload: async ({ url, type, userId, data }) => {
|
|
67
73
|
* if (type === "DealDocument" && url) {
|
|
68
|
-
*
|
|
74
|
+
* // `data` is absent unless the client sent one — check before reading it.
|
|
75
|
+
* const payload = data as { dealId?: number; fileName?: string } | undefined;
|
|
76
|
+
* if (!payload?.dealId) return {};
|
|
69
77
|
* const [row] = await db.insert(DocumentsTable).values({
|
|
70
78
|
* dealId: payload.dealId, fileURL: url, fileName: payload.fileName,
|
|
71
79
|
* }).returning();
|
package/dist/file/setup.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ import '@aws-sdk/client-s3';
|
|
|
5
5
|
/**
|
|
6
6
|
* Options for setting up file upload via {@link setupFileUpload}.
|
|
7
7
|
*
|
|
8
|
+
* Every callback receives the same `{ type, userId, data }` context. **`data` is
|
|
9
|
+
* optional** — it is whatever the caller passed as `config.data` to
|
|
10
|
+
* `useFileUpload`, and the client only appends the form field when a value is
|
|
11
|
+
* given, so the route parses it to `undefined` otherwise. Narrow it before use;
|
|
12
|
+
* destructuring it unguarded throws at runtime.
|
|
13
|
+
*
|
|
8
14
|
* @property getKey - Optional. Given `{ type, userId, data }`, returns the S3 object key for the upload. If omitted, a UUID is generated.
|
|
9
15
|
* @property processFile - Optional. Given the uploaded `file` and `{ type, userId, data }`, returns the blob to
|
|
10
16
|
* store in its place — e.g. re-encoding an image, or stripping metadata. Runs after authentication and before the
|
|
@@ -22,23 +28,23 @@ interface SetupFileUploadOptions {
|
|
|
22
28
|
getKey?: (data: {
|
|
23
29
|
type: string;
|
|
24
30
|
userId: number;
|
|
25
|
-
data
|
|
31
|
+
data?: object;
|
|
26
32
|
}) => Promise<string>;
|
|
27
33
|
processFile?: (file: File, data: {
|
|
28
34
|
type: string;
|
|
29
35
|
userId: number;
|
|
30
|
-
data
|
|
36
|
+
data?: object;
|
|
31
37
|
}) => Promise<Blob>;
|
|
32
38
|
putOptions?: (data: {
|
|
33
39
|
type: string;
|
|
34
40
|
userId: number;
|
|
35
|
-
data
|
|
41
|
+
data?: object;
|
|
36
42
|
}) => PutOverrides;
|
|
37
43
|
onUpload: (data: {
|
|
38
44
|
url: string | null;
|
|
39
45
|
type: string;
|
|
40
46
|
userId: number;
|
|
41
|
-
data
|
|
47
|
+
data?: object;
|
|
42
48
|
}) => Promise<object>;
|
|
43
49
|
}
|
|
44
50
|
/**
|
|
@@ -65,7 +71,9 @@ interface SetupFileUploadOptions {
|
|
|
65
71
|
* export const { PUT } = setupFileUpload({
|
|
66
72
|
* onUpload: async ({ url, type, userId, data }) => {
|
|
67
73
|
* if (type === "DealDocument" && url) {
|
|
68
|
-
*
|
|
74
|
+
* // `data` is absent unless the client sent one — check before reading it.
|
|
75
|
+
* const payload = data as { dealId?: number; fileName?: string } | undefined;
|
|
76
|
+
* if (!payload?.dealId) return {};
|
|
69
77
|
* const [row] = await db.insert(DocumentsTable).values({
|
|
70
78
|
* dealId: payload.dealId, fileURL: url, fileName: payload.fileName,
|
|
71
79
|
* }).returning();
|
package/dist/index.d.mts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - `naystack/file` — S3 file upload setup
|
|
11
11
|
* - `naystack/file/client` — Client-side file upload hook (useFileUpload)
|
|
12
12
|
* - `naystack/client` — Client hooks (useVisibility, useBreakpoint) and SEO (setupSEO)
|
|
13
|
-
* - `naystack/socials` — Instagram and Threads
|
|
13
|
+
* - `naystack/socials` — publish to and read from Instagram and Threads
|
|
14
14
|
*
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - `naystack/file` — S3 file upload setup
|
|
11
11
|
* - `naystack/file/client` — Client-side file upload hook (useFileUpload)
|
|
12
12
|
* - `naystack/client` — Client hooks (useVisibility, useBreakpoint) and SEO (setupSEO)
|
|
13
|
-
* - `naystack/socials` — Instagram and Threads
|
|
13
|
+
* - `naystack/socials` — publish to and read from Instagram and Threads
|
|
14
14
|
*
|
|
15
15
|
* @module
|
|
16
16
|
*/
|