webpeel 0.12.0 → 0.12.2
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 +82 -9
- package/dist/cli.js +97 -6
- package/dist/cli.js.map +1 -1
- package/dist/core/actions.d.ts +28 -0
- package/dist/core/actions.d.ts.map +1 -1
- package/dist/core/actions.js +60 -0
- package/dist/core/actions.js.map +1 -1
- package/dist/core/bm25-filter.d.ts +10 -0
- package/dist/core/bm25-filter.d.ts.map +1 -1
- package/dist/core/bm25-filter.js +40 -0
- package/dist/core/bm25-filter.js.map +1 -1
- package/dist/core/content-pruner.d.ts +12 -5
- package/dist/core/content-pruner.d.ts.map +1 -1
- package/dist/core/content-pruner.js +247 -190
- package/dist/core/content-pruner.js.map +1 -1
- package/dist/core/research.d.ts +67 -0
- package/dist/core/research.d.ts.map +1 -0
- package/dist/core/research.js +254 -0
- package/dist/core/research.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +107 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/server/app.d.ts +14 -0
- package/dist/server/app.d.ts.map +1 -0
- package/dist/server/app.js +189 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/auth-store.d.ts +28 -0
- package/dist/server/auth-store.d.ts.map +1 -0
- package/dist/server/auth-store.js +89 -0
- package/dist/server/auth-store.js.map +1 -0
- package/dist/server/job-queue.d.ts +93 -0
- package/dist/server/job-queue.d.ts.map +1 -0
- package/dist/server/job-queue.js +144 -0
- package/dist/server/job-queue.js.map +1 -0
- package/dist/server/middleware/auth.d.ts +28 -0
- package/dist/server/middleware/auth.d.ts.map +1 -0
- package/dist/server/middleware/auth.js +183 -0
- package/dist/server/middleware/auth.js.map +1 -0
- package/dist/server/middleware/rate-limit.d.ts +23 -0
- package/dist/server/middleware/rate-limit.d.ts.map +1 -0
- package/dist/server/middleware/rate-limit.js +126 -0
- package/dist/server/middleware/rate-limit.js.map +1 -0
- package/dist/server/middleware/url-validator.d.ts +16 -0
- package/dist/server/middleware/url-validator.d.ts.map +1 -0
- package/dist/server/middleware/url-validator.js +187 -0
- package/dist/server/middleware/url-validator.js.map +1 -0
- package/dist/server/pg-auth-store.d.ts +129 -0
- package/dist/server/pg-auth-store.d.ts.map +1 -0
- package/dist/server/pg-auth-store.js +457 -0
- package/dist/server/pg-auth-store.js.map +1 -0
- package/dist/server/pg-job-queue.d.ts +60 -0
- package/dist/server/pg-job-queue.d.ts.map +1 -0
- package/dist/server/pg-job-queue.js +365 -0
- package/dist/server/pg-job-queue.js.map +1 -0
- package/dist/server/premium/domain-intel.d.ts +17 -0
- package/dist/server/premium/domain-intel.d.ts.map +1 -0
- package/dist/server/premium/domain-intel.js +134 -0
- package/dist/server/premium/domain-intel.js.map +1 -0
- package/dist/server/premium/index.d.ts +18 -0
- package/dist/server/premium/index.d.ts.map +1 -0
- package/dist/server/premium/index.js +36 -0
- package/dist/server/premium/index.js.map +1 -0
- package/dist/server/premium/swr-cache.d.ts +15 -0
- package/dist/server/premium/swr-cache.d.ts.map +1 -0
- package/dist/server/premium/swr-cache.js +35 -0
- package/dist/server/premium/swr-cache.js.map +1 -0
- package/dist/server/routes/activity.d.ts +7 -0
- package/dist/server/routes/activity.d.ts.map +1 -0
- package/dist/server/routes/activity.js +66 -0
- package/dist/server/routes/activity.js.map +1 -0
- package/dist/server/routes/agent.d.ts +12 -0
- package/dist/server/routes/agent.d.ts.map +1 -0
- package/dist/server/routes/agent.js +356 -0
- package/dist/server/routes/agent.js.map +1 -0
- package/dist/server/routes/answer.d.ts +6 -0
- package/dist/server/routes/answer.d.ts.map +1 -0
- package/dist/server/routes/answer.js +124 -0
- package/dist/server/routes/answer.js.map +1 -0
- package/dist/server/routes/batch.d.ts +7 -0
- package/dist/server/routes/batch.d.ts.map +1 -0
- package/dist/server/routes/batch.js +287 -0
- package/dist/server/routes/batch.js.map +1 -0
- package/dist/server/routes/cli-usage.d.ts +7 -0
- package/dist/server/routes/cli-usage.d.ts.map +1 -0
- package/dist/server/routes/cli-usage.js +121 -0
- package/dist/server/routes/cli-usage.js.map +1 -0
- package/dist/server/routes/compat.d.ts +24 -0
- package/dist/server/routes/compat.d.ts.map +1 -0
- package/dist/server/routes/compat.js +651 -0
- package/dist/server/routes/compat.js.map +1 -0
- package/dist/server/routes/extract.d.ts +9 -0
- package/dist/server/routes/extract.d.ts.map +1 -0
- package/dist/server/routes/extract.js +121 -0
- package/dist/server/routes/extract.js.map +1 -0
- package/dist/server/routes/fetch.d.ts +7 -0
- package/dist/server/routes/fetch.d.ts.map +1 -0
- package/dist/server/routes/fetch.js +537 -0
- package/dist/server/routes/fetch.js.map +1 -0
- package/dist/server/routes/health.d.ts +8 -0
- package/dist/server/routes/health.d.ts.map +1 -0
- package/dist/server/routes/health.js +36 -0
- package/dist/server/routes/health.js.map +1 -0
- package/dist/server/routes/jobs.d.ts +8 -0
- package/dist/server/routes/jobs.d.ts.map +1 -0
- package/dist/server/routes/jobs.js +374 -0
- package/dist/server/routes/jobs.js.map +1 -0
- package/dist/server/routes/mcp.d.ts +16 -0
- package/dist/server/routes/mcp.d.ts.map +1 -0
- package/dist/server/routes/mcp.js +475 -0
- package/dist/server/routes/mcp.js.map +1 -0
- package/dist/server/routes/oauth.d.ts +10 -0
- package/dist/server/routes/oauth.d.ts.map +1 -0
- package/dist/server/routes/oauth.js +296 -0
- package/dist/server/routes/oauth.js.map +1 -0
- package/dist/server/routes/screenshot.d.ts +10 -0
- package/dist/server/routes/screenshot.d.ts.map +1 -0
- package/dist/server/routes/screenshot.js +217 -0
- package/dist/server/routes/screenshot.js.map +1 -0
- package/dist/server/routes/search.d.ts +7 -0
- package/dist/server/routes/search.d.ts.map +1 -0
- package/dist/server/routes/search.js +287 -0
- package/dist/server/routes/search.js.map +1 -0
- package/dist/server/routes/stats.d.ts +7 -0
- package/dist/server/routes/stats.d.ts.map +1 -0
- package/dist/server/routes/stats.js +65 -0
- package/dist/server/routes/stats.js.map +1 -0
- package/dist/server/routes/stripe.d.ts +9 -0
- package/dist/server/routes/stripe.d.ts.map +1 -0
- package/dist/server/routes/stripe.js +233 -0
- package/dist/server/routes/stripe.js.map +1 -0
- package/dist/server/routes/users.d.ts +9 -0
- package/dist/server/routes/users.d.ts.map +1 -0
- package/dist/server/routes/users.js +954 -0
- package/dist/server/routes/users.js.map +1 -0
- package/dist/server/routes/webhooks.d.ts +15 -0
- package/dist/server/routes/webhooks.d.ts.map +1 -0
- package/dist/server/routes/webhooks.js +73 -0
- package/dist/server/routes/webhooks.js.map +1 -0
- package/dist/server/sentry.d.ts +14 -0
- package/dist/server/sentry.d.ts.map +1 -0
- package/dist/server/sentry.js +39 -0
- package/dist/server/sentry.js.map +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth authentication routes
|
|
3
|
+
* Handles OAuth login from Auth.js (GitHub, Google)
|
|
4
|
+
*/
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
import crypto from 'crypto';
|
|
7
|
+
import jwt from 'jsonwebtoken';
|
|
8
|
+
import pg from 'pg';
|
|
9
|
+
import { PostgresAuthStore } from '../pg-auth-store.js';
|
|
10
|
+
const { Pool } = pg;
|
|
11
|
+
/**
|
|
12
|
+
* Validate email format
|
|
13
|
+
*/
|
|
14
|
+
function isValidEmail(email) {
|
|
15
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
16
|
+
return emailRegex.test(email);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Simple in-memory rate limiter for OAuth endpoint
|
|
20
|
+
*/
|
|
21
|
+
class OAuthRateLimiter {
|
|
22
|
+
attempts = new Map();
|
|
23
|
+
maxAttempts = 10;
|
|
24
|
+
windowMs = 60000; // 1 minute
|
|
25
|
+
check(identifier) {
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
const attempts = this.attempts.get(identifier) || [];
|
|
28
|
+
// Remove old attempts outside the window
|
|
29
|
+
const recentAttempts = attempts.filter(time => now - time < this.windowMs);
|
|
30
|
+
if (recentAttempts.length >= this.maxAttempts) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
recentAttempts.push(now);
|
|
34
|
+
this.attempts.set(identifier, recentAttempts);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
cleanup() {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
for (const [key, attempts] of this.attempts.entries()) {
|
|
40
|
+
const recentAttempts = attempts.filter(time => now - time < this.windowMs);
|
|
41
|
+
if (recentAttempts.length === 0) {
|
|
42
|
+
this.attempts.delete(key);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.attempts.set(key, recentAttempts);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const rateLimiter = new OAuthRateLimiter();
|
|
51
|
+
// Clean up rate limiter every 2 minutes
|
|
52
|
+
setInterval(() => {
|
|
53
|
+
rateLimiter.cleanup();
|
|
54
|
+
}, 2 * 60 * 1000);
|
|
55
|
+
/**
|
|
56
|
+
* Create OAuth routes
|
|
57
|
+
*/
|
|
58
|
+
export function createOAuthRouter() {
|
|
59
|
+
const router = Router();
|
|
60
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
61
|
+
if (!dbUrl) {
|
|
62
|
+
throw new Error('DATABASE_URL environment variable is required');
|
|
63
|
+
}
|
|
64
|
+
const pool = new Pool({
|
|
65
|
+
connectionString: dbUrl,
|
|
66
|
+
// TLS: enabled when DATABASE_URL contains sslmode=require.
|
|
67
|
+
// Secure by default (rejectUnauthorized: true); set PG_REJECT_UNAUTHORIZED=false
|
|
68
|
+
// only for managed DBs (Render/Neon/Supabase) that use self-signed certs.
|
|
69
|
+
ssl: process.env.DATABASE_URL?.includes('sslmode=require')
|
|
70
|
+
? { rejectUnauthorized: process.env.PG_REJECT_UNAUTHORIZED !== 'false' }
|
|
71
|
+
: undefined,
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Helper: generate a refresh token and store its jti in the database
|
|
75
|
+
*/
|
|
76
|
+
async function createRefreshToken(userId, jwtSecret) {
|
|
77
|
+
const jti = crypto.randomUUID();
|
|
78
|
+
const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 days
|
|
79
|
+
await pool.query(`INSERT INTO refresh_tokens (id, user_id, expires_at) VALUES ($1, $2, $3)`, [jti, userId, expiresAt]);
|
|
80
|
+
return jwt.sign({ userId, jti }, jwtSecret, { expiresIn: '30d' });
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* POST /v1/auth/oauth
|
|
84
|
+
* OAuth callback handler - called by Auth.js after successful OAuth flow
|
|
85
|
+
* Auto-creates users if they don't exist
|
|
86
|
+
*/
|
|
87
|
+
router.post('/v1/auth/oauth', async (req, res) => {
|
|
88
|
+
try {
|
|
89
|
+
const { provider, accessToken, name, avatar } = req.body;
|
|
90
|
+
// Rate limiting — scoped per-IP per-provider (not global) to prevent DoS.
|
|
91
|
+
// IP extracted from cf-connecting-ip (Cloudflare) > x-forwarded-for (reverse proxy) > req.ip.
|
|
92
|
+
// Limit: 10 attempts per minute per IP+provider combination.
|
|
93
|
+
const clientIp = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip || 'unknown';
|
|
94
|
+
if (!rateLimiter.check(`${clientIp}:${provider || 'unknown'}`)) {
|
|
95
|
+
res.status(429).json({
|
|
96
|
+
error: 'rate_limit_exceeded',
|
|
97
|
+
message: 'Too many OAuth attempts. Please try again in a minute.',
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Input validation
|
|
102
|
+
if (!provider || !accessToken) {
|
|
103
|
+
res.status(400).json({
|
|
104
|
+
error: 'missing_fields',
|
|
105
|
+
message: 'provider and accessToken are required',
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Validate provider
|
|
110
|
+
if (provider !== 'github' && provider !== 'google') {
|
|
111
|
+
res.status(400).json({
|
|
112
|
+
error: 'invalid_provider',
|
|
113
|
+
message: 'provider must be "github" or "google"',
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// SECURITY: Verify the OAuth token server-side and extract trusted identity
|
|
118
|
+
let providerId;
|
|
119
|
+
let email;
|
|
120
|
+
if (provider === 'github') {
|
|
121
|
+
// Verify GitHub access token
|
|
122
|
+
const ghRes = await fetch('https://api.github.com/user', {
|
|
123
|
+
headers: {
|
|
124
|
+
Authorization: `Bearer ${accessToken}`,
|
|
125
|
+
Accept: 'application/vnd.github+json',
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
if (!ghRes.ok) {
|
|
129
|
+
res.status(401).json({
|
|
130
|
+
error: 'invalid_token',
|
|
131
|
+
message: 'Invalid GitHub access token',
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const ghUser = await ghRes.json();
|
|
136
|
+
providerId = String(ghUser.id);
|
|
137
|
+
// GitHub may not return email on /user; fetch from /user/emails
|
|
138
|
+
if (ghUser.email) {
|
|
139
|
+
email = ghUser.email;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const emailRes = await fetch('https://api.github.com/user/emails', {
|
|
143
|
+
headers: {
|
|
144
|
+
Authorization: `Bearer ${accessToken}`,
|
|
145
|
+
Accept: 'application/vnd.github+json',
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
if (emailRes.ok) {
|
|
149
|
+
const emails = await emailRes.json();
|
|
150
|
+
const primary = emails.find(e => e.primary && e.verified);
|
|
151
|
+
email = primary?.email || emails[0]?.email || '';
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
email = '';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
// Verify Google ID token
|
|
160
|
+
const gRes = await fetch(`https://oauth2.googleapis.com/tokeninfo?id_token=${encodeURIComponent(accessToken)}`);
|
|
161
|
+
if (!gRes.ok) {
|
|
162
|
+
res.status(401).json({
|
|
163
|
+
error: 'invalid_token',
|
|
164
|
+
message: 'Invalid Google token',
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const gUser = await gRes.json();
|
|
169
|
+
providerId = gUser.sub;
|
|
170
|
+
email = gUser.email || '';
|
|
171
|
+
}
|
|
172
|
+
// Validate email from verified token
|
|
173
|
+
if (!email || !isValidEmail(email)) {
|
|
174
|
+
res.status(400).json({
|
|
175
|
+
error: 'invalid_email',
|
|
176
|
+
message: 'Could not retrieve a valid email from OAuth provider',
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const client = await pool.connect();
|
|
181
|
+
try {
|
|
182
|
+
await client.query('BEGIN');
|
|
183
|
+
// Check if OAuth account already exists
|
|
184
|
+
const oauthResult = await client.query(`SELECT user_id FROM oauth_accounts
|
|
185
|
+
WHERE provider = $1 AND provider_id = $2`, [provider, providerId]);
|
|
186
|
+
let userId;
|
|
187
|
+
let isNew = false;
|
|
188
|
+
let apiKey;
|
|
189
|
+
if (oauthResult.rows.length > 0) {
|
|
190
|
+
// Existing OAuth account - get user
|
|
191
|
+
userId = oauthResult.rows[0].user_id;
|
|
192
|
+
// Update OAuth account info
|
|
193
|
+
await client.query(`UPDATE oauth_accounts
|
|
194
|
+
SET email = $1, name = $2, avatar_url = $3, updated_at = now()
|
|
195
|
+
WHERE provider = $4 AND provider_id = $5`, [email, name || null, avatar || null, provider, providerId]);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// New OAuth account - check if user with this email exists
|
|
199
|
+
const userResult = await client.query('SELECT id FROM users WHERE email = $1', [email]);
|
|
200
|
+
if (userResult.rows.length > 0) {
|
|
201
|
+
// User exists - link OAuth account to existing user
|
|
202
|
+
userId = userResult.rows[0].id;
|
|
203
|
+
// Update user info
|
|
204
|
+
await client.query(`UPDATE users
|
|
205
|
+
SET name = COALESCE($1, name),
|
|
206
|
+
avatar_url = COALESCE($2, avatar_url),
|
|
207
|
+
updated_at = now()
|
|
208
|
+
WHERE id = $3`, [name || null, avatar || null, userId]);
|
|
209
|
+
// Create OAuth account link
|
|
210
|
+
await client.query(`INSERT INTO oauth_accounts
|
|
211
|
+
(user_id, provider, provider_id, email, name, avatar_url)
|
|
212
|
+
VALUES ($1, $2, $3, $4, $5, $6)`, [userId, provider, providerId, email, name || null, avatar || null]);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
// New user - create account
|
|
216
|
+
const newUserResult = await client.query(`INSERT INTO users
|
|
217
|
+
(email, password_hash, tier, weekly_limit, burst_limit, rate_limit, name, avatar_url)
|
|
218
|
+
VALUES ($1, NULL, 'free', 125, 25, 10, $2, $3)
|
|
219
|
+
RETURNING id`, [email, name || null, avatar || null]);
|
|
220
|
+
userId = newUserResult.rows[0].id;
|
|
221
|
+
isNew = true;
|
|
222
|
+
// Create OAuth account link
|
|
223
|
+
await client.query(`INSERT INTO oauth_accounts
|
|
224
|
+
(user_id, provider, provider_id, email, name, avatar_url)
|
|
225
|
+
VALUES ($1, $2, $3, $4, $5, $6)`, [userId, provider, providerId, email, name || null, avatar || null]);
|
|
226
|
+
// Generate first API key for new user
|
|
227
|
+
apiKey = PostgresAuthStore.generateApiKey();
|
|
228
|
+
const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
|
|
229
|
+
const keyPrefix = PostgresAuthStore.getKeyPrefix(apiKey);
|
|
230
|
+
await client.query(`INSERT INTO api_keys (user_id, key_hash, key_prefix, name)
|
|
231
|
+
VALUES ($1, $2, $3, 'Default')`, [userId, keyHash, keyPrefix]);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Get user info for response
|
|
235
|
+
const userInfoResult = await client.query('SELECT id, email, tier, name, avatar_url FROM users WHERE id = $1', [userId]);
|
|
236
|
+
const user = userInfoResult.rows[0];
|
|
237
|
+
// Generate JWT
|
|
238
|
+
const jwtSecret = process.env.JWT_SECRET;
|
|
239
|
+
if (!jwtSecret) {
|
|
240
|
+
throw new Error('JWT_SECRET not configured');
|
|
241
|
+
}
|
|
242
|
+
const token = jwt.sign({
|
|
243
|
+
userId: user.id,
|
|
244
|
+
email: user.email,
|
|
245
|
+
tier: user.tier,
|
|
246
|
+
}, jwtSecret, { expiresIn: '1h' });
|
|
247
|
+
await client.query('COMMIT');
|
|
248
|
+
// Generate refresh token (after commit, uses pool not client)
|
|
249
|
+
const refreshToken = await createRefreshToken(user.id, jwtSecret);
|
|
250
|
+
// Response
|
|
251
|
+
const response = {
|
|
252
|
+
user: {
|
|
253
|
+
id: user.id,
|
|
254
|
+
email: user.email,
|
|
255
|
+
tier: user.tier,
|
|
256
|
+
name: user.name,
|
|
257
|
+
avatar: user.avatar_url,
|
|
258
|
+
},
|
|
259
|
+
token,
|
|
260
|
+
refreshToken,
|
|
261
|
+
expiresIn: 3600,
|
|
262
|
+
isNew,
|
|
263
|
+
};
|
|
264
|
+
// Include API key only for new users
|
|
265
|
+
if (isNew && apiKey) {
|
|
266
|
+
response.apiKey = apiKey;
|
|
267
|
+
}
|
|
268
|
+
res.json(response);
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
await client.query('ROLLBACK');
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
client.release();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
console.error('OAuth error:', error);
|
|
280
|
+
// Handle specific errors
|
|
281
|
+
if (error.code === '23505') { // Unique violation
|
|
282
|
+
res.status(409).json({
|
|
283
|
+
error: 'oauth_conflict',
|
|
284
|
+
message: 'OAuth account already exists',
|
|
285
|
+
});
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
res.status(500).json({
|
|
289
|
+
error: 'oauth_failed',
|
|
290
|
+
message: 'Failed to process OAuth login',
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
return router;
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../../src/server/routes/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshot endpoint — POST /v1/screenshot
|
|
3
|
+
*
|
|
4
|
+
* Takes a screenshot of a URL and returns base64-encoded image data.
|
|
5
|
+
* Uses the same rate limiting / credit system as the fetch endpoint (1 credit).
|
|
6
|
+
*/
|
|
7
|
+
import { Router } from 'express';
|
|
8
|
+
import type { AuthStore } from '../auth-store.js';
|
|
9
|
+
export declare function createScreenshotRouter(authStore: AuthStore): Router;
|
|
10
|
+
//# sourceMappingURL=screenshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../src/server/routes/screenshot.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAEpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIlD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAwPnE"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshot endpoint — POST /v1/screenshot
|
|
3
|
+
*
|
|
4
|
+
* Takes a screenshot of a URL and returns base64-encoded image data.
|
|
5
|
+
* Uses the same rate limiting / credit system as the fetch endpoint (1 credit).
|
|
6
|
+
*/
|
|
7
|
+
import { Router } from 'express';
|
|
8
|
+
import { takeScreenshot } from '../../core/screenshot.js';
|
|
9
|
+
import { validateUrlForSSRF, SSRFError } from '../middleware/url-validator.js';
|
|
10
|
+
import { normalizeActions } from '../../core/actions.js';
|
|
11
|
+
export function createScreenshotRouter(authStore) {
|
|
12
|
+
const router = Router();
|
|
13
|
+
router.post('/v1/screenshot', async (req, res) => {
|
|
14
|
+
try {
|
|
15
|
+
const { url, fullPage = false, width, height, format = 'png', quality, waitFor, timeout, actions, headers, cookies, stealth, } = req.body;
|
|
16
|
+
// --- Validate URL --------------------------------------------------
|
|
17
|
+
if (!url || typeof url !== 'string') {
|
|
18
|
+
res.status(400).json({
|
|
19
|
+
error: 'invalid_request',
|
|
20
|
+
message: 'Missing or invalid "url" parameter',
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (url.length > 2048) {
|
|
25
|
+
res.status(400).json({
|
|
26
|
+
error: 'invalid_url',
|
|
27
|
+
message: 'URL too long (max 2048 characters)',
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const parsed = new URL(url);
|
|
33
|
+
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
34
|
+
res.status(400).json({
|
|
35
|
+
error: 'invalid_url',
|
|
36
|
+
message: 'Only HTTP and HTTPS protocols are allowed',
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
res.status(400).json({
|
|
43
|
+
error: 'invalid_url',
|
|
44
|
+
message: 'Invalid URL format',
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
validateUrlForSSRF(url);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof SSRFError) {
|
|
53
|
+
res.status(400).json({
|
|
54
|
+
error: 'ssrf_blocked',
|
|
55
|
+
message: 'Cannot fetch localhost, private networks, or non-HTTP URLs',
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
// --- Validate options -----------------------------------------------
|
|
62
|
+
if (format !== undefined && !['png', 'jpeg', 'jpg'].includes(format)) {
|
|
63
|
+
res.status(400).json({
|
|
64
|
+
error: 'invalid_request',
|
|
65
|
+
message: 'Invalid format: must be "png", "jpeg", or "jpg"',
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (width !== undefined && (typeof width !== 'number' || width < 100 || width > 5000)) {
|
|
70
|
+
res.status(400).json({
|
|
71
|
+
error: 'invalid_request',
|
|
72
|
+
message: 'Invalid width: must be between 100 and 5000',
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (height !== undefined && (typeof height !== 'number' || height < 100 || height > 5000)) {
|
|
77
|
+
res.status(400).json({
|
|
78
|
+
error: 'invalid_request',
|
|
79
|
+
message: 'Invalid height: must be between 100 and 5000',
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (quality !== undefined && (typeof quality !== 'number' || quality < 1 || quality > 100)) {
|
|
84
|
+
res.status(400).json({
|
|
85
|
+
error: 'invalid_request',
|
|
86
|
+
message: 'Invalid quality: must be between 1 and 100',
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (waitFor !== undefined && (typeof waitFor !== 'number' || waitFor < 0 || waitFor > 60000)) {
|
|
91
|
+
res.status(400).json({
|
|
92
|
+
error: 'invalid_request',
|
|
93
|
+
message: 'Invalid waitFor: must be between 0 and 60000ms',
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Normalize user-provided actions (accepts Firecrawl-style too)
|
|
98
|
+
let normalizedActions;
|
|
99
|
+
if (actions !== undefined) {
|
|
100
|
+
try {
|
|
101
|
+
normalizedActions = normalizeActions(actions);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
res.status(400).json({
|
|
105
|
+
error: 'invalid_request',
|
|
106
|
+
message: `Invalid actions: ${e.message}`,
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// --- Take the screenshot -------------------------------------------
|
|
112
|
+
const startTime = Date.now();
|
|
113
|
+
const result = await takeScreenshot(url, {
|
|
114
|
+
fullPage: fullPage === true,
|
|
115
|
+
width,
|
|
116
|
+
height,
|
|
117
|
+
format,
|
|
118
|
+
quality,
|
|
119
|
+
waitFor,
|
|
120
|
+
timeout: timeout || 30000,
|
|
121
|
+
actions: normalizedActions,
|
|
122
|
+
headers,
|
|
123
|
+
cookies,
|
|
124
|
+
stealth: stealth === true,
|
|
125
|
+
});
|
|
126
|
+
const elapsed = Date.now() - startTime;
|
|
127
|
+
// --- Track usage ---------------------------------------------------
|
|
128
|
+
const isSoftLimited = req.auth?.softLimited === true;
|
|
129
|
+
const hasExtraUsage = req.auth?.extraUsageAvailable === true;
|
|
130
|
+
const pgStore = authStore;
|
|
131
|
+
if (req.auth?.keyInfo?.key && typeof pgStore.trackBurstUsage === 'function') {
|
|
132
|
+
await pgStore.trackBurstUsage(req.auth.keyInfo.key);
|
|
133
|
+
if (isSoftLimited && hasExtraUsage) {
|
|
134
|
+
const extraResult = await pgStore.trackExtraUsage(req.auth.keyInfo.key, 'stealth', url, elapsed, 200);
|
|
135
|
+
if (extraResult.success) {
|
|
136
|
+
res.setHeader('X-Extra-Usage-Charged', `$${extraResult.cost.toFixed(4)}`);
|
|
137
|
+
res.setHeader('X-Extra-Usage-New-Balance', extraResult.newBalance.toFixed(2));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (!isSoftLimited) {
|
|
141
|
+
await pgStore.trackUsage(req.auth.keyInfo.key, 'stealth');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Log to usage_logs (fire and forget)
|
|
145
|
+
if (req.auth?.keyInfo?.accountId && typeof pgStore.pool !== 'undefined') {
|
|
146
|
+
pgStore.pool.query(`INSERT INTO usage_logs
|
|
147
|
+
(user_id, endpoint, url, method, processing_time_ms, status_code, ip_address, user_agent)
|
|
148
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
|
|
149
|
+
req.auth.keyInfo.accountId,
|
|
150
|
+
'screenshot',
|
|
151
|
+
url,
|
|
152
|
+
'stealth',
|
|
153
|
+
elapsed,
|
|
154
|
+
200,
|
|
155
|
+
req.ip || req.socket.remoteAddress,
|
|
156
|
+
req.get('user-agent'),
|
|
157
|
+
]).catch((err) => {
|
|
158
|
+
console.error('Failed to log screenshot request:', err);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// --- Respond -------------------------------------------------------
|
|
162
|
+
res.setHeader('X-Credits-Used', '1');
|
|
163
|
+
res.setHeader('X-Processing-Time', elapsed.toString());
|
|
164
|
+
res.setHeader('X-Fetch-Type', 'screenshot');
|
|
165
|
+
res.json({
|
|
166
|
+
success: true,
|
|
167
|
+
data: {
|
|
168
|
+
url: result.url,
|
|
169
|
+
screenshot: `data:${result.contentType};base64,${result.screenshot}`,
|
|
170
|
+
metadata: {
|
|
171
|
+
sourceURL: result.url,
|
|
172
|
+
format: result.format,
|
|
173
|
+
width: width || 1280,
|
|
174
|
+
height: height || 720,
|
|
175
|
+
fullPage: fullPage === true,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error('Screenshot error:', error);
|
|
182
|
+
// Log error (fire and forget)
|
|
183
|
+
const pgStore = authStore;
|
|
184
|
+
if (req.auth?.keyInfo?.accountId && typeof pgStore.pool !== 'undefined') {
|
|
185
|
+
pgStore.pool.query(`INSERT INTO usage_logs
|
|
186
|
+
(user_id, endpoint, url, method, status_code, error, ip_address, user_agent)
|
|
187
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
|
|
188
|
+
req.auth.keyInfo.accountId,
|
|
189
|
+
'screenshot',
|
|
190
|
+
req.body?.url,
|
|
191
|
+
'stealth',
|
|
192
|
+
500,
|
|
193
|
+
error.message || 'Unknown error',
|
|
194
|
+
req.ip || req.socket.remoteAddress,
|
|
195
|
+
req.get('user-agent'),
|
|
196
|
+
]).catch((logErr) => {
|
|
197
|
+
console.error('Failed to log screenshot error:', logErr);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (error.code) {
|
|
201
|
+
const safeMessage = error.message.replace(/[<>"']/g, '');
|
|
202
|
+
res.status(500).json({
|
|
203
|
+
error: 'screenshot_error',
|
|
204
|
+
message: safeMessage,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
res.status(500).json({
|
|
209
|
+
error: 'internal_error',
|
|
210
|
+
message: 'An unexpected error occurred while taking the screenshot',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
return router;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=screenshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../../src/server/routes/screenshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search endpoint with caching — supports DuckDuckGo (default) and Brave (BYOK)
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import { AuthStore } from '../auth-store.js';
|
|
6
|
+
export declare function createSearchRouter(authStore: AuthStore): Router;
|
|
7
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/server/routes/search.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAIpD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAuC7C,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAgU/D"}
|