♻️ feat: implement session management for PTY sessions in the server
- Add SessionManager class to handle PTY sessions with WebSocket connections. - Implement methods for creating, retrieving, and destroying sessions. - Handle PTY output and WebSocket messages for terminal interaction. - Ensure graceful session destruction and cleanup. feat: initialize web application with Next.js and Tailwind CSS - Create initial Next.js application structure with TypeScript support. - Set up Tailwind CSS for styling with custom theme configurations. - Add ESLint configuration for code quality and consistency. feat: implement chat API and UI components - Create chat API route to handle chat requests and responses. - Develop chat layout with sidebar, header, chat window, and input components. - Integrate Zustand for state management of conversations and messages. - Add utility functions for formatting dates and managing class names. chore: add environment variables and configuration files - Create .env.example for environment variable setup. - Add configuration files for PostCSS, Tailwind CSS, and TypeScript. - Set up package.json with necessary dependencies and scripts for development.
This commit is contained in:
65
src/server/web/auth.ts
Normal file
65
src/server/web/auth.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import type { IncomingMessage } from "http";
|
||||
|
||||
/**
|
||||
* Validates the auth token from a WebSocket upgrade request.
|
||||
* If AUTH_TOKEN is not set, all connections are allowed.
|
||||
*/
|
||||
export function validateAuthToken(req: IncomingMessage): boolean {
|
||||
const authToken = process.env.AUTH_TOKEN;
|
||||
if (!authToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
||||
const token = url.searchParams.get("token");
|
||||
return token === authToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple per-IP rate limiter for new connections.
|
||||
*/
|
||||
export class ConnectionRateLimiter {
|
||||
private attempts = new Map<string, number[]>();
|
||||
private readonly maxPerWindow: number;
|
||||
private readonly windowMs: number;
|
||||
|
||||
constructor(maxPerWindow = 5, windowMs = 60_000) {
|
||||
this.maxPerWindow = maxPerWindow;
|
||||
this.windowMs = windowMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the connection should be allowed.
|
||||
*/
|
||||
allow(ip: string): boolean {
|
||||
const now = Date.now();
|
||||
const timestamps = this.attempts.get(ip) ?? [];
|
||||
|
||||
// Prune old entries
|
||||
const recent = timestamps.filter((t) => now - t < this.windowMs);
|
||||
|
||||
if (recent.length >= this.maxPerWindow) {
|
||||
this.attempts.set(ip, recent);
|
||||
return false;
|
||||
}
|
||||
|
||||
recent.push(now);
|
||||
this.attempts.set(ip, recent);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodically clean up stale entries.
|
||||
*/
|
||||
cleanup(): void {
|
||||
const now = Date.now();
|
||||
for (const [ip, timestamps] of this.attempts) {
|
||||
const recent = timestamps.filter((t) => now - t < this.windowMs);
|
||||
if (recent.length === 0) {
|
||||
this.attempts.delete(ip);
|
||||
} else {
|
||||
this.attempts.set(ip, recent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user