mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
refactoring the api endpoints to be separate files to reduce context usage
This commit is contained in:
137
apps/server/src/routes/terminal/common.ts
Normal file
137
apps/server/src/routes/terminal/common.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Common utilities and state for terminal routes
|
||||
*/
|
||||
|
||||
import { createLogger } from "../../lib/logger.js";
|
||||
import type { Request, Response, NextFunction } from "express";
|
||||
import { getTerminalService } from "../../services/terminal-service.js";
|
||||
|
||||
const logger = createLogger("Terminal");
|
||||
|
||||
// Read env variables lazily to ensure dotenv has loaded them
|
||||
function getTerminalPassword(): string | undefined {
|
||||
return process.env.TERMINAL_PASSWORD;
|
||||
}
|
||||
|
||||
function getTerminalEnabledConfig(): boolean {
|
||||
return process.env.TERMINAL_ENABLED !== "false"; // Enabled by default
|
||||
}
|
||||
|
||||
// In-memory session tokens (would use Redis in production)
|
||||
export const validTokens: Map<string, { createdAt: Date; expiresAt: Date }> =
|
||||
new Map();
|
||||
const TOKEN_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
|
||||
|
||||
/**
|
||||
* Generate a secure random token
|
||||
*/
|
||||
export function generateToken(): string {
|
||||
return `term-${Date.now()}-${Math.random()
|
||||
.toString(36)
|
||||
.substr(2, 15)}${Math.random().toString(36).substr(2, 15)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up expired tokens
|
||||
*/
|
||||
export function cleanupExpiredTokens(): void {
|
||||
const now = new Date();
|
||||
validTokens.forEach((data, token) => {
|
||||
if (data.expiresAt < now) {
|
||||
validTokens.delete(token);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up expired tokens every 5 minutes
|
||||
setInterval(cleanupExpiredTokens, 5 * 60 * 1000);
|
||||
|
||||
/**
|
||||
* Validate a terminal session token
|
||||
*/
|
||||
export function validateTerminalToken(token: string | undefined): boolean {
|
||||
if (!token) return false;
|
||||
|
||||
const tokenData = validTokens.get(token);
|
||||
if (!tokenData) return false;
|
||||
|
||||
if (tokenData.expiresAt < new Date()) {
|
||||
validTokens.delete(token);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if terminal requires password
|
||||
*/
|
||||
export function isTerminalPasswordRequired(): boolean {
|
||||
return !!getTerminalPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if terminal is enabled
|
||||
*/
|
||||
export function isTerminalEnabled(): boolean {
|
||||
return getTerminalEnabledConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminal authentication middleware
|
||||
* Checks for valid session token if password is configured
|
||||
*/
|
||||
export function terminalAuthMiddleware(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): void {
|
||||
// Check if terminal is enabled
|
||||
if (!getTerminalEnabledConfig()) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: "Terminal access is disabled",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If no password configured, allow all requests
|
||||
if (!getTerminalPassword()) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for session token
|
||||
const token =
|
||||
(req.headers["x-terminal-token"] as string) || (req.query.token as string);
|
||||
|
||||
if (!validateTerminalToken(token)) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
error: "Terminal authentication required",
|
||||
passwordRequired: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
export function getTerminalPasswordConfig(): string | undefined {
|
||||
return getTerminalPassword();
|
||||
}
|
||||
|
||||
export function getTerminalEnabledConfigValue(): boolean {
|
||||
return getTerminalEnabledConfig();
|
||||
}
|
||||
|
||||
export function getTokenExpiryMs(): number {
|
||||
return TOKEN_EXPIRY_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error message from error object
|
||||
*/
|
||||
export function getErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : "Unknown error";
|
||||
}
|
||||
44
apps/server/src/routes/terminal/index.ts
Normal file
44
apps/server/src/routes/terminal/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Terminal routes with password protection
|
||||
*
|
||||
* Provides REST API for terminal session management and authentication.
|
||||
* WebSocket connections for real-time I/O are handled separately in index.ts.
|
||||
*/
|
||||
|
||||
import { Router } from "express";
|
||||
import {
|
||||
terminalAuthMiddleware,
|
||||
validateTerminalToken,
|
||||
isTerminalEnabled,
|
||||
isTerminalPasswordRequired,
|
||||
} from "./common.js";
|
||||
import { createStatusHandler } from "./routes/status.js";
|
||||
import { createAuthHandler } from "./routes/auth.js";
|
||||
import { createLogoutHandler } from "./routes/logout.js";
|
||||
import {
|
||||
createSessionsListHandler,
|
||||
createSessionsCreateHandler,
|
||||
} from "./routes/sessions.js";
|
||||
import { createSessionDeleteHandler } from "./routes/session-delete.js";
|
||||
import { createSessionResizeHandler } from "./routes/session-resize.js";
|
||||
|
||||
// Re-export for use in main index.ts
|
||||
export { validateTerminalToken, isTerminalEnabled, isTerminalPasswordRequired };
|
||||
|
||||
export function createTerminalRoutes(): Router {
|
||||
const router = Router();
|
||||
|
||||
router.get("/status", createStatusHandler());
|
||||
router.post("/auth", createAuthHandler());
|
||||
router.post("/logout", createLogoutHandler());
|
||||
|
||||
// Apply terminal auth middleware to all routes below
|
||||
router.use(terminalAuthMiddleware);
|
||||
|
||||
router.get("/sessions", createSessionsListHandler());
|
||||
router.post("/sessions", createSessionsCreateHandler());
|
||||
router.delete("/sessions/:id", createSessionDeleteHandler());
|
||||
router.post("/sessions/:id/resize", createSessionResizeHandler());
|
||||
|
||||
return router;
|
||||
}
|
||||
66
apps/server/src/routes/terminal/routes/auth.ts
Normal file
66
apps/server/src/routes/terminal/routes/auth.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* POST /auth endpoint - Authenticate with password to get a session token
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import {
|
||||
getTerminalEnabledConfigValue,
|
||||
getTerminalPasswordConfig,
|
||||
generateToken,
|
||||
validTokens,
|
||||
getTokenExpiryMs,
|
||||
getErrorMessage,
|
||||
} from "../common.js";
|
||||
|
||||
export function createAuthHandler() {
|
||||
return (req: Request, res: Response): void => {
|
||||
if (!getTerminalEnabledConfigValue()) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: "Terminal access is disabled",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const terminalPassword = getTerminalPasswordConfig();
|
||||
|
||||
// If no password required, return immediate success
|
||||
if (!terminalPassword) {
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
authenticated: true,
|
||||
passwordRequired: false,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { password } = req.body;
|
||||
|
||||
if (!password || password !== terminalPassword) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
error: "Invalid password",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate session token
|
||||
const token = generateToken();
|
||||
const now = new Date();
|
||||
validTokens.set(token, {
|
||||
createdAt: now,
|
||||
expiresAt: new Date(now.getTime() + getTokenExpiryMs()),
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
authenticated: true,
|
||||
token,
|
||||
expiresIn: getTokenExpiryMs(),
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
20
apps/server/src/routes/terminal/routes/logout.ts
Normal file
20
apps/server/src/routes/terminal/routes/logout.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* POST /logout endpoint - Invalidate a session token
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { validTokens } from "../common.js";
|
||||
|
||||
export function createLogoutHandler() {
|
||||
return (req: Request, res: Response): void => {
|
||||
const token = (req.headers["x-terminal-token"] as string) || req.body.token;
|
||||
|
||||
if (token) {
|
||||
validTokens.delete(token);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
26
apps/server/src/routes/terminal/routes/session-delete.ts
Normal file
26
apps/server/src/routes/terminal/routes/session-delete.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* DELETE /sessions/:id endpoint - Kill a terminal session
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { getTerminalService } from "../../../services/terminal-service.js";
|
||||
|
||||
export function createSessionDeleteHandler() {
|
||||
return (req: Request, res: Response): void => {
|
||||
const terminalService = getTerminalService();
|
||||
const { id } = req.params;
|
||||
const killed = terminalService.killSession(id);
|
||||
|
||||
if (!killed) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: "Session not found",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
36
apps/server/src/routes/terminal/routes/session-resize.ts
Normal file
36
apps/server/src/routes/terminal/routes/session-resize.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* POST /sessions/:id/resize endpoint - Resize a terminal session
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { getTerminalService } from "../../../services/terminal-service.js";
|
||||
|
||||
export function createSessionResizeHandler() {
|
||||
return (req: Request, res: Response): void => {
|
||||
const terminalService = getTerminalService();
|
||||
const { id } = req.params;
|
||||
const { cols, rows } = req.body;
|
||||
|
||||
if (!cols || !rows) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: "cols and rows are required",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const resized = terminalService.resize(id, cols, rows);
|
||||
|
||||
if (!resized) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: "Session not found",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
55
apps/server/src/routes/terminal/routes/sessions.ts
Normal file
55
apps/server/src/routes/terminal/routes/sessions.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* GET /sessions endpoint - List all active terminal sessions
|
||||
* POST /sessions endpoint - Create a new terminal session
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { getTerminalService } from "../../../services/terminal-service.js";
|
||||
import { getErrorMessage } from "../common.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
|
||||
const logger = createLogger("Terminal");
|
||||
|
||||
export function createSessionsListHandler() {
|
||||
return (_req: Request, res: Response): void => {
|
||||
const terminalService = getTerminalService();
|
||||
const sessions = terminalService.getAllSessions();
|
||||
res.json({
|
||||
success: true,
|
||||
data: sessions,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function createSessionsCreateHandler() {
|
||||
return (req: Request, res: Response): void => {
|
||||
try {
|
||||
const terminalService = getTerminalService();
|
||||
const { cwd, cols, rows, shell } = req.body;
|
||||
|
||||
const session = terminalService.createSession({
|
||||
cwd,
|
||||
cols: cols || 80,
|
||||
rows: rows || 24,
|
||||
shell,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
id: session.id,
|
||||
cwd: session.cwd,
|
||||
shell: session.shell,
|
||||
createdAt: session.createdAt,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("[Terminal] Error creating session:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: "Failed to create terminal session",
|
||||
details: getErrorMessage(error),
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
24
apps/server/src/routes/terminal/routes/status.ts
Normal file
24
apps/server/src/routes/terminal/routes/status.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* GET /status endpoint - Get terminal status
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { getTerminalService } from "../../../services/terminal-service.js";
|
||||
import {
|
||||
getTerminalEnabledConfigValue,
|
||||
isTerminalPasswordRequired,
|
||||
} from "../common.js";
|
||||
|
||||
export function createStatusHandler() {
|
||||
return (_req: Request, res: Response): void => {
|
||||
const terminalService = getTerminalService();
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
enabled: getTerminalEnabledConfigValue(),
|
||||
passwordRequired: isTerminalPasswordRequired(),
|
||||
platform: terminalService.getPlatformInfo(),
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user