fix: audit only newly created durable search definitions
This commit is contained in:
@@ -37,19 +37,18 @@ const ENVELOPE_MAPPINGS: Record<string, unknown> = {
|
||||
export class SearchService {
|
||||
private readonly definitions = new Map<string, SearchIndexDefinition>();
|
||||
private readonly lineage?: LineageEmitter;
|
||||
private readonly aud0?: Aud0Client;
|
||||
private readonly definitionRepository?: SearchDefinitionRepository;
|
||||
|
||||
constructor(
|
||||
private readonly adapter: OpenSearchAdapter,
|
||||
opts?: { aud0Client?: Aud0Client; definitionRepository?: SearchDefinitionRepository },
|
||||
) {
|
||||
this.aud0 = opts?.aud0Client;
|
||||
this.lineage = opts?.aud0Client ? new LineageEmitter(opts.aud0Client) : undefined;
|
||||
this.definitionRepository = opts?.definitionRepository;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Index definitions
|
||||
// ---------------------------------------------------------------------
|
||||
private validateDefinition(def: SearchIndexDefinition): void {
|
||||
if (!def.index_name?.trim() || !/^[a-z0-9][a-z0-9_-]{0,62}$/i.test(def.index_name)) {
|
||||
throw new SiaxError("INVALID_REQUEST", "0pt.search", "index_name must be 1-63 letters, digits, _ or -");
|
||||
@@ -92,12 +91,38 @@ export class SearchService {
|
||||
return copy;
|
||||
}
|
||||
|
||||
/** Production registration: versioned durable write followed by cache refresh. */
|
||||
/** Production registration: durable versioned write, one stable audit event, then cache refresh. */
|
||||
async registerIndexDefinitionDurable(def: SearchIndexDefinition, actorId?: string): Promise<SearchIndexDefinition> {
|
||||
this.validateDefinition(def);
|
||||
if (!this.definitionRepository) return this.registerIndexDefinition(def);
|
||||
const stored = await this.definitionRepository.save(def, actorId);
|
||||
return this.registerIndexDefinition(stored);
|
||||
const registered = this.registerIndexDefinition(stored.definition);
|
||||
if (stored.created && this.aud0) {
|
||||
const id = `search-definition:${registered.index_name}:v${registered.version}`;
|
||||
await this.aud0.ingestEvent({
|
||||
event_id: `evt_0pt_${id}`,
|
||||
event_type: "0pt.search_definition_registered",
|
||||
tenant_id: "t_platform",
|
||||
project_id: "p_platform",
|
||||
actor_id: stored.created_by ?? actorId ?? "system",
|
||||
app_id: "0pt",
|
||||
capability_id: "0pt.search.definition.register.v1",
|
||||
resource_type: "search_index_definition",
|
||||
resource_id: `${registered.index_name}@${registered.version}`,
|
||||
risk_level: "L2",
|
||||
result: "success",
|
||||
payload: {
|
||||
index_name: registered.index_name,
|
||||
version: registered.version,
|
||||
fields: Object.keys(registered.fields).sort(),
|
||||
default_search_fields: registered.default_search_fields,
|
||||
},
|
||||
created_at: stored.created_at,
|
||||
occurred_at: stored.created_at,
|
||||
idempotency_key: id,
|
||||
});
|
||||
}
|
||||
return registered;
|
||||
}
|
||||
|
||||
getIndexDefinition(indexName: string): SearchIndexDefinition {
|
||||
@@ -269,8 +294,6 @@ export class SearchService {
|
||||
await flush();
|
||||
await this.adapter.refresh(physical);
|
||||
} catch (error) {
|
||||
// Best-effort cleanup of an incomplete generation. Preserve the original
|
||||
// failure even if cleanup itself fails.
|
||||
await this.adapter.deleteIndex(physical).catch(() => undefined);
|
||||
throw error;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user