@supaku/agentfactory-nextjs 0.3.0 → 0.4.1
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/dist/src/factory.d.ts +34 -6
- package/dist/src/factory.d.ts.map +1 -1
- package/dist/src/factory.js +37 -7
- package/dist/src/handlers/oauth/callback.d.ts +36 -0
- package/dist/src/handlers/oauth/callback.d.ts.map +1 -0
- package/dist/src/handlers/oauth/callback.js +92 -0
- package/dist/src/index.d.ts +11 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -2
- package/dist/src/linear-client-resolver.d.ts +37 -0
- package/dist/src/linear-client-resolver.d.ts.map +1 -0
- package/dist/src/linear-client-resolver.js +60 -0
- package/dist/src/middleware/factory.d.ts +32 -0
- package/dist/src/middleware/factory.d.ts.map +1 -0
- package/dist/src/middleware/factory.js +115 -0
- package/dist/src/middleware/types.d.ts +35 -0
- package/dist/src/middleware/types.d.ts.map +1 -0
- package/dist/src/middleware/types.js +4 -0
- package/dist/src/orchestrator/error-formatting.d.ts +8 -0
- package/dist/src/orchestrator/error-formatting.d.ts.map +1 -0
- package/dist/src/orchestrator/error-formatting.js +35 -0
- package/dist/src/orchestrator/index.d.ts +4 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -0
- package/dist/src/orchestrator/index.js +2 -0
- package/dist/src/orchestrator/types.d.ts +53 -0
- package/dist/src/orchestrator/types.d.ts.map +1 -0
- package/dist/src/orchestrator/types.js +4 -0
- package/dist/src/orchestrator/webhook-orchestrator.d.ts +32 -0
- package/dist/src/orchestrator/webhook-orchestrator.d.ts.map +1 -0
- package/dist/src/orchestrator/webhook-orchestrator.js +300 -0
- package/dist/src/types.d.ts +16 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/webhook/handlers/issue-updated.d.ts +2 -2
- package/dist/src/webhook/handlers/issue-updated.d.ts.map +1 -1
- package/dist/src/webhook/handlers/session-created.d.ts +2 -2
- package/dist/src/webhook/handlers/session-created.d.ts.map +1 -1
- package/dist/src/webhook/handlers/session-prompted.d.ts +2 -2
- package/dist/src/webhook/handlers/session-prompted.d.ts.map +1 -1
- package/dist/src/webhook/handlers/session-updated.d.ts +2 -2
- package/dist/src/webhook/handlers/session-updated.d.ts.map +1 -1
- package/dist/src/webhook/processor.d.ts +2 -2
- package/dist/src/webhook/processor.d.ts.map +1 -1
- package/dist/src/webhook/utils.d.ts +5 -5
- package/dist/src/webhook/utils.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/src/factory.d.ts
CHANGED
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
* Wires all handlers together from a single configuration object.
|
|
5
5
|
* Consumers call `createAllRoutes(config)` and get back a nested
|
|
6
6
|
* route tree that maps 1:1 onto Next.js App Router exports.
|
|
7
|
+
*
|
|
8
|
+
* When optional config fields are omitted, sensible defaults from
|
|
9
|
+
* @supaku/agentfactory-linear are used (defaultGeneratePrompt, etc.).
|
|
7
10
|
*/
|
|
8
11
|
import type { RouteHandler, WebhookConfig, CronConfig } from './types.js';
|
|
12
|
+
import { type OAuthConfig } from './handlers/oauth/callback.js';
|
|
9
13
|
export interface AllRoutes {
|
|
10
14
|
workers: {
|
|
11
15
|
register: {
|
|
@@ -84,22 +88,46 @@ export interface AllRoutes {
|
|
|
84
88
|
POST: RouteHandler;
|
|
85
89
|
GET: RouteHandler;
|
|
86
90
|
};
|
|
91
|
+
oauth: {
|
|
92
|
+
callback: {
|
|
93
|
+
GET: RouteHandler;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Configuration for createAllRoutes.
|
|
99
|
+
* Extends WebhookConfig & CronConfig with optional OAuth config.
|
|
100
|
+
*/
|
|
101
|
+
export interface AllRoutesConfig extends WebhookConfig, CronConfig {
|
|
102
|
+
/** OAuth configuration for the callback handler */
|
|
103
|
+
oauth?: OAuthConfig;
|
|
87
104
|
}
|
|
88
105
|
/**
|
|
89
106
|
* Create all route handlers from a single config object.
|
|
90
107
|
*
|
|
108
|
+
* Optional fields fall back to sensible defaults from @supaku/agentfactory-linear:
|
|
109
|
+
* - `generatePrompt` → `defaultGeneratePrompt`
|
|
110
|
+
* - `detectWorkTypeFromPrompt` → `defaultDetectWorkTypeFromPrompt`
|
|
111
|
+
* - `getPriority` → `defaultGetPriority`
|
|
112
|
+
* - `buildParentQAContext` → `defaultBuildParentQAContext`
|
|
113
|
+
* - `buildParentAcceptanceContext` → `defaultBuildParentAcceptanceContext`
|
|
114
|
+
*
|
|
91
115
|
* @example
|
|
92
116
|
* ```typescript
|
|
93
|
-
* import { createAllRoutes } from '@supaku/agentfactory-nextjs'
|
|
117
|
+
* import { createAllRoutes, createDefaultLinearClientResolver } from '@supaku/agentfactory-nextjs'
|
|
94
118
|
*
|
|
119
|
+
* // Minimal — everything uses defaults
|
|
95
120
|
* const routes = createAllRoutes({
|
|
96
|
-
* linearClient:
|
|
97
|
-
* generatePrompt: (id, workType) => `Work on ${id}`,
|
|
121
|
+
* linearClient: createDefaultLinearClientResolver(),
|
|
98
122
|
* })
|
|
99
123
|
*
|
|
100
|
-
* //
|
|
101
|
-
*
|
|
124
|
+
* // Custom — override specific callbacks
|
|
125
|
+
* const routes = createAllRoutes({
|
|
126
|
+
* linearClient: createDefaultLinearClientResolver(),
|
|
127
|
+
* generatePrompt: myCustomPromptFn,
|
|
128
|
+
* oauth: { clientId: '...', clientSecret: '...' },
|
|
129
|
+
* })
|
|
102
130
|
* ```
|
|
103
131
|
*/
|
|
104
|
-
export declare function createAllRoutes(config:
|
|
132
|
+
export declare function createAllRoutes(config: AllRoutesConfig): AllRoutes;
|
|
105
133
|
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/factory.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAe,aAAa,EAAyB,UAAU,EAAE,MAAM,YAAY,CAAA;AA4C7G,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAE3F,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE;QACP,QAAQ,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QAChC,IAAI,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;QAC3B,MAAM,EAAE;YAAE,GAAG,EAAE,YAAY,CAAC;YAAC,MAAM,EAAE,YAAY,CAAA;SAAE,CAAA;QACnD,SAAS,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QACjC,IAAI,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;KAC5B,CAAA;IACD,QAAQ,EAAE;QACR,IAAI,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;QAC3B,MAAM,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;QAC7B,KAAK,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QAC7B,MAAM,EAAE;YAAE,GAAG,EAAE,YAAY,CAAC;YAAC,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QACjD,WAAW,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QACnC,OAAO,EAAE;YAAE,GAAG,EAAE,YAAY,CAAC;YAAC,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QAClD,iBAAiB,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QACzC,QAAQ,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QAChC,UAAU,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QAClC,YAAY,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QACpC,QAAQ,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;QAChC,SAAS,EAAE;YAAE,IAAI,EAAE,YAAY,CAAA;SAAE,CAAA;KAClC,CAAA;IACD,MAAM,EAAE;QACN,KAAK,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;QAC5B,QAAQ,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;QAC/B,aAAa,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;KACrC,CAAA;IACD,OAAO,EAAE;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAA;IAClD,OAAO,EAAE;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAA;IAClD,KAAK,EAAE;QACL,QAAQ,EAAE;YAAE,GAAG,EAAE,YAAY,CAAA;SAAE,CAAA;KAChC,CAAA;CACF;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,aAAa,EAAE,UAAU;IAChE,mDAAmD;IACnD,KAAK,CAAC,EAAE,WAAW,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAqDlE"}
|
package/dist/src/factory.js
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* Wires all handlers together from a single configuration object.
|
|
5
5
|
* Consumers call `createAllRoutes(config)` and get back a nested
|
|
6
6
|
* route tree that maps 1:1 onto Next.js App Router exports.
|
|
7
|
+
*
|
|
8
|
+
* When optional config fields are omitted, sensible defaults from
|
|
9
|
+
* @supaku/agentfactory-linear are used (defaultGeneratePrompt, etc.).
|
|
7
10
|
*/
|
|
11
|
+
import { defaultGeneratePrompt, defaultDetectWorkTypeFromPrompt, defaultGetPriority, defaultBuildParentQAContext, defaultBuildParentAcceptanceContext, } from '@supaku/agentfactory-linear';
|
|
8
12
|
// Worker handlers
|
|
9
13
|
import { createWorkerRegisterHandler } from './handlers/workers/register.js';
|
|
10
14
|
import { createWorkerListHandler } from './handlers/workers/list.js';
|
|
@@ -33,29 +37,52 @@ import { createPublicSessionDetailHandler } from './handlers/public/session-deta
|
|
|
33
37
|
import { createCleanupHandler } from './handlers/cleanup.js';
|
|
34
38
|
// Webhook processor
|
|
35
39
|
import { createWebhookHandler } from './webhook/processor.js';
|
|
40
|
+
// OAuth handler
|
|
41
|
+
import { createOAuthCallbackHandler } from './handlers/oauth/callback.js';
|
|
36
42
|
/**
|
|
37
43
|
* Create all route handlers from a single config object.
|
|
38
44
|
*
|
|
45
|
+
* Optional fields fall back to sensible defaults from @supaku/agentfactory-linear:
|
|
46
|
+
* - `generatePrompt` → `defaultGeneratePrompt`
|
|
47
|
+
* - `detectWorkTypeFromPrompt` → `defaultDetectWorkTypeFromPrompt`
|
|
48
|
+
* - `getPriority` → `defaultGetPriority`
|
|
49
|
+
* - `buildParentQAContext` → `defaultBuildParentQAContext`
|
|
50
|
+
* - `buildParentAcceptanceContext` → `defaultBuildParentAcceptanceContext`
|
|
51
|
+
*
|
|
39
52
|
* @example
|
|
40
53
|
* ```typescript
|
|
41
|
-
* import { createAllRoutes } from '@supaku/agentfactory-nextjs'
|
|
54
|
+
* import { createAllRoutes, createDefaultLinearClientResolver } from '@supaku/agentfactory-nextjs'
|
|
42
55
|
*
|
|
56
|
+
* // Minimal — everything uses defaults
|
|
43
57
|
* const routes = createAllRoutes({
|
|
44
|
-
* linearClient:
|
|
45
|
-
* generatePrompt: (id, workType) => `Work on ${id}`,
|
|
58
|
+
* linearClient: createDefaultLinearClientResolver(),
|
|
46
59
|
* })
|
|
47
60
|
*
|
|
48
|
-
* //
|
|
49
|
-
*
|
|
61
|
+
* // Custom — override specific callbacks
|
|
62
|
+
* const routes = createAllRoutes({
|
|
63
|
+
* linearClient: createDefaultLinearClientResolver(),
|
|
64
|
+
* generatePrompt: myCustomPromptFn,
|
|
65
|
+
* oauth: { clientId: '...', clientSecret: '...' },
|
|
66
|
+
* })
|
|
50
67
|
* ```
|
|
51
68
|
*/
|
|
52
69
|
export function createAllRoutes(config) {
|
|
70
|
+
// Apply defaults for optional webhook config fields
|
|
71
|
+
const webhookConfig = {
|
|
72
|
+
...config,
|
|
73
|
+
generatePrompt: config.generatePrompt ?? defaultGeneratePrompt,
|
|
74
|
+
detectWorkTypeFromPrompt: config.detectWorkTypeFromPrompt ?? defaultDetectWorkTypeFromPrompt,
|
|
75
|
+
getPriority: config.getPriority ?? defaultGetPriority,
|
|
76
|
+
buildParentQAContext: config.buildParentQAContext ?? defaultBuildParentQAContext,
|
|
77
|
+
buildParentAcceptanceContext: config.buildParentAcceptanceContext ?? defaultBuildParentAcceptanceContext,
|
|
78
|
+
};
|
|
53
79
|
const routeConfig = {
|
|
54
80
|
linearClient: config.linearClient,
|
|
55
81
|
appUrl: config.appUrl,
|
|
56
82
|
};
|
|
57
|
-
const cleanup = createCleanupHandler(
|
|
58
|
-
const webhook = createWebhookHandler(
|
|
83
|
+
const cleanup = createCleanupHandler(webhookConfig);
|
|
84
|
+
const webhook = createWebhookHandler(webhookConfig);
|
|
85
|
+
const oauth = createOAuthCallbackHandler(config.oauth);
|
|
59
86
|
return {
|
|
60
87
|
workers: {
|
|
61
88
|
register: { POST: createWorkerRegisterHandler() },
|
|
@@ -85,5 +112,8 @@ export function createAllRoutes(config) {
|
|
|
85
112
|
},
|
|
86
113
|
cleanup: { POST: cleanup.POST, GET: cleanup.GET },
|
|
87
114
|
webhook: { POST: webhook.POST, GET: webhook.GET },
|
|
115
|
+
oauth: {
|
|
116
|
+
callback: { GET: oauth.GET },
|
|
117
|
+
},
|
|
88
118
|
};
|
|
89
119
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Callback Handler for Linear
|
|
3
|
+
*
|
|
4
|
+
* Exchanges an authorization code for an access token and stores it
|
|
5
|
+
* in Redis for workspace-specific token resolution.
|
|
6
|
+
*
|
|
7
|
+
* @see https://developers.linear.app/docs/oauth/authentication
|
|
8
|
+
*/
|
|
9
|
+
import type { RouteHandler } from '../../types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Configuration for the OAuth callback handler.
|
|
12
|
+
*/
|
|
13
|
+
export interface OAuthConfig {
|
|
14
|
+
/** Linear OAuth client ID (defaults to LINEAR_CLIENT_ID env) */
|
|
15
|
+
clientId?: string;
|
|
16
|
+
/** Linear OAuth client secret (defaults to LINEAR_CLIENT_SECRET env) */
|
|
17
|
+
clientSecret?: string;
|
|
18
|
+
/** Application URL for redirect URI (defaults to NEXT_PUBLIC_APP_URL env) */
|
|
19
|
+
appUrl?: string;
|
|
20
|
+
/** Redirect path on success (defaults to '/?oauth=success') */
|
|
21
|
+
successRedirect?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create an OAuth callback route handler for Linear.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // In app/callback/route.ts:
|
|
29
|
+
* import { createOAuthCallbackHandler } from '@supaku/agentfactory-nextjs'
|
|
30
|
+
* export const { GET } = createOAuthCallbackHandler()
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function createOAuthCallbackHandler(config?: OAuthConfig): {
|
|
34
|
+
GET: RouteHandler;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../../../src/handlers/oauth/callback.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAID;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG;IAAE,GAAG,EAAE,YAAY,CAAA;CAAE,CA8FtF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Callback Handler for Linear
|
|
3
|
+
*
|
|
4
|
+
* Exchanges an authorization code for an access token and stores it
|
|
5
|
+
* in Redis for workspace-specific token resolution.
|
|
6
|
+
*
|
|
7
|
+
* @see https://developers.linear.app/docs/oauth/authentication
|
|
8
|
+
*/
|
|
9
|
+
import { NextResponse } from 'next/server';
|
|
10
|
+
import { storeToken, fetchOrganization, isRedisConfigured, createLogger, generateRequestId, } from '@supaku/agentfactory-server';
|
|
11
|
+
const baseLogger = createLogger('oauth-callback');
|
|
12
|
+
/**
|
|
13
|
+
* Create an OAuth callback route handler for Linear.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // In app/callback/route.ts:
|
|
18
|
+
* import { createOAuthCallbackHandler } from '@supaku/agentfactory-nextjs'
|
|
19
|
+
* export const { GET } = createOAuthCallbackHandler()
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function createOAuthCallbackHandler(config) {
|
|
23
|
+
return {
|
|
24
|
+
GET: async (request) => {
|
|
25
|
+
const requestId = generateRequestId();
|
|
26
|
+
const log = baseLogger.child({ requestId });
|
|
27
|
+
const clientId = config?.clientId ?? process.env.LINEAR_CLIENT_ID;
|
|
28
|
+
const clientSecret = config?.clientSecret ?? process.env.LINEAR_CLIENT_SECRET;
|
|
29
|
+
const appUrl = config?.appUrl ?? process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3002';
|
|
30
|
+
const successRedirect = config?.successRedirect ?? '/?oauth=success';
|
|
31
|
+
const code = request.nextUrl.searchParams.get('code');
|
|
32
|
+
const error = request.nextUrl.searchParams.get('error');
|
|
33
|
+
const errorDescription = request.nextUrl.searchParams.get('error_description');
|
|
34
|
+
if (error) {
|
|
35
|
+
log.error('OAuth error from Linear', { oauthError: error, errorDescription });
|
|
36
|
+
return NextResponse.json({ error, description: errorDescription, requestId }, { status: 400 });
|
|
37
|
+
}
|
|
38
|
+
if (!code) {
|
|
39
|
+
log.warn('Missing authorization code');
|
|
40
|
+
return NextResponse.json({ error: 'Missing authorization code', requestId }, { status: 400 });
|
|
41
|
+
}
|
|
42
|
+
if (!clientId || !clientSecret) {
|
|
43
|
+
log.error('OAuth credentials not configured');
|
|
44
|
+
return NextResponse.json({ error: 'OAuth not configured', requestId }, { status: 500 });
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
log.debug('Exchanging authorization code for token');
|
|
48
|
+
const tokenResponse = await fetch('https://api.linear.app/oauth/token', {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
51
|
+
body: new URLSearchParams({
|
|
52
|
+
grant_type: 'authorization_code',
|
|
53
|
+
client_id: clientId,
|
|
54
|
+
client_secret: clientSecret,
|
|
55
|
+
redirect_uri: `${appUrl}/callback`,
|
|
56
|
+
code,
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
if (!tokenResponse.ok) {
|
|
60
|
+
const errorText = await tokenResponse.text();
|
|
61
|
+
log.error('Token exchange failed', {
|
|
62
|
+
statusCode: tokenResponse.status,
|
|
63
|
+
errorDetails: errorText,
|
|
64
|
+
});
|
|
65
|
+
return NextResponse.json({ error: 'Token exchange failed', details: errorText, requestId }, { status: 400 });
|
|
66
|
+
}
|
|
67
|
+
const tokenData = (await tokenResponse.json());
|
|
68
|
+
if (isRedisConfigured()) {
|
|
69
|
+
const organization = await fetchOrganization(tokenData.access_token);
|
|
70
|
+
if (organization) {
|
|
71
|
+
await storeToken(organization.id, tokenData, organization.name);
|
|
72
|
+
log.info('OAuth successful, token stored', {
|
|
73
|
+
workspaceId: organization.id,
|
|
74
|
+
workspaceName: organization.name,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
log.warn('OAuth successful but could not fetch organization info - token not stored');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
log.info('OAuth successful, token received (Redis not configured for storage)');
|
|
83
|
+
}
|
|
84
|
+
return NextResponse.redirect(new URL(successRedirect, appUrl));
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
log.error('OAuth callback error', { error: err });
|
|
88
|
+
return NextResponse.json({ error: 'Internal server error', requestId }, { status: 500 });
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
* @supaku/agentfactory-nextjs
|
|
3
3
|
*
|
|
4
4
|
* Next.js API route handlers for AgentFactory.
|
|
5
|
-
* Provides webhook processing, worker/session management,
|
|
5
|
+
* Provides webhook processing, worker/session management, public stats,
|
|
6
|
+
* OAuth callback, middleware factory, webhook orchestrator, and Linear client resolver.
|
|
6
7
|
*/
|
|
7
8
|
export type { LinearClientResolver, RouteConfig, WebhookConfig, AutoTriggerConfig, CronConfig, RouteHandler, } from './types.js';
|
|
8
9
|
export { createAllRoutes } from './factory.js';
|
|
9
|
-
export type { AllRoutes } from './factory.js';
|
|
10
|
+
export type { AllRoutes, AllRoutesConfig } from './factory.js';
|
|
11
|
+
export { createOAuthCallbackHandler } from './handlers/oauth/callback.js';
|
|
12
|
+
export type { OAuthConfig } from './handlers/oauth/callback.js';
|
|
13
|
+
export { createAgentFactoryMiddleware } from './middleware/factory.js';
|
|
14
|
+
export type { MiddlewareConfig } from './middleware/types.js';
|
|
15
|
+
export { createWebhookOrchestrator, formatErrorForComment, } from './orchestrator/index.js';
|
|
16
|
+
export type { WebhookOrchestratorConfig, WebhookOrchestratorHooks, WebhookOrchestratorInstance, } from './orchestrator/index.js';
|
|
17
|
+
export { createDefaultLinearClientResolver } from './linear-client-resolver.js';
|
|
18
|
+
export type { DefaultLinearClientResolverConfig } from './linear-client-resolver.js';
|
|
10
19
|
export { verifyCronAuth } from './middleware/cron-auth.js';
|
|
11
20
|
export { verifyWorkerAuth, requireWorkerAuth, unauthorizedResponse, isWorkerAuthConfigured, } from './middleware/worker-auth.js';
|
|
12
21
|
export { createWebhookHandler } from './webhook/processor.js';
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAG9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAA;AACzE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAG/D,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,OAAO,EACL,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,yBAAyB,CAAA;AAChC,YAAY,EACV,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,yBAAyB,CAAA;AAGhC,OAAO,EAAE,iCAAiC,EAAE,MAAM,6BAA6B,CAAA;AAC/E,YAAY,EAAE,iCAAiC,EAAE,MAAM,6BAA6B,CAAA;AAGpF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,6BAA6B,CAAA;AAGpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAK/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAA;AACpG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAA;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AAGpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AACtE,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAA;AACxE,OAAO,EAAE,8BAA8B,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAA;AAC7G,OAAO,EAAE,+BAA+B,EAAE,MAAM,qCAAqC,CAAA;AACrF,OAAO,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAA;AAChH,OAAO,EAAE,qCAAqC,EAAE,MAAM,2CAA2C,CAAA;AAGjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAA;AAC9E,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAA;AAClF,OAAO,EAAE,gCAAgC,EAAE,MAAM,sCAAsC,CAAA;AACvF,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAA;AAC9E,OAAO,EAAE,6BAA6B,EAAE,MAAM,mCAAmC,CAAA;AAGjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,+BAA+B,EAAE,MAAM,oCAAoC,CAAA;AACpF,YAAY,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAA;AAC/E,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAA;AACtF,YAAY,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAA;AAGtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA"}
|
package/dist/src/index.js
CHANGED
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
* @supaku/agentfactory-nextjs
|
|
3
3
|
*
|
|
4
4
|
* Next.js API route handlers for AgentFactory.
|
|
5
|
-
* Provides webhook processing, worker/session management,
|
|
5
|
+
* Provides webhook processing, worker/session management, public stats,
|
|
6
|
+
* OAuth callback, middleware factory, webhook orchestrator, and Linear client resolver.
|
|
6
7
|
*/
|
|
7
8
|
// Factory
|
|
8
9
|
export { createAllRoutes } from './factory.js';
|
|
9
|
-
//
|
|
10
|
+
// OAuth handler
|
|
11
|
+
export { createOAuthCallbackHandler } from './handlers/oauth/callback.js';
|
|
12
|
+
// Middleware factory
|
|
13
|
+
export { createAgentFactoryMiddleware } from './middleware/factory.js';
|
|
14
|
+
// Webhook orchestrator
|
|
15
|
+
export { createWebhookOrchestrator, formatErrorForComment, } from './orchestrator/index.js';
|
|
16
|
+
// Default Linear client resolver
|
|
17
|
+
export { createDefaultLinearClientResolver } from './linear-client-resolver.js';
|
|
18
|
+
// Middleware (existing)
|
|
10
19
|
export { verifyCronAuth } from './middleware/cron-auth.js';
|
|
11
20
|
export { verifyWorkerAuth, requireWorkerAuth, unauthorizedResponse, isWorkerAuthConfigured, } from './middleware/worker-auth.js';
|
|
12
21
|
// Webhook
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Linear Client Resolver
|
|
3
|
+
*
|
|
4
|
+
* Provides a generic LinearClientResolver that uses:
|
|
5
|
+
* 1. Workspace-specific OAuth tokens from Redis (if configured)
|
|
6
|
+
* 2. Falls back to a global API key from environment
|
|
7
|
+
*
|
|
8
|
+
* This is the pattern every consumer needs — extracted from Supaku's
|
|
9
|
+
* linear.ts for reuse.
|
|
10
|
+
*/
|
|
11
|
+
import type { LinearClientResolver } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Configuration for the default Linear client resolver.
|
|
14
|
+
*/
|
|
15
|
+
export interface DefaultLinearClientResolverConfig {
|
|
16
|
+
/** Environment variable name for the API key (default: 'LINEAR_ACCESS_TOKEN') */
|
|
17
|
+
apiKeyEnvVar?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a default Linear client resolver.
|
|
21
|
+
*
|
|
22
|
+
* Resolves Linear clients with workspace-aware OAuth token support:
|
|
23
|
+
* - If an organizationId is provided and Redis is configured, attempts to
|
|
24
|
+
* fetch a workspace-specific OAuth token.
|
|
25
|
+
* - Falls back to a global API key from the environment.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { createDefaultLinearClientResolver } from '@supaku/agentfactory-nextjs'
|
|
30
|
+
*
|
|
31
|
+
* const resolver = createDefaultLinearClientResolver()
|
|
32
|
+
* // Use in route config:
|
|
33
|
+
* const routes = createAllRoutes({ linearClient: resolver, ... })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function createDefaultLinearClientResolver(config?: DefaultLinearClientResolverConfig): LinearClientResolver;
|
|
37
|
+
//# sourceMappingURL=linear-client-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-client-resolver.d.ts","sourceRoot":"","sources":["../../src/linear-client-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAItD;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,CAAC,EAAE,iCAAiC,GACzC,oBAAoB,CAwCtB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Linear Client Resolver
|
|
3
|
+
*
|
|
4
|
+
* Provides a generic LinearClientResolver that uses:
|
|
5
|
+
* 1. Workspace-specific OAuth tokens from Redis (if configured)
|
|
6
|
+
* 2. Falls back to a global API key from environment
|
|
7
|
+
*
|
|
8
|
+
* This is the pattern every consumer needs — extracted from Supaku's
|
|
9
|
+
* linear.ts for reuse.
|
|
10
|
+
*/
|
|
11
|
+
import { createLinearAgentClient, } from '@supaku/agentfactory-linear';
|
|
12
|
+
import { getAccessToken, isRedisConfigured, createLogger, } from '@supaku/agentfactory-server';
|
|
13
|
+
const log = createLogger('linear-client-resolver');
|
|
14
|
+
/**
|
|
15
|
+
* Create a default Linear client resolver.
|
|
16
|
+
*
|
|
17
|
+
* Resolves Linear clients with workspace-aware OAuth token support:
|
|
18
|
+
* - If an organizationId is provided and Redis is configured, attempts to
|
|
19
|
+
* fetch a workspace-specific OAuth token.
|
|
20
|
+
* - Falls back to a global API key from the environment.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { createDefaultLinearClientResolver } from '@supaku/agentfactory-nextjs'
|
|
25
|
+
*
|
|
26
|
+
* const resolver = createDefaultLinearClientResolver()
|
|
27
|
+
* // Use in route config:
|
|
28
|
+
* const routes = createAllRoutes({ linearClient: resolver, ... })
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function createDefaultLinearClientResolver(config) {
|
|
32
|
+
const apiKeyEnvVar = config?.apiKeyEnvVar ?? 'LINEAR_ACCESS_TOKEN';
|
|
33
|
+
let _globalClient = null;
|
|
34
|
+
function getGlobalClient() {
|
|
35
|
+
if (!_globalClient) {
|
|
36
|
+
const apiKey = process.env[apiKeyEnvVar];
|
|
37
|
+
if (!apiKey) {
|
|
38
|
+
throw new Error(`${apiKeyEnvVar} not set - Linear API operations will fail`);
|
|
39
|
+
}
|
|
40
|
+
_globalClient = createLinearAgentClient({ apiKey });
|
|
41
|
+
}
|
|
42
|
+
return _globalClient;
|
|
43
|
+
}
|
|
44
|
+
async function getClientForWorkspace(workspaceId) {
|
|
45
|
+
if (isRedisConfigured()) {
|
|
46
|
+
const accessToken = await getAccessToken(workspaceId);
|
|
47
|
+
if (accessToken) {
|
|
48
|
+
log.debug('Using OAuth token from Redis', { workspaceId });
|
|
49
|
+
return createLinearAgentClient({ apiKey: accessToken });
|
|
50
|
+
}
|
|
51
|
+
log.debug('No OAuth token in Redis, falling back to env var', { workspaceId });
|
|
52
|
+
}
|
|
53
|
+
return getGlobalClient();
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
getClient: async (organizationId) => organizationId
|
|
57
|
+
? await getClientForWorkspace(organizationId)
|
|
58
|
+
: getGlobalClient(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates a Next.js middleware function that handles authentication,
|
|
5
|
+
* rate limiting, and security for AgentFactory API routes.
|
|
6
|
+
*
|
|
7
|
+
* Uses the rate limiter and worker auth from @supaku/agentfactory-server
|
|
8
|
+
* for proper LRU eviction and crypto.timingSafeEqual.
|
|
9
|
+
*/
|
|
10
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
11
|
+
import type { MiddlewareConfig } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Create an AgentFactory middleware function with configurable routes
|
|
14
|
+
* and rate limiting.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // In middleware.ts:
|
|
19
|
+
* import { createAgentFactoryMiddleware } from '@supaku/agentfactory-nextjs'
|
|
20
|
+
*
|
|
21
|
+
* const { middleware, matcherConfig } = createAgentFactoryMiddleware()
|
|
22
|
+
* export { middleware }
|
|
23
|
+
* export const config = matcherConfig
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function createAgentFactoryMiddleware(userConfig?: MiddlewareConfig): {
|
|
27
|
+
middleware: (request: NextRequest) => NextResponse | undefined;
|
|
28
|
+
matcherConfig: {
|
|
29
|
+
matcher: string[];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/middleware/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAOvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAQlD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,CAAC,EAAE,gBAAgB;0BAO3C,WAAW,KAAG,YAAY,GAAG,SAAS;;;;EA8GpE"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates a Next.js middleware function that handles authentication,
|
|
5
|
+
* rate limiting, and security for AgentFactory API routes.
|
|
6
|
+
*
|
|
7
|
+
* Uses the rate limiter and worker auth from @supaku/agentfactory-server
|
|
8
|
+
* for proper LRU eviction and crypto.timingSafeEqual.
|
|
9
|
+
*/
|
|
10
|
+
import { NextResponse } from 'next/server';
|
|
11
|
+
import { checkRateLimit, getClientIP, buildRateLimitHeaders, verifyApiKey, } from '@supaku/agentfactory-server';
|
|
12
|
+
const DEFAULT_PUBLIC_ROUTES = ['/api/public/', '/dashboard', '/'];
|
|
13
|
+
const DEFAULT_PROTECTED_ROUTES = ['/api/sessions', '/api/workers'];
|
|
14
|
+
const DEFAULT_SESSION_PAGES = ['/sessions/'];
|
|
15
|
+
const DEFAULT_WEBHOOK_ROUTE = '/webhook';
|
|
16
|
+
const DEFAULT_PASSTHROUGH_ROUTES = ['/api/cleanup'];
|
|
17
|
+
/**
|
|
18
|
+
* Create an AgentFactory middleware function with configurable routes
|
|
19
|
+
* and rate limiting.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // In middleware.ts:
|
|
24
|
+
* import { createAgentFactoryMiddleware } from '@supaku/agentfactory-nextjs'
|
|
25
|
+
*
|
|
26
|
+
* const { middleware, matcherConfig } = createAgentFactoryMiddleware()
|
|
27
|
+
* export { middleware }
|
|
28
|
+
* export const config = matcherConfig
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function createAgentFactoryMiddleware(userConfig) {
|
|
32
|
+
const publicRoutes = userConfig?.routes?.public ?? DEFAULT_PUBLIC_ROUTES;
|
|
33
|
+
const protectedRoutes = userConfig?.routes?.protected ?? DEFAULT_PROTECTED_ROUTES;
|
|
34
|
+
const sessionPages = userConfig?.routes?.sessionPages ?? DEFAULT_SESSION_PAGES;
|
|
35
|
+
const webhookRoute = userConfig?.routes?.webhook ?? DEFAULT_WEBHOOK_ROUTE;
|
|
36
|
+
const passthroughRoutes = userConfig?.routes?.passthrough ?? DEFAULT_PASSTHROUGH_ROUTES;
|
|
37
|
+
function middleware(request) {
|
|
38
|
+
const { pathname } = request.nextUrl;
|
|
39
|
+
const clientIP = getClientIP(request.headers);
|
|
40
|
+
// === PUBLIC ROUTES ===
|
|
41
|
+
if (publicRoutes.some(route => pathname === route || pathname.startsWith(route))) {
|
|
42
|
+
const result = checkRateLimit('public', clientIP);
|
|
43
|
+
if (!result.allowed) {
|
|
44
|
+
return new NextResponse(JSON.stringify({ error: 'Too many requests' }), {
|
|
45
|
+
status: 429,
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
...buildRateLimitHeaders(result),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const response = NextResponse.next();
|
|
53
|
+
const rateLimitHeaders = buildRateLimitHeaders(result);
|
|
54
|
+
for (const [key, value] of Object.entries(rateLimitHeaders)) {
|
|
55
|
+
response.headers.set(key, value);
|
|
56
|
+
}
|
|
57
|
+
return response;
|
|
58
|
+
}
|
|
59
|
+
// === SESSION DETAIL PAGES ===
|
|
60
|
+
if (sessionPages.some(route => pathname.startsWith(route))) {
|
|
61
|
+
return NextResponse.next();
|
|
62
|
+
}
|
|
63
|
+
// === WEBHOOK ROUTE ===
|
|
64
|
+
if (pathname === webhookRoute) {
|
|
65
|
+
const result = checkRateLimit('webhook', clientIP);
|
|
66
|
+
if (!result.allowed) {
|
|
67
|
+
return new NextResponse(JSON.stringify({ error: 'Too many requests' }), {
|
|
68
|
+
status: 429,
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
...buildRateLimitHeaders(result),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return NextResponse.next();
|
|
76
|
+
}
|
|
77
|
+
// === PROTECTED INTERNAL APIS ===
|
|
78
|
+
if (protectedRoutes.some(route => pathname.startsWith(route))) {
|
|
79
|
+
const workerApiKey = process.env.WORKER_API_KEY;
|
|
80
|
+
// In development without key, allow access
|
|
81
|
+
if (!workerApiKey && process.env.NODE_ENV !== 'production') {
|
|
82
|
+
return NextResponse.next();
|
|
83
|
+
}
|
|
84
|
+
if (!workerApiKey) {
|
|
85
|
+
console.error('WORKER_API_KEY not configured - blocking protected API access');
|
|
86
|
+
return new NextResponse(JSON.stringify({ error: 'Unauthorized', message: 'Invalid or missing API key' }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
|
87
|
+
}
|
|
88
|
+
const authHeader = request.headers.get('authorization');
|
|
89
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
90
|
+
return new NextResponse(JSON.stringify({ error: 'Unauthorized', message: 'Invalid or missing API key' }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
|
91
|
+
}
|
|
92
|
+
const token = authHeader.slice(7);
|
|
93
|
+
if (!verifyApiKey(token, workerApiKey)) {
|
|
94
|
+
return new NextResponse(JSON.stringify({ error: 'Unauthorized', message: 'Invalid or missing API key' }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
|
95
|
+
}
|
|
96
|
+
return NextResponse.next();
|
|
97
|
+
}
|
|
98
|
+
// === PASSTHROUGH ROUTES ===
|
|
99
|
+
if (passthroughRoutes.some(route => pathname === route || pathname.startsWith(route))) {
|
|
100
|
+
return NextResponse.next();
|
|
101
|
+
}
|
|
102
|
+
// === ALL OTHER ROUTES ===
|
|
103
|
+
return NextResponse.next();
|
|
104
|
+
}
|
|
105
|
+
const matcherConfig = {
|
|
106
|
+
matcher: [
|
|
107
|
+
'/api/:path*',
|
|
108
|
+
webhookRoute,
|
|
109
|
+
'/dashboard',
|
|
110
|
+
'/sessions/:path*',
|
|
111
|
+
'/',
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
return { middleware, matcherConfig };
|
|
115
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the middleware factory.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for the AgentFactory middleware.
|
|
6
|
+
*/
|
|
7
|
+
export interface MiddlewareConfig {
|
|
8
|
+
/** Route path configuration */
|
|
9
|
+
routes?: {
|
|
10
|
+
/** Public routes - no auth, with rate limiting (default: ['/api/public/', '/dashboard', '/']) */
|
|
11
|
+
public?: string[];
|
|
12
|
+
/** Protected routes - require WORKER_API_KEY (default: ['/api/sessions', '/api/workers']) */
|
|
13
|
+
protected?: string[];
|
|
14
|
+
/** Session detail pages - allow public access (default: ['/sessions/']) */
|
|
15
|
+
sessionPages?: string[];
|
|
16
|
+
/** Webhook route (default: '/webhook') */
|
|
17
|
+
webhook?: string;
|
|
18
|
+
/** Routes with custom auth in handler (default: ['/api/cleanup']) */
|
|
19
|
+
passthrough?: string[];
|
|
20
|
+
};
|
|
21
|
+
/** Rate limit configurations */
|
|
22
|
+
rateLimits?: {
|
|
23
|
+
/** Public endpoint rate limit (default: 60/min) */
|
|
24
|
+
public?: {
|
|
25
|
+
max: number;
|
|
26
|
+
windowMs: number;
|
|
27
|
+
};
|
|
28
|
+
/** Webhook endpoint rate limit (default: 10/sec) */
|
|
29
|
+
webhook?: {
|
|
30
|
+
max: number;
|
|
31
|
+
windowMs: number;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/middleware/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,MAAM,CAAC,EAAE;QACP,iGAAiG;QACjG,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;QACjB,6FAA6F;QAC7F,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;QACpB,2EAA2E;QAC3E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;QACvB,0CAA0C;QAC1C,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,qEAAqE;QACrE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;KACvB,CAAA;IACD,gCAAgC;IAChC,UAAU,CAAC,EAAE;QACX,mDAAmD;QACnD,MAAM,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;QAC1C,oDAAoD;QACpD,OAAO,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;KAC5C,CAAA;CACF"}
|