mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
refactor: encapsulate state management for spec and suggestions generation
- Made the generation status variables private and introduced getter functions for both spec and suggestions generation states. - Updated relevant route handlers to utilize the new getter functions, improving encapsulation and reducing direct access to shared state. - Enhanced code maintainability by centralizing state management logic.
This commit is contained in:
@@ -3,13 +3,22 @@
|
||||
*/
|
||||
|
||||
import { createLogger } from "../../lib/logger.js";
|
||||
import { getErrorMessage as getErrorMessageShared } from "../common.js";
|
||||
|
||||
const logger = createLogger("SpecRegeneration");
|
||||
|
||||
// Shared state for tracking generation status
|
||||
export let isRunning = false;
|
||||
export let currentAbortController: AbortController | null = null;
|
||||
// Shared state for tracking generation status - private
|
||||
let isRunning = false;
|
||||
let currentAbortController: AbortController | null = null;
|
||||
|
||||
/**
|
||||
* Get the current running state
|
||||
*/
|
||||
export function getSpecRegenerationStatus(): {
|
||||
isRunning: boolean;
|
||||
currentAbortController: AbortController | null;
|
||||
} {
|
||||
return { isRunning, currentAbortController };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the running state and abort controller
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Request, Response } from "express";
|
||||
import type { EventEmitter } from "../../../lib/events.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import {
|
||||
isRunning,
|
||||
getSpecRegenerationStatus,
|
||||
setRunningState,
|
||||
logAuthStatus,
|
||||
logError,
|
||||
@@ -48,6 +48,7 @@ export function createCreateHandler(events: EventEmitter) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { isRunning } = getSpecRegenerationStatus();
|
||||
if (isRunning) {
|
||||
logger.warn("Generation already running, rejecting request");
|
||||
res.json({ success: false, error: "Spec generation already running" });
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Request, Response } from "express";
|
||||
import type { EventEmitter } from "../../../lib/events.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import {
|
||||
isRunning,
|
||||
getSpecRegenerationStatus,
|
||||
setRunningState,
|
||||
logAuthStatus,
|
||||
logError,
|
||||
@@ -32,6 +32,7 @@ export function createGenerateFeaturesHandler(events: EventEmitter) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { isRunning } = getSpecRegenerationStatus();
|
||||
if (isRunning) {
|
||||
logger.warn("Generation already running, rejecting request");
|
||||
res.json({ success: false, error: "Generation already running" });
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Request, Response } from "express";
|
||||
import type { EventEmitter } from "../../../lib/events.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import {
|
||||
isRunning,
|
||||
getSpecRegenerationStatus,
|
||||
setRunningState,
|
||||
logAuthStatus,
|
||||
logError,
|
||||
@@ -52,6 +52,7 @@ export function createGenerateHandler(events: EventEmitter) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { isRunning } = getSpecRegenerationStatus();
|
||||
if (isRunning) {
|
||||
logger.warn("Generation already running, rejecting request");
|
||||
res.json({ success: false, error: "Spec generation already running" });
|
||||
|
||||
@@ -3,11 +3,15 @@
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { isRunning, getErrorMessage } from "../common.js";
|
||||
import {
|
||||
getSpecRegenerationStatus,
|
||||
getErrorMessage,
|
||||
} from "../common.js";
|
||||
|
||||
export function createStatusHandler() {
|
||||
return async (_req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { isRunning } = getSpecRegenerationStatus();
|
||||
res.json({ success: true, isRunning });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: getErrorMessage(error) });
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import {
|
||||
currentAbortController,
|
||||
getSpecRegenerationStatus,
|
||||
setRunningState,
|
||||
getErrorMessage,
|
||||
} from "../common.js";
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
export function createStopHandler() {
|
||||
return async (_req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { currentAbortController } = getSpecRegenerationStatus();
|
||||
if (currentAbortController) {
|
||||
currentAbortController.abort();
|
||||
}
|
||||
|
||||
@@ -12,8 +12,29 @@ import {
|
||||
|
||||
const logger = createLogger("Setup");
|
||||
|
||||
// Storage for API keys (in-memory cache)
|
||||
export const apiKeys: Record<string, string> = {};
|
||||
// Storage for API keys (in-memory cache) - private
|
||||
const apiKeys: Record<string, string> = {};
|
||||
|
||||
/**
|
||||
* Get an API key for a provider
|
||||
*/
|
||||
export function getApiKey(provider: string): string | undefined {
|
||||
return apiKeys[provider];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an API key for a provider
|
||||
*/
|
||||
export function setApiKey(provider: string, key: string): void {
|
||||
apiKeys[provider] = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all API keys (for read-only access)
|
||||
*/
|
||||
export function getAllApiKeys(): Record<string, string> {
|
||||
return { ...apiKeys };
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to persist API keys to .env file
|
||||
|
||||
@@ -7,7 +7,7 @@ import { promisify } from "util";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import { apiKeys } from "./common.js";
|
||||
import { getApiKey } from "./common.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -71,8 +71,8 @@ export async function getClaudeStatus() {
|
||||
method: "none" as string,
|
||||
hasCredentialsFile: false,
|
||||
hasToken: false,
|
||||
hasStoredOAuthToken: !!apiKeys.anthropic_oauth_token,
|
||||
hasStoredApiKey: !!apiKeys.anthropic,
|
||||
hasStoredOAuthToken: !!getApiKey("anthropic_oauth_token"),
|
||||
hasStoredApiKey: !!getApiKey("anthropic"),
|
||||
hasEnvApiKey: !!process.env.ANTHROPIC_API_KEY,
|
||||
hasEnvOAuthToken: !!process.env.CLAUDE_CODE_OAUTH_TOKEN,
|
||||
// Additional fields for detailed status
|
||||
@@ -159,14 +159,14 @@ export async function getClaudeStatus() {
|
||||
}
|
||||
|
||||
// In-memory stored OAuth token (from setup wizard - subscription auth)
|
||||
if (!auth.authenticated && apiKeys.anthropic_oauth_token) {
|
||||
if (!auth.authenticated && getApiKey("anthropic_oauth_token")) {
|
||||
auth.authenticated = true;
|
||||
auth.oauthTokenValid = true;
|
||||
auth.method = "oauth_token"; // Stored OAuth token from setup wizard
|
||||
}
|
||||
|
||||
// In-memory stored API key (from settings UI - pay-per-use)
|
||||
if (!auth.authenticated && apiKeys.anthropic) {
|
||||
if (!auth.authenticated && getApiKey("anthropic")) {
|
||||
auth.authenticated = true;
|
||||
auth.apiKeyValid = true;
|
||||
auth.method = "api_key"; // Manually stored API key
|
||||
|
||||
@@ -3,15 +3,19 @@
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { apiKeys, getErrorMessage, logError } from "../common.js";
|
||||
import {
|
||||
getApiKey,
|
||||
getErrorMessage,
|
||||
logError,
|
||||
} from "../common.js";
|
||||
|
||||
export function createApiKeysHandler() {
|
||||
return async (_req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
res.json({
|
||||
success: true,
|
||||
hasAnthropicKey: !!apiKeys.anthropic || !!process.env.ANTHROPIC_API_KEY,
|
||||
hasGoogleKey: !!apiKeys.google || !!process.env.GOOGLE_API_KEY,
|
||||
hasAnthropicKey: !!getApiKey("anthropic") || !!process.env.ANTHROPIC_API_KEY,
|
||||
hasGoogleKey: !!getApiKey("google") || !!process.env.GOOGLE_API_KEY,
|
||||
});
|
||||
} catch (error) {
|
||||
logError(error, "Get API keys failed");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import {
|
||||
apiKeys,
|
||||
setApiKey,
|
||||
persistApiKeyToEnv,
|
||||
getErrorMessage,
|
||||
logError,
|
||||
@@ -28,7 +28,7 @@ export function createStoreApiKeyHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
apiKeys[provider] = apiKey;
|
||||
setApiKey(provider, apiKey);
|
||||
|
||||
// Also set as environment variable and persist to .env
|
||||
// IMPORTANT: OAuth tokens and API keys must be stored separately
|
||||
|
||||
@@ -10,9 +10,19 @@ import {
|
||||
|
||||
const logger = createLogger("Suggestions");
|
||||
|
||||
// Shared state for tracking generation status
|
||||
export let isRunning = false;
|
||||
export let currentAbortController: AbortController | null = null;
|
||||
// Shared state for tracking generation status - private
|
||||
let isRunning = false;
|
||||
let currentAbortController: AbortController | null = null;
|
||||
|
||||
/**
|
||||
* Get the current running state
|
||||
*/
|
||||
export function getSuggestionsStatus(): {
|
||||
isRunning: boolean;
|
||||
currentAbortController: AbortController | null;
|
||||
} {
|
||||
return { isRunning, currentAbortController };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the running state and abort controller
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Request, Response } from "express";
|
||||
import type { EventEmitter } from "../../../lib/events.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import {
|
||||
isRunning,
|
||||
getSuggestionsStatus,
|
||||
setRunningState,
|
||||
getErrorMessage,
|
||||
logError,
|
||||
@@ -28,6 +28,7 @@ export function createGenerateHandler(events: EventEmitter) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { isRunning } = getSuggestionsStatus();
|
||||
if (isRunning) {
|
||||
res.json({
|
||||
success: false,
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { isRunning, getErrorMessage, logError } from "../common.js";
|
||||
import {
|
||||
getSuggestionsStatus,
|
||||
getErrorMessage,
|
||||
logError,
|
||||
} from "../common.js";
|
||||
|
||||
export function createStatusHandler() {
|
||||
return async (_req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { isRunning } = getSuggestionsStatus();
|
||||
res.json({ success: true, isRunning });
|
||||
} catch (error) {
|
||||
logError(error, "Get status failed");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import {
|
||||
currentAbortController,
|
||||
getSuggestionsStatus,
|
||||
setRunningState,
|
||||
getErrorMessage,
|
||||
logError,
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
export function createStopHandler() {
|
||||
return async (_req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { currentAbortController } = getSuggestionsStatus();
|
||||
if (currentAbortController) {
|
||||
currentAbortController.abort();
|
||||
}
|
||||
|
||||
@@ -17,11 +17,37 @@ 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 }> =
|
||||
// In-memory session tokens (would use Redis in production) - private
|
||||
const validTokens: Map<string, { createdAt: Date; expiresAt: Date }> =
|
||||
new Map();
|
||||
const TOKEN_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
|
||||
|
||||
/**
|
||||
* Add a token to the valid tokens map
|
||||
*/
|
||||
export function addToken(
|
||||
token: string,
|
||||
data: { createdAt: Date; expiresAt: Date }
|
||||
): void {
|
||||
validTokens.set(token, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a token from the valid tokens map
|
||||
*/
|
||||
export function deleteToken(token: string): void {
|
||||
validTokens.delete(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token data for a given token
|
||||
*/
|
||||
export function getTokenData(
|
||||
token: string
|
||||
): { createdAt: Date; expiresAt: Date } | undefined {
|
||||
return validTokens.get(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secure random token
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getTerminalEnabledConfigValue,
|
||||
getTerminalPasswordConfig,
|
||||
generateToken,
|
||||
validTokens,
|
||||
addToken,
|
||||
getTokenExpiryMs,
|
||||
getErrorMessage,
|
||||
} from "../common.js";
|
||||
@@ -49,7 +49,7 @@ export function createAuthHandler() {
|
||||
// Generate session token
|
||||
const token = generateToken();
|
||||
const now = new Date();
|
||||
validTokens.set(token, {
|
||||
addToken(token, {
|
||||
createdAt: now,
|
||||
expiresAt: new Date(now.getTime() + getTokenExpiryMs()),
|
||||
});
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { validTokens } from "../common.js";
|
||||
import { deleteToken } 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);
|
||||
deleteToken(token);
|
||||
}
|
||||
|
||||
res.json({
|
||||
|
||||
Reference in New Issue
Block a user