feat(mindspace): enforce PostgreSQL user data delivery
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
export const MEMIND_RUNTIME_PROFILES = new Set(['local', 'split-service', 'production']);
|
||||
|
||||
/**
|
||||
* Load Memind env files in standard order. Later files only fill unset keys.
|
||||
*/
|
||||
export function loadMemindEnvFiles(rootDir, env = process.env) {
|
||||
const root = path.resolve(rootDir);
|
||||
for (const relativePath of ['../../.env.local', '.env']) {
|
||||
loadEnvFile(path.join(root, relativePath), env);
|
||||
}
|
||||
}
|
||||
|
||||
function loadEnvFile(filePath, env = process.env) {
|
||||
if (!fs.existsSync(filePath)) return;
|
||||
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith('#')) continue;
|
||||
const separator = trimmed.indexOf('=');
|
||||
if (separator < 0) continue;
|
||||
const key = trimmed.slice(0, separator).trim();
|
||||
const value = trimmed.slice(separator + 1).trim();
|
||||
if (!env[key]) env[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function expandEnvReference(value, env = process.env) {
|
||||
const raw = String(value ?? '').trim();
|
||||
const match = raw.match(/^\$\{([A-Z0-9_]+)\}$/);
|
||||
if (!match) return raw;
|
||||
return String(env[match[1]] ?? '').trim() || raw;
|
||||
}
|
||||
|
||||
export function resolveMemindRuntimeProfile(env = process.env) {
|
||||
const explicit = String(env.MEMIND_RUNTIME_PROFILE ?? '').trim().toLowerCase();
|
||||
if (explicit) {
|
||||
if (!MEMIND_RUNTIME_PROFILES.has(explicit)) {
|
||||
throw new Error(
|
||||
`Unsupported MEMIND_RUNTIME_PROFILE "${explicit}". Expected: ${[...MEMIND_RUNTIME_PROFILES].join(', ')}`,
|
||||
);
|
||||
}
|
||||
return explicit;
|
||||
}
|
||||
if (String(env.NODE_ENV ?? '').trim().toLowerCase() === 'production') {
|
||||
return 'production';
|
||||
}
|
||||
return 'local';
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply environment-specific defaults after .env files are loaded.
|
||||
*
|
||||
* - local: monolith MindSpace adapter, repo-local storage (pnpm dev default)
|
||||
* - split-service: remote adapter against standalone MindSpace on 8082
|
||||
* - production: no overrides; 103 .env is source of truth
|
||||
*/
|
||||
export function applyMemindRuntimeProfile({
|
||||
rootDir = process.cwd(),
|
||||
env = process.env,
|
||||
profile,
|
||||
} = {}) {
|
||||
const resolvedProfile = profile ?? resolveMemindRuntimeProfile(env);
|
||||
env.MEMIND_RUNTIME_PROFILE = resolvedProfile;
|
||||
|
||||
if (resolvedProfile === 'local') {
|
||||
env.MINDSPACE_SERVER_ADAPTER = 'local';
|
||||
if (!env.MINDSPACE_STORAGE_ROOT) {
|
||||
env.MINDSPACE_STORAGE_ROOT = path.join(path.resolve(rootDir), 'data', 'mindspace');
|
||||
}
|
||||
return {
|
||||
profile: resolvedProfile,
|
||||
mindspaceAdapter: 'local',
|
||||
storageRoot: env.MINDSPACE_STORAGE_ROOT,
|
||||
};
|
||||
}
|
||||
|
||||
if (resolvedProfile === 'split-service') {
|
||||
env.MINDSPACE_SERVER_ADAPTER = 'remote';
|
||||
env.MINDSPACE_REMOTE_BASE_URL = String(
|
||||
env.MINDSPACE_REMOTE_BASE_URL ?? 'http://127.0.0.1:8082',
|
||||
).trim();
|
||||
const expandedToken = expandEnvReference(env.MINDSPACE_REMOTE_AUTH_TOKEN, env);
|
||||
if (expandedToken) {
|
||||
env.MINDSPACE_REMOTE_AUTH_TOKEN = expandedToken;
|
||||
} else if (env.TKMIND_SERVER__SECRET_KEY) {
|
||||
env.MINDSPACE_REMOTE_AUTH_TOKEN = String(env.TKMIND_SERVER__SECRET_KEY).trim();
|
||||
}
|
||||
if (!env.MINDSPACE_STORAGE_ROOT) {
|
||||
env.MINDSPACE_STORAGE_ROOT = '/Users/john/MindSpace/data/mindspace';
|
||||
}
|
||||
return {
|
||||
profile: resolvedProfile,
|
||||
mindspaceAdapter: 'remote',
|
||||
remoteBaseUrl: env.MINDSPACE_REMOTE_BASE_URL,
|
||||
storageRoot: env.MINDSPACE_STORAGE_ROOT,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
profile: resolvedProfile,
|
||||
mindspaceAdapter: String(env.MINDSPACE_SERVER_ADAPTER ?? 'local').trim().toLowerCase() || 'local',
|
||||
storageRoot: env.MINDSPACE_STORAGE_ROOT ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export function describeMemindRuntimeProfile(env = process.env) {
|
||||
const profile = resolveMemindRuntimeProfile(env);
|
||||
const adapter = String(env.MINDSPACE_SERVER_ADAPTER ?? 'local').trim().toLowerCase() || 'local';
|
||||
const parts = [`MEMIND_RUNTIME_PROFILE=${profile}`, `MINDSPACE_SERVER_ADAPTER=${adapter}`];
|
||||
if (adapter === 'remote') {
|
||||
parts.push(`MINDSPACE_REMOTE_BASE_URL=${env.MINDSPACE_REMOTE_BASE_URL ?? '(unset)'}`);
|
||||
}
|
||||
if (env.MINDSPACE_STORAGE_ROOT) {
|
||||
parts.push(`MINDSPACE_STORAGE_ROOT=${env.MINDSPACE_STORAGE_ROOT}`);
|
||||
}
|
||||
return parts.join(', ');
|
||||
}
|
||||
Reference in New Issue
Block a user