♻️ 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:
nirholas
2026-03-31 12:35:31 +00:00
parent d31c2bec03
commit 38648ae5f4
53 changed files with 4177 additions and 4 deletions

65
src/server/web/auth.ts Normal file
View 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);
}
}
}
}