460 lines
15 KiB
JavaScript
460 lines
15 KiB
JavaScript
|
|
/**
|
|||
|
|
* @description 本地登录代理脚本:启动后会调用后端登录接口并缓存 accessToken,供前端调试读取。
|
|||
|
|
*
|
|||
|
|
* 使用方式:
|
|||
|
|
* - 通过 package.json 脚本启动(推荐):
|
|||
|
|
* - `pnpm dev`
|
|||
|
|
* - `pnpm dev:pet`
|
|||
|
|
* - `pnpm dev:qa`
|
|||
|
|
* - 或直接启动:
|
|||
|
|
* - `node ./scripts/local-auth-proxy.mjs --mode pet`
|
|||
|
|
*
|
|||
|
|
* 可配置项(优先级:process.env > .env/.env.<mode>):
|
|||
|
|
* - `BUSINESS_RULES_MODE`:环境模式(默认 `pet`)
|
|||
|
|
* - `LOCAL_AUTH_PROXY_PORT`:代理服务端口(默认 `9530`)
|
|||
|
|
* - `LOCAL_AUTH_PROFILE`:启动时指定登录 profile,未配置时读取本地配置中的 defaultProfile
|
|||
|
|
* - `LOCAL_AUTH_CONFIG_PATH`:本地账号配置文件路径,默认 `./dev-tools/local-auth/config.local.json`
|
|||
|
|
* - `VITE_API_BASE`:后端网关地址(必填)
|
|||
|
|
*
|
|||
|
|
* 代理接口:
|
|||
|
|
* - `GET /__local_auth/access-token`
|
|||
|
|
* - `GET /__local_auth/refresh-token`
|
|||
|
|
* - `GET /__local_auth/state`
|
|||
|
|
* - `GET /__local_auth/switch-profile?profile=<profileName>`
|
|||
|
|
*/
|
|||
|
|
import { readFile } from 'node:fs/promises';
|
|||
|
|
import http from 'node:http';
|
|||
|
|
import { dirname, relative, resolve } from 'node:path';
|
|||
|
|
import process from 'node:process';
|
|||
|
|
import { fileURLToPath } from 'node:url';
|
|||
|
|
import {
|
|||
|
|
LOCAL_AUTH_ACCESS_TOKEN_PATH,
|
|||
|
|
LOCAL_AUTH_PROXY_DEFAULT_PORT,
|
|||
|
|
LOCAL_AUTH_PROXY_PORT_SCAN_LIMIT,
|
|||
|
|
LOCAL_AUTH_PROXY_SERVICE_ID,
|
|||
|
|
LOCAL_AUTH_REFRESH_TOKEN_PATH,
|
|||
|
|
LOCAL_AUTH_STATE_PATH,
|
|||
|
|
LOCAL_AUTH_SWITCH_PROFILE_PATH,
|
|||
|
|
} from './local-auth-shared.mjs';
|
|||
|
|
|
|||
|
|
const __filename = fileURLToPath(import.meta.url);
|
|||
|
|
const __dirname = dirname(__filename);
|
|||
|
|
const pkgRoot = resolve(__dirname, '..');
|
|||
|
|
const localAuthDir = resolve(pkgRoot, 'dev-tools/local-auth');
|
|||
|
|
const defaultLocalAuthConfigPath = resolve(localAuthDir, 'config.local.json');
|
|||
|
|
const localAuthConfigPath = process.env.LOCAL_AUTH_CONFIG_PATH
|
|||
|
|
? resolve(pkgRoot, process.env.LOCAL_AUTH_CONFIG_PATH)
|
|||
|
|
: defaultLocalAuthConfigPath;
|
|||
|
|
const localAuthExampleConfigPath = resolve(localAuthDir, 'config.example.json');
|
|||
|
|
|
|||
|
|
const proxyPort = Number(process.env.LOCAL_AUTH_PROXY_PORT || LOCAL_AUTH_PROXY_DEFAULT_PORT);
|
|||
|
|
const cliArgs = process.argv.slice(2);
|
|||
|
|
const modeFlagIndex = cliArgs.findIndex(arg => arg === '--mode');
|
|||
|
|
const modeArg = modeFlagIndex >= 0 ? String(cliArgs[modeFlagIndex + 1] || '').trim() : '';
|
|||
|
|
const mode = modeArg || String(process.env.BUSINESS_RULES_MODE || 'pet').trim() || 'pet';
|
|||
|
|
const instanceId = String(process.env.LOCAL_AUTH_INSTANCE_ID || '').trim();
|
|||
|
|
const loginPath = '/biz-gateway/base/foundation/api/auth/login';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 解析 .env 文件文本为键值对象。
|
|||
|
|
* @param {string} source .env 文件原始文本。
|
|||
|
|
* @returns {Record<string, string>} 解析后的环境变量映射。
|
|||
|
|
*/
|
|||
|
|
function parseEnvText(source) {
|
|||
|
|
return source
|
|||
|
|
.split('\n')
|
|||
|
|
.map(line => line.trim())
|
|||
|
|
.filter(line => line && !line.startsWith('#'))
|
|||
|
|
.reduce((acc, line) => {
|
|||
|
|
const equalIndex = line.indexOf('=');
|
|||
|
|
if (equalIndex <= 0) {
|
|||
|
|
return acc;
|
|||
|
|
}
|
|||
|
|
const key = line.slice(0, equalIndex).trim();
|
|||
|
|
const value = line.slice(equalIndex + 1).trim();
|
|||
|
|
acc[key] = value;
|
|||
|
|
return acc;
|
|||
|
|
}, /** @type {Record<string, string>} */ ({}));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 读取基础环境和 mode 环境,合并生成最终环境变量。
|
|||
|
|
* @returns {Promise<Record<string, string>>} 当前模式生效的环境变量。
|
|||
|
|
*/
|
|||
|
|
async function loadBusinessRulesEnv() {
|
|||
|
|
const baseEnvPath = resolve(pkgRoot, '.env');
|
|||
|
|
const modeEnvPath = resolve(pkgRoot, `.env.${mode}`);
|
|||
|
|
const [baseText, modeText] = await Promise.all([
|
|||
|
|
readFile(baseEnvPath, 'utf8').catch(() => ''),
|
|||
|
|
readFile(modeEnvPath, 'utf8').catch(() => ''),
|
|||
|
|
]);
|
|||
|
|
return {
|
|||
|
|
...parseEnvText(baseText),
|
|||
|
|
...parseEnvText(modeText),
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 读取本地账号配置。
|
|||
|
|
* @returns {Promise<{ config: { defaultProfile: string, profiles: Record<string, { userCode: string, password: string }> }, configPath: string } | null>} 账号配置;文件不存在时返回 null。
|
|||
|
|
*/
|
|||
|
|
async function loadLocalAuthConfig() {
|
|||
|
|
let source = '';
|
|||
|
|
try {
|
|||
|
|
source = await readFile(localAuthConfigPath, 'utf8');
|
|||
|
|
} catch (error) {
|
|||
|
|
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let parsed;
|
|||
|
|
try {
|
|||
|
|
parsed = JSON.parse(source);
|
|||
|
|
} catch (error) {
|
|||
|
|
throw new Error(`local auth config parse failed: ${error instanceof Error ? error.message : String(error)}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const rawProfiles = parsed?.profiles;
|
|||
|
|
if (!rawProfiles || typeof rawProfiles !== 'object' || Array.isArray(rawProfiles)) {
|
|||
|
|
throw new Error(`local auth config invalid: "profiles" must be an object in ${localAuthConfigPath}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const profiles = Object.entries(rawProfiles).reduce((acc, [profileName, profile]) => {
|
|||
|
|
const userCode = String(profile?.userCode || '').trim();
|
|||
|
|
const password = String(profile?.password || '').trim();
|
|||
|
|
if (!userCode || !password) {
|
|||
|
|
throw new Error(`local auth config invalid: profile "${profileName}" must contain "userCode" and "password"`);
|
|||
|
|
}
|
|||
|
|
acc[profileName] = { userCode, password };
|
|||
|
|
return acc;
|
|||
|
|
}, /** @type {Record<string, { userCode: string, password: string }>} */ ({}));
|
|||
|
|
|
|||
|
|
if (!Object.keys(profiles).length) {
|
|||
|
|
throw new Error(`local auth config invalid: no profiles found in ${localAuthConfigPath}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
config: {
|
|||
|
|
defaultProfile: String(parsed?.defaultProfile || '').trim(),
|
|||
|
|
profiles,
|
|||
|
|
},
|
|||
|
|
configPath: localAuthConfigPath,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 解析启动时使用的 profile 名称。
|
|||
|
|
* @param {{ defaultProfile: string, profiles: Record<string, { userCode: string, password: string }> }} config 本地账号配置。
|
|||
|
|
* @returns {string} 当前应使用的 profile。
|
|||
|
|
*/
|
|||
|
|
function resolveInitialProfileName(config) {
|
|||
|
|
const preferredProfile = String(process.env.LOCAL_AUTH_PROFILE || '').trim();
|
|||
|
|
if (preferredProfile) {
|
|||
|
|
if (!config.profiles[preferredProfile]) {
|
|||
|
|
throw new Error(`LOCAL_AUTH_PROFILE "${preferredProfile}" not found in current local auth config`);
|
|||
|
|
}
|
|||
|
|
return preferredProfile;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (config.defaultProfile) {
|
|||
|
|
if (!config.profiles[config.defaultProfile]) {
|
|||
|
|
throw new Error(`defaultProfile "${config.defaultProfile}" not found in current local auth config`);
|
|||
|
|
}
|
|||
|
|
return config.defaultProfile;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Object.keys(config.profiles)[0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 启动登录流程并返回登录结果。
|
|||
|
|
* @param {string} apiBase 远端网关基础域名。
|
|||
|
|
* @param {{ userCode: string, password: string }} credential 登录凭据。
|
|||
|
|
* @returns {Promise<any>} 后端登录接口返回 JSON。
|
|||
|
|
*/
|
|||
|
|
async function login(apiBase, credential) {
|
|||
|
|
const url = new URL(loginPath, apiBase).toString();
|
|||
|
|
const response = await fetch(url, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'App-Id': 'app-cisdigital-base-foundation',
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify({
|
|||
|
|
userCode: credential.userCode,
|
|||
|
|
password: credential.password,
|
|||
|
|
}),
|
|||
|
|
});
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`login failed: ${response.status} ${response.statusText}`);
|
|||
|
|
}
|
|||
|
|
return response.json();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 从不同响应结构中提取 accessToken。
|
|||
|
|
* @param {any} payload 登录接口响应体。
|
|||
|
|
* @returns {string} 提取到的 accessToken;无值时返回空字符串。
|
|||
|
|
*/
|
|||
|
|
function extractAccessToken(payload) {
|
|||
|
|
return (
|
|||
|
|
payload?.accessToken
|
|||
|
|
?? payload?.data?.token
|
|||
|
|
?? payload?.data?.accessToken
|
|||
|
|
?? payload?.result?.accessToken
|
|||
|
|
?? ''
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const env = await loadBusinessRulesEnv();
|
|||
|
|
const apiBase = env.VITE_API_BASE || '';
|
|||
|
|
const authConfigResult = await loadLocalAuthConfig();
|
|||
|
|
const authConfig = authConfigResult?.config || null;
|
|||
|
|
const activeLocalAuthConfigPath = authConfigResult?.configPath || localAuthConfigPath;
|
|||
|
|
const enabled = Boolean(authConfig);
|
|||
|
|
const availableProfiles = authConfig ? Object.keys(authConfig.profiles) : [];
|
|||
|
|
|
|||
|
|
function toProjectRelativePath(filePath) {
|
|||
|
|
return relative(pkgRoot, filePath) || '.';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (enabled && !apiBase) {
|
|||
|
|
throw new Error(`VITE_API_BASE is empty for mode "${mode}"`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let accessToken = '';
|
|||
|
|
let loginError = '';
|
|||
|
|
let currentProfile = authConfig ? resolveInitialProfileName(authConfig) : '';
|
|||
|
|
let lastAttemptProfile = currentProfile;
|
|||
|
|
let listeningPort = proxyPort;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 使用指定 profile 重新登录并更新缓存 token。
|
|||
|
|
* @param {string} profileName profile 名称。
|
|||
|
|
* @returns {Promise<void>}
|
|||
|
|
*/
|
|||
|
|
async function refreshAccessToken(profileName) {
|
|||
|
|
if (!authConfig) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const credential = authConfig.profiles[profileName];
|
|||
|
|
if (!credential) {
|
|||
|
|
throw new Error(`profile "${profileName}" not found`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lastAttemptProfile = profileName;
|
|||
|
|
const nextLoginResponse = await login(apiBase, credential);
|
|||
|
|
const nextAccessToken = extractAccessToken(nextLoginResponse);
|
|||
|
|
if (!nextAccessToken) {
|
|||
|
|
throw new Error(`profile "${profileName}" login succeeded but accessToken is empty`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
accessToken = nextAccessToken;
|
|||
|
|
currentProfile = profileName;
|
|||
|
|
loginError = '';
|
|||
|
|
console.log(`[local-auth-proxy] login success, profile: ${profileName}, accessToken exists: true`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 获取当前代理状态。
|
|||
|
|
* @returns {{
|
|||
|
|
* enabled: boolean
|
|||
|
|
* serviceId: string
|
|||
|
|
* instanceId: string
|
|||
|
|
* mode: string
|
|||
|
|
* port: number
|
|||
|
|
* apiBase: string
|
|||
|
|
* accessToken: string
|
|||
|
|
* accessTokenReady: boolean
|
|||
|
|
* activeProfile: string
|
|||
|
|
* lastAttemptProfile: string
|
|||
|
|
* availableProfiles: string[]
|
|||
|
|
* configPath: string
|
|||
|
|
* exampleConfigPath: string
|
|||
|
|
* error?: string
|
|||
|
|
* }} 当前状态。
|
|||
|
|
*/
|
|||
|
|
function getProxyState() {
|
|||
|
|
return {
|
|||
|
|
enabled,
|
|||
|
|
serviceId: LOCAL_AUTH_PROXY_SERVICE_ID,
|
|||
|
|
instanceId,
|
|||
|
|
mode,
|
|||
|
|
port: listeningPort,
|
|||
|
|
apiBase,
|
|||
|
|
accessToken,
|
|||
|
|
accessTokenReady: Boolean(accessToken),
|
|||
|
|
activeProfile: currentProfile,
|
|||
|
|
lastAttemptProfile,
|
|||
|
|
availableProfiles,
|
|||
|
|
configPath: toProjectRelativePath(activeLocalAuthConfigPath),
|
|||
|
|
exampleConfigPath: toProjectRelativePath(localAuthExampleConfigPath),
|
|||
|
|
error: loginError || undefined,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 写入 JSON 响应。
|
|||
|
|
* @param {http.ServerResponse} res 响应对象。
|
|||
|
|
* @param {number} statusCode 状态码。
|
|||
|
|
* @param {unknown} payload 响应体。
|
|||
|
|
*/
|
|||
|
|
function writeJson(res, statusCode, payload) {
|
|||
|
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|||
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|||
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS');
|
|||
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|||
|
|
res.writeHead(statusCode);
|
|||
|
|
res.end(JSON.stringify(payload));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @description 在给定端口上启动服务;若端口被占用则自动向后探测可用端口。
|
|||
|
|
* @param {http.Server} targetServer http 服务实例。
|
|||
|
|
* @param {number} startPort 起始端口。
|
|||
|
|
* @returns {Promise<number>} 实际监听端口。
|
|||
|
|
*/
|
|||
|
|
async function listenOnAvailablePort(targetServer, startPort) {
|
|||
|
|
const maxAttempts = Math.max(1, LOCAL_AUTH_PROXY_PORT_SCAN_LIMIT);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @param {number} port 目标端口。
|
|||
|
|
* @returns {Promise<number>} 监听成功后的实际端口。
|
|||
|
|
*/
|
|||
|
|
function listenOnce(port) {
|
|||
|
|
return new Promise((resolvePromise, rejectPromise) => {
|
|||
|
|
const cleanup = () => {
|
|||
|
|
targetServer.off('error', handleError);
|
|||
|
|
targetServer.off('listening', handleListening);
|
|||
|
|
};
|
|||
|
|
function handleError(error) {
|
|||
|
|
cleanup();
|
|||
|
|
rejectPromise(error);
|
|||
|
|
}
|
|||
|
|
function handleListening() {
|
|||
|
|
cleanup();
|
|||
|
|
resolvePromise(port);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
targetServer.once('error', handleError);
|
|||
|
|
targetServer.once('listening', handleListening);
|
|||
|
|
targetServer.listen(port, '0.0.0.0');
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (let offset = 0; offset < maxAttempts; offset += 1) {
|
|||
|
|
const nextPort = startPort + offset;
|
|||
|
|
try {
|
|||
|
|
return await listenOnce(nextPort);
|
|||
|
|
} catch (error) {
|
|||
|
|
if (error && typeof error === 'object' && 'code' in error && error.code === 'EADDRINUSE') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
throw new Error(`no available local auth port found from ${startPort} to ${startPort + maxAttempts - 1}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
if (enabled) {
|
|||
|
|
await refreshAccessToken(currentProfile);
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
loginError = error instanceof Error ? error.message : String(error);
|
|||
|
|
console.error('[local-auth-proxy] login failed:', loginError);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const server = http.createServer((req, res) => {
|
|||
|
|
void (async () => {
|
|||
|
|
if (req.method === 'OPTIONS') {
|
|||
|
|
writeJson(res, 204, {});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const requestUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|||
|
|
|
|||
|
|
if (req.method === 'GET' && requestUrl.pathname === LOCAL_AUTH_ACCESS_TOKEN_PATH) {
|
|||
|
|
writeJson(res, loginError ? 500 : 200, getProxyState());
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (req.method === 'GET' && requestUrl.pathname === LOCAL_AUTH_REFRESH_TOKEN_PATH) {
|
|||
|
|
if (!enabled) {
|
|||
|
|
writeJson(res, 200, getProxyState());
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const profileName = requestUrl.searchParams.get('profile')?.trim() || currentProfile;
|
|||
|
|
try {
|
|||
|
|
await refreshAccessToken(profileName);
|
|||
|
|
writeJson(res, 200, getProxyState());
|
|||
|
|
} catch (error) {
|
|||
|
|
loginError = error instanceof Error ? error.message : String(error);
|
|||
|
|
console.error('[local-auth-proxy] refresh token failed:', loginError);
|
|||
|
|
writeJson(res, 500, getProxyState());
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (req.method === 'GET' && requestUrl.pathname === LOCAL_AUTH_STATE_PATH) {
|
|||
|
|
writeJson(res, 200, getProxyState());
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (req.method === 'GET' && requestUrl.pathname === LOCAL_AUTH_SWITCH_PROFILE_PATH) {
|
|||
|
|
if (!enabled) {
|
|||
|
|
writeJson(res, 200, getProxyState());
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const profileName = requestUrl.searchParams.get('profile')?.trim();
|
|||
|
|
if (!profileName) {
|
|||
|
|
writeJson(res, 400, {
|
|||
|
|
...getProxyState(),
|
|||
|
|
error: 'query param "profile" is required',
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await refreshAccessToken(profileName);
|
|||
|
|
writeJson(res, 200, getProxyState());
|
|||
|
|
} catch (error) {
|
|||
|
|
loginError = error instanceof Error ? error.message : String(error);
|
|||
|
|
console.error('[local-auth-proxy] switch profile failed:', loginError);
|
|||
|
|
writeJson(res, 500, getProxyState());
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
writeJson(res, 404, { message: 'Not Found' });
|
|||
|
|
})().catch((error) => {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|||
|
|
console.error('[local-auth-proxy] request failed:', errorMessage);
|
|||
|
|
writeJson(res, 500, { ...getProxyState(), error: errorMessage });
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
listeningPort = await listenOnAvailablePort(server, proxyPort);
|
|||
|
|
|
|||
|
|
console.log(`[local-auth-proxy] listening on http://localhost:${listeningPort}`);
|
|||
|
|
if (listeningPort !== proxyPort) {
|
|||
|
|
console.log(`[local-auth-proxy] default port ${proxyPort} is busy, switched to ${listeningPort}`);
|
|||
|
|
}
|
|||
|
|
if (instanceId) {
|
|||
|
|
console.log(`[local-auth-proxy] instance id: ${instanceId}`);
|
|||
|
|
}
|
|||
|
|
if (!enabled) {
|
|||
|
|
console.log(`[local-auth-proxy] local auth disabled: config not found at ${toProjectRelativePath(localAuthConfigPath)}`);
|
|||
|
|
console.log(`[local-auth-proxy] copy template from ${toProjectRelativePath(localAuthExampleConfigPath)} when needed`);
|
|||
|
|
} else {
|
|||
|
|
console.log(`[local-auth-proxy] config path: ${toProjectRelativePath(activeLocalAuthConfigPath)}`);
|
|||
|
|
console.log(`[local-auth-proxy] active profile: ${currentProfile}`);
|
|||
|
|
console.log(`[local-auth-proxy] available profiles: ${availableProfiles.join(', ')}`);
|
|||
|
|
console.log(`[local-auth-proxy] accessToken exists: ${Boolean(accessToken)}`);
|
|||
|
|
}
|