UNPKG

@binlebin/c2pa-ssl

Version:

Node.js wrapper for C2PA content authentication using Rust CLI backend

133 lines (111 loc) 4.04 kB
const { spawn } = require('child_process'); const fs = require('fs-extra'); const path = require('path'); class C2PAService { constructor() { // No longer need to manage ports or HTTP servers this.binaryPath = this.getBinaryPath(); } getBinaryPath() { // Platform detection for available binaries const platform = process.platform; if (platform === 'darwin') { return path.join(__dirname, 'bin', 'c2pa-cli'); } else if (platform === 'linux') { return path.join(__dirname, 'bin', 'c2pa-cli-linux'); } else { throw new Error( `Platform '${platform}' is not currently supported. ` + `This package currently supports macOS (darwin) and Linux. ` + `For Windows support, we need to set up cross-compilation.` ); } } async start() { // For CLI approach, we need the private key and use bundled certificate const privateKey = process.env.C2PA_PRIVATE_KEY; if (!privateKey) { throw new Error('C2PA_PRIVATE_KEY environment variable is required'); } // Create test_certs directory const certsDir = path.resolve(__dirname, 'test_certs'); await fs.ensureDir(certsDir); // Write private key to where CLI binary expects it const keyPath = path.join(certsDir, 'anh_ec.key'); // Check if private key file already exists if (!await fs.pathExists(keyPath)) { await fs.writeFile(keyPath, privateKey); console.log('✅ Private key written for CLI usage'); } else { console.log('✅ Private key already exists, skipping write'); } // Check if bundled certificate exists const certPath = path.join(certsDir, 'anh_ec.pem'); if (!await fs.pathExists(certPath)) { throw new Error(`Certificate file not found in package. Please ensure anh_ec.pem is included.`); } console.log('✅ Certificate file found'); // Check if binary exists if (!await fs.pathExists(this.binaryPath)) { throw new Error(`CLI binary not found at ${this.binaryPath}`); } console.log('✅ C2PA CLI ready'); } async stop() { // Clean up only the private key file (certificate stays as it's bundled) const certsDir = path.resolve(__dirname, 'test_certs'); const keyPath = path.join(certsDir, 'anh_ec.key'); try { await fs.remove(keyPath); console.log('🧹 Cleaned up private key file'); } catch (error) { // Ignore cleanup errors } } async signFile(inputPath, outputPath, metadata = {}) { return new Promise((resolve, reject) => { // Prepare CLI arguments const args = [ inputPath, outputPath, '--title', metadata.title || 'Signed Content', '--description', metadata.description || 'Content signed with C2PA', '--author', metadata.author || 'visflow.ai' ]; console.log(`🚀 Signing file: ${inputPath} -> ${outputPath}`); // Execute CLI const process = spawn(this.binaryPath, args, { cwd: __dirname, stdio: ['ignore', 'pipe', 'pipe'] }); let stdout = ''; let stderr = ''; process.stdout.on('data', (data) => { stdout += data.toString(); }); process.stderr.on('data', (data) => { stderr += data.toString(); }); process.on('close', (code) => { if (code === 0) { console.log('✅ File signed successfully'); resolve({ success: true, message: 'File signed successfully', stdout: stdout.trim(), stderr: stderr.trim() }); } else { console.error('❌ CLI signing failed:', stderr); reject(new Error(`CLI signing failed with code ${code}: ${stderr}`)); } }); process.on('error', (error) => { console.error('❌ Failed to execute CLI:', error); reject(new Error(`Failed to execute CLI: ${error.message}`)); }); }); } } module.exports = C2PAService;