Loading start.sh +320 −56 Original line number Diff line number Diff line Loading @@ -679,8 +679,10 @@ chmod 644 /app/data/web/runtime-config.js # Create a custom URL patch file to fix the URL constructor error echo "==> Creating URL and SRP patch file" cat << EOF > /app/data/web/ente-patches.js cat > /app/data/web/photos/static/ente-patches.js << 'ENDPATCHES' (function() { console.log('Applying Ente URL and SRP patches...'); // Save original URL constructor const originalURL = window.URL; Loading Loading @@ -710,102 +712,364 @@ cat << EOF > /app/data/web/ente-patches.js } }; // More robust Buffer implementation for SRP window.Buffer = window.Buffer || { // Comprehensive Buffer polyfill for SRP const originalBuffer = window.Buffer; window.Buffer = { from: function(data, encoding) { // Handle undefined data - critical fix if (data === undefined) { console.warn('Buffer.from called with undefined data, creating empty buffer'); return { data: '', // Debug logging for the SRP calls console.debug('Buffer.from called with:', typeof data, data === undefined ? 'undefined' : data === null ? 'null' : Array.isArray(data) ? 'array[' + data.length + ']' : 'value', 'encoding:', encoding); // Handle undefined/null data - critical fix if (data === undefined || data === null) { console.warn('Buffer.from called with ' + (data === undefined ? 'undefined' : 'null') + ' data, creating empty buffer'); const result = { data: new Uint8Array(0), length: 0, toString: function() { return ''; } toString: function(enc) { return ''; } }; // Add additional methods that SRP might use result.slice = function() { return Buffer.from([]); }; result.readUInt32BE = function() { return 0; }; result.writeUInt32BE = function() { return result; }; return result; } // Special case for hex strings - very important for SRP if (typeof data === 'string' && encoding === 'hex') { // Convert hex string to byte array const bytes = []; for (let i = 0; i < data.length; i += 2) { if (data.length - i >= 2) { bytes.push(parseInt(data.substr(i, 2), 16)); } } const result = { data: new Uint8Array(bytes), length: bytes.length, toString: function(enc) { if (enc === 'hex' || !enc) { return data; // Return original hex string } return bytes.map(b => String.fromCharCode(b)).join(''); } }; // Add methods needed by SRP result.slice = function(start, end) { const slicedData = bytes.slice(start, end); return Buffer.from(slicedData.map(b => b.toString(16).padStart(2, '0')).join(''), 'hex'); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } // Handle string data if (typeof data === 'string') { return { data: data, length: data.length, const bytes = Array.from(data).map(c => c.charCodeAt(0)); const result = { data: new Uint8Array(bytes), length: bytes.length, toString: function(enc) { if (enc === 'hex' && encoding === 'hex') { // Return the original hex string return data; if (enc === 'hex') { return bytes.map(b => b.toString(16).padStart(2, '0')).join(''); } return data; } }; // Add SRP methods result.slice = function(start, end) { return Buffer.from(data.slice(start, end)); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } // Handle array/buffer data if (Array.isArray(data) || ArrayBuffer.isView(data)) { return { data: data, length: data.length, toString: function() { return Array.from(data).map(b => String.fromCharCode(b)).join(''); if (Array.isArray(data) || ArrayBuffer.isView(data) || (data instanceof ArrayBuffer)) { const bytes = Array.isArray(data) ? data : new Uint8Array(data.buffer || data); const result = { data: new Uint8Array(bytes), length: bytes.length, toString: function(enc) { if (enc === 'hex') { return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); } return Array.from(bytes).map(b => String.fromCharCode(b)).join(''); } }; // Add SRP methods result.slice = function(start, end) { return Buffer.from(bytes.slice(start, end)); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; // Default fallback return { data: data || '', length: (data && data.length) || 0, toString: function() { return ''; } result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } // Handle object data (last resort) if (typeof data === 'object') { console.warn('Buffer.from called with object type', data); const result = { data: data, length: data.length || 0, toString: function() { return JSON.stringify(data); } }; // Add SRP methods result.slice = function() { return Buffer.from({}); }; result.readUInt32BE = function() { return 0; }; result.writeUInt32BE = function() { return result; }; return result; } // Default fallback for any other type console.warn('Buffer.from called with unsupported type:', typeof data); const result = { data: new Uint8Array(0), length: 0, toString: function() { return ''; }, slice: function() { return Buffer.from([]); }, readUInt32BE: function() { return 0; }, writeUInt32BE: function() { return result; } }; return result; }, isBuffer: function(obj) { return obj && obj.data !== undefined; }, alloc: function(size) { const arr = new Array(size).fill(0); return { data: arr, isBuffer: function(obj) { return obj && (obj.data !== undefined || (originalBuffer && originalBuffer.isBuffer && originalBuffer.isBuffer(obj))); }, alloc: function(size, fill = 0) { const bytes = new Array(size).fill(fill); const result = { data: new Uint8Array(bytes), length: size, toString: function() { return ''; } toString: function(enc) { if (enc === 'hex') { return bytes.map(b => b.toString(16).padStart(2, '0')).join(''); } return bytes.map(b => String.fromCharCode(b)).join(''); } }; // Add SRP methods result.slice = function(start, end) { return Buffer.from(bytes.slice(start, end)); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; }, concat: function(list) { // Simple implementation that handles our use case return { data: list.map(b => b.data).join(''), length: list.reduce((acc, b) => acc + (b.length || 0), 0), toString: function() { return list.map(b => b.toString()).join(''); if (!Array.isArray(list) || list.length === 0) { return Buffer.alloc(0); } // Combine all buffers into one const totalLength = list.reduce((acc, buf) => acc + (buf ? (buf.length || 0) : 0), 0); const combinedArray = new Uint8Array(totalLength); let offset = 0; for (const buf of list) { if (buf && buf.data) { const data = buf.data instanceof Uint8Array ? buf.data : new Uint8Array(buf.data); combinedArray.set(data, offset); offset += buf.length; } } const result = { data: combinedArray, length: totalLength, toString: function(enc) { if (enc === 'hex') { return Array.from(combinedArray).map(b => b.toString(16).padStart(2, '0')).join(''); } return Array.from(combinedArray).map(b => String.fromCharCode(b)).join(''); } }; // Add SRP methods result.slice = function(start, end) { const slicedData = combinedArray.slice(start, end); return Buffer.from(slicedData); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < combinedArray.length ? combinedArray[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < combinedArray.length) { combinedArray[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } }; // Add missing crypto methods that SRP might need if (window.crypto && !window.crypto.randomBytes) { if (window.crypto) { if (!window.crypto.randomBytes) { window.crypto.randomBytes = function(size) { const array = new Uint8Array(size); window.crypto.getRandomValues(array); return Buffer.from(array); }; } // Add cryptographic hash functions if needed if (!window.crypto.createHash) { window.crypto.createHash = function(algorithm) { return { data: array, length: size, toString: function(encoding) { update: function(data) { this.data = data; return this; }, digest: async function(encoding) { // Use the SubtleCrypto API for actual hashing const dataBuffer = typeof this.data === 'string' ? new TextEncoder().encode(this.data) : this.data; let hashBuffer; try { if (algorithm === 'sha256') { hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer); } else if (algorithm === 'sha1') { hashBuffer = await window.crypto.subtle.digest('SHA-1', dataBuffer); } else { console.error('Unsupported hash algorithm:', algorithm); return Buffer.alloc(32); // Return empty buffer as fallback } const hashArray = Array.from(new Uint8Array(hashBuffer)); if (encoding === 'hex') { return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join(''); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } return Buffer.from(hashArray); } catch (e) { console.error('Hash calculation failed:', e); return Buffer.alloc(32); // Return empty buffer as fallback } return Array.from(array).map(b => String.fromCharCode(b)).join(''); } }; }; } } // Patch the SRP implementation for browser compatibility if (window.process) { if (!window.process) { window.process = { env: { NODE_ENV: 'production' } }; } // Add any missing process methods window.process.nextTick = window.process.nextTick || function(fn) { setTimeout(fn, 0); }; } console.log('Ente URL and SRP patches applied successfully'); })(); EOF ENDPATCHES # Create the static HTML files with scripts pre-injected for app_dir in photos accounts auth cast; do Loading Loading
start.sh +320 −56 Original line number Diff line number Diff line Loading @@ -679,8 +679,10 @@ chmod 644 /app/data/web/runtime-config.js # Create a custom URL patch file to fix the URL constructor error echo "==> Creating URL and SRP patch file" cat << EOF > /app/data/web/ente-patches.js cat > /app/data/web/photos/static/ente-patches.js << 'ENDPATCHES' (function() { console.log('Applying Ente URL and SRP patches...'); // Save original URL constructor const originalURL = window.URL; Loading Loading @@ -710,102 +712,364 @@ cat << EOF > /app/data/web/ente-patches.js } }; // More robust Buffer implementation for SRP window.Buffer = window.Buffer || { // Comprehensive Buffer polyfill for SRP const originalBuffer = window.Buffer; window.Buffer = { from: function(data, encoding) { // Handle undefined data - critical fix if (data === undefined) { console.warn('Buffer.from called with undefined data, creating empty buffer'); return { data: '', // Debug logging for the SRP calls console.debug('Buffer.from called with:', typeof data, data === undefined ? 'undefined' : data === null ? 'null' : Array.isArray(data) ? 'array[' + data.length + ']' : 'value', 'encoding:', encoding); // Handle undefined/null data - critical fix if (data === undefined || data === null) { console.warn('Buffer.from called with ' + (data === undefined ? 'undefined' : 'null') + ' data, creating empty buffer'); const result = { data: new Uint8Array(0), length: 0, toString: function() { return ''; } toString: function(enc) { return ''; } }; // Add additional methods that SRP might use result.slice = function() { return Buffer.from([]); }; result.readUInt32BE = function() { return 0; }; result.writeUInt32BE = function() { return result; }; return result; } // Special case for hex strings - very important for SRP if (typeof data === 'string' && encoding === 'hex') { // Convert hex string to byte array const bytes = []; for (let i = 0; i < data.length; i += 2) { if (data.length - i >= 2) { bytes.push(parseInt(data.substr(i, 2), 16)); } } const result = { data: new Uint8Array(bytes), length: bytes.length, toString: function(enc) { if (enc === 'hex' || !enc) { return data; // Return original hex string } return bytes.map(b => String.fromCharCode(b)).join(''); } }; // Add methods needed by SRP result.slice = function(start, end) { const slicedData = bytes.slice(start, end); return Buffer.from(slicedData.map(b => b.toString(16).padStart(2, '0')).join(''), 'hex'); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } // Handle string data if (typeof data === 'string') { return { data: data, length: data.length, const bytes = Array.from(data).map(c => c.charCodeAt(0)); const result = { data: new Uint8Array(bytes), length: bytes.length, toString: function(enc) { if (enc === 'hex' && encoding === 'hex') { // Return the original hex string return data; if (enc === 'hex') { return bytes.map(b => b.toString(16).padStart(2, '0')).join(''); } return data; } }; // Add SRP methods result.slice = function(start, end) { return Buffer.from(data.slice(start, end)); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } // Handle array/buffer data if (Array.isArray(data) || ArrayBuffer.isView(data)) { return { data: data, length: data.length, toString: function() { return Array.from(data).map(b => String.fromCharCode(b)).join(''); if (Array.isArray(data) || ArrayBuffer.isView(data) || (data instanceof ArrayBuffer)) { const bytes = Array.isArray(data) ? data : new Uint8Array(data.buffer || data); const result = { data: new Uint8Array(bytes), length: bytes.length, toString: function(enc) { if (enc === 'hex') { return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); } return Array.from(bytes).map(b => String.fromCharCode(b)).join(''); } }; // Add SRP methods result.slice = function(start, end) { return Buffer.from(bytes.slice(start, end)); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; // Default fallback return { data: data || '', length: (data && data.length) || 0, toString: function() { return ''; } result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } // Handle object data (last resort) if (typeof data === 'object') { console.warn('Buffer.from called with object type', data); const result = { data: data, length: data.length || 0, toString: function() { return JSON.stringify(data); } }; // Add SRP methods result.slice = function() { return Buffer.from({}); }; result.readUInt32BE = function() { return 0; }; result.writeUInt32BE = function() { return result; }; return result; } // Default fallback for any other type console.warn('Buffer.from called with unsupported type:', typeof data); const result = { data: new Uint8Array(0), length: 0, toString: function() { return ''; }, slice: function() { return Buffer.from([]); }, readUInt32BE: function() { return 0; }, writeUInt32BE: function() { return result; } }; return result; }, isBuffer: function(obj) { return obj && obj.data !== undefined; }, alloc: function(size) { const arr = new Array(size).fill(0); return { data: arr, isBuffer: function(obj) { return obj && (obj.data !== undefined || (originalBuffer && originalBuffer.isBuffer && originalBuffer.isBuffer(obj))); }, alloc: function(size, fill = 0) { const bytes = new Array(size).fill(fill); const result = { data: new Uint8Array(bytes), length: size, toString: function() { return ''; } toString: function(enc) { if (enc === 'hex') { return bytes.map(b => b.toString(16).padStart(2, '0')).join(''); } return bytes.map(b => String.fromCharCode(b)).join(''); } }; // Add SRP methods result.slice = function(start, end) { return Buffer.from(bytes.slice(start, end)); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < bytes.length) { bytes[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; }, concat: function(list) { // Simple implementation that handles our use case return { data: list.map(b => b.data).join(''), length: list.reduce((acc, b) => acc + (b.length || 0), 0), toString: function() { return list.map(b => b.toString()).join(''); if (!Array.isArray(list) || list.length === 0) { return Buffer.alloc(0); } // Combine all buffers into one const totalLength = list.reduce((acc, buf) => acc + (buf ? (buf.length || 0) : 0), 0); const combinedArray = new Uint8Array(totalLength); let offset = 0; for (const buf of list) { if (buf && buf.data) { const data = buf.data instanceof Uint8Array ? buf.data : new Uint8Array(buf.data); combinedArray.set(data, offset); offset += buf.length; } } const result = { data: combinedArray, length: totalLength, toString: function(enc) { if (enc === 'hex') { return Array.from(combinedArray).map(b => b.toString(16).padStart(2, '0')).join(''); } return Array.from(combinedArray).map(b => String.fromCharCode(b)).join(''); } }; // Add SRP methods result.slice = function(start, end) { const slicedData = combinedArray.slice(start, end); return Buffer.from(slicedData); }; result.readUInt32BE = function(offset = 0) { let value = 0; for (let i = 0; i < 4; i++) { value = (value << 8) + (offset + i < combinedArray.length ? combinedArray[offset + i] : 0); } return value; }; result.writeUInt32BE = function(value, offset = 0) { for (let i = 0; i < 4; i++) { if (offset + i < combinedArray.length) { combinedArray[offset + 3 - i] = value & 0xFF; value >>>= 8; } } return result; }; return result; } }; // Add missing crypto methods that SRP might need if (window.crypto && !window.crypto.randomBytes) { if (window.crypto) { if (!window.crypto.randomBytes) { window.crypto.randomBytes = function(size) { const array = new Uint8Array(size); window.crypto.getRandomValues(array); return Buffer.from(array); }; } // Add cryptographic hash functions if needed if (!window.crypto.createHash) { window.crypto.createHash = function(algorithm) { return { data: array, length: size, toString: function(encoding) { update: function(data) { this.data = data; return this; }, digest: async function(encoding) { // Use the SubtleCrypto API for actual hashing const dataBuffer = typeof this.data === 'string' ? new TextEncoder().encode(this.data) : this.data; let hashBuffer; try { if (algorithm === 'sha256') { hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer); } else if (algorithm === 'sha1') { hashBuffer = await window.crypto.subtle.digest('SHA-1', dataBuffer); } else { console.error('Unsupported hash algorithm:', algorithm); return Buffer.alloc(32); // Return empty buffer as fallback } const hashArray = Array.from(new Uint8Array(hashBuffer)); if (encoding === 'hex') { return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join(''); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } return Buffer.from(hashArray); } catch (e) { console.error('Hash calculation failed:', e); return Buffer.alloc(32); // Return empty buffer as fallback } return Array.from(array).map(b => String.fromCharCode(b)).join(''); } }; }; } } // Patch the SRP implementation for browser compatibility if (window.process) { if (!window.process) { window.process = { env: { NODE_ENV: 'production' } }; } // Add any missing process methods window.process.nextTick = window.process.nextTick || function(fn) { setTimeout(fn, 0); }; } console.log('Ente URL and SRP patches applied successfully'); })(); EOF ENDPATCHES # Create the static HTML files with scripts pre-injected for app_dir in photos accounts auth cast; do Loading