/** * Piclet Verification Helper for Frontend * Use this in your Svelte game to generate verified Piclets */ // This should match the server's secret key const PICLET_SECRET_KEY = "piclets-dev-key-change-in-production"; /** * Create HMAC-SHA256 signature (requires crypto-js or similar) * For browser compatibility, you'll need to include crypto-js: * npm install crypto-js * or include via CDN: https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js */ async function createHMAC(message, key) { // Browser-native crypto API version (modern browsers) if (window.crypto && window.crypto.subtle) { const encoder = new TextEncoder(); const keyData = encoder.encode(key); const messageData = encoder.encode(message); const cryptoKey = await window.crypto.subtle.importKey( 'raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); const signature = await window.crypto.subtle.sign('HMAC', cryptoKey, messageData); const hashArray = Array.from(new Uint8Array(signature)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } // Fallback: Use crypto-js if available if (window.CryptoJS) { return CryptoJS.HmacSHA256(message, key).toString(); } throw new Error("No crypto implementation available. Include crypto-js or use modern browser."); } /** * Create verification data for a Piclet * Call this when generating a Piclet in your game */ async function createPicletVerification(picletData, imageCaption = "", conceptString = "") { const timestamp = Math.floor(Date.now() / 1000); // Core data used for signature const coreData = { name: picletData.name || "", primaryType: picletData.primaryType || "", baseStats: picletData.baseStats || {}, movepool: picletData.movepool || [], timestamp: timestamp }; // Generation metadata const generationData = { image_caption: imageCaption, concept_string: conceptString, generation_method: "official_app" }; // Add generation data to core data coreData.generation_data = generationData; // Create deterministic JSON string const dataString = JSON.stringify(coreData, null, 0); // Create signature const signature = await createHMAC(dataString, PICLET_SECRET_KEY); return { signature: signature, timestamp: timestamp, generation_data: generationData, verification_version: "1.0" }; } /** * Add verification to a Piclet before saving * Use this in your PicletGenerator component */ async function verifyAndPreparePiclet(picletData, imageCaption = "", conceptString = "") { try { // Create verification data const verification = await createPicletVerification(picletData, imageCaption, conceptString); // Add verification to Piclet data const verifiedPiclet = { ...picletData, verification: verification }; return { success: true, piclet: verifiedPiclet, signature: verification.signature }; } catch (error) { return { success: false, error: error.message }; } } /** * Save a verified Piclet to the server * Use this instead of direct API calls */ async function saveVerifiedPiclet(gradioClient, picletData, imageFile = null, imageCaption = "", conceptString = "") { try { // Create verified Piclet const verificationResult = await verifyAndPreparePiclet(picletData, imageCaption, conceptString); if (!verificationResult.success) { return { success: false, error: `Verification failed: ${verificationResult.error}` }; } // Save to server const result = await gradioClient.predict("/save_piclet_api", [ JSON.stringify(verificationResult.piclet), verificationResult.signature, imageFile ]); return JSON.parse(result.data[0]); } catch (error) { return { success: false, error: `Save failed: ${error.message}` }; } } /** * Example usage in your Svelte component: * * // In PicletGenerator.svelte or similar component * import { saveVerifiedPiclet } from './verification_helper.js'; * * async function savePiclet() { * const picletData = { * name: generatedName, * primaryType: detectedType, * baseStats: calculatedStats, * movepool: generatedMoves, * // ... other data * }; * * const result = await saveVerifiedPiclet( * gradioClient, * picletData, * uploadedImageFile, * imageCaption, * conceptString * ); * * if (result.success) { * console.log(`Piclet saved with ID: ${result.piclet_id}`); * } else { * console.error(`Save failed: ${result.error}`); * } * } */ // Export for ES modules if (typeof module !== 'undefined' && module.exports) { module.exports = { createPicletVerification, verifyAndPreparePiclet, saveVerifiedPiclet }; } // Global functions for script tag usage if (typeof window !== 'undefined') { window.PicletVerification = { createPicletVerification, verifyAndPreparePiclet, saveVerifiedPiclet }; }