Añadido el modo producción / test

This commit is contained in:
Jordan Diaz
2026-04-08 23:52:54 +00:00
parent c1a29bbbf8
commit 993e7d3000
9 changed files with 240 additions and 90 deletions

View File

@@ -1,11 +1,9 @@
import { z } from "zod";
import fs from "fs";
import path from "path";
import axios from "axios";
import { sessionCredentials } from "../../auth/credentials.js";
import { withAuthParams } from "../helpers/authSchema.js";
const LOCAL_SERVER_URL = `http://localhost:${process.env.ACAI_HOST_PORT || 29871}`;
import { fetchProjectInfo } from "../../auth/localClient.js";
export function registerAuthTools(server) {
server.tool(
@@ -54,14 +52,10 @@ export function registerAuthTools(server) {
// Step 3: If expired, ask Python server to refresh it
if (isExpired) {
try {
// Call the compile-module endpoint pattern — but we need a refresh endpoint
// Use the server's existing auto-refresh: just call any endpoint that triggers refresh
// The simplest: GET /api/projects which auto-refreshes expired tokens
const res = await axios.get(`${LOCAL_SERVER_URL}/api/projects`, { timeout: 15000 });
// Re-read .acai after server refreshed it
const data = JSON.parse(fs.readFileSync(acaiFilePath, "utf-8"));
token = data.token || "";
tokenHash = data.tokenHash || "";
const info = await fetchProjectInfo({ project_dir: projectDir });
token = info?.token || token;
tokenHash = info?.tokenHash || tokenHash;
domain = info?.domain || domain;
} catch (e) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: `Token refresh failed: ${e.message}` }) }],
@@ -70,14 +64,18 @@ export function registerAuthTools(server) {
}
}
// Step 4: Update credentials in memory
const webUrl = process.env.ACAI_WEB_URL || "";
const website = domain || process.env.ACAI_WEBSITE || "";
// Step 4: Update credentials in memory from the canonical server resolver
const info = await fetchProjectInfo({ project_dir: projectDir });
const webUrl = info?.web_url || process.env.ACAI_WEB_URL || "";
const apiWebUrl = info?.api_web_url || process.env.ACAI_API_WEB_URL || webUrl;
const website = info?.domain || domain || process.env.ACAI_WEBSITE || "";
const freshCreds = {
token,
tokenHash,
website,
web_url: webUrl,
api_web_url: apiWebUrl,
forge_host: info?.forge_host || null,
profileName: "stdio",
role: "developer",
};

View File

@@ -5,6 +5,15 @@ import path from 'path';
* Check if the current user has write access to a table.
* Reads .acai file from ACAI_PROJECT_DIR.
* Returns { allowed: true } or { allowed: false, error: "..." }
*
* NOTA: Esta funcion NO depende del campo `mode`. En modo produccion los
* registros de tablas (contenido CMS) se siguen pudiendo editar — son los
* datos reales del usuario, no codigo. El bloqueo de produccion solo aplica
* a escritura de archivos de codigo (gestionado por is_project_admin en el
* server Python al recibir POST /api/files/*).
*
* Si existe ACAI_MODE_OVERRIDE en el entorno (cronjob con override), se usa
* en lugar del .acai para determinar el modo.
*/
export function canAccessTable(tableName) {
const projectDir = process.env.ACAI_PROJECT_DIR || "";
@@ -14,6 +23,10 @@ export function canAccessTable(tableName) {
try {
if (!fs.existsSync(acaiFile)) return { allowed: true };
const data = JSON.parse(fs.readFileSync(acaiFile, "utf-8"));
// Override de modo (cronjobs lo inyectan via env var)
if (process.env.ACAI_MODE_OVERRIDE) {
data.mode = process.env.ACAI_MODE_OVERRIDE;
}
const user = data.user || {};
// Admin has full access

View File

@@ -154,7 +154,7 @@ export function registerUploadImageToAssetsTool(server) {
// Upload using saveFileBuilder
const uploadResult = await saveFileBuilder({
web_url: credentials.web_url,
web_url: credentials.api_web_url || credentials.web_url,
token: credentials.token,
tokenHash: credentials.tokenHash,
path: assetsPath,

View File

@@ -1,5 +1,5 @@
import { z } from "zod";
import { withAuth, getSessionCredentials } from "../../auth/index.js";
import { getSessionCredentials } from "../../auth/index.js";
import { handleToolError } from "../helpers/errorHandler.js";
import { withAuthParams } from "../helpers/authSchema.js";
@@ -9,9 +9,10 @@ export function registerGetWebUrlTool(server) {
`Get the correct URL for the project's development website. Always use this URL for fetch, Playwright, or any HTTP request to the site. Never guess or use production domains.`,
withAuthParams({}),
{ readOnlyHint: true, destructiveHint: false },
withAuth(async (_params, extra) => {
async (_params, extra) => {
try {
const credentials = await getSessionCredentials(extra.sessionId);
const sessionId = extra?.sessionId || "_default";
const credentials = await getSessionCredentials(sessionId);
if (!credentials || !credentials.web_url) {
return {
@@ -20,17 +21,11 @@ export function registerGetWebUrlTool(server) {
};
}
// Inside Docker, HTTPS is not available — force HTTP for internal requests
let webUrl = credentials.web_url;
if (webUrl && webUrl.startsWith("https://") && webUrl.includes(".forge.")) {
webUrl = webUrl.replace("https://", "http://");
}
return {
content: [{
type: "text",
text: JSON.stringify({
web_url: webUrl,
web_url: credentials.web_url,
api_web_url: credentials.api_web_url || null,
website: credentials.website || null,
note: "Always use web_url for Playwright/fetch. IMPORTANT: Always append ?pruebas=1 to any URL you visit (e.g. web_url + '/?pruebas=1' or web_url + '/servicios/?pruebas=1'). Never use the production domain directly.",
@@ -40,6 +35,6 @@ export function registerGetWebUrlTool(server) {
} catch (error) {
return handleToolError(error, "get_web_url", {});
}
})
}
);
}