mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-07-03 18:45:07 +08:00
refactor: extract upsertEnvVar and add generic afterCreate CRUD hook
Structural prep for the instance-wide default provider, no behavior change: - Extract the .env upsert from set-env's run() into a reusable upsertEnvVar() so setup code can persist a key without reinventing the grep/sed pipeline. - Add an optional afterCreate hook to the CRUD ResourceDef + genericCreate so a resource can seed a dependent row the single-table INSERT can't cover. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+30
-25
@@ -18,6 +18,33 @@ import path from 'path';
|
||||
import { log } from '../src/log.js';
|
||||
import { emitStatus } from './status.js';
|
||||
|
||||
/**
|
||||
* Upsert a `KEY=VALUE` line into the project's `.env`, returning whether the
|
||||
* key already existed. The single writer for `.env` edits so flows don't invent
|
||||
* grep/sed pipelines (which can't be allowlisted tightly).
|
||||
*/
|
||||
export function upsertEnvVar(key: string, value: string): { existed: boolean } {
|
||||
if (!/^[A-Z][A-Z0-9_]*$/.test(key)) {
|
||||
throw new Error(`Invalid env key: ${key} (must be UPPER_SNAKE_CASE)`);
|
||||
}
|
||||
const envFile = path.join(process.cwd(), '.env');
|
||||
let content = '';
|
||||
if (fs.existsSync(envFile)) {
|
||||
content = fs.readFileSync(envFile, 'utf-8');
|
||||
}
|
||||
const lineRegex = new RegExp(`^${key}=.*$`, 'm');
|
||||
const existed = lineRegex.test(content);
|
||||
const newLine = `${key}=${value}`;
|
||||
if (existed) {
|
||||
content = content.replace(lineRegex, newLine);
|
||||
} else {
|
||||
const sep = content && !content.endsWith('\n') ? '\n' : '';
|
||||
content = content + sep + newLine + '\n';
|
||||
}
|
||||
fs.writeFileSync(envFile, content);
|
||||
return { existed };
|
||||
}
|
||||
|
||||
export async function run(args: string[]): Promise<void> {
|
||||
const keyIdx = args.indexOf('--key');
|
||||
const valueIdx = args.indexOf('--value');
|
||||
@@ -33,37 +60,15 @@ export async function run(args: string[]): Promise<void> {
|
||||
const key = args[keyIdx + 1];
|
||||
const value = args[valueIdx + 1];
|
||||
|
||||
if (!/^[A-Z][A-Z0-9_]*$/.test(key)) {
|
||||
throw new Error(`Invalid env key: ${key} (must be UPPER_SNAKE_CASE)`);
|
||||
}
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
const envFile = path.join(projectRoot, '.env');
|
||||
|
||||
let content = '';
|
||||
if (fs.existsSync(envFile)) {
|
||||
content = fs.readFileSync(envFile, 'utf-8');
|
||||
}
|
||||
|
||||
const lineRegex = new RegExp(`^${key}=.*$`, 'm');
|
||||
const newLine = `${key}=${value}`;
|
||||
const existed = lineRegex.test(content);
|
||||
|
||||
if (existed) {
|
||||
content = content.replace(lineRegex, newLine);
|
||||
} else {
|
||||
const sep = content && !content.endsWith('\n') ? '\n' : '';
|
||||
content = content + sep + newLine + '\n';
|
||||
}
|
||||
|
||||
fs.writeFileSync(envFile, content);
|
||||
const { existed } = upsertEnvVar(key, value);
|
||||
log.info('Updated .env', { key, existed });
|
||||
|
||||
let synced = false;
|
||||
if (syncContainer) {
|
||||
const projectRoot = process.cwd();
|
||||
const dataEnvDir = path.join(projectRoot, 'data', 'env');
|
||||
fs.mkdirSync(dataEnvDir, { recursive: true });
|
||||
fs.copyFileSync(envFile, path.join(dataEnvDir, 'env'));
|
||||
fs.copyFileSync(path.join(projectRoot, '.env'), path.join(dataEnvDir, 'env'));
|
||||
synced = true;
|
||||
log.info('Synced .env to container mount', { path: 'data/env/env' });
|
||||
}
|
||||
|
||||
@@ -71,6 +71,12 @@ export interface ResourceDef {
|
||||
};
|
||||
/** Non-standard verbs (grant, revoke, add, remove, restart, etc.). */
|
||||
customOperations?: Record<string, CustomOperation>;
|
||||
/**
|
||||
* Hook run after a successful generic create, with the inserted row's values.
|
||||
* For dependent rows the single-table INSERT can't seed (e.g. a group's
|
||||
* container_configs row).
|
||||
*/
|
||||
afterCreate?: (created: Record<string, unknown>) => void | Promise<void>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -162,6 +168,7 @@ function genericCreate(def: ResourceDef) {
|
||||
getDb()
|
||||
.prepare(`INSERT INTO ${def.table} (${colNames.join(', ')}) VALUES (${placeholders.join(', ')})`)
|
||||
.run(values);
|
||||
if (def.afterCreate) await def.afterCreate(values);
|
||||
return values;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user