This commit is contained in:
Pierre
2026-01-10 19:15:06 +01:00
parent 5b68fb72ca
commit 6d19702d26
2 changed files with 42 additions and 7 deletions
+20
View File
@@ -1,6 +1,26 @@
/** /**
* MetaMesh Plugin: full-hash * MetaMesh Plugin: full-hash
*
* ============================================================================
* PLUGIN MOUNT ARCHITECTURE - DO NOT MODIFY WITHOUT AUTHORIZATION
* ============================================================================
*
* Each plugin container has exactly 3 mounts (2 for plugins without output):
*
* 1. /files (READ-ONLY) - Shared media files, read access only
* 2. /cache (READ-WRITE) - Plugin-specific cache folder
* 3. /files/plugin/<id> (READ-WRITE) - Plugin output folder (if needed)
*
* This plugin (full-hash) only requires mounts 1 and 2:
* - /files (RO) - to read entire files for SHA-256 hashing
* - /cache (RW) - to cache computed hashes
*
* SECURITY: Plugins must NEVER write to /files directly.
* All write operations go to /cache or /files/plugin/<id> only.
*
* ============================================================================
*/ */
import Fastify from 'fastify'; import Fastify from 'fastify';
import type { HealthResponse, ProcessRequest, ProcessResponse, CallbackPayload, ConfigureRequest, ConfigureResponse } from './types.js'; import type { HealthResponse, ProcessRequest, ProcessResponse, CallbackPayload, ConfigureRequest, ConfigureResponse } from './types.js';
import { manifest, process as processFile } from './plugin.js'; import { manifest, process as processFile } from './plugin.js';
+22 -7
View File
@@ -1,6 +1,12 @@
/** /**
* Full Hash Plugin * Full Hash Plugin
* Computes SHA-256 hash of the entire file *
* Computes the SHA-256 hash of the entire file.
* This is a background queue plugin - it's slow but provides
* definitive content identification.
*
* Output:
* - cid_sha2-256: SHA-256 hash formatted as CID
*/ */
import { createReadStream } from 'fs'; import { createReadStream } from 'fs';
@@ -30,6 +36,9 @@ export const manifest: PluginManifest = {
config: {}, config: {},
}; };
/**
* Compute SHA-256 hash of a file using streaming
*/
async function computeSHA256(filePath: string): Promise<string> { async function computeSHA256(filePath: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const hash = createHash('sha256'); const hash = createHash('sha256');
@@ -52,6 +61,7 @@ export async function process(
// Skip if already computed // Skip if already computed
if (existingMeta?.['cid_sha2-256']) { if (existingMeta?.['cid_sha2-256']) {
console.log(`[full-hash] SHA-256 already computed for ${filePath}, skipping`);
await sendCallback({ await sendCallback({
taskId: request.taskId, taskId: request.taskId,
status: 'skipped', status: 'skipped',
@@ -61,16 +71,18 @@ export async function process(
return; return;
} }
console.log(`[full-hash] Computing SHA-256 for ${filePath}`); console.log(`[full-hash] Computing SHA-256 hash for ${filePath}`);
const sha256 = await computeSHA256(filePath);
// Format as CID-like string // Compute SHA-256 hash
const sha256Cid = `sha256-${sha256}`; const sha256Hex = await computeSHA256(filePath);
// Format as CID-like string (matching old format)
const sha256Cid = `sha256-${sha256Hex}`;
await metaCore.setProperty(cid, 'cid_sha2-256', sha256Cid); await metaCore.setProperty(cid, 'cid_sha2-256', sha256Cid);
console.log(`[full-hash] SHA-256 computed: ${sha256Cid.substring(0, 20)}...`);
const duration = Date.now() - startTime; const duration = Date.now() - startTime;
console.log(`[full-hash] Computed in ${duration}ms: ${sha256Cid.substring(0, 20)}...`);
await sendCallback({ await sendCallback({
taskId: request.taskId, taskId: request.taskId,
@@ -79,11 +91,14 @@ export async function process(
}); });
} catch (error) { } catch (error) {
const duration = Date.now() - startTime; const duration = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`[full-hash] Error computing SHA-256 hash for ${request.filePath}:`, errorMessage);
await sendCallback({ await sendCallback({
taskId: request.taskId, taskId: request.taskId,
status: 'failed', status: 'failed',
duration, duration,
error: error instanceof Error ? error.message : String(error), error: errorMessage,
}); });
} }
} }