Migration Cleanup
This commit is contained in:
parent
d2c014e744
commit
84225a66f9
8 changed files with 224 additions and 354 deletions
306
checkpoint.js
306
checkpoint.js
|
|
@ -19,6 +19,7 @@ import {
|
|||
verifyPoW,
|
||||
verifyPoS,
|
||||
} from './utils/proof.js';
|
||||
import express from 'express';
|
||||
// Import recordEvent dynamically to avoid circular dependency issues
|
||||
let recordEvent;
|
||||
let statsLoadPromise = import('./plugins/stats.js')
|
||||
|
|
@ -560,52 +561,6 @@ async function validateToken(tokenStr, request) {
|
|||
}
|
||||
}
|
||||
|
||||
async function createProxyResponse(targetURL, request) {
|
||||
const url = new URL(request.url);
|
||||
const targetUrl = new URL(url.pathname + url.search, targetURL);
|
||||
|
||||
const headers = Object.fromEntries(request.headers.entries());
|
||||
delete headers.host;
|
||||
|
||||
try {
|
||||
const method = request.method;
|
||||
const options = {
|
||||
method,
|
||||
headers,
|
||||
redirect: 'manual',
|
||||
};
|
||||
|
||||
if (method !== 'GET' && method !== 'HEAD') {
|
||||
options.body = await request.blob();
|
||||
}
|
||||
|
||||
const response = await fetch(targetUrl.toString(), options);
|
||||
|
||||
const responseHeaders = new Headers(response.headers);
|
||||
const hopByHopHeaders = [
|
||||
'connection',
|
||||
'keep-alive',
|
||||
'proxy-authenticate',
|
||||
'proxy-authorization',
|
||||
'te',
|
||||
'trailer',
|
||||
'transfer-encoding',
|
||||
'upgrade',
|
||||
];
|
||||
|
||||
hopByHopHeaders.forEach((h) => responseHeaders.delete(h));
|
||||
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Proxy error:', err);
|
||||
return new Response('Bad Gateway', { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTokenRedirect(request) {
|
||||
const url = new URL(request.url);
|
||||
const tokenStr = url.searchParams.get('token');
|
||||
|
|
@ -660,162 +615,145 @@ async function handleTokenRedirect(request) {
|
|||
function CheckpointMiddleware() {
|
||||
// Return Express-compatible middleware
|
||||
return {
|
||||
middleware: async (req, res, next) => {
|
||||
// Check if checkpoint is enabled
|
||||
if (checkpointConfig.Enabled === false) {
|
||||
return next();
|
||||
}
|
||||
middleware: [
|
||||
// Add body parser middleware for JSON
|
||||
express.json({ limit: '10mb' }),
|
||||
// Main checkpoint middleware
|
||||
async (req, res, next) => {
|
||||
// Check if checkpoint is enabled
|
||||
if (checkpointConfig.Enabled === false) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Convert Express request to the format expected by checkpoint logic
|
||||
const request = {
|
||||
url: `${req.protocol}://${req.get('host')}${req.originalUrl}`,
|
||||
method: req.method,
|
||||
headers: {
|
||||
get: (name) => req.get(name),
|
||||
entries: () => Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(', ') : v])
|
||||
},
|
||||
json: () => new Promise((resolve, reject) => {
|
||||
let body = '';
|
||||
req.on('data', chunk => body += chunk);
|
||||
req.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(body));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
// Convert Express request to the format expected by checkpoint logic
|
||||
const request = {
|
||||
url: `${req.protocol}://${req.get('host')}${req.originalUrl}`,
|
||||
method: req.method,
|
||||
headers: {
|
||||
get: (name) => req.get(name),
|
||||
entries: () => Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(', ') : v])
|
||||
},
|
||||
json: () => Promise.resolve(req.body)
|
||||
};
|
||||
|
||||
const urlObj = new URL(request.url);
|
||||
const host = request.headers.get('host')?.split(':')[0];
|
||||
const userAgent = request.headers.get('user-agent') || '';
|
||||
|
||||
// 1) Bypass via query keys
|
||||
for (const { Key, Value, Domains } of checkpointConfig.BypassQueryKeys) {
|
||||
if (urlObj.searchParams.get(Key) === Value) {
|
||||
if (!Array.isArray(Domains) || Domains.length === 0 || Domains.includes(host)) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Bypass via header keys
|
||||
for (const { Name, Value, Domains } of checkpointConfig.BypassHeaderKeys) {
|
||||
const headerVal = request.headers.get(Name);
|
||||
if (headerVal === Value) {
|
||||
if (!Array.isArray(Domains) || Domains.length === 0 || Domains.includes(host)) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle token redirect for URL-token login
|
||||
const tokenResponse = await handleTokenRedirect(request);
|
||||
if (tokenResponse) {
|
||||
// Convert Response to Express response
|
||||
res.status(tokenResponse.status);
|
||||
tokenResponse.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
req.on('error', reject);
|
||||
})
|
||||
};
|
||||
|
||||
const urlObj = new URL(request.url);
|
||||
const host = request.headers.get('host')?.split(':')[0];
|
||||
const userAgent = request.headers.get('user-agent') || '';
|
||||
|
||||
// 1) Bypass via query keys
|
||||
for (const { Key, Value, Domains } of checkpointConfig.BypassQueryKeys) {
|
||||
if (urlObj.searchParams.get(Key) === Value) {
|
||||
if (!Array.isArray(Domains) || Domains.length === 0 || Domains.includes(host)) {
|
||||
return next();
|
||||
}
|
||||
const body = await tokenResponse.text();
|
||||
return res.send(body);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Bypass via header keys
|
||||
for (const { Name, Value, Domains } of checkpointConfig.BypassHeaderKeys) {
|
||||
const headerVal = request.headers.get(Name);
|
||||
if (headerVal === Value) {
|
||||
if (!Array.isArray(Domains) || Domains.length === 0 || Domains.includes(host)) {
|
||||
return next();
|
||||
}
|
||||
// Setup request context
|
||||
const url = new URL(request.url);
|
||||
let path = url.pathname;
|
||||
if (checkpointConfig.SanitizeURLs) {
|
||||
path = sanitizePath(path);
|
||||
}
|
||||
}
|
||||
const method = request.method;
|
||||
|
||||
// Handle token redirect for URL-token login
|
||||
const tokenResponse = await handleTokenRedirect(request);
|
||||
if (tokenResponse) {
|
||||
// Convert Response to Express response
|
||||
res.status(tokenResponse.status);
|
||||
tokenResponse.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
const body = await tokenResponse.text();
|
||||
return res.send(body);
|
||||
}
|
||||
// Always allow challenge & verify endpoints
|
||||
if (method === 'GET' && path === '/api/challenge') {
|
||||
const response = await handleGetCheckpointChallenge(request);
|
||||
res.status(response.status);
|
||||
response.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
const body = await response.text();
|
||||
return res.send(body);
|
||||
}
|
||||
if (method === 'POST' && path === '/api/verify') {
|
||||
const response = await handleVerifyCheckpoint(request);
|
||||
res.status(response.status);
|
||||
response.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
const body = await response.text();
|
||||
return res.send(body);
|
||||
}
|
||||
|
||||
// Setup request context
|
||||
const url = new URL(request.url);
|
||||
let path = url.pathname;
|
||||
if (checkpointConfig.SanitizeURLs) {
|
||||
path = sanitizePath(path);
|
||||
}
|
||||
const method = request.method;
|
||||
|
||||
// Always allow challenge & verify endpoints
|
||||
if (method === 'GET' && path === '/api/challenge') {
|
||||
const response = await handleGetCheckpointChallenge(request);
|
||||
res.status(response.status);
|
||||
response.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
const body = await response.text();
|
||||
return res.send(body);
|
||||
}
|
||||
if (method === 'POST' && path === '/api/verify') {
|
||||
const response = await handleVerifyCheckpoint(request);
|
||||
res.status(response.status);
|
||||
response.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
const body = await response.text();
|
||||
return res.send(body);
|
||||
}
|
||||
|
||||
// Check new exclusion rules
|
||||
if (checkpointConfig.ExclusionRules && checkpointConfig.ExclusionRules.length > 0) {
|
||||
for (const rule of checkpointConfig.ExclusionRules) {
|
||||
// Check if path matches
|
||||
if (!rule.Path || !path.startsWith(rule.Path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if host matches (if specified)
|
||||
if (rule.Hosts && rule.Hosts.length > 0 && !rule.Hosts.includes(host)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if user agent matches (if specified)
|
||||
if (rule.UserAgents && rule.UserAgents.length > 0) {
|
||||
const matchesUA = rule.UserAgents.some((ua) => userAgent.includes(ua));
|
||||
if (!matchesUA) {
|
||||
// Check new exclusion rules
|
||||
if (checkpointConfig.ExclusionRules && checkpointConfig.ExclusionRules.length > 0) {
|
||||
for (const rule of checkpointConfig.ExclusionRules) {
|
||||
// Check if path matches
|
||||
if (!rule.Path || !path.startsWith(rule.Path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if host matches (if specified)
|
||||
if (rule.Hosts && rule.Hosts.length > 0 && !rule.Hosts.includes(host)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if user agent matches (if specified)
|
||||
if (rule.UserAgents && rule.UserAgents.length > 0) {
|
||||
const matchesUA = rule.UserAgents.some((ua) => userAgent.includes(ua));
|
||||
if (!matchesUA) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// All conditions match - exclude this request
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
// All conditions match - exclude this request
|
||||
// Skip checkpoint for requests that don't accept HTML
|
||||
if (!req.accepts('html')) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
// Check file extensions
|
||||
const ext = path.includes('.') ? path.slice(path.lastIndexOf('.')) : '';
|
||||
|
||||
// First check excluded extensions
|
||||
if (ext && checkpointConfig.HTMLCheckpointExcludedExtensions.includes(ext)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Then check if we should only include specific extensions
|
||||
if (checkpointConfig.HTMLCheckpointIncludedExtensions.length > 0) {
|
||||
// If extension list is specified and current extension is not in it, skip
|
||||
if (!checkpointConfig.HTMLCheckpointIncludedExtensions.includes(ext)) {
|
||||
// Validate session token
|
||||
const cookies = cookie.parse(request.headers.get('cookie') || '');
|
||||
const tokenCookie = cookies[checkpointConfig.CookieName];
|
||||
const validation = await validateToken(tokenCookie, request);
|
||||
if (validation) {
|
||||
// Active session: bypass checkpoint
|
||||
return next();
|
||||
}
|
||||
|
||||
// Log new checkpoint flow
|
||||
console.log(`checkpoint: incoming ${method} ${request.url}`);
|
||||
console.log(`checkpoint: tokenCookie=${tokenCookie}`);
|
||||
console.log(`checkpoint: validateToken => ${validation}`);
|
||||
|
||||
// Serve interstitial challenge
|
||||
const response = await serveInterstitial(request);
|
||||
res.status(response.status);
|
||||
response.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
const body = await response.text();
|
||||
return res.send(body);
|
||||
}
|
||||
|
||||
// Validate session token
|
||||
const cookies = cookie.parse(request.headers.get('cookie') || '');
|
||||
const tokenCookie = cookies[checkpointConfig.CookieName];
|
||||
const validation = await validateToken(tokenCookie, request);
|
||||
if (validation) {
|
||||
// Active session: bypass checkpoint
|
||||
return next();
|
||||
}
|
||||
|
||||
// Log new checkpoint flow
|
||||
console.log(`checkpoint: incoming ${method} ${request.url}`);
|
||||
console.log(`checkpoint: tokenCookie=${tokenCookie}`);
|
||||
console.log(`checkpoint: validateToken => ${validation}`);
|
||||
|
||||
// Serve interstitial challenge
|
||||
const response = await serveInterstitial(request);
|
||||
res.status(response.status);
|
||||
response.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
const body = await response.text();
|
||||
return res.send(body);
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue