- Add intelligent content-game compatibility system with visual badges - Fix Adventure Reader to work with Dragon's Pearl content structure - Implement multi-column games grid for faster navigation - Add pronunciation display for Chinese vocabulary and sentences - Fix navigation breadcrumb to show proper hierarchy (Home > Levels > Content) - Add back buttons to all navigation pages - Improve JSONContentLoader to preserve story structure - Add comprehensive debugging and diagnostic tools 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
147 lines
4.8 KiB
JavaScript
147 lines
4.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
// Test de signature AWS pour DigitalOcean Spaces
|
|
const crypto = require('crypto');
|
|
|
|
// Configuration avec ta clé read-only
|
|
const config = {
|
|
DO_ACCESS_KEY: 'DO801XTYPE968NZGAQM3',
|
|
DO_SECRET_KEY: 'rfKPjampdpUCYhn02XrKg6IWKmqebjg9HQTGxNLzJQY',
|
|
DO_REGION: 'fra1',
|
|
DO_ENDPOINT: 'https://autocollant.fra1.digitaloceanspaces.com',
|
|
DO_CONTENT_PATH: 'Class_generator/ContentMe'
|
|
};
|
|
|
|
// Fonction de hash SHA256
|
|
function sha256(message) {
|
|
return crypto.createHash('sha256').update(message, 'utf8').digest('hex');
|
|
}
|
|
|
|
// Fonction HMAC SHA256
|
|
function hmacSha256(key, message) {
|
|
return crypto.createHmac('sha256', key).update(message, 'utf8').digest();
|
|
}
|
|
|
|
// Génération de la signature AWS V4
|
|
async function generateAWSSignature(method, url) {
|
|
const accessKey = config.DO_ACCESS_KEY;
|
|
const secretKey = config.DO_SECRET_KEY;
|
|
const region = config.DO_REGION;
|
|
const service = 's3';
|
|
|
|
const now = new Date();
|
|
const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, '');
|
|
const timeStamp = now.toISOString().slice(0, 19).replace(/[-:]/g, '') + 'Z';
|
|
|
|
console.log(`🕐 Timestamp: ${timeStamp}`);
|
|
console.log(`📅 Date: ${dateStamp}`);
|
|
|
|
// Parse URL
|
|
const urlObj = new URL(url);
|
|
const host = urlObj.hostname;
|
|
const canonicalUri = urlObj.pathname || '/';
|
|
const canonicalQueryString = urlObj.search ? urlObj.search.slice(1) : '';
|
|
|
|
console.log(`🌐 Host: ${host}`);
|
|
console.log(`📍 URI: ${canonicalUri}`);
|
|
console.log(`❓ Query: ${canonicalQueryString}`);
|
|
|
|
// Canonical headers
|
|
const canonicalHeaders = `host:${host}\nx-amz-date:${timeStamp}\n`;
|
|
const signedHeaders = 'host;x-amz-date';
|
|
|
|
console.log(`📝 Canonical headers:\n${canonicalHeaders}`);
|
|
|
|
// Create canonical request
|
|
const payloadHash = method === 'GET' ? sha256('') : 'UNSIGNED-PAYLOAD';
|
|
const canonicalRequest = [
|
|
method,
|
|
canonicalUri,
|
|
canonicalQueryString,
|
|
canonicalHeaders,
|
|
signedHeaders,
|
|
payloadHash
|
|
].join('\n');
|
|
|
|
console.log(`📋 Canonical request:\n${canonicalRequest}`);
|
|
console.log(`🔢 Payload hash: ${payloadHash}`);
|
|
|
|
// Create string to sign
|
|
const algorithm = 'AWS4-HMAC-SHA256';
|
|
const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
|
|
const canonicalRequestHash = sha256(canonicalRequest);
|
|
const stringToSign = [
|
|
algorithm,
|
|
timeStamp,
|
|
credentialScope,
|
|
canonicalRequestHash
|
|
].join('\n');
|
|
|
|
console.log(`🔐 String to sign:\n${stringToSign}`);
|
|
console.log(`🔗 Canonical request hash: ${canonicalRequestHash}`);
|
|
|
|
// Calculate signature
|
|
const kDate = hmacSha256('AWS4' + secretKey, dateStamp);
|
|
const kRegion = hmacSha256(kDate, region);
|
|
const kService = hmacSha256(kRegion, service);
|
|
const kSigning = hmacSha256(kService, 'aws4_request');
|
|
const signature = hmacSha256(kSigning, stringToSign).toString('hex');
|
|
|
|
console.log(`✍️ Signature: ${signature}`);
|
|
|
|
// Create authorization header
|
|
const authorization = `${algorithm} Credential=${accessKey}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
|
|
console.log(`🔑 Authorization: ${authorization}`);
|
|
|
|
return {
|
|
'Authorization': authorization,
|
|
'X-Amz-Date': timeStamp,
|
|
'X-Amz-Content-Sha256': payloadHash
|
|
};
|
|
}
|
|
|
|
// Test avec fetch
|
|
async function testDigitalOceanAccess() {
|
|
console.log('🚀 Test d\'accès DigitalOcean Spaces avec signature AWS\n');
|
|
|
|
const testUrl = `${config.DO_ENDPOINT}/${config.DO_CONTENT_PATH}/greetings-basic.json`;
|
|
console.log(`🎯 URL de test: ${testUrl}\n`);
|
|
|
|
try {
|
|
const headers = await generateAWSSignature('GET', testUrl);
|
|
|
|
console.log('\n📦 Headers finaux:');
|
|
console.log(JSON.stringify(headers, null, 2));
|
|
|
|
// Test avec node-fetch si disponible
|
|
try {
|
|
const fetch = require('node-fetch');
|
|
console.log('\n🌐 Test avec node-fetch...');
|
|
|
|
const response = await fetch(testUrl, {
|
|
method: 'GET',
|
|
headers: headers
|
|
});
|
|
|
|
console.log(`📡 Status: ${response.status} ${response.statusText}`);
|
|
|
|
if (response.ok) {
|
|
const content = await response.text();
|
|
console.log(`✅ Succès ! Contenu (${content.length} chars): ${content.substring(0, 200)}...`);
|
|
} else {
|
|
const errorText = await response.text();
|
|
console.log(`❌ Erreur: ${errorText}`);
|
|
}
|
|
} catch (fetchError) {
|
|
console.log('⚠️ node-fetch non disponible, test signature seulement');
|
|
console.log('✅ Signature générée avec succès !');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`❌ Erreur: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
// Lancer le test
|
|
testDigitalOceanAccess(); |